shader_types/
lib.rs

1//! # DO NOT USE THIS CRATE
2//!
3//! There are fundamental flaws with this method of defining padding. This crate is kept live
4//! and not yanked for backwards compat only. For a crate that serves this exact purpose, look
5//! at the wonderful [`glsl-layout`](https://docs.rs/glsl-layout/) which doesn't suffer from
6//! the same problems.
7//!
8//! # Old Readme:
9//!
10//! Vector and Matrix types that are properly aligned for use in std140 uniforms.
11//!
12//! All the types in this library have the same alignment and size as the equivilant glsl type in the
13//! default mode (std140).
14//!
15//! This fixes the padding within members of structs but padding between members needs to be minded.
16//! The types in [`padding`] are there to make this easier.
17//!
18//! Vectors are constructable to/from an array of their underlying type. Matrices are constructable
19//! to/from both 1d and 2d arrays as well as an array of the underlying _vector_ type. (eg. [`Mat2`] can be
20//! constructed from `[Vec2; 2]`)
21//!
22//! # Features
23//!
24//! - `bytemuck` all types implement `Zeroable` and `Pod`.
25//! - `mint` enable conversions to/from equivalent mint types.
26//!
27//! # Example
28//!
29//! For the following glsl:
30//!
31//! ```glsl
32//! layout(set = 0, binding = 0) uniform Block {
33//!     mat4 mvp;
34//!     vec3 position;
35//!     vec3 normal;
36//!     vec2 uv;
37//!     int constants[3];
38//! };
39//! ```
40//!
41//! This struct is rife with padding. However it's now easy to mind the padding:
42//!
43//! ```rust
44//! use shader_types::{Vec2, Vec3, Mat4, ArrayMember};
45//!
46//! // Definition
47//! #[repr(C)]
48//! #[derive(Copy, Clone)]
49//! struct UniformBlock {
50//!     mvp: Mat4, // 16 align + 64 size
51//!     position: Vec3, // 16 align + 12 size
52//!     normal: Vec3, // 16 align + 12 size
53//!     uv: Vec2, // 8 align + 8 size
54//!     constants: [ArrayMember<i32>; 3] // 3x 16 align + 4 size
55//! }
56//!
57//! fn generate_mvp() -> [f32; 16] {
58//!     // ...
59//! #     unsafe { std::mem::zeroed() }
60//! }
61//!
62//! // Construction
63//! let block = UniformBlock {
64//!     // Anything that can be converted to a [f32; 16] or [[f32; 4]; 4] works
65//!     mvp: Mat4::from(generate_mvp()),
66//!     position: Vec3::new([0.0, 1.0, 2.0]), // `from` also works
67//!     normal: Vec3::new([-2.0, 2.0, 3.0]),
68//!     uv: Vec2::new([0.0, 1.0]),
69//!     constants: [ArrayMember(0), ArrayMember(1), ArrayMember(2)]
70//! };
71//!
72//! // Supports bytemuck with the `bytemuck` feature
73//! unsafe impl bytemuck::Zeroable for UniformBlock {}
74//! unsafe impl bytemuck::Pod for UniformBlock {}
75//!
76//! let block_u8: &[u8] = bytemuck::cast_slice(&[block]);
77//! ```
78//!
79//! # MSRV
80//!
81//! Rust 1.34
82
83#![cfg_attr(not(feature = "std"), no_std)]
84#![cfg_attr(docsrs, feature(doc_cfg))]
85
86/// A single signed integer. Has size 4 and alignment 4.
87pub type Int = i32;
88/// A single unsigned integer. Has size 4 and alignment 4.
89pub type Uint = u32;
90/// A single single-precision floating point number. Has size 4 and alignment 4.
91pub type Float = f32;
92/// A single double-precision floating point number. Has size 8 and alignment 8.
93pub type Double = f64;
94
95/// A boolean value. Has size 4 and alignment 4.
96#[repr(transparent)]
97#[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd)]
98pub struct Bool(u32);
99
100impl Bool {
101    pub fn new(value: bool) -> Self {
102        Self(value as u32)
103    }
104}
105
106impl From<Bool> for bool {
107    fn from(other: Bool) -> Self {
108        other.0 != 0
109    }
110}
111
112impl From<bool> for Bool {
113    fn from(other: bool) -> Self {
114        Self(other as u32)
115    }
116}
117
118#[cfg(feature = "bytemuck")]
119unsafe impl bytemuck::Zeroable for Bool {}
120#[cfg(feature = "bytemuck")]
121unsafe impl bytemuck::Pod for Bool {}
122
123macro_rules! define_vectors {
124    ( $(( $name:ident, $mint_name:ident, $prim:ident * $count:literal, align: $align:literal, size: $size:literal ),)* ) => {
125        $(
126            define_vectors!(@impl
127                $name,
128                mint::$mint_name<$prim>,
129                $prim,
130                $count,
131                $align,
132                concat!(
133                    "Vector of ", stringify!($count), " `", stringify!($prim), "` values. ",
134                    "Has size ", stringify!($size), " and alignment ", stringify!($align), "."
135                ),
136                concat!(
137                    "Construct a `", stringify!($name), "` from any type which is convertable into a ",
138                    "`mint::", stringify!($mint_name), "<", stringify!($prim), ">`."
139                )
140            );
141        )*
142    };
143
144    (@impl $name:ident, $mint_type:ty, $ty:ty, $count:literal, $align:literal, $doc:expr, $mint_doc:expr) => {
145        #[doc = $doc]
146        #[repr(C, align($align))]
147        #[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd)]
148        pub struct $name {
149            pub inner: [$ty; $count],
150        }
151
152        #[cfg(feature = "bytemuck")]
153        unsafe impl bytemuck::Zeroable for $name {}
154        #[cfg(feature = "bytemuck")]
155        unsafe impl bytemuck::Pod for $name {}
156
157        impl $name {
158            #[inline(always)]
159            pub fn new(inner: [$ty; $count]) -> Self {
160                Self {
161                    inner,
162                }
163            }
164
165            #[cfg(feature = "mint")]
166            #[cfg_attr(docsrs, doc(cfg(feature = "mint")))]
167            #[doc = $mint_doc]
168            #[inline(always)]
169            pub fn from_mint<T: Into<$mint_type>>(value: T) -> Self {
170                Self::from(value.into())
171            }
172        }
173
174        #[cfg(feature = "mint")]
175        impl From<$mint_type> for $name {
176            #[inline(always)]
177            fn from(other: $mint_type) -> Self {
178                // Mint's types do not implement From for arrays, only Into.
179                let inner: [$ty; $count] = other.into();
180
181                Self { inner }
182            }
183        }
184
185        impl From<[$ty; $count]> for $name {
186            #[inline(always)]
187            fn from(inner: [$ty; $count]) -> Self {
188                Self {
189                    inner,
190                }
191            }
192        }
193
194        #[cfg(feature = "mint")]
195        impl From<$name> for $mint_type {
196            #[inline(always)]
197            fn from(other: $name) -> Self {
198                other.inner.into()
199            }
200        }
201
202        impl From<$name> for [$ty; $count] {
203            #[inline(always)]
204            fn from(other: $name) -> Self {
205                other.inner
206            }
207        }
208    };
209}
210
211define_vectors! {
212    (Vec2, Vector2, f32 * 2, align: 8, size: 8),
213    (Vec3, Vector3, f32 * 3, align: 16, size: 12),
214    (Vec4, Vector4, f32 * 4, align: 16, size: 16),
215    (DVec2, Vector2, f64 * 2, align: 16, size: 16),
216    (DVec3, Vector3, f64 * 3, align: 32, size: 24),
217    (DVec4, Vector4, f64 * 4, align: 32, size: 32),
218    (UVec2, Vector2, u32 * 2, align: 8, size: 8),
219    (UVec3, Vector3, u32 * 3, align: 16, size: 12),
220    (UVec4, Vector4, u32 * 4, align: 16, size: 16),
221    (IVec2, Vector2, i32 * 2, align: 8, size: 8),
222    (IVec3, Vector3, i32 * 3, align: 16, size: 12),
223    (IVec4, Vector4, i32 * 4, align: 16, size: 16),
224    (BVec2, Vector2, Bool * 2, align: 8, size: 8),
225    (BVec3, Vector3, Bool * 3, align: 16, size: 12),
226    (BVec4, Vector4, Bool * 4, align: 16, size: 16),
227}
228
229macro_rules! define_matrices {
230    ( $(( $name:ident, $mint_name:ident, $prim_ty:ty, $row_ty:ty, $rows:literal * $cols:literal, align: $align:literal, size: $size:literal, pad: $pad:literal, [$($idx:literal),*] ),)* ) => {
231        $(
232            define_matrices!(@impl
233                $name,
234                mint::$mint_name<$prim_ty>,
235                $align,
236                $prim_ty,
237                $row_ty,
238                $rows,
239                $cols,
240                $pad,
241                [$( $idx ),*],
242                concat!(
243                    "Matrix of `", stringify!($prim_ty), "` values with ", stringify!($rows), " rows and ", stringify!($cols), " columns. ",
244                    "Has size ", stringify!($size), " and alignment ", stringify!($align), "."
245                ),
246                concat!(
247                    "Construct a `", stringify!($name), "` from any type which is convertable into a ",
248                    "`mint::", stringify!($mint_name), "<", stringify!($prim_ty), ">`."
249                )
250            );
251        )*
252    };
253
254    (@impl $name:ident, $mint_type:ty, $align:literal, $inner_ty:ty, $ty:ty, $count_x:literal, $count_y:literal, $padding:literal, [$( $idx:literal ),*], $doc:expr, $mint_doc:expr) => {
255        #[doc = $doc]
256        #[repr(C, align($align))]
257        #[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd)]
258        pub struct $name {
259            pub inner: [$ty; $count_y],
260            _padding: [u8; $padding],
261        }
262
263        #[cfg(feature = "bytemuck")]
264        unsafe impl bytemuck::Zeroable for $name {}
265        #[cfg(feature = "bytemuck")]
266        unsafe impl bytemuck::Pod for $name {}
267
268        impl $name {
269            #[inline(always)]
270            pub fn new(inner: [$ty; $count_y]) -> Self {
271                Self { inner, _padding: [0; $padding] }
272            }
273
274            #[cfg(feature = "mint")]
275            #[cfg_attr(docsrs, doc(cfg(feature = "mint")))]
276            #[doc = $mint_doc]
277            #[inline(always)]
278            pub fn from_mint<T: Into<$mint_type>>(value: T) -> Self {
279                Self::from(value.into())
280            }
281        }
282
283        #[cfg(feature = "mint")]
284        impl From<$mint_type> for $name {
285            #[inline(always)]
286            fn from(other: $mint_type) -> Self {
287                // Mint's types do not implement From for arrays, only Into.
288                let as_arr: [$inner_ty; $count_x * $count_y] = other.into();
289                as_arr.into()
290            }
291        }
292
293        impl From<[$ty; $count_y]> for $name {
294            #[inline(always)]
295            fn from(inner: [$ty; $count_y]) -> Self {
296                Self { inner, _padding: [0; $padding]  }
297            }
298        }
299
300        impl From<[$inner_ty; $count_x * $count_y]> for $name {
301            #[inline(always)]
302            fn from(inner: [$inner_ty; $count_x * $count_y]) -> Self {
303                let d2: [[$inner_ty; $count_x]; $count_y] = unsafe { core::mem::transmute(inner) };
304                Self {
305                    inner: [$(<$ty>::from(d2[$idx])),*],
306                    _padding: [0; $padding],
307                }
308            }
309        }
310
311        impl From<[[$inner_ty; $count_x]; $count_y]> for $name {
312            #[inline(always)]
313            fn from(inner: [[$inner_ty; $count_x]; $count_y]) -> Self {
314                Self {
315                    inner: [$(<$ty>::from(inner[$idx])),*],
316                    _padding: [0; $padding],
317                }
318            }
319        }
320
321        #[cfg(feature = "mint")]
322        impl From<$name> for $mint_type {
323            #[inline(always)]
324            fn from(other: $name) -> Self {
325                let as_arr = <[[$inner_ty; $count_x]; $count_y]>::from(other);
326                as_arr.into()
327            }
328        }
329
330        impl From<$name> for [$ty; $count_y] {
331            #[inline(always)]
332            fn from(other: $name) -> Self {
333                other.inner
334            }
335        }
336
337        impl From<$name> for [$inner_ty; $count_x * $count_y] {
338            #[inline(always)]
339            fn from(other: $name) -> Self {
340                let d2: [[$inner_ty; $count_x]; $count_y] = [$(<[$inner_ty; $count_x]>::from(other.inner[$idx])),*];
341                unsafe { core::mem::transmute(d2) }
342            }
343        }
344
345        impl From<$name> for [[$inner_ty; $count_x]; $count_y] {
346            #[inline(always)]
347            fn from(other: $name) -> Self {
348                [$(<[$inner_ty; $count_x]>::from(other.inner[$idx])),*]
349            }
350        }
351    };
352}
353
354define_matrices! {
355    (Mat2x2, ColumnMatrix2, f32, Vec2, 2 * 2, align: 8, size: 16, pad: 0, [0, 1]),
356    (Mat2x3, ColumnMatrix2x3, f32, Vec2, 2 * 3, align: 8, size: 32, pad: 8, [0, 1, 2]),
357    (Mat2x4, ColumnMatrix2x4, f32, Vec2, 2 * 4, align: 8, size: 32, pad: 0, [0, 1, 2, 3]),
358
359    (Mat3x2, ColumnMatrix3x2, f32, Vec3, 3 * 2, align: 16, size: 32, pad: 4, [0, 1]),
360    (Mat3x3, ColumnMatrix3, f32, Vec3, 3 * 3, align: 16, size: 48, pad: 4, [0, 1, 2]),
361    (Mat3x4, ColumnMatrix3x4, f32, Vec3, 3 * 4, align: 16, size: 64, pad: 4, [0, 1, 2, 3]),
362
363    (Mat4x2, ColumnMatrix4x2, f32, Vec4, 4 * 2, align: 16, size: 32, pad: 0, [0, 1]),
364    (Mat4x3, ColumnMatrix4x3, f32, Vec4, 4 * 3, align: 16, size: 48, pad: 0, [0, 1, 2]),
365    (Mat4x4, ColumnMatrix4, f32, Vec4, 4 * 4, align: 16, size: 64, pad: 0, [0, 1, 2, 3]),
366
367    (DMat2x2, ColumnMatrix2, f64, DVec2, 2 * 2, align: 16, size: 32, pad: 0, [0, 1]),
368    (DMat2x3, ColumnMatrix2x3, f64, DVec2, 2 * 3, align: 16, size: 48, pad: 0, [0, 1, 2]),
369    (DMat2x4, ColumnMatrix2x4, f64, DVec2, 2 * 4, align: 16, size: 64, pad: 0, [0, 1, 2, 3]),
370
371    (DMat3x2, ColumnMatrix3x2, f64, DVec3, 3 * 2, align: 32, size: 64, pad: 0, [0, 1]),
372    (DMat3x3, ColumnMatrix3, f64, DVec3, 3 * 3, align: 32, size: 96, pad: 0, [0, 1, 2]),
373    (DMat3x4, ColumnMatrix3x4, f64, DVec3, 3 * 4, align: 32, size: 128, pad: 0, [0, 1, 2, 3]),
374
375    (DMat4x2, ColumnMatrix4x2, f64, DVec4, 4 * 2, align: 32, size: 64, pad: 0, [0, 1]),
376    (DMat4x3, ColumnMatrix4x3, f64, DVec4, 4 * 3, align: 32, size: 96, pad: 0, [0, 1, 2]),
377    (DMat4x4, ColumnMatrix4, f64, DVec4, 4 * 4, align: 32, size: 128, pad: 0, [0, 1, 2, 3]),
378}
379
380/// Matrix of f32s with 2 columns and 2 rows. Alignment 8, size 16.
381pub type Mat2 = Mat2x2;
382/// Matrix of f32s with 3 columns and 3 rows. Alignment 16, size 48.
383pub type Mat3 = Mat3x3;
384/// Matrix of f32s with 4 columns and 4 rows. Alignment 16, size 64.
385pub type Mat4 = Mat4x4;
386/// Matrix of f64s with 2 columns and 3 rows. Alignment 16, size 48.
387pub type DMat2 = DMat2x2;
388/// Matrix of f64s with 3 columns and 3 rows. Alignment 32, size 96.
389pub type DMat3 = DMat3x3;
390/// Matrix of f64s with 4 columns and 4 rows. Alignment 32, size 128.
391pub type DMat4 = DMat4x4;
392
393/// Pads an element to be in an array in a shader.
394///
395/// All elements in arrays need to be aligned to 16 bytes. This automatically aligns your types to 16 bytes.
396///
397/// This glsl:
398///
399/// ```glsl
400/// struct FloatArray {
401///     float array[45];
402/// };
403/// ```
404///
405/// turns into:
406///
407/// ```rust
408/// #[repr(C)]
409/// struct FloatArray {
410///     array: [shader_types::ArrayMember<f32>; 45]
411/// }
412/// ```
413#[repr(C, align(16))]
414#[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Hash)]
415pub struct ArrayMember<T>(pub T);
416
417#[cfg(feature = "bytemuck")]
418unsafe impl<T: bytemuck::Zeroable> bytemuck::Zeroable for ArrayMember<T> {}
419#[cfg(feature = "bytemuck")]
420unsafe impl<T: bytemuck::Pod> bytemuck::Pod for ArrayMember<T> {}
421
422/// Pads a structure for use with dynamic offsets in graphics apis.
423///
424/// All dynamic offsets need to be aligned to 256 bytes. This automatically aligns your types to 256s.
425///
426/// Given a shader of:
427///
428/// ```glsl
429/// uniform Uniforms {
430///     mat4 mvp;
431///     mat4 mv;
432/// };
433/// ```
434///
435/// An array of rust structs can be made and used:
436///
437/// ```rust
438/// use shader_types::{Mat4, DynamicOffsetMember};
439/// # use std::mem::size_of;
440///
441/// // Implementations don't matter
442/// fn generate_mvp(_: usize) -> [f32; 16] {
443///     // ...
444/// #     unsafe { std::mem::zeroed() }
445/// }
446/// fn generate_mv(_: usize) -> [f32; 16] {
447///     // ...
448/// #     unsafe { std::mem::zeroed() }
449/// }
450/// fn set_uniform_buffer(_: &[DynamicOffsetMember<Uniforms>]) {
451///     // ...
452/// }
453/// fn bind_uniform_with_offset(_: usize) {
454///     // ...
455/// }
456/// fn render_object(_: usize) {
457///     // ...
458/// }
459///
460/// #[repr(C)]
461/// struct Uniforms {
462///     mvp: Mat4,
463///     mv: Mat4,
464/// }
465///
466/// // Generate buffer
467/// let mut vec: Vec<DynamicOffsetMember<Uniforms>> = Vec::new();
468/// for obj_idx in 0..10 {
469///     vec.push(DynamicOffsetMember(Uniforms {
470///         mvp: Mat4::from(generate_mvp(obj_idx)),
471///         mv: Mat4::from(generate_mv(obj_idx)),
472///     }))
473/// }
474///
475/// // Use Buffer
476/// set_uniform_buffer(&vec);
477/// for obj_idx in 0..10 {
478///     let offset = obj_idx * size_of::<DynamicOffsetMember<Uniforms>>();
479///     // Offset must be aligned by 256
480///     assert_eq!(offset % 256, 0);
481///     bind_uniform_with_offset(offset);
482///     render_object(obj_idx);
483/// }
484/// ```
485#[repr(C, align(256))]
486#[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Hash)]
487pub struct DynamicOffsetMember<T>(pub T);
488
489#[cfg(feature = "bytemuck")]
490unsafe impl<T: bytemuck::Zeroable> bytemuck::Zeroable for DynamicOffsetMember<T> {}
491#[cfg(feature = "bytemuck")]
492unsafe impl<T: bytemuck::Pod> bytemuck::Pod for DynamicOffsetMember<T> {}
493
494/// Correctly sized padding helpers.
495pub mod padding {
496    macro_rules! define_padding {
497        ($name:ident, $count:literal <- $doc:literal) => {
498            #[doc = $doc]
499            #[repr(C)]
500            #[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd)]
501            pub struct $name {
502                _padding: [u8; $count],
503            }
504
505            #[cfg(feature = "bytemuck")]
506            unsafe impl bytemuck::Zeroable for $name {}
507            #[cfg(feature = "bytemuck")]
508            unsafe impl bytemuck::Pod for $name {}
509
510            impl $name {
511                #[inline(always)]
512                pub fn new() -> Self {
513                    Self::default()
514                }
515            }
516        };
517    }
518
519    define_padding!(Pad1Float, 4 <- "Padding the size of a single float/uint/int. 4 bytes.");
520    define_padding!(Pad2Float, 8 <- "Padding the size of two floats/uints/ints. 8 bytes.");
521    define_padding!(Pad3Float, 12 <- "Padding the size of three floats/uints/ints. 12 bytes.");
522    define_padding!(Pad4Float, 16 <- "Padding the size of four floats/uints/ints. 16 bytes.");
523    define_padding!(Pad1Double, 8 <- "Padding the size of a single double. 8 bytes.");
524    define_padding!(Pad2Double, 16 <- "Padding the size of two doubles. 16 bytes.");
525    define_padding!(Pad3Double, 24 <- "Padding the size of three doubles. 24 bytes.");
526    define_padding!(Pad4Double, 32 <- "Padding the size of four doubles. 32 bytes.");
527}
528
529#[cfg(test)]
530mod test {
531    use crate::*;
532    use core::mem::size_of;
533    use glsl_layout::AsStd140;
534    use std::ptr::null;
535
536    #[repr(C)]
537    struct Test1 {
538        a: Vec3,
539        b: u32,
540    }
541
542    #[repr(C)]
543    struct Test2 {
544        a: std140::vec3,
545        b: std140::uint,
546    }
547
548    #[derive(AsStd140)]
549    struct Test3 {
550        a: glsl_layout::vec3,
551        b: glsl_layout::int,
552    }
553
554    type Test3U = <Test3 as AsStd140>::Std140;
555
556    #[test]
557    fn sizes() {
558        assert_eq!((&unsafe { &*null::<Test3U>() }.b) as *const i32 as usize, 12);
559        assert_eq!(size_of::<Test3U>(), 16);
560    }
561}