wgpu_examples/ray_cube_fragment/
mod.rs

1use crate::utils;
2use bytemuck::{Pod, Zeroable};
3use glam::{Mat4, Quat, Vec3};
4use std::ops::IndexMut;
5use std::{borrow::Cow, future::Future, iter, mem, pin::Pin, task};
6use wgpu::util::DeviceExt;
7
8// from cube
9#[repr(C)]
10#[derive(Clone, Copy, Pod, Zeroable)]
11struct Vertex {
12    _pos: [f32; 4],
13    _tex_coord: [f32; 2],
14}
15
16fn vertex(pos: [i8; 3], tc: [i8; 2]) -> Vertex {
17    Vertex {
18        _pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0],
19        _tex_coord: [tc[0] as f32, tc[1] as f32],
20    }
21}
22
23fn create_vertices() -> (Vec<Vertex>, Vec<u16>) {
24    let vertex_data = [
25        // top (0, 0, 1)
26        vertex([-1, -1, 1], [0, 0]),
27        vertex([1, -1, 1], [1, 0]),
28        vertex([1, 1, 1], [1, 1]),
29        vertex([-1, 1, 1], [0, 1]),
30        // bottom (0, 0, -1)
31        vertex([-1, 1, -1], [1, 0]),
32        vertex([1, 1, -1], [0, 0]),
33        vertex([1, -1, -1], [0, 1]),
34        vertex([-1, -1, -1], [1, 1]),
35        // right (1, 0, 0)
36        vertex([1, -1, -1], [0, 0]),
37        vertex([1, 1, -1], [1, 0]),
38        vertex([1, 1, 1], [1, 1]),
39        vertex([1, -1, 1], [0, 1]),
40        // left (-1, 0, 0)
41        vertex([-1, -1, 1], [1, 0]),
42        vertex([-1, 1, 1], [0, 0]),
43        vertex([-1, 1, -1], [0, 1]),
44        vertex([-1, -1, -1], [1, 1]),
45        // front (0, 1, 0)
46        vertex([1, 1, -1], [1, 0]),
47        vertex([-1, 1, -1], [0, 0]),
48        vertex([-1, 1, 1], [0, 1]),
49        vertex([1, 1, 1], [1, 1]),
50        // back (0, -1, 0)
51        vertex([1, -1, 1], [0, 0]),
52        vertex([-1, -1, 1], [1, 0]),
53        vertex([-1, -1, -1], [1, 1]),
54        vertex([1, -1, -1], [0, 1]),
55    ];
56
57    let index_data: &[u16] = &[
58        0, 1, 2, 2, 3, 0, // top
59        4, 5, 6, 6, 7, 4, // bottom
60        8, 9, 10, 10, 11, 8, // right
61        12, 13, 14, 14, 15, 12, // left
62        16, 17, 18, 18, 19, 16, // front
63        20, 21, 22, 22, 23, 20, // back
64    ];
65
66    (vertex_data.to_vec(), index_data.to_vec())
67}
68
69#[repr(C)]
70#[derive(Clone, Copy, Pod, Zeroable)]
71struct Uniforms {
72    view_inverse: Mat4,
73    proj_inverse: Mat4,
74}
75
76/// A wrapper for `pop_error_scope` futures that panics if an error occurs.
77///
78/// Given a future `inner` of an `Option<E>` for some error type `E`,
79/// wait for the future to be ready, and panic if its value is `Some`.
80///
81/// This can be done simpler with `FutureExt`, but we don't want to add
82/// a dependency just for this small case.
83struct ErrorFuture<F> {
84    inner: F,
85}
86impl<F: Future<Output = Option<wgpu::Error>>> Future for ErrorFuture<F> {
87    type Output = ();
88    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<()> {
89        let inner = unsafe { self.map_unchecked_mut(|me| &mut me.inner) };
90        inner.poll(cx).map(|error| {
91            if let Some(e) = error {
92                panic!("Rendering {e}");
93            }
94        })
95    }
96}
97
98struct Example {
99    uniforms: Uniforms,
100    uniform_buf: wgpu::Buffer,
101    blas: wgpu::Blas,
102    tlas: wgpu::Tlas,
103    pipeline: wgpu::RenderPipeline,
104    bind_group: wgpu::BindGroup,
105    animation_timer: utils::AnimationTimer,
106}
107
108impl crate::framework::Example for Example {
109    fn required_features() -> wgpu::Features {
110        wgpu::Features::EXPERIMENTAL_RAY_QUERY
111    }
112
113    fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {
114        wgpu::DownlevelCapabilities {
115            flags: wgpu::DownlevelFlags::COMPUTE_SHADERS,
116            ..Default::default()
117        }
118    }
119
120    fn required_limits() -> wgpu::Limits {
121        wgpu::Limits::default().using_minimum_supported_acceleration_structure_values()
122    }
123
124    fn init(
125        config: &wgpu::SurfaceConfiguration,
126        _adapter: &wgpu::Adapter,
127        device: &wgpu::Device,
128        queue: &wgpu::Queue,
129    ) -> Self {
130        let side_count = 8;
131
132        let uniforms = {
133            let view = Mat4::look_at_rh(Vec3::new(0.0, 0.0, 2.5), Vec3::ZERO, Vec3::Y);
134            let proj = Mat4::perspective_rh(
135                59.0_f32.to_radians(),
136                config.width as f32 / config.height as f32,
137                0.001,
138                1000.0,
139            );
140
141            Uniforms {
142                view_inverse: view.inverse(),
143                proj_inverse: proj.inverse(),
144            }
145        };
146
147        let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
148            label: Some("Uniform Buffer"),
149            contents: bytemuck::cast_slice(&[uniforms]),
150            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
151        });
152
153        let (vertex_data, index_data) = create_vertices();
154
155        let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
156            label: Some("Vertex Buffer"),
157            contents: bytemuck::cast_slice(&vertex_data),
158            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT,
159        });
160
161        let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
162            label: Some("Index Buffer"),
163            contents: bytemuck::cast_slice(&index_data),
164            usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::BLAS_INPUT,
165        });
166
167        let blas_geo_size_desc = wgpu::BlasTriangleGeometrySizeDescriptor {
168            vertex_format: wgpu::VertexFormat::Float32x3,
169            vertex_count: vertex_data.len() as u32,
170            index_format: Some(wgpu::IndexFormat::Uint16),
171            index_count: Some(index_data.len() as u32),
172            flags: wgpu::AccelerationStructureGeometryFlags::OPAQUE,
173        };
174
175        let blas = device.create_blas(
176            &wgpu::CreateBlasDescriptor {
177                label: None,
178                flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,
179                update_mode: wgpu::AccelerationStructureUpdateMode::Build,
180            },
181            wgpu::BlasGeometrySizeDescriptors::Triangles {
182                descriptors: vec![blas_geo_size_desc.clone()],
183            },
184        );
185
186        let tlas = device.create_tlas(&wgpu::CreateTlasDescriptor {
187            label: None,
188            flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,
189            update_mode: wgpu::AccelerationStructureUpdateMode::Build,
190            max_instances: side_count * side_count,
191        });
192
193        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
194            label: None,
195            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
196        });
197
198        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
199            label: None,
200            layout: None,
201            vertex: wgpu::VertexState {
202                module: &shader,
203                entry_point: Some("vs_main"),
204                compilation_options: Default::default(),
205                buffers: &[],
206            },
207            fragment: Some(wgpu::FragmentState {
208                module: &shader,
209                entry_point: Some("fs_main"),
210                compilation_options: Default::default(),
211                targets: &[Some(config.format.into())],
212            }),
213            primitive: wgpu::PrimitiveState {
214                topology: wgpu::PrimitiveTopology::TriangleList,
215                ..Default::default()
216            },
217            depth_stencil: None,
218            multisample: wgpu::MultisampleState::default(),
219            multiview: None,
220            cache: None,
221        });
222
223        let bind_group_layout = pipeline.get_bind_group_layout(0);
224
225        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
226            label: None,
227            layout: &bind_group_layout,
228            entries: &[
229                wgpu::BindGroupEntry {
230                    binding: 0,
231                    resource: uniform_buf.as_entire_binding(),
232                },
233                wgpu::BindGroupEntry {
234                    binding: 1,
235                    resource: wgpu::BindingResource::AccelerationStructure(&tlas),
236                },
237            ],
238        });
239
240        let mut encoder =
241            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
242
243        encoder.build_acceleration_structures(
244            iter::once(&wgpu::BlasBuildEntry {
245                blas: &blas,
246                geometry: wgpu::BlasGeometries::TriangleGeometries(vec![
247                    wgpu::BlasTriangleGeometry {
248                        size: &blas_geo_size_desc,
249                        vertex_buffer: &vertex_buf,
250                        first_vertex: 0,
251                        vertex_stride: mem::size_of::<Vertex>() as u64,
252                        index_buffer: Some(&index_buf),
253                        first_index: Some(0),
254                        transform_buffer: None,
255                        transform_buffer_offset: None,
256                    },
257                ]),
258            }),
259            // iter::empty(),
260            iter::once(&tlas),
261        );
262
263        queue.submit(Some(encoder.finish()));
264
265        Example {
266            uniforms,
267            uniform_buf,
268            blas,
269            tlas,
270            pipeline,
271            bind_group,
272            animation_timer: utils::AnimationTimer::default(),
273        }
274    }
275
276    fn update(&mut self, _event: winit::event::WindowEvent) {}
277
278    fn resize(
279        &mut self,
280        config: &wgpu::SurfaceConfiguration,
281        _device: &wgpu::Device,
282        queue: &wgpu::Queue,
283    ) {
284        let proj = Mat4::perspective_rh(
285            59.0_f32.to_radians(),
286            config.width as f32 / config.height as f32,
287            0.001,
288            1000.0,
289        );
290
291        self.uniforms.proj_inverse = proj.inverse();
292
293        queue.write_buffer(&self.uniform_buf, 0, bytemuck::cast_slice(&[self.uniforms]));
294    }
295
296    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
297        device.push_error_scope(wgpu::ErrorFilter::Validation);
298
299        // scene update
300        {
301            let dist = 12.0;
302
303            let side_count = 8;
304
305            let anim_time = self.animation_timer.time();
306
307            for x in 0..side_count {
308                for y in 0..side_count {
309                    let instance = self.tlas.index_mut((x + y * side_count) as usize);
310
311                    let x = x as f32 / (side_count - 1) as f32;
312                    let y = y as f32 / (side_count - 1) as f32;
313                    let x = x * 2.0 - 1.0;
314                    let y = y * 2.0 - 1.0;
315
316                    let transform = Mat4::from_rotation_translation(
317                        Quat::from_euler(
318                            glam::EulerRot::XYZ,
319                            anim_time * 0.5 * 0.342,
320                            anim_time * 0.5 * 0.254,
321                            anim_time * 0.5 * 0.832,
322                        ),
323                        Vec3 {
324                            x: x * dist,
325                            y: y * dist,
326                            z: -24.0,
327                        },
328                    );
329                    let transform = transform.transpose().to_cols_array()[..12]
330                        .try_into()
331                        .unwrap();
332
333                    *instance = Some(wgpu::TlasInstance::new(&self.blas, transform, 0, 0xff));
334                }
335            }
336        }
337
338        let mut encoder =
339            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
340
341        encoder.build_acceleration_structures(iter::empty(), iter::once(&self.tlas));
342
343        {
344            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
345                label: None,
346                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
347                    view,
348                    depth_slice: None,
349                    resolve_target: None,
350                    ops: wgpu::Operations {
351                        load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
352                        store: wgpu::StoreOp::Store,
353                    },
354                })],
355                depth_stencil_attachment: None,
356                timestamp_writes: None,
357                occlusion_query_set: None,
358            });
359
360            rpass.set_pipeline(&self.pipeline);
361            rpass.set_bind_group(0, Some(&self.bind_group), &[]);
362            rpass.draw(0..3, 0..1);
363        }
364
365        queue.submit(Some(encoder.finish()));
366    }
367}
368
369pub fn main() {
370    crate::framework::run::<Example>("ray-cube");
371}
372
373#[cfg(test)]
374#[wgpu_test::gpu_test]
375static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {
376    name: "ray_cube_fragment",
377    image_path: "/examples/features/src/ray_cube_fragment/screenshot.png",
378    width: 1024,
379    height: 768,
380    optional_features: wgpu::Features::default(),
381    base_test_parameters: wgpu_test::TestParameters::default(),
382    comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],
383    _phantom: std::marker::PhantomData::<Example>,
384};