gloss_renderer/forward_renderer/render_passes/
shadow_pass.rs

1extern crate nalgebra as na;
2
3use crate::{
4    components::{FacesGPU, LightEmit, ModelMatrix, Name, PosLookat, Renderable, ShadowCaster, ShadowMap, ShadowMapDirty, VertsGPU, VisMesh},
5    scene::Scene,
6};
7use easy_wgpu::{
8    bind_group::{BindGroupBuilder, BindGroupDesc, BindGroupWrapper},
9    bind_group_layout::{BindGroupLayoutBuilder, BindGroupLayoutDesc},
10    buffer::Buffer,
11    gpu::Gpu,
12    pipeline::RenderPipelineDescBuilder,
13};
14use gloss_hecs::{Changed, CommandBuffer, Entity, Query, QueryBorrow};
15use gloss_utils::numerical::{align, align_usz};
16use log::debug;
17use std::collections::HashMap;
18
19/// Shadow pass which renders depth maps for each of the lights. Only updates
20/// the depth map when the vertices on the gpu have changed.
21pub struct ShadowPass {
22    render_pipeline: wgpu::RenderPipeline,
23    locals_uniform: LocalsUniform,                // a uniform buffer that we suballocate for the locals of every mesh
24    iterator_light_uniform: IteratorLightUniform, //a uniform buffer that we allocate only an index to use for indexing into our locals
25    command_buffer: CommandBuffer,
26}
27use super::upload_pass::PerFrameUniforms;
28
29//shaders
30#[include_wgsl_oil::include_wgsl_oil("../../../shaders/shadow_map.wgsl")]
31mod shader_code {}
32
33impl ShadowPass {
34    pub fn new(gpu: &Gpu) -> Self {
35        //render pipeline
36        let render_pipeline = RenderPipelineDescBuilder::new()
37            .label("shadow pass pipeline")
38            .shader_code(shader_code::SOURCE)
39            .add_bind_group_layout_desc(PerFrameUniforms::build_layout_desc())
40            .add_bind_group_layout_desc(IteratorLightUniform::layout_desc())
41            .add_bind_group_layout_desc(LocalsUniform::layout_desc())
42            .add_vertex_buffer_layout(VertsGPU::vertex_buffer_layout::<0>())
43            .depth_state(Some(wgpu::DepthStencilState {
44                format: wgpu::TextureFormat::Depth32Float,
45                depth_write_enabled: true,
46                depth_compare: wgpu::CompareFunction::Greater,
47                stencil: wgpu::StencilState::default(),
48                bias: wgpu::DepthBiasState::default(),
49            }))
50            .multisample(wgpu::MultisampleState::default())
51            .build_pipeline(gpu.device());
52
53        let locals_uniform = LocalsUniform::new(gpu);
54
55        let iterator_light_uniform = IteratorLightUniform::new(gpu);
56
57        let command_buffer = CommandBuffer::new();
58
59        Self {
60            render_pipeline,
61            locals_uniform,
62            iterator_light_uniform,
63            command_buffer,
64        }
65    }
66
67    pub fn run(&mut self, gpu: &Gpu, per_frame_uniforms: &PerFrameUniforms, scene: &mut Scene) {
68        self.begin_pass();
69
70        self.render_shadows(gpu, per_frame_uniforms, scene);
71
72        self.end_pass(scene);
73    }
74
75    fn render_shadows(&mut self, gpu: &Gpu, per_frame_uniforms: &PerFrameUniforms, scene: &Scene) {
76        let shadow_map_requires_update = self.check_shadow_maps_dirty(scene);
77        debug!("shadow_map_requires_update {}", shadow_map_requires_update);
78        if !shadow_map_requires_update {
79            return; //nothing to do
80        }
81
82        let mut query_all_renderables = scene.world.query::<&Renderable>();
83        let mut query_meshes_for_shadow = scene.world.query::<(&VertsGPU, &FacesGPU, &VisMesh)>().with::<&Renderable>();
84
85        //upload to gpu the local information for each mesh like model matrix
86        self.update_locals(gpu, &mut query_all_renderables, scene);
87
88        //check for every light if it needs update and if it does, render all the
89        // meshes towards it's shadowmap
90        for (entity_light, shadow_map) in &mut scene.world.query::<&ShadowMap>().with::<(&LightEmit, &ShadowCaster)>() {
91            //do the actual rendering now
92            let mut encoder = gpu.device().create_command_encoder(&wgpu::CommandEncoderDescriptor {
93                label: Some("Shadow pass encoder"),
94            });
95            {
96                let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
97                    label: Some("Shadow Pass"),
98                    color_attachments: &[
99                        //depth moments
100                        // Some(wgpu::RenderPassColorAttachment {
101                        //     view: &shadow_map.tex_depth_moments.view,
102                        //     resolve_target: None,
103                        //     ops: wgpu::Operations {
104                        //         load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
105                        //         store: true,
106                        //     },
107                        // }),
108                    ],
109                    // depth_stencil_attachment: None,
110                    depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
111                        view: &shadow_map.tex_depth.view,
112                        depth_ops: Some(wgpu::Operations {
113                            load: wgpu::LoadOp::Clear(0.0),
114                            store: wgpu::StoreOp::Store,
115                        }),
116                        stencil_ops: None,
117                    }),
118                    timestamp_writes: None,
119                    occlusion_query_set: None,
120                });
121                render_pass.set_pipeline(&self.render_pipeline);
122
123                //gloal binding
124                render_pass.set_bind_group(0, &per_frame_uniforms.bind_group, &[]);
125
126                //iterator the light, we need to pass this idx into a buffer so we can access
127                // it in a shader
128                let light_name = scene.get_comp::<&Name>(&entity_light).unwrap().0.clone();
129                let light_idx = per_frame_uniforms.light2idx_ubo[&light_name];
130                self.iterator_light_uniform.set(gpu, light_idx); //writes to gpu
131                render_pass.set_bind_group(1, self.iterator_light_uniform.bind_group.bg(), &[]);
132
133                for (entity_mesh, (verts, faces, vis)) in query_meshes_for_shadow.iter() {
134                    if !vis.show_mesh {
135                        continue;
136                    }
137
138                    //local bindings
139                    let name = scene.get_comp::<&Name>(&entity_mesh).unwrap().0.clone();
140                    let (local_bg, offset) = &self.locals_uniform.mesh2local_bind[&name];
141                    render_pass.set_bind_group(2, local_bg.bg(), &[*offset]);
142                    // println!("Rendering mesh to shadow map {}", name);
143
144                    render_pass.set_vertex_buffer(0, verts.buf.slice(..));
145                    render_pass.set_index_buffer(faces.buf.slice(..), wgpu::IndexFormat::Uint32);
146                    render_pass.draw_indexed(0..faces.nr_triangles * 3, 0, 0..1);
147                }
148
149                //TODO render points
150            }
151            debug!("shadow encoder");
152            gpu.queue().submit(Some(encoder.finish()));
153        }
154    }
155
156    fn begin_pass(&mut self) {
157        // self.locals_uniform.buffer.reset_chunks_offset();
158        self.locals_uniform.buffer.reset_chunks_offset_if_necessary();
159    }
160
161    fn end_pass(&mut self, scene: &mut Scene) {
162        //remove all dirty components from entities
163        {
164            let mut query_all_renderables = scene.world.query::<&Renderable>();
165            for (id, _comps) in query_all_renderables.iter() {
166                self.command_buffer.remove_one::<ShadowMapDirty>(id);
167            }
168        }
169
170        self.command_buffer.run_on(&mut scene.world);
171    }
172
173    fn check_shadow_maps_dirty(&self, scene: &Scene) -> bool {
174        let mut query_for_shadow = scene
175            .world
176            .query::<(Option<&ShadowMapDirty>, Changed<VertsGPU>, Changed<ModelMatrix>)>()
177            .with::<&Renderable>();
178
179        //check if this light has had any changes in the scene and therefore we might
180        // need to update the shadow map. we do this check first because if the
181        // scene has not changed we don't even need to start a encoder or a render pass
182        let mut shadow_map_requires_update = false;
183        //meshes
184        for (_entity_mesh, (shadow_map_opt, changed_verts, changed_model_matrix)) in query_for_shadow.iter() {
185            shadow_map_requires_update = shadow_map_requires_update | changed_verts | changed_model_matrix | shadow_map_opt.is_some();
186        }
187
188        //check if the light shadow casting component has changed, like it was disabled
189        // or the resolution has changed
190        let mut query_for_lights = scene.world.query::<Changed<ShadowCaster>>();
191        for (_ent, changed_shadow_casting) in query_for_lights.iter() {
192            shadow_map_requires_update |= changed_shadow_casting;
193        }
194
195        //check if the renderable component has been added or changed in which case we
196        // need to update the shadows
197        let mut query_for_renderable = scene.world.query::<Changed<Renderable>>();
198        for (_entity_mesh, changed_renderable) in query_for_renderable.iter() {
199            shadow_map_requires_update |= changed_renderable;
200        }
201
202        //if ANY entity has their renderable component removed, then we also update
203        // shadows
204        if !scene.world.removed::<Renderable>().is_empty() {
205            shadow_map_requires_update = true;
206        }
207
208        //if any light has moves we also update shadows
209        let mut query_for_lights = scene.world.query::<(Changed<PosLookat>,)>().with::<&LightEmit>();
210        for (_entity_mesh, (changed_poslookat,)) in query_for_lights.iter() {
211            shadow_map_requires_update |= changed_poslookat;
212        }
213
214        shadow_map_requires_update
215    }
216
217    fn update_locals<Q: Query>(&mut self, gpu: &Gpu, query_state: &mut QueryBorrow<'_, Q>, scene: &Scene) {
218        // Update the local binding groups for the meshes we render. We do it in two
219        // passes because the binding group cannot be created and consumed in the same
220        // loop
221        for (id, _comps) in query_state.iter() {
222            let name = scene.get_comp::<&Name>(&id).unwrap().0.clone();
223
224            //upload local stuff
225            let locals = Locals::new(id, scene);
226            let offset_in_ubo = self.locals_uniform.buffer.push_cpu_chunk_aligned::<Locals>(&locals);
227
228            //chekc if we need to recreate bind group (for example when any of the textures
229            // of the meshes have changed)
230            self.locals_uniform.update_bind_group(id, gpu, &name, offset_in_ubo, scene);
231        }
232        self.locals_uniform.buffer.upload_from_cpu_chunks(gpu.queue()); //important to upload everything to gpu at the end
233    }
234}
235
236/// Keep in sync with shader
237#[repr(C)]
238#[derive(Clone, Copy, encase::ShaderType)]
239struct Locals {
240    model_matrix: nalgebra::Matrix4<f32>,
241}
242impl Locals {
243    pub fn new(entity: Entity, scene: &Scene) -> Self {
244        let model_matrix = scene.get_comp::<&ModelMatrix>(&entity).unwrap().0.to_homogeneous();
245        Locals { model_matrix }
246    }
247}
248
249struct LocalsUniform {
250    pub buffer: Buffer,
251    layout: wgpu::BindGroupLayout,
252    pub mesh2local_bind: HashMap<String, (BindGroupWrapper, u32)>,
253}
254impl LocalsUniform {
255    pub fn new(gpu: &Gpu) -> Self {
256        let size_bytes = 0x10000;
257        let usage = wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM;
258        let buffer = Buffer::new_empty(gpu.device(), usage, Some("local_buffer"), size_bytes);
259
260        let layout = Self::layout_desc().into_bind_group_layout(gpu.device());
261
262        Self {
263            buffer,
264            layout,
265            mesh2local_bind: HashMap::default(),
266        }
267    }
268
269    //keep as associated function so we can call it in the pipeline creation
270    // without and object
271    pub fn layout_desc() -> BindGroupLayoutDesc {
272        BindGroupLayoutBuilder::new()
273            .label("shadow_pass_locals_layout")
274            //Locals
275            .add_entry_uniform(
276                wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
277                true,
278                wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<Locals>()).unwrap(), 256))),
279            )
280            .build()
281    }
282
283    pub fn update_bind_group(
284        &mut self,
285        _entity: Entity,
286        gpu: &Gpu,
287        // offset: &u32,
288        mesh_name: &str,
289        offset_in_ubo: u32,
290        _scene: &Scene,
291    ) {
292        let entries = BindGroupBuilder::new().add_entry_buf_chunk::<Locals>(&self.buffer.buffer).build_entries();
293
294        //if there is no entry for the bind group or if the current one is stale, we
295        // recreate it
296        if !self.mesh2local_bind.contains_key(mesh_name) || self.mesh2local_bind[mesh_name].0.is_stale(&entries) {
297            debug!("shadowmap_local_bg_recreating");
298            let bg = BindGroupDesc::new("shadow_local_bg", entries).into_bind_group_wrapper(gpu.device(), &self.layout);
299            let bg_and_offset = (bg, offset_in_ubo);
300            self.mesh2local_bind.insert(mesh_name.to_string(), bg_and_offset);
301        }
302
303        //sometimes just the offset of the bind group changes so we also make sure to
304        // update this.
305        self.mesh2local_bind.entry(mesh_name.to_string()).and_modify(|r| r.1 = offset_in_ubo);
306    }
307}
308
309/// Keep in sync with shader
310#[repr(C)]
311#[derive(Clone, Copy, encase::ShaderType)]
312struct IteratorLight {
313    light_idx: u32,
314    //wasm needs padding to 16 bytes https://github.com/gfx-rs/wgpu/issues/2932
315    pad_0: u32,
316    pad_1: u32,
317    pad_2: u32,
318}
319impl IteratorLight {
320    pub fn new(light_idx: u32) -> Self {
321        IteratorLight {
322            light_idx,
323            pad_0: 0,
324            pad_1: 0,
325            pad_2: 0,
326        }
327    }
328}
329
330struct IteratorLightUniform {
331    pub buffer: Buffer,
332    pub bind_group: BindGroupWrapper,
333}
334impl IteratorLightUniform {
335    pub fn new(gpu: &Gpu) -> Self {
336        let size_bytes = align_usz(std::mem::size_of::<IteratorLight>(), 256);
337        let usage = wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM;
338        let buffer = Buffer::new_empty(gpu.device(), usage, Some("iterator_light_buffer"), size_bytes);
339
340        let layout = Self::layout_desc().into_bind_group_layout(gpu.device());
341
342        //can build it only once and leave it like this because we won't reallocate the
343        // buffer
344        let bind_group = BindGroupBuilder::new().add_entry_buf(&buffer.buffer).build(gpu.device(), &layout);
345
346        Self {
347            buffer,
348            // layout,
349            bind_group,
350        }
351    }
352
353    pub fn set(&mut self, gpu: &Gpu, idx: u32) {
354        let iterator = IteratorLight::new(idx);
355        self.buffer.reset_chunks_offset();
356        self.buffer.push_cpu_chunk_packed(&iterator);
357        self.buffer.upload_from_cpu_chunks(gpu.queue());
358    }
359
360    //keep as associated function so we can call it in the pipeline creation
361    // without and object
362    pub fn layout_desc() -> BindGroupLayoutDesc {
363        BindGroupLayoutBuilder::new()
364            .label("shadow_pass_iterator_layout")
365            //InteratorLight
366            .add_entry_uniform(
367                wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
368                false,
369                wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<IteratorLight>()).unwrap(), 256))),
370            )
371            .build()
372    }
373}