From d79a4505ab711ad86a5c298be0439602b27f9a9a Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 29 Oct 2023 23:37:31 -0700 Subject: [PATCH 1/4] Refactor TLAS creation into the API, remove Scene --- blade-graphics/src/lib.rs | 21 ++ blade-render/src/lib.rs | 33 +-- blade-render/src/render/mod.rs | 408 +++++++++++++++++-------------- blade-render/src/render/scene.rs | 53 ---- examples/init/main.rs | 14 +- examples/scene/main.rs | 92 ++++--- 6 files changed, 308 insertions(+), 313 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..db414309 100644 --- a/blade-render/src/lib.rs +++ b/blade-render/src/lib.rs @@ -40,44 +40,15 @@ pub struct Camera { } pub struct Object { - pub model: blade_asset::Handle, pub transform: blade_graphics::Transform, + pub model: blade_asset::Handle, } impl From> for Object { fn from(model: blade_asset::Handle) -> Self { Self { + transform: blade_graphics::IDENTITY_TRANSFORM, 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(), - } - } -} - -#[derive(Clone, Debug)] -pub struct PostProcessing { - //TODO: remove this, compute automatically - pub average_luminocity: f32, - pub exposure_key_value: f32, - pub white_level: f32, -} -impl Default for PostProcessing { - fn default() -> Self { - Self { - average_luminocity: 1.0, - exposure_key_value: 1.0, - white_level: 1.0, } } } - -#[derive(Default)] -pub struct Scene { - pub objects: 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..34b06759 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; @@ -86,6 +85,23 @@ pub struct DenoiserConfig { pub temporal_weight: f32, } +#[derive(Clone, Copy, Debug)] +pub struct PostProcConfig { + //TODO: compute automatically + pub average_luminocity: f32, + pub exposure_key_value: f32, + pub white_level: f32, +} +impl Default for PostProcConfig { + fn default() -> Self { + Self { + average_luminocity: 1.0, + exposure_key_value: 1.0, + white_level: 1.0, + } + } +} + pub struct SelectionInfo { pub std_deviation: mint::Vector3, pub std_deviation_history: u32, @@ -125,7 +141,7 @@ struct DebugEntry { } struct DebugRender { - capacity: u32, + _capacity: u32, buffer: blade_graphics::Buffer, variance_buffer: blade_graphics::Buffer, entry_buffer: blade_graphics::Buffer, @@ -345,7 +361,6 @@ pub struct Renderer { main_pipeline: blade_graphics::ComputePipeline, post_proc_pipeline: blade_graphics::RenderPipeline, blur: Blur, - scene: super::Scene, acceleration_structure: blade_graphics::AccelerationStructure, env_map: EnvironmentMap, dummy: DummyResources, @@ -356,7 +371,6 @@ pub struct Renderer { samplers: Samplers, reservoir_size: u32, debug: DebugRender, - is_tlas_dirty: bool, screen_size: blade_graphics::Extent, frame_index: u32, //TODO: refactor `ResourceArray` to not carry the freelist logic @@ -744,7 +758,7 @@ impl Renderer { let sp = ShaderPipelines::init(&shaders, config, gpu, shader_man).unwrap(); let debug = DebugRender { - capacity: config.max_debug_lines, + _capacity: config.max_debug_lines, buffer: gpu.create_buffer(blade_graphics::BufferDesc { name: "debug", size: (sp.debug_buffer_size + (config.max_debug_lines - 1) * sp.debug_line_size) @@ -767,6 +781,9 @@ impl Renderer { buffer_size: sp.debug_buffer_size, }; + let debug_init_data = [2u32, 0, 0, config.max_debug_lines]; + let debug_init_size = debug_init_data.len() * mem::size_of::(); + assert!(debug_init_size <= mem::size_of::()); unsafe { ptr::write_bytes( debug.variance_buffer.data(), @@ -774,6 +791,20 @@ impl Renderer { mem::size_of::(), ); ptr::write_bytes(debug.entry_buffer.data(), 0, mem::size_of::()); + // piggyback on the staging buffers to upload the data + ptr::copy_nonoverlapping( + debug_init_data.as_ptr(), + debug.entry_buffer.data() as *mut u32, + debug_init_data.len(), + ); + } + { + let mut transfers = encoder.transfer(); + transfers.copy_buffer_to_buffer( + debug.entry_buffer.at(0), + debug.buffer.at(0), + debug_init_size as u64, + ); } let frame_data = [ @@ -821,7 +852,6 @@ impl Renderer { post_proc_input: blade_graphics::TextureView::default(), debug_texture, debug_view, - scene: super::Scene::default(), fill_pipeline: sp.fill, main_pipeline: sp.main, post_proc_pipeline: sp.post_proc, @@ -839,7 +869,6 @@ impl Renderer { samplers, reservoir_size: sp.reservoir_size, debug, - is_tlas_dirty: true, screen_size: config.screen_size, frame_index: 0, texture_resource_lookup: HashMap::default(), @@ -872,11 +901,6 @@ impl Renderer { gpu.destroy_buffer(self.debug.entry_buffer); } - pub fn merge_scene(&mut self, scene: super::Scene) { - self.scene = scene; - self.is_tlas_dirty = true; - } - #[profiling::function] pub fn hot_reload( &mut self, @@ -1000,192 +1024,209 @@ impl Renderer { self.debug_view = debug_view; } - /// Prepare to render a frame. #[profiling::function] - #[allow(clippy::too_many_arguments)] - pub fn prepare( + pub fn build_scene( &mut self, command_encoder: &mut blade_graphics::CommandEncoder, - camera: &super::Camera, + objects: &[crate::Object], + env_map: Option>, 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 (env_view, env_extent) = match env_map { + Some(handle) => { + let asset = &asset_hub.textures[handle]; + (asset.view, asset.extent) } + None => (self.dummy.white_view, blade_graphics::Extent::default()), + }; + self.env_map + .assign(env_view, env_extent, command_encoder, gpu); - 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"); + if self.acceleration_structure != blade_graphics::AccelerationStructure::default() { + temp_acceleration_structures.push(self.acceleration_structure); + } + + let geometry_count = objects + .iter() + .map(|object| { + let model = &asset_hub.models[object.model]; + model.geometries.len() + }) + .sum::(); + let hit_size = (geometry_count.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 hit_staging = gpu.create_buffer(blade_graphics::BufferDesc { + name: "hit staging", + size: hit_size, + memory: blade_graphics::Memory::Upload, + }); + temp_buffers.push(hit_staging); + { let mut transfers = command_encoder.transfer(); + transfers.copy_buffer_to_buffer(hit_staging.at(0), self.hit_buffer.at(0), hit_size); + } + + 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 geometry_index = 0; + let mut instances = Vec::with_capacity(objects.len()); + let mut blases = Vec::with_capacity(objects.len()); + let mut texture_indices = HashMap::new(); + + for object in objects { + let m3_object = glam::Mat3 { + x_axis: glam::Vec4::from(object.transform.x).truncate(), + y_axis: glam::Vec4::from(object.transform.y).truncate(), + z_axis: glam::Vec4::from(object.transform.z).truncate(), + }; + + let model = &asset_hub.models[object.model]; + instances.push(blade_graphics::AccelerationStructureInstance { + acceleration_structure_index: blases.len() as u32, + transform: object.transform, + mask: 0xFF, + custom_index: geometry_index as u32, + }); + blases.push(model.acceleration_structure); + + for geometry in model.geometries.iter() { + let material = &model.materials[geometry.material_index]; + let vertex_offset = + geometry.vertex_range.start as u64 * mem::size_of::() as u64; + let geometry_to_world_rotation = { + let colm = mint::ColumnMatrix3x4::from(geometry.transform); + let m3_geo = glam::Mat3 { + x_axis: colm.x.into(), + y_axis: colm.y.into(), + z_axis: colm.z.into(), + }; + let m3_normal = (m3_object * m3_geo).inverse().transpose(); + let quat = glam::Quat::from_mat3(&m3_normal); + let qv = glam::Vec4::from(quat) * 127.0; + [qv.x as i8, qv.y as i8, qv.z as i8, qv.w as i8] + }; + + let hit_entry = HitEntry { + index_buf: match geometry.index_type { + Some(_) => self + .index_buffers + .alloc(model.index_buffer.at(geometry.index_offset)), + None => !0, + }, + vertex_buf: self + .vertex_buffers + .alloc(model.vertex_buffer.at(vertex_offset)), + geometry_to_world_rotation, + unused: 0, + geometry_to_object: mint::ColumnMatrix4::from(mint::RowMatrix4 { + x: geometry.transform.x, + y: geometry.transform.y, + z: geometry.transform.z, + w: [0.0, 0.0, 0.0, 1.0].into(), + }), + base_color_texture: match material.base_color_texture { + Some(handle) => *texture_indices.entry(handle).or_insert_with(|| { + let texture = &asset_hub.textures[handle]; + self.textures.alloc(texture.view) + }), + None => dummy_white, + }, + base_color_factor: { + let c = material.base_color_factor; + [ + (c[0] * 255.0) as u8, + (c[1] * 255.0) as u8, + (c[2] * 255.0) as u8, + (c[3] * 255.0) as u8, + ] + }, + normal_texture: match material.normal_texture { + Some(handle) => *texture_indices.entry(handle).or_insert_with(|| { + let texture = &asset_hub.textures[handle]; + self.textures.alloc(texture.view) + }), + None => dummy_black, + }, + finish_pad: [0; 1], + }; - { - // 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, - }); + log::debug!("Entry[{geometry_index}] = {hit_entry:?}"); unsafe { - ptr::write(staging.data() as *mut _, data); + ptr::write( + (hit_staging.data() as *mut HitEntry).add(geometry_index), + hit_entry, + ); } - transfers.copy_buffer_to_buffer(staging.into(), self.debug.buffer.into(), size); - temp_buffers.push(staging); + geometry_index += 1; } + } - 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.texture_resource_lookup.clear(); + for (handle, res_id) in texture_indices { + self.texture_resource_lookup.insert(res_id, handle); + } - 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()) - } + assert_eq!(geometry_index, geometry_count); + log::info!( + "Preparing ray tracing with {} geometries in total", + geometry_count + ); - 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 - * mem::size_of::() as u64; - let geometry_to_world_rotation = { - let m3_geo = extract_matrix3(geometry.transform); - let m3_normal = (m3_object * m3_geo).inverse().transpose(); - let quat = glam::Quat::from_mat3(&m3_normal); - let qv = glam::Vec4::from(quat) * 127.0; - [qv.x as i8, qv.y as i8, qv.z as i8, qv.w as i8] - }; - fn extend(v: mint::Vector3) -> mint::Vector4 { - mint::Vector4 { - x: v.x, - y: v.y, - z: v.z, - w: 0.0, - } - } - - let hit_entry = HitEntry { - index_buf: match geometry.index_type { - Some(_) => self - .index_buffers - .alloc(model.index_buffer.at(geometry.index_offset)), - None => !0, - }, - vertex_buf: self - .vertex_buffers - .alloc(model.vertex_buffer.at(vertex_offset)), - geometry_to_world_rotation, - unused: 0, - geometry_to_object: { - let m = mint::ColumnMatrix3x4::from(geometry.transform); - mint::ColumnMatrix4 { - x: extend(m.x), - y: extend(m.y), - z: extend(m.z), - w: extend(m.w), - } - }, - base_color_texture: match material.base_color_texture { - Some(handle) => { - *texture_indices.entry(handle).or_insert_with(|| { - let texture = &asset_hub.textures[handle]; - self.textures.alloc(texture.view) - }) - } - None => dummy_white, - }, - base_color_factor: { - let c = material.base_color_factor; - [ - (c[0] * 255.0) as u8, - (c[1] * 255.0) as u8, - (c[2] * 255.0) as u8, - (c[3] * 255.0) as u8, - ] - }, - normal_texture: match material.normal_texture { - Some(handle) => { - *texture_indices.entry(handle).or_insert_with(|| { - let texture = &asset_hub.textures[handle]; - self.textures.alloc(texture.view) - }) - } - None => dummy_black, - }, - 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; - } - } - assert_eq!(geometry_index, geometry_count as usize); + // 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, + }); - 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, - }); - } - } + 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), + ); - if let Some(handle) = self.scene.environment_map { - let asset = &asset_hub.textures[handle]; - self.env_map - .assign(asset.view, asset.extent, command_encoder, gpu); - }; + temp_buffers.push(instance_buf); + temp_buffers.push(scratch_buf); + } + /// Prepare to render a frame. + #[profiling::function] + pub fn prepare( + &mut self, + command_encoder: &mut blade_graphics::CommandEncoder, + camera: &crate::Camera, + enable_debug_draw: bool, + accumulate_variance: bool, + reset_reservoirs: bool, + ) { let mut transfer = command_encoder.transfer(); + if enable_debug_draw { // reset the debug line count transfer.fill_buffer(self.debug.buffer.at(4), 4, 0); @@ -1193,6 +1234,7 @@ impl Renderer { } 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,6 +1256,7 @@ impl Renderer { self.debug.entry_buffer.into(), mem::size_of::() as u64, ); + if reset_reservoirs { if !enable_debug_draw { transfer.fill_buffer(self.debug.buffer.at(4), 4, 0); @@ -1269,7 +1312,6 @@ impl Renderer { debug_config: DebugConfig, ray_config: RayConfig, ) { - assert!(!self.is_tlas_dirty); let debug = self.make_debug_params(&debug_config); let cur = self.frame_data.first().unwrap(); let prev = self.frame_data.last().unwrap(); @@ -1417,9 +1459,9 @@ impl Renderer { &self, pass: &mut blade_graphics::RenderCommandEncoder, debug_config: DebugConfig, + pp_config: PostProcConfig, debug_blits: &[DebugBlit], ) { - let pp = &self.scene.post_processing; let cur = self.frame_data.first().unwrap(); if let mut pc = pass.with(&self.post_proc_pipeline) { let debug_params = self.make_debug_params(&debug_config); @@ -1431,9 +1473,9 @@ impl Renderer { t_debug: self.debug_view, tone_map_params: ToneMapParams { enabled: 1, - average_lum: pp.average_luminocity, - key_value: pp.exposure_key_value, - white_level: pp.white_level, + average_lum: pp_config.average_luminocity, + key_value: pp_config.exposure_key_value, + white_level: pp_config.white_level, }, debug_params, }, @@ -1506,8 +1548,4 @@ impl Renderer { .cloned(), } } - - pub fn configure_post_processing(&mut self) -> &mut crate::PostProcessing { - &mut self.scene.post_processing - } } 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/init/main.rs b/examples/init/main.rs index af6e40cb..1325113d 100644 --- a/examples/init/main.rs +++ b/examples/init/main.rs @@ -151,8 +151,9 @@ fn main() { .collect::>(); let mut asset_hub = blade_render::AssetHub::new(Path::new("asset-cache"), &choir, &context); + let mut environment_map = None; + let mut _object = None; - let mut scene = blade_render::Scene::default(); println!("Populating the scene"); let mut load_finish = choir.spawn("load finish").init_dummy(); let (shader_handle, shader_task) = asset_hub @@ -169,7 +170,7 @@ fn main() { }; let (texture, texture_task) = asset_hub.textures.load(arg, meta); load_finish.depend_on(texture_task); - scene.environment_map = Some(texture); + environment_map = Some(texture); } else if arg.ends_with(".gltf") { println!("\tmodels += {}", arg); let (model, model_task) = asset_hub.models.load( @@ -179,7 +180,7 @@ fn main() { }, ); load_finish.depend_on(model_task); - scene.objects.push(model.into()); + _object = Some(blade_render::Object::from(model)); } else { print!("\tunrecognized: {}", arg); } @@ -198,7 +199,7 @@ fn main() { asset_hub.flush(&mut command_encoder, &mut temp_buffers); let mut env_map = blade_render::EnvironmentMap::new(&dummy, &context); - let env_size = match scene.environment_map { + let env_size = match environment_map { Some(handle) => { let texture = &asset_hub.textures[handle]; env_map.assign(texture.view, texture.extent, &mut command_encoder, &context); @@ -212,12 +213,11 @@ fn main() { let sync_point = context.submit(&mut command_encoder); context.wait_for(&sync_point, !0); + context.destroy_command_encoder(command_encoder); for buffer in temp_buffers { context.destroy_buffer(buffer); } - if scene.environment_map.is_some() { - env_map.destroy(&context); - } + env_map.destroy(&context); env_sampler.destroy(&context); dummy.destroy(&context); asset_hub.destroy(); diff --git a/examples/scene/main.rs b/examples/scene/main.rs index c8d5a1e7..4df0c2dc 100644 --- a/examples/scene/main.rs +++ b/examples/scene/main.rs @@ -71,11 +71,13 @@ struct Example { prev_acceleration_structures: Vec, prev_sync_point: Option, renderer: Renderer, - pending_scene: Option<(choir::RunningTask, blade_render::Scene)>, + scene_load_task: Option, gui_painter: blade_egui::GuiPainter, command_encoder: Option, asset_hub: AssetHub, context: Arc, + environment_map: Option>, + objects: Vec, camera: blade_render::Camera, fly_speed: f32, debug: blade_render::DebugConfig, @@ -87,6 +89,7 @@ struct Example { ray_config: blade_render::RayConfig, denoiser_enabled: bool, denoiser_config: blade_render::DenoiserConfig, + post_proc_config: blade_render::PostProcConfig, debug_blit: Option, debug_blit_input: DebugBlitInput, workers: Vec, @@ -150,13 +153,8 @@ impl Example { depth: config_scene.camera.max_depth, }; - let mut scene = blade_render::Scene::default(); - scene.post_processing = blade_render::PostProcessing { - average_luminocity: config_scene.average_luminocity, - exposure_key_value: 1.0 / 9.6, - white_level: 1.0, - }; - + let mut environment_map = None; + let mut objects = Vec::new(); let parent = scene_path.parent().unwrap(); let mut load_finish = choir.spawn("load finish").init_dummy(); if !config_scene.environment_map.is_empty() { @@ -169,7 +167,7 @@ impl Example { .textures .load(parent.join(&config_scene.environment_map), meta); load_finish.depend_on(texture_task); - scene.environment_map = Some(texture); + environment_map = Some(texture); } for config_model in config_scene.models { let (model, model_task) = asset_hub.models.load( @@ -179,9 +177,9 @@ impl Example { }, ); load_finish.depend_on(model_task); - scene.objects.push(blade_render::Object { - model, + objects.push(blade_render::Object { transform: config_model.transform, + model, }); } @@ -213,11 +211,13 @@ impl Example { prev_acceleration_structures: Vec::new(), prev_sync_point: Some(sync_point), renderer, - pending_scene: Some((load_finish.run(), scene)), + scene_load_task: Some(load_finish.run()), gui_painter, command_encoder: Some(command_encoder), asset_hub, context, + environment_map, + objects, camera, fly_speed: config_scene.camera.speed, debug: blade_render::DebugConfig::default(), @@ -236,9 +236,14 @@ impl Example { }, denoiser_enabled: true, denoiser_config: blade_render::DenoiserConfig { - num_passes: 2, + num_passes: 3, temporal_weight: 0.1, }, + post_proc_config: blade_render::PostProcConfig { + average_luminocity: config_scene.average_luminocity, + exposure_key_value: 1.0 / 9.6, + white_level: 1.0, + }, debug_blit: None, debug_blit_input: DebugBlitInput::None, workers, @@ -276,14 +281,6 @@ impl Example { physical_size: winit::dpi::PhysicalSize, scale_factor: f32, ) { - if let Some((ref task, _)) = self.pending_scene { - if task.is_done() { - log::info!("Scene is loaded"); - let (_, scene) = self.pending_scene.take().unwrap(); - self.renderer.merge_scene(scene); - } - } - if self.track_hot_reloads { self.need_accumulation_reset |= self.renderer.hot_reload( &self.asset_hub, @@ -316,17 +313,29 @@ impl Example { let mut temp_buffers = Vec::new(); let mut temp_acceleration_structures = Vec::new(); self.asset_hub.flush(command_encoder, &mut temp_buffers); + + if let Some(ref task) = self.scene_load_task { + if task.is_done() { + log::info!("Scene is loaded"); + self.scene_load_task = None; + self.renderer.build_scene( + command_encoder, + &self.objects, + self.environment_map, + &self.asset_hub, + &self.context, + &mut temp_buffers, + &mut temp_acceleration_structures, + ); + } + } //TODO: remove these checks. // We should be able to update TLAS and render content // even while it's still being loaded. - if self.pending_scene.is_none() { + if self.scene_load_task.is_none() { self.renderer.prepare( command_encoder, &self.camera, - &self.asset_hub, - &self.context, - &mut temp_buffers, - &mut temp_acceleration_structures, self.is_debug_drawing, self.debug.mouse_pos.is_some(), self.need_accumulation_reset, @@ -354,7 +363,7 @@ impl Example { physical_size: (physical_size.width, physical_size.height), scale_factor, }; - if self.pending_scene.is_none() { + if self.scene_load_task.is_none() { let mut debug_blit_array = [blade_render::DebugBlit::default()]; let debug_blits = match self.debug_blit { Some(ref blit) => { @@ -363,7 +372,8 @@ impl Example { } None => &[], }; - self.renderer.post_proc(&mut pass, self.debug, debug_blits); + self.renderer + .post_proc(&mut pass, self.debug, self.post_proc_config, debug_blits); } self.gui_painter .paint(&mut pass, gui_primitives, &screen_desc, &self.context); @@ -391,7 +401,7 @@ impl Example { } self.render_times.push_front(delta.as_millis() as u32); - if self.pending_scene.is_some() { + if self.scene_load_task.is_some() { ui.horizontal(|ui| { ui.label("Loading..."); ui.spinner(); @@ -620,18 +630,26 @@ impl Example { }); egui::CollapsingHeader::new("Tone Map").show(ui, |ui| { - let pp = self.renderer.configure_post_processing(); ui.add( - egui::Slider::new(&mut pp.average_luminocity, 0.1f32..=1_000f32) - .text("Average luminocity") - .logarithmic(true), + egui::Slider::new( + &mut self.post_proc_config.average_luminocity, + 0.1f32..=1_000f32, + ) + .text("Average luminocity") + .logarithmic(true), ); ui.add( - egui::Slider::new(&mut pp.exposure_key_value, 0.01f32..=10f32) - .text("Key value") - .logarithmic(true), + egui::Slider::new( + &mut self.post_proc_config.exposure_key_value, + 0.01f32..=10f32, + ) + .text("Key value") + .logarithmic(true), + ); + ui.add( + egui::Slider::new(&mut self.post_proc_config.white_level, 0.1f32..=2f32) + .text("White level"), ); - ui.add(egui::Slider::new(&mut pp.white_level, 0.1f32..=2f32).text("White level")); }); egui::CollapsingHeader::new("Performance").show(ui, |ui| { From d47e1421e1855c2dbab833c2612ea4d657b4d880 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 30 Oct 2023 23:08:17 -0700 Subject: [PATCH 2/4] Update to naga-0.14 --- Cargo.toml | 3 +-- blade-graphics/src/metal/pipeline.rs | 4 ++-- blade-graphics/src/vulkan/pipeline.rs | 3 ++- blade-render/code/fill-gbuf.wgsl | 5 ++++- blade-render/code/ray-trace.wgsl | 6 +++++- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e8cd2973..e0f722ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,7 @@ egui = "0.22" glam = { version = "0.23", features = ["mint"] } log = "0.4" mint = "0.5" -# Need 0.12.2 since this is where buffer arrays are supported -naga = { version = "0.12.2", features = ["wgsl-in", "span", "validate"] } +naga = { version = "0.14", features = ["wgsl-in", "span", "validate"] } profiling = "1" strum = { version = "0.25", features = ["derive"] } web-sys = "0.3.60" diff --git a/blade-graphics/src/metal/pipeline.rs b/blade-graphics/src/metal/pipeline.rs index c0b95a12..ce9a7b5a 100644 --- a/blade-graphics/src/metal/pipeline.rs +++ b/blade-graphics/src/metal/pipeline.rs @@ -191,7 +191,7 @@ impl super::Context { let naga_stage = sf.shader.module.entry_points[ep_index].stage; let mut module = sf.shader.module.clone(); let mut layouter = naga::proc::Layouter::default(); - layouter.update(&module.types, &module.constants).unwrap(); + layouter.update(module.to_ctx()).unwrap(); for (handle, var) in module.global_variables.iter_mut() { if ep_info[handle].is_empty() { @@ -301,7 +301,7 @@ impl super::Context { }; let pipeline_options = msl::PipelineOptions { - allow_point_size: flags.contains(ShaderFlags::ALLOW_POINT_SIZE), + allow_and_force_point_size: flags.contains(ShaderFlags::ALLOW_POINT_SIZE), }; let (source, info) = msl::write_string(&module, &sf.shader.info, &naga_options, &pipeline_options).unwrap(); diff --git a/blade-graphics/src/vulkan/pipeline.rs b/blade-graphics/src/vulkan/pipeline.rs index 7235360e..47b7058e 100644 --- a/blade-graphics/src/vulkan/pipeline.rs +++ b/blade-graphics/src/vulkan/pipeline.rs @@ -61,6 +61,7 @@ impl super::Context { capabilities: None, bounds_check_policies: naga::proc::BoundsCheckPolicies::default(), zero_initialize_workgroup_memory: spv::ZeroInitializeWorkgroupMemoryMode::None, + debug_info: None, } } @@ -77,7 +78,7 @@ impl super::Context { let mut module = sf.shader.module.clone(); let mut layouter = naga::proc::Layouter::default(); - layouter.update(&module.types, &module.constants).unwrap(); + layouter.update(module.to_ctx()).unwrap(); for (handle, var) in module.global_variables.iter_mut() { if ep_info[handle].is_empty() { diff --git a/blade-render/code/fill-gbuf.wgsl b/blade-render/code/fill-gbuf.wgsl index 1dde3ce9..4f777903 100644 --- a/blade-render/code/fill-gbuf.wgsl +++ b/blade-render/code/fill-gbuf.wgsl @@ -3,6 +3,9 @@ #include "debug.inc.wgsl" #include "debug-param.inc.wgsl" +//TODO: use proper WGSL +const RAY_FLAG_CULL_NO_OPAQUE: u32 = 0x80u; + // Has to match the host! struct Vertex { pos: vec3, @@ -64,7 +67,7 @@ fn main(@builtin(global_invocation_id) global_id: vec3) { var rq: ray_query; let ray_dir = get_ray_direction(camera, vec2(global_id.xy)); - rayQueryInitialize(&rq, acc_struct, RayDesc(0x90u, 0xFFu, 0.0, camera.depth, camera.position, ray_dir)); + rayQueryInitialize(&rq, acc_struct, RayDesc(RAY_FLAG_CULL_NO_OPAQUE, 0xFFu, 0.0, camera.depth, camera.position, ray_dir)); rayQueryProceed(&rq); let intersection = rayQueryGetCommittedIntersection(&rq); diff --git a/blade-render/code/ray-trace.wgsl b/blade-render/code/ray-trace.wgsl index 8d52bfc0..e262761d 100644 --- a/blade-render/code/ray-trace.wgsl +++ b/blade-render/code/ray-trace.wgsl @@ -6,6 +6,9 @@ #include "camera.inc.wgsl" #include "surface.inc.wgsl" +//TODO: use proper WGSL +const RAY_FLAG_CULL_NO_OPAQUE: u32 = 0x80u; + const PI: f32 = 3.1415926; const MAX_RESERVOIRS: u32 = 2u; // See "9.1 pairwise mis for robust reservoir reuse" @@ -218,8 +221,9 @@ fn evaluate_brdf(surface: Surface, dir: vec3) -> f32 { fn check_ray_occluded(position: vec3, direction: vec3, debug_len: f32) -> bool { let start_t = 0.5; // some offset required to avoid self-shadowing var rq: ray_query; + let flags = RAY_FLAG_TERMINATE_ON_FIRST_HIT | RAY_FLAG_CULL_NO_OPAQUE; rayQueryInitialize(&rq, acc_struct, - RayDesc(RAY_FLAG_TERMINATE_ON_FIRST_HIT | 0x80u, 0xFFu, start_t, camera.depth, position, direction) + RayDesc(flags, 0xFFu, start_t, camera.depth, position, direction) ); rayQueryProceed(&rq); let intersection = rayQueryGetCommittedIntersection(&rq); From 2c819fe3c027f44fffa9fefd220f3437c456f8ed Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 30 Oct 2023 23:13:32 -0700 Subject: [PATCH 3/4] Read custom index and depth from the selection --- blade-render/code/debug.inc.wgsl | 2 ++ blade-render/code/fill-gbuf.wgsl | 2 ++ blade-render/src/render/mod.rs | 8 ++++++++ examples/scene/main.rs | 7 +++++++ 4 files changed, 19 insertions(+) diff --git a/blade-render/code/debug.inc.wgsl b/blade-render/code/debug.inc.wgsl index 5a2e4382..00ec256f 100644 --- a/blade-render/code/debug.inc.wgsl +++ b/blade-render/code/debug.inc.wgsl @@ -12,6 +12,8 @@ struct DebugVariance { count: u32, } struct DebugEntry { + custom_index: u32, + depth: f32, tex_coords: vec2, base_color_texture: u32, normal_texture: u32, diff --git a/blade-render/code/fill-gbuf.wgsl b/blade-render/code/fill-gbuf.wgsl index 4f777903..0c53e56b 100644 --- a/blade-render/code/fill-gbuf.wgsl +++ b/blade-render/code/fill-gbuf.wgsl @@ -124,6 +124,8 @@ fn main(@builtin(global_invocation_id) global_id: vec3) { let hit_position = camera.position + intersection.t * ray_dir; if (enable_debug) { + debug_buf.entry.custom_index = intersection.instance_custom_index; + debug_buf.entry.depth = intersection.t; debug_buf.entry.tex_coords = tex_coords; debug_buf.entry.base_color_texture = entry.base_color_texture; debug_buf.entry.normal_texture = entry.normal_texture; diff --git a/blade-render/src/render/mod.rs b/blade-render/src/render/mod.rs index 34b06759..c9b13671 100644 --- a/blade-render/src/render/mod.rs +++ b/blade-render/src/render/mod.rs @@ -105,6 +105,8 @@ impl Default for PostProcConfig { pub struct SelectionInfo { pub std_deviation: mint::Vector3, pub std_deviation_history: u32, + pub custom_index: u32, + pub depth: f32, pub tex_coords: mint::Vector2, pub base_color_texture: Option>, pub normal_texture: Option>, @@ -114,6 +116,8 @@ impl Default for SelectionInfo { Self { std_deviation: [0.0; 3].into(), std_deviation_history: 0, + custom_index: 0, + depth: 0.0, tex_coords: [0.0; 2].into(), base_color_texture: None, normal_texture: None, @@ -135,6 +139,8 @@ struct DebugVariance { #[repr(C)] #[derive(Debug)] struct DebugEntry { + custom_index: u32, + depth: f32, tex_coords: [f32; 2], base_color_texture: u32, normal_texture: u32, @@ -1537,6 +1543,8 @@ impl Renderer { } }, std_deviation_history: db_v.count, + custom_index: db_e.custom_index, + depth: db_e.depth, tex_coords: db_e.tex_coords.into(), base_color_texture: self .texture_resource_lookup diff --git a/examples/scene/main.rs b/examples/scene/main.rs index 4df0c2dc..daf83d7a 100644 --- a/examples/scene/main.rs +++ b/examples/scene/main.rs @@ -497,6 +497,13 @@ impl Example { ui.colored_label(egui::Color32::WHITE, format!("2^{}", power)); self.need_accumulation_reset |= ui.button("Reset").clicked(); }); + ui.horizontal(|ui| { + ui.label("Depth:"); + ui.colored_label( + egui::Color32::WHITE, + format!("{:.2}", selection.depth), + ); + }); ui.horizontal(|ui| { ui.label("Texture coords:"); ui.colored_label( From b4f5d5116905f9e28e73d41872a93826f5724d93 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 31 Oct 2023 00:16:33 -0700 Subject: [PATCH 4/4] Egui gizmo integration --- Cargo.toml | 8 ++-- examples/scene/main.rs | 91 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 87 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e0f722ea..a847bfcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,8 @@ base64 = "0.21" bitflags = "2" bytemuck = { version = "1", features = ["derive"] } choir = { git = "https://github.com/kvark/choir", rev = "2d7c6ffb74a21a34c18c70aeb0b93a61a42eb04f" } -egui = "0.22" -glam = { version = "0.23", features = ["mint"] } +egui = "0.23" +glam = { version = "0.24", features = ["mint"] } log = "0.4" mint = "0.5" naga = { version = "0.14", features = ["wgsl-in", "span", "validate"] } @@ -63,7 +63,9 @@ strum = { workspace = true } winit = "0.28" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] -egui-winit = "0.22" +egui-winit = "0.23" +egui_plot = "0.23" +egui-gizmo = "0.12" env_logger = "0.10" [target.'cfg(target_arch = "wasm32")'.dev-dependencies] diff --git a/examples/scene/main.rs b/examples/scene/main.rs index daf83d7a..d34db16c 100644 --- a/examples/scene/main.rs +++ b/examples/scene/main.rs @@ -78,6 +78,7 @@ struct Example { context: Arc, environment_map: Option>, objects: Vec, + have_objects_changed: bool, camera: blade_render::Camera, fly_speed: f32, debug: blade_render::DebugConfig, @@ -218,6 +219,7 @@ impl Example { context, environment_map, objects, + have_objects_changed: false, camera, fly_speed: config_scene.camera.speed, debug: blade_render::DebugConfig::default(), @@ -318,6 +320,15 @@ impl Example { if task.is_done() { log::info!("Scene is loaded"); self.scene_load_task = None; + self.have_objects_changed = true; + } + } + + //TODO: remove these checks. + // We should be able to update TLAS and render content + // even while it's still being loaded. + if self.scene_load_task.is_none() { + if self.have_objects_changed { self.renderer.build_scene( command_encoder, &self.objects, @@ -327,12 +338,9 @@ impl Example { &mut temp_buffers, &mut temp_acceleration_structures, ); + self.have_objects_changed = false; } - } - //TODO: remove these checks. - // We should be able to update TLAS and render content - // even while it's still being loaded. - if self.scene_load_task.is_none() { + self.renderer.prepare( command_encoder, &self.camera, @@ -341,6 +349,7 @@ impl Example { self.need_accumulation_reset, ); self.need_accumulation_reset = false; + self.renderer .ray_trace(command_encoder, self.debug, self.ray_config); if self.denoiser_enabled { @@ -464,10 +473,12 @@ impl Example { ui.checkbox(&mut enabled, name); self.debug.texture_flags.set(bit, enabled); } + // selection info let mut selection = blade_render::SelectionInfo::default(); if let Some(screen_pos) = self.debug.mouse_pos { selection = self.renderer.read_debug_selection_info(); + let style = ui.style(); egui::Frame::group(style).show(ui, |ui| { ui.horizontal(|ui| { @@ -539,7 +550,57 @@ impl Example { } }); }); + + let view_matrix = glam::Mat4::from_rotation_translation( + self.camera.rot.into(), + self.camera.pos.into(), + ) + .inverse(); + let extent = self.renderer.get_screen_size(); + let aspect = extent.width as f32 / extent.height as f32; + let projection_matrix = glam::Mat4::perspective_rh( + self.camera.fov_y, + aspect, + 1.0, + self.camera.depth, + ); + let obj_index = self.find_object(selection.custom_index); + let model_matrix = mint::ColumnMatrix4::from({ + let t = self.objects[obj_index].transform; + mint::RowMatrix4 { + x: t.x, + y: t.y, + z: t.z, + w: mint::Vector4 { + x: 0.0, + y: 0.0, + z: 0.0, + w: 1.0, + }, + } + }); + let gizmo = egui_gizmo::Gizmo::new("Object") + .view_matrix(mint::ColumnMatrix4::from(view_matrix)) + .projection_matrix(mint::ColumnMatrix4::from(projection_matrix)) + .model_matrix(mint::ColumnMatrix4::from(model_matrix)) + .orientation(egui_gizmo::GizmoOrientation::Global) + .snapping(true); + if let Some(response) = gizmo.interact(ui) { + let m = glam::Mat4::from_scale_rotation_translation( + response.scale, + response.rotation, + response.translation, + ) + .transpose(); + self.objects[obj_index].transform = blade_graphics::Transform { + x: m.x_axis.into(), + y: m.y_axis.into(), + z: m.z_axis.into(), + }; + self.have_objects_changed = true; + } } + // blits ui.label("Debug blit:"); egui::ComboBox::from_label("Input") @@ -661,7 +722,7 @@ impl Example { egui::CollapsingHeader::new("Performance").show(ui, |ui| { let times = self.render_times.as_slices(); - let fd_points = egui::plot::PlotPoints::from_iter( + let fd_points = egui_plot::PlotPoints::from_iter( times .0 .iter() @@ -669,8 +730,8 @@ impl Example { .enumerate() .map(|(x, &y)| [x as f64, y as f64]), ); - let fd_line = egui::plot::Line::new(fd_points).name("last"); - egui::plot::Plot::new("Frame time") + let fd_line = egui_plot::Line::new(fd_points).name("last"); + egui_plot::Plot::new("Frame time") .allow_zoom(false) .allow_scroll(false) .allow_drag(false) @@ -679,11 +740,23 @@ impl Example { .show_axes([false, true]) .show(ui, |plot_ui| { plot_ui.line(fd_line); - plot_ui.hline(egui::plot::HLine::new(1000.0 / 60.0).name("smooth")); + plot_ui.hline(egui_plot::HLine::new(1000.0 / 60.0).name("smooth")); }); }); } + fn find_object(&self, geometry_index: u32) -> usize { + let mut index = geometry_index as usize; + for (i, object) in self.objects.iter().enumerate() { + let model = &self.asset_hub.models[object.model]; + match index.checked_sub(model.geometries.len()) { + Some(i) => index = i, + None => return i, + } + } + panic!("Object with geometry index {geometry_index} is not found"); + } + fn move_camera_by(&mut self, offset: glam::Vec3) { let dir = glam::Quat::from(self.camera.rot) * offset; self.camera.pos = (glam::Vec3::from(self.camera.pos) + dir).into();