add system profile data to Diagnostics (opt in feature)
This commit is contained in:
parent
0c9c0a9b3b
commit
4f73dca34d
@ -7,6 +7,7 @@ edition = "2018"
|
|||||||
[features]
|
[features]
|
||||||
default = ["bevy_wgpu", "bevy_winit"]
|
default = ["bevy_wgpu", "bevy_winit"]
|
||||||
more-system-fns = ["legion/more-system-fns"]
|
more-system-fns = ["legion/more-system-fns"]
|
||||||
|
profiler = ["bevy_app/profiler", "bevy_diagnostic/profiler"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
|
|||||||
@ -4,6 +4,9 @@ version = "0.1.0"
|
|||||||
authors = ["Carter Anderson <mcanders1@gmail.com>"]
|
authors = ["Carter Anderson <mcanders1@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
profiler = ["legion/profiler"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy_derive = { path = "../bevy_derive" }
|
bevy_derive = { path = "../bevy_derive" }
|
||||||
legion = { path = "../bevy_legion", features = ["serialize"] }
|
legion = { path = "../bevy_legion", features = ["serialize"] }
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
plugin::{load_plugin, AppPlugin},
|
plugin::{load_plugin, AppPlugin},
|
||||||
schedule_plan::SchedulePlan,
|
schedule_plan::SchedulePlan,
|
||||||
stage, App, AppExit, Events, FromResources, System, startup_stage,
|
stage, startup_stage, App, AppExit, Events, FromResources, System,
|
||||||
};
|
};
|
||||||
|
|
||||||
use legion::prelude::{IntoSystem, Resources, World};
|
use legion::prelude::{IntoSystem, Resources, World};
|
||||||
|
|||||||
@ -4,6 +4,9 @@ version = "0.1.0"
|
|||||||
authors = ["Carter Anderson <mcanders1@gmail.com>"]
|
authors = ["Carter Anderson <mcanders1@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
profiler = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy_app = { path = "../bevy_app" }
|
bevy_app = { path = "../bevy_app" }
|
||||||
bevy_core = { path = "../bevy_core" }
|
bevy_core = { path = "../bevy_core" }
|
||||||
|
|||||||
@ -13,6 +13,13 @@ impl DiagnosticId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for DiagnosticId {
|
||||||
|
fn default() -> Self {
|
||||||
|
DiagnosticId(Uuid::new_v4())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DiagnosticMeasurement {
|
pub struct DiagnosticMeasurement {
|
||||||
pub time: SystemTime,
|
pub time: SystemTime,
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
mod diagnostic;
|
mod diagnostic;
|
||||||
mod frame_time_diagnostics_plugin;
|
mod frame_time_diagnostics_plugin;
|
||||||
mod print_diagnostics_plugin;
|
mod print_diagnostics_plugin;
|
||||||
|
#[cfg(feature = "profiler")]
|
||||||
|
mod system_profiler;
|
||||||
pub use diagnostic::*;
|
pub use diagnostic::*;
|
||||||
pub use frame_time_diagnostics_plugin::FrameTimeDiagnosticsPlugin;
|
pub use frame_time_diagnostics_plugin::FrameTimeDiagnosticsPlugin;
|
||||||
pub use print_diagnostics_plugin::PrintDiagnosticsPlugin;
|
pub use print_diagnostics_plugin::PrintDiagnosticsPlugin;
|
||||||
@ -15,5 +17,11 @@ pub struct DiagnosticsPlugin;
|
|||||||
impl AppPlugin for DiagnosticsPlugin {
|
impl AppPlugin for DiagnosticsPlugin {
|
||||||
fn build(&self, app: &mut AppBuilder) {
|
fn build(&self, app: &mut AppBuilder) {
|
||||||
app.init_resource::<Diagnostics>();
|
app.init_resource::<Diagnostics>();
|
||||||
|
#[cfg(feature = "profiler")]
|
||||||
|
{
|
||||||
|
use legion::prelude::IntoSystem;
|
||||||
|
app.add_resource::<Box<dyn legion::systems::profiler::Profiler>>(Box::new(system_profiler::SystemProfiler::default()))
|
||||||
|
.add_system_to_stage(bevy_app::stage::LAST, system_profiler::profiler_diagnostic_system.system());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,7 +53,7 @@ impl PrintDiagnosticsPlugin {
|
|||||||
|
|
||||||
fn print_diagnostic(diagnostic: &Diagnostic) {
|
fn print_diagnostic(diagnostic: &Diagnostic) {
|
||||||
if let Some(value) = diagnostic.value() {
|
if let Some(value) = diagnostic.value() {
|
||||||
print!("{:<20}: {:<19.6}", diagnostic.name, value);
|
print!("{:<65}: {:<10.6}", diagnostic.name, value);
|
||||||
if let Some(average) = diagnostic.average() {
|
if let Some(average) = diagnostic.average() {
|
||||||
print!(" (avg {:.6})", average);
|
print!(" (avg {:.6})", average);
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ impl PrintDiagnosticsPlugin {
|
|||||||
state.timer.tick(time.delta_seconds);
|
state.timer.tick(time.delta_seconds);
|
||||||
if state.timer.finished {
|
if state.timer.finished {
|
||||||
println!("Diagnostics:");
|
println!("Diagnostics:");
|
||||||
println!("{}", "-".repeat(60));
|
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::print_diagnostic(diagnostic);
|
||||||
@ -93,7 +93,7 @@ impl PrintDiagnosticsPlugin {
|
|||||||
state.timer.tick(time.delta_seconds);
|
state.timer.tick(time.delta_seconds);
|
||||||
if state.timer.finished {
|
if state.timer.finished {
|
||||||
println!("Diagnostics (Debug):");
|
println!("Diagnostics (Debug):");
|
||||||
println!("{}", "-".repeat(60));
|
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);
|
println!("{:#?}\n", diagnostic);
|
||||||
|
|||||||
73
crates/bevy_diagnostic/src/system_profiler.rs
Normal file
73
crates/bevy_diagnostic/src/system_profiler.rs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
use crate::{Diagnostic, DiagnosticId, Diagnostics};
|
||||||
|
use legion::{
|
||||||
|
systems::{profiler::Profiler, Res, ResMut},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
time::Instant, borrow::Cow,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct SystemRunInfo {
|
||||||
|
start: Instant,
|
||||||
|
stop: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct SystemProfiles {
|
||||||
|
diagnostic_id: DiagnosticId,
|
||||||
|
history: Vec<SystemRunInfo>,
|
||||||
|
current_start: Option<Instant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SystemProfiler {
|
||||||
|
system_profiles: Arc<RwLock<HashMap<Cow<'static, str>, SystemProfiles>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Profiler for SystemProfiler {
|
||||||
|
fn start(&self, scope: Cow<'static, str>) {
|
||||||
|
let mut system_profiles = self.system_profiles.write().unwrap();
|
||||||
|
let profiles = system_profiles
|
||||||
|
.entry(scope.clone())
|
||||||
|
.or_insert_with(|| SystemProfiles::default());
|
||||||
|
|
||||||
|
profiles.current_start = Some(Instant::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&self, scope: Cow<'static, str>) {
|
||||||
|
let now = Instant::now();
|
||||||
|
let mut system_profiles = self.system_profiles.write().unwrap();
|
||||||
|
let profiles = system_profiles.get_mut(&scope).unwrap();
|
||||||
|
if let Some(current_start) = profiles.current_start.take() {
|
||||||
|
profiles.history.push(SystemRunInfo {
|
||||||
|
start: current_start,
|
||||||
|
stop: now,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn profiler_diagnostic_system(
|
||||||
|
mut diagnostics: ResMut<Diagnostics>,
|
||||||
|
system_profiler: Res<Box<dyn Profiler>>,
|
||||||
|
) {
|
||||||
|
let system_profiler = system_profiler.downcast_ref::<SystemProfiler>().unwrap();
|
||||||
|
let mut system_profiles = system_profiler.system_profiles.write().unwrap();
|
||||||
|
for (scope, profiles) in system_profiles.iter_mut() {
|
||||||
|
if diagnostics.get(profiles.diagnostic_id).is_none() {
|
||||||
|
diagnostics.add(Diagnostic::new(
|
||||||
|
profiles.diagnostic_id,
|
||||||
|
&scope,
|
||||||
|
20,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
for profile in profiles.history.drain(..) {
|
||||||
|
diagnostics.add_measurement(
|
||||||
|
profiles.diagnostic_id,
|
||||||
|
(profile.stop - profile.start).as_secs_f64(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -22,6 +22,7 @@ ffi = ["legion-core/ffi"]
|
|||||||
serialize = ["legion-core/serialize"]
|
serialize = ["legion-core/serialize"]
|
||||||
metrics = ["legion-core/metrics"]
|
metrics = ["legion-core/metrics"]
|
||||||
more-system-fns = ["legion-systems/more-system-fns"]
|
more-system-fns = ["legion-systems/more-system-fns"]
|
||||||
|
profiler = ["legion-systems/profiler"]
|
||||||
|
|
||||||
# [workspace]
|
# [workspace]
|
||||||
# members = [
|
# members = [
|
||||||
|
|||||||
@ -20,7 +20,7 @@ serialize = ["serde"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
parking_lot = "0.10"
|
parking_lot = "0.10"
|
||||||
downcast-rs = "1.0"
|
downcast-rs = "1.1.1"
|
||||||
itertools = "0.9"
|
itertools = "0.9"
|
||||||
rayon = { version = "1.2", optional = true }
|
rayon = { version = "1.2", optional = true }
|
||||||
crossbeam-queue = { version = "0.2.0", optional = true }
|
crossbeam-queue = { version = "0.2.0", optional = true }
|
||||||
|
|||||||
@ -17,12 +17,13 @@ travis-ci = { repository = "TomGillen/legion", branch = "master" }
|
|||||||
par-iter = ["rayon", "legion-core/par-iter"]
|
par-iter = ["rayon", "legion-core/par-iter"]
|
||||||
par-schedule = ["rayon", "crossbeam-queue"]
|
par-schedule = ["rayon", "crossbeam-queue"]
|
||||||
more-system-fns = []
|
more-system-fns = []
|
||||||
|
profiler = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
legion-core = { path = "../legion_core", version = "0.2.4", default-features = false }
|
legion-core = { path = "../legion_core", version = "0.2.4", default-features = false }
|
||||||
legion_fn_system_macro = { path = "../legion_fn_system_macro" }
|
legion_fn_system_macro = { path = "../legion_fn_system_macro" }
|
||||||
|
|
||||||
downcast-rs = "1.0"
|
downcast-rs = "1.1.1"
|
||||||
itertools = "0.9"
|
itertools = "0.9"
|
||||||
rayon = { version = "1.2", optional = true }
|
rayon = { version = "1.2", optional = true }
|
||||||
crossbeam-queue = { version = "0.2.0", optional = true }
|
crossbeam-queue = { version = "0.2.0", optional = true }
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
pub mod resource;
|
pub mod resource;
|
||||||
pub mod schedule;
|
pub mod schedule;
|
||||||
|
pub mod profiler;
|
||||||
|
|
||||||
mod system;
|
mod system;
|
||||||
mod system_fn;
|
mod system_fn;
|
||||||
|
|||||||
24
crates/bevy_legion/legion_systems/src/profiler.rs
Normal file
24
crates/bevy_legion/legion_systems/src/profiler.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use crate::{
|
||||||
|
resource::Resources,
|
||||||
|
};
|
||||||
|
use downcast_rs::{impl_downcast, Downcast};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
pub trait Profiler: Downcast + Send + Sync + 'static {
|
||||||
|
fn start(&self, scope: Cow<'static, str>);
|
||||||
|
fn stop(&self, scope: Cow<'static, str>);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn profiler_start(resources: &Resources, scope: Cow<'static, str>) {
|
||||||
|
if let Some(profiler) = resources.get::<Box<dyn Profiler>>() {
|
||||||
|
profiler.start(scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn profiler_stop(resources: &Resources, scope: Cow<'static, str>) {
|
||||||
|
if let Some(profiler) = resources.get::<Box<dyn Profiler>>() {
|
||||||
|
profiler.stop(scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_downcast!(Profiler);
|
||||||
@ -307,8 +307,12 @@ impl Executor {
|
|||||||
pub fn run_systems(&mut self, world: &mut World, resources: &mut Resources) {
|
pub fn run_systems(&mut self, world: &mut World, resources: &mut Resources) {
|
||||||
self.systems.iter_mut().for_each(|system| {
|
self.systems.iter_mut().for_each(|system| {
|
||||||
let system = unsafe { system.get_mut() };
|
let system = unsafe { system.get_mut() };
|
||||||
|
#[cfg(feature = "profiler")]
|
||||||
|
crate::profiler::profiler_start(resources, system.name().name());
|
||||||
system.prepare(world);
|
system.prepare(world);
|
||||||
system.run(world, resources);
|
system.run(world, resources);
|
||||||
|
#[cfg(feature = "profiler")]
|
||||||
|
crate::profiler::profiler_stop(resources, system.name().name());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,8 +332,12 @@ impl Executor {
|
|||||||
// safety: we have exlusive access to all systems, world and resources here
|
// safety: we have exlusive access to all systems, world and resources here
|
||||||
unsafe {
|
unsafe {
|
||||||
let system = self.systems[0].get_mut();
|
let system = self.systems[0].get_mut();
|
||||||
|
#[cfg(feature = "profiler")]
|
||||||
|
crate::profiler::profiler_start(resources, system.name().name());
|
||||||
system.prepare(world);
|
system.prepare(world);
|
||||||
system.run(world, resources);
|
system.run(world, resources);
|
||||||
|
#[cfg(feature = "profiler")]
|
||||||
|
crate::profiler::profiler_stop(resources, system.name().name());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -415,7 +423,11 @@ impl Executor {
|
|||||||
#[cfg(feature = "par-schedule")]
|
#[cfg(feature = "par-schedule")]
|
||||||
unsafe fn run_recursive(&self, i: usize, world: &World, resources: &Resources) {
|
unsafe fn run_recursive(&self, i: usize, world: &World, resources: &Resources) {
|
||||||
// safety: the caller ensures nothing else is accessing systems[i]
|
// safety: the caller ensures nothing else is accessing systems[i]
|
||||||
|
#[cfg(feature = "profiler")]
|
||||||
|
crate::profiler::profiler_start(resources, self.systems[i].get().name().name());
|
||||||
self.systems[i].get_mut().run_unsafe(world, resources);
|
self.systems[i].get_mut().run_unsafe(world, resources);
|
||||||
|
#[cfg(feature = "profiler")]
|
||||||
|
crate::profiler::profiler_stop(resources, self.systems[i].get().name().name());
|
||||||
|
|
||||||
self.static_dependants[i].par_iter().for_each(|dep| {
|
self.static_dependants[i].par_iter().for_each(|dep| {
|
||||||
if self.awaiting[*dep].fetch_sub(1, Ordering::Relaxed) == 1 {
|
if self.awaiting[*dep].fetch_sub(1, Ordering::Relaxed) == 1 {
|
||||||
@ -550,10 +562,20 @@ impl Schedule {
|
|||||||
ToFlush::System(mut cmd) => cmd.write(world),
|
ToFlush::System(mut cmd) => cmd.write(world),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Step::ThreadLocalFn(function) => function(world, resources),
|
Step::ThreadLocalFn(function) => {
|
||||||
|
#[cfg(feature = "profiler")]
|
||||||
|
crate::profiler::profiler_start(resources, std::borrow::Cow::Borrowed("thread_local"));
|
||||||
|
function(world, resources);
|
||||||
|
#[cfg(feature = "profiler")]
|
||||||
|
crate::profiler::profiler_stop(resources, std::borrow::Cow::Borrowed("thread_local"));
|
||||||
|
},
|
||||||
Step::ThreadLocalSystem(system) => {
|
Step::ThreadLocalSystem(system) => {
|
||||||
|
#[cfg(feature = "profiler")]
|
||||||
|
crate::profiler::profiler_start(resources, system.name().name());
|
||||||
system.prepare(world);
|
system.prepare(world);
|
||||||
system.run(world, resources);
|
system.run(world, resources);
|
||||||
|
#[cfg(feature = "profiler")]
|
||||||
|
crate::profiler::profiler_stop(resources, system.name().name());
|
||||||
if let Some(cmd) = system.command_buffer_mut(world.id()) {
|
if let Some(cmd) = system.command_buffer_mut(world.id()) {
|
||||||
waiting_flush.push(ToFlush::System(cmd));
|
waiting_flush.push(ToFlush::System(cmd));
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user