veridian_kernel/graphics/
vsync_sw.rs1#![allow(dead_code)]
10
11use alloc::vec::Vec;
12#[cfg(not(target_arch = "x86_64"))]
13use core::sync::atomic::{AtomicU64, Ordering};
14
15use spin::Mutex;
16
17pub(crate) const VSYNC_INTERVAL_NS: u64 = 16_666_667;
24
25const VSYNC_INTERVAL_US: u64 = 16_667;
27
28const FRAME_HISTORY_SIZE: usize = 60;
30
31const DROPPED_FRAME_THRESHOLD_NS: u64 = VSYNC_INTERVAL_NS * 2;
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub(crate) enum SwapState {
41 Idle,
43 Rendering,
45 WaitingFlip,
47 Flipped,
49}
50
51#[derive(Debug, Clone)]
57pub(crate) struct FrameStats {
58 pub frame_count: u64,
60 pub dropped_frames: u64,
62 frame_times: Vec<u64>,
64 write_idx: usize,
66 pub min_frame_time_ns: u64,
68 pub max_frame_time_ns: u64,
70}
71
72impl FrameStats {
73 fn new() -> Self {
74 Self {
75 frame_count: 0,
76 dropped_frames: 0,
77 frame_times: Vec::new(),
78 write_idx: 0,
79 min_frame_time_ns: u64::MAX,
80 max_frame_time_ns: 0,
81 }
82 }
83
84 fn record(&mut self, frame_time_ns: u64) {
86 self.frame_count += 1;
87
88 if frame_time_ns > DROPPED_FRAME_THRESHOLD_NS {
89 self.dropped_frames += 1;
90 }
91
92 if frame_time_ns < self.min_frame_time_ns {
93 self.min_frame_time_ns = frame_time_ns;
94 }
95 if frame_time_ns > self.max_frame_time_ns {
96 self.max_frame_time_ns = frame_time_ns;
97 }
98
99 if self.frame_times.len() < FRAME_HISTORY_SIZE {
101 self.frame_times.push(frame_time_ns);
102 } else {
103 self.frame_times[self.write_idx] = frame_time_ns;
104 }
105 self.write_idx = (self.write_idx + 1) % FRAME_HISTORY_SIZE;
106 }
107
108 pub(crate) fn average_frame_time_ns(&self) -> u64 {
110 if self.frame_times.is_empty() {
111 return 0;
112 }
113 let sum: u64 = self.frame_times.iter().sum();
114 sum / self.frame_times.len() as u64
115 }
116
117 pub(crate) fn estimated_fps(&self) -> u32 {
119 let avg = self.average_frame_time_ns();
120 if avg == 0 {
121 return 0;
122 }
123 (1_000_000_000u64 / avg) as u32
125 }
126
127 pub(crate) fn drop_rate_permille(&self) -> u32 {
129 if self.frame_count == 0 {
130 return 0;
131 }
132 ((self.dropped_frames * 1000) / self.frame_count) as u32
133 }
134}
135
136pub(crate) struct SwVsyncState {
142 last_vsync_ns: u64,
144 interval_ns: u64,
146 swap_state: SwapState,
148 stats: FrameStats,
150 tsc_per_ns_fp32: u64,
154 enabled: bool,
156 frame_start_ns: u64,
158}
159
160impl SwVsyncState {
161 pub(crate) fn new() -> Self {
163 Self {
164 last_vsync_ns: 0,
165 interval_ns: VSYNC_INTERVAL_NS,
166 swap_state: SwapState::Idle,
167 stats: FrameStats::new(),
168 tsc_per_ns_fp32: 0,
169 enabled: true,
170 frame_start_ns: 0,
171 }
172 }
173
174 pub(crate) fn with_interval_ns(interval_ns: u64) -> Self {
176 let mut s = Self::new();
177 s.interval_ns = interval_ns;
178 s
179 }
180
181 pub(crate) fn set_tsc_calibration(&mut self, tsc_per_ns_fp32: u64) {
183 self.tsc_per_ns_fp32 = tsc_per_ns_fp32;
184 }
185
186 fn tsc_to_ns(&self, tsc: u64) -> u64 {
188 if self.tsc_per_ns_fp32 == 0 {
189 tsc / 3
192 } else {
193 let numerator = (tsc as u128) << 32;
197 (numerator / self.tsc_per_ns_fp32 as u128) as u64
198 }
199 }
200
201 #[inline]
203 fn read_tsc(&self) -> u64 {
204 #[cfg(target_arch = "x86_64")]
205 {
206 unsafe { core::arch::x86_64::_rdtsc() }
208 }
209 #[cfg(not(target_arch = "x86_64"))]
210 {
211 static COUNTER: AtomicU64 = AtomicU64::new(0);
213 COUNTER.fetch_add(1, Ordering::Relaxed)
214 }
215 }
216
217 fn now_ns(&self) -> u64 {
219 self.tsc_to_ns(self.read_tsc())
220 }
221
222 pub(crate) fn set_enabled(&mut self, enabled: bool) {
226 self.enabled = enabled;
227 }
228
229 pub(crate) fn is_enabled(&self) -> bool {
231 self.enabled
232 }
233
234 pub(crate) fn set_refresh_mhz(&mut self, mhz: u32) {
236 if mhz > 0 {
237 self.interval_ns = 1_000_000_000_000u64 / mhz as u64;
239 }
240 }
241
242 pub(crate) fn wait(&mut self) {
248 if !self.enabled {
249 return;
250 }
251
252 let now = self.now_ns();
253
254 if self.last_vsync_ns == 0 {
255 self.last_vsync_ns = now;
257 return;
258 }
259
260 let mut next_vsync = self.last_vsync_ns.saturating_add(self.interval_ns);
262
263 if now > next_vsync {
265 let elapsed = now - self.last_vsync_ns;
266 let skipped = elapsed / self.interval_ns;
267 next_vsync = self
268 .last_vsync_ns
269 .saturating_add(self.interval_ns.saturating_mul(skipped + 1));
270 }
271
272 while self.now_ns() < next_vsync {
274 core::hint::spin_loop();
275 }
276
277 self.last_vsync_ns = next_vsync;
278 }
279
280 pub(crate) fn begin_frame(&mut self) {
282 self.frame_start_ns = self.now_ns();
283 self.swap_state = SwapState::Rendering;
284 }
285
286 pub(crate) fn end_render(&mut self) {
288 self.swap_state = SwapState::WaitingFlip;
289 }
290
291 pub(crate) fn signal_complete(&mut self) {
293 let now = self.now_ns();
294 if self.frame_start_ns > 0 {
295 let frame_time = now.saturating_sub(self.frame_start_ns);
296 self.stats.record(frame_time);
297 }
298 self.swap_state = SwapState::Flipped;
299 }
300
301 pub(crate) fn request_swap(&mut self) -> bool {
305 match self.swap_state {
306 SwapState::WaitingFlip => {
307 if self.enabled {
308 self.wait();
309 }
310 self.swap_state = SwapState::Flipped;
311 true
312 }
313 SwapState::Idle => {
314 self.swap_state = SwapState::Flipped;
316 true
317 }
318 _ => false,
319 }
320 }
321
322 pub(crate) fn swap_complete(&mut self) {
324 self.signal_complete();
325 self.swap_state = SwapState::Idle;
326 }
327
328 pub(crate) fn swap_state(&self) -> SwapState {
332 self.swap_state
333 }
334
335 pub(crate) fn stats(&self) -> &FrameStats {
337 &self.stats
338 }
339
340 pub(crate) fn interval_ns(&self) -> u64 {
342 self.interval_ns
343 }
344}
345
346static SW_VSYNC: Mutex<Option<SwVsyncState>> = Mutex::new(None);
351
352pub(crate) fn sw_vsync_init() {
354 let mut guard = SW_VSYNC.lock();
355 if guard.is_none() {
356 *guard = Some(SwVsyncState::new());
357 }
358}
359
360pub(crate) fn sw_vsync_init_with_refresh(refresh_mhz: u32) {
362 let mut guard = SW_VSYNC.lock();
363 let mut state = SwVsyncState::new();
364 state.set_refresh_mhz(refresh_mhz);
365 *guard = Some(state);
366}
367
368pub(crate) fn sw_vsync_wait() {
370 if let Some(ref mut state) = *SW_VSYNC.lock() {
371 state.wait();
372 }
373}
374
375pub(crate) fn sw_vsync_signal() {
377 if let Some(ref mut state) = *SW_VSYNC.lock() {
378 state.signal_complete();
379 }
380}
381
382pub(crate) fn with_sw_vsync<R, F: FnOnce(&mut SwVsyncState) -> R>(f: F) -> Option<R> {
384 SW_VSYNC.lock().as_mut().map(f)
385}
386
387#[cfg(test)]
392mod tests {
393 use super::*;
394
395 #[test]
396 fn test_vsync_interval_ns() {
397 assert_eq!(VSYNC_INTERVAL_NS, 16_666_667);
399 }
400
401 #[test]
402 fn test_swap_state_transitions() {
403 let mut state = SwVsyncState::new();
404 state.set_enabled(false); assert_eq!(state.swap_state(), SwapState::Idle);
407
408 state.begin_frame();
409 assert_eq!(state.swap_state(), SwapState::Rendering);
410
411 state.end_render();
412 assert_eq!(state.swap_state(), SwapState::WaitingFlip);
413
414 assert!(state.request_swap());
415 assert_eq!(state.swap_state(), SwapState::Flipped);
416
417 state.swap_complete();
418 assert_eq!(state.swap_state(), SwapState::Idle);
419 }
420
421 #[test]
422 fn test_swap_request_from_idle() {
423 let mut state = SwVsyncState::new();
424 state.set_enabled(false);
425 assert!(state.request_swap());
426 }
427
428 #[test]
429 fn test_swap_request_during_render() {
430 let mut state = SwVsyncState::new();
431 state.begin_frame();
432 assert!(!state.request_swap());
434 }
435
436 #[test]
437 fn test_frame_stats_new() {
438 let stats = FrameStats::new();
439 assert_eq!(stats.frame_count, 0);
440 assert_eq!(stats.dropped_frames, 0);
441 assert_eq!(stats.average_frame_time_ns(), 0);
442 assert_eq!(stats.estimated_fps(), 0);
443 }
444
445 #[test]
446 fn test_frame_stats_record() {
447 let mut stats = FrameStats::new();
448 stats.record(16_666_667); assert_eq!(stats.frame_count, 1);
450 assert_eq!(stats.dropped_frames, 0);
451 assert_eq!(stats.min_frame_time_ns, 16_666_667);
452 assert_eq!(stats.max_frame_time_ns, 16_666_667);
453 }
454
455 #[test]
456 fn test_frame_stats_dropped() {
457 let mut stats = FrameStats::new();
458 stats.record(40_000_000);
460 assert_eq!(stats.dropped_frames, 1);
461 }
462
463 #[test]
464 fn test_frame_stats_average() {
465 let mut stats = FrameStats::new();
466 stats.record(10_000_000);
467 stats.record(20_000_000);
468 assert_eq!(stats.average_frame_time_ns(), 15_000_000);
469 }
470
471 #[test]
472 fn test_frame_stats_fps() {
473 let mut stats = FrameStats::new();
474 for _ in 0..60 {
476 stats.record(16_666_667);
477 }
478 let fps = stats.estimated_fps();
479 assert!(fps >= 59 && fps <= 60, "fps was {}", fps);
481 }
482
483 #[test]
484 fn test_frame_stats_drop_rate() {
485 let mut stats = FrameStats::new();
486 for _ in 0..9 {
487 stats.record(16_000_000); }
489 stats.record(40_000_000); assert_eq!(stats.drop_rate_permille(), 100);
492 }
493
494 #[test]
495 fn test_set_refresh_60hz() {
496 let mut state = SwVsyncState::new();
497 state.set_refresh_mhz(60_000);
498 assert_eq!(state.interval_ns(), 16_666_666);
500 }
501
502 #[test]
503 fn test_set_refresh_144hz() {
504 let mut state = SwVsyncState::new();
505 state.set_refresh_mhz(144_000);
506 assert_eq!(state.interval_ns(), 6_944_444);
508 }
509
510 #[test]
511 fn test_custom_interval() {
512 let state = SwVsyncState::with_interval_ns(8_333_333); assert_eq!(state.interval_ns(), 8_333_333);
514 }
515
516 #[test]
517 fn test_enable_disable() {
518 let mut state = SwVsyncState::new();
519 assert!(state.is_enabled());
520 state.set_enabled(false);
521 assert!(!state.is_enabled());
522 }
523
524 #[test]
525 fn test_frame_stats_circular_buffer() {
526 let mut stats = FrameStats::new();
527 for i in 0..FRAME_HISTORY_SIZE + 10 {
529 stats.record((i as u64 + 1) * 1_000_000);
530 }
531 assert_eq!(stats.frame_times.len(), FRAME_HISTORY_SIZE);
532 assert_eq!(stats.frame_count, (FRAME_HISTORY_SIZE + 10) as u64);
533 }
534}