mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-09 12:21:31 +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.
|
||||
- Arrow keys switch between SVG images in the current set.
|
||||
- Space resets the position and zoom of the image.
|
||||
- S toggles the frame statistics layer
|
||||
- Escape exits the program.
|
||||
|
|
|
@ -37,6 +37,7 @@ use winit::{
|
|||
#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
|
||||
mod hot_reload;
|
||||
mod multi_touch;
|
||||
mod stats;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[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 simple_text = SimpleText::new();
|
||||
let mut images = ImageCache::new();
|
||||
let mut stats = stats::Stats::new();
|
||||
let mut stats_toggle = false;
|
||||
let start = Instant::now();
|
||||
|
||||
let mut touch_state = multi_touch::TouchState::new();
|
||||
|
@ -142,6 +145,9 @@ fn run(
|
|||
Some(VirtualKeyCode::Space) => {
|
||||
transform = Affine::IDENTITY;
|
||||
}
|
||||
Some(VirtualKeyCode::S) => {
|
||||
stats_toggle = !stats_toggle;
|
||||
}
|
||||
Some(VirtualKeyCode::Escape) => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
|
@ -253,6 +259,8 @@ fn run(
|
|||
let width = render_state.surface.config.width;
|
||||
let height = render_state.surface.config.height;
|
||||
let device_handle = &render_cx.devices[render_state.surface.dev_id];
|
||||
let snapshot = stats.snapshot();
|
||||
let _stats_frame = stats.frame_scope();
|
||||
|
||||
// Allow looping forever
|
||||
scene_ix = scene_ix.rem_euclid(scenes.scenes.len() as i32);
|
||||
|
@ -296,6 +304,9 @@ fn run(
|
|||
transform = transform * Affine::scale(scale_factor);
|
||||
}
|
||||
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
|
||||
.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