oxihuman_core/
frame_counter.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
7#[derive(Debug, Clone)]
8pub struct FrameCounter {
9 frame: u64,
10 total_time: f64,
11 frame_times: Vec<f64>,
12 max_samples: usize,
13}
14
15#[allow(dead_code)]
16impl FrameCounter {
17 pub fn new(max_samples: usize) -> Self {
18 Self {
19 frame: 0,
20 total_time: 0.0,
21 frame_times: Vec::with_capacity(max_samples),
22 max_samples,
23 }
24 }
25
26 pub fn tick(&mut self, dt: f64) {
27 self.frame += 1;
28 self.total_time += dt;
29 if self.frame_times.len() >= self.max_samples {
30 self.frame_times.remove(0);
31 }
32 self.frame_times.push(dt);
33 }
34
35 pub fn frame(&self) -> u64 {
36 self.frame
37 }
38
39 pub fn total_time(&self) -> f64 {
40 self.total_time
41 }
42
43 pub fn avg_frame_time(&self) -> f64 {
44 if self.frame_times.is_empty() {
45 return 0.0;
46 }
47 let sum: f64 = self.frame_times.iter().sum();
48 sum / self.frame_times.len() as f64
49 }
50
51 pub fn fps(&self) -> f64 {
52 let avg = self.avg_frame_time();
53 if avg <= 0.0 {
54 0.0
55 } else {
56 1.0 / avg
57 }
58 }
59
60 pub fn min_frame_time(&self) -> f64 {
61 self.frame_times
62 .iter()
63 .copied()
64 .reduce(f64::min)
65 .unwrap_or(0.0)
66 }
67
68 pub fn max_frame_time(&self) -> f64 {
69 self.frame_times
70 .iter()
71 .copied()
72 .reduce(f64::max)
73 .unwrap_or(0.0)
74 }
75
76 pub fn sample_count(&self) -> usize {
77 self.frame_times.len()
78 }
79
80 pub fn reset(&mut self) {
81 self.frame = 0;
82 self.total_time = 0.0;
83 self.frame_times.clear();
84 }
85
86 pub fn last_frame_time(&self) -> f64 {
87 self.frame_times.last().copied().unwrap_or(0.0)
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_new() {
97 let fc = FrameCounter::new(60);
98 assert_eq!(fc.frame(), 0);
99 assert!((fc.total_time()).abs() < 1e-12);
100 }
101
102 #[test]
103 fn test_tick() {
104 let mut fc = FrameCounter::new(60);
105 fc.tick(0.016);
106 assert_eq!(fc.frame(), 1);
107 assert!((fc.total_time() - 0.016).abs() < 1e-12);
108 }
109
110 #[test]
111 fn test_avg_frame_time() {
112 let mut fc = FrameCounter::new(60);
113 fc.tick(0.010);
114 fc.tick(0.020);
115 assert!((fc.avg_frame_time() - 0.015).abs() < 1e-12);
116 }
117
118 #[test]
119 fn test_fps() {
120 let mut fc = FrameCounter::new(60);
121 fc.tick(0.01);
122 assert!((fc.fps() - 100.0).abs() < 1e-6);
123 }
124
125 #[test]
126 fn test_min_max() {
127 let mut fc = FrameCounter::new(60);
128 fc.tick(0.010);
129 fc.tick(0.005);
130 fc.tick(0.020);
131 assert!((fc.min_frame_time() - 0.005).abs() < 1e-12);
132 assert!((fc.max_frame_time() - 0.020).abs() < 1e-12);
133 }
134
135 #[test]
136 fn test_max_samples() {
137 let mut fc = FrameCounter::new(3);
138 fc.tick(1.0);
139 fc.tick(2.0);
140 fc.tick(3.0);
141 fc.tick(4.0);
142 assert_eq!(fc.sample_count(), 3);
143 assert!((fc.min_frame_time() - 2.0).abs() < 1e-12);
144 }
145
146 #[test]
147 fn test_reset() {
148 let mut fc = FrameCounter::new(60);
149 fc.tick(0.016);
150 fc.tick(0.016);
151 fc.reset();
152 assert_eq!(fc.frame(), 0);
153 assert_eq!(fc.sample_count(), 0);
154 }
155
156 #[test]
157 fn test_last_frame_time() {
158 let mut fc = FrameCounter::new(60);
159 fc.tick(0.01);
160 fc.tick(0.02);
161 assert!((fc.last_frame_time() - 0.02).abs() < 1e-12);
162 }
163
164 #[test]
165 fn test_empty_stats() {
166 let fc = FrameCounter::new(60);
167 assert!((fc.avg_frame_time()).abs() < 1e-12);
168 assert!((fc.fps()).abs() < 1e-12);
169 assert!((fc.last_frame_time()).abs() < 1e-12);
170 }
171
172 #[test]
173 fn test_total_time_accumulates() {
174 let mut fc = FrameCounter::new(2);
175 fc.tick(1.0);
176 fc.tick(2.0);
177 fc.tick(3.0);
178 assert!((fc.total_time() - 6.0).abs() < 1e-12);
179 }
180}