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};
13use easy_wgpu::{
14 bind_group::{BindGroupBuilder, BindGroupDesc, BindGroupWrapper},
15 bind_group_layout::{BindGroupLayoutBuilder, BindGroupLayoutDesc},
16 buffer::Buffer,
17};
18use easy_wgpu::{
21 gpu::Gpu,
22 texture::{TexParams, Texture},
23 utils::create_empty_group,
24};
25use gloss_hecs::Entity;
26use log::debug;
27
28use super::{pipeline_runner::PipelineRunner, upload_pass::PerFrameUniforms};
29
30use easy_wgpu::pipeline::RenderPipelineDescBuilder;
31
32use super::upload_pass::MAX_NUM_SHADOWS;
33use encase;
34
35use gloss_utils::numerical::align;
36
37#[include_wgsl_oil::include_wgsl_oil("../../../shaders/gbuffer_mesh_vert.wgsl")]
39mod vert_shader_code {}
40#[allow(clippy::approx_constant)]
41#[include_wgsl_oil::include_wgsl_oil("../../../shaders/gbuffer_mesh_frag.wgsl")]
42mod frag_shader_code {}
43
44pub struct MeshPipeline {
46 render_pipeline: wgpu::RenderPipeline,
47 _empty_group: wgpu::BindGroup,
48 locals_uniform: Buffer, locals_bind_groups: LocalsBindGroups,
50 input_layout: wgpu::BindGroupLayout,
53 input_bind_group: Option<BindGroupWrapper>,
54 local_scene: Scene,
58 passthrough_light: Light,
62}
63
64impl MeshPipeline {
65 pub fn new(gpu: &Gpu, params: &RenderConfig, color_target_format: wgpu::TextureFormat, depth_target_format: wgpu::TextureFormat) -> Self {
69 const_assert!(std::mem::size_of::<Locals>() % 16 == 0);
71
72 let input_layout_desc = Self::input_layout_desc();
73 let input_layout = input_layout_desc.clone().into_bind_group_layout(gpu.device());
74
75 let render_pipeline = RenderPipelineDescBuilder::new()
77 .label("mesh_pipeline")
78 .shader_code_vert(vert_shader_code::SOURCE)
80 .shader_code_frag(frag_shader_code::SOURCE)
81 .shader_label("mesh_shader")
82 .add_bind_group_layout_desc(PerFrameUniforms::build_layout_desc())
83 .add_bind_group_layout_desc(input_layout_desc)
84 .add_bind_group_layout_desc(LocalsBindGroups::build_layout_desc())
85 .add_vertex_buffer_layout(VertsGPU::vertex_buffer_layout::<0>())
86 .add_vertex_buffer_layout(UVsGPU::vertex_buffer_layout::<1>())
87 .add_vertex_buffer_layout(NormalsGPU::vertex_buffer_layout::<2>())
88 .add_vertex_buffer_layout(TangentsGPU::vertex_buffer_layout::<3>())
89 .add_vertex_buffer_layout(ColorsGPU::vertex_buffer_layout::<4>())
90 .add_render_target(wgpu::ColorTargetState {
91 format: color_target_format,
92 blend: None,
93 write_mask: wgpu::ColorWrites::ALL,
94 })
95 .depth_state(Some(wgpu::DepthStencilState {
96 format: depth_target_format,
97 depth_write_enabled: true,
98 depth_compare: wgpu::CompareFunction::Greater,
99 stencil: wgpu::StencilState::default(),
100 bias: wgpu::DepthBiasState::default(),
101 }))
102 .multisample(wgpu::MultisampleState {
103 count: params.msaa_nr_samples,
104 ..Default::default()
105 })
106 .build_pipeline(gpu.device());
107
108 let empty_group = create_empty_group(gpu.device());
109
110 let size_bytes = 0x10000;
111 let usage = wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM;
112 let locals_uniform = Buffer::new_empty(gpu.device(), usage, Some("local_buffer"), size_bytes);
113
114 let locals_bind_groups = LocalsBindGroups::new(gpu);
115
116 let mut local_scene = Scene::new();
119 let passthrough_tex = Texture::new(
122 gpu.device(),
123 4,
124 4,
125 wgpu::TextureFormat::Depth32Float,
126 wgpu::TextureUsages::TEXTURE_BINDING,
127 TexParams::default(),
128 );
129 let passthrough_light = Light::new("compose_pass_passthrough_light", &mut local_scene);
132 let _ = local_scene.world.insert_one(
133 passthrough_light.entity,
134 ShadowMap {
135 tex_depth: passthrough_tex,
136 },
138 );
139
140 Self {
141 render_pipeline,
142 _empty_group: empty_group,
143 locals_uniform,
144 locals_bind_groups,
145 input_layout,
146 input_bind_group: None,
147 local_scene,
149 passthrough_light,
150 }
151 }
152}
153impl PipelineRunner for MeshPipeline {
154 type QueryItems<'a> = (
155 &'a VertsGPU,
156 &'a FacesGPU,
157 &'a UVsGPU,
158 &'a NormalsGPU,
159 &'a TangentsGPU,
160 &'a ColorsGPU,
161 &'a DiffuseTex,
162 &'a NormalTex,
163 &'a RoughnessTex,
164 &'a VisMesh,
165 &'a Name,
166 );
167 type QueryState<'a> = gloss_hecs::QueryBorrow<'a, gloss_hecs::With<Self::QueryItems<'a>, &'a Renderable>>;
168
169 fn query_state(scene: &Scene) -> Self::QueryState<'_> {
170 scene.world.query::<Self::QueryItems<'_>>().with::<&Renderable>()
171 }
172
173 fn prepare<'a>(&mut self, gpu: &Gpu, per_frame_uniforms: &PerFrameUniforms, scene: &'a Scene) -> Self::QueryState<'a> {
174 self.begin_pass();
175
176 self.update_locals(gpu, scene);
177
178 self.update_input_bind_group(gpu, scene, per_frame_uniforms);
180
181 Self::query_state(scene)
182 }
183
184 #[allow(clippy::too_many_lines)]
187 fn run<'r>(
188 &'r mut self,
189 render_pass: &mut wgpu::RenderPass<'r>,
190 per_frame_uniforms: &'r PerFrameUniforms,
191 _render_params: &RenderConfig,
192 query_state: &'r mut Self::QueryState<'_>,
193 ) {
194 if query_state.iter().count() == 0 {
196 return;
197 }
198
199 render_pass.set_pipeline(&self.render_pipeline);
200
201 render_pass.set_bind_group(0, &per_frame_uniforms.bind_group, &[]);
203 render_pass.set_bind_group(1, self.input_bind_group.as_ref().unwrap().bg(), &[]);
205
206 for (_id, (verts, faces, uvs, normals, tangents, colors, _diffuse_tex, _normal_tex, _roughness_tex, vis_mesh, name)) in query_state.iter() {
207 if !vis_mesh.show_mesh {
208 continue;
209 }
210
211 let (local_bg, offset) = &self.locals_bind_groups.mesh2local_bind[&name.0.clone()];
213 render_pass.set_bind_group(2, local_bg.bg(), &[*offset]);
214
215 render_pass.set_vertex_buffer(0, verts.buf.slice(..));
216 render_pass.set_vertex_buffer(1, uvs.buf.slice(..));
217 render_pass.set_vertex_buffer(2, normals.buf.slice(..));
218 render_pass.set_vertex_buffer(3, tangents.buf.slice(..));
219 render_pass.set_vertex_buffer(4, colors.buf.slice(..));
220 render_pass.set_index_buffer(faces.buf.slice(..), wgpu::IndexFormat::Uint32);
221 render_pass.draw_indexed(0..faces.nr_triangles * 3, 0, 0..1);
222 }
223 }
224
225 fn begin_pass(&mut self) {}
226
227 fn input_layout_desc() -> BindGroupLayoutDesc {
228 BindGroupLayoutBuilder::new()
229 .label("compose_input_layout")
230 .add_entry_cubemap(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
232 .add_entry_cubemap(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
234 .add_entries_tex(
236 wgpu::ShaderStages::FRAGMENT,
237 wgpu::TextureSampleType::Depth, MAX_NUM_SHADOWS,
241 )
242 .build()
243 }
244
245 fn update_input_bind_group(&mut self, gpu: &Gpu, scene: &Scene, per_frame_uniforms: &PerFrameUniforms) {
246 let env_map = scene.get_resource::<&EnvironmentMapGpu>().unwrap();
248
249 let mut shadow_maps = Vec::new();
250
251 for i in 0..MAX_NUM_SHADOWS {
254 let is_within_valid_lights: bool = i < per_frame_uniforms.idx_ubo2light.len();
255 if is_within_valid_lights && scene.world.has::<ShadowCaster>(per_frame_uniforms.idx_ubo2light[i]).unwrap() {
256 let entity = per_frame_uniforms.idx_ubo2light[i];
257 let shadow = scene
258 .get_comp::<&ShadowMap>(&entity)
259 .expect("The lights who have a ShadowCaster should also have ShadowMap at this point.");
260 shadow_maps.push(shadow);
261 } else {
262 let shadow: gloss_hecs::Ref<'_, ShadowMap> = self
264 .local_scene
265 .get_comp::<&ShadowMap>(&self.passthrough_light.entity)
266 .expect("Dummy light should have ShadowMap");
267 shadow_maps.push(shadow);
268 }
269 }
270
271 let entries = BindGroupBuilder::new()
272 .add_entry_tex(&env_map.diffuse_tex)
273 .add_entry_tex(&env_map.specular_tex)
274 .add_entry_tex(&shadow_maps[0].tex_depth)
275 .add_entry_tex(&shadow_maps[1].tex_depth)
276 .add_entry_tex(&shadow_maps[2].tex_depth)
277 .build_entries();
278 let stale = self.input_bind_group.as_ref().map_or(true, |b| b.is_stale(&entries)); if stale {
280 debug!("compose input bind group is stale, recreating");
281 self.input_bind_group = Some(BindGroupDesc::new("compose_input_bg", entries).into_bind_group_wrapper(gpu.device(), &self.input_layout));
282 }
283 }
284
285 fn update_locals(&mut self, gpu: &Gpu, scene: &Scene) {
288 Self::update_locals_inner::<Locals, _>(
289 gpu,
290 scene,
291 &mut self.locals_uniform,
292 &mut self.locals_bind_groups,
293 &mut Self::query_state(scene),
294 );
295 }
296}
297
298#[repr(C)]
300#[derive(Clone, Copy, encase::ShaderType)]
301struct Locals {
302 model_matrix: nalgebra::Matrix4<f32>,
303 color_type: i32,
304 solid_color: nalgebra::Vector4<f32>,
305 metalness: f32,
306 perceptual_roughness: f32,
307 roughness_black_lvl: f32,
308 uv_scale: f32,
309 is_floor: u32,
310 pad_c: f32,
313 pad_d: f32,
314}
315impl LocalEntData for Locals {
316 fn new(entity: Entity, scene: &Scene) -> Self {
317 let model_matrix = scene.get_comp::<&ModelMatrix>(&entity).unwrap().0.to_homogeneous();
318 let vis_mesh = scene.get_comp::<&VisMesh>(&entity).unwrap();
319 let color_type = vis_mesh.color_type as i32;
320 let is_floor = if let Some(floor) = scene.get_floor() {
321 floor.entity == entity
322 } else {
323 false
324 };
325 let is_floor = u32::from(is_floor);
326 Locals {
327 model_matrix,
328 color_type,
329 solid_color: vis_mesh.solid_color,
330 metalness: vis_mesh.metalness,
331 perceptual_roughness: vis_mesh.perceptual_roughness,
332 roughness_black_lvl: vis_mesh.roughness_black_lvl,
333 uv_scale: vis_mesh.uv_scale,
334 is_floor,
335 pad_c: 0.0,
336 pad_d: 0.0,
337 }
338 }
339}
340
341struct LocalsBindGroups {
342 layout: wgpu::BindGroupLayout,
343 pub mesh2local_bind: HashMap<String, (BindGroupWrapper, u32)>,
344}
345impl BindGroupCollection for LocalsBindGroups {
346 fn new(gpu: &Gpu) -> Self {
347 Self {
348 layout: Self::build_layout_desc().into_bind_group_layout(gpu.device()),
349 mesh2local_bind: HashMap::default(),
350 }
351 }
352
353 fn build_layout_desc() -> BindGroupLayoutDesc {
356 BindGroupLayoutBuilder::new()
357 .label("gbuffer_pass_locals_layout")
358 .add_entry_uniform(
360 wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
361 true,
362 wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<Locals>()).unwrap(), 256))),
363 )
364 .add_entry_tex(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
366 .add_entry_tex(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
368 .add_entry_tex(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
370 .build()
371 }
372
373 fn update_bind_group(&mut self, entity: Entity, gpu: &Gpu, mesh_name: &str, ubo: &Buffer, offset_in_ubo: u32, scene: &Scene) {
374 let diffuse_tex = &scene.get_comp::<&DiffuseTex>(&entity).unwrap().0;
376 let normal_tex = &scene.get_comp::<&NormalTex>(&entity).unwrap().0;
377 let roughness_tex = &scene.get_comp::<&RoughnessTex>(&entity).unwrap().0;
378
379 let entries = BindGroupBuilder::new()
380 .add_entry_buf_chunk::<Locals>(&ubo.buffer)
381 .add_entry_tex(diffuse_tex)
382 .add_entry_tex(normal_tex)
383 .add_entry_tex(roughness_tex)
384 .build_entries();
385
386 self.update_if_stale(mesh_name, entries, offset_in_ubo, gpu);
387 }
388
389 fn get_layout(&self) -> &wgpu::BindGroupLayout {
390 &self.layout
391 }
392 fn get_mut_entity2binds(&mut self) -> &mut HashMap<String, (BindGroupWrapper, u32)> {
393 &mut self.mesh2local_bind
394 }
395}