summaryrefslogtreecommitdiff
path: root/graphics/src/timer.rs
blob: e2209c7e4f4bc28547fe7e95ccfcec8a05ae2513 (plain)
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
63
64
65
66
67
68
69
70
71
use std::time::{Duration, Instant};

/// Number of fps samples to store
const MAX_SAMPLES: usize = 100;

/// Keeps track of frame intervals and FPS
pub struct Timer {
	previous: Instant,
	tick_index: usize,
	tick_sum: Duration,
	tick_list: Box<[Duration; MAX_SAMPLES]>,
	frame: u32,
}
impl Timer {
	pub fn new() -> Self {
		let previous = Instant::now();
		let tick_index = 0usize;
		let tick_sum = Duration::ZERO;
		let tick_list = Box::new([tick_sum; MAX_SAMPLES]);
		let frame = 0u32;

		Self {
			previous,
			tick_index,
			tick_sum,
			tick_list,
			frame,
		}
	}

	pub fn update(&mut self) {
		let time = self.previous.elapsed();
		self.previous = Instant::now();

		// caculate average tick
		self.tick_index += 1;
		self.tick_index %= MAX_SAMPLES;
		self.tick_sum -= self.tick_list[self.tick_index];
		self.tick_sum += time;
		self.tick_list[self.tick_index] = time;

		// update frame frame
		self.frame += 1;
	}

	pub const fn frame(&self) -> u32 {
		self.frame
	}

	#[expect(clippy::cast_possible_truncation)]
	pub const fn fps(&self) -> u32 {
        match self.frame_time().as_nanos() {
            0 => u32::MAX,
            // fps can never be bigger than u32::MAX since we're
            // computing in nanoseconds
            nanos => 1_000_000_000u128.div_ceil(nanos) as u32,
        }
    }

	#[expect(clippy::cast_possible_truncation)]
	pub const fn frame_time(&self) -> Duration {
		match self.tick_sum.checked_div(MAX_SAMPLES as u32) {
			Some(duration) => duration,
			None => Duration::ZERO,
		}
	}

	pub const fn delta_time(&self) -> Duration {
		self.tick_list[self.tick_index]
	}
}