1#![allow(clippy::cast_precision_loss)]
2
3extern crate nalgebra as na;
4
5use crate::{
8 camera::Camera,
9 components::{
10 Colors, ColorsGPU, DiffuseImg, DiffuseTex, Edges, EdgesV1, EdgesV1GPU, EdgesV2, EdgesV2GPU, EnvironmentMap, EnvironmentMapGpu, Faces,
11 FacesGPU, GpuAtrib, LightEmit, MeshColorType, Name, NormalImg, NormalTex, Normals, NormalsGPU, PosLookat, Projection, ProjectionWithFov,
12 Renderable, RoughnessImg, RoughnessTex, ShadowCaster, Tangents, TangentsGPU, UVs, UVsGPU, Verts, VertsGPU, VisMesh,
13 },
14 config::RenderConfig,
15 scene::Scene,
16};
17
18use easy_wgpu::{
19 bind_group::BindGroupBuilder,
20 bind_group_layout::{BindGroupLayoutBuilder, BindGroupLayoutDesc},
21 buffer::Buffer,
22 gpu::Gpu,
23 mipmap::RenderMipmapGenerator,
24 texture::Texture,
25};
26use gloss_utils::tensor::{DynamicMatrixOps, DynamicTensorFloat2D, DynamicTensorOps};
27
28use gloss_hecs::{Changed, CommandBuffer, Component, Entity};
29use gloss_utils::numerical::{align, align_usz};
30use log::{debug, info, warn};
31use std::collections::HashMap;
32use wgpu::util::DeviceExt;
33
34use encase;
35
36pub const MAX_NUM_LIGHTS: usize = 20; pub const MAX_NUM_SHADOWS: usize = 3; pub fn index_vertices_from_edges(matrix: &na::DMatrix<f32>, v_indices: &na::DMatrix<u32>, col_id: usize) -> na::DMatrix<f32> {
40 let index_slice = v_indices.column(col_id).into_owned();
41 let indices: Vec<usize> = index_slice.iter().copied().map(|x| x as usize).collect();
42
43 let mut selected_rows = Vec::new();
45 for &index in &indices {
46 let row = matrix.row(index);
47 selected_rows.push(row);
48 }
49 na::DMatrix::from_rows(&selected_rows)
50}
51
52pub struct UploadPass {
55 per_frame_uniforms: PerFrameUniforms,
57 mipmapper: Option<RenderMipmapGenerator>,
58 pub command_buffer: CommandBuffer, pub staging_buffer: Option<Buffer>,
61}
62
63impl UploadPass {
64 pub fn new(gpu: &Gpu, params: &RenderConfig) -> Self {
65 const_assert!(std::mem::size_of::<PerFrameSceneCPU>() % 16 == 0);
67 const_assert!(std::mem::size_of::<PerFrameCamCPU>() % 16 == 0);
68 const_assert!(std::mem::size_of::<PerFrameLightCPU>() % 16 == 0);
69 const_assert!(std::mem::size_of::<PerFrameParamsCPU>() % 16 == 0);
70
71 let per_frame_uniforms = PerFrameUniforms::new(gpu);
72
73 let mipmapper = Some(RenderMipmapGenerator::new_with_format_hints(
89 gpu.device(),
90 &[
91 wgpu::TextureFormat::Rgba8Unorm, wgpu::TextureFormat::Rgba8UnormSrgb, wgpu::TextureFormat::R8Unorm, ],
95 ));
96
97 let command_buffer = CommandBuffer::new();
98
99 let staging_buffer = if params.preallocated_staging_buffer_bytes != 0 {
100 info!(
101 "Using preallocated staging buffer with {} MB",
102 params.preallocated_staging_buffer_bytes / (1024 * 1024)
103 );
104 Some(Buffer::new_empty(
105 gpu.device(),
106 wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::MAP_WRITE,
107 Some("gloss_staging_buffer"),
108 align_usz(params.preallocated_staging_buffer_bytes as usize, 256),
109 ))
110 } else {
111 None
112 };
113
114 Self {
115 per_frame_uniforms,
116 mipmapper,
117 command_buffer,
118 staging_buffer,
119 }
120 }
121
122 pub fn run(&mut self, gpu: &Gpu, camera: &Camera, scene: &mut Scene, render_params: &RenderConfig) -> &PerFrameUniforms {
123 self.upload_v(gpu, scene);
125 self.upload_e(gpu, scene);
126 self.upload_f(gpu, scene);
127 self.upload_uv(gpu, scene);
128 self.upload_nv(gpu, scene);
129 self.upload_t(gpu, scene);
130 self.upload_c(gpu, scene);
131 self.upload_textures(gpu, scene);
132
133 self.upload_scene(gpu, scene);
134 self.upload_cam(gpu, camera, scene);
135 self.upload_lights(gpu, scene);
136 self.upload_params(gpu, scene, render_params);
137
138 &self.per_frame_uniforms
139 }
140
141 pub fn upload_textures(&mut self, gpu: &Gpu, scene: &mut Scene) {
142 self.upload_diffuse_tex(gpu, scene);
143 self.upload_normal_tex(gpu, scene);
144 self.upload_roughness_tex(gpu, scene);
145 self.upload_environment_map(gpu, scene);
146 }
147
148 #[allow(clippy::unnecessary_unwrap)] fn upload_dynamic_vertex_atrib<T, C: DynamicTensorOps<T> + Component, G: GpuAtrib + Component>(
151 &mut self,
152 entity: Entity,
153 atrib: &C,
154 atrib_gpu: Option<&mut G>,
155 gpu: &Gpu,
156 additional_usage: wgpu::BufferUsages, label: &str,
158 ) {
159 let verts_bytes = atrib.as_bytes();
161 let size_bytes = verts_bytes.len();
162 if atrib_gpu.is_none() || atrib_gpu.as_ref().unwrap().data_ref().size() != std::convert::TryInto::<u64>::try_into(size_bytes).unwrap() {
163 let desc = wgpu::util::BufferInitDescriptor {
166 label: Some(label),
167 contents: &verts_bytes, usage: additional_usage | wgpu::BufferUsages::COPY_DST,
169 };
170
171 let buf: wgpu::Buffer = gpu.device().create_buffer_init(&desc);
172
173 self.command_buffer
175 .insert_one(entity, G::new_from(buf, u32::try_from(atrib.nrows()).unwrap()));
176 } else {
177 gpu.queue().write_buffer(
178 atrib_gpu.unwrap().data_ref(),
179 0,
180 &verts_bytes, );
182 }
183 }
184
185 fn upload_v(&mut self, gpu: &Gpu, scene: &mut Scene) {
187 let query = scene
188 .world
189 .query_mut::<(&Verts, Option<&mut VertsGPU>, Changed<Verts>)>()
190 .with::<&Renderable>();
191 let usage = wgpu::BufferUsages::VERTEX;
192
193 for (ent, (verts, mut verts_gpu, changed_verts)) in query {
194 if changed_verts {
195 self.upload_dynamic_vertex_atrib(ent, &verts.0, verts_gpu.as_deref_mut(), gpu, usage, "verts");
196 }
197 }
198 self.command_buffer.run_on(&mut scene.world);
199 }
200
201 fn upload_e(&mut self, gpu: &Gpu, scene: &mut Scene) {
202 let query = scene
203 .world
204 .query_mut::<(
205 &Verts,
206 &Edges,
207 Option<&mut EdgesV1GPU>,
208 Option<&mut EdgesV2GPU>,
209 Changed<Verts>,
210 Changed<Edges>,
211 )>()
212 .with::<&Renderable>();
213
214 let usage = wgpu::BufferUsages::VERTEX;
215 for (ent, (verts, edges, mut edges_v1_gpu, mut edges_v2_gpu, changed_verts, changed_edges)) in query {
216 if changed_verts || changed_edges {
217 let edges_v1_mat = index_vertices_from_edges(&verts.0.to_dmatrix(), &edges.0.to_dmatrix(), 0);
218 let edges_v2_mat = index_vertices_from_edges(&verts.0.to_dmatrix(), &edges.0.to_dmatrix(), 1);
219
220 let edges_v1_mat_tensor = DynamicTensorFloat2D::from_dmatrix(&edges_v1_mat);
221 let edges_v2_mat_tensor = DynamicTensorFloat2D::from_dmatrix(&edges_v2_mat);
222 let edges_v1 = EdgesV1(edges_v1_mat_tensor);
223 let edges_v2 = EdgesV2(edges_v2_mat_tensor);
224
225 self.upload_dynamic_vertex_atrib(ent, &edges_v1.0, edges_v1_gpu.as_deref_mut(), gpu, usage, "edges_v1");
226 self.upload_dynamic_vertex_atrib(ent, &edges_v2.0, edges_v2_gpu.as_deref_mut(), gpu, usage, "edges_v2");
227 }
228 }
229 self.command_buffer.run_on(&mut scene.world);
230 }
231
232 fn upload_f(&mut self, gpu: &Gpu, scene: &mut Scene) {
233 let query = scene
234 .world
235 .query_mut::<(&Faces, Option<&mut FacesGPU>, Changed<Faces>)>()
236 .with::<&Renderable>();
237 let usage = wgpu::BufferUsages::INDEX;
238 for (ent, (faces, mut faces_gpu, changed_faces)) in query {
239 if changed_faces {
240 self.upload_dynamic_vertex_atrib(ent, &faces.0, faces_gpu.as_deref_mut(), gpu, usage, "faces");
241 }
242 }
243 self.command_buffer.run_on(&mut scene.world);
244 }
245 fn upload_uv(&mut self, gpu: &Gpu, scene: &mut Scene) {
246 let query = scene.world.query_mut::<(&UVs, Option<&mut UVsGPU>, Changed<UVs>)>().with::<&Renderable>();
247 let usage = wgpu::BufferUsages::VERTEX;
248 for (ent, (uvs, mut uvs_gpu, changed_uvs)) in query {
249 if changed_uvs {
250 self.upload_dynamic_vertex_atrib(ent, &uvs.0, uvs_gpu.as_deref_mut(), gpu, usage, "uv");
251 }
252 }
253 self.command_buffer.run_on(&mut scene.world);
254 }
255
256 fn upload_nv(&mut self, gpu: &Gpu, scene: &mut Scene) {
257 let query = scene
258 .world
259 .query_mut::<(&Normals, Option<&mut NormalsGPU>, Changed<Normals>)>()
260 .with::<&Renderable>();
261 let usage = wgpu::BufferUsages::VERTEX;
262 for (ent, (normals, mut normals_gpu, changed_normals)) in query {
263 if changed_normals {
264 self.upload_dynamic_vertex_atrib(ent, &normals.0, normals_gpu.as_deref_mut(), gpu, usage, "normals");
265 }
266 }
267 self.command_buffer.run_on(&mut scene.world);
268 }
269
270 fn upload_t(&mut self, gpu: &Gpu, scene: &mut Scene) {
271 let query = scene
272 .world
273 .query_mut::<(&Tangents, Option<&mut TangentsGPU>, Changed<Tangents>)>()
274 .with::<&Renderable>();
275 let usage = wgpu::BufferUsages::VERTEX;
276 for (ent, (tangents, mut tangents_gpu, changed_tangents)) in query {
277 if changed_tangents {
278 self.upload_dynamic_vertex_atrib(ent, &tangents.0, tangents_gpu.as_deref_mut(), gpu, usage, "tangents");
279 }
280 }
281 self.command_buffer.run_on(&mut scene.world);
282 }
283
284 fn upload_c(&mut self, gpu: &Gpu, scene: &mut Scene) {
285 let query = scene
286 .world
287 .query_mut::<(&Colors, Option<&mut ColorsGPU>, Changed<Colors>)>()
288 .with::<&Renderable>();
289 let usage = wgpu::BufferUsages::VERTEX;
290 for (ent, (colors, mut colors_gpu, changed_colors)) in query {
291 if changed_colors {
292 self.upload_dynamic_vertex_atrib(ent, &colors.0, colors_gpu.as_deref_mut(), gpu, usage, "colors");
293 }
294 }
295 self.command_buffer.run_on(&mut scene.world);
296 }
297
298 fn upload_diffuse_tex(&mut self, gpu: &Gpu, scene: &mut Scene) {
299 let mut modified_entities = Vec::new();
300 {
301 let mut query = scene
302 .world
303 .query::<(&mut DiffuseImg, Option<&mut DiffuseTex>, Changed<DiffuseImg>)>()
304 .with::<&Renderable>();
305 for (entity, (mut img, tex_opt, changed_img)) in query.iter() {
306 if changed_img && img.generic_img.cpu_img.is_some() {
307 debug!("DiffuseImg changed for entity {entity:?}");
308 let nr_channels = img.generic_img.img_ref().color().channel_count();
309 if nr_channels != 4 {
310 warn!("unoptimal use of memory: diffuse does not have 4 channels, it has {nr_channels}");
311 }
312 modified_entities.push(entity);
313 let is_srgb = true; let keep_on_cpu = img.generic_img.config.keep_on_cpu;
317 let staging_buffer = if img.generic_img.config.fast_upload {
318 None
319 } else {
320 if self.staging_buffer.is_none() {
322 warn!("The diffuse image is set to slow upload which would require a preallocated staging buffer. However no bytes have been allocated for it. Check the config.toml for the preallocated_staging_buffer. Now we default to fast upload through wgpu staging buffer which might use more memory than necessary.");
323 }
324 self.staging_buffer.as_ref()
325 };
326
327 let mut tex_uploaded = false;
329 if let Some(mut existing_tex) = tex_opt {
330 let new_tex_extent = Texture::extent_from_img(img.generic_img.img_ref());
331 let new_tex_format = Texture::format_from_img(img.generic_img.img_ref(), is_srgb);
332 let old_tex_extent = existing_tex.0.extent();
333 let old_format = existing_tex.0.texture.format();
334 if new_tex_format == old_format && new_tex_extent == old_tex_extent {
335 debug!("reusing diffuse tex");
336 existing_tex.0.update_from_img(
337 img.generic_img.img_ref(),
338 gpu.device(),
339 gpu.queue(),
340 is_srgb,
341 img.generic_img.config.generate_mipmaps,
342 img.generic_img.config.mipmap_generation_cpu,
343 staging_buffer,
344 self.mipmapper.as_ref(),
345 );
346 tex_uploaded = true;
347 }
348 }
349 if !tex_uploaded {
351 let tex = Texture::from_img(
352 img.generic_img.img_ref(),
353 gpu.device(),
354 gpu.queue(),
355 is_srgb,
356 img.generic_img.config.generate_mipmaps,
357 img.generic_img.config.mipmap_generation_cpu,
358 staging_buffer,
359 self.mipmapper.as_ref(),
360 );
361 self.command_buffer.insert_one(entity, DiffuseTex(tex));
362 }
363
364 if !keep_on_cpu {
365 let _ = img.generic_img.cpu_img.take();
367 }
368 }
369 }
370
371 for entity in modified_entities {
373 if let Ok(mut vis_mesh) = scene.get_comp::<&mut VisMesh>(&entity) {
375 if vis_mesh.added_automatically {
376 vis_mesh.color_type = MeshColorType::Texture;
377 }
378 }
379 }
380 }
381 self.command_buffer.run_on(&mut scene.world);
382 }
383
384 fn upload_normal_tex(&mut self, gpu: &Gpu, scene: &mut Scene) {
385 let mut modified_entities = Vec::new();
386 {
387 let mut query = scene
388 .world
389 .query::<(&mut NormalImg, Option<&mut NormalTex>, Changed<NormalImg>)>()
390 .with::<&Renderable>();
391 for (entity, (mut img, tex_opt, changed_img)) in query.iter() {
392 if changed_img && img.generic_img.cpu_img.is_some() {
393 debug!("NormalImg changed for entity {entity:?}");
394 let nr_channels = img.generic_img.img_ref().color().channel_count();
395 if nr_channels != 4 {
396 warn!("unoptimal use of memory: normal does not have 4 channels, it has {nr_channels}");
397 }
398 modified_entities.push(entity);
399 let is_srgb = false; let keep_on_cpu = img.generic_img.config.keep_on_cpu;
402 let staging_buffer = if img.generic_img.config.fast_upload {
403 None
404 } else {
405 if self.staging_buffer.is_none() {
407 warn!("The normal image is set to slow upload which would require a preallocated staging buffer. However no bytes have been allocated for it. Check the config.toml for the preallocated_staging_buffer. Now we default to fast upload through wgpu staging buffer which might use more memory than necessary.");
408 }
409 self.staging_buffer.as_ref()
410 };
411
412 let mut tex_uploaded = false;
414 if let Some(mut existing_tex) = tex_opt {
415 let new_tex_extent = Texture::extent_from_img(img.generic_img.img_ref());
416 let new_tex_format = Texture::format_from_img(img.generic_img.img_ref(), is_srgb);
417 let old_tex_extent = existing_tex.0.extent();
418 let old_format = existing_tex.0.texture.format();
419 if new_tex_format == old_format && new_tex_extent == old_tex_extent {
420 debug!("reusing normal tex");
421 existing_tex.0.update_from_img(
422 img.generic_img.img_ref(),
423 gpu.device(),
424 gpu.queue(),
425 is_srgb,
426 img.generic_img.config.generate_mipmaps,
427 img.generic_img.config.mipmap_generation_cpu,
428 staging_buffer,
429 self.mipmapper.as_ref(),
430 );
431 tex_uploaded = true;
432 }
433 }
434 if !tex_uploaded {
436 let tex = Texture::from_img(
437 img.generic_img.img_ref(),
438 gpu.device(),
439 gpu.queue(),
440 is_srgb,
441 img.generic_img.config.generate_mipmaps,
442 img.generic_img.config.mipmap_generation_cpu,
443 staging_buffer,
444 self.mipmapper.as_ref(),
445 );
446 self.command_buffer.insert_one(entity, NormalTex(tex));
447 }
448
449 if !keep_on_cpu {
450 let _ = img.generic_img.cpu_img.take();
452 }
453 }
454 }
455
456 for entity in modified_entities {
458 if let Ok(mut vis_mesh) = scene.get_comp::<&mut VisMesh>(&entity) {
459 if vis_mesh.added_automatically {
460 vis_mesh.color_type = MeshColorType::Texture;
461 }
462 }
463 }
464 }
465
466 self.command_buffer.run_on(&mut scene.world);
467 }
468
469 fn upload_roughness_tex(&mut self, gpu: &Gpu, scene: &mut Scene) {
470 let mut modified_entities = Vec::new();
471 {
472 let mut query = scene
473 .world
474 .query::<(&mut RoughnessImg, Option<&mut RoughnessTex>, Changed<RoughnessImg>)>()
475 .with::<&Renderable>();
476 for (entity, (mut img, tex_opt, changed_img)) in query.iter() {
477 if changed_img && img.generic_img.cpu_img.is_some() {
478 debug!("RoughnessImg changed for entity {entity:?}");
479 let nr_channels = img.generic_img.img_ref().color().channel_count();
480 if nr_channels != 1 {
481 warn!("unoptimal use of memory: roughness does not have 1 channels, it has {nr_channels}");
482 }
483 modified_entities.push(entity);
484 let is_srgb = false; let keep_on_cpu = img.generic_img.config.keep_on_cpu;
487 let staging_buffer = if img.generic_img.config.fast_upload {
488 None
489 } else {
490 if self.staging_buffer.is_none() {
492 warn!("The roughness image is set to slow upload which would require a preallocated staging buffer. However no bytes have been allocated for it. Check the config.toml for the preallocated_staging_buffer. Now we default to fast upload through wgpu staging buffer which might use more memory than necessary.");
493 }
494 self.staging_buffer.as_ref()
495 };
496
497 let mut tex_uploaded = false;
499 if let Some(mut existing_tex) = tex_opt {
500 let new_tex_extent = Texture::extent_from_img(img.generic_img.img_ref());
501 let new_tex_format = Texture::format_from_img(img.generic_img.img_ref(), is_srgb);
502 let old_tex_extent = existing_tex.0.extent();
503 let old_format = existing_tex.0.texture.format();
504 if new_tex_format == old_format && new_tex_extent == old_tex_extent {
505 debug!("reusing roughness tex");
506 existing_tex.0.update_from_img(
507 img.generic_img.img_ref(),
508 gpu.device(),
509 gpu.queue(),
510 is_srgb,
511 img.generic_img.config.generate_mipmaps,
512 img.generic_img.config.mipmap_generation_cpu,
513 staging_buffer,
514 self.mipmapper.as_ref(),
515 );
516 tex_uploaded = true;
517 }
518 }
519 if !tex_uploaded {
521 let tex = Texture::from_img(
522 img.generic_img.img_ref(),
523 gpu.device(),
524 gpu.queue(),
525 is_srgb,
526 img.generic_img.config.generate_mipmaps,
527 img.generic_img.config.mipmap_generation_cpu,
528 staging_buffer,
529 self.mipmapper.as_ref(),
530 );
531 self.command_buffer.insert_one(entity, RoughnessTex(tex));
532 }
533
534 if !keep_on_cpu {
535 let _ = img.generic_img.cpu_img.take();
537 }
538 }
539 }
540
541 for entity in modified_entities {
543 if let Ok(mut vis_mesh) = scene.get_comp::<&mut VisMesh>(&entity) {
544 if vis_mesh.added_automatically {
545 vis_mesh.color_type = MeshColorType::Texture;
546 }
547 }
548 }
549 }
550
551 self.command_buffer.run_on(&mut scene.world);
552 }
553
554 fn upload_environment_map(&mut self, gpu: &Gpu, scene: &mut Scene) {
555 let query = scene.world.query_mut::<(&EnvironmentMap, Changed<EnvironmentMap>)>();
557 for (entity, (env_map, changed_env)) in query {
558 if changed_env {
559 let diffue_raw_data = std::fs::read(env_map.diffuse_path.clone()).unwrap();
560 let diffuse_reader = ktx2::Reader::new(diffue_raw_data.as_slice()).expect("Can't create diffuse_reader");
561 let specular_raw_data = std::fs::read(env_map.specular_path.clone()).unwrap();
562 let specular_reader = ktx2::Reader::new(specular_raw_data.as_slice()).expect("Can't create specular_reader");
563
564 let diffuse_tex = EnvironmentMapGpu::reader2texture(&diffuse_reader, gpu.device(), gpu.queue());
565 let specular_tex = EnvironmentMapGpu::reader2texture(&specular_reader, gpu.device(), gpu.queue());
566
567 let env_map_gpu = EnvironmentMapGpu { diffuse_tex, specular_tex };
568
569 self.command_buffer.insert_one(entity, env_map_gpu);
571 }
572 }
573
574 self.command_buffer.run_on(&mut scene.world);
575 }
576
577 fn upload_scene(&mut self, gpu: &Gpu, scene: &mut Scene) {
578 let entities_lights = scene.get_lights(false);
579 let env_map = scene.get_resource::<&EnvironmentMapGpu>().unwrap();
580 let environment_map_smallest_specular_mip_level = env_map.specular_tex.texture.mip_level_count() - 1;
581
582 let per_frame_scene_data = PerFrameSceneCPU {
583 nr_lights: u32::try_from(entities_lights.len()).unwrap(),
584 environment_map_smallest_specular_mip_level,
585 pad_1: 0,
586 pad_2: 0,
587 };
588
589 self.per_frame_uniforms.scene_buf.push_cpu_chunk_packed(&per_frame_scene_data);
590 self.per_frame_uniforms.scene_buf.upload_from_cpu_chunks(gpu.queue());
591 self.per_frame_uniforms.scene_buf.reset_chunks_offset();
592 }
593
594 fn upload_cam(&mut self, gpu: &Gpu, camera: &Camera, scene: &mut Scene) {
595 let pos_lookat = if let Ok(pos_lookat) = scene.world.get::<&mut PosLookat>(camera.entity) {
596 pos_lookat.clone()
597 } else {
598 PosLookat::default()
599 };
600
601 let view_matrix = pos_lookat.view_matrix();
602 let view_inv_matrix = pos_lookat.view_matrix_isometry().inverse().to_matrix();
603
604 let proj_matrix;
608 let near;
609 let far;
610 if scene.world.has::<Projection>(camera.entity).unwrap() {
611 proj_matrix = camera.proj_matrix_reverse_z(scene);
612 (near, far) = camera.near_far(scene);
613 } else {
614 let proj = ProjectionWithFov::default();
615 proj_matrix = proj.proj_matrix_reverse_z();
616 (near, far) = (proj.near, proj.far);
617 }
618 let (width, height) = camera.get_target_res(scene);
619 let aspect_ratio = width as f32 / height as f32;
620 let proj_inv_matrix = proj_matrix.try_inverse().unwrap();
621
622 let vp_matrix = proj_matrix * view_matrix;
623 let pos_world = pos_lookat.position.coords;
624
625 #[allow(clippy::cast_precision_loss)]
626 let per_frame_cam_data = PerFrameCamCPU {
627 view_matrix,
628 view_inv_matrix,
629 proj_matrix,
630 proj_inv_matrix,
631 vp_matrix,
632 pos_world,
633 near,
634 far,
635 aspect_ratio,
636 width: width as f32,
637 height: height as f32,
638 };
639
640 self.per_frame_uniforms.cam_buf.push_cpu_chunk_packed(&per_frame_cam_data);
641 self.per_frame_uniforms.cam_buf.upload_from_cpu_chunks(gpu.queue());
642 self.per_frame_uniforms.cam_buf.reset_chunks_offset();
643 }
644
645 fn upload_lights(&mut self, gpu: &Gpu, scene: &mut Scene) {
646 self.per_frame_uniforms.idx_ubo2light.clear();
647
648 let query = scene
649 .world
650 .query_mut::<(&Name, &PosLookat, &Projection, &LightEmit, Option<&ShadowCaster>)>();
651 for (idx_light, (entity, (name, pos_lookat, proj, light_emit, shadow_caster))) in query.into_iter().enumerate() {
652 let view_matrix = pos_lookat.view_matrix();
653 let proj_matrix = match *proj {
654 Projection::WithFov(ref proj) => proj.proj_matrix_reverse_z(),
655 Projection::WithIntrinsics(_) => {
656 panic!("We don't deal with light that have projection as intrinsics")
657 }
658 };
659 let (near, far) = proj.near_far();
660 let vp_matrix = proj_matrix * view_matrix;
661 let pos_world = pos_lookat.position.coords;
662 let lookat_dir_world = pos_lookat.direction();
663
664 let color = light_emit.color;
665 let intensity = light_emit.intensity;
666 let range = light_emit.range;
667 let inverse_square_range = 1.0 / (range * range);
668 let radius = light_emit.radius;
669 let is_shadow_casting_bool = shadow_caster.is_some();
670 let is_shadow_casting: u32 = u32::from(is_shadow_casting_bool);
671
672 let shadow_bias_fixed = if let Some(shadow_caster) = shadow_caster {
673 shadow_caster.shadow_bias_fixed
674 } else {
675 0.0
676 };
677 let shadow_bias = if let Some(shadow_caster) = shadow_caster {
678 shadow_caster.shadow_bias
679 } else {
680 0.0
681 };
682 let shadow_bias_normal = if let Some(shadow_caster) = shadow_caster {
683 shadow_caster.shadow_bias_normal
684 } else {
685 0.0
686 };
687
688 let outer_angle = light_emit.outer_angle;
694 let inner_angle = light_emit.inner_angle;
695
696 let per_frame_light_data = PerFrameLightCPU {
698 view_matrix,
699 proj_matrix,
700 vp_matrix,
701 pos_world,
702 lookat_dir_world,
703 color,
704 intensity,
705 range,
706 inverse_square_range,
707 radius,
708 outer_angle,
710 inner_angle,
711 near,
712 far,
713 is_shadow_casting,
714 shadow_bias_fixed,
715 shadow_bias,
716 shadow_bias_normal,
717 pad_b: 1.0,
718 pad_c: 1.0,
719 pad_d: 1.0,
720 };
721
722 self.per_frame_uniforms.lights_buf.push_cpu_chunk_packed(&per_frame_light_data);
724
725 self.per_frame_uniforms
727 .light2idx_ubo
728 .insert(name.0.clone(), u32::try_from(idx_light).unwrap());
729 self.per_frame_uniforms.idx_ubo2light.push(entity);
730 }
731
732 self.per_frame_uniforms.lights_buf.upload_from_cpu_chunks(gpu.queue());
733 self.per_frame_uniforms.lights_buf.reset_chunks_offset();
734 }
735
736 fn upload_params(&mut self, gpu: &Gpu, _scene: &mut Scene, render_params: &RenderConfig) {
737 let per_frame_params_data = PerFrameParamsCPU {
738 ambient_factor: render_params.ambient_factor,
739 environment_factor: render_params.environment_factor,
740 bg_color: render_params.bg_color,
741 enable_distance_fade: u32::from(render_params.enable_distance_fade.unwrap_or(false)),
742 distance_fade_center: render_params.distance_fade_center.unwrap_or_default().coords,
743 distance_fade_start: render_params.distance_fade_start.unwrap_or(0.0),
744 distance_fade_end: render_params.distance_fade_end.unwrap_or(0.0),
745 apply_lighting: u32::from(render_params.apply_lighting),
746 saturation: render_params.saturation,
747 gamma: render_params.gamma,
748 exposure: render_params.exposure,
749 shadow_filter_method: render_params.shadow_filter_method as i32, pad_b: 0.0,
751 pad_c: 0.0,
752 pad_d: 0.0,
753 };
754
755 self.per_frame_uniforms.params_buf.push_cpu_chunk_packed(&per_frame_params_data);
756 self.per_frame_uniforms.params_buf.upload_from_cpu_chunks(gpu.queue());
757 self.per_frame_uniforms.params_buf.reset_chunks_offset();
758 }
759}
760
761#[repr(C)]
762#[derive(Clone, Copy, encase::ShaderType)]
763struct PerFrameSceneCPU {
764 nr_lights: u32,
765 environment_map_smallest_specular_mip_level: u32,
766 pad_1: u32,
768 pad_2: u32,
769}
770#[repr(C)]
772#[derive(Clone, Copy, encase::ShaderType)]
773struct PerFrameCamCPU {
774 view_matrix: na::Matrix4<f32>,
775 view_inv_matrix: na::Matrix4<f32>,
776 proj_matrix: na::Matrix4<f32>,
777 proj_inv_matrix: na::Matrix4<f32>,
778 vp_matrix: na::Matrix4<f32>, pos_world: na::Vector3<f32>,
781 near: f32,
782 far: f32,
783 aspect_ratio: f32,
784 width: f32,
785 height: f32,
786}
787#[repr(C)]
789#[derive(Clone, Copy, encase::ShaderType)]
790struct PerFrameLightCPU {
792 view_matrix: na::Matrix4<f32>,
793 proj_matrix: na::Matrix4<f32>,
794 vp_matrix: na::Matrix4<f32>, pos_world: na::Vector3<f32>,
797 lookat_dir_world: na::Vector3<f32>,
798 color: na::Vector3<f32>,
799 intensity: f32,
800 range: f32,
801 inverse_square_range: f32, radius: f32,
803 outer_angle: f32,
804 inner_angle: f32,
805 near: f32,
806 far: f32,
807 is_shadow_casting: u32, shadow_bias_fixed: f32,
809 shadow_bias: f32,
810 shadow_bias_normal: f32,
811 pad_b: f32,
813 pad_c: f32,
814 pad_d: f32,
815}
816#[repr(C)]
817#[derive(Clone, Copy, encase::ShaderType)]
818struct PerFrameParamsCPU {
819 ambient_factor: f32,
820 environment_factor: f32,
821 bg_color: na::Vector4<f32>,
822 enable_distance_fade: u32,
823 distance_fade_center: na::Vector3<f32>,
824 distance_fade_start: f32,
825 distance_fade_end: f32,
826 apply_lighting: u32,
828 saturation: f32,
829 gamma: f32,
830 exposure: f32,
831 shadow_filter_method: i32,
832 pad_b: f32,
835 pad_c: f32,
836 pad_d: f32,
837}
838
839#[non_exhaustive]
842pub struct PerFrameUniforms {
843 scene_buf: Buffer, cam_buf: Buffer, lights_buf: Buffer, params_buf: Buffer, #[allow(dead_code)]
849 sampler_nearest: wgpu::Sampler, #[allow(dead_code)]
852 sampler_linear: wgpu::Sampler, #[allow(dead_code)]
854 sampler_comparison: wgpu::Sampler, pub bind_group: wgpu::BindGroup,
858 pub light2idx_ubo: HashMap<String, u32>,
860 pub idx_ubo2light: Vec<Entity>,
861}
862impl PerFrameUniforms {
863 pub fn new(gpu: &Gpu) -> Self {
864 let scene_buf = Buffer::new_empty(
865 gpu.device(),
866 wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
867 Some("global_scene_uniform"),
868 align_usz(std::mem::size_of::<PerFrameSceneCPU>(), 256),
869 );
870 let cam_buf = Buffer::new_empty(
872 gpu.device(),
873 wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
874 Some("global_cam_uniform"),
875 align_usz(std::mem::size_of::<PerFrameCamCPU>(), 256),
876 );
877 let lights_buf = Buffer::new_empty(
879 gpu.device(),
880 wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
881 Some("global_lights_uniform"),
883 MAX_NUM_LIGHTS * align_usz(std::mem::size_of::<PerFrameLightCPU>(), 256),
884 );
885 let params_buf = Buffer::new_empty(
886 gpu.device(),
887 wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
888 Some("global_params_uniform"),
889 align_usz(std::mem::size_of::<PerFrameParamsCPU>(), 256),
890 );
891
892 let sampler_nearest = gpu.device().create_sampler(&wgpu::SamplerDescriptor {
894 label: Some("sampler_nearest"),
895 address_mode_u: wgpu::AddressMode::Repeat,
896 address_mode_v: wgpu::AddressMode::Repeat,
897 address_mode_w: wgpu::AddressMode::Repeat,
898 min_filter: wgpu::FilterMode::Nearest,
899 mag_filter: wgpu::FilterMode::Nearest,
900 mipmap_filter: wgpu::FilterMode::Nearest,
901 ..Default::default()
902 });
903 let sampler_linear = gpu.device().create_sampler(&wgpu::SamplerDescriptor {
904 label: Some("sampler_linear"),
905 address_mode_u: wgpu::AddressMode::Repeat,
906 address_mode_v: wgpu::AddressMode::Repeat,
907 address_mode_w: wgpu::AddressMode::Repeat,
908 min_filter: wgpu::FilterMode::Linear,
909 mag_filter: wgpu::FilterMode::Linear,
910 mipmap_filter: wgpu::FilterMode::Linear,
911 ..Default::default()
912 });
913 let sampler_comparison = gpu.device().create_sampler(&wgpu::SamplerDescriptor {
914 label: Some("sampler_shadow_map"),
915 min_filter: wgpu::FilterMode::Linear,
916 mag_filter: wgpu::FilterMode::Linear,
917 compare: Some(wgpu::CompareFunction::Greater),
918 ..Default::default()
919 });
920
921 let layout = Self::create_layout(gpu);
922 let bind_group = BindGroupBuilder::new()
923 .label("per_frame_bind_group")
924 .add_entry_buf(&scene_buf.buffer)
925 .add_entry_buf(&cam_buf.buffer)
926 .add_entry_buf(&lights_buf.buffer)
927 .add_entry_buf(¶ms_buf.buffer)
928 .add_entry_sampler(&sampler_nearest)
929 .add_entry_sampler(&sampler_linear)
930 .add_entry_sampler(&sampler_comparison)
931 .build_bind_group(gpu.device(), &layout);
932
933 Self {
934 scene_buf,
935 cam_buf,
936 lights_buf,
937 params_buf,
938 sampler_nearest,
939 sampler_linear,
940 sampler_comparison,
941 bind_group,
942 light2idx_ubo: HashMap::new(),
943 idx_ubo2light: Vec::new(),
944 }
945 }
946
947 pub fn create_layout(gpu: &Gpu) -> wgpu::BindGroupLayout {
950 let global_bind_group_layout = Self::build_layout_desc().into_bind_group_layout(gpu.device());
951 global_bind_group_layout
952 }
953
954 pub fn build_layout_desc() -> BindGroupLayoutDesc {
957 BindGroupLayoutBuilder::new()
958 .label("locals_layout")
959 .add_entry_uniform(
961 wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
962 false,
963 wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<PerFrameSceneCPU>()).unwrap(), 256))),
964 )
965 .add_entry_uniform(
967 wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
968 false,
969 wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<PerFrameCamCPU>()).unwrap(), 256))),
970 )
971 .add_entry_uniform(
973 wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
974 false,
975 wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<PerFrameLightCPU>()).unwrap(), 256))),
976 )
977 .add_entry_uniform(
979 wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
980 false,
981 wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<PerFrameParamsCPU>()).unwrap(), 256))),
982 )
983 .add_entry_sampler(wgpu::ShaderStages::FRAGMENT, wgpu::SamplerBindingType::NonFiltering)
985 .add_entry_sampler(wgpu::ShaderStages::FRAGMENT, wgpu::SamplerBindingType::Filtering)
986 .add_entry_sampler(wgpu::ShaderStages::FRAGMENT, wgpu::SamplerBindingType::Comparison)
987 .build()
988 }
989}