gloss_renderer/forward_renderer/render_passes/
shadow_pass.rs1extern 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
19pub struct ShadowPass {
22 render_pipeline: wgpu::RenderPipeline,
23 locals_uniform: LocalsUniform, iterator_light_uniform: IteratorLightUniform, command_buffer: CommandBuffer,
26}
27use super::upload_pass::PerFrameUniforms;
28
29#[include_wgsl_oil::include_wgsl_oil("../../../shaders/shadow_map.wgsl")]
31mod shader_code {}
32
33impl ShadowPass {
34 pub fn new(gpu: &Gpu) -> Self {
35 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; }
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 self.update_locals(gpu, &mut query_all_renderables, scene);
87
88 for (entity_light, shadow_map) in &mut scene.world.query::<&ShadowMap>().with::<(&LightEmit, &ShadowCaster)>() {
91 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 ],
109 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 render_pass.set_bind_group(0, &per_frame_uniforms.bind_group, &[]);
125
126 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); 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 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 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 }
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_if_necessary();
159 }
160
161 fn end_pass(&mut self, scene: &mut Scene) {
162 {
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 let mut shadow_map_requires_update = false;
183 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 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 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 !scene.world.removed::<Renderable>().is_empty() {
205 shadow_map_requires_update = true;
206 }
207
208 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 for (id, _comps) in query_state.iter() {
222 let name = scene.get_comp::<&Name>(&id).unwrap().0.clone();
223
224 let locals = Locals::new(id, scene);
226 let offset_in_ubo = self.locals_uniform.buffer.push_cpu_chunk_aligned::<Locals>(&locals);
227
228 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()); }
234}
235
236#[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 pub fn layout_desc() -> BindGroupLayoutDesc {
272 BindGroupLayoutBuilder::new()
273 .label("shadow_pass_locals_layout")
274 .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 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 !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 self.mesh2local_bind.entry(mesh_name.to_string()).and_modify(|r| r.1 = offset_in_ubo);
306 }
307}
308
309#[repr(C)]
311#[derive(Clone, Copy, encase::ShaderType)]
312struct IteratorLight {
313 light_idx: u32,
314 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 let bind_group = BindGroupBuilder::new().add_entry_buf(&buffer.buffer).build(gpu.device(), &layout);
345
346 Self {
347 buffer,
348 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 pub fn layout_desc() -> BindGroupLayoutDesc {
363 BindGroupLayoutBuilder::new()
364 .label("shadow_pass_iterator_layout")
365 .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}