Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/diagnostics/log_diagnostics.rs
6592 views
1
//! Shows different built-in plugins that logs diagnostics, like frames per second (FPS), to the console.
2
3
use bevy::{
4
color::palettes,
5
diagnostic::{
6
DiagnosticPath, EntityCountDiagnosticsPlugin, FrameTimeDiagnosticsPlugin,
7
LogDiagnosticsPlugin, LogDiagnosticsState, SystemInformationDiagnosticsPlugin,
8
},
9
prelude::*,
10
};
11
12
const FRAME_TIME_DIAGNOSTICS: [DiagnosticPath; 3] = [
13
FrameTimeDiagnosticsPlugin::FPS,
14
FrameTimeDiagnosticsPlugin::FRAME_COUNT,
15
FrameTimeDiagnosticsPlugin::FRAME_TIME,
16
];
17
const ENTITY_COUNT_DIAGNOSTICS: [DiagnosticPath; 1] = [EntityCountDiagnosticsPlugin::ENTITY_COUNT];
18
const SYSTEM_INFO_DIAGNOSTICS: [DiagnosticPath; 4] = [
19
SystemInformationDiagnosticsPlugin::PROCESS_CPU_USAGE,
20
SystemInformationDiagnosticsPlugin::PROCESS_MEM_USAGE,
21
SystemInformationDiagnosticsPlugin::SYSTEM_CPU_USAGE,
22
SystemInformationDiagnosticsPlugin::SYSTEM_MEM_USAGE,
23
];
24
25
fn main() {
26
App::new()
27
.add_plugins((
28
// The diagnostics plugins need to be added after DefaultPlugins as they use e.g. the time plugin for timestamps.
29
DefaultPlugins,
30
// Adds a system that prints diagnostics to the console.
31
// The other diagnostics plugins can still be used without this if you want to use them in an ingame overlay for example.
32
LogDiagnosticsPlugin::default(),
33
// Adds frame time, FPS and frame count diagnostics.
34
FrameTimeDiagnosticsPlugin::default(),
35
// Adds an entity count diagnostic.
36
EntityCountDiagnosticsPlugin::default(),
37
// Adds cpu and memory usage diagnostics for systems and the entire game process.
38
SystemInformationDiagnosticsPlugin,
39
// Forwards various diagnostics from the render app to the main app.
40
// These are pretty verbose but can be useful to pinpoint performance issues.
41
bevy::render::diagnostic::RenderDiagnosticsPlugin,
42
))
43
// No rendering diagnostics are emitted unless something is drawn to the screen,
44
// so we spawn a small scene.
45
.add_systems(Startup, setup)
46
.add_systems(Update, filters_inputs)
47
.add_systems(
48
Update,
49
update_commands.run_if(
50
resource_exists_and_changed::<LogDiagnosticsStatus>
51
.or(resource_exists_and_changed::<LogDiagnosticsFilters>),
52
),
53
)
54
.run();
55
}
56
57
/// set up a simple 3D scene
58
fn setup(
59
mut commands: Commands,
60
mut meshes: ResMut<Assets<Mesh>>,
61
mut materials: ResMut<Assets<StandardMaterial>>,
62
) {
63
// circular base
64
commands.spawn((
65
Mesh3d(meshes.add(Circle::new(4.0))),
66
MeshMaterial3d(materials.add(Color::WHITE)),
67
Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
68
));
69
// cube
70
commands.spawn((
71
Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
72
MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
73
Transform::from_xyz(0.0, 0.5, 0.0),
74
));
75
// light
76
commands.spawn((
77
PointLight {
78
shadows_enabled: true,
79
..default()
80
},
81
Transform::from_xyz(4.0, 8.0, 4.0),
82
));
83
// camera
84
commands.spawn((
85
Camera3d::default(),
86
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
87
));
88
89
commands.init_resource::<LogDiagnosticsFilters>();
90
commands.init_resource::<LogDiagnosticsStatus>();
91
92
commands.spawn((
93
LogDiagnosticsCommands,
94
Node {
95
top: px(5),
96
left: px(5),
97
flex_direction: FlexDirection::Column,
98
..default()
99
},
100
));
101
}
102
103
fn filters_inputs(
104
keys: Res<ButtonInput<KeyCode>>,
105
mut status: ResMut<LogDiagnosticsStatus>,
106
mut filters: ResMut<LogDiagnosticsFilters>,
107
mut log_state: ResMut<LogDiagnosticsState>,
108
) {
109
if keys.just_pressed(KeyCode::KeyQ) {
110
*status = match *status {
111
LogDiagnosticsStatus::Enabled => {
112
log_state.disable_filtering();
113
LogDiagnosticsStatus::Disabled
114
}
115
LogDiagnosticsStatus::Disabled => {
116
log_state.enable_filtering();
117
if filters.frame_time {
118
enable_filters(&mut log_state, FRAME_TIME_DIAGNOSTICS);
119
}
120
if filters.entity_count {
121
enable_filters(&mut log_state, ENTITY_COUNT_DIAGNOSTICS);
122
}
123
if filters.system_info {
124
enable_filters(&mut log_state, SYSTEM_INFO_DIAGNOSTICS);
125
}
126
LogDiagnosticsStatus::Enabled
127
}
128
};
129
}
130
131
let enabled = *status == LogDiagnosticsStatus::Enabled;
132
if keys.just_pressed(KeyCode::Digit1) {
133
filters.frame_time = !filters.frame_time;
134
if enabled {
135
if filters.frame_time {
136
enable_filters(&mut log_state, FRAME_TIME_DIAGNOSTICS);
137
} else {
138
disable_filters(&mut log_state, FRAME_TIME_DIAGNOSTICS);
139
}
140
}
141
}
142
if keys.just_pressed(KeyCode::Digit2) {
143
filters.entity_count = !filters.entity_count;
144
if enabled {
145
if filters.entity_count {
146
enable_filters(&mut log_state, ENTITY_COUNT_DIAGNOSTICS);
147
} else {
148
disable_filters(&mut log_state, ENTITY_COUNT_DIAGNOSTICS);
149
}
150
}
151
}
152
if keys.just_pressed(KeyCode::Digit3) {
153
filters.system_info = !filters.system_info;
154
if enabled {
155
if filters.system_info {
156
enable_filters(&mut log_state, SYSTEM_INFO_DIAGNOSTICS);
157
} else {
158
disable_filters(&mut log_state, SYSTEM_INFO_DIAGNOSTICS);
159
}
160
}
161
}
162
}
163
164
fn enable_filters(
165
log_state: &mut LogDiagnosticsState,
166
diagnostics: impl IntoIterator<Item = DiagnosticPath>,
167
) {
168
log_state.extend_filter(diagnostics);
169
}
170
171
fn disable_filters(
172
log_state: &mut LogDiagnosticsState,
173
diagnostics: impl IntoIterator<Item = DiagnosticPath>,
174
) {
175
for diagnostic in diagnostics {
176
log_state.remove_filter(&diagnostic);
177
}
178
}
179
180
fn update_commands(
181
mut commands: Commands,
182
log_commands: Single<Entity, With<LogDiagnosticsCommands>>,
183
status: Res<LogDiagnosticsStatus>,
184
filters: Res<LogDiagnosticsFilters>,
185
) {
186
let enabled = *status == LogDiagnosticsStatus::Enabled;
187
let alpha = if enabled { 1. } else { 0.25 };
188
let enabled_color = |enabled| {
189
if enabled {
190
Color::from(palettes::tailwind::GREEN_400)
191
} else {
192
Color::from(palettes::tailwind::RED_400)
193
}
194
};
195
commands
196
.entity(*log_commands)
197
.despawn_related::<Children>()
198
.insert(children![
199
(
200
Node {
201
flex_direction: FlexDirection::Row,
202
column_gap: px(5),
203
..default()
204
},
205
children![
206
Text::new("[Q] Toggle filtering:"),
207
(
208
Text::new(format!("{:?}", *status)),
209
TextColor(enabled_color(enabled))
210
)
211
]
212
),
213
(
214
Node {
215
flex_direction: FlexDirection::Row,
216
column_gap: px(5),
217
..default()
218
},
219
children![
220
(
221
Text::new("[1] Frame times:"),
222
TextColor(Color::WHITE.with_alpha(alpha))
223
),
224
(
225
Text::new(format!("{:?}", filters.frame_time)),
226
TextColor(enabled_color(filters.frame_time).with_alpha(alpha))
227
)
228
]
229
),
230
(
231
Node {
232
flex_direction: FlexDirection::Row,
233
column_gap: px(5),
234
..default()
235
},
236
children![
237
(
238
Text::new("[2] Entity count:"),
239
TextColor(Color::WHITE.with_alpha(alpha))
240
),
241
(
242
Text::new(format!("{:?}", filters.entity_count)),
243
TextColor(enabled_color(filters.entity_count).with_alpha(alpha))
244
)
245
]
246
),
247
(
248
Node {
249
flex_direction: FlexDirection::Row,
250
column_gap: px(5),
251
..default()
252
},
253
children![
254
(
255
Text::new("[3] System info:"),
256
TextColor(Color::WHITE.with_alpha(alpha))
257
),
258
(
259
Text::new(format!("{:?}", filters.system_info)),
260
TextColor(enabled_color(filters.system_info).with_alpha(alpha))
261
)
262
]
263
),
264
(
265
Node {
266
flex_direction: FlexDirection::Row,
267
column_gap: px(5),
268
..default()
269
},
270
children![
271
(
272
Text::new("[4] Render diagnostics:"),
273
TextColor(Color::WHITE.with_alpha(alpha))
274
),
275
(
276
Text::new("Private"),
277
TextColor(enabled_color(false).with_alpha(alpha))
278
)
279
]
280
),
281
]);
282
}
283
284
#[derive(Debug, Default, PartialEq, Eq, Resource)]
285
enum LogDiagnosticsStatus {
286
/// No filtering, showing all logs
287
#[default]
288
Disabled,
289
/// Filtering enabled, showing only subset of logs
290
Enabled,
291
}
292
293
#[derive(Default, Resource)]
294
struct LogDiagnosticsFilters {
295
frame_time: bool,
296
entity_count: bool,
297
system_info: bool,
298
#[expect(
299
dead_code,
300
reason = "Currently the diagnostic paths referent to RenderDiagnosticPlugin are private"
301
)]
302
render_diagnostics: bool,
303
}
304
305
#[derive(Component)]
306
/// Marks the UI node that has instructions on how to change the filtering
307
struct LogDiagnosticsCommands;
308
309