diff --git a/Cargo.toml b/Cargo.toml index 2306477cc4012..9ef628a18962a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1246,6 +1246,16 @@ description = "Displays many sprites in a grid arragement! Used for performance category = "Stress Tests" wasm = true +[[example]] +name = "time_smoothness" +path = "examples/stress_tests/time_smoothness.rs" + +[package.metadata.example.time_smoothness] +name = "Time Smoothness" +description = "Renders a scrolling background behind an animated sprite. Used for performance consistency testing." +category = "Stress Tests" +wasm = true + [[example]] name = "transform_hierarchy" path = "examples/stress_tests/transform_hierarchy.rs" diff --git a/examples/README.md b/examples/README.md index 9791e63499649..2fe60bfed6ace 100644 --- a/examples/README.md +++ b/examples/README.md @@ -279,6 +279,7 @@ Example | Description [Many Foxes](../examples/stress_tests/many_foxes.rs) | Loads an animated fox model and spawns lots of them. Good for testing skinned mesh performance. Takes an unsigned integer argument for the number of foxes to spawn. Defaults to 1000 [Many Lights](../examples/stress_tests/many_lights.rs) | Simple benchmark to test rendering many point lights. Run with `WGPU_SETTINGS_PRIO=webgl2` to restrict to uniform buffers and max 256 lights [Many Sprites](../examples/stress_tests/many_sprites.rs) | Displays many sprites in a grid arragement! Used for performance testing +[Time Smoothness](../examples/stress_tests/time_smoothness.rs) | Renders a scrolling background behind an animated sprite. Used for performance consistency testing. [Transform Hierarchy](../examples/stress_tests/transform_hierarchy.rs) | Various test cases for hierarchy and transform propagation performance ## Tools diff --git a/examples/stress_tests/time_smoothness.rs b/examples/stress_tests/time_smoothness.rs new file mode 100644 index 0000000000000..fe1f1d6eb763b --- /dev/null +++ b/examples/stress_tests/time_smoothness.rs @@ -0,0 +1,355 @@ +//! Renders a scrolling background behind an animated sprite at a high zoom +//! level, to test consistency and smoothness of performance. +//! +//! To measure performance realistically, be sure to run this in release mode. +//! `cargo run --example time_smoothness --release` +//! +//! By default, this example scrolls the background at 120 pixels per second, +//! and always moves in whole-pixel increments (since limiting movement to whole +//! pixels at high zoom seems to make it easier to perceive any problems with +//! frame consistency). There are several keyboard controls for changing the +//! example's behavior at runtime: +//! +//! - P: Cycle the `PresentMode` between `Fifo`, `Mailbox`, and `Immediate`. +//! - W: Cycle the `WindowMode` between `Windowed`, `BorderlessFullscreen`, and +//! `Fullscreen`. +//! - T: Cycle the delta time between normal (measured from the `Time` resource) +//! and fixed-interval. This can be useful when trying to distinguish slow +//! frametimes from inaccurate time measurement. Fixed-interval delta time is +//! hardcoded to 1/60th of a second per frame, which will look highly wacky +//! unless you're using Fifo mode on a 60hz display. +//! - M: Cycle the scrolling motion style between whole-pixel and sub-pixel +//! Transform increments. +//! +//! A number of factors contribute to scrolling without perceptible +//! hiccups/stutters/jank, including the accuracy of the delta time measurement, +//! the ability to consistently present frames to the GPU at the expected pace, +//! etc. This example doesn't isolate all of those factors, but it can help +//! identify when a problem exists and provide a starting point for further +//! investigation. +use bevy::{ + diagnostic::{Diagnostic, DiagnosticId, Diagnostics}, + prelude::*, + render::texture::ImageSettings, + window::{PresentMode, WindowMode}, +}; + +// BG_WIDTH is smaller than the image's actual pixel width, because we want the +// empty space on the background tiles to overlap a bit. That way there's always +// a "landmark" on screen at the default window size. +const BG_WIDTH: f32 = 755.0; +const BG_HEIGHT: f32 = 363.0; +const BG_TILES: usize = 3; +const BG_SPEED: f32 = 120.0; + +const CUSTOM_FRAME_TIME: DiagnosticId = + DiagnosticId::from_u128(76860576947891895965111337840552081898); +const MAX_FRAME_HISTORY: usize = 800; +const FRAME_ANALYSIS_INTERVAL_SECONDS: f32 = 10.0; + +fn main() { + App::new() + .insert_resource(WindowDescriptor { + present_mode: PresentMode::Fifo, + mode: WindowMode::Windowed, + ..Default::default() + }) + // Prevents blurry sprites + .insert_resource(ImageSettings::default_nearest()) + .add_plugins(DefaultPlugins) + // Adds frame time diagnostics + .add_startup_system(setup_diagnostics) + .add_system(update_diagnostics) + .add_system(log_diagnostics) + .insert_resource(FrameAnalysisTimer(Timer::from_seconds( + FRAME_ANALYSIS_INTERVAL_SECONDS, + true, + ))) + // Main app setup + .add_startup_system(setup) + .insert_resource(MoveRemainder(Vec2::ZERO)) + .insert_resource(TimeStyle::Normal) + .insert_resource(MoveStyle::WholePixel) + .add_system(change_settings) + .add_system(animate_runner) + .add_system(scroll_background) + .run(); +} + +// Create a custom frame time diagnostic. We need this because +// FrameTimeDiagnosticsPlugin only keeps 20 frames, which is too narrow a view +// to be useful when hunting irregular blips. +fn setup_diagnostics(mut diagnostics: ResMut) { + diagnostics + .add(Diagnostic::new(CUSTOM_FRAME_TIME, "frame_time", MAX_FRAME_HISTORY).with_suffix("s")); +} + +// Update our custom frame time diagnostic with the delta time in milliseconds. +fn update_diagnostics(mut diagnostics: ResMut, time: Res