1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// Frame timing — fixed timestep, delta time, FPS tracking.
use std::time::Instant;
/// Frame timer with delta time and FPS tracking.
pub struct FrameTimer {
last_frame: Instant,
delta: f32,
fps: f32,
frame_count: u64,
}
impl Default for FrameTimer {
fn default() -> Self {
Self::new()
}
}
impl FrameTimer {
pub fn new() -> Self {
Self {
last_frame: Instant::now(),
delta: 0.0,
fps: 0.0,
frame_count: 0,
}
}
/// Call once per frame to update timing.
pub fn tick(&mut self) {
let now = Instant::now();
self.delta = now.duration_since(self.last_frame).as_secs_f32();
self.last_frame = now;
self.frame_count += 1;
// Clamp delta to avoid division by zero or Inf propagation.
// Upper bound of 50ms prevents spiral-of-death on hitches.
let dt = self.delta.clamp(0.0001, 0.05);
// Exponential moving average FPS
let instant_fps = 1.0 / dt;
let new_fps = self.fps * 0.95 + instant_fps * 0.05;
if new_fps.is_finite() {
self.fps = new_fps.clamp(0.0, 1000.0);
}
}
/// Seconds since last frame.
pub fn delta_time(&self) -> f32 {
self.delta
}
/// Smoothed frames per second.
pub fn fps(&self) -> f32 {
self.fps
}
/// Total frames rendered.
pub fn frame_count(&self) -> u64 {
self.frame_count
}
}