From b521df20acb53e7322935647243eed2ec316a62c Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 29 Oct 2023 23:37:31 -0700 Subject: [PATCH] Support node hierarchy in the scene --- blade-graphics/src/lib.rs | 21 +++ blade-render/src/lib.rs | 23 ++- blade-render/src/render/mod.rs | 248 +++++++++++++++++++------------ blade-render/src/render/scene.rs | 53 ------- examples/scene/main.rs | 10 +- 5 files changed, 191 insertions(+), 164 deletions(-) delete mode 100644 blade-render/src/render/scene.rs diff --git a/blade-graphics/src/lib.rs b/blade-graphics/src/lib.rs index 096ba671..e2e14db7 100644 --- a/blade-graphics/src/lib.rs +++ b/blade-graphics/src/lib.rs @@ -26,6 +26,27 @@ pub use naga::{StorageAccess, VectorSize}; pub type Transform = mint::RowMatrix3x4; +pub const IDENTITY_TRANSFORM: Transform = mint::RowMatrix3x4 { + x: mint::Vector4 { + x: 1.0, + y: 0.0, + z: 0.0, + w: 0.0, + }, + y: mint::Vector4 { + x: 0.0, + y: 1.0, + z: 0.0, + w: 0.0, + }, + z: mint::Vector4 { + x: 0.0, + y: 0.0, + z: 1.0, + w: 0.0, + }, +}; + #[cfg_attr( all(not(vulkan), not(gles), any(target_os = "ios", target_os = "macos")), path = "metal/mod.rs" diff --git a/blade-render/src/lib.rs b/blade-render/src/lib.rs index 7e596541..3604f3d2 100644 --- a/blade-render/src/lib.rs +++ b/blade-render/src/lib.rs @@ -39,21 +39,20 @@ pub struct Camera { pub depth: f32, } -pub struct Object { - pub model: blade_asset::Handle, +pub struct Node { + pub label: String, pub transform: blade_graphics::Transform, + pub models: Vec>, + pub children: Vec, } -impl From> for Object { - fn from(model: blade_asset::Handle) -> Self { +impl Default for Node { + fn default() -> Self { Self { - model, - transform: [ - [1.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0], - ] - .into(), + label: String::new(), + transform: blade_graphics::IDENTITY_TRANSFORM, + models: Vec::new(), + children: Vec::new(), } } } @@ -77,7 +76,7 @@ impl Default for PostProcessing { #[derive(Default)] pub struct Scene { - pub objects: Vec, + pub roots: Vec, pub environment_map: Option>, pub post_processing: PostProcessing, } diff --git a/blade-render/src/render/mod.rs b/blade-render/src/render/mod.rs index 59314c32..1b537839 100644 --- a/blade-render/src/render/mod.rs +++ b/blade-render/src/render/mod.rs @@ -1,6 +1,5 @@ mod dummy; mod env_map; -mod scene; pub use dummy::DummyResources; pub use env_map::EnvironmentMap; @@ -872,7 +871,7 @@ impl Renderer { gpu.destroy_buffer(self.debug.entry_buffer); } - pub fn merge_scene(&mut self, scene: super::Scene) { + pub fn change_scene(&mut self, scene: super::Scene) { self.scene = scene; self.is_tlas_dirty = true; } @@ -1000,89 +999,60 @@ impl Renderer { self.debug_view = debug_view; } - /// Prepare to render a frame. #[profiling::function] - #[allow(clippy::too_many_arguments)] - pub fn prepare( + pub fn rebuild_acceleration_structure( &mut self, command_encoder: &mut blade_graphics::CommandEncoder, - camera: &super::Camera, asset_hub: &crate::AssetHub, gpu: &blade_graphics::Context, temp_buffers: &mut Vec, - temp_acceleration_structures: &mut Vec, - enable_debug_draw: bool, - accumulate_variance: bool, - reset_reservoirs: bool, ) { - if self.is_tlas_dirty { - self.is_tlas_dirty = false; - if self.acceleration_structure != blade_graphics::AccelerationStructure::default() { - temp_buffers.push(self.hit_buffer); - temp_acceleration_structures.push(self.acceleration_structure); - } - - let (tlas, geometry_count) = self.scene.build_top_level_acceleration_structure( - command_encoder, - &asset_hub.models, - gpu, - temp_buffers, - ); - self.acceleration_structure = tlas; - log::info!("Preparing ray tracing with {geometry_count} geometries in total"); - let mut transfers = command_encoder.transfer(); - - { - // init the debug buffer - let data = [2, 0, 0, 0, self.debug.capacity, 0]; - let size = 4 * data.len() as u64; - let staging = gpu.create_buffer(blade_graphics::BufferDesc { - name: "debug buf staging", - size, - memory: blade_graphics::Memory::Upload, - }); - unsafe { - ptr::write(staging.data() as *mut _, data); - } - transfers.copy_buffer_to_buffer(staging.into(), self.debug.buffer.into(), size); - temp_buffers.push(staging); - } + fn extract_matrix3(transform: blade_graphics::Transform) -> glam::Mat3 { + let col_mx = mint::ColumnMatrix3x4::from(transform); + glam::Mat3::from_cols(col_mx.x.into(), col_mx.y.into(), col_mx.z.into()) + } - self.vertex_buffers.clear(); - self.index_buffers.clear(); - self.textures.clear(); - let dummy_white = self.textures.alloc(self.dummy.white_view); - let dummy_black = self.textures.alloc(self.dummy.black_view); - - if geometry_count != 0 { - let hit_staging = { - // init the hit buffer - let hit_size = (geometry_count as usize * mem::size_of::()) as u64; - self.hit_buffer = gpu.create_buffer(blade_graphics::BufferDesc { - name: "hit entries", - size: hit_size, - memory: blade_graphics::Memory::Device, - }); - let staging = gpu.create_buffer(blade_graphics::BufferDesc { - name: "hit staging", - size: hit_size, - memory: blade_graphics::Memory::Upload, - }); - temp_buffers.push(staging); - transfers.copy_buffer_to_buffer(staging.at(0), self.hit_buffer.at(0), hit_size); - staging + self.vertex_buffers.clear(); + self.index_buffers.clear(); + self.textures.clear(); + let dummy_white = self.textures.alloc(self.dummy.white_view); + let dummy_black = self.textures.alloc(self.dummy.black_view); + + let mut instances = Vec::new(); + let mut blases = Vec::new(); + let mut hit_entries = Vec::new(); + let mut texture_indices = HashMap::new(); + let mut node_queue = vec![(glam::Mat4::IDENTITY, self.scene.roots.as_slice())]; + + while let Some((parent_transform, nodes)) = node_queue.pop() { + for node in nodes { + let transform = parent_transform + * glam::Mat4 { + x_axis: node.transform.x.into(), + y_axis: node.transform.y.into(), + z_axis: node.transform.z.into(), + w_axis: glam::Vec4::W, + }; + let m3_object = glam::Mat3 { + x_axis: transform.x_axis.truncate(), + y_axis: transform.y_axis.truncate(), + z_axis: transform.z_axis.truncate(), }; - fn extract_matrix3(transform: blade_graphics::Transform) -> glam::Mat3 { - let col_mx = mint::ColumnMatrix3x4::from(transform); - glam::Mat3::from_cols(col_mx.x.into(), col_mx.y.into(), col_mx.z.into()) - } + for &model_handle in node.models.iter() { + let model = &asset_hub.models[model_handle]; + instances.push(blade_graphics::AccelerationStructureInstance { + acceleration_structure_index: blases.len() as u32, + transform: blade_graphics::Transform { + x: transform.x_axis.into(), + y: transform.y_axis.into(), + z: transform.z_axis.into(), + }, + mask: 0xFF, + custom_index: hit_entries.len() as u32, + }); + blases.push(model.acceleration_structure); - let mut texture_indices = HashMap::new(); - let mut geometry_index = 0; - for object in self.scene.objects.iter() { - let m3_object = extract_matrix3(object.transform); - let model = &asset_hub.models[object.model]; for geometry in model.geometries.iter() { let material = &model.materials[geometry.material_index]; let vertex_offset = geometry.vertex_range.start as u64 @@ -1154,29 +1124,100 @@ impl Renderer { finish_pad: [0; 1], }; - log::debug!("Entry[{geometry_index}] = {hit_entry:?}"); - unsafe { - ptr::write( - (hit_staging.data() as *mut HitEntry).add(geometry_index), - hit_entry, - ); - } - geometry_index += 1; + log::debug!("Entry[{}] = {:?}", hit_entries.len(), hit_entry); + hit_entries.push(hit_entry); } } - assert_eq!(geometry_index, geometry_count as usize); - self.texture_resource_lookup.clear(); - for (handle, res_id) in texture_indices { - self.texture_resource_lookup.insert(res_id, handle); - } - } else { - self.hit_buffer = gpu.create_buffer(blade_graphics::BufferDesc { - name: "hit entries", - size: mem::size_of::() as u64, - memory: blade_graphics::Memory::Device, - }); + node_queue.push((transform, &node.children)); + } + } + + self.texture_resource_lookup.clear(); + for (handle, res_id) in texture_indices { + self.texture_resource_lookup.insert(res_id, handle); + } + + log::info!( + "Preparing ray tracing with {} geometries in total", + hit_entries.len() + ); + let hit_size = (hit_entries.len().max(1) * mem::size_of::()) as u64; + //TODO: reuse the hit buffer + if self.hit_buffer != blade_graphics::Buffer::default() { + temp_buffers.push(self.hit_buffer); + } + self.hit_buffer = gpu.create_buffer(blade_graphics::BufferDesc { + name: "hit entries", + size: hit_size, + memory: blade_graphics::Memory::Device, + }); + let staging = gpu.create_buffer(blade_graphics::BufferDesc { + name: "hit staging", + size: hit_size, + memory: blade_graphics::Memory::Upload, + }); + temp_buffers.push(staging); + let mut transfers = command_encoder.transfer(); + transfers.copy_buffer_to_buffer(staging.at(0), self.hit_buffer.at(0), hit_size); + unsafe { + ptr::copy_nonoverlapping( + hit_entries.as_ptr(), + staging.data() as *mut _, + hit_entries.len(), + ); + } + + // Needs to be a separate encoder in order to force synchronization + let sizes = gpu.get_top_level_acceleration_structure_sizes(instances.len() as u32); + self.acceleration_structure = + gpu.create_acceleration_structure(blade_graphics::AccelerationStructureDesc { + name: "TLAS", + ty: blade_graphics::AccelerationStructureType::TopLevel, + size: sizes.data, + }); + let instance_buf = gpu.create_acceleration_structure_instance_buffer(&instances, &blases); + let scratch_buf = gpu.create_buffer(blade_graphics::BufferDesc { + name: "TLAS scratch", + size: sizes.scratch, + memory: blade_graphics::Memory::Device, + }); + + let mut tlas_encoder = command_encoder.acceleration_structure(); + tlas_encoder.build_top_level( + self.acceleration_structure, + &blases, + instances.len() as u32, + instance_buf.at(0), + scratch_buf.at(0), + ); + + temp_buffers.push(instance_buf); + temp_buffers.push(scratch_buf); + } + + /// Prepare to render a frame. + #[profiling::function] + #[allow(clippy::too_many_arguments)] + pub fn prepare( + &mut self, + command_encoder: &mut blade_graphics::CommandEncoder, + camera: &super::Camera, + asset_hub: &crate::AssetHub, + gpu: &blade_graphics::Context, + temp_buffers: &mut Vec, + temp_acceleration_structures: &mut Vec, + enable_debug_draw: bool, + accumulate_variance: bool, + reset_reservoirs: bool, + ) { + let reset_debug_buffer = self.frame_index == 0; + if self.is_tlas_dirty { + self.is_tlas_dirty = false; + if self.acceleration_structure != blade_graphics::AccelerationStructure::default() { + temp_acceleration_structures.push(self.acceleration_structure); } + self.rebuild_acceleration_structure(command_encoder, asset_hub, gpu, temp_buffers); } if let Some(handle) = self.scene.environment_map { @@ -1186,13 +1227,29 @@ impl Renderer { }; let mut transfer = command_encoder.transfer(); - if enable_debug_draw { + + if reset_debug_buffer { + // init the debug buffer + let data = [2, 0, 0, 0, self.debug.capacity, 0]; + let size = 4 * data.len() as u64; + let staging = gpu.create_buffer(blade_graphics::BufferDesc { + name: "debug buf staging", + size, + memory: blade_graphics::Memory::Upload, + }); + unsafe { + ptr::write(staging.data() as *mut _, data); + } + transfer.copy_buffer_to_buffer(staging.into(), self.debug.buffer.into(), size); + temp_buffers.push(staging); + } else if enable_debug_draw { // reset the debug line count transfer.fill_buffer(self.debug.buffer.at(4), 4, 0); transfer.fill_buffer(self.debug.buffer.at(20), 4, 1); } else { transfer.fill_buffer(self.debug.buffer.at(20), 4, 0); } + if reset_reservoirs || !accumulate_variance { transfer.fill_buffer( self.debug.buffer.at(32), @@ -1214,8 +1271,9 @@ impl Renderer { self.debug.entry_buffer.into(), mem::size_of::() as u64, ); + if reset_reservoirs { - if !enable_debug_draw { + if !enable_debug_draw && !reset_debug_buffer { transfer.fill_buffer(self.debug.buffer.at(4), 4, 0); } let total_reservoirs = self.screen_size.width as u64 * self.screen_size.height as u64; diff --git a/blade-render/src/render/scene.rs b/blade-render/src/render/scene.rs deleted file mode 100644 index 6588cc70..00000000 --- a/blade-render/src/render/scene.rs +++ /dev/null @@ -1,53 +0,0 @@ -impl crate::Scene { - pub(super) fn build_top_level_acceleration_structure( - &self, - command_encoder: &mut blade_graphics::CommandEncoder, - asset_models: &blade_asset::AssetManager, - gpu: &blade_graphics::Context, - temp_buffers: &mut Vec, - ) -> (blade_graphics::AccelerationStructure, u32) { - let mut instances = Vec::with_capacity(self.objects.len()); - let mut blases = Vec::with_capacity(self.objects.len()); - let mut custom_index = 0; - - for object in self.objects.iter() { - let model = &asset_models[object.model]; - instances.push(blade_graphics::AccelerationStructureInstance { - acceleration_structure_index: blases.len() as u32, - transform: object.transform, - mask: 0xFF, - custom_index, - }); - blases.push(model.acceleration_structure); - custom_index += model.geometries.len() as u32; - } - - // Needs to be a separate encoder in order to force synchronization - let sizes = gpu.get_top_level_acceleration_structure_sizes(instances.len() as u32); - let acceleration_structure = - gpu.create_acceleration_structure(blade_graphics::AccelerationStructureDesc { - name: "TLAS", - ty: blade_graphics::AccelerationStructureType::TopLevel, - size: sizes.data, - }); - let instance_buf = gpu.create_acceleration_structure_instance_buffer(&instances, &blases); - let scratch_buf = gpu.create_buffer(blade_graphics::BufferDesc { - name: "TLAS scratch", - size: sizes.scratch, - memory: blade_graphics::Memory::Device, - }); - - let mut tlas_encoder = command_encoder.acceleration_structure(); - tlas_encoder.build_top_level( - acceleration_structure, - &blases, - instances.len() as u32, - instance_buf.at(0), - scratch_buf.at(0), - ); - - temp_buffers.push(instance_buf); - temp_buffers.push(scratch_buf); - (acceleration_structure, custom_index) - } -} diff --git a/examples/scene/main.rs b/examples/scene/main.rs index c8d5a1e7..23b3e70f 100644 --- a/examples/scene/main.rs +++ b/examples/scene/main.rs @@ -179,9 +179,11 @@ impl Example { }, ); load_finish.depend_on(model_task); - scene.objects.push(blade_render::Object { - model, + scene.roots.push(blade_render::Node { + label: String::new(), transform: config_model.transform, + models: vec![model], + children: Vec::new(), }); } @@ -236,7 +238,7 @@ impl Example { }, denoiser_enabled: true, denoiser_config: blade_render::DenoiserConfig { - num_passes: 2, + num_passes: 3, temporal_weight: 0.1, }, debug_blit: None, @@ -280,7 +282,7 @@ impl Example { if task.is_done() { log::info!("Scene is loaded"); let (_, scene) = self.pending_scene.take().unwrap(); - self.renderer.merge_scene(scene); + self.renderer.change_scene(scene); } }