From abfe9fbb56b97f14dffe2259db2a573e09f56222 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Thu, 18 May 2023 18:50:57 +0100 Subject: [PATCH] Add a stats display for scene complexity (#322) --- examples/with_winit/README.md | 1 + examples/with_winit/src/lib.rs | 10 ++++++++-- examples/with_winit/src/stats.rs | 15 +++++++++++++-- src/lib.rs | 25 ++++++++++++++++++------- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/examples/with_winit/README.md b/examples/with_winit/README.md index f1c0910..abcc8f3 100644 --- a/examples/with_winit/README.md +++ b/examples/with_winit/README.md @@ -20,5 +20,6 @@ $ cargo run -p with_winit --release -- [SVG FILES] - Space resets the position and zoom of the image. - S toggles the frame statistics layer - C resets the min/max frame time tracked by statistics +- D toggles displaying the required number of each kind of dynamically allocated element (default: off) - V toggles VSync on/off (default: on) - Escape exits the program. diff --git a/examples/with_winit/src/lib.rs b/examples/with_winit/src/lib.rs index e08add2..5bbf5fa 100644 --- a/examples/with_winit/src/lib.rs +++ b/examples/with_winit/src/lib.rs @@ -27,7 +27,7 @@ use vello::{ util::RenderContext, Renderer, Scene, SceneBuilder, }; -use vello::{RendererOptions, SceneFragment}; +use vello::{BumpAllocators, RendererOptions, SceneFragment}; use winit::{ event_loop::{EventLoop, EventLoopBuilder}, @@ -100,6 +100,8 @@ fn run( let mut images = ImageCache::new(); let mut stats = stats::Stats::new(); let mut stats_shown = true; + let mut scene_complexity: Option = None; + let mut complexity_shown = false; let mut vsync_on = true; let mut frame_start_time = Instant::now(); let start = Instant::now(); @@ -150,6 +152,9 @@ fn run( Some(VirtualKeyCode::S) => { stats_shown = !stats_shown; } + Some(VirtualKeyCode::D) => { + complexity_shown = !complexity_shown; + } Some(VirtualKeyCode::C) => { stats.clear_min_and_max(); } @@ -326,6 +331,7 @@ fn run( width as f64, height as f64, stats.samples(), + complexity_shown.then_some(scene_complexity).flatten(), vsync_on, ); } @@ -336,7 +342,7 @@ fn run( .expect("failed to get surface texture"); #[cfg(not(target_arch = "wasm32"))] { - vello::block_on_wgpu( + scene_complexity = vello::block_on_wgpu( &device_handle.device, renderers[render_state.surface.dev_id] .as_mut() diff --git a/examples/with_winit/src/stats.rs b/examples/with_winit/src/stats.rs index a6531e6..81dda86 100644 --- a/examples/with_winit/src/stats.rs +++ b/examples/with_winit/src/stats.rs @@ -19,7 +19,7 @@ use std::collections::VecDeque; use vello::{ kurbo::{Affine, PathEl, Rect}, peniko::{Brush, Color, Fill, Stroke}, - SceneBuilder, + BumpAllocators, SceneBuilder, }; const SLIDING_WINDOW_SIZE: usize = 100; @@ -40,6 +40,7 @@ impl Snapshot { viewport_width: f64, viewport_height: f64, samples: T, + bump: Option, vsync: bool, ) where T: Iterator, @@ -59,13 +60,23 @@ impl Snapshot { &Rect::new(0., 0., width, height), ); - let labels = [ + let mut labels = vec![ format!("Frame Time: {:.2} ms", self.frame_time_ms), format!("Frame Time (min): {:.2} ms", self.frame_time_min_ms), format!("Frame Time (max): {:.2} ms", self.frame_time_max_ms), format!("VSync: {}", if vsync { "on" } else { "off" }), format!("Resolution: {viewport_width}x{viewport_height}"), ]; + if let Some(bump) = &bump { + if bump.failed >= 1 { + labels.push(format!("Allocation Failed!")); + } + labels.push(format!("binning: {}", bump.binning)); + labels.push(format!("ptcl: {}", bump.ptcl)); + labels.push(format!("tile: {}", bump.tile)); + labels.push(format!("segments: {}", bump.segments)); + labels.push(format!("blend: {}", bump.blend)); + } // height / 2 is dedicated to the text labels and the rest is filled by the bar graph. let text_height = height * 0.5 / (1 + labels.len()) as f64; diff --git a/src/lib.rs b/src/lib.rs index b0f35a6..a1e3b03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,8 @@ pub use util::block_on_wgpu; use engine::{Engine, ExternalResource, Recording}; use shaders::FullShaders; +/// Temporary export, used in with_winit for stats +pub use vello_encoding::BumpAllocators; use wgpu::{Device, Queue, SurfaceTexture, TextureFormat, TextureView}; /// Catch-all error type. @@ -196,6 +198,12 @@ impl Renderer { /// The texture is assumed to be of the specified dimensions and have been created with /// the [wgpu::TextureFormat::Rgba8Unorm] format and the [wgpu::TextureUsages::STORAGE_BINDING] /// flag set. + /// + /// The return value is the value of the `BumpAllocators` in this rendering, which is currently used + /// for debug output. + /// + /// This return type is not stable, and will likely be changed when a more principled way to access + /// relevant statistics is implemented pub async fn render_to_texture_async( &mut self, device: &Device, @@ -203,13 +211,15 @@ impl Renderer { scene: &Scene, texture: &TextureView, params: &RenderParams, - ) -> Result<()> { + ) -> Result> { let mut render = Render::new(); let encoding = scene.data(); let recording = render.render_encoding_coarse(encoding, &self.shaders, params, true); let target = render.out_image(); let bump_buf = render.bump_buf(); self.engine.run_recording(device, queue, &recording, &[])?; + + let mut bump: Option = None; if let Some(bump_buf) = self.engine.get_download(bump_buf) { let buf_slice = bump_buf.slice(..); let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel(); @@ -219,8 +229,8 @@ impl Renderer { } else { return Err("channel was closed".into()); } - let _mapped = buf_slice.get_mapped_range(); - // println!("{:?}", bytemuck::cast_slice::<_, u32>(&mapped)); + let mapped = buf_slice.get_mapped_range(); + bump = Some(bytemuck::pod_read_unaligned(&*mapped)); } // TODO: apply logic to determine whether we need to rerun coarse, and also // allocate the blend stack as needed. @@ -231,7 +241,7 @@ impl Renderer { let external_resources = [ExternalResource::Image(target, texture)]; self.engine .run_recording(device, queue, &recording, &external_resources)?; - Ok(()) + Ok(bump) } /// See [Self::render_to_surface] @@ -242,7 +252,7 @@ impl Renderer { scene: &Scene, surface: &SurfaceTexture, params: &RenderParams, - ) -> Result<()> { + ) -> Result> { let width = params.width; let height = params.height; let mut target = self @@ -254,7 +264,8 @@ impl Renderer { if target.width != width || target.height != height { target = TargetTexture::new(device, width, height); } - self.render_to_texture_async(device, queue, scene, &target.view, params) + let bump = self + .render_to_texture_async(device, queue, scene, &target.view, params) .await?; let blit = self .blit @@ -292,7 +303,7 @@ impl Renderer { } queue.submit(Some(encoder.finish())); self.target = Some(target); - Ok(()) + Ok(bump) } }