1use std::collections::HashMap;
2
3use crate::{
4 components::{
5 ColorsGPU, DiffuseTex, EnvironmentMapGpu, FacesGPU, ModelMatrix, Name, NormalTex, NormalsGPU, Renderable, RoughnessTex, ShadowCaster,
6 ShadowMap, TangentsGPU, UVsGPU, VertsGPU, VisMesh,
7 },
8 config::RenderConfig,
9 forward_renderer::{bind_group_collection::BindGroupCollection, locals::LocalEntData},
10 light::Light,
11 scene::Scene,
12 selector::Selector,
13};
14use easy_wgpu::{
15 bind_group::{BindGroupBuilder, BindGroupDesc, BindGroupWrapper},
16 bind_group_layout::{BindGroupLayoutBuilder, BindGroupLayoutDesc},
17 buffer::Buffer,
18};
19use easy_wgpu::{
22 gpu::Gpu,
23 texture::{TexParams, Texture},
24 utils::create_empty_group,
25};
26use gloss_hecs::Entity;
27use log::debug;
28
29use super::{pipeline_runner::PipelineRunner, upload_pass::PerFrameUniforms};
30
31use easy_wgpu::pipeline::RenderPipelineDescBuilder;
32
33use super::upload_pass::MAX_NUM_SHADOWS;
34use encase;
35
36use gloss_utils::numerical::align;
37
38#[include_wgsl_oil::include_wgsl_oil("../../../shaders/gbuffer_mesh_vert.wgsl")]
40mod vert_shader_code {}
41#[allow(clippy::approx_constant)]
42#[include_wgsl_oil::include_wgsl_oil("../../../shaders/gbuffer_mesh_frag.wgsl")]
43mod frag_shader_code {}
44
45pub struct MeshPipeline {
47 render_pipeline: wgpu::RenderPipeline,
48 _empty_group: wgpu::BindGroup,
49 locals_uniform: Buffer, locals_bind_groups: LocalsBindGroups,
51 input_layout: wgpu::BindGroupLayout,
54 input_bind_group: Option<BindGroupWrapper>,
55 local_scene: Scene,
59 passthrough_light: Light,
63}
64
65impl MeshPipeline {
66 pub fn new(gpu: &Gpu, params: &RenderConfig, color_target_format: wgpu::TextureFormat, depth_target_format: wgpu::TextureFormat) -> Self {
70 const_assert!(std::mem::size_of::<Locals>() % 16 == 0);
72
73 let input_layout_desc = Self::input_layout_desc();
74 let input_layout = input_layout_desc.clone().into_bind_group_layout(gpu.device());
75
76 let render_pipeline = RenderPipelineDescBuilder::new()
78 .label("mesh_pipeline")
79 .shader_code_vert(vert_shader_code::SOURCE)
81 .shader_code_frag(frag_shader_code::SOURCE)
82 .shader_label("mesh_shader")
83 .add_bind_group_layout_desc(PerFrameUniforms::build_layout_desc())
84 .add_bind_group_layout_desc(input_layout_desc)
85 .add_bind_group_layout_desc(LocalsBindGroups::build_layout_desc())
86 .add_vertex_buffer_layout(VertsGPU::vertex_buffer_layout::<0>())
87 .add_vertex_buffer_layout(UVsGPU::vertex_buffer_layout::<1>())
88 .add_vertex_buffer_layout(NormalsGPU::vertex_buffer_layout::<2>())
89 .add_vertex_buffer_layout(TangentsGPU::vertex_buffer_layout::<3>())
90 .add_vertex_buffer_layout(ColorsGPU::vertex_buffer_layout::<4>())
91 .add_render_target(wgpu::ColorTargetState {
92 format: color_target_format,
93 blend: None,
94 write_mask: wgpu::ColorWrites::ALL,
95 })
96 .depth_state(Some(wgpu::DepthStencilState {
97 format: depth_target_format,
98 depth_write_enabled: true,
99 depth_compare: wgpu::CompareFunction::Greater,
100 stencil: wgpu::StencilState {
101 front: wgpu::StencilFaceState {
102 compare: wgpu::CompareFunction::Always,
103 fail_op: wgpu::StencilOperation::Replace,
104 depth_fail_op: wgpu::StencilOperation::Replace,
105 pass_op: wgpu::StencilOperation::Replace,
106 },
107 back: wgpu::StencilFaceState {
108 compare: wgpu::CompareFunction::Always,
109 fail_op: wgpu::StencilOperation::Replace,
110 depth_fail_op: wgpu::StencilOperation::Replace,
111 pass_op: wgpu::StencilOperation::Replace,
112 },
113 read_mask: 0x00,
114 write_mask: 0xFF,
115 },
116 bias: wgpu::DepthBiasState::default(),
117 }))
118 .multisample(wgpu::MultisampleState {
119 count: params.msaa_nr_samples,
120 ..Default::default()
121 })
122 .build_pipeline(gpu.device());
123
124 let empty_group = create_empty_group(gpu.device());
125
126 let size_bytes = 0x10000;
127 let usage = wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM;
128 let locals_uniform = Buffer::new_empty(gpu.device(), usage, Some("local_buffer"), size_bytes);
129
130 let locals_bind_groups = LocalsBindGroups::new(gpu);
131
132 let mut local_scene = Scene::new();
135 let passthrough_tex = Texture::new(
138 gpu.device(),
139 4,
140 4,
141 wgpu::TextureFormat::Depth32Float,
142 wgpu::TextureUsages::TEXTURE_BINDING,
143 TexParams::default(),
144 );
145 let passthrough_light = Light::new("compose_pass_passthrough_light", &mut local_scene);
148 let _ = local_scene.world.insert_one(
149 passthrough_light.entity,
150 ShadowMap {
151 tex_depth: passthrough_tex,
152 },
154 );
155
156 Self {
157 render_pipeline,
158 _empty_group: empty_group,
159 locals_uniform,
160 locals_bind_groups,
161 input_layout,
162 input_bind_group: None,
163 local_scene,
165 passthrough_light,
166 }
167 }
168}
169impl PipelineRunner for MeshPipeline {
170 type QueryItems<'a> = (
171 &'a VertsGPU,
172 &'a FacesGPU,
173 &'a UVsGPU,
174 &'a NormalsGPU,
175 &'a TangentsGPU,
176 &'a ColorsGPU,
177 &'a DiffuseTex,
178 &'a NormalTex,
179 &'a RoughnessTex,
180 &'a VisMesh,
181 &'a Name,
182 );
183 type QueryState<'a> = gloss_hecs::QueryBorrow<'a, gloss_hecs::With<Self::QueryItems<'a>, &'a Renderable>>;
184
185 fn query_state(scene: &Scene) -> Self::QueryState<'_> {
186 scene.world.query::<Self::QueryItems<'_>>().with::<&Renderable>()
187 }
188
189 fn prepare<'a>(&mut self, gpu: &Gpu, per_frame_uniforms: &PerFrameUniforms, scene: &'a Scene) -> Self::QueryState<'a> {
190 self.begin_pass();
191
192 self.update_locals(gpu, scene);
193
194 self.update_input_bind_group(gpu, scene, per_frame_uniforms);
196
197 Self::query_state(scene)
198 }
199
200 #[allow(clippy::too_many_lines)]
203 fn run<'r>(
204 &'r mut self,
205 render_pass: &mut wgpu::RenderPass<'r>,
206 per_frame_uniforms: &'r PerFrameUniforms,
207 _render_params: &RenderConfig,
208 query_state: &'r mut Self::QueryState<'_>,
209 scene: &Scene,
210 ) {
211 if query_state.iter().count() == 0 {
213 return;
214 }
215 let selector = scene.get_resource::<&Selector>();
216 render_pass.set_pipeline(&self.render_pipeline);
217
218 render_pass.set_bind_group(0, &per_frame_uniforms.bind_group, &[]);
220 render_pass.set_bind_group(1, self.input_bind_group.as_ref().unwrap().bg(), &[]);
222
223 render_pass.set_stencil_reference(0);
232 for (_id, (verts, faces, uvs, normals, tangents, colors, _diffuse_tex, _normal_tex, _roughness_tex, vis_mesh, name)) in query_state.iter() {
233 if !vis_mesh.show_mesh {
234 continue;
235 }
236
237 if let Ok(ref current_selector) = selector {
239 if let Some(current_selected) = ¤t_selector.current_selected {
240 if name.0 == *current_selected {
241 continue;
242 }
243 }
244 }
245
246 let (local_bg, offset) = &self.locals_bind_groups.mesh2local_bind[&name.0.clone()];
248 render_pass.set_bind_group(2, local_bg.bg(), &[*offset]);
249 render_pass.set_vertex_buffer(0, verts.buf.slice(..));
250 render_pass.set_vertex_buffer(1, uvs.buf.slice(..));
251 render_pass.set_vertex_buffer(2, normals.buf.slice(..));
252 render_pass.set_vertex_buffer(3, tangents.buf.slice(..));
253 render_pass.set_vertex_buffer(4, colors.buf.slice(..));
254 render_pass.set_index_buffer(faces.buf.slice(..), wgpu::IndexFormat::Uint32);
255 render_pass.draw_indexed(0..faces.nr_triangles * 3, 0, 0..1);
256 }
257
258 for (_id, (verts, faces, uvs, normals, tangents, colors, _diffuse_tex, _normal_tex, _roughness_tex, vis_mesh, name)) in query_state.iter() {
259 if let Ok(ref current_selector) = selector {
260 if let Some(current_selected) = ¤t_selector.current_selected {
261 if name.0 == *current_selected {
262 if !vis_mesh.show_mesh {
263 return;
264 }
265
266 render_pass.set_stencil_reference(1);
267
268 let (local_bg, offset) = &self.locals_bind_groups.mesh2local_bind[¤t_selected.clone()];
269 render_pass.set_bind_group(2, local_bg.bg(), &[*offset]);
270 render_pass.set_vertex_buffer(0, verts.buf.slice(..));
271 render_pass.set_vertex_buffer(1, uvs.buf.slice(..));
272 render_pass.set_vertex_buffer(2, normals.buf.slice(..));
273 render_pass.set_vertex_buffer(3, tangents.buf.slice(..));
274 render_pass.set_vertex_buffer(4, colors.buf.slice(..));
275 render_pass.set_index_buffer(faces.buf.slice(..), wgpu::IndexFormat::Uint32);
276 render_pass.draw_indexed(0..faces.nr_triangles * 3, 0, 0..1);
277 }
278 }
279 }
280 }
281
282 }
312
313 fn begin_pass(&mut self) {}
314
315 fn input_layout_desc() -> BindGroupLayoutDesc {
316 BindGroupLayoutBuilder::new()
317 .label("compose_input_layout")
318 .add_entry_cubemap(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
320 .add_entry_cubemap(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
322 .add_entries_tex(
324 wgpu::ShaderStages::FRAGMENT,
325 wgpu::TextureSampleType::Depth, MAX_NUM_SHADOWS,
329 )
330 .build()
331 }
332
333 fn update_input_bind_group(&mut self, gpu: &Gpu, scene: &Scene, per_frame_uniforms: &PerFrameUniforms) {
334 let env_map = scene.get_resource::<&EnvironmentMapGpu>().unwrap();
336
337 let mut shadow_maps = Vec::new();
338
339 for i in 0..MAX_NUM_SHADOWS {
342 let is_within_valid_lights: bool = i < per_frame_uniforms.idx_ubo2light.len();
343 if is_within_valid_lights && scene.world.has::<ShadowCaster>(per_frame_uniforms.idx_ubo2light[i]).unwrap() {
344 let entity = per_frame_uniforms.idx_ubo2light[i];
345 let shadow = scene
346 .get_comp::<&ShadowMap>(&entity)
347 .expect("The lights who have a ShadowCaster should also have ShadowMap at this point.");
348 shadow_maps.push(shadow);
349 } else {
350 let shadow: gloss_hecs::Ref<'_, ShadowMap> = self
352 .local_scene
353 .get_comp::<&ShadowMap>(&self.passthrough_light.entity)
354 .expect("Dummy light should have ShadowMap");
355 shadow_maps.push(shadow);
356 }
357 }
358
359 let entries = BindGroupBuilder::new()
360 .add_entry_tex(&env_map.diffuse_tex)
361 .add_entry_tex(&env_map.specular_tex)
362 .add_entry_tex(&shadow_maps[0].tex_depth)
363 .add_entry_tex(&shadow_maps[1].tex_depth)
364 .add_entry_tex(&shadow_maps[2].tex_depth)
365 .build_entries();
366 let stale = self.input_bind_group.as_ref().is_none_or(|b| b.is_stale(&entries));
368 if stale {
369 debug!("compose input bind group is stale, recreating");
370 self.input_bind_group = Some(BindGroupDesc::new("compose_input_bg", entries).into_bind_group_wrapper(gpu.device(), &self.input_layout));
371 }
372 }
373
374 fn update_locals(&mut self, gpu: &Gpu, scene: &Scene) {
377 Self::update_locals_inner::<Locals, _>(
378 gpu,
379 scene,
380 &mut self.locals_uniform,
381 &mut self.locals_bind_groups,
382 &mut Self::query_state(scene),
383 );
384 }
385}
386
387#[repr(C)]
389#[derive(Clone, Copy, encase::ShaderType)]
390struct Locals {
391 model_matrix: nalgebra::Matrix4<f32>,
392 color_type: i32,
393 solid_color: nalgebra::Vector4<f32>,
394 metalness: f32,
395 perceptual_roughness: f32,
396 roughness_black_lvl: f32,
397 uv_scale: f32,
398 is_floor: u32,
399 pad_c: f32,
402 pad_d: f32,
403}
404impl LocalEntData for Locals {
405 fn new(entity: Entity, scene: &Scene) -> Self {
406 let model_matrix = scene.get_comp::<&ModelMatrix>(&entity).unwrap().0.to_homogeneous();
407 let vis_mesh = scene.get_comp::<&VisMesh>(&entity).unwrap();
408 let color_type = vis_mesh.color_type as i32;
409 let is_floor = if let Some(floor) = scene.get_floor() {
410 floor.entity == entity
411 } else {
412 false
413 };
414 let is_floor = u32::from(is_floor);
415 Locals {
416 model_matrix,
417 color_type,
418 solid_color: vis_mesh.solid_color,
419 metalness: vis_mesh.metalness,
420 perceptual_roughness: vis_mesh.perceptual_roughness,
421 roughness_black_lvl: vis_mesh.roughness_black_lvl,
422 uv_scale: vis_mesh.uv_scale,
423 is_floor,
424 pad_c: 0.0,
425 pad_d: 0.0,
426 }
427 }
428}
429
430struct LocalsBindGroups {
431 layout: wgpu::BindGroupLayout,
432 pub mesh2local_bind: HashMap<String, (BindGroupWrapper, u32)>,
433}
434impl BindGroupCollection for LocalsBindGroups {
435 fn new(gpu: &Gpu) -> Self {
436 Self {
437 layout: Self::build_layout_desc().into_bind_group_layout(gpu.device()),
438 mesh2local_bind: HashMap::default(),
439 }
440 }
441
442 fn build_layout_desc() -> BindGroupLayoutDesc {
445 BindGroupLayoutBuilder::new()
446 .label("gbuffer_pass_locals_layout")
447 .add_entry_uniform(
449 wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
450 true,
451 wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<Locals>()).unwrap(), 256))),
452 )
453 .add_entry_tex(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
455 .add_entry_tex(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
457 .add_entry_tex(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
459 .build()
460 }
461
462 fn update_bind_group(&mut self, entity: Entity, gpu: &Gpu, mesh_name: &str, ubo: &Buffer, offset_in_ubo: u32, scene: &Scene) {
463 let diffuse_tex = &scene.get_comp::<&DiffuseTex>(&entity).unwrap().0;
465 let normal_tex = &scene.get_comp::<&NormalTex>(&entity).unwrap().0;
466 let roughness_tex = &scene.get_comp::<&RoughnessTex>(&entity).unwrap().0;
467
468 let entries = BindGroupBuilder::new()
469 .add_entry_buf_chunk::<Locals>(&ubo.buffer)
470 .add_entry_tex(diffuse_tex)
471 .add_entry_tex(normal_tex)
472 .add_entry_tex(roughness_tex)
473 .build_entries();
474
475 self.update_if_stale(mesh_name, entries, offset_in_ubo, gpu);
476 }
477
478 fn get_layout(&self) -> &wgpu::BindGroupLayout {
479 &self.layout
480 }
481 fn get_mut_entity2binds(&mut self) -> &mut HashMap<String, (BindGroupWrapper, u32)> {
482 &mut self.mesh2local_bind
483 }
484}