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"
[[example]]
name = "print_diagnostics"
path = "examples/diagnostics/print_diagnostics.rs"
name = "log_diagnostics"
path = "examples/diagnostics/log_diagnostics.rs"
[[example]]
name = "event"

View file

@ -19,6 +19,7 @@ filesystem_watcher = ["notify"]
[dependencies]
# bevy
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_reflect = { path = "../bevy_reflect", version = "0.4.0", features = ["bevy"] }
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 assets;
pub mod diagnostic;
#[cfg(all(
feature = "filesystem_watcher",
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_core = { path = "../bevy_core", 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" }
# other

View file

@ -1,8 +1,8 @@
use bevy_utils::{Duration, HashMap, Instant, Uuid};
use std::collections::VecDeque;
use std::collections::{BTreeSet, VecDeque};
/// 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);
impl DiagnosticId {
@ -102,10 +102,12 @@ impl Diagnostic {
#[derive(Debug, Default)]
pub struct Diagnostics {
diagnostics: HashMap<DiagnosticId, Diagnostic>,
ordered_diagnostics: BTreeSet<DiagnosticId>,
}
impl Diagnostics {
pub fn add(&mut self, diagnostic: Diagnostic) {
self.ordered_diagnostics.insert(diagnostic.id);
self.diagnostics.insert(diagnostic.id, diagnostic);
}
@ -132,4 +134,10 @@ impl Diagnostics {
pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> {
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 entity_count_diagnostics_plugin;
mod frame_time_diagnostics_plugin;
mod print_diagnostics_plugin;
mod log_diagnostics_plugin;
pub use diagnostic::*;
pub use entity_count_diagnostics_plugin::EntityCountDiagnosticsPlugin;
pub use frame_time_diagnostics_plugin::FrameTimeDiagnosticsPlugin;
pub use print_diagnostics_plugin::PrintDiagnosticsPlugin;
pub use log_diagnostics_plugin::LogDiagnosticsPlugin;
use bevy_app::prelude::*;

View file

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

View file

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

View file

@ -108,7 +108,7 @@ Example | File | Description
Example | File | Description
--- | --- | ---
`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)

View file

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

View file

@ -1,5 +1,5 @@
use bevy::{
diagnostic::{Diagnostic, DiagnosticId, Diagnostics, PrintDiagnosticsPlugin},
diagnostic::{Diagnostic, DiagnosticId, Diagnostics, LogDiagnosticsPlugin},
prelude::*,
};
@ -8,7 +8,7 @@ fn main() {
App::build()
.add_plugins(DefaultPlugins)
// 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_system(my_system.system())
.run();

View file

@ -1,5 +1,5 @@
use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, PrintDiagnosticsPlugin},
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*,
};
@ -9,9 +9,13 @@ fn main() {
// Adds frame time diagnostics
.add_plugin(FrameTimeDiagnosticsPlugin::default())
// Adds a system that prints diagnostics to the console
.add_plugin(PrintDiagnosticsPlugin::default())
.add_plugin(LogDiagnosticsPlugin::default())
// Any plugin can register diagnostics
// Uncomment this to add some render resource diagnostics:
// .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();
}