mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-09 20:31:29 +11:00
[frame_stats] Add frame statistics UI to with_winit example
Added a module for frame time statistics and UI layer that displays the average, minimum, and maximum frame time alongside FPS. The UI can be toggled by pressing the `S` key.
This commit is contained in:
parent
17096ad878
commit
89fb1b89da
|
@ -18,4 +18,5 @@ $ cargo run -p with_winit --release -- [SVG FILES]
|
||||||
- Mouse scroll wheel will zoom.
|
- Mouse scroll wheel will zoom.
|
||||||
- Arrow keys switch between SVG images in the current set.
|
- Arrow keys switch between SVG images in the current set.
|
||||||
- Space resets the position and zoom of the image.
|
- Space resets the position and zoom of the image.
|
||||||
|
- S toggles the frame statistics layer
|
||||||
- Escape exits the program.
|
- Escape exits the program.
|
||||||
|
|
|
@ -37,6 +37,7 @@ use winit::{
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
|
#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
|
||||||
mod hot_reload;
|
mod hot_reload;
|
||||||
mod multi_touch;
|
mod multi_touch;
|
||||||
|
mod stats;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(about, long_about = None, bin_name="cargo run -p with_winit --")]
|
#[command(about, long_about = None, bin_name="cargo run -p with_winit --")]
|
||||||
|
@ -97,6 +98,8 @@ fn run(
|
||||||
let mut fragment = SceneFragment::new();
|
let mut fragment = SceneFragment::new();
|
||||||
let mut simple_text = SimpleText::new();
|
let mut simple_text = SimpleText::new();
|
||||||
let mut images = ImageCache::new();
|
let mut images = ImageCache::new();
|
||||||
|
let mut stats = stats::Stats::new();
|
||||||
|
let mut stats_toggle = false;
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
let mut touch_state = multi_touch::TouchState::new();
|
let mut touch_state = multi_touch::TouchState::new();
|
||||||
|
@ -142,6 +145,9 @@ fn run(
|
||||||
Some(VirtualKeyCode::Space) => {
|
Some(VirtualKeyCode::Space) => {
|
||||||
transform = Affine::IDENTITY;
|
transform = Affine::IDENTITY;
|
||||||
}
|
}
|
||||||
|
Some(VirtualKeyCode::S) => {
|
||||||
|
stats_toggle = !stats_toggle;
|
||||||
|
}
|
||||||
Some(VirtualKeyCode::Escape) => {
|
Some(VirtualKeyCode::Escape) => {
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
}
|
}
|
||||||
|
@ -253,6 +259,8 @@ fn run(
|
||||||
let width = render_state.surface.config.width;
|
let width = render_state.surface.config.width;
|
||||||
let height = render_state.surface.config.height;
|
let height = render_state.surface.config.height;
|
||||||
let device_handle = &render_cx.devices[render_state.surface.dev_id];
|
let device_handle = &render_cx.devices[render_state.surface.dev_id];
|
||||||
|
let snapshot = stats.snapshot();
|
||||||
|
let _stats_frame = stats.frame_scope();
|
||||||
|
|
||||||
// Allow looping forever
|
// Allow looping forever
|
||||||
scene_ix = scene_ix.rem_euclid(scenes.scenes.len() as i32);
|
scene_ix = scene_ix.rem_euclid(scenes.scenes.len() as i32);
|
||||||
|
@ -296,6 +304,9 @@ fn run(
|
||||||
transform = transform * Affine::scale(scale_factor);
|
transform = transform * Affine::scale(scale_factor);
|
||||||
}
|
}
|
||||||
builder.append(&fragment, Some(transform));
|
builder.append(&fragment, Some(transform));
|
||||||
|
if stats_toggle {
|
||||||
|
snapshot.draw_layer(&mut builder, &mut scene_params.text, width as f64);
|
||||||
|
}
|
||||||
let surface_texture = render_state
|
let surface_texture = render_state
|
||||||
.surface
|
.surface
|
||||||
.surface
|
.surface
|
||||||
|
|
148
examples/with_winit/src/stats.rs
Normal file
148
examples/with_winit/src/stats.rs
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
// Also licensed under MIT license, at your choice.
|
||||||
|
|
||||||
|
use scenes::SimpleText;
|
||||||
|
use std::{collections::VecDeque, time::Instant};
|
||||||
|
use vello::{
|
||||||
|
kurbo::{Affine, Rect},
|
||||||
|
peniko::{Brush, Color, Fill},
|
||||||
|
SceneBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SLIDING_WINDOW_SIZE: usize = 100;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Snapshot {
|
||||||
|
pub fps: f64,
|
||||||
|
pub frame_time_ms: f64,
|
||||||
|
pub frame_time_min_ms: f64,
|
||||||
|
pub frame_time_max_ms: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Snapshot {
|
||||||
|
pub fn draw_layer(&self, sb: &mut SceneBuilder, text: &mut SimpleText, width: f64) {
|
||||||
|
let x_offset = width - 450.;
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
|
&Brush::Solid(Color::rgba8(0, 0, 0, 200)),
|
||||||
|
None,
|
||||||
|
&Rect::new(x_offset, 0., width, 115.),
|
||||||
|
);
|
||||||
|
text.add(
|
||||||
|
sb,
|
||||||
|
None,
|
||||||
|
25.,
|
||||||
|
Some(&Brush::Solid(Color::WHITE)),
|
||||||
|
Affine::translate((x_offset + 15., 30.)),
|
||||||
|
&format!("Frame Time: {:.2} ms", self.frame_time_ms),
|
||||||
|
);
|
||||||
|
text.add(
|
||||||
|
sb,
|
||||||
|
None,
|
||||||
|
25.,
|
||||||
|
Some(&Brush::Solid(Color::WHITE)),
|
||||||
|
Affine::translate((x_offset + 15., 60.)),
|
||||||
|
&format!("Frame Time (min): {:.2} ms", self.frame_time_min_ms),
|
||||||
|
);
|
||||||
|
text.add(
|
||||||
|
sb,
|
||||||
|
None,
|
||||||
|
25.,
|
||||||
|
Some(&Brush::Solid(Color::WHITE)),
|
||||||
|
Affine::translate((x_offset + 15., 90.)),
|
||||||
|
&format!("Frame Time (max): {:.2} ms", self.frame_time_max_ms),
|
||||||
|
);
|
||||||
|
text.add(
|
||||||
|
sb,
|
||||||
|
None,
|
||||||
|
25.,
|
||||||
|
Some(&Brush::Solid(Color::WHITE)),
|
||||||
|
Affine::translate((x_offset + 300., 30.)),
|
||||||
|
&format!("FPS: {:.2}", self.fps),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Stats {
|
||||||
|
count: usize,
|
||||||
|
sum: u64,
|
||||||
|
min: u64,
|
||||||
|
max: u64,
|
||||||
|
samples: VecDeque<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FrameScope<'a> {
|
||||||
|
stats: &'a mut Stats,
|
||||||
|
start: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Drop for FrameScope<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.stats
|
||||||
|
.add_sample(self.start.elapsed().as_micros() as u64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stats {
|
||||||
|
pub fn new() -> Stats {
|
||||||
|
Stats {
|
||||||
|
count: 0,
|
||||||
|
sum: 0,
|
||||||
|
min: u64::MAX,
|
||||||
|
max: u64::MIN,
|
||||||
|
samples: VecDeque::with_capacity(SLIDING_WINDOW_SIZE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn snapshot(&self) -> Snapshot {
|
||||||
|
let frame_time_ms = (self.sum as f64 / self.count as f64) * 0.001;
|
||||||
|
let fps = 1000. / frame_time_ms;
|
||||||
|
Snapshot {
|
||||||
|
fps,
|
||||||
|
frame_time_ms,
|
||||||
|
frame_time_min_ms: self.min as f64 * 0.001,
|
||||||
|
frame_time_max_ms: self.max as f64 * 0.001,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn frame_scope<'a>(&'a mut self) -> FrameScope<'a> {
|
||||||
|
FrameScope {
|
||||||
|
stats: self,
|
||||||
|
start: Instant::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_sample(&mut self, micros: u64) {
|
||||||
|
let oldest = if self.count < SLIDING_WINDOW_SIZE {
|
||||||
|
self.count += 1;
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.samples.pop_front()
|
||||||
|
};
|
||||||
|
self.sum += micros;
|
||||||
|
self.samples.push_back(micros);
|
||||||
|
if let Some(oldest) = oldest {
|
||||||
|
self.sum -= oldest;
|
||||||
|
}
|
||||||
|
if micros < self.min {
|
||||||
|
self.min = micros;
|
||||||
|
}
|
||||||
|
if micros > self.max {
|
||||||
|
self.max = micros;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue