hojicha_core/debug/
metrics.rs1use std::collections::VecDeque;
4use std::time::{Duration, Instant};
5
6#[derive(Debug, Clone)]
8pub struct FrameMetrics {
9 pub update_duration: Duration,
11 pub view_duration: Duration,
13 pub frame_duration: Duration,
15 pub events_processed: usize,
17 pub commands_executed: usize,
19 pub timestamp: Instant,
21}
22
23impl FrameMetrics {
24 pub fn fps(&self) -> f32 {
26 if self.frame_duration.as_secs_f32() > 0.0 {
27 1.0 / self.frame_duration.as_secs_f32()
28 } else {
29 0.0
30 }
31 }
32}
33
34#[derive(Debug, Clone)]
36pub struct PerformanceMetrics {
37 frames: VecDeque<FrameMetrics>,
39 max_frames: usize,
41 total_events: usize,
43 total_commands: usize,
45 start_time: Instant,
47 current_frame: Option<FrameBuilder>,
49}
50
51#[derive(Debug, Clone)]
52struct FrameBuilder {
53 update_start: Option<Instant>,
54 update_duration: Option<Duration>,
55 view_start: Option<Instant>,
56 view_duration: Option<Duration>,
57 frame_start: Instant,
58 events_processed: usize,
59 commands_executed: usize,
60}
61
62impl PerformanceMetrics {
63 pub fn new() -> Self {
65 Self::with_capacity(1000)
66 }
67
68 pub fn with_capacity(max_frames: usize) -> Self {
70 Self {
71 frames: VecDeque::with_capacity(max_frames),
72 max_frames,
73 total_events: 0,
74 total_commands: 0,
75 start_time: Instant::now(),
76 current_frame: None,
77 }
78 }
79
80 pub fn start_frame(&mut self) {
82 self.current_frame = Some(FrameBuilder {
83 update_start: None,
84 update_duration: None,
85 view_start: None,
86 view_duration: None,
87 frame_start: Instant::now(),
88 events_processed: 0,
89 commands_executed: 0,
90 });
91 }
92
93 pub fn start_update(&mut self) {
95 if let Some(frame) = &mut self.current_frame {
96 frame.update_start = Some(Instant::now());
97 }
98 }
99
100 pub fn end_update(&mut self) {
102 if let Some(frame) = &mut self.current_frame {
103 if let Some(start) = frame.update_start {
104 frame.update_duration = Some(start.elapsed());
105 }
106 }
107 }
108
109 pub fn start_view(&mut self) {
111 if let Some(frame) = &mut self.current_frame {
112 frame.view_start = Some(Instant::now());
113 }
114 }
115
116 pub fn end_view(&mut self) {
118 if let Some(frame) = &mut self.current_frame {
119 if let Some(start) = frame.view_start {
120 frame.view_duration = Some(start.elapsed());
121 }
122 }
123 }
124
125 pub fn record_event(&mut self) {
127 self.total_events += 1;
128 if let Some(frame) = &mut self.current_frame {
129 frame.events_processed += 1;
130 }
131 }
132
133 pub fn record_command(&mut self) {
135 self.total_commands += 1;
136 if let Some(frame) = &mut self.current_frame {
137 frame.commands_executed += 1;
138 }
139 }
140
141 pub fn end_frame(&mut self) {
143 if let Some(builder) = self.current_frame.take() {
144 let metrics = FrameMetrics {
145 update_duration: builder.update_duration.unwrap_or_default(),
146 view_duration: builder.view_duration.unwrap_or_default(),
147 frame_duration: builder.frame_start.elapsed(),
148 events_processed: builder.events_processed,
149 commands_executed: builder.commands_executed,
150 timestamp: builder.frame_start,
151 };
152 self.record_frame(metrics);
153 }
154 }
155
156 pub fn record_frame(&mut self, metrics: FrameMetrics) {
158 self.total_events += metrics.events_processed;
160 self.total_commands += metrics.commands_executed;
161
162 if self.frames.len() >= self.max_frames {
164 self.frames.pop_front();
165 }
166 self.frames.push_back(metrics);
167 }
168
169 pub fn average_fps(&self) -> f32 {
171 if self.frames.is_empty() {
172 return 0.0;
173 }
174
175 let total_duration: Duration = self.frames.iter().map(|f| f.frame_duration).sum();
176
177 if total_duration.as_secs_f32() > 0.0 {
178 self.frames.len() as f32 / total_duration.as_secs_f32()
179 } else {
180 0.0
181 }
182 }
183
184 pub fn current_fps(&self) -> f32 {
186 self.frames.back().map_or(0.0, FrameMetrics::fps)
187 }
188
189 pub fn average_frame_time(&self) -> Duration {
191 if self.frames.is_empty() {
192 return Duration::ZERO;
193 }
194
195 let total: Duration = self.frames.iter().map(|f| f.frame_duration).sum();
196
197 total / self.frames.len() as u32
198 }
199
200 pub fn summary(&self) -> MetricsSummary {
202 MetricsSummary {
203 average_fps: self.average_fps(),
204 current_fps: self.current_fps(),
205 average_frame_time: self.average_frame_time(),
206 total_events: self.total_events,
207 total_commands: self.total_commands,
208 uptime: self.start_time.elapsed(),
209 frame_count: self.frames.len(),
210 }
211 }
212
213 pub fn clear(&mut self) {
215 self.frames.clear();
216 self.total_events = 0;
217 self.total_commands = 0;
218 self.start_time = Instant::now();
219 }
220}
221
222impl Default for PerformanceMetrics {
223 fn default() -> Self {
224 Self::new()
225 }
226}
227
228#[derive(Debug, Clone)]
230pub struct MetricsSummary {
231 pub average_fps: f32,
233 pub current_fps: f32,
235 pub average_frame_time: Duration,
237 pub total_events: usize,
239 pub total_commands: usize,
241 pub uptime: Duration,
243 pub frame_count: usize,
245}
246
247impl std::fmt::Display for MetricsSummary {
248 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249 write!(
250 f,
251 "FPS: {:.1} (avg: {:.1}) | Frame: {:?} | Events: {} | Commands: {} | Uptime: {:?}",
252 self.current_fps,
253 self.average_fps,
254 self.average_frame_time,
255 self.total_events,
256 self.total_commands,
257 self.uptime
258 )
259 }
260}