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}