1
1
//! Module containing logic for FPS overlay.
2
2
3
3
use bevy_app:: { Plugin , Startup , Update } ;
4
- use bevy_asset:: Handle ;
4
+ use bevy_asset:: { Assets , Handle } ;
5
5
use bevy_color:: Color ;
6
6
use bevy_diagnostic:: { DiagnosticsStore , FrameTimeDiagnosticsPlugin } ;
7
7
use bevy_ecs:: {
@@ -12,22 +12,31 @@ use bevy_ecs::{
12
12
query:: With ,
13
13
resource:: Resource ,
14
14
schedule:: { common_conditions:: resource_changed, IntoScheduleConfigs } ,
15
- system:: { Commands , Query , Res } ,
15
+ system:: { Commands , Query , Res , ResMut } ,
16
16
} ;
17
- use bevy_render:: view:: Visibility ;
17
+ use bevy_render:: { storage :: ShaderStorageBuffer , view:: Visibility } ;
18
18
use bevy_text:: { Font , TextColor , TextFont , TextSpan } ;
19
19
use bevy_time:: Time ;
20
20
use bevy_ui:: {
21
21
widget:: { Text , TextUiWriter } ,
22
- GlobalZIndex , Node , PositionType ,
22
+ FlexDirection , GlobalZIndex , Node , PositionType , Val ,
23
23
} ;
24
+ use bevy_ui_render:: prelude:: MaterialNode ;
24
25
use core:: time:: Duration ;
25
26
27
+ use crate :: frame_time_graph:: {
28
+ FrameTimeGraphConfigUniform , FrameTimeGraphPlugin , FrametimeGraphMaterial ,
29
+ } ;
30
+
26
31
/// [`GlobalZIndex`] used to render the fps overlay.
27
32
///
28
33
/// We use a number slightly under `i32::MAX` so you can render on top of it if you really need to.
29
34
pub const FPS_OVERLAY_ZINDEX : i32 = i32:: MAX - 32 ;
30
35
36
+ // Used to scale the frame time graph based on the fps text size
37
+ const FRAME_TIME_GRAPH_WIDTH_SCALE : f32 = 6.0 ;
38
+ const FRAME_TIME_GRAPH_HEIGHT_SCALE : f32 = 2.0 ;
39
+
31
40
/// A plugin that adds an FPS overlay to the Bevy application.
32
41
///
33
42
/// This plugin will add the [`FrameTimeDiagnosticsPlugin`] if it wasn't added before.
@@ -47,12 +56,18 @@ impl Plugin for FpsOverlayPlugin {
47
56
if !app. is_plugin_added :: < FrameTimeDiagnosticsPlugin > ( ) {
48
57
app. add_plugins ( FrameTimeDiagnosticsPlugin :: default ( ) ) ;
49
58
}
59
+
60
+ if !app. is_plugin_added :: < FrameTimeGraphPlugin > ( ) {
61
+ app. add_plugins ( FrameTimeGraphPlugin ) ;
62
+ }
63
+
50
64
app. insert_resource ( self . config . clone ( ) )
51
65
. add_systems ( Startup , setup)
52
66
. add_systems (
53
67
Update ,
54
68
(
55
- ( customize_text, toggle_display) . run_if ( resource_changed :: < FpsOverlayConfig > ) ,
69
+ ( toggle_display, customize_overlay)
70
+ . run_if ( resource_changed :: < FpsOverlayConfig > ) ,
56
71
update_text,
57
72
) ,
58
73
) ;
@@ -72,6 +87,8 @@ pub struct FpsOverlayConfig {
72
87
///
73
88
/// Defaults to once every 100 ms.
74
89
pub refresh_interval : Duration ,
90
+ /// Configuration of the frame time graph
91
+ pub frame_time_graph_config : FrameTimeGraphConfig ,
75
92
}
76
93
77
94
impl Default for FpsOverlayConfig {
@@ -85,19 +102,65 @@ impl Default for FpsOverlayConfig {
85
102
text_color : Color :: WHITE ,
86
103
enabled : true ,
87
104
refresh_interval : Duration :: from_millis ( 100 ) ,
105
+ // TODO set this to display refresh rate if possible
106
+ frame_time_graph_config : FrameTimeGraphConfig :: target_fps ( 60.0 ) ,
107
+ }
108
+ }
109
+ }
110
+
111
+ /// Configuration of the frame time graph
112
+ #[ derive( Clone , Copy ) ]
113
+ pub struct FrameTimeGraphConfig {
114
+ /// Is the graph visible
115
+ pub enabled : bool ,
116
+ /// The minimum acceptable FPS
117
+ ///
118
+ /// Anything below this will show a red bar
119
+ pub min_fps : f32 ,
120
+ /// The target FPS
121
+ ///
122
+ /// Anything above this will show a green bar
123
+ pub target_fps : f32 ,
124
+ }
125
+
126
+ impl FrameTimeGraphConfig {
127
+ /// Constructs a default config for a given target fps
128
+ pub fn target_fps ( target_fps : f32 ) -> Self {
129
+ Self {
130
+ target_fps,
131
+ ..Self :: default ( )
132
+ }
133
+ }
134
+ }
135
+
136
+ impl Default for FrameTimeGraphConfig {
137
+ fn default ( ) -> Self {
138
+ Self {
139
+ enabled : true ,
140
+ min_fps : 30.0 ,
141
+ target_fps : 60.0 ,
88
142
}
89
143
}
90
144
}
91
145
92
146
#[ derive( Component ) ]
93
147
struct FpsText ;
94
148
95
- fn setup ( mut commands : Commands , overlay_config : Res < FpsOverlayConfig > ) {
149
+ #[ derive( Component ) ]
150
+ struct FrameTimeGraph ;
151
+
152
+ fn setup (
153
+ mut commands : Commands ,
154
+ overlay_config : Res < FpsOverlayConfig > ,
155
+ mut frame_time_graph_materials : ResMut < Assets < FrametimeGraphMaterial > > ,
156
+ mut buffers : ResMut < Assets < ShaderStorageBuffer > > ,
157
+ ) {
96
158
commands
97
159
. spawn ( (
98
160
Node {
99
161
// We need to make sure the overlay doesn't affect the position of other UI nodes
100
162
position_type : PositionType :: Absolute ,
163
+ flex_direction : FlexDirection :: Column ,
101
164
..Default :: default ( )
102
165
} ,
103
166
// Render overlay on top of everything
@@ -111,6 +174,29 @@ fn setup(mut commands: Commands, overlay_config: Res<FpsOverlayConfig>) {
111
174
FpsText ,
112
175
) )
113
176
. with_child ( ( TextSpan :: default ( ) , overlay_config. text_config . clone ( ) ) ) ;
177
+
178
+ let font_size = overlay_config. text_config . font_size ;
179
+ p. spawn ( (
180
+ Node {
181
+ width : Val :: Px ( font_size * FRAME_TIME_GRAPH_WIDTH_SCALE ) ,
182
+ height : Val :: Px ( font_size * FRAME_TIME_GRAPH_HEIGHT_SCALE ) ,
183
+ display : if overlay_config. frame_time_graph_config . enabled {
184
+ bevy_ui:: Display :: DEFAULT
185
+ } else {
186
+ bevy_ui:: Display :: None
187
+ } ,
188
+ ..Default :: default ( )
189
+ } ,
190
+ MaterialNode :: from ( frame_time_graph_materials. add ( FrametimeGraphMaterial {
191
+ values : buffers. add ( ShaderStorageBuffer :: default ( ) ) ,
192
+ config : FrameTimeGraphConfigUniform :: new (
193
+ overlay_config. frame_time_graph_config . target_fps ,
194
+ overlay_config. frame_time_graph_config . min_fps ,
195
+ true ,
196
+ ) ,
197
+ } ) ) ,
198
+ FrameTimeGraph ,
199
+ ) ) ;
114
200
} ) ;
115
201
}
116
202
@@ -135,7 +221,7 @@ fn update_text(
135
221
}
136
222
}
137
223
138
- fn customize_text (
224
+ fn customize_overlay (
139
225
overlay_config : Res < FpsOverlayConfig > ,
140
226
query : Query < Entity , With < FpsText > > ,
141
227
mut writer : TextUiWriter ,
@@ -151,11 +237,25 @@ fn customize_text(
151
237
fn toggle_display (
152
238
overlay_config : Res < FpsOverlayConfig > ,
153
239
mut query : Query < & mut Visibility , With < FpsText > > ,
240
+ mut graph_style : Query < & mut Node , With < FrameTimeGraph > > ,
154
241
) {
155
242
for mut visibility in & mut query {
156
243
visibility. set_if_neq ( match overlay_config. enabled {
157
244
true => Visibility :: Visible ,
158
245
false => Visibility :: Hidden ,
159
246
} ) ;
160
247
}
248
+
249
+ if let Ok ( mut graph_style) = graph_style. single_mut ( ) {
250
+ if overlay_config. frame_time_graph_config . enabled {
251
+ // Scale the frame time graph based on the font size of the overlay
252
+ let font_size = overlay_config. text_config . font_size ;
253
+ graph_style. width = Val :: Px ( font_size * FRAME_TIME_GRAPH_WIDTH_SCALE ) ;
254
+ graph_style. height = Val :: Px ( font_size * FRAME_TIME_GRAPH_HEIGHT_SCALE ) ;
255
+
256
+ graph_style. display = bevy_ui:: Display :: DEFAULT ;
257
+ } else {
258
+ graph_style. display = bevy_ui:: Display :: None ;
259
+ }
260
+ }
161
261
}
0 commit comments