From 2d29954034a86b0eca5b8e5076614b4ccd113e78 Mon Sep 17 00:00:00 2001 From: Mateusz Wachowiak Date: Mon, 11 Mar 2024 20:26:14 +0100 Subject: [PATCH] Fps overlay (#12382) # Objective - Part of #12351 - Add fps overlay ## Solution - Create `FpsOverlayPlugin` - Allow for configuration through resource `FpsOverlayConfig` - Allow for configuration during runtime ### Preview on default settings ![20240308_22h23m25s_grim](https://github.com/bevyengine/bevy/assets/62356462/33d3d7a9-435e-4e0b-9814-d3274e779a69) --------- Co-authored-by: Alice Cecile --- Cargo.toml | 12 +++ crates/bevy_dev_tools/Cargo.toml | 6 ++ crates/bevy_dev_tools/src/fps_overlay.rs | 98 ++++++++++++++++++++++++ crates/bevy_dev_tools/src/lib.rs | 2 + examples/README.md | 7 ++ examples/dev_tools/fps_overlay.rs | 65 ++++++++++++++++ 6 files changed, 190 insertions(+) create mode 100644 crates/bevy_dev_tools/src/fps_overlay.rs create mode 100644 examples/dev_tools/fps_overlay.rs diff --git a/Cargo.toml b/Cargo.toml index fd5343af55a8f..ab9da009fd4d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2763,6 +2763,18 @@ description = "A scene showcasing light gizmos" category = "Gizmos" wasm = true +[[example]] +name = "fps_overlay" +path = "examples/dev_tools/fps_overlay.rs" +doc-scrape-examples = true +required-features = ["bevy_dev_tools"] + +[package.metadata.example.fps_overlay] +name = "FPS overlay" +description = "Demonstrates FPS overlay" +category = "Dev tools" +wasm = true + [profile.wasm-release] inherits = "release" opt-level = "z" diff --git a/crates/bevy_dev_tools/Cargo.toml b/crates/bevy_dev_tools/Cargo.toml index 45c7c561872b6..7be2541583148 100644 --- a/crates/bevy_dev_tools/Cargo.toml +++ b/crates/bevy_dev_tools/Cargo.toml @@ -19,6 +19,12 @@ bevy_ecs = { path = "../bevy_ecs", version = "0.14.0-dev" } bevy_render = { path = "../bevy_render", version = "0.14.0-dev" } bevy_time = { path = "../bevy_time", version = "0.14.0-dev" } bevy_window = { path = "../bevy_window", version = "0.14.0-dev" } +bevy_asset = { path = "../bevy_asset", version = "0.14.0-dev" } +bevy_ui = { path = "../bevy_ui", version = "0.14.0-dev" } +bevy_text = { path = "../bevy_text", version = "0.14.0-dev" } +bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.14.0-dev" } +bevy_color = { path = "../bevy_color", version = "0.14.0-dev" } +bevy_input = { path = "../bevy_input", version = "0.14.0-dev" } # other serde = { version = "1.0", features = ["derive"], optional = true } diff --git a/crates/bevy_dev_tools/src/fps_overlay.rs b/crates/bevy_dev_tools/src/fps_overlay.rs new file mode 100644 index 0000000000000..51b8e7886dcf9 --- /dev/null +++ b/crates/bevy_dev_tools/src/fps_overlay.rs @@ -0,0 +1,98 @@ +//! Module containing logic for FPS overlay. + +use bevy_app::{Plugin, Startup, Update}; +use bevy_asset::Handle; +use bevy_color::Color; +use bevy_diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin}; +use bevy_ecs::{ + component::Component, + query::With, + schedule::{common_conditions::resource_changed, IntoSystemConfigs}, + system::{Commands, Query, Res, Resource}, +}; +use bevy_text::{Font, Text, TextSection, TextStyle}; +use bevy_ui::node_bundles::TextBundle; + +/// A plugin that adds an FPS overlay to the Bevy application. +/// +/// This plugin will add the [`FrameTimeDiagnosticsPlugin`] if it wasn't added before. +/// +/// Note: It is recommended to use native overlay of rendering statistics when possible for lower overhead and more accurate results. +/// The correct way to do this will vary by platform: +/// - **Metal**: setting env variable `MTL_HUD_ENABLED=1` +#[derive(Default)] +pub struct FpsOverlayPlugin { + /// Starting configuration of overlay, this can be later be changed through [`FpsOverlayConfig`] resource. + pub config: FpsOverlayConfig, +} + +impl Plugin for FpsOverlayPlugin { + fn build(&self, app: &mut bevy_app::App) { + // TODO: Use plugin dependencies, see https://github.com/bevyengine/bevy/issues/69 + if !app.is_plugin_added::() { + app.add_plugins(FrameTimeDiagnosticsPlugin); + } + app.insert_resource(self.config.clone()) + .add_systems(Startup, setup) + .add_systems( + Update, + ( + customize_text.run_if(resource_changed::), + update_text, + ), + ); + } +} + +/// Configuration options for the FPS overlay. +#[derive(Resource, Clone)] +pub struct FpsOverlayConfig { + /// Configuration of text in the overlay. + pub text_config: TextStyle, +} + +impl Default for FpsOverlayConfig { + fn default() -> Self { + FpsOverlayConfig { + text_config: TextStyle { + font: Handle::::default(), + font_size: 32.0, + color: Color::WHITE, + }, + } + } +} + +#[derive(Component)] +struct FpsText; + +fn setup(mut commands: Commands, overlay_config: Res) { + commands.spawn(( + TextBundle::from_sections([ + TextSection::new("FPS: ", overlay_config.text_config.clone()), + TextSection::from_style(overlay_config.text_config.clone()), + ]), + FpsText, + )); +} + +fn update_text(diagnostic: Res, mut query: Query<&mut Text, With>) { + for mut text in &mut query { + if let Some(fps) = diagnostic.get(&FrameTimeDiagnosticsPlugin::FPS) { + if let Some(value) = fps.smoothed() { + text.sections[1].value = format!("{value:.2}"); + } + } + } +} + +fn customize_text( + overlay_config: Res, + mut query: Query<&mut Text, With>, +) { + for mut text in &mut query { + for section in text.sections.iter_mut() { + section.style = overlay_config.text_config.clone(); + } + } +} diff --git a/crates/bevy_dev_tools/src/lib.rs b/crates/bevy_dev_tools/src/lib.rs index b1f2ea1142809..adad8cec9030b 100644 --- a/crates/bevy_dev_tools/src/lib.rs +++ b/crates/bevy_dev_tools/src/lib.rs @@ -3,8 +3,10 @@ #![cfg_attr(docsrs, feature(doc_auto_cfg))] use bevy_app::prelude::*; + #[cfg(feature = "bevy_ci_testing")] pub mod ci_testing; +pub mod fps_overlay; /// Enables developer tools in an [`App`]. This plugin is added automatically with `bevy_dev_tools` /// feature. diff --git a/examples/README.md b/examples/README.md index c7e19f1e06323..47c59cb9069d0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -45,6 +45,7 @@ git checkout v0.4.0 - [Assets](#assets) - [Async Tasks](#async-tasks) - [Audio](#audio) + - [Dev tools](#dev-tools) - [Diagnostics](#diagnostics) - [ECS (Entity Component System)](#ecs-entity-component-system) - [Games](#games) @@ -219,6 +220,12 @@ Example | Description [Spatial Audio 2D](../examples/audio/spatial_audio_2d.rs) | Shows how to play spatial audio, and moving the emitter in 2D [Spatial Audio 3D](../examples/audio/spatial_audio_3d.rs) | Shows how to play spatial audio, and moving the emitter in 3D +## Dev tools + +Example | Description +--- | --- +[FPS overlay](../examples/dev_tools/fps_overlay.rs) | Demonstrates FPS overlay + ## Diagnostics Example | Description diff --git a/examples/dev_tools/fps_overlay.rs b/examples/dev_tools/fps_overlay.rs new file mode 100644 index 0000000000000..e7f4cada95462 --- /dev/null +++ b/examples/dev_tools/fps_overlay.rs @@ -0,0 +1,65 @@ +//! Showcase how to use and configure FPS overlay. + +use bevy::{ + dev_tools::fps_overlay::{FpsOverlayConfig, FpsOverlayPlugin}, + prelude::*, +}; + +fn main() { + App::new() + .add_plugins(( + DefaultPlugins, + FpsOverlayPlugin { + config: FpsOverlayConfig { + text_config: TextStyle { + // Here we define size of our overlay + font_size: 50.0, + // We can also change color of the overlay + color: Color::srgb(0.0, 1.0, 0.0), + // If we want, we can use a custom font + font: default(), + }, + }, + }, + )) + .add_systems(Startup, setup) + .add_systems(Update, customize_config) + .run(); +} + +fn setup(mut commands: Commands) { + // We need to spawn camera to see overlay + commands.spawn(Camera2dBundle::default()); + commands.spawn( + TextBundle::from_sections([ + TextSection::new( + "Press 1 to change color of the overlay.", + TextStyle { + font_size: 25.0, + ..default() + }, + ), + TextSection::new( + "\nPress 2 to change size of the overlay", + TextStyle { + font_size: 25.0, + ..default() + }, + ), + ]) + .with_style(Style { + justify_self: JustifySelf::Center, + ..default() + }), + ); +} + +fn customize_config(input: Res>, mut overlay: ResMut) { + if input.just_pressed(KeyCode::Digit1) { + // Changing resource will affect overlay + overlay.text_config.color = Color::srgb(1.0, 0.0, 0.0); + } + if input.just_pressed(KeyCode::Digit2) { + overlay.text_config.font_size -= 2.0; + } +}