ursus_core/render/
triple_buffer.rs1use std::cell::UnsafeCell;
2use std::sync::atomic::{AtomicU8, Ordering};
3
4pub struct TripleBuffer<T: Send> {
5 slots: [UnsafeCell<T>; 3],
6 state: AtomicU8,
7}
8
9unsafe impl<T: Send> Send for TripleBuffer<T> {}
10unsafe impl<T: Send> Sync for TripleBuffer<T> {}
11
12const NEW_FRAME_BIT: u8 = 0b1000_0000;
13const READY_SHIFT: u8 = 4;
14const WRITE_SHIFT: u8 = 2;
15const IDX_MASK: u8 = 0b11;
16
17const INITIAL_STATE: u8 = (1 << READY_SHIFT) | (0 << WRITE_SHIFT);
18
19impl<T: Send + Default> TripleBuffer<T> {
20 pub fn new() -> Self {
21 Self {
22 slots: [
23 UnsafeCell::new(T::default()),
24 UnsafeCell::new(T::default()),
25 UnsafeCell::new(T::default()),
26 ],
27 state: AtomicU8::new(INITIAL_STATE),
28 }
29 }
30}
31
32impl<T: Send> TripleBuffer<T> {
33 pub fn new_with(a: T, b: T, c: T) -> Self {
34 Self {
35 slots: [UnsafeCell::new(a), UnsafeCell::new(b), UnsafeCell::new(c)],
36 state: AtomicU8::new(INITIAL_STATE),
37 }
38 }
39
40 #[allow(clippy::mut_from_ref)]
44 pub fn write_slot(&self) -> &mut T {
45 let state = self.state.load(Ordering::Relaxed);
46 let idx = ((state >> WRITE_SHIFT) & IDX_MASK) as usize;
47 unsafe { &mut *self.slots[idx].get() }
48 }
49
50 pub fn publish(&self) {
51 let mut state = self.state.load(Ordering::Relaxed);
52 loop {
53 let write = (state >> WRITE_SHIFT) & IDX_MASK;
54 let ready = (state >> READY_SHIFT) & IDX_MASK;
55
56 let new_state = NEW_FRAME_BIT | (write << READY_SHIFT) | (ready << WRITE_SHIFT);
57
58 match self.state.compare_exchange_weak(state, new_state, Ordering::Release, Ordering::Relaxed) {
59 Ok(_) => return,
60 Err(s) => state = s,
61 }
62 }
63 }
64
65 pub fn consume(&self, render_idx: &mut usize) -> bool {
66 let mut state = self.state.load(Ordering::Acquire);
67
68 if state & NEW_FRAME_BIT == 0 {
69 return false;
70 }
71
72 loop {
73 let ready = ((state >> READY_SHIFT) & IDX_MASK) as usize;
74 let render = *render_idx;
75
76 let new_state = (state & !NEW_FRAME_BIT) & !(IDX_MASK << READY_SHIFT) | ((render as u8) << READY_SHIFT);
77
78 match self.state.compare_exchange_weak(state, new_state, Ordering::AcqRel, Ordering::Acquire) {
79 Ok(_) => {
80 *render_idx = ready;
81 return true;
82 }
83 Err(s) => {
84 state = s;
85 if state & NEW_FRAME_BIT == 0 {
86 return false;
87 }
88 }
89 }
90 }
91 }
92
93 pub fn render_slot(&self, render_idx: usize) -> &T {
94 debug_assert!(render_idx < 3, "render_idx вне диапазона");
95 unsafe { &*self.slots[render_idx].get() }
96 }
97}
98
99impl<T: Send + Default> Default for TripleBuffer<T> {
100 fn default() -> Self {
101 Self::new()
102 }
103}