Add more statistics to the performance debug window

This commit is contained in:
Patrick Walton 2019-02-25 16:12:47 -08:00
parent 19c14da3aa
commit 5c7423d59c
3 changed files with 131 additions and 29 deletions

View File

@ -351,7 +351,8 @@ impl DemoApp {
}
let rendering_time = self.renderer.shift_timer_query();
self.renderer.debug_ui.add_sample(tile_time, rendering_time);
let stats = built_scene.stats();
self.renderer.debug_ui.add_sample(stats, tile_time, rendering_time);
self.renderer.debug_ui.draw();
if !ui_event.is_none() {
@ -686,7 +687,6 @@ impl Options {
fn load_scene(input_path: &Path) -> Scene {
let usvg = Tree::from_file(input_path, &UsvgOptions::default()).unwrap();
let scene = Scene::from_tree(usvg);
println!("Scene bounds: {:?}", scene.bounds);
println!("{} objects, {} paints", scene.objects.len(), scene.paints.len());
scene
}
@ -724,7 +724,7 @@ fn build_scene(scene: &Scene, build_options: BuildOptions, jobs: Option<usize>)
}
};
let mut built_scene = BuiltScene::new(scene.view_box, &quad);
let mut built_scene = BuiltScene::new(scene.view_box, &quad, scene.objects.len() as u32);
built_scene.shaders = scene.build_shaders();
let mut scene_builder = SceneBuilder::new(built_objects, z_buffer, scene.view_box);

View File

@ -21,11 +21,13 @@ use gl::types::{GLfloat, GLint, GLsizei, GLuint};
use gl;
use pathfinder_geometry::basic::point::Point2DI32;
use pathfinder_geometry::basic::rect::RectI32;
use pathfinder_renderer::gpu_data::Stats;
use pathfinder_renderer::paint::ColorU;
use serde_json;
use std::collections::{HashMap, VecDeque};
use std::fs::File;
use std::io::BufReader;
use std::ops::{Add, Div};
use std::ptr;
use std::time::Duration;
@ -42,8 +44,8 @@ pub const BUTTON_TEXT_OFFSET: i32 = PADDING + 36;
pub static TEXT_COLOR: ColorU = ColorU { r: 255, g: 255, b: 255, a: 255 };
pub static WINDOW_COLOR: ColorU = ColorU { r: 0, g: 0, b: 0, a: 255 - 90 };
const PERF_WINDOW_WIDTH: i32 = 300;
const PERF_WINDOW_HEIGHT: i32 = LINE_HEIGHT * 2 + PADDING + 2;
const PERF_WINDOW_WIDTH: i32 = 375;
const PERF_WINDOW_HEIGHT: i32 = LINE_HEIGHT * 6 + PADDING + 2;
const FONT_ASCENT: i32 = 28;
const LINE_HEIGHT: i32 = 42;
const ICON_SIZE: i32 = 48;
@ -99,8 +101,8 @@ pub struct DebugUI {
solid_vertex_array: DebugSolidVertexArray,
font_texture: Texture,
cpu_samples: SampleBuffer,
gpu_samples: SampleBuffer,
cpu_samples: SampleBuffer<CPUSample>,
gpu_samples: SampleBuffer<GPUSample>,
}
impl DebugUI {
@ -138,10 +140,13 @@ impl DebugUI {
self.framebuffer_size = window_size;
}
pub fn add_sample(&mut self, tile_time: Duration, rendering_time: Option<Duration>) {
self.cpu_samples.push(tile_time);
pub fn add_sample(&mut self,
stats: Stats,
tile_time: Duration,
rendering_time: Option<Duration>) {
self.cpu_samples.push(CPUSample { stats, elapsed: tile_time });
if let Some(rendering_time) = rendering_time {
self.gpu_samples.push(rendering_time);
self.gpu_samples.push(GPUSample { elapsed: rendering_time });
}
}
@ -153,14 +158,26 @@ impl DebugUI {
bottom - PERF_WINDOW_HEIGHT),
Point2DI32::new(PERF_WINDOW_WIDTH, PERF_WINDOW_HEIGHT));
self.draw_solid_rect(window_rect, WINDOW_COLOR);
self.draw_text(&format!("CPU: {:.3} ms", self.cpu_samples.mean_ms()),
Point2DI32::new(window_rect.min_x() + PADDING,
window_rect.min_y() + PADDING + FONT_ASCENT),
let origin = window_rect.origin() + Point2DI32::new(PADDING, PADDING + FONT_ASCENT);
let mean_cpu_sample = self.cpu_samples.mean();
self.draw_text(&format!("Objects: {}", mean_cpu_sample.stats.object_count), origin, false);
self.draw_text(&format!("Solid Tiles: {}", mean_cpu_sample.stats.solid_tile_count),
origin + Point2DI32::new(0, LINE_HEIGHT * 1),
false);
self.draw_text(&format!("GPU: {:.3} ms", self.gpu_samples.mean_ms()),
Point2DI32::new(
window_rect.min_x() + PADDING,
window_rect.min_y() + PADDING + FONT_ASCENT + LINE_HEIGHT),
self.draw_text(&format!("Mask Tiles: {}", mean_cpu_sample.stats.mask_tile_count),
origin + Point2DI32::new(0, LINE_HEIGHT * 2),
false);
self.draw_text(&format!("Fills: {}", mean_cpu_sample.stats.fill_count),
origin + Point2DI32::new(0, LINE_HEIGHT * 3),
false);
self.draw_text(&format!("CPU Time: {:.3} ms", duration_to_ms(mean_cpu_sample.elapsed)),
origin + Point2DI32::new(0, LINE_HEIGHT * 4),
false);
let mean_gpu_sample = self.gpu_samples.mean();
self.draw_text(&format!("GPU Time: {:.3} ms", duration_to_ms(mean_gpu_sample.elapsed)),
origin + Point2DI32::new(0, LINE_HEIGHT * 5),
false);
}
@ -457,32 +474,33 @@ impl DebugSolidVertex {
}
}
struct SampleBuffer {
samples: VecDeque<Duration>,
struct SampleBuffer<S> where S: Add<S, Output=S> + Div<u32, Output=S> + Clone + Default {
samples: VecDeque<S>,
}
impl SampleBuffer {
fn new() -> SampleBuffer {
impl<S> SampleBuffer<S> where S: Add<S, Output=S> + Div<u32, Output=S> + Clone + Default {
fn new() -> SampleBuffer<S> {
SampleBuffer { samples: VecDeque::with_capacity(SAMPLE_BUFFER_SIZE) }
}
fn push(&mut self, time: Duration) {
fn push(&mut self, time: S) {
self.samples.push_back(time);
while self.samples.len() > SAMPLE_BUFFER_SIZE {
self.samples.pop_front();
}
}
fn mean_ms(&self) -> f64 {
fn mean(&self) -> S {
let mut mean = Default::default();
if self.samples.is_empty() {
return 0.0;
return mean;
}
let mut ms = 0.0;
for time in &self.samples {
ms += time.as_secs() as f64 * 1000.0 + time.subsec_nanos() as f64 / 1000000.0;
mean = mean + (*time).clone();
}
ms / self.samples.len() as f64
mean / self.samples.len() as u32
}
}
@ -495,3 +513,62 @@ fn set_color_uniform(uniform: &Uniform, color: ColorU) {
color.a as f32 * (1.0 / 255.0));
}
}
#[derive(Clone, Default)]
struct CPUSample {
elapsed: Duration,
stats: Stats,
}
impl Add<CPUSample> for CPUSample {
type Output = CPUSample;
fn add(self, other: CPUSample) -> CPUSample {
CPUSample {
elapsed: self.elapsed + other.elapsed,
stats: Stats {
object_count: self.stats.object_count + other.stats.object_count,
solid_tile_count: self.stats.solid_tile_count + other.stats.solid_tile_count,
mask_tile_count: self.stats.mask_tile_count + other.stats.mask_tile_count,
fill_count: self.stats.fill_count + other.stats.fill_count,
},
}
}
}
impl Div<u32> for CPUSample {
type Output = CPUSample;
fn div(self, divisor: u32) -> CPUSample {
CPUSample {
elapsed: self.elapsed / divisor,
stats: Stats {
object_count: self.stats.object_count / divisor,
solid_tile_count: self.stats.solid_tile_count / divisor,
mask_tile_count: self.stats.mask_tile_count / divisor,
fill_count: self.stats.fill_count / divisor,
},
}
}
}
#[derive(Clone, Default)]
struct GPUSample {
elapsed: Duration,
}
impl Add<GPUSample> for GPUSample {
type Output = GPUSample;
fn add(self, other: GPUSample) -> GPUSample {
GPUSample { elapsed: self.elapsed + other.elapsed }
}
}
impl Div<u32> for GPUSample {
type Output = GPUSample;
fn div(self, divisor: u32) -> GPUSample {
GPUSample { elapsed: self.elapsed / divisor }
}
}
fn duration_to_ms(time: Duration) -> f64 {
time.as_secs() as f64 * 1000.0 + time.subsec_nanos() as f64 / 1000000.0
}

View File

@ -33,6 +33,7 @@ pub struct BuiltObject {
pub struct BuiltScene {
pub view_box: RectF32,
pub quad: [Point3DF32; 4],
pub object_count: u32,
pub batches: Vec<Batch>,
pub solid_tiles: Vec<SolidTileScenePrimitive>,
pub shaders: Vec<ObjectShader>,
@ -84,6 +85,14 @@ pub struct MaskTileBatchPrimitive {
pub shader: ShaderId,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Stats {
pub object_count: u32,
pub solid_tile_count: u32,
pub mask_tile_count: u32,
pub fill_count: u32,
}
// Utilities for built objects
impl BuiltObject {
@ -251,8 +260,24 @@ impl BuiltObject {
impl BuiltScene {
#[inline]
pub fn new(view_box: RectF32, quad: &[Point3DF32; 4]) -> BuiltScene {
BuiltScene { view_box, quad: *quad, batches: vec![], solid_tiles: vec![], shaders: vec![] }
pub fn new(view_box: RectF32, quad: &[Point3DF32; 4], object_count: u32) -> BuiltScene {
BuiltScene {
view_box,
quad: *quad,
object_count,
batches: vec![],
solid_tiles: vec![],
shaders: vec![],
}
}
pub fn stats(&self) -> Stats {
Stats {
object_count: self.object_count,
solid_tile_count: self.solid_tiles.len() as u32,
mask_tile_count: self.batches.iter().map(|batch| batch.mask_tiles.len() as u32).sum(),
fill_count: self.batches.iter().map(|batch| batch.fills.len() as u32).sum(),
}
}
}