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