concinnity_core/render/
frame_dirty.rs1#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
16pub struct FrameDirty {
17 all: u32,
18 pending: u32,
19}
20
21pub const MAX_TRACKED_FRAMES: usize = 32;
23
24impl FrameDirty {
25 pub const fn new(frames: usize) -> Self {
28 let all = if frames >= MAX_TRACKED_FRAMES {
29 u32::MAX
30 } else {
31 (1u32 << frames) - 1
32 };
33 Self { all, pending: all }
34 }
35
36 pub fn mark_all(&mut self) {
38 self.pending = self.all;
39 }
40
41 pub fn take(&mut self, frame: usize) -> bool {
43 if frame >= MAX_TRACKED_FRAMES {
44 return true;
45 }
46 let bit = 1u32 << frame;
47 let pending = self.pending & bit != 0;
48 self.pending &= !bit;
49 pending
50 }
51
52 pub const fn any_pending(&self) -> bool {
54 self.pending != 0
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn every_slot_is_pending_until_it_is_taken_once() {
64 let mut dirty = FrameDirty::new(3);
65 assert!(dirty.any_pending());
66 for frame in 0..3 {
67 assert!(dirty.take(frame), "slot {frame} seeds pending");
68 }
69 assert!(!dirty.any_pending());
70 for frame in 0..3 {
71 assert!(!dirty.take(frame), "slot {frame} stays clear");
72 }
73 }
74
75 #[test]
76 fn a_change_re_arms_every_slot() {
77 let mut dirty = FrameDirty::new(2);
78 dirty.take(0);
79 dirty.take(1);
80 dirty.mark_all();
81 assert!(dirty.take(1));
82 assert!(dirty.take(0));
83 assert!(!dirty.any_pending());
84 }
85
86 #[test]
87 fn taking_one_slot_leaves_the_others_alone() {
88 let mut dirty = FrameDirty::new(3);
89 assert!(dirty.take(1));
90 assert!(dirty.any_pending());
91 assert!(dirty.take(0));
92 assert!(dirty.take(2));
93 assert!(!dirty.any_pending());
94 }
95
96 #[test]
97 fn an_untracked_slot_always_reports_pending() {
98 let mut dirty = FrameDirty::new(2);
99 assert!(dirty.take(MAX_TRACKED_FRAMES));
100 assert!(dirty.take(MAX_TRACKED_FRAMES));
101 }
102
103 #[test]
104 fn a_ring_wider_than_the_mask_tracks_every_bit_it_has() {
105 let dirty = FrameDirty::new(MAX_TRACKED_FRAMES);
106 assert_eq!(dirty, FrameDirty::new(MAX_TRACKED_FRAMES + 4));
107 assert!(dirty.any_pending());
108 }
109}