new Diagnostics system

This commit is contained in:
Carter Anderson 2020-03-27 15:03:47 -07:00
parent 0073f4a58b
commit 93bf728475
10 changed files with 245 additions and 41 deletions

View File

@ -5,7 +5,8 @@ fn main() {
App::build()
.add_defaults()
.add_system(build_move_system())
.add_system(bevy::diagnostics::build_fps_printer_system())
.add_default_diagnostics()
.print_diagnostics(std::time::Duration::from_secs_f64(1.0))
.setup_world(setup)
.run();
}

View File

@ -24,7 +24,6 @@ fn main() {
.add_system(build_wander_system())
.add_system(build_navigate_system())
.add_system(build_move_system())
.add_system(bevy::diagnostics::build_fps_printer_system())
.run();
}

View File

@ -5,7 +5,8 @@ fn main() {
App::build()
.add_defaults()
.add_system(build_move_system())
.add_system(bevy::diagnostics::build_fps_printer_system())
.add_default_diagnostics()
.print_diagnostics(std::time::Duration::from_secs_f64(1.0))
.setup_world(setup)
.run();
}

View File

@ -5,7 +5,8 @@ fn main() {
.add_defaults()
.setup_world(setup)
.add_system(build_move_system())
.add_system(bevy::diagnostics::build_fps_printer_system())
.add_default_diagnostics()
.print_diagnostics(std::time::Duration::from_secs_f64(1.0))
.run();
}

View File

