use crate::timers::*;
use std::cell::RefCell;
use std::convert::TryFrom;
use std::rc::Rc;
enum RefreshMode {
Lazy, FullSpeed, }
impl<'a> TryFrom<&Vec<&'a str>> for RefreshMode {
type Error = ();
fn try_from(options: &Vec<&'a str>) -> Result<Self, Self::Error> {
if options.contains(&"refresh_lazy") {
Ok(Self::Lazy)
} else if options.contains(&"refresh_full_speed") {
Ok(Self::FullSpeed)
} else {
Err(())
}
}
}
pub struct FPSCounter {
frame_times: RefCell<Vec<instant::Instant>>,
update_timer: Timer,
refresh_mode: RefreshMode,
output_console: bool,
output_overlay: bool,
}
impl FPSCounter {
pub fn new() -> Option<Rc<Self>> {
let options = match std::env::var("SIXTYFPS_DEBUG_PERFORMANCE") {
Ok(var) => var,
_ => return None,
};
let options: Vec<&str> = options.split(',').collect();
let refresh_mode = match RefreshMode::try_from(&options) {
Ok(mode) => mode,
Err(_) => {
eprintln!("Missing refresh mode in SIXTYFPS_DEBUG_PERFORMANCE. Please specify either refresh_full_speed or refresh_lazy");
return None;
}
};
let output_console = options.contains(&"console");
let output_overlay = options.contains(&"overlay");
if !output_console && !output_overlay {
eprintln!("Missing output mode in SIXTYFPS_DEBUG_PERFORMANCE. Please specify either console or overlay (or both)");
return None;
}
Some(Rc::new(Self {
frame_times: Default::default(),
update_timer: Default::default(),
refresh_mode,
output_console,
output_overlay,
}))
}
pub fn start(self: &Rc<Self>, winsys_info: &str) {
#[cfg(debug_assertions)]
let build_config = "debug";
#[cfg(not(debug_assertions))]
let build_config = "release";
eprintln!("SixtyFPS: Build config: {}; Backend: {}", build_config, winsys_info);
let this = self.clone();
self.update_timer.stop();
self.update_timer.start(TimerMode::Repeated, std::time::Duration::from_secs(1), move || {
this.trim_frame_times();
if this.output_console {
eprintln!("average frames per second: {}", this.frame_times.borrow().len());
}
})
}
fn trim_frame_times(self: &Rc<Self>) {
let mut i = 0;
let mut frame_times = self.frame_times.borrow_mut();
while i < frame_times.len() {
if frame_times[i].elapsed() > std::time::Duration::from_secs(1) {
frame_times.remove(i);
} else {
i += 1
}
}
}
pub fn measure_frame_rendered(
self: &Rc<Self>,
renderer_for_overlay: &mut dyn crate::item_rendering::ItemRenderer,
) {
self.frame_times.borrow_mut().push(instant::Instant::now());
if matches!(self.refresh_mode, RefreshMode::FullSpeed) {
crate::animations::CURRENT_ANIMATION_DRIVER
.with(|driver| driver.set_has_active_animations());
}
self.trim_frame_times();
if self.output_overlay {
renderer_for_overlay.draw_string(
&format!("FPS: {}", self.frame_times.borrow().len()),
crate::Color::from_rgb_u8(0, 128, 128),
);
}
}
}