use std::time::{Duration, Instant};
const FRAME_BUFFER_MAX: usize = 512;
const FRAME_TTL: Duration = Duration::from_millis(5000);
const REPORT_INTERVAL: Duration = Duration::from_millis(1000);
const REPORT_FIRST_FRAMES_MIN: usize = 5;
pub struct FpsCounter {
frames: Vec<Instant>,
last_report: Option<Instant>,
}
impl FpsCounter {
pub fn new() -> FpsCounter {
FpsCounter {
frames: Vec::with_capacity(FRAME_BUFFER_MAX),
last_report: None,
}
}
pub fn tick(&mut self) {
if self.frames.len() >= FRAME_BUFFER_MAX {
self.frames.remove(0);
}
self.frames.push(Instant::now());
self.report_periodically();
}
pub fn calculate_fps(&mut self) -> Option<f64> {
self.cleanup_frames();
if self.frames.len() == 0 {
return None;
}
let passed = Instant::now().duration_since(self.frames[0]).as_micros();
Some((self.frames.len() as f64) / ((passed as f64) / 1_000_000f64))
}
pub fn report_periodically(&mut self) {
if self.last_report.is_none() && self.frames.len() < REPORT_FIRST_FRAMES_MIN {
return;
}
if let Some(last_report) = self.last_report {
let passed = Instant::now().duration_since(last_report);
if passed < REPORT_INTERVAL {
return;
}
}
self.report();
}
pub fn report(&mut self) {
if let Some(fps) = self.calculate_fps() {
println!("FPS: {:.1}", fps);
self.last_report = Some(Instant::now());
}
}
fn cleanup_frames(&mut self) {
if self.frames.len() > FRAME_BUFFER_MAX {
let overhead = self.frames.len() - FRAME_BUFFER_MAX;
self.frames.drain(0..overhead);
}
let now = Instant::now();
let dead = self
.frames
.iter()
.take_while(|frame| now.duration_since(**frame) > FRAME_TTL)
.count();
if dead > 0 {
self.frames.drain(0..dead);
}
}
}