updates on diagnostics (log + new diagnostics) (#1085)

* move print diagnostics to log

* entity count diagnostic

* asset count diagnostic

* remove useless `pub`s

* use `BTreeMap` instead of `HashMap`

* get entity count from world

* keep ordered list of diagnostics
This commit is contained in:
François 2020-12-24 20:28:31 +01:00 committed by GitHub
parent d3d6c35789
commit b28365f966
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 144 additions and 45 deletions

View File

@ -191,8 +191,8 @@ name = "custom_diagnostic"
path = "examples/diagnostics/custom_diagnostic.rs" path = "examples/diagnostics/custom_diagnostic.rs"
[[example]] [[example]]
name = "print_diagnostics" name = "log_diagnostics"
path = "examples/diagnostics/print_diagnostics.rs" path = "examples/diagnostics/log_diagnostics.rs"
[[example]] [[example]]
name = "event" name = "event"

View File

@ -19,6 +19,7 @@ filesystem_watcher = ["notify"]
[dependencies] [dependencies]
# bevy # bevy
bevy_app = { path = "../bevy_app", version = "0.4.0" } bevy_app = { path = "../bevy_app", version = "0.4.0" }
bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.4.0" }
bevy_ecs = { path = "../bevy_ecs", version = "0.4.0" } bevy_ecs = { path = "../bevy_ecs", version = "0.4.0" }
bevy_reflect = { path = "../bevy_reflect", version = "0.4.0", features = ["bevy"] } bevy_reflect = { path = "../bevy_reflect", version = "0.4.0", features = ["bevy"] }
bevy_tasks = { path = "../bevy_tasks", version = "0.4.0" } bevy_tasks = { path = "../bevy_tasks", version = "0.4.0" }

View File

@ -0,0 +1,35 @@
use crate::{Asset, Assets};
use bevy_app::prelude::*;
use bevy_diagnostic::{Diagnostic, DiagnosticId, Diagnostics};
use bevy_ecs::{IntoSystem, Res, ResMut};
/// Adds "asset count" diagnostic to an App
#[derive(Default)]
pub struct AssetCountDiagnosticsPlugin<T: Asset> {
marker: std::marker::PhantomData<T>,
}
impl<T: Asset> Plugin for AssetCountDiagnosticsPlugin<T> {
fn build(&self, app: &mut AppBuilder) {
app.add_startup_system(Self::setup_system.system())
.add_system(Self::diagnostic_system.system());
}
}
impl<T: Asset> AssetCountDiagnosticsPlugin<T> {
pub fn diagnostic_id() -> DiagnosticId {
DiagnosticId(T::TYPE_UUID)
}
pub fn setup_system(mut diagnostics: ResMut<Diagnostics>) {
diagnostics.add(Diagnostic::new(
Self::diagnostic_id(),
&format!("asset_count {}", std::any::type_name::<T>()),
20,
));
}
pub fn diagnostic_system(mut diagnostics: ResMut<Diagnostics>, assets: Res<Assets<T>>) {
diagnostics.add_measurement(Self::diagnostic_id(), assets.len() as f64);
}
}

View File

@ -0,0 +1,2 @@
mod asset_count_diagnostics_plugin;
pub use asset_count_diagnostics_plugin::AssetCountDiagnosticsPlugin;

View File

@ -1,5 +1,6 @@
mod asset_server; mod asset_server;
mod assets; mod assets;
pub mod diagnostic;
#[cfg(all( #[cfg(all(
feature = "filesystem_watcher", feature = "filesystem_watcher",
all(not(target_arch = "wasm32"), not(target_os = "android")) all(not(target_arch = "wasm32"), not(target_os = "android"))

View File

@ -18,6 +18,7 @@ keywords = ["bevy"]
bevy_app = { path = "../bevy_app", version = "0.4.0" } bevy_app = { path = "../bevy_app", version = "0.4.0" }
bevy_core = { path = "../bevy_core", version = "0.4.0" } bevy_core = { path = "../bevy_core", version = "0.4.0" }
bevy_ecs = { path = "../bevy_ecs", version = "0.4.0" } bevy_ecs = { path = "../bevy_ecs", version = "0.4.0" }
bevy_log = { path = "../bevy_log", version = "0.4.0" }
bevy_utils = { path = "../bevy_utils", version = "0.4.0" } bevy_utils = { path = "../bevy_utils", version = "0.4.0" }
# other # other

View File

@ -1,8 +1,8 @@
use bevy_utils::{Duration, HashMap, Instant, Uuid}; use bevy_utils::{Duration, HashMap, Instant, Uuid};
use std::collections::VecDeque; use std::collections::{BTreeSet, VecDeque};
/// Unique identifier for a [Diagnostic] /// Unique identifier for a [Diagnostic]
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct DiagnosticId(pub Uuid); pub struct DiagnosticId(pub Uuid);
impl DiagnosticId { impl DiagnosticId {
@ -102,10 +102,12 @@ impl Diagnostic {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Diagnostics { pub struct Diagnostics {
diagnostics: HashMap<DiagnosticId, Diagnostic>, diagnostics: HashMap<DiagnosticId, Diagnostic>,
ordered_diagnostics: BTreeSet<DiagnosticId>,
} }
impl Diagnostics { impl Diagnostics {
pub fn add(&mut self, diagnostic: Diagnostic) { pub fn add(&mut self, diagnostic: Diagnostic) {
self.ordered_diagnostics.insert(diagnostic.id);
self.diagnostics.insert(diagnostic.id, diagnostic); self.diagnostics.insert(diagnostic.id, diagnostic);
} }
@ -132,4 +134,10 @@ impl Diagnostics {
pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> { pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> {
self.diagnostics.values() self.diagnostics.values()
} }
pub fn ordered_iter(&self) -> impl Iterator<Item = &Diagnostic> {
self.ordered_diagnostics
.iter()
.filter_map(move |k| self.diagnostics.get(k))
}
} }

View File

@ -0,0 +1,29 @@
use crate::{Diagnostic, DiagnosticId, Diagnostics};
use bevy_app::prelude::*;
use bevy_ecs::{IntoSystem, ResMut, Resources, World};
/// Adds "entity count" diagnostic to an App
#[derive(Default)]
pub struct EntityCountDiagnosticsPlugin;
impl Plugin for EntityCountDiagnosticsPlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_startup_system(Self::setup_system.system())
.add_system(Self::diagnostic_system.system());
}
}
impl EntityCountDiagnosticsPlugin {
pub const ENTITY_COUNT: DiagnosticId =
DiagnosticId::from_u128(187513512115068938494459732780662867798);
pub fn setup_system(mut diagnostics: ResMut<Diagnostics>) {
diagnostics.add(Diagnostic::new(Self::ENTITY_COUNT, "entity_count", 20));
}
pub fn diagnostic_system(world: &mut World, resources: &mut Resources) {
if let Some(mut diagnostics) = resources.get_mut::<Diagnostics>() {
diagnostics.add_measurement(Self::ENTITY_COUNT, world.entity_count() as f64);
}
}
}

View File

@ -1,9 +1,11 @@
mod diagnostic; mod diagnostic;
mod entity_count_diagnostics_plugin;
mod frame_time_diagnostics_plugin; mod frame_time_diagnostics_plugin;
mod print_diagnostics_plugin; mod log_diagnostics_plugin;
pub use diagnostic::*; pub use diagnostic::*;
pub use entity_count_diagnostics_plugin::EntityCountDiagnosticsPlugin;
pub use frame_time_diagnostics_plugin::FrameTimeDiagnosticsPlugin; pub use frame_time_diagnostics_plugin::FrameTimeDiagnosticsPlugin;
pub use print_diagnostics_plugin::PrintDiagnosticsPlugin; pub use log_diagnostics_plugin::LogDiagnosticsPlugin;
use bevy_app::prelude::*; use bevy_app::prelude::*;

View File

@ -2,24 +2,25 @@ use super::{Diagnostic, DiagnosticId, Diagnostics};
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_core::{Time, Timer}; use bevy_core::{Time, Timer};
use bevy_ecs::{IntoSystem, Res, ResMut}; use bevy_ecs::{IntoSystem, Res, ResMut};
use bevy_log::{debug, info};
use bevy_utils::Duration; use bevy_utils::Duration;
/// An App Plugin that prints diagnostics to the console /// An App Plugin that logs diagnostics to the console
pub struct PrintDiagnosticsPlugin { pub struct LogDiagnosticsPlugin {
pub debug: bool, pub debug: bool,
pub wait_duration: Duration, pub wait_duration: Duration,
pub filter: Option<Vec<DiagnosticId>>, pub filter: Option<Vec<DiagnosticId>>,
} }
/// State used by the [PrintDiagnosticsPlugin] /// State used by the [LogDiagnosticsPlugin]
pub struct PrintDiagnosticsState { struct LogDiagnosticsState {
timer: Timer, timer: Timer,
filter: Option<Vec<DiagnosticId>>, filter: Option<Vec<DiagnosticId>>,
} }
impl Default for PrintDiagnosticsPlugin { impl Default for LogDiagnosticsPlugin {
fn default() -> Self { fn default() -> Self {
PrintDiagnosticsPlugin { LogDiagnosticsPlugin {
debug: false, debug: false,
wait_duration: Duration::from_secs(1), wait_duration: Duration::from_secs(1),
filter: None, filter: None,
@ -27,9 +28,9 @@ impl Default for PrintDiagnosticsPlugin {
} }
} }
impl Plugin for PrintDiagnosticsPlugin { impl Plugin for LogDiagnosticsPlugin {
fn build(&self, app: &mut bevy_app::AppBuilder) { fn build(&self, app: &mut bevy_app::AppBuilder) {
app.add_resource(PrintDiagnosticsState { app.add_resource(LogDiagnosticsState {
timer: Timer::new(self.wait_duration, true), timer: Timer::new(self.wait_duration, true),
filter: self.filter.clone(), filter: self.filter.clone(),
}); });
@ -37,68 +38,66 @@ impl Plugin for PrintDiagnosticsPlugin {
if self.debug { if self.debug {
app.add_system_to_stage( app.add_system_to_stage(
stage::POST_UPDATE, stage::POST_UPDATE,
Self::print_diagnostics_debug_system.system(), Self::log_diagnostics_debug_system.system(),
); );
} else { } else {
app.add_system_to_stage(stage::POST_UPDATE, Self::print_diagnostics_system.system()); app.add_system_to_stage(stage::POST_UPDATE, Self::log_diagnostics_system.system());
} }
} }
} }
impl PrintDiagnosticsPlugin { impl LogDiagnosticsPlugin {
pub fn filtered(filter: Vec<DiagnosticId>) -> Self { pub fn filtered(filter: Vec<DiagnosticId>) -> Self {
PrintDiagnosticsPlugin { LogDiagnosticsPlugin {
filter: Some(filter), filter: Some(filter),
..Default::default() ..Default::default()
} }
} }
fn print_diagnostic(diagnostic: &Diagnostic) { fn log_diagnostic(diagnostic: &Diagnostic) {
if let Some(value) = diagnostic.value() { if let Some(value) = diagnostic.value() {
print!("{:<65}: {:<10.6}", diagnostic.name, value);
if let Some(average) = diagnostic.average() { if let Some(average) = diagnostic.average() {
print!(" (avg {:.6})", average); info!(
"{:<65}: {:<10.6} (avg {:.6})",
diagnostic.name, value, average
);
} else {
info!("{:<65}: {:<10.6}", diagnostic.name, value);
} }
println!("\n");
} }
} }
pub fn print_diagnostics_system( fn log_diagnostics_system(
mut state: ResMut<PrintDiagnosticsState>, mut state: ResMut<LogDiagnosticsState>,
time: Res<Time>, time: Res<Time>,
diagnostics: Res<Diagnostics>, diagnostics: Res<Diagnostics>,
) { ) {
if state.timer.tick(time.delta_seconds()).finished() { if state.timer.tick(time.delta_seconds()).finished() {
println!("Diagnostics:");
println!("{}", "-".repeat(93));
if let Some(ref filter) = state.filter { if let Some(ref filter) = state.filter {
for diagnostic in filter.iter().map(|id| diagnostics.get(*id).unwrap()) { for diagnostic in filter.iter().map(|id| diagnostics.get(*id).unwrap()) {
Self::print_diagnostic(diagnostic); Self::log_diagnostic(diagnostic);
} }
} else { } else {
for diagnostic in diagnostics.iter() { for diagnostic in diagnostics.ordered_iter() {
Self::print_diagnostic(diagnostic); Self::log_diagnostic(diagnostic);
} }
} }
} }
} }
pub fn print_diagnostics_debug_system( fn log_diagnostics_debug_system(
mut state: ResMut<PrintDiagnosticsState>, mut state: ResMut<LogDiagnosticsState>,
time: Res<Time>, time: Res<Time>,
diagnostics: Res<Diagnostics>, diagnostics: Res<Diagnostics>,
) { ) {
if state.timer.tick(time.delta_seconds()).finished() { if state.timer.tick(time.delta_seconds()).finished() {
println!("Diagnostics (Debug):");
println!("{}", "-".repeat(93));
if let Some(ref filter) = state.filter { if let Some(ref filter) = state.filter {
for diagnostic in filter.iter().map(|id| diagnostics.get(*id).unwrap()) { for diagnostic in filter.iter().map(|id| diagnostics.get(*id).unwrap()) {
println!("{:#?}\n", diagnostic); debug!("{:#?}\n", diagnostic);
} }
} else { } else {
for diagnostic in diagnostics.iter() { for diagnostic in diagnostics.ordered_iter() {
println!("{:#?}\n", diagnostic); debug!("{:#?}\n", diagnostic);
} }
} }
} }

View File

@ -252,9 +252,20 @@ impl Entities {
} }
} }
/// Number of freed entities in `self.meta`
pub fn freed_len(&self) -> u32 {
self.free_cursor.load(Ordering::Relaxed)
}
/// Number of reserved entities outside of `self.meta`
pub fn pending_len(&self) -> u32 {
self.pending.load(Ordering::Relaxed)
}
// The following three methods allow iteration over `reserved` simultaneous to location // The following three methods allow iteration over `reserved` simultaneous to location
// writes. This is a lazy hack, but we only use it in `World::flush` so the complexity and unsafety // writes. This is a lazy hack, but we only use it in `World::flush` so the complexity and unsafety
// involved in producing an `impl Iterator<Item=(u32, &mut Location)>` isn't a clear win. // involved in producing an `impl Iterator<Item=(u32, &mut Location)>` isn't a clear win.
/// Number of reserved entities in `self.meta`
pub fn reserved_len(&self) -> u32 { pub fn reserved_len(&self) -> u32 {
self.reserved_cursor.load(Ordering::Relaxed) self.reserved_cursor.load(Ordering::Relaxed)
} }

View File

@ -500,6 +500,12 @@ impl World {
}) })
} }
pub fn entity_count(&self) -> u32 {
self.entities.meta.len() as u32 - self.entities.freed_len()
+ self.entities.reserved_len()
+ self.entities.pending_len()
}
/// Borrow the `T` component of `entity` without checking if it can be mutated /// Borrow the `T` component of `entity` without checking if it can be mutated
/// ///
/// # Safety /// # Safety

View File

@ -1,5 +1,5 @@
use bevy::{ use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, PrintDiagnosticsPlugin}, diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*, prelude::*,
}; };
use rand::{rngs::StdRng, Rng, SeedableRng}; use rand::{rngs::StdRng, Rng, SeedableRng};
@ -12,7 +12,7 @@ fn main() {
App::build() App::build()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_plugin(FrameTimeDiagnosticsPlugin::default()) .add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(PrintDiagnosticsPlugin::default()) .add_plugin(LogDiagnosticsPlugin::default())
.add_startup_system(setup.system()) .add_startup_system(setup.system())
.add_system(move_cubes.system()) .add_system(move_cubes.system())
.run(); .run();

View File

@ -108,7 +108,7 @@ Example | File | Description
Example | File | Description Example | File | Description
--- | --- | --- --- | --- | ---
`custom_diagnostic` | [`diagnostics/custom_diagnostic.rs`](./diagnostics/custom_diagnostic.rs) | Shows how to create a custom diagnostic `custom_diagnostic` | [`diagnostics/custom_diagnostic.rs`](./diagnostics/custom_diagnostic.rs) | Shows how to create a custom diagnostic
`print_diagnostics` | [`diagnostics/print_diagnostics.rs`](./diagnostics/print_diagnostics.rs) | Add a plugin that prints diagnostics to the console `log_diagnostics` | [`diagnostics/log_diagnostics.rs`](./diagnostics/log_diagnostics.rs) | Add a plugin that logs diagnostics to the console
## ECS (Entity Component System) ## ECS (Entity Component System)

View File

@ -11,7 +11,7 @@ fn main() {
// .add_plugins_with(HelloWorldPlugins, |group| { // .add_plugins_with(HelloWorldPlugins, |group| {
// group // group
// .disable::<PrintWorldPlugin>() // .disable::<PrintWorldPlugin>()
// .add_before::<PrintHelloPlugin, _>(bevy::diagnostic::PrintDiagnosticsPlugin::default()) // .add_before::<PrintHelloPlugin, _>(bevy::diagnostic::LogDiagnosticsPlugin::default())
// }) // })
.run(); .run();
} }

View File

@ -1,5 +1,5 @@
use bevy::{ use bevy::{
diagnostic::{Diagnostic, DiagnosticId, Diagnostics, PrintDiagnosticsPlugin}, diagnostic::{Diagnostic, DiagnosticId, Diagnostics, LogDiagnosticsPlugin},
prelude::*, prelude::*,
}; };
@ -8,7 +8,7 @@ fn main() {
App::build() App::build()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
// The "print diagnostics" plugin is optional. It just visualizes our diagnostics in the console // The "print diagnostics" plugin is optional. It just visualizes our diagnostics in the console
.add_plugin(PrintDiagnosticsPlugin::default()) .add_plugin(LogDiagnosticsPlugin::default())
.add_startup_system(setup_diagnostic_system.system()) .add_startup_system(setup_diagnostic_system.system())
.add_system(my_system.system()) .add_system(my_system.system())
.run(); .run();

View File

@ -1,5 +1,5 @@
use bevy::{ use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, PrintDiagnosticsPlugin}, diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*, prelude::*,
}; };
@ -9,9 +9,13 @@ fn main() {
// Adds frame time diagnostics // Adds frame time diagnostics
.add_plugin(FrameTimeDiagnosticsPlugin::default()) .add_plugin(FrameTimeDiagnosticsPlugin::default())
// Adds a system that prints diagnostics to the console // Adds a system that prints diagnostics to the console
.add_plugin(PrintDiagnosticsPlugin::default()) .add_plugin(LogDiagnosticsPlugin::default())
// Any plugin can register diagnostics // Any plugin can register diagnostics
// Uncomment this to add some render resource diagnostics: // Uncomment this to add some render resource diagnostics:
// .add_plugin(bevy::wgpu::diagnostic::WgpuResourceDiagnosticsPlugin::default()) // .add_plugin(bevy::wgpu::diagnostic::WgpuResourceDiagnosticsPlugin::default())
// Uncomment this to add an entity count diagnostics:
// .add_plugin(bevy::diagnostic::EntityCountDiagnosticsPlugin::default())
// Uncomment this to add an asset count diagnostics:
// .add_plugin(bevy::asset::diagnostic::AssetCountDiagnosticsPlugin::<Texture>::default())
.run(); .run();
} }