Expose LogDiagnosticsState
(#19323)
# Objective Closes #19175 Make `LogDiagnosticsState` public to be able to edit its filters ## Solution Make `LogDiagnosticsState` public and add methods to allow editing the duration and filter ## Testing `cargo run -p ci` ## Showcase Updated `log_diagnostics` example 
This commit is contained in:
parent
f95287ca9b
commit
d3012df755
@ -29,7 +29,7 @@ pub use diagnostic::*;
|
||||
pub use entity_count_diagnostics_plugin::EntityCountDiagnosticsPlugin;
|
||||
pub use frame_count_diagnostics_plugin::{update_frame_count, FrameCount, FrameCountPlugin};
|
||||
pub use frame_time_diagnostics_plugin::FrameTimeDiagnosticsPlugin;
|
||||
pub use log_diagnostics_plugin::LogDiagnosticsPlugin;
|
||||
pub use log_diagnostics_plugin::{LogDiagnosticsPlugin, LogDiagnosticsState};
|
||||
#[cfg(feature = "sysinfo_plugin")]
|
||||
pub use system_information_diagnostics_plugin::{SystemInfo, SystemInformationDiagnosticsPlugin};
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
use super::{Diagnostic, DiagnosticPath, DiagnosticsStore};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_platform::collections::HashSet;
|
||||
use bevy_time::{Real, Time, Timer, TimerMode};
|
||||
use core::time::Duration;
|
||||
use log::{debug, info};
|
||||
@ -16,14 +17,68 @@ use log::{debug, info};
|
||||
pub struct LogDiagnosticsPlugin {
|
||||
pub debug: bool,
|
||||
pub wait_duration: Duration,
|
||||
pub filter: Option<Vec<DiagnosticPath>>,
|
||||
pub filter: Option<HashSet<DiagnosticPath>>,
|
||||
}
|
||||
|
||||
/// State used by the [`LogDiagnosticsPlugin`]
|
||||
#[derive(Resource)]
|
||||
struct LogDiagnosticsState {
|
||||
pub struct LogDiagnosticsState {
|
||||
timer: Timer,
|
||||
filter: Option<Vec<DiagnosticPath>>,
|
||||
filter: Option<HashSet<DiagnosticPath>>,
|
||||
}
|
||||
|
||||
impl LogDiagnosticsState {
|
||||
/// Sets a new duration for the log timer
|
||||
pub fn set_timer_duration(&mut self, duration: Duration) {
|
||||
self.timer.set_duration(duration);
|
||||
self.timer.set_elapsed(Duration::ZERO);
|
||||
}
|
||||
|
||||
/// Add a filter to the log state, returning `true` if the [`DiagnosticPath`]
|
||||
/// was not present
|
||||
pub fn add_filter(&mut self, diagnostic_path: DiagnosticPath) -> bool {
|
||||
if let Some(filter) = &mut self.filter {
|
||||
filter.insert(diagnostic_path)
|
||||
} else {
|
||||
self.filter = Some(HashSet::from_iter([diagnostic_path]));
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends the filter of the log state with multiple [`DiagnosticPaths`](DiagnosticPath)
|
||||
pub fn extend_filter(&mut self, iter: impl IntoIterator<Item = DiagnosticPath>) {
|
||||
if let Some(filter) = &mut self.filter {
|
||||
filter.extend(iter);
|
||||
} else {
|
||||
self.filter = Some(HashSet::from_iter(iter));
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a filter from the log state, returning `true` if it was present
|
||||
pub fn remove_filter(&mut self, diagnostic_path: &DiagnosticPath) -> bool {
|
||||
if let Some(filter) = &mut self.filter {
|
||||
filter.remove(diagnostic_path)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears the filters of the log state
|
||||
pub fn clear_filter(&mut self) {
|
||||
if let Some(filter) = &mut self.filter {
|
||||
filter.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables filtering with empty filters
|
||||
pub fn enable_filtering(&mut self) {
|
||||
self.filter = Some(HashSet::new());
|
||||
}
|
||||
|
||||
/// Disables filtering
|
||||
pub fn disable_filtering(&mut self) {
|
||||
self.filter = None;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LogDiagnosticsPlugin {
|
||||
@ -52,7 +107,7 @@ impl Plugin for LogDiagnosticsPlugin {
|
||||
}
|
||||
|
||||
impl LogDiagnosticsPlugin {
|
||||
pub fn filtered(filter: Vec<DiagnosticPath>) -> Self {
|
||||
pub fn filtered(filter: HashSet<DiagnosticPath>) -> Self {
|
||||
LogDiagnosticsPlugin {
|
||||
filter: Some(filter),
|
||||
..Default::default()
|
||||
@ -65,7 +120,7 @@ impl LogDiagnosticsPlugin {
|
||||
mut callback: impl FnMut(&Diagnostic),
|
||||
) {
|
||||
if let Some(filter) = &state.filter {
|
||||
for path in filter {
|
||||
for path in filter.iter() {
|
||||
if let Some(diagnostic) = diagnostics.get(path) {
|
||||
if diagnostic.is_enabled {
|
||||
callback(diagnostic);
|
||||
|
@ -1,10 +1,27 @@
|
||||
//! Shows different built-in plugins that logs diagnostics, like frames per second (FPS), to the console.
|
||||
|
||||
use bevy::{
|
||||
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
||||
color::palettes,
|
||||
diagnostic::{
|
||||
DiagnosticPath, EntityCountDiagnosticsPlugin, FrameTimeDiagnosticsPlugin,
|
||||
LogDiagnosticsPlugin, LogDiagnosticsState, SystemInformationDiagnosticsPlugin,
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
const FRAME_TIME_DIAGNOSTICS: [DiagnosticPath; 3] = [
|
||||
FrameTimeDiagnosticsPlugin::FPS,
|
||||
FrameTimeDiagnosticsPlugin::FRAME_COUNT,
|
||||
FrameTimeDiagnosticsPlugin::FRAME_TIME,
|
||||
];
|
||||
const ENTITY_COUNT_DIAGNOSTICS: [DiagnosticPath; 1] = [EntityCountDiagnosticsPlugin::ENTITY_COUNT];
|
||||
const SYSTEM_INFO_DIAGNOSTICS: [DiagnosticPath; 4] = [
|
||||
SystemInformationDiagnosticsPlugin::PROCESS_CPU_USAGE,
|
||||
SystemInformationDiagnosticsPlugin::PROCESS_MEM_USAGE,
|
||||
SystemInformationDiagnosticsPlugin::SYSTEM_CPU_USAGE,
|
||||
SystemInformationDiagnosticsPlugin::SYSTEM_MEM_USAGE,
|
||||
];
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins((
|
||||
@ -16,9 +33,9 @@ fn main() {
|
||||
// Adds frame time, FPS and frame count diagnostics.
|
||||
FrameTimeDiagnosticsPlugin::default(),
|
||||
// Adds an entity count diagnostic.
|
||||
bevy::diagnostic::EntityCountDiagnosticsPlugin,
|
||||
EntityCountDiagnosticsPlugin,
|
||||
// Adds cpu and memory usage diagnostics for systems and the entire game process.
|
||||
bevy::diagnostic::SystemInformationDiagnosticsPlugin,
|
||||
SystemInformationDiagnosticsPlugin,
|
||||
// Forwards various diagnostics from the render app to the main app.
|
||||
// These are pretty verbose but can be useful to pinpoint performance issues.
|
||||
bevy::render::diagnostic::RenderDiagnosticsPlugin,
|
||||
@ -26,6 +43,14 @@ fn main() {
|
||||
// No rendering diagnostics are emitted unless something is drawn to the screen,
|
||||
// so we spawn a small scene.
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, filters_inputs)
|
||||
.add_systems(
|
||||
Update,
|
||||
update_commands.run_if(
|
||||
resource_exists_and_changed::<LogDiagnosticsStatus>
|
||||
.or(resource_exists_and_changed::<LogDiagnosticsFilters>),
|
||||
),
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
@ -60,4 +85,223 @@ fn setup(
|
||||
Camera3d::default(),
|
||||
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||
));
|
||||
|
||||
commands.init_resource::<LogDiagnosticsFilters>();
|
||||
commands.init_resource::<LogDiagnosticsStatus>();
|
||||
|
||||
commands.spawn((
|
||||
LogDiagnosticsCommands,
|
||||
Node {
|
||||
top: Val::Px(5.),
|
||||
left: Val::Px(5.),
|
||||
flex_direction: FlexDirection::Column,
|
||||
..default()
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
fn filters_inputs(
|
||||
keys: Res<ButtonInput<KeyCode>>,
|
||||
mut status: ResMut<LogDiagnosticsStatus>,
|
||||
mut filters: ResMut<LogDiagnosticsFilters>,
|
||||
mut log_state: ResMut<LogDiagnosticsState>,
|
||||
) {
|
||||
if keys.just_pressed(KeyCode::KeyQ) {
|
||||
*status = match *status {
|
||||
LogDiagnosticsStatus::Enabled => {
|
||||
log_state.disable_filtering();
|
||||
LogDiagnosticsStatus::Disabled
|
||||
}
|
||||
LogDiagnosticsStatus::Disabled => {
|
||||
log_state.enable_filtering();
|
||||
if filters.frame_time {
|
||||
enable_filters(&mut log_state, FRAME_TIME_DIAGNOSTICS);
|
||||
}
|
||||
if filters.entity_count {
|
||||
enable_filters(&mut log_state, ENTITY_COUNT_DIAGNOSTICS);
|
||||
}
|
||||
if filters.system_info {
|
||||
enable_filters(&mut log_state, SYSTEM_INFO_DIAGNOSTICS);
|
||||
}
|
||||
LogDiagnosticsStatus::Enabled
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let enabled = *status == LogDiagnosticsStatus::Enabled;
|
||||
if keys.just_pressed(KeyCode::Digit1) {
|
||||
filters.frame_time = !filters.frame_time;
|
||||
if enabled {
|
||||
if filters.frame_time {
|
||||
enable_filters(&mut log_state, FRAME_TIME_DIAGNOSTICS);
|
||||
} else {
|
||||
disable_filters(&mut log_state, FRAME_TIME_DIAGNOSTICS);
|
||||
}
|
||||
}
|
||||
}
|
||||
if keys.just_pressed(KeyCode::Digit2) {
|
||||
filters.entity_count = !filters.entity_count;
|
||||
if enabled {
|
||||
if filters.entity_count {
|
||||
enable_filters(&mut log_state, ENTITY_COUNT_DIAGNOSTICS);
|
||||
} else {
|
||||
disable_filters(&mut log_state, ENTITY_COUNT_DIAGNOSTICS);
|
||||
}
|
||||
}
|
||||
}
|
||||
if keys.just_pressed(KeyCode::Digit3) {
|
||||
filters.system_info = !filters.system_info;
|
||||
if enabled {
|
||||
if filters.system_info {
|
||||
enable_filters(&mut log_state, SYSTEM_INFO_DIAGNOSTICS);
|
||||
} else {
|
||||
disable_filters(&mut log_state, SYSTEM_INFO_DIAGNOSTICS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_filters(
|
||||
log_state: &mut LogDiagnosticsState,
|
||||
diagnostics: impl IntoIterator<Item = DiagnosticPath>,
|
||||
) {
|
||||
log_state.extend_filter(diagnostics);
|
||||
}
|
||||
|
||||
fn disable_filters(
|
||||
log_state: &mut LogDiagnosticsState,
|
||||
diagnostics: impl IntoIterator<Item = DiagnosticPath>,
|
||||
) {
|
||||
for diagnostic in diagnostics {
|
||||
log_state.remove_filter(&diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_commands(
|
||||
mut commands: Commands,
|
||||
log_commands: Single<Entity, With<LogDiagnosticsCommands>>,
|
||||
status: Res<LogDiagnosticsStatus>,
|
||||
filters: Res<LogDiagnosticsFilters>,
|
||||
) {
|
||||
let enabled = *status == LogDiagnosticsStatus::Enabled;
|
||||
let alpha = if enabled { 1. } else { 0.25 };
|
||||
let enabled_color = |enabled| {
|
||||
if enabled {
|
||||
Color::from(palettes::tailwind::GREEN_400)
|
||||
} else {
|
||||
Color::from(palettes::tailwind::RED_400)
|
||||
}
|
||||
};
|
||||
commands
|
||||
.entity(*log_commands)
|
||||
.despawn_related::<Children>()
|
||||
.insert(children![
|
||||
(
|
||||
Node {
|
||||
flex_direction: FlexDirection::Row,
|
||||
column_gap: Val::Px(5.),
|
||||
..default()
|
||||
},
|
||||
children![
|
||||
Text::new("[Q] Toggle filtering:"),
|
||||
(
|
||||
Text::new(format!("{:?}", *status)),
|
||||
TextColor(enabled_color(enabled))
|
||||
)
|
||||
]
|
||||
),
|
||||
(
|
||||
Node {
|
||||
flex_direction: FlexDirection::Row,
|
||||
column_gap: Val::Px(5.),
|
||||
..default()
|
||||
},
|
||||
children![
|
||||
(
|
||||
Text::new("[1] Frame times:"),
|
||||
TextColor(Color::WHITE.with_alpha(alpha))
|
||||
),
|
||||
(
|
||||
Text::new(format!("{:?}", filters.frame_time)),
|
||||
TextColor(enabled_color(filters.frame_time).with_alpha(alpha))
|
||||
)
|
||||
]
|
||||
),
|
||||
(
|
||||
Node {
|
||||
flex_direction: FlexDirection::Row,
|
||||
column_gap: Val::Px(5.),
|
||||
..default()
|
||||
},
|
||||
children![
|
||||
(
|
||||
Text::new("[2] Entity count:"),
|
||||
TextColor(Color::WHITE.with_alpha(alpha))
|
||||
),
|
||||
(
|
||||
Text::new(format!("{:?}", filters.entity_count)),
|
||||
TextColor(enabled_color(filters.entity_count).with_alpha(alpha))
|
||||
)
|
||||
]
|
||||
),
|
||||
(
|
||||
Node {
|
||||
flex_direction: FlexDirection::Row,
|
||||
column_gap: Val::Px(5.),
|
||||
..default()
|
||||
},
|
||||
children![
|
||||
(
|
||||
Text::new("[3] System info:"),
|
||||
TextColor(Color::WHITE.with_alpha(alpha))
|
||||
),
|
||||
(
|
||||
Text::new(format!("{:?}", filters.system_info)),
|
||||
TextColor(enabled_color(filters.system_info).with_alpha(alpha))
|
||||
)
|
||||
]
|
||||
),
|
||||
(
|
||||
Node {
|
||||
flex_direction: FlexDirection::Row,
|
||||
column_gap: Val::Px(5.),
|
||||
..default()
|
||||
},
|
||||
children![
|
||||
(
|
||||
Text::new("[4] Render diagnostics:"),
|
||||
TextColor(Color::WHITE.with_alpha(alpha))
|
||||
),
|
||||
(
|
||||
Text::new("Private"),
|
||||
TextColor(enabled_color(false).with_alpha(alpha))
|
||||
)
|
||||
]
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, Resource)]
|
||||
enum LogDiagnosticsStatus {
|
||||
/// No filtering, showing all logs
|
||||
#[default]
|
||||
Disabled,
|
||||
/// Filtering enabled, showing only subset of logs
|
||||
Enabled,
|
||||
}
|
||||
|
||||
#[derive(Default, Resource)]
|
||||
struct LogDiagnosticsFilters {
|
||||
frame_time: bool,
|
||||
entity_count: bool,
|
||||
system_info: bool,
|
||||
#[expect(
|
||||
dead_code,
|
||||
reason = "Currently the diagnostic paths referent to RenderDiagnosticPlugin are private"
|
||||
)]
|
||||
render_diagnostics: bool,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
/// Marks the UI node that has instructions on how to change the filtering
|
||||
struct LogDiagnosticsCommands;
|
||||
|
@ -22,6 +22,7 @@ use bevy::{
|
||||
world::FilteredEntityMut,
|
||||
},
|
||||
log::LogPlugin,
|
||||
platform::collections::HashSet,
|
||||
prelude::{App, In, IntoSystem, Query, Schedule, SystemParamBuilder, Update},
|
||||
ptr::{OwningPtr, PtrMut},
|
||||
MinimalPlugins,
|
||||
@ -168,9 +169,9 @@ fn stress_test(num_entities: u32, num_components: u32, num_systems: u32) {
|
||||
.add_plugins(DiagnosticsPlugin)
|
||||
.add_plugins(LogPlugin::default())
|
||||
.add_plugins(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugins(LogDiagnosticsPlugin::filtered(vec![DiagnosticPath::new(
|
||||
"fps",
|
||||
)]));
|
||||
.add_plugins(LogDiagnosticsPlugin::filtered(HashSet::from_iter([
|
||||
DiagnosticPath::new("fps"),
|
||||
])));
|
||||
app.run();
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
title: Change filters container of `LogDiagnosticsState` to `HashSet`
|
||||
authors: ["@hukasu"]
|
||||
pull_requests: [19323]
|
||||
---
|
||||
|
||||
Make `LogDiagnosticsState`'s filter container and argument of
|
||||
`LogDiagnosticPlugin::filtered` into `HashSet`.
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
title: LogDiagnosticsState is now public
|
||||
authors: ["@hukasu"]
|
||||
pull_requests: [19323]
|
||||
---
|
||||
|
||||
Make `LogDiagnosticsState` public to allow editing its duration and filters during
|
||||
runtime.
|
Loading…
Reference in New Issue
Block a user