From 1bf595bb9d3eb557ef037cbf04dac7fb85f41147 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 24 Oct 2023 23:30:58 -0700 Subject: [PATCH 1/2] Separate debug view target --- blade-render/code/fill-gbuf.wgsl | 11 ++++-- blade-render/code/post-proc.wgsl | 31 +++++++++------ blade-render/code/ray-trace.wgsl | 6 +-- blade-render/src/asset_hub.rs | 1 + blade-render/src/render/mod.rs | 66 ++++++++++++++++++++++++++++---- examples/scene/data/scene.ron | 2 +- examples/scene/main.rs | 7 +++- 7 files changed, 95 insertions(+), 29 deletions(-) diff --git a/blade-render/code/fill-gbuf.wgsl b/blade-render/code/fill-gbuf.wgsl index f379c8a0..9d43c382 100644 --- a/blade-render/code/fill-gbuf.wgsl +++ b/blade-render/code/fill-gbuf.wgsl @@ -3,8 +3,6 @@ #include "debug.inc.wgsl" #include "debug-param.inc.wgsl" -const DEBUG_CONSISTENCY: bool = false; - // Has to match the host! struct Vertex { pos: vec3, @@ -46,6 +44,7 @@ var out_depth: texture_storage_2d; var out_flat_normal: texture_storage_2d; var out_basis: texture_storage_2d; var out_albedo: texture_storage_2d; +var out_debug: texture_storage_2d; fn decode_normal(raw: u32) -> vec3 { return unpack4x8snorm(raw).xyz; @@ -149,16 +148,20 @@ fn main(@builtin(global_invocation_id) global_id: vec3) { let base_color_sample = textureSampleLevel(textures[entry.base_color_texture], sampler_linear, tex_coords, lod); albedo = (base_color_factor * base_color_sample).xyz; } - if (DEBUG_CONSISTENCY) { + if (debug.view_mode == DebugMode_HitConsistency) { let reprojected = get_projected_pixel(camera, hit_position); let barycentrics_pos_diff = positions * barycentrics - hit_position; let camera_projection_diff = vec2(global_id.xy) - vec2(reprojected); - albedo = vec3(length(barycentrics_pos_diff), length(camera_projection_diff), 0.0); + let consistency = vec4(length(barycentrics_pos_diff), length(camera_projection_diff), 0.0, 0.0); + textureStore(out_debug, global_id.xy, consistency); } } else { if (enable_debug) { debug_buf.entry = DebugEntry(); } + if (debug.view_mode != DebugMode_Final) { + textureStore(out_debug, global_id.xy, vec4(0.0)); + } } textureStore(out_depth, global_id.xy, vec4(depth, 0.0, 0.0, 0.0)); diff --git a/blade-render/code/post-proc.wgsl b/blade-render/code/post-proc.wgsl index 3c5cd41f..c0eea300 100644 --- a/blade-render/code/post-proc.wgsl +++ b/blade-render/code/post-proc.wgsl @@ -1,5 +1,7 @@ +#use PostProcMode + struct ToneMapParams { - enabled: u32, + mode: u32, average_lum: f32, key_value: f32, // minimum value of the pixels mapped to white brightness @@ -8,6 +10,7 @@ struct ToneMapParams { var t_albedo: texture_2d; var light_diffuse: texture_2d; +var t_debug: texture_2d; var tone_map_params: ToneMapParams; struct VertexOutput { @@ -25,17 +28,21 @@ fn blit_vs(@builtin(vertex_index) vi: u32) -> VertexOutput { @fragment fn blit_fs(vo: VertexOutput) -> @location(0) vec4 { - let tc = vec2(i32(vo.clip_pos.x), i32(vo.input_size.y) - i32(vo.clip_pos.y)); - let albedo = textureLoad(t_albedo, tc, 0); - let radiance = textureLoad(light_diffuse, tc, 0); - let color = albedo * radiance; - if (tone_map_params.enabled != 0u) { - // Following https://blog.en.uwa4d.com/2022/07/19/physically-based-renderingg-hdr-tone-mapping/ - let l_adjusted = tone_map_params.key_value / tone_map_params.average_lum * color.xyz; - let l_white = tone_map_params.white_level; - let l_ldr = l_adjusted * (1.0 + l_adjusted / (l_white*l_white)) / (1.0 + l_adjusted); - return vec4(l_ldr, 1.0); + let tc = vec2(i32(vo.clip_pos.x), i32(vo.input_size.y) - i32(vo.clip_pos.y) - 1); + if (tone_map_params.mode == PostProcMode_Debug) { + return textureLoad(t_debug, tc, 0); } else { - return color; + let albedo = textureLoad(t_albedo, tc, 0); + let radiance = textureLoad(light_diffuse, tc, 0); + let color = albedo * radiance; + if (tone_map_params.mode == PostProcMode_Tonemap) { + // Following https://blog.en.uwa4d.com/2022/07/19/physically-based-renderingg-hdr-tone-mapping/ + let l_adjusted = tone_map_params.key_value / tone_map_params.average_lum * color.xyz; + let l_white = tone_map_params.white_level; + let l_ldr = l_adjusted * (1.0 + l_adjusted / (l_white*l_white)) / (1.0 + l_adjusted); + return vec4(l_ldr, 1.0); + } else { + return color; + } } } diff --git a/blade-render/code/ray-trace.wgsl b/blade-render/code/ray-trace.wgsl index 253862fa..8b30b7b2 100644 --- a/blade-render/code/ray-trace.wgsl +++ b/blade-render/code/ray-trace.wgsl @@ -131,6 +131,7 @@ var t_prev_basis: texture_2d; var t_flat_normal: texture_2d; var t_prev_flat_normal: texture_2d; var out_diffuse: texture_storage_2d; +var out_debug: texture_storage_2d; fn sample_circle(random: f32) -> vec2 { let angle = 2.0 * PI * random; @@ -312,8 +313,7 @@ struct RestirOutput { fn compute_restir(surface: Surface, pixel: vec2, rng: ptr, enable_debug: bool) -> RestirOutput { if (debug.view_mode == DebugMode_Depth) { - let depth = vec3(surface.depth / camera.depth); - return RestirOutput(depth); + textureStore(out_debug, pixel, vec4(surface.depth / camera.depth)); } let ray_dir = get_ray_direction(camera, pixel); let pixel_index = get_reservoir_index(pixel, camera); @@ -327,7 +327,7 @@ fn compute_restir(surface: Surface, pixel: vec2, rng: ptr(0.0, 0.0, 1.0)); if (debug.view_mode == DebugMode_Normal) { - return RestirOutput(normal); + textureStore(out_debug, pixel, vec4(normal, 0.0)); } var canonical = LiveReservoir(); diff --git a/blade-render/src/asset_hub.rs b/blade-render/src/asset_hub.rs index aa297e52..60904e19 100644 --- a/blade-render/src/asset_hub.rs +++ b/blade-render/src/asset_hub.rs @@ -37,6 +37,7 @@ impl AssetHub { sh_baker.register_enum::(); sh_baker.register_bitflags::(); sh_baker.register_bitflags::(); + sh_baker.register_enum::(); let shaders = AssetManager::new(target, choir, sh_baker); Self { diff --git a/blade-render/src/render/mod.rs b/blade-render/src/render/mod.rs index 5bbb7fb6..84a42e49 100644 --- a/blade-render/src/render/mod.rs +++ b/blade-render/src/render/mod.rs @@ -25,17 +25,26 @@ struct Samplers { #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, blade_macros::AsPrimitive, strum::EnumIter)] #[repr(u32)] pub enum DebugMode { - None = 0, + Final = 0, Depth = 1, Normal = 2, + HitConsistency = 3, } impl Default for DebugMode { fn default() -> Self { - Self::None + Self::Final } } +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, blade_macros::AsPrimitive, strum::EnumIter)] +#[repr(u32)] +pub enum PostProcMode { + Tonemap = 0, + Reconstruct = 1, + Debug = 2, +} + bitflags::bitflags! { #[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd)] pub struct DebugDrawFlags: u32 { @@ -250,6 +259,7 @@ impl FrameData { let (depth, depth_view) = Self::create_target("depth", blade_graphics::TextureFormat::R32Float, size, gpu); encoder.init_texture(depth); + let (basis, basis_view) = Self::create_target( "basis", blade_graphics::TextureFormat::Rgba8Snorm, @@ -257,6 +267,7 @@ impl FrameData { gpu, ); encoder.init_texture(basis); + let (flat_normal, flat_normal_view) = Self::create_target( "flat_normal", blade_graphics::TextureFormat::Rgba8Snorm, @@ -264,6 +275,7 @@ impl FrameData { gpu, ); encoder.init_texture(flat_normal); + let (albedo, albedo_view) = Self::create_target( "basis", blade_graphics::TextureFormat::Rgba8Unorm, @@ -271,6 +283,7 @@ impl FrameData { gpu, ); encoder.init_texture(albedo); + Self { reservoir_buf, depth, @@ -312,6 +325,8 @@ pub struct Renderer { shaders: Shaders, frame_data: [FrameData; 2], lighting_diffuse: DoubleRenderTarget, + debug_texture: blade_graphics::Texture, + debug_view: blade_graphics::TextureView, fill_pipeline: blade_graphics::ComputePipeline, main_pipeline: blade_graphics::ComputePipeline, atrous_pipeline: blade_graphics::ComputePipeline, @@ -383,6 +398,7 @@ struct FillData<'a> { out_basis: blade_graphics::TextureView, out_flat_normal: blade_graphics::TextureView, out_albedo: blade_graphics::TextureView, + out_debug: blade_graphics::TextureView, } #[derive(blade_macros::ShaderData)] @@ -406,6 +422,7 @@ struct MainData { reservoirs: blade_graphics::BufferPiece, prev_reservoirs: blade_graphics::BufferPiece, out_diffuse: blade_graphics::TextureView, + out_debug: blade_graphics::TextureView, } #[repr(C)] @@ -426,16 +443,17 @@ struct AtrousData { #[repr(C)] #[derive(Clone, Copy, Default, bytemuck::Zeroable, bytemuck::Pod)] struct ToneMapParams { - enabled: u32, + mode: u32, average_lum: f32, key_value: f32, white_level: f32, } #[derive(blade_macros::ShaderData)] -struct BlitData { +struct PostProcData { t_albedo: blade_graphics::TextureView, light_diffuse: blade_graphics::TextureView, + t_debug: blade_graphics::TextureView, tone_map_params: ToneMapParams, } @@ -563,7 +581,7 @@ impl ShaderPipelines { format: blade_graphics::TextureFormat, gpu: &blade_graphics::Context, ) -> blade_graphics::RenderPipeline { - let layout = ::layout(); + let layout = ::layout(); gpu.create_render_pipeline(blade_graphics::RenderPipelineDesc { name: "main", data_layouts: &[&layout], @@ -712,6 +730,13 @@ impl Renderer { FrameData::new(config.screen_size, sp.reservoir_size, encoder, gpu), ]; let dummy = DummyResources::new(encoder, gpu); + let (debug_texture, debug_view) = FrameData::create_target( + "debug", + blade_graphics::TextureFormat::Rgba8Unorm, + config.screen_size, + gpu, + ); + encoder.init_texture(debug_texture); let samplers = Samplers { nearest: gpu.create_sampler(blade_graphics::SamplerDesc { @@ -743,6 +768,8 @@ impl Renderer { encoder, gpu, ), + debug_texture, + debug_view, scene: super::Scene::default(), fill_pipeline: sp.fill, main_pipeline: sp.main, @@ -772,6 +799,8 @@ impl Renderer { frame_data.destroy(gpu); } self.lighting_diffuse.destroy(gpu); + gpu.destroy_texture(self.debug_texture); + gpu.destroy_texture_view(self.debug_view); if self.hit_buffer != blade_graphics::Buffer::default() { gpu.destroy_buffer(self.hit_buffer); } @@ -889,9 +918,22 @@ impl Renderer { frame_data.destroy(gpu); *frame_data = FrameData::new(size, self.reservoir_size, encoder, gpu); } + self.lighting_diffuse.destroy(gpu); self.lighting_diffuse = DoubleRenderTarget::new("light/diffuse", RADIANCE_FORMAT, size, encoder, gpu); + + gpu.destroy_texture(self.debug_texture); + gpu.destroy_texture_view(self.debug_view); + let (debug_texture, debug_view) = FrameData::create_target( + "debug", + blade_graphics::TextureFormat::Rgba8Unorm, + size, + gpu, + ); + encoder.init_texture(debug_texture); + self.debug_texture = debug_texture; + self.debug_view = debug_view; } /// Prepare to render a frame. @@ -1180,6 +1222,7 @@ impl Renderer { out_basis: cur.basis_view, out_flat_normal: cur.flat_normal_view, out_albedo: cur.albedo_view, + out_debug: self.debug_view, }, ); pc.dispatch(groups); @@ -1219,6 +1262,7 @@ impl Renderer { reservoirs: cur.reservoir_buf.into(), prev_reservoirs: prev.reservoir_buf.into(), out_diffuse: self.lighting_diffuse.cur(), + out_debug: self.debug_view, }, ); pc.dispatch(groups); @@ -1254,17 +1298,23 @@ impl Renderer { } /// Blit the rendering result into a specified render pass. - pub fn blit(&self, pass: &mut blade_graphics::RenderCommandEncoder, debug_blits: &[DebugBlit]) { + pub fn post_proc( + &self, + pass: &mut blade_graphics::RenderCommandEncoder, + mode: PostProcMode, + debug_blits: &[DebugBlit], + ) { let pp = &self.scene.post_processing; let cur = self.frame_data.first().unwrap(); if let mut pc = pass.with(&self.blit_pipeline) { pc.bind( 0, - &BlitData { + &PostProcData { t_albedo: cur.albedo_view, light_diffuse: self.lighting_diffuse.cur(), + t_debug: self.debug_view, tone_map_params: ToneMapParams { - enabled: 1, + mode: mode as u32, average_lum: pp.average_luminocity, key_value: pp.exposure_key_value, white_level: pp.white_level, diff --git a/examples/scene/data/scene.ron b/examples/scene/data/scene.ron index b5afd906..284cbaa8 100644 --- a/examples/scene/data/scene.ron +++ b/examples/scene/data/scene.ron @@ -1,7 +1,7 @@ ( camera: ( position: (2.7, 1.6, 2.1), - orientation: (-0.04, 0.92, -0.05, -0.37), + orientation: (-0.07, 0.36, 0.01, 0.93), fov_y: 1.0, max_depth: 100.0, speed: 1000.0, diff --git a/examples/scene/main.rs b/examples/scene/main.rs index 36e48a3c..55856535 100644 --- a/examples/scene/main.rs +++ b/examples/scene/main.rs @@ -342,6 +342,11 @@ impl Example { scale_factor, }; if self.pending_scene.is_none() { + let pp_mode = if self.debug.view_mode == blade_render::DebugMode::default() { + blade_render::PostProcMode::Tonemap + } else { + blade_render::PostProcMode::Debug + }; let mut debug_blit_array = [blade_render::DebugBlit::default()]; let debug_blits = match self.debug_blit { Some(ref blit) => { @@ -350,7 +355,7 @@ impl Example { } None => &[], }; - self.renderer.blit(&mut pass, debug_blits); + self.renderer.post_proc(&mut pass, pp_mode, debug_blits); } self.gui_painter .paint(&mut pass, gui_primitives, &screen_desc, &self.context); From 42f50a2f0c8ed7a48982220957aa2bdd82a06e9e Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 25 Oct 2023 23:05:44 -0700 Subject: [PATCH 2/2] UI cleanup, proper variance reset, decoupled shading code --- blade-render/code/ray-trace.wgsl | 64 ++++++++++++++++++++------------ blade-render/src/render/mod.rs | 25 +++++++------ examples/scene/main.rs | 29 +++++++++++---- 3 files changed, 75 insertions(+), 43 deletions(-) diff --git a/blade-render/code/ray-trace.wgsl b/blade-render/code/ray-trace.wgsl index 8b30b7b2..0d244adb 100644 --- a/blade-render/code/ray-trace.wgsl +++ b/blade-render/code/ray-trace.wgsl @@ -15,6 +15,9 @@ const PAIRWISE_MIS: bool = true; // Bitterli's pseudocode (where it's 1) and NVidia's RTXDI implementation (where it's 0). // With Bitterli's 1 we have MIS not respecting the prior history enough. const BASE_CANONICAL_MIS: f32 = 0.05; +// See "DECOUPLING SHADING AND REUSE" in +// "Rearchitecting Spatiotemporal Resampling for Production" +const DECOUPLED_SHADING: bool = false; struct MainParams { frame_index: u32, @@ -54,6 +57,7 @@ struct LiveReservoir { selected_uv: vec2, selected_light_index: u32, selected_target_score: f32, + radiance: vec3, weight_sum: f32, history: f32, } @@ -81,9 +85,10 @@ fn bump_reservoir(r: ptr, history: f32) { } fn make_reservoir(ls: LightSample, light_index: u32, brdf: vec3) -> LiveReservoir { var r: LiveReservoir; + r.radiance = ls.radiance * brdf; r.selected_uv = ls.uv; r.selected_light_index = light_index; - r.selected_target_score = compute_target_score(ls.radiance * brdf); + r.selected_target_score = compute_target_score(r.radiance); r.weight_sum = r.selected_target_score / ls.pdf; r.history = 1.0; return r; @@ -95,6 +100,7 @@ fn merge_reservoir(r: ptr, other: LiveReservoir, random (*r).selected_light_index = other.selected_light_index; (*r).selected_uv = other.selected_uv; (*r).selected_target_score = other.selected_target_score; + (*r).radiance = other.radiance; return true; } else { return false; @@ -105,6 +111,7 @@ fn unpack_reservoir(f: StoredReservoir, max_history: u32) -> LiveReservoir { r.selected_light_index = f.light_index; r.selected_uv = f.light_uv; r.selected_target_score = f.target_score; + r.radiance = vec3(0.0); // to be continued... let history = min(f.confidence, f32(max_history)); r.weight_sum = f.contribution_weight * f.target_score * history; r.history = history; @@ -238,34 +245,36 @@ fn evaluate_reflected_light(surface: Surface, light_index: u32, light_uv: vec2, score: f32, } -fn make_target_pdf(color: vec3) -> TargetPdf { - return TargetPdf(color, compute_target_score(color)); +fn make_target_score(color: vec3) -> TargetScore { + return TargetScore(color, compute_target_score(color)); } -fn estimate_target_pdf_with_occlusion(surface: Surface, position: vec3, light_index: u32, light_uv: vec2, debug_len: f32) -> TargetPdf { +fn estimate_target_score_with_occlusion( + surface: Surface, position: vec3, light_index: u32, light_uv: vec2, debug_len: f32 +) -> TargetScore { if (light_index != 0u) { - return TargetPdf(); + return TargetScore(); } let direction = map_equirect_uv_to_dir(light_uv); if (dot(direction, surface.flat_normal) <= 0.0) { - return TargetPdf(); + return TargetScore(); } let brdf = evaluate_brdf(surface, direction); if (brdf <= 0.0) { - return TargetPdf(); + return TargetScore(); } if (check_ray_occluded(position, direction, debug_len)) { - return TargetPdf(); + return TargetScore(); } else { //Note: same as `evaluate_reflected_light` let radiance = textureSampleLevel(env_map, sampler_nearest, light_uv, 0.0).xyz; - return make_target_pdf(brdf * radiance); + return make_target_score(brdf * radiance); } } @@ -331,7 +340,6 @@ fn compute_restir(surface: Surface, pixel: vec2, rng: ptr(0.0); for (var i = 0u; i < parameters.num_environment_samples; i += 1u) { var ls: LightSample; if (parameters.environment_importance_sampling != 0u) { @@ -343,9 +351,7 @@ fn compute_restir(surface: Surface, pixel: vec2, rng: ptr 0.0) { let other = make_reservoir(ls, 0u, vec3(brdf)); - if (merge_reservoir(&canonical, other, random_gen(rng))) { - canonical_radiance = ls.radiance * brdf; - } + merge_reservoir(&canonical, other, random_gen(rng)); } else { bump_reservoir(&canonical, 1.0); } @@ -396,13 +402,13 @@ fn compute_restir(surface: Surface, pixel: vec2, rng: ptr(0.0); var mis_canonical = BASE_CANONICAL_MIS; + var color_and_weight = vec4(0.0); for (var rid = 0u; rid < accepted_count; rid += 1u) { let neighbor_index = accepted_reservoir_indices[rid]; let neighbor = prev_reservoirs[neighbor_index]; let max_history = select(parameters.spatial_tap_history, parameters.temporal_history, rid == temporal_index); var other: LiveReservoir; - var other_color: vec3; if (PAIRWISE_MIS) { let neighbor_pixel = get_pixel_from_reservoir_index(neighbor_index, prev_camera); let neighbor_history = min(neighbor.confidence, f32(max_history)); @@ -411,7 +417,7 @@ fn compute_restir(surface: Surface, pixel: vec2, rng: ptr, rng: ptr, rng: ptr(neighbor.contribution_weight * other.radiance, 1.0); + } if (other.weight_sum <= 0.0) { bump_reservoir(&reservoir, other.history); - } else if (merge_reservoir(&reservoir, other, random_gen(rng))) { - shaded_color = other_color; + } else { + merge_reservoir(&reservoir, other, random_gen(rng)); } } @@ -454,15 +463,22 @@ fn compute_restir(surface: Surface, pixel: vec2, rng: ptr(cw * canonical.radiance, 1.0); } + merge_reservoir(&reservoir, canonical, random_gen(rng)); let effective_history = select(reservoir.history, BASE_CANONICAL_MIS + f32(accepted_count), PAIRWISE_MIS); let stored = pack_reservoir_detail(reservoir, effective_history); reservoirs[pixel_index] = stored; var ro = RestirOutput(); - ro.radiance = stored.contribution_weight * shaded_color; + if (DECOUPLED_SHADING) { + ro.radiance = color_and_weight.xyz / max(color_and_weight.w, 0.001); + } else { + ro.radiance = stored.contribution_weight * reservoir.radiance; + } return ro; } diff --git a/blade-render/src/render/mod.rs b/blade-render/src/render/mod.rs index 84a42e49..c50f411d 100644 --- a/blade-render/src/render/mod.rs +++ b/blade-render/src/render/mod.rs @@ -93,7 +93,8 @@ pub struct DenoiserConfig { } pub struct SelectionInfo { - pub std_deviation: Option>, + pub std_deviation: mint::Vector3, + pub std_deviation_history: u32, pub tex_coords: mint::Vector2, pub base_color_texture: Option>, pub normal_texture: Option>, @@ -101,7 +102,8 @@ pub struct SelectionInfo { impl Default for SelectionInfo { fn default() -> Self { Self { - std_deviation: None, + std_deviation: [0.0; 3].into(), + std_deviation_history: 0, tex_coords: [0.0; 2].into(), base_color_texture: None, normal_texture: None, @@ -1128,18 +1130,18 @@ impl Renderer { } else { transfer.fill_buffer(self.debug.buffer.at(20), 4, 0); } - if accumulate_variance { - // copy the previous frame variance - transfer.copy_buffer_to_buffer( + if reset_reservoirs || !accumulate_variance { + transfer.fill_buffer( self.debug.buffer.at(32), - self.debug.variance_buffer.into(), mem::size_of::() as u64, + 0, ); } else { - transfer.fill_buffer( + // copy the previous frame variance + transfer.copy_buffer_to_buffer( self.debug.buffer.at(32), + self.debug.variance_buffer.into(), mem::size_of::() as u64, - 0, ); } transfer.copy_buffer_to_buffer( @@ -1365,17 +1367,18 @@ impl Renderer { let db_e = unsafe { &*(self.debug.entry_buffer.data() as *const DebugEntry) }; SelectionInfo { std_deviation: if db_v.count == 0 { - None + [0.0; 3].into() } else { let sum_avg = glam::Vec3::from(db_v.color_sum) / (db_v.count as f32); let sum2_avg = glam::Vec3::from(db_v.color2_sum) / (db_v.count as f32); let variance = sum2_avg - sum_avg * sum_avg; - Some(mint::Vector3 { + mint::Vector3 { x: variance.x.sqrt(), y: variance.y.sqrt(), z: variance.z.sqrt(), - }) + } }, + std_deviation_history: db_v.count, 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 55856535..546d0206 100644 --- a/examples/scene/main.rs +++ b/examples/scene/main.rs @@ -275,7 +275,7 @@ impl Example { } } - self.renderer.hot_reload( + self.need_accumulation_reset |= self.renderer.hot_reload( &self.asset_hub, &self.context, self.prev_sync_point.as_ref().unwrap(), @@ -444,23 +444,39 @@ impl Example { ui.checkbox(&mut enabled, name); self.debug.texture_flags.set(bit, enabled); } - // reset accumulation - self.need_accumulation_reset |= ui.button("reset").clicked(); // 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 sd = selection.std_deviation.unwrap_or([0.0; 3].into()); let style = ui.style(); egui::Frame::group(style).show(ui, |ui| { - ui.label(format!("Pixel: {screen_pos:?}")); ui.horizontal(|ui| { + ui.label("Pixel:"); + ui.colored_label( + egui::Color32::WHITE, + format!("{}x{}", screen_pos[0], screen_pos[1]), + ); + if ui.button("Unselect").clicked() { + self.debug.mouse_pos = None; + } + }); + ui.horizontal(|ui| { + let sd = &selection.std_deviation; ui.label("Std Deviation:"); ui.colored_label( egui::Color32::WHITE, format!("{:.2} {:.2} {:.2}", sd.x, sd.y, sd.z), ); }); + ui.horizontal(|ui| { + ui.label("Samples:"); + let power = selection + .std_deviation_history + .next_power_of_two() + .trailing_zeros(); + ui.colored_label(egui::Color32::WHITE, format!("2^{}", power)); + self.need_accumulation_reset |= ui.button("Reset").clicked(); + }); ui.horizontal(|ui| { ui.label("Texture coords:"); ui.colored_label( @@ -495,9 +511,6 @@ impl Example { ui.colored_label(egui::Color32::WHITE, name); } }); - if ui.button("Unselect").clicked() { - self.debug.mouse_pos = None; - } }); } // blits