use crate::{ metrics::{EcsSystemMetrics, PhysicsMetrics, TickMetrics}, HwStats, Tick, TickStart, }; use common::{resources::TimeOfDay, terrain::TerrainGrid}; use common_ecs::{Job, Origin, Phase, SysMetrics, System}; use specs::{Entities, Join, Read, ReadExpect}; use std::time::Instant; /// This system exports metrics #[derive(Default)] pub struct Sys; impl<'a> System<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( Option>, ReadExpect<'a, HwStats>, ReadExpect<'a, Tick>, ReadExpect<'a, TimeOfDay>, ReadExpect<'a, TickStart>, Option>, Read<'a, SysMetrics>, Read<'a, common_ecs::PhysicsMetrics>, ReadExpect<'a, EcsSystemMetrics>, ReadExpect<'a, TickMetrics>, ReadExpect<'a, PhysicsMetrics>, ); const NAME: &'static str = "metrics"; const ORIGIN: Origin = Origin::Server; const PHASE: Phase = Phase::Apply; fn run( _job: &mut Job, ( entities, hw_stats, tick, time_of_day, tick_start, terrain, sys_metrics, phys_metrics, export_ecs, export_tick, export_physics, ): Self::SystemData, ) { const NANOSEC_PER_SEC: f64 = std::time::Duration::from_secs(1).as_nanos() as f64; let start = Instant::now(); let mut state = sys_metrics.stats.lock().unwrap(); //this system hasn't run yet state.remove(Self::NAME); for (name, stat) in common_ecs::gen_stats( &state, tick_start.0, hw_stats.rayon_threads, hw_stats.hardware_threads, ) { export_ecs .system_start_time .with_label_values(&[&name]) .set(stat.start_ns() as i64); export_ecs .system_thread_avg .with_label_values(&[&name]) .set(stat.avg_threads() as f64); let len = stat.length_ns(); export_ecs .system_length_time .with_label_values(&[&name]) .set(len as i64); export_ecs .system_length_count .with_label_values(&[&name]) .inc_by(len); export_ecs .system_length_hist .with_label_values(&[&name]) .observe(len as f64 / NANOSEC_PER_SEC); } // Report other info export_tick.time_of_day.set(time_of_day.0); if tick.0.rem_euclid(100) == 0 { if let Some(terrain) = terrain { let mut chonk_cnt = 0; let mut group_cnt = 0; let chunk_cnt = terrain.iter().fold(0, |a, (_, c)| { chonk_cnt += 1; group_cnt += c.sub_chunk_groups(); a + c.sub_chunks_len() }); export_tick.chonks_count.set(chonk_cnt as i64); export_tick.chunks_count.set(chunk_cnt as i64); export_tick.chunk_groups_count.set(group_cnt as i64); } if let Some(entities) = entities { let entity_count = entities.join().count(); export_tick.entity_count.set(entity_count as i64); common_base::plot!("entity count", entity_count as f64); } } //detailed physics metrics export_physics .entity_entity_collision_checks_count .inc_by(phys_metrics.entity_entity_collision_checks); export_physics .entity_entity_collisions_count .inc_by(phys_metrics.entity_entity_collisions); // export self time as best as possible export_ecs .system_start_time .with_label_values(&["metrics"]) .set(start.duration_since(tick_start.0).as_nanos() as i64); export_ecs .system_thread_avg .with_label_values(&["metrics"]) .set(1.0); let len = start.elapsed().as_nanos() as u64; export_ecs .system_length_time .with_label_values(&["metrics"]) .set(len as i64); export_ecs .system_length_count .with_label_values(&["metrics"]) .inc_by(len); export_ecs .system_length_hist .with_label_values(&["metrics"]) .observe(len as f64 / NANOSEC_PER_SEC); } }