use crate::app::plugin::Plugin;
use crate::app::{App, RendererFactory};
use crate::ecs::MonoClock;
use crate::surface::Surface;
pub struct FrameRateCapPlugin {
period_ns: u64,
last_frame_ns: Option<u64>,
sleep_ns: fn(u64),
}
impl FrameRateCapPlugin {
pub fn with_sleep_fn(target_fps: u32, sleep_ns: fn(u64)) -> Self {
assert!(target_fps > 0, "FrameRateCapPlugin: target_fps must be > 0");
Self {
period_ns: 1_000_000_000 / u64::from(target_fps),
last_frame_ns: None,
sleep_ns,
}
}
#[cfg(feature = "std")]
pub fn new(target_fps: u32) -> Self {
fn std_sleep(ns: u64) {
std::thread::sleep(core::time::Duration::from_nanos(ns));
}
Self::with_sleep_fn(target_fps, std_sleep)
}
}
impl<B, F> Plugin<B, F> for FrameRateCapPlugin
where
B: Surface,
F: RendererFactory<B>,
{
fn build(&mut self, _app: &mut App<B, F>) {}
fn post_render(&mut self, world: &mut crate::ecs::World, _render_nanos: u64) {
let Some(now_ns) = world.resource::<MonoClock>().map(|c| c.now_ns()) else {
return;
};
let target = self
.last_frame_ns
.map_or(now_ns, |prev| prev + self.period_ns);
if target > now_ns {
(self.sleep_ns)(target - now_ns);
}
self.last_frame_ns = Some(target.max(now_ns));
}
}