From 61694a45c223f47cb08816a5a96de3f5c1d34066 Mon Sep 17 00:00:00 2001 From: Afonso Lage Date: Wed, 1 Oct 2025 12:35:36 -0300 Subject: [PATCH 1/6] Added fps overlay position config --- crates/bevy_dev_tools/src/fps_overlay.rs | 23 +++++++++++++++++++++++ examples/dev_tools/fps_overlay.rs | 9 ++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/crates/bevy_dev_tools/src/fps_overlay.rs b/crates/bevy_dev_tools/src/fps_overlay.rs index ea5c0fde4318b..496db58ba914f 100644 --- a/crates/bevy_dev_tools/src/fps_overlay.rs +++ b/crates/bevy_dev_tools/src/fps_overlay.rs @@ -74,6 +74,22 @@ impl Plugin for FpsOverlayPlugin { } } +/// Configuration options for the FPS overlay position on the screen. +#[derive(Default, Copy, Clone)] +pub struct FpsOverlayPosition { + /// The horizontal position of the left edge of the overlay. + pub left: Val, + + /// The horizontal position of the right edge of the overlay. + pub right: Val, + + /// The vertical position of the top edge of the overlay. + pub top: Val, + + /// The vertical position of the bottom edge of the overlay. + pub bottom: Val, +} + /// Configuration options for the FPS overlay. #[derive(Resource, Clone)] pub struct FpsOverlayConfig { @@ -89,6 +105,8 @@ pub struct FpsOverlayConfig { pub refresh_interval: Duration, /// Configuration of the frame time graph pub frame_time_graph_config: FrameTimeGraphConfig, + /// Configuration of the overlay position. + pub position: FpsOverlayPosition, } impl Default for FpsOverlayConfig { @@ -104,6 +122,7 @@ impl Default for FpsOverlayConfig { refresh_interval: Duration::from_millis(100), // TODO set this to display refresh rate if possible frame_time_graph_config: FrameTimeGraphConfig::target_fps(60.0), + position: FpsOverlayPosition::default(), } } } @@ -161,6 +180,10 @@ fn setup( // We need to make sure the overlay doesn't affect the position of other UI nodes position_type: PositionType::Absolute, flex_direction: FlexDirection::Column, + top: overlay_config.position.top, + right: overlay_config.position.right, + bottom: overlay_config.position.bottom, + left: overlay_config.position.left, ..Default::default() }, // Render overlay on top of everything diff --git a/examples/dev_tools/fps_overlay.rs b/examples/dev_tools/fps_overlay.rs index 5637706c756ed..a86b478dd972a 100644 --- a/examples/dev_tools/fps_overlay.rs +++ b/examples/dev_tools/fps_overlay.rs @@ -1,7 +1,9 @@ //! Showcase how to use and configure FPS overlay. use bevy::{ - dev_tools::fps_overlay::{FpsOverlayConfig, FpsOverlayPlugin, FrameTimeGraphConfig}, + dev_tools::fps_overlay::{ + FpsOverlayConfig, FpsOverlayPlugin, FpsOverlayPosition, FrameTimeGraphConfig, + }, prelude::*, text::FontSmoothing, }; @@ -40,6 +42,11 @@ fn main() { // The target fps target_fps: 144.0, }, + position: FpsOverlayPosition { + top: px(1.0), + left: px(1.0), + ..default() + }, }, }, )) From 3ec38d73419e9a1a470b6c76507d729433bec958 Mon Sep 17 00:00:00 2001 From: Afonso Lage Date: Wed, 1 Oct 2025 12:48:03 -0300 Subject: [PATCH 2/6] Added fps overlay bar color config --- crates/bevy_dev_tools/src/fps_overlay.rs | 14 +++++++++++--- .../src/frame_time_graph/frame_time_graph.wgsl | 7 +++---- crates/bevy_dev_tools/src/frame_time_graph/mod.rs | 13 ++++++++++++- examples/dev_tools/fps_overlay.rs | 2 ++ 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/crates/bevy_dev_tools/src/fps_overlay.rs b/crates/bevy_dev_tools/src/fps_overlay.rs index 496db58ba914f..5c1e482d77007 100644 --- a/crates/bevy_dev_tools/src/fps_overlay.rs +++ b/crates/bevy_dev_tools/src/fps_overlay.rs @@ -2,7 +2,7 @@ use bevy_app::{Plugin, Startup, Update}; use bevy_asset::{Assets, Handle}; -use bevy_color::Color; +use bevy_color::{Color, LinearRgba}; use bevy_diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin}; use bevy_ecs::{ component::Component, @@ -134,12 +134,16 @@ pub struct FrameTimeGraphConfig { pub enabled: bool, /// The minimum acceptable FPS /// - /// Anything below this will show a red bar + /// Anything below this will show a `min_color` bar pub min_fps: f32, /// The target FPS /// - /// Anything above this will show a green bar + /// Anything above this will show a `max_color` bar pub target_fps: f32, + /// The color of the bar when having lower values + pub min_color: LinearRgba, + /// The color of the bar when having higher values + pub max_color: LinearRgba, } impl FrameTimeGraphConfig { @@ -158,6 +162,8 @@ impl Default for FrameTimeGraphConfig { enabled: true, min_fps: 30.0, target_fps: 60.0, + min_color: LinearRgba::GREEN, + max_color: LinearRgba::RED, } } } @@ -225,6 +231,8 @@ fn setup( overlay_config.frame_time_graph_config.target_fps, overlay_config.frame_time_graph_config.min_fps, true, + overlay_config.frame_time_graph_config.min_color, + overlay_config.frame_time_graph_config.max_color, ), })), FrameTimeGraph, diff --git a/crates/bevy_dev_tools/src/frame_time_graph/frame_time_graph.wgsl b/crates/bevy_dev_tools/src/frame_time_graph/frame_time_graph.wgsl index 82b5a46cc71d6..af73b656181bd 100644 --- a/crates/bevy_dev_tools/src/frame_time_graph/frame_time_graph.wgsl +++ b/crates/bevy_dev_tools/src/frame_time_graph/frame_time_graph.wgsl @@ -7,16 +7,15 @@ struct Config { dt_min_log2: f32, dt_max_log2: f32, proportional_width: u32, + min_color: vec4, + max_color: vec4, } @group(1) @binding(1) var config: Config; -const RED: vec4 = vec4(1.0, 0.0, 0.0, 1.0); -const GREEN: vec4 = vec4(0.0, 1.0, 0.0, 1.0); - // Gets a color based on the delta time // TODO use customizable gradient fn color_from_dt(dt: f32) -> vec4 { - return mix(GREEN, RED, dt / config.dt_max); + return mix(config.min_color, config.max_color, dt / config.dt_max); } // Draw an SDF square diff --git a/crates/bevy_dev_tools/src/frame_time_graph/mod.rs b/crates/bevy_dev_tools/src/frame_time_graph/mod.rs index 8412b186531d9..386393ee84f3b 100644 --- a/crates/bevy_dev_tools/src/frame_time_graph/mod.rs +++ b/crates/bevy_dev_tools/src/frame_time_graph/mod.rs @@ -2,6 +2,7 @@ use bevy_app::{Plugin, Update}; use bevy_asset::{load_internal_asset, uuid_handle, Asset, Assets, Handle}; +use bevy_color::LinearRgba; use bevy_diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin}; use bevy_ecs::system::{Res, ResMut}; use bevy_math::ops::log2; @@ -52,11 +53,19 @@ pub struct FrameTimeGraphConfigUniform { dt_max_log2: f32, // controls whether or not the bars width are proportional to their delta time proportional_width: u32, + min_color: LinearRgba, + max_color: LinearRgba, } impl FrameTimeGraphConfigUniform { /// `proportional_width`: controls whether or not the bars width are proportional to their delta time - pub fn new(target_fps: f32, min_fps: f32, proportional_width: bool) -> Self { + pub fn new( + target_fps: f32, + min_fps: f32, + proportional_width: bool, + min_color: LinearRgba, + max_color: LinearRgba, + ) -> Self { // we want an upper limit that is above the target otherwise the bars will disappear let dt_min = 1. / (target_fps * 1.2); let dt_max = 1. / min_fps; @@ -66,6 +75,8 @@ impl FrameTimeGraphConfigUniform { dt_min_log2: log2(dt_min), dt_max_log2: log2(dt_max), proportional_width: u32::from(proportional_width), + min_color, + max_color, } } } diff --git a/examples/dev_tools/fps_overlay.rs b/examples/dev_tools/fps_overlay.rs index a86b478dd972a..239467496c50b 100644 --- a/examples/dev_tools/fps_overlay.rs +++ b/examples/dev_tools/fps_overlay.rs @@ -41,6 +41,8 @@ fn main() { min_fps: 30.0, // The target fps target_fps: 144.0, + min_color: LinearRgba::GREEN, + max_color: LinearRgba::RED, }, position: FpsOverlayPosition { top: px(1.0), From 661c14bbdc4572bbe50c9a924747cbbe74baecc3 Mon Sep 17 00:00:00 2001 From: Afonso Lage Date: Wed, 1 Oct 2025 13:08:35 -0300 Subject: [PATCH 3/6] Added better enable/disable settings --- crates/bevy_dev_tools/src/fps_overlay.rs | 65 +++++++++++++++--------- examples/dev_tools/fps_overlay.rs | 52 ++++++++++++------- 2 files changed, 74 insertions(+), 43 deletions(-) diff --git a/crates/bevy_dev_tools/src/fps_overlay.rs b/crates/bevy_dev_tools/src/fps_overlay.rs index 5c1e482d77007..02b10cd951322 100644 --- a/crates/bevy_dev_tools/src/fps_overlay.rs +++ b/crates/bevy_dev_tools/src/fps_overlay.rs @@ -76,29 +76,49 @@ impl Plugin for FpsOverlayPlugin { /// Configuration options for the FPS overlay position on the screen. #[derive(Default, Copy, Clone)] -pub struct FpsOverlayPosition { +pub struct FpsOverlayPositionConfig { /// The horizontal position of the left edge of the overlay. pub left: Val, - /// The horizontal position of the right edge of the overlay. pub right: Val, - /// The vertical position of the top edge of the overlay. pub top: Val, - /// The vertical position of the bottom edge of the overlay. pub bottom: Val, } /// Configuration options for the FPS overlay. #[derive(Resource, Clone)] -pub struct FpsOverlayConfig { +pub struct FpsOverlayTextConfig { /// Configuration of text in the overlay. - pub text_config: TextFont, + pub font: TextFont, /// Color of text in the overlay. - pub text_color: Color, + pub color: Color, + /// Displays the FPS text overlay if true. + pub enabled: bool, +} + +impl Default for FpsOverlayTextConfig { + fn default() -> Self { + Self { + font: TextFont { + font: Handle::::default(), + font_size: 32.0, + ..Default::default() + }, + color: Color::WHITE, + enabled: true, + } + } +} + +/// Configuration options for the FPS overlay. +#[derive(Resource, Clone)] +pub struct FpsOverlayConfig { /// Displays the FPS overlay if true. pub enabled: bool, + /// Configuration of text in the overlay. + pub text_config: FpsOverlayTextConfig, /// The period after which the FPS overlay re-renders. /// /// Defaults to once every 100 ms. @@ -106,23 +126,18 @@ pub struct FpsOverlayConfig { /// Configuration of the frame time graph pub frame_time_graph_config: FrameTimeGraphConfig, /// Configuration of the overlay position. - pub position: FpsOverlayPosition, + pub position: FpsOverlayPositionConfig, } impl Default for FpsOverlayConfig { fn default() -> Self { FpsOverlayConfig { - text_config: TextFont { - font: Handle::::default(), - font_size: 32.0, - ..Default::default() - }, - text_color: Color::WHITE, + text_config: FpsOverlayTextConfig::default(), enabled: true, refresh_interval: Duration::from_millis(100), // TODO set this to display refresh rate if possible frame_time_graph_config: FrameTimeGraphConfig::target_fps(60.0), - position: FpsOverlayPosition::default(), + position: FpsOverlayPositionConfig::default(), } } } @@ -199,14 +214,14 @@ fn setup( .with_children(|p| { p.spawn(( Text::new("FPS: "), - overlay_config.text_config.clone(), - TextColor(overlay_config.text_color), + overlay_config.text_config.font.clone(), + TextColor(overlay_config.text_config.color), FpsText, Pickable::IGNORE, )) - .with_child((TextSpan::default(), overlay_config.text_config.clone())); + .with_child((TextSpan::default(), overlay_config.text_config.font.clone())); - let font_size = overlay_config.text_config.font_size; + let font_size = overlay_config.text_config.font.font_size; p.spawn(( Node { width: Val::Px(font_size * FRAME_TIME_GRAPH_WIDTH_SCALE), @@ -268,9 +283,11 @@ fn customize_overlay( ) { for entity in &query { writer.for_each_font(entity, |mut font| { - *font = overlay_config.text_config.clone(); + *font = overlay_config.text_config.font.clone(); + }); + writer.for_each_color(entity, |mut color| { + color.0 = overlay_config.text_config.color; }); - writer.for_each_color(entity, |mut color| color.0 = overlay_config.text_color); } } @@ -279,15 +296,15 @@ fn toggle_display( mut text_node: Single<&mut Node, (With, Without)>, mut graph_node: Single<&mut Node, (With, Without)>, ) { - if overlay_config.enabled { + if overlay_config.enabled && overlay_config.text_config.enabled { text_node.display = bevy_ui::Display::DEFAULT; } else { text_node.display = bevy_ui::Display::None; } - if overlay_config.frame_time_graph_config.enabled { + if overlay_config.enabled && overlay_config.frame_time_graph_config.enabled { // Scale the frame time graph based on the font size of the overlay - let font_size = overlay_config.text_config.font_size; + let font_size = overlay_config.text_config.font.font_size; graph_node.width = Val::Px(font_size * FRAME_TIME_GRAPH_WIDTH_SCALE); graph_node.height = Val::Px(font_size * FRAME_TIME_GRAPH_HEIGHT_SCALE); diff --git a/examples/dev_tools/fps_overlay.rs b/examples/dev_tools/fps_overlay.rs index 239467496c50b..aba64c24a13ed 100644 --- a/examples/dev_tools/fps_overlay.rs +++ b/examples/dev_tools/fps_overlay.rs @@ -2,7 +2,8 @@ use bevy::{ dev_tools::fps_overlay::{ - FpsOverlayConfig, FpsOverlayPlugin, FpsOverlayPosition, FrameTimeGraphConfig, + FpsOverlayConfig, FpsOverlayPlugin, FpsOverlayPositionConfig, FpsOverlayTextConfig, + FrameTimeGraphConfig, }, prelude::*, text::FontSmoothing, @@ -21,19 +22,25 @@ fn main() { DefaultPlugins, FpsOverlayPlugin { config: FpsOverlayConfig { - text_config: TextFont { - // Here we define size of our overlay - font_size: 42.0, - // If we want, we can use a custom font - font: default(), - // We could also disable font smoothing, - font_smoothing: FontSmoothing::default(), - ..default() + // Configure the fps text on the overlay + text_config: FpsOverlayTextConfig { + // Enable or disable only the fps text + enabled: true, + font: TextFont { + // Here we define size of our overlay + font_size: 42.0, + // If we want, we can use a custom font + font: default(), + // We could also disable font smoothing, + font_smoothing: FontSmoothing::default(), + ..default() + }, + // We can also change color of the overlay + color: OverlayColor::GREEN, }, - // We can also change color of the overlay - text_color: OverlayColor::GREEN, // We can also set the refresh interval for the FPS counter refresh_interval: core::time::Duration::from_millis(100), + // Enable or disable the entire fps overlay enabled: true, frame_time_graph_config: FrameTimeGraphConfig { enabled: true, @@ -41,10 +48,13 @@ fn main() { min_fps: 30.0, // The target fps target_fps: 144.0, + // When the bar is low, this color will be used min_color: LinearRgba::GREEN, + // When the bar is high, this color will be used max_color: LinearRgba::RED, }, - position: FpsOverlayPosition { + // Set custom positioning of the overlay. Defaults to top-left on the screen. + position: FpsOverlayPositionConfig { top: px(1.0), left: px(1.0), ..default() @@ -68,8 +78,9 @@ fn setup(mut commands: Commands) { "Press 1 to toggle the overlay color.\n", "Press 2 to decrease the overlay size.\n", "Press 3 to increase the overlay size.\n", - "Press 4 to toggle the text visibility.\n", - "Press 5 to toggle the frame time graph." + "Press 4 to toggle the overlay visibility.\n", + "Press 5 to toggle the frame time graph.\n", + "Press 6 to toggle the text visibility.", )), Node { position_type: PositionType::Absolute, @@ -83,17 +94,17 @@ fn setup(mut commands: Commands) { fn customize_config(input: Res>, mut overlay: ResMut) { if input.just_pressed(KeyCode::Digit1) { // Changing resource will affect overlay - if overlay.text_color == OverlayColor::GREEN { - overlay.text_color = OverlayColor::RED; + if overlay.text_config.color == OverlayColor::GREEN { + overlay.text_config.color = OverlayColor::RED; } else { - overlay.text_color = OverlayColor::GREEN; + overlay.text_config.color = OverlayColor::GREEN; } } if input.just_pressed(KeyCode::Digit2) { - overlay.text_config.font_size -= 2.0; + overlay.text_config.font.font_size -= 2.0; } if input.just_pressed(KeyCode::Digit3) { - overlay.text_config.font_size += 2.0; + overlay.text_config.font.font_size += 2.0; } if input.just_pressed(KeyCode::Digit4) { overlay.enabled = !overlay.enabled; @@ -101,4 +112,7 @@ fn customize_config(input: Res>, mut overlay: ResMut Date: Wed, 1 Oct 2025 16:45:55 -0300 Subject: [PATCH 4/6] Fixed broken test --- tests/window/desktop_request_redraw.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/window/desktop_request_redraw.rs b/tests/window/desktop_request_redraw.rs index ccaafd86579db..4a75928e73052 100644 --- a/tests/window/desktop_request_redraw.rs +++ b/tests/window/desktop_request_redraw.rs @@ -1,6 +1,6 @@ //! Desktop request redraw use bevy::{ - dev_tools::fps_overlay::{FpsOverlayConfig, FpsOverlayPlugin}, + dev_tools::fps_overlay::{FpsOverlayConfig, FpsOverlayPlugin, FpsOverlayTextConfig}, prelude::*, window::RequestRedraw, winit::WinitSettings, @@ -18,11 +18,14 @@ fn main() { // Left and Right clicking the cube should roggle rotation on/off. .add_plugins(FpsOverlayPlugin { config: FpsOverlayConfig { - text_config: TextFont { - font_size: 12.0, + text_config: FpsOverlayTextConfig { + font: TextFont { + font_size: 12.0, + ..default() + }, + color: Color::srgb(0.0, 1.0, 0.0), ..default() }, - text_color: Color::srgb(0.0, 1.0, 0.0), refresh_interval: core::time::Duration::from_millis(16), ..default() }, From 465ca9bb0d3204e01edc814668a406b947b27be5 Mon Sep 17 00:00:00 2001 From: Afonso Lage Date: Thu, 2 Oct 2025 17:51:11 -0300 Subject: [PATCH 5/6] Renamed frame_time_graph_config to graph_config --- crates/bevy_dev_tools/src/fps_overlay.rs | 16 ++++++++-------- .../bevy_dev_tools/src/frame_time_graph/mod.rs | 2 +- examples/dev_tools/fps_overlay.rs | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/bevy_dev_tools/src/fps_overlay.rs b/crates/bevy_dev_tools/src/fps_overlay.rs index 02b10cd951322..120f10d3a94b1 100644 --- a/crates/bevy_dev_tools/src/fps_overlay.rs +++ b/crates/bevy_dev_tools/src/fps_overlay.rs @@ -124,7 +124,7 @@ pub struct FpsOverlayConfig { /// Defaults to once every 100 ms. pub refresh_interval: Duration, /// Configuration of the frame time graph - pub frame_time_graph_config: FrameTimeGraphConfig, + pub graph_config: FrameTimeGraphConfig, /// Configuration of the overlay position. pub position: FpsOverlayPositionConfig, } @@ -136,7 +136,7 @@ impl Default for FpsOverlayConfig { enabled: true, refresh_interval: Duration::from_millis(100), // TODO set this to display refresh rate if possible - frame_time_graph_config: FrameTimeGraphConfig::target_fps(60.0), + graph_config: FrameTimeGraphConfig::target_fps(60.0), position: FpsOverlayPositionConfig::default(), } } @@ -226,7 +226,7 @@ fn setup( Node { width: Val::Px(font_size * FRAME_TIME_GRAPH_WIDTH_SCALE), height: Val::Px(font_size * FRAME_TIME_GRAPH_HEIGHT_SCALE), - display: if overlay_config.frame_time_graph_config.enabled { + display: if overlay_config.graph_config.enabled { bevy_ui::Display::DEFAULT } else { bevy_ui::Display::None @@ -243,11 +243,11 @@ fn setup( ..Default::default() }), config: FrameTimeGraphConfigUniform::new( - overlay_config.frame_time_graph_config.target_fps, - overlay_config.frame_time_graph_config.min_fps, + overlay_config.graph_config.target_fps, + overlay_config.graph_config.min_fps, true, - overlay_config.frame_time_graph_config.min_color, - overlay_config.frame_time_graph_config.max_color, + overlay_config.graph_config.min_color, + overlay_config.graph_config.max_color, ), })), FrameTimeGraph, @@ -302,7 +302,7 @@ fn toggle_display( text_node.display = bevy_ui::Display::None; } - if overlay_config.enabled && overlay_config.frame_time_graph_config.enabled { + if overlay_config.enabled && overlay_config.graph_config.enabled { // Scale the frame time graph based on the font size of the overlay let font_size = overlay_config.text_config.font.font_size; graph_node.width = Val::Px(font_size * FRAME_TIME_GRAPH_WIDTH_SCALE); diff --git a/crates/bevy_dev_tools/src/frame_time_graph/mod.rs b/crates/bevy_dev_tools/src/frame_time_graph/mod.rs index 386393ee84f3b..97883d1bb5686 100644 --- a/crates/bevy_dev_tools/src/frame_time_graph/mod.rs +++ b/crates/bevy_dev_tools/src/frame_time_graph/mod.rs @@ -107,7 +107,7 @@ fn update_frame_time_values( diagnostics_store: Res, config: Option>, ) { - if !config.is_none_or(|c| c.frame_time_graph_config.enabled) { + if !config.is_none_or(|c| c.graph_config.enabled) { return; } let Some(frame_time) = diagnostics_store.get(&FrameTimeDiagnosticsPlugin::FRAME_TIME) else { diff --git a/examples/dev_tools/fps_overlay.rs b/examples/dev_tools/fps_overlay.rs index aba64c24a13ed..d8ad414c5843e 100644 --- a/examples/dev_tools/fps_overlay.rs +++ b/examples/dev_tools/fps_overlay.rs @@ -42,7 +42,7 @@ fn main() { refresh_interval: core::time::Duration::from_millis(100), // Enable or disable the entire fps overlay enabled: true, - frame_time_graph_config: FrameTimeGraphConfig { + graph_config: FrameTimeGraphConfig { enabled: true, // The minimum acceptable fps min_fps: 30.0, From 2cba4c7859664d49bd4271e89491cb369e723e0c Mon Sep 17 00:00:00 2001 From: Afonso Lage Date: Thu, 2 Oct 2025 18:11:54 -0300 Subject: [PATCH 6/6] Fixed example --- examples/dev_tools/fps_overlay.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/dev_tools/fps_overlay.rs b/examples/dev_tools/fps_overlay.rs index d8ad414c5843e..e2bc70777e42b 100644 --- a/examples/dev_tools/fps_overlay.rs +++ b/examples/dev_tools/fps_overlay.rs @@ -110,7 +110,7 @@ fn customize_config(input: Res>, mut overlay: ResMut