gloss_renderer/forward_renderer/render_passes/
shadow_pass.rs1use crate::{
2 components::{FacesGPU, LightEmit, ModelMatrix, Name, PosLookat, Renderable, ShadowCaster, ShadowMap, ShadowMapDirty, VertsGPU, VisMesh},
3 scene::Scene,
4};
5use easy_wgpu::{
6 bind_group::{BindGroupBuilder, BindGroupDesc, BindGroupWrapper},
7 bind_group_layout::{BindGroupLayoutBuilder, BindGroupLayoutDesc},
8 buffer::Buffer,
9 gpu::Gpu,
10 pipeline::RenderPipelineDescBuilder,
11};
12use gloss_hecs::{Changed, CommandBuffer, Entity, Query, QueryBorrow};
13use gloss_utils::numerical::{align, align_usz};
14use log::debug;
15use std::collections::HashMap;
16
17pub struct ShadowPass {
20 render_pipeline: wgpu::RenderPipeline,
21 locals_uniform: LocalsUniform, iterator_light_uniform: IteratorLightUniform, command_buffer: CommandBuffer,
24}
25use super::upload_pass::PerFrameUniforms;
26
27#[include_wgsl_oil::include_wgsl_oil("../../../shaders/shadow_map.wgsl")]
29mod shader_code {}
30
31impl ShadowPass {
32 pub fn new(gpu: &Gpu) -> Self {
33 let render_pipeline = RenderPipelineDescBuilder::new()
35 .label("shadow pass pipeline")
36 .shader_code(shader_code::SOURCE)
37 .add_bind_group_layout_desc(PerFrameUniforms::build_layout_desc())
38 .add_bind_group_layout_desc(IteratorLightUniform::layout_desc())
39 .add_bind_group_layout_desc(LocalsUniform::layout_desc())
40 .add_vertex_buffer_layout(VertsGPU::vertex_buffer_layout::<0>())
41 .depth_state(Some(wgpu::DepthStencilState {
42 format: wgpu::TextureFormat::Depth32Float,
43 depth_write_enabled: true,
44 depth_compare: wgpu::CompareFunction::Greater,
45 stencil: wgpu::StencilState::default(),
46 bias: wgpu::DepthBiasState::default(),
47 }))
48 .multisample(wgpu::MultisampleState::default())
49 .build_pipeline(gpu.device());
50
51 let locals_uniform = LocalsUniform::new(gpu);
52
53 let iterator_light_uniform = IteratorLightUniform::new(gpu);
54
55 let command_buffer = CommandBuffer::new();
56
57 Self {
58 render_pipeline,
59 locals_uniform,
60 iterator_light_uniform,
61 command_buffer,
62 }
63 }
64
65 pub fn run(&mut self, gpu: &Gpu, per_frame_uniforms: &PerFrameUniforms, scene: &mut Scene) {
66 self.begin_pass();
67
68 self.render_shadows(gpu, per_frame_uniforms, scene);
69
70 self.end_pass(scene);
71 }
72
73 fn render_shadows(&mut self, gpu: &Gpu, per_frame_uniforms: &PerFrameUniforms, scene: &Scene) {
74 let shadow_map_requires_update = self.check_shadow_maps_dirty(scene);
75 debug!("shadow_map_requires_update {shadow_map_requires_update}");
76 if !shadow_map_requires_update {
77 return; }
79
80 let mut query_all_renderables = scene.world.query::<&Renderable>();
81 let mut query_meshes_for_shadow = scene.world.query::<(&VertsGPU, &FacesGPU, &VisMesh)>().with::<&Renderable>();
82
83 self.update_locals(gpu, &mut query_all_renderables, scene);
85
86 for (entity_light, shadow_map) in &mut scene.world.query::<&ShadowMap>().with::<(&LightEmit, &ShadowCaster)>() {
89 let mut encoder = gpu.device().create_command_encoder(&wgpu::CommandEncoderDescriptor {
91 label: Some("Shadow pass encoder"),
92 });
93 {
94 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
95 label: Some("Shadow Pass"),
96 color_attachments: &[
97 ],
107 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
109 view: &shadow_map.tex_depth.view,
110 depth_ops: Some(wgpu::Operations {
111 load: wgpu::LoadOp::Clear(0.0),
112 store: wgpu::StoreOp::Store,
113 }),
114 stencil_ops: None,
115 }),
116 timestamp_writes: None,
117 occlusion_query_set: None,
118 });
119 render_pass.set_pipeline(&self.render_pipeline);
120
121 render_pass.set_bind_group(0, &per_frame_uniforms.bind_group, &[]);
123
124 let light_name = scene.get_comp::<&Name>(&entity_light).unwrap().0.clone();
127 let light_idx = per_frame_uniforms.light2idx_ubo[&light_name];
128 self.iterator_light_uniform.set(gpu, light_idx); render_pass.set_bind_group(1, self.iterator_light_uniform.bind_group.bg(), &[]);
130
131 for (entity_mesh, (verts, faces, vis)) in query_meshes_for_shadow.iter() {
132 if !vis.show_mesh {
133 continue;
134 }
135
136 let name = scene.get_comp::<&Name>(&entity_mesh).unwrap().0.clone();
138 let (local_bg, offset) = &self.locals_uniform.mesh2local_bind[&name];
139 render_pass.set_bind_group(2, local_bg.bg(), &[*offset]);
140 render_pass.set_vertex_buffer(0, verts.buf.slice(..));
143 render_pass.set_index_buffer(faces.buf.slice(..), wgpu::IndexFormat::Uint32);
144 render_pass.draw_indexed(0..faces.nr_triangles * 3, 0, 0..1);
145 }
146
147 }
149 debug!("shadow encoder");
150 gpu.queue().submit(Some(encoder.finish()));
151 }
152 }
153
154 fn begin_pass(&mut self) {
155 self.locals_uniform.buffer.reset_chunks_offset_if_necessary();
157 }
158
159 fn end_pass(&mut self, scene: &mut Scene) {
160 {
162 let mut query_all_renderables = scene.world.query::<&Renderable>();
163 for (id, _comps) in query_all_renderables.iter() {
164 self.command_buffer.remove_one::<ShadowMapDirty>(id);
165 }
166 }
167
168 self.command_buffer.run_on(&mut scene.world);
169 }
170
171 fn check_shadow_maps_dirty(&self, scene: &Scene) -> bool {
172 let mut query_for_shadow = scene
173 .world
174 .query::<(Option<&ShadowMapDirty>, Changed<VertsGPU>, Changed<ModelMatrix>)>()
175 .with::<&Renderable>();
176
177 let mut shadow_map_requires_update = false;
181 for (_entity_mesh, (shadow_map_opt, changed_verts, changed_model_matrix)) in query_for_shadow.iter() {
183 shadow_map_requires_update = shadow_map_requires_update | changed_verts | changed_model_matrix | shadow_map_opt.is_some();
184 }
185
186 let mut query_for_lights = scene.world.query::<Changed<ShadowCaster>>();
189 for (_ent, changed_shadow_casting) in query_for_lights.iter() {
190 shadow_map_requires_update |= changed_shadow_casting;
191 }
192
193 let mut query_for_renderable = scene.world.query::<Changed<Renderable>>();
196 for (_entity_mesh, changed_renderable) in query_for_renderable.iter() {
197 shadow_map_requires_update |= changed_renderable;
198 }
199
200 if !scene.world.removed::<Renderable>().is_empty() {
203 shadow_map_requires_update = true;
204 }
205
206 let mut query_for_lights = scene.world.query::<(Changed<PosLookat>,)>().with::<&LightEmit>();
208 for (_entity_mesh, (changed_poslookat,)) in query_for_lights.iter() {
209 shadow_map_requires_update |= changed_poslookat;
210 }
211
212 shadow_map_requires_update
213 }
214
215 fn update_locals<Q: Query>(&mut self, gpu: &Gpu, query_state: &mut QueryBorrow<'_, Q>, scene: &Scene) {
216 for (id, _comps) in query_state.iter() {
220 let name = scene.get_comp::<&Name>(&id).unwrap().0.clone();
221
222 let locals = Locals::new(id, scene);
224 let offset_in_ubo = self.locals_uniform.buffer.push_cpu_chunk_aligned::<Locals>(&locals);
225
226 self.locals_uniform.update_bind_group(id, gpu, &name, offset_in_ubo, scene);
229 }
230 self.locals_uniform.buffer.upload_from_cpu_chunks(gpu.queue()); }
232}
233
234#[repr(C)]
236#[derive(Clone, Copy, encase::ShaderType)]
237struct Locals {
238 model_matrix: nalgebra::Matrix4<f32>,
239}
240impl Locals {
241 pub fn new(entity: Entity, scene: &Scene) -> Self {
242 let model_matrix = scene.get_comp::<&ModelMatrix>(&entity).unwrap().0.to_homogeneous();
243 Locals { model_matrix }
244 }
245}
246
247struct LocalsUniform {
248 pub buffer: Buffer,
249 layout: wgpu::BindGroupLayout,
250 pub mesh2local_bind: HashMap<String, (BindGroupWrapper, u32)>,
251}
252impl LocalsUniform {
253 pub fn new(gpu: &Gpu) -> Self {
254 let size_bytes = 0x10000;
255 let usage = wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM;
256 let buffer = Buffer::new_empty(gpu.device(), usage, Some("local_buffer"), size_bytes);
257
258 let layout = Self::layout_desc().into_bind_group_layout(gpu.device());
259
260 Self {
261 buffer,
262 layout,
263 mesh2local_bind: HashMap::default(),
264 }
265 }
266
267 pub fn layout_desc() -> BindGroupLayoutDesc {
270 BindGroupLayoutBuilder::new()
271 .label("shadow_pass_locals_layout")
272 .add_entry_uniform(
274 wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
275 true,
276 wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<Locals>()).unwrap(), 256))),
277 )
278 .build()
279 }
280
281 pub fn update_bind_group(
282 &mut self,
283 _entity: Entity,
284 gpu: &Gpu,
285 mesh_name: &str,
287 offset_in_ubo: u32,
288 _scene: &Scene,
289 ) {
290 let entries = BindGroupBuilder::new().add_entry_buf_chunk::<Locals>(&self.buffer.buffer).build_entries();
291
292 if !self.mesh2local_bind.contains_key(mesh_name) || self.mesh2local_bind[mesh_name].0.is_stale(&entries) {
295 debug!("shadowmap_local_bg_recreating");
296 let bg = BindGroupDesc::new("shadow_local_bg", entries).into_bind_group_wrapper(gpu.device(), &self.layout);
297 let bg_and_offset = (bg, offset_in_ubo);
298 self.mesh2local_bind.insert(mesh_name.to_string(), bg_and_offset);
299 }
300
301 self.mesh2local_bind.entry(mesh_name.to_string()).and_modify(|r| r.1 = offset_in_ubo);
304 }
305}
306
307#[repr(C)]
309#[derive(Clone, Copy, encase::ShaderType)]
310struct IteratorLight {
311 light_idx: u32,
312 pad_0: u32,
314 pad_1: u32,
315 pad_2: u32,
316}
317impl IteratorLight {
318 pub fn new(light_idx: u32) -> Self {
319 IteratorLight {
320 light_idx,
321 pad_0: 0,
322 pad_1: 0,
323 pad_2: 0,
324 }
325 }
326}
327
328struct IteratorLightUniform {
329 pub buffer: Buffer,
330 pub bind_group: BindGroupWrapper,
331}
332impl IteratorLightUniform {
333 pub fn new(gpu: &Gpu) -> Self {
334 let size_bytes = align_usz(std::mem::size_of::<IteratorLight>(), 256);
335 let usage = wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM;
336 let buffer = Buffer::new_empty(gpu.device(), usage, Some("iterator_light_buffer"), size_bytes);
337
338 let layout = Self::layout_desc().into_bind_group_layout(gpu.device());
339
340 let bind_group = BindGroupBuilder::new().add_entry_buf(&buffer.buffer).build(gpu.device(), &layout);
343
344 Self {
345 buffer,
346 bind_group,
348 }
349 }
350
351 pub fn set(&mut self, gpu: &Gpu, idx: u32) {
352 let iterator = IteratorLight::new(idx);
353 self.buffer.reset_chunks_offset();
354 self.buffer.push_cpu_chunk_packed(&iterator);
355 self.buffer.upload_from_cpu_chunks(gpu.queue());
356 }
357
358 pub fn layout_desc() -> BindGroupLayoutDesc {
361 BindGroupLayoutBuilder::new()
362 .label("shadow_pass_iterator_layout")
363 .add_entry_uniform(
365 wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
366 false,
367 wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<IteratorLight>()).unwrap(), 256))),
368 )
369 .build()
370 }
371}