Skip to main content

mew/
shaderstruct.rs

1#[allow(unused_imports)]
2use crate::shaderprimitive;
3
4/// Define a shader struct (to use in a **storage** or **uniform** buffer).
5///
6/// Give each field a name and a standard shader type from [`shaderprimitive`].
7///
8/// Uniform buffers are another can of worms, with their own weird alignment
9/// rules. The best way to deal with them is probably to make 
10///
11/// Note that fields **must** be numbered correctly, starting from 0. This is
12/// because I don't know how to teach macros how to count and still be able to
13/// index tuples.  
14/// Also, the fields are _not_ rearranged to be optimized to their alignment
15/// rules, though they should be padded correctly for regular shader structs.
16///
17/// ```
18/// shader_struct! { ShaderStruct [
19///     0 int => I32,
20///     1 mat => Mat4x4f,
21///     2 float => F32,
22/// ] }
23///
24/// buffer! { ShaderBuffer <ShaderStruct> as STORAGE | COPY_DST }
25///
26/// // Big enough to store 10 ShaderStructs, including padding
27/// let buffer: ShaderBuffer = context.new_buffer(10);
28/// ```
29///
30/// When you create a shader struct on the Rust side, its memory layout should
31/// match exactly as it is shader-side. You can write directly to each field
32/// (though everything's in arrays) and then safely-ish transmute it to a byte
33/// slice to write to the buffer.
34///
35/// ```
36/// let single_struct: ShaderStruct = (
37///     [5_i32],
38///     [[0.0, 1.0, 2.0, 3.0]; 4],
39///     [4.0],
40/// ).into();
41/// let mut lotsa_structs = [single_struct; 10];
42/// *lotsa_structs[7].float = [5.0];
43///
44/// context.queue.write_buffer(
45///     &buffer,
46///     some_offset * size_of::<ShaderStruct>() as u64,
47///     lotsa_structs.as_byte_slice(),
48/// );
49/// ```
50#[macro_export]
51macro_rules! shader_struct {
52    { $struct:ident [
53        $( $num:tt $field:ident => $ty:ident ),* $(,)?
54    ] } => {
55        #[derive(Clone, Debug, PartialEq, PartialOrd)]
56        #[repr(C)]
57        pub struct $struct {
58            $( pub $field: $crate::shaderprimitive::$ty, )*
59            _padding: [u8; Self::PADDING],
60        }
61
62        impl $struct {
63            //pub const SUM_SIZE: usize = ::std::mem::size_of::<( $( $ty, )* )>();
64            const SUM_SIZE: usize = {
65                #[repr(C)]
66                struct SizeTest {
67                    $( $field: $crate::shaderprimitive::$ty, )*
68                }
69                ::std::mem::size_of::<SizeTest>()
70            };
71            const MAX_SIZE: usize = $crate::max_size! { $( $crate::shaderprimitive::$ty )* };
72            /// The total size of the struct, including padding for alignment.
73            pub const BYTES: usize = Self::SUM_SIZE.next_multiple_of(Self::MAX_SIZE);
74            /// The unused padding bytes necessary to bring the alignment up to a multiple of the
75            /// biggest struct member's size. Aim to minimize this by rearranging members.
76            pub const PADDING: usize = Self::BYTES - Self::SUM_SIZE;
77        }
78
79        impl $crate::shaderstruct::MewShaderStruct for $struct {
80            type Bytes = [u8; Self::BYTES];
81            type Inners = ( $( <$crate::shaderprimitive::$ty as $crate::shaderprimitive::MewShaderPrimitive>::Inner , )* );
82
83            fn from_inners(inners: Self::Inners) -> Self {
84                #[allow(unused_imports)]
85                use $crate::shaderprimitive::MewShaderPrimitive;
86                Self {
87                    $( $field: $crate::shaderprimitive::$ty::from_inner(inners.$num), )*
88                    _padding: [0; Self::PADDING],
89                }
90            }
91
92            fn from_bytes(bytes: Self::Bytes) -> Self {
93                unsafe { ::std::mem::transmute(bytes) }
94            }
95
96            fn zeroed() -> Self {
97                unsafe {
98                    Self {
99                        //$( $field: [0; $crate::shader_struct! { @size $ty } ], )*
100                        $( $field: ::std::mem::zeroed(), )*
101                        _padding: [0; Self::PADDING],
102                    }
103                }
104            }
105        }
106
107
108        impl From<[u8; Self::BYTES]> for $struct {
109            fn from(bytes: [u8; Self::BYTES]) -> Self {
110                use $crate::shaderstruct::MewShaderStruct;
111                Self::from_bytes(bytes)
112            }
113        }
114        impl From<( $( <$crate::shaderprimitive::$ty as $crate::shaderprimitive::MewShaderPrimitive>::Inner , )* )> for $struct {
115            fn from(inners: ( $( <$crate::shaderprimitive::$ty as $crate::shaderprimitive::MewShaderPrimitive>::Inner , )* )) -> Self {
116                use $crate::shaderstruct::MewShaderStruct;
117                Self::from_inners(inners)
118            }
119        }
120
121        impl AsRef<[u8; Self::BYTES]> for $struct {
122            fn as_ref(&self) -> &[u8; Self::BYTES] {
123                unsafe { ::std::mem::transmute(self) }
124            }
125        }
126        impl AsMut<[u8; Self::BYTES]> for $struct {
127            fn as_mut(&mut self) -> &mut [u8; Self::BYTES] {
128                unsafe { ::std::mem::transmute(self) }
129            }
130        }
131
132        impl Default for $struct {
133            fn default() -> Self {
134                use $crate::shaderstruct::MewShaderStruct;
135                Self::zeroed()
136            }
137        }
138    }
139}
140pub use shader_struct;
141
142
143/// Trait for internal use to work with shader structs.
144pub trait MewShaderStruct {
145    /// A byte array (`[u8; N]`) the same size as the struct.
146    type Bytes;
147    /// A tuple of all internal types.
148    type Inners;
149    /// Build the struct using its internal types.
150    fn from_inners(inners: Self::Inners) -> Self;
151    /// Build the struct out of a byte array.
152    fn from_bytes(bytes: Self::Bytes) -> Self;
153    /// Build a struct with all fields zeroed.
154    fn zeroed() -> Self;
155}
156
157
158/// Convenience trait to convert shader struct slices directly to byte slices.
159///
160/// Automatically implemented on `&[S]` where `S` is any shader struct.
161pub trait MewShaderStructByteSlice {
162    /// Get a byte slice to write directly to a buffer.
163    fn as_byte_slice(&self) -> &[u8];
164}
165
166impl<S: MewShaderStruct> MewShaderStructByteSlice for [S] {
167    fn as_byte_slice(&self) -> &[u8] {
168        unsafe {
169            std::slice::from_raw_parts(
170                self as *const [S] as *const u8,
171                size_of::<S>() * self.len(),
172            )
173        }
174    }
175}