1#[macro_export]
12macro_rules! or_return {
13 ($expr:expr) => {
14 match $expr {
15 ::core::option::Option::Some(value) => value,
16 ::core::option::Option::None => return,
17 }
18 };
19 ($expr:expr, $ret:expr) => {
20 match $expr {
21 ::core::option::Option::Some(value) => value,
22 ::core::option::Option::None => return $ret,
23 }
24 };
25}
26
27#[macro_export]
41macro_rules! bind_mat {
42 ($pass:expr, $materials:expr, $handle:expr) => {{
43 let material = $crate::or_return!($materials.get($handle));
44 $pass.set_pipeline(&material.pipeline);
45 $pass.set_bind_group(0, &material.bind_group, &[]);
46 material
47 }};
48}
49
50#[macro_export]
57macro_rules! bind_comp {
58 ($pass:expr, $computes:expr, $handle:expr) => {{
59 let compute = $crate::or_return!($computes.get($handle));
60 $pass.set_pipeline(&compute.pipeline);
61 $pass.set_bind_group(0, &compute.bind_group, &[]);
62 compute
63 }};
64}
65
66#[macro_export]
76macro_rules! draw_mesh {
77 ($pass:expr, $meshes:expr, $handle:expr) => {
78 $crate::draw_mesh!($pass, $meshes, $handle, 0..1)
79 };
80 ($pass:expr, $meshes:expr, $handle:expr, $instances:expr) => {{
81 let mesh = $crate::or_return!($meshes.get($handle));
82 $pass.set_vertex_buffer(0, &mesh.vertex_buffer);
83 $pass.set_index_buffer(&mesh.index_buffer, $crate::graphics::types::IndexFormat::Uint32);
84 $pass.draw_indexed(0..mesh.index_count, 0, $instances);
85 }};
86}
87
88#[cfg(test)]
89mod tests {
90 use crate::graphics::types::IndexFormat;
91
92 struct Assets<T>(Option<T>);
93
94 impl<T> Assets<T> {
95 fn get(&self, _handle: u32) -> Option<&T> {
96 self.0.as_ref()
97 }
98 }
99
100 struct GPUMaterial {
101 pipeline: &'static str,
102 bind_group: &'static str,
103 }
104
105 #[derive(Default)]
106 struct RecordingPass {
107 pipeline: Option<&'static str>,
108 bind_group: Option<&'static str>,
109 vertex_buffer: Option<&'static str>,
110 index_buffer: Option<(&'static str, IndexFormat)>,
111 drawn: Option<(std::ops::Range<u32>, i32, std::ops::Range<u32>)>,
112 }
113
114 impl RecordingPass {
115 fn set_pipeline(&mut self, pipeline: &&'static str) {
116 self.pipeline = Some(pipeline);
117 }
118
119 fn set_bind_group(&mut self, _index: u32, bind_group: &&'static str, _offsets: &[u32]) {
120 self.bind_group = Some(bind_group);
121 }
122
123 fn set_vertex_buffer(&mut self, _slot: u32, buffer: &&'static str) {
124 self.vertex_buffer = Some(buffer);
125 }
126
127 fn set_index_buffer(&mut self, buffer: &&'static str, format: IndexFormat) {
128 self.index_buffer = Some((buffer, format));
129 }
130
131 fn draw_indexed(&mut self, indices: std::ops::Range<u32>, base_vertex: i32, instances: std::ops::Range<u32>) {
132 self.drawn = Some((indices, base_vertex, instances));
133 }
134 }
135
136 fn bind_missing(pass: &mut RecordingPass) {
137 let materials: Assets<GPUMaterial> = Assets(None);
138 bind_mat!(pass, materials, 0u32);
139 }
140
141 #[test]
142 fn bind_mat_returns_early_when_material_missing() {
143 let mut pass = RecordingPass::default();
144 bind_missing(&mut pass);
145 assert!(pass.pipeline.is_none());
146 assert!(pass.bind_group.is_none());
147 }
148
149 fn bind_present(pass: &mut RecordingPass) {
150 let materials = Assets(Some(GPUMaterial { pipeline: "pipeline", bind_group: "bind_group" }));
151 let material = bind_mat!(pass, materials, 0u32);
152 assert_eq!(material.bind_group, "bind_group");
153 }
154
155 #[test]
156 fn bind_mat_sets_pipeline_and_bind_group() {
157 let mut pass = RecordingPass::default();
158 bind_present(&mut pass);
159 assert_eq!(pass.pipeline, Some("pipeline"));
160 assert_eq!(pass.bind_group, Some("bind_group"));
161 }
162
163 struct GPUMesh {
164 vertex_buffer: &'static str,
165 index_buffer: &'static str,
166 index_count: u32,
167 }
168
169 #[test]
170 fn draw_mesh_defaults_to_a_single_instance() {
171 let mut pass = RecordingPass::default();
172 let meshes = Assets(Some(GPUMesh { vertex_buffer: "vbo", index_buffer: "ibo", index_count: 6 }));
173 draw_mesh!(pass, meshes, 0u32);
174
175 assert_eq!(pass.vertex_buffer, Some("vbo"));
176 assert_eq!(pass.index_buffer, Some(("ibo", IndexFormat::Uint32)));
177 assert_eq!(pass.drawn, Some((0..6, 0, 0..1)));
178 }
179
180 #[test]
181 fn draw_mesh_accepts_an_explicit_instance_range() {
182 let mut pass = RecordingPass::default();
183 let meshes = Assets(Some(GPUMesh { vertex_buffer: "vbo", index_buffer: "ibo", index_count: 6 }));
184 draw_mesh!(pass, meshes, 0u32, 0..12);
185
186 assert_eq!(pass.drawn, Some((0..6, 0, 0..12)));
187 }
188
189 #[test]
190 fn draw_mesh_returns_early_when_missing() {
191 let mut pass = RecordingPass::default();
192 let meshes: Assets<GPUMesh> = Assets(None);
193 draw_mesh!(pass, meshes, 0u32);
194
195 assert!(pass.drawn.is_none());
196 }
197}