Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 69 additions & 21 deletions crates/bevy_dev_tools/src/fps_overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -74,36 +74,70 @@ impl Plugin for FpsOverlayPlugin {
}
}

/// Configuration options for the FPS overlay position on the screen.
#[derive(Default, Copy, Clone)]
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::<Font>::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.
pub refresh_interval: Duration,
/// Configuration of the frame time graph
pub frame_time_graph_config: FrameTimeGraphConfig,
/// Configuration of the overlay position.
pub position: FpsOverlayPositionConfig,
}

impl Default for FpsOverlayConfig {
fn default() -> Self {
FpsOverlayConfig {
text_config: TextFont {
font: Handle::<Font>::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: FpsOverlayPositionConfig::default(),
}
}
}
Expand All @@ -115,12 +149,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 {
Expand All @@ -139,6 +177,8 @@ impl Default for FrameTimeGraphConfig {
enabled: true,
min_fps: 30.0,
target_fps: 60.0,
min_color: LinearRgba::GREEN,
max_color: LinearRgba::RED,
}
}
}
Expand All @@ -161,6 +201,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
Expand All @@ -170,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),
Expand All @@ -202,6 +246,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,
Expand Down Expand Up @@ -237,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);
}
}

Expand All @@ -248,15 +296,15 @@ fn toggle_display(
mut text_node: Single<&mut Node, (With<FpsText>, Without<FrameTimeGraph>)>,
mut graph_node: Single<&mut Node, (With<FrameTimeGraph>, Without<FpsText>)>,
) {
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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ struct Config {
dt_min_log2: f32,
dt_max_log2: f32,
proportional_width: u32,
min_color: vec4<f32>,
max_color: vec4<f32>,
}
@group(1) @binding(1) var<uniform> config: Config;

const RED: vec4<f32> = vec4(1.0, 0.0, 0.0, 1.0);
const GREEN: vec4<f32> = 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<f32> {
return mix(GREEN, RED, dt / config.dt_max);
return mix(config.min_color, config.max_color, dt / config.dt_max);
}

// Draw an SDF square
Expand Down
13 changes: 12 additions & 1 deletion crates/bevy_dev_tools/src/frame_time_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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,
}
}
}
Expand Down
59 changes: 41 additions & 18 deletions examples/dev_tools/fps_overlay.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! Showcase how to use and configure FPS overlay.

use bevy::{
dev_tools::fps_overlay::{FpsOverlayConfig, FpsOverlayPlugin, FrameTimeGraphConfig},
dev_tools::fps_overlay::{
FpsOverlayConfig, FpsOverlayPlugin, FpsOverlayPositionConfig, FpsOverlayTextConfig,
FrameTimeGraphConfig,
},
prelude::*,
text::FontSmoothing,
};
Expand All @@ -19,26 +22,42 @@ 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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In terms of enabled , I suggest rename

  • in FrameTimeGraphConfig change enabled to graph_enabled
  • in FpsOverlayTextConfig change enabled to text_enabled

And then for FpsOverlayConfig remove enabled and replace any usage with frame_time_graph_config.graph_enabled || text_config.text_enabled

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well rename frame_time_graph_config to graph_config

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with removing enabled from FpsOverlayConfig is that if the user wanna hide the overlay, like using some toggle key, it needs to work with two nested enabled properties, like described in #21201. Ideally to hide/show there should be only one property config for the entire overlay.

I'm ok with the other suggestions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe set_enabled(bool) could be a method that sets both text_enabled and graph_enabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still keeps the problem of "verbosity", because Bevy uses a lot of "declaration config". Imagine that I wanna it disabled so I can enable it with hotkey later on:

app.add_plugins(FpsOverlayPlugin {
    config: FpsOverlayConfig {
       text_config: FpsOverlayTextConfig {
           enabled: false,
           ..default()
       },
       frame_time_graph_config: FrameTimeGraphConfig {
            enabled: false,
            ..default()
        },
        ..default()
    },
});

vs

app.add_plugins(FpsOverlayPlugin {
    config: FpsOverlayConfig {
       enabled: false,
        ..default()
    },
});

// The minimum acceptable fps
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,
},
// Set custom positioning of the overlay. Defaults to top-left on the screen.
position: FpsOverlayPositionConfig {
top: px(1.0),
left: px(1.0),
..default()
},
},
},
Expand All @@ -59,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,
Expand All @@ -74,22 +94,25 @@ fn setup(mut commands: Commands) {
fn customize_config(input: Res<ButtonInput<KeyCode>>, mut overlay: ResMut<FpsOverlayConfig>) {
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;
}
if input.just_released(KeyCode::Digit5) {
overlay.frame_time_graph_config.enabled = !overlay.frame_time_graph_config.enabled;
}
if input.just_released(KeyCode::Digit6) {
overlay.text_config.enabled = !overlay.text_config.enabled;
}
}
11 changes: 7 additions & 4 deletions tests/window/desktop_request_redraw.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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()
},
Expand Down