@ -9,7 +9,7 @@ use crate::{
draw_target::draw_targets::*, mesh::Mesh, pass::passes::*, pipeline::pipelines::*,
render_resource::resource_providers::*, renderer::Renderer, texture::Texture, *,
},
ui,
ui, diagnostic::{Diagnostics, diagnostics},
};
use bevy_transform::{prelude::LocalToWorld, transform_system_bundle};
@ -20,7 +20,7 @@ use render_resource::{
EntityRenderResourceAssignments, RenderResourceAssignments,
};
use shader::Shader;
use std::collections::HashMap;
use std::{time::Duration, collections::HashMap};
pub struct AppBuilder {
pub world: Option<World>,
@ -160,6 +160,7 @@ impl AppBuilder {
pub fn add_default_resources(&mut self) -> &mut Self {
let resources = self.resources.as_mut().unwrap();
resources.insert(Time::new());
resources.insert(Diagnostics::default());
resources.insert(AssetStorage::<Mesh>::new());
resources.insert(AssetStorage::<Texture>::new());
resources.insert(AssetStorage::<Shader>::new());
@ -173,6 +174,18 @@ impl AppBuilder {
self
}
pub fn add_default_diagnostics(&mut self) -> &mut Self {
let frame_time_diagnostic_system = {
let resources = self.resources.as_mut().unwrap();
diagnostics::frame_time_diagnostic_system(resources, 10)
};
self.add_system(frame_time_diagnostic_system)
}
pub fn print_diagnostics(&mut self, wait: Duration) -> &mut Self {
self.add_system(diagnostics::print_diagnostics_system(wait))
}
pub fn batch_types2<T1, T2>(&mut self) -> &mut Self
where
T1: 'static,

View File

@ -3,6 +3,7 @@ use std::time::{Duration, Instant};
pub struct Time {
pub delta: Duration,
pub instant: Instant,
pub delta_seconds_f64: f64,
pub delta_seconds: f32,
}
@ -11,6 +12,7 @@ impl Time {
Time {
delta: Duration::from_secs(0),
instant: Instant::now(),
delta_seconds_f64: 0.0,
delta_seconds: 0.0,
}
}
@ -21,7 +23,8 @@ impl Time {
pub fn stop(&mut self) {
self.delta = Instant::now() - self.instant;
self.delta_seconds =
self.delta.as_secs() as f32 + (self.delta.subsec_nanos() as f32 / 1.0e9);
self.delta_seconds_f64 =
self.delta.as_secs() as f64 + (self.delta.subsec_nanos() as f64 / 1.0e9);
self.delta_seconds = self.delta_seconds_f64 as f32;
}
}

View File

@ -0,0 +1,94 @@
use super::{Diagnostic, DiagnosticId, Diagnostics};
use crate::{
core::Time,
prelude::{Resources, SystemBuilder},
};
use legion::prelude::Schedulable;
use std::time::Duration;
use uuid::Uuid;
pub const FPS: DiagnosticId = DiagnosticId(Uuid::from_bytes([
157, 191, 0, 72, 223, 223, 70, 128, 137, 117, 54, 177, 132, 13, 170, 124,
]));
pub const FRAME_TIME: DiagnosticId = DiagnosticId(Uuid::from_bytes([
216, 184, 55, 12, 28, 116, 69, 201, 187, 137, 176, 77, 83, 89, 251, 241,
]));
pub fn frame_time_diagnostic_system(
resources: &Resources,
max_history_length: usize,
) -> Box<dyn Schedulable> {
let mut diagnostics = resources.get_mut::<Diagnostics>().unwrap();
diagnostics.add(Diagnostic::new(
FRAME_TIME,
"frame_time",
max_history_length,
));
diagnostics.add(Diagnostic::new(FPS, "fps", max_history_length));
SystemBuilder::new("FrameTimeDiagnostic")
.read_resource::<Time>()
.write_resource::<Diagnostics>()
.build(move |_, _world, (time, ref mut diagnostics), _queries| {
if time.delta_seconds_f64 == 0.0 {
return;
}
diagnostics.add_measurement(FRAME_TIME, time.delta_seconds_f64);
if let Some(fps) = diagnostics
.get(FRAME_TIME)
.and_then(|frame_time_diagnostic| {
frame_time_diagnostic
.average()
.and_then(|frame_time_average| {
if frame_time_average > 0.0 {
Some(1.0 / frame_time_average)
} else {
None
}
})
})
{
diagnostics.add_measurement(FPS, fps);
}
})
}
pub fn print_diagnostics_system(wait: Duration) -> Box<dyn Schedulable> {
let mut elasped = 0.0;
let wait_seconds = wait.as_secs_f64();
SystemBuilder::new("PrintDiagnostics")
.read_resource::<Time>()
.read_resource::<Diagnostics>()
.build(move |_, _world, (time, diagnostics), _queries| {
elasped += time.delta_seconds_f64;
if elasped >= wait_seconds {
elasped = 0.0;
for diagnostic in diagnostics.iter() {
if let Some(value) = diagnostic.value() {
println!("{}: {}", diagnostic.name, value);
if let Some(average) = diagnostic.average() {
println!(" average: {}", average);
}
}
}
}
})
}
pub fn print_diagnostics_debug_system(wait: Duration) -> Box<dyn Schedulable> {
let mut elasped = 0.0;
let wait_seconds = wait.as_secs_f64();
SystemBuilder::new("PrintDiagnostics")
.read_resource::<Time>()
.read_resource::<Diagnostics>()
.build(move |_, _world, (time, diagnostics), _queries| {
elasped += time.delta_seconds_f64;
if elasped >= wait_seconds {
elasped = 0.0;
for diagnostic in diagnostics.iter() {
println!("{:#?}\n", diagnostic);
}
}
})
}

124
src/diagnostic/mod.rs Normal file
View File

@ -0,0 +1,124 @@
pub mod diagnostics;
use std::{
collections::{HashMap, VecDeque},
time::{Duration, SystemTime},
};
use uuid::Uuid;
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub struct DiagnosticId(Uuid);
#[derive(Debug)]
pub struct DiagnosticMeasurement {
pub time: SystemTime,
pub value: f64,
}
#[derive(Debug)]
pub struct Diagnostic {
pub id: DiagnosticId,
pub name: String,
history: VecDeque<DiagnosticMeasurement>,
sum: f64,
max_history_length: usize,
}
impl Diagnostic {
pub fn add_measurement(&mut self, value: f64) {
let time = SystemTime::now();
if self.history.len() == self.max_history_length {
if let Some(removed_diagnostic) = self.history.pop_back() {
self.sum -= removed_diagnostic.value;
}
}
self.sum += value;
self.history.push_front(DiagnosticMeasurement {
time,
value,
});
}
pub fn new(id: DiagnosticId, name: &str, max_history_length: usize) -> Diagnostic {
Diagnostic {
id,
name: name.to_string(),
history: VecDeque::with_capacity(max_history_length),
max_history_length,
sum: 0.0,
}
}
pub fn value(&self) -> Option<f64> {
self.history.back().map(|measurement| measurement.value)
}
pub fn sum(&self) -> f64 {
self.sum
}
pub fn average(&self) -> Option<f64> {
if self.history.len() > 0 {
Some(self.sum / self.history.len() as f64)
} else {
None
}
}
pub fn history_len(&self) -> usize {
self.history.len()
}
pub fn duration(&self) -> Option<Duration> {
if self.history.len() < 2 {
return None
}
if let Some(oldest) = self.history.back() {
if let Some(newest) = self.history.front() {
return newest.time.duration_since(oldest.time).ok()
}
}
return None
}
pub fn get_max_history_length(&self) -> usize {
self.max_history_length
}
}
#[derive(Default)]
pub struct Diagnostics {
diagnostics: HashMap<DiagnosticId, Diagnostic>,
}
impl Diagnostics {
pub fn add(&mut self, diagnostic: Diagnostic) {
self.diagnostics.insert(diagnostic.id, diagnostic);
}
pub fn get(&self, id: DiagnosticId) -> Option<&Diagnostic> {
self.diagnostics.get(&id)
}
pub fn get_mut(&mut self, id: DiagnosticId) -> Option<&mut Diagnostic> {
self.diagnostics.get_mut(&id)
}
pub fn get_measurement(&self, id: DiagnosticId) -> Option<&DiagnosticMeasurement> {
self.diagnostics.get(&id).and_then(|diagnostic| diagnostic.history.front())
}
pub fn add_measurement(&mut self, id: DiagnosticId, value: f64) {
if let Some(diagnostic) = self.diagnostics.get_mut(&id) {
diagnostic.add_measurement(value);
}
}
pub fn iter(&self) -> impl Iterator<Item=&Diagnostic> {
self.diagnostics.values()
}
}

View File

@ -1,32 +0,0 @@
use crate::{core::Time, prelude::SystemBuilder};
use legion::prelude::Schedulable;
use std::collections::VecDeque;
pub fn build_fps_printer_system() -> Box<dyn Schedulable> {
let mut elapsed = 0.0;
let mut frame_time_total = 0.0;
let mut frame_time_count = 0;
let frame_time_max = 10;
let mut frame_time_values = VecDeque::new();
SystemBuilder::new("FpsPrinter")
.read_resource::<Time>()
.build(move |_, _world, time, _queries| {
elapsed += time.delta_seconds;
frame_time_values.push_front(time.delta_seconds);
frame_time_total += time.delta_seconds;
frame_time_count += 1;
if frame_time_count > frame_time_max {
frame_time_count = frame_time_max;
frame_time_total -= frame_time_values.pop_back().unwrap();
}
if elapsed > 1.0 {
if frame_time_count > 0 && frame_time_total > 0.0 {
println!(
"fps: {}",
1.0 / (frame_time_total / frame_time_count as f32)
)
}
elapsed = 0.0;
}
})
}

View File

@ -2,7 +2,7 @@
pub mod app;
pub mod asset;
pub mod core;
pub mod diagnostics;
pub mod diagnostic;
pub mod ecs;
pub mod plugin;
pub mod prelude;