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 entity_count_diagnostics_plugin::EntityCountDiagnosticsPlugin;
|
||||||
pub use frame_count_diagnostics_plugin::{update_frame_count, FrameCount, FrameCountPlugin};
|
pub use frame_count_diagnostics_plugin::{update_frame_count, FrameCount, FrameCountPlugin};
|
||||||
pub use frame_time_diagnostics_plugin::FrameTimeDiagnosticsPlugin;
|
pub use frame_time_diagnostics_plugin::FrameTimeDiagnosticsPlugin;
|
||||||
pub use log_diagnostics_plugin::LogDiagnosticsPlugin;
|
pub use log_diagnostics_plugin::{LogDiagnosticsPlugin, LogDiagnosticsState};
|
||||||
#[cfg(feature = "sysinfo_plugin")]
|
#[cfg(feature = "sysinfo_plugin")]
|
||||||
pub use system_information_diagnostics_plugin::{SystemInfo, SystemInformationDiagnosticsPlugin};
|
pub use system_information_diagnostics_plugin::{SystemInfo, SystemInformationDiagnosticsPlugin};
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use super::{Diagnostic, DiagnosticPath, DiagnosticsStore};
|
use super::{Diagnostic, DiagnosticPath, DiagnosticsStore};
|
||||||
use alloc::vec::Vec;
|
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
use bevy_platform::collections::HashSet;
|
||||||
use bevy_time::{Real, Time, Timer, TimerMode};
|
use bevy_time::{Real, Time, Timer, TimerMode};
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
@ -16,14 +17,68 @@ use log::{debug, info};
|
|||||||
pub struct LogDiagnosticsPlugin {
|
pub struct LogDiagnosticsPlugin {
|
||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
pub wait_duration: Duration,
|
pub wait_duration: Duration,
|
||||||
pub filter: Option<Vec<DiagnosticPath>>,
|
pub filter: Option<HashSet<DiagnosticPath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State used by the [`LogDiagnosticsPlugin`]
|
/// State used by the [`LogDiagnosticsPlugin`]
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct LogDiagnosticsState {
|
pub struct LogDiagnosticsState {
|
||||||
timer: Timer,
|
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 {
|
impl Default for LogDiagnosticsPlugin {
|
||||||
@ -52,7 +107,7 @@ impl Plugin for LogDiagnosticsPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LogDiagnosticsPlugin {
|
impl LogDiagnosticsPlugin {
|
||||||
pub fn filtered(filter: Vec<DiagnosticPath>) -> Self {
|
pub fn filtered(filter: HashSet<DiagnosticPath>) -> Self {
|
||||||
LogDiagnosticsPlugin {
|
LogDiagnosticsPlugin {
|
||||||
filter: Some(filter),
|
filter: Some(filter),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -65,7 +120,7 @@ impl LogDiagnosticsPlugin {
|
|||||||
mut callback: impl FnMut(&Diagnostic),
|
mut callback: impl FnMut(&Diagnostic),
|
||||||
) {
|
) {
|
||||||
if let Some(filter) = &state.filter {
|
if let Some(filter) = &state.filter {
|
||||||
for path in filter {
|
for path in filter.iter() {
|
||||||
if let Some(diagnostic) = diagnostics.get(path) {
|
if let Some(diagnostic) = diagnostics.get(path) {
|
||||||
if diagnostic.is_enabled {
|
if diagnostic.is_enabled {
|
||||||
callback(diagnostic);
|
callback(diagnostic);
|
||||||
|
@ -1,10 +1,27 @@
|
|||||||
//! Shows different built-in plugins that logs diagnostics, like frames per second (FPS), to the console.
|
//! Shows different built-in plugins that logs diagnostics, like frames per second (FPS), to the console.
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
color::palettes,
|
||||||
|
diagnostic::{
|
||||||
|
DiagnosticPath, EntityCountDiagnosticsPlugin, FrameTimeDiagnosticsPlugin,
|
||||||
|
LogDiagnosticsPlugin, LogDiagnosticsState, SystemInformationDiagnosticsPlugin,
|
||||||
|
},
|
||||||
prelude::*,
|
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() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((
|
.add_plugins((
|
||||||
@ -16,9 +33,9 @@ fn main() {
|
|||||||
// Adds frame time, FPS and frame count diagnostics.
|
// Adds frame time, FPS and frame count diagnostics.
|
||||||
FrameTimeDiagnosticsPlugin::default(),
|
FrameTimeDiagnosticsPlugin::default(),
|
||||||
// Adds an entity count diagnostic.
|
// Adds an entity count diagnostic.
|
||||||
bevy::diagnostic::EntityCountDiagnosticsPlugin,
|
EntityCountDiagnosticsPlugin,
|
||||||
// Adds cpu and memory usage diagnostics for systems and the entire game process.
|
// 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.
|
// Forwards various diagnostics from the render app to the main app.
|
||||||
// These are pretty verbose but can be useful to pinpoint performance issues.
|
// These are pretty verbose but can be useful to pinpoint performance issues.
|
||||||
bevy::render::diagnostic::RenderDiagnosticsPlugin,
|
bevy::render::diagnostic::RenderDiagnosticsPlugin,
|
||||||
@ -26,6 +43,14 @@ fn main() {
|
|||||||
// No rendering diagnostics are emitted unless something is drawn to the screen,
|
// No rendering diagnostics are emitted unless something is drawn to the screen,
|
||||||
// so we spawn a small scene.
|
// so we spawn a small scene.
|
||||||
.add_systems(Startup, setup)
|
.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();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,4 +85,223 @@ fn setup(
|
|||||||
Camera3d::default(),
|
Camera3d::default(),
|
||||||
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
|
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,
|
world::FilteredEntityMut,
|
||||||
},
|
},
|
||||||
log::LogPlugin,
|
log::LogPlugin,
|
||||||
|
platform::collections::HashSet,
|
||||||
prelude::{App, In, IntoSystem, Query, Schedule, SystemParamBuilder, Update},
|
prelude::{App, In, IntoSystem, Query, Schedule, SystemParamBuilder, Update},
|
||||||
ptr::{OwningPtr, PtrMut},
|
ptr::{OwningPtr, PtrMut},
|
||||||
MinimalPlugins,
|
MinimalPlugins,
|
||||||
@ -168,9 +169,9 @@ fn stress_test(num_entities: u32, num_components: u32, num_systems: u32) {
|
|||||||
.add_plugins(DiagnosticsPlugin)
|
.add_plugins(DiagnosticsPlugin)
|
||||||
.add_plugins(LogPlugin::default())
|
.add_plugins(LogPlugin::default())
|
||||||
.add_plugins(FrameTimeDiagnosticsPlugin::default())
|
.add_plugins(FrameTimeDiagnosticsPlugin::default())
|
||||||
.add_plugins(LogDiagnosticsPlugin::filtered(vec![DiagnosticPath::new(
|
.add_plugins(LogDiagnosticsPlugin::filtered(HashSet::from_iter([
|
||||||
"fps",
|
DiagnosticPath::new("fps"),
|
||||||
)]));
|
])));
|
||||||
app.run();
|
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