pathfinder/renderer/src/gpu/debug.rs

274 lines
7.9 KiB
Rust
Raw Normal View History

// pathfinder/renderer/src/gpu/debug.rs
2019-01-30 17:42:06 -05:00
//
// Copyright © 2019 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A debug overlay.
2019-01-30 17:42:06 -05:00
//!
//! We don't render the demo UI text using Pathfinder itself so that we can use the debug UI to
//! debug Pathfinder if it's totally busted.
//!
//! The debug font atlas was generated using: https://evanw.github.io/font-texture-generator/
use crate::gpu::renderer::{RenderStats, RenderTime};
use pathfinder_geometry::vector::Vector2I;
use pathfinder_geometry::rect::RectI;
2019-04-29 19:45:29 -04:00
use pathfinder_gpu::Device;
use pathfinder_resources::ResourceLoader;
use pathfinder_ui::{FONT_ASCENT, LINE_HEIGHT, PADDING, UIPresenter, WINDOW_COLOR};
use std::collections::VecDeque;
use std::ops::{Add, Div};
2019-01-30 17:42:06 -05:00
use std::time::Duration;
2019-02-07 18:35:21 -05:00
const SAMPLE_BUFFER_SIZE: usize = 60;
const STATS_WINDOW_WIDTH: i32 = 325;
const STATS_WINDOW_HEIGHT: i32 = LINE_HEIGHT * 4 + PADDING + 2;
const PERFORMANCE_WINDOW_WIDTH: i32 = 400;
const PERFORMANCE_WINDOW_HEIGHT: i32 = LINE_HEIGHT * 4 + PADDING + 2;
2019-01-30 17:42:06 -05:00
pub struct DebugUIPresenter<D>
2019-04-29 19:45:29 -04:00
where
D: Device,
{
pub ui_presenter: UIPresenter<D>,
2019-02-07 18:35:21 -05:00
cpu_samples: SampleBuffer<CPUSample>,
gpu_samples: SampleBuffer<GPUSample>,
2019-01-30 17:42:06 -05:00
}
impl<D> DebugUIPresenter<D>
2019-04-29 19:45:29 -04:00
where
D: Device,
{
pub fn new(
device: &D,
resources: &dyn ResourceLoader,
framebuffer_size: Vector2I,
) -> DebugUIPresenter<D> {
let ui_presenter = UIPresenter::new(device, resources, framebuffer_size);
DebugUIPresenter {
ui_presenter,
2019-04-29 19:45:29 -04:00
cpu_samples: SampleBuffer::new(),
gpu_samples: SampleBuffer::new(),
}
2019-01-30 17:42:06 -05:00
}
2019-04-29 19:45:29 -04:00
pub fn add_sample(
&mut self,
stats: RenderStats,
tile_time: Duration,
rendering_time: Option<RenderTime>,
2019-04-29 19:45:29 -04:00
) {
self.cpu_samples.push(CPUSample {
stats,
elapsed: tile_time,
});
if let Some(time) = rendering_time {
self.gpu_samples.push(GPUSample { time })
2019-02-07 18:35:21 -05:00
}
}
2019-03-04 17:55:32 -05:00
pub fn draw(&self, device: &D) {
let mean_cpu_sample = self.cpu_samples.mean();
self.draw_stats_window(device, &mean_cpu_sample);
self.draw_performance_window(device, &mean_cpu_sample);
}
fn draw_stats_window(&self, device: &D, mean_cpu_sample: &CPUSample) {
let framebuffer_size = self.ui_presenter.framebuffer_size();
let bottom = framebuffer_size.y() - PADDING;
let window_rect = RectI::new(
Vector2I::new(
framebuffer_size.x() - PADDING - STATS_WINDOW_WIDTH,
bottom - PERFORMANCE_WINDOW_HEIGHT - PADDING - STATS_WINDOW_HEIGHT,
2019-04-29 19:45:29 -04:00
),
Vector2I::new(STATS_WINDOW_WIDTH, STATS_WINDOW_HEIGHT),
2019-04-29 19:45:29 -04:00
);
self.ui_presenter.draw_solid_rounded_rect(device, window_rect, WINDOW_COLOR);
let origin = window_rect.origin() + Vector2I::new(PADDING, PADDING + FONT_ASCENT);
self.ui_presenter.draw_text(
2019-04-29 19:45:29 -04:00
device,
&format!("Paths: {}", mean_cpu_sample.stats.path_count),
2019-04-29 19:45:29 -04:00
origin,
false,
);
self.ui_presenter.draw_text(
2019-04-29 19:45:29 -04:00
device,
&format!("Solid Tiles: {}", mean_cpu_sample.stats.solid_tile_count),
origin + Vector2I::new(0, LINE_HEIGHT * 1),
2019-04-29 19:45:29 -04:00
false,
);
self.ui_presenter.draw_text(
2019-04-29 19:45:29 -04:00
device,
&format!("Alpha Tiles: {}", mean_cpu_sample.stats.alpha_tile_count),
origin + Vector2I::new(0, LINE_HEIGHT * 2),
2019-04-29 19:45:29 -04:00
false,
);
self.ui_presenter.draw_text(
2019-04-29 19:45:29 -04:00
device,
&format!("Fills: {}", mean_cpu_sample.stats.fill_count),
origin + Vector2I::new(0, LINE_HEIGHT * 3),
2019-04-29 19:45:29 -04:00
false,
);
}
2019-04-29 19:45:29 -04:00
fn draw_performance_window(&self, device: &D, mean_cpu_sample: &CPUSample) {
let framebuffer_size = self.ui_presenter.framebuffer_size();
let bottom = framebuffer_size.y() - PADDING;
let window_rect = RectI::new(
Vector2I::new(
framebuffer_size.x() - PADDING - PERFORMANCE_WINDOW_WIDTH,
bottom - PERFORMANCE_WINDOW_HEIGHT,
),
Vector2I::new(PERFORMANCE_WINDOW_WIDTH, PERFORMANCE_WINDOW_HEIGHT),
);
self.ui_presenter.draw_solid_rounded_rect(device, window_rect, WINDOW_COLOR);
let origin = window_rect.origin() + Vector2I::new(PADDING, PADDING + FONT_ASCENT);
self.ui_presenter.draw_text(
2019-04-29 19:45:29 -04:00
device,
&format!(
"Stage 0 CPU: {:.3} ms",
2019-04-29 19:45:29 -04:00
duration_to_ms(mean_cpu_sample.elapsed)
),
origin,
2019-04-29 19:45:29 -04:00
false,
);
let mean_gpu_sample = self.gpu_samples.mean();
self.ui_presenter.draw_text(
2019-04-29 19:45:29 -04:00
device,
&format!(
"Stage 0 GPU: {:.3} ms",
duration_to_ms(mean_gpu_sample.time.stage_0)
),
origin + Vector2I::new(0, LINE_HEIGHT * 1),
false,
);
self.ui_presenter.draw_text(
device,
&format!(
"Stage 1 GPU: {:.3} ms",
duration_to_ms(mean_gpu_sample.time.stage_1)
2019-04-29 19:45:29 -04:00
),
origin + Vector2I::new(0, LINE_HEIGHT * 2),
false,
);
let wallclock_time = f64::max(duration_to_ms(mean_gpu_sample.time.stage_0),
duration_to_ms(mean_cpu_sample.elapsed)) +
duration_to_ms(mean_gpu_sample.time.stage_1);
self.ui_presenter.draw_text(
device,
&format!("Wallclock: {:.3} ms", wallclock_time),
origin + Vector2I::new(0, LINE_HEIGHT * 3),
2019-04-29 19:45:29 -04:00
false,
);
2019-01-30 17:42:06 -05:00
}
2019-01-30 17:42:06 -05:00
}
2019-04-29 19:45:29 -04:00
struct SampleBuffer<S>
where
S: Add<S, Output = S> + Div<usize, Output = S> + Clone + Default,
{
samples: VecDeque<S>,
2019-02-07 18:35:21 -05:00
}
2019-04-29 19:45:29 -04:00
impl<S> SampleBuffer<S>
where
S: Add<S, Output = S> + Div<usize, Output = S> + Clone + Default,
{
fn new() -> SampleBuffer<S> {
2019-04-29 19:45:29 -04:00
SampleBuffer {
samples: VecDeque::with_capacity(SAMPLE_BUFFER_SIZE),
}
2019-02-07 18:35:21 -05:00
}
fn push(&mut self, time: S) {
2019-02-07 18:35:21 -05:00
self.samples.push_back(time);
while self.samples.len() > SAMPLE_BUFFER_SIZE {
self.samples.pop_front();
}
}
fn mean(&self) -> S {
let mut mean = Default::default();
2019-02-07 18:35:21 -05:00
if self.samples.is_empty() {
return mean;
2019-02-07 18:35:21 -05:00
}
for time in &self.samples {
mean = mean + (*time).clone();
2019-02-07 18:35:21 -05:00
}
mean / self.samples.len()
2019-02-07 18:35:21 -05:00
}
}
#[derive(Clone, Default)]
struct CPUSample {
elapsed: Duration,
stats: RenderStats,
}
impl Add<CPUSample> for CPUSample {
type Output = CPUSample;
fn add(self, other: CPUSample) -> CPUSample {
2019-04-29 19:45:29 -04:00
CPUSample {
elapsed: self.elapsed + other.elapsed,
stats: self.stats + other.stats,
}
}
}
impl Div<usize> for CPUSample {
type Output = CPUSample;
fn div(self, divisor: usize) -> CPUSample {
2019-04-29 19:45:29 -04:00
CPUSample {
elapsed: self.elapsed / (divisor as u32),
stats: self.stats / divisor,
}
}
}
#[derive(Clone, Default)]
struct GPUSample {
time: RenderTime,
}
impl Add<GPUSample> for GPUSample {
type Output = GPUSample;
fn add(self, other: GPUSample) -> GPUSample {
2019-04-29 19:45:29 -04:00
GPUSample {
time: self.time + other.time,
2019-04-29 19:45:29 -04:00
}
}
}
impl Div<usize> for GPUSample {
type Output = GPUSample;
fn div(self, divisor: usize) -> GPUSample {
2019-04-29 19:45:29 -04:00
GPUSample {
time: RenderTime {
stage_0: self.time.stage_0 / (divisor as u32),
stage_1: self.time.stage_1 / (divisor as u32),
}
2019-04-29 19:45:29 -04:00
}
}
}
fn duration_to_ms(time: Duration) -> f64 {
time.as_secs() as f64 * 1000.0 + time.subsec_nanos() as f64 / 1000000.0
}