use crate::components::{FpsCounter, TextLabel};
use crate::ecs::asset_id::AssetId;
use crate::ecs::{PipelineContext, StepResult, System};
use std::time::Instant;
#[derive(Debug)]
pub(crate) struct FpsCounterSystem {
last_time: Instant,
frame_count: u32,
label: Option<AssetId>,
}
impl FpsCounterSystem {
pub(crate) fn new(config: FpsCounter) -> Self {
Self {
last_time: Instant::now(),
frame_count: 0,
label: config.label,
}
}
}
impl System for FpsCounterSystem {
fn access(&self) -> crate::ecs::Access {
crate::ecs::Access::new()
.writes_components(crate::component_mask![crate::components::TextLabel])
}
fn step(&mut self, ctx: &mut PipelineContext) -> StepResult {
self.frame_count += 1;
let now = Instant::now();
let elapsed = now.duration_since(self.last_time).as_secs_f64();
if elapsed >= 1.0 {
let fps = self.frame_count as f64 / elapsed;
if let Some(label_id) = self.label {
for lbl in ctx.query_mut::<TextLabel>() {
if lbl.asset_id == label_id {
lbl.content = format!("FPS: {:.0}", fps);
break;
}
}
}
self.frame_count = 0;
self.last_time = now;
}
StepResult::Continue
}
}
#[cfg(test)]
mod tests {
use super::FpsCounterSystem;
use crate::components::FpsCounter;
use crate::ecs::SYSTEMS;
use crate::ecs::World;
#[test]
fn fps_counter_component_spawns_internal_system() {
let mut world = World::new();
world.add_component(FpsCounter::default());
world.start(SYSTEMS).unwrap();
let names: Vec<&str> = world.systems().iter().map(|s| s.name()).collect();
assert_eq!(names, ["FpsCounter"]);
}
#[test]
fn no_fps_counter_no_system() {
let mut world = World::new();
world.start(SYSTEMS).unwrap();
assert!(world.systems().is_empty());
}
#[test]
fn rate_written_into_label_after_a_second() {
use crate::components::TextLabel;
use crate::ecs::asset_id::AssetId;
use std::time::{Duration, Instant};
let mut world = World::new();
world.add_component(FpsCounter {
label: Some(AssetId(1)),
});
world.add_component(TextLabel {
asset_id: AssetId(1),
..Default::default()
});
world.start(SYSTEMS).unwrap();
for system in world.systems_mut() {
if let Some(s) = system.downcast_mut::<FpsCounterSystem>() {
s.last_time = Instant::now() - Duration::from_millis(1100);
}
}
world.step();
let content = world
.query::<TextLabel>()
.find(|l| l.asset_id == AssetId(1))
.map(|l| l.content.clone())
.unwrap_or_default();
assert!(content.starts_with("FPS: "), "{content}");
}
}