1#[cfg(not(test))]
2use crate::drm_render_target::{FramebufferTarget, RenderTarget};
3use crate::framebuffer::DmaReadyFramebuffer;
4use log::{debug, error, info, trace};
5use std::sync::Arc;
6#[cfg(not(feature = "tokio-threads"))]
7use std::sync::Mutex;
8#[cfg(feature = "tokio-threads")]
9use tokio::sync::Mutex;
10
11pub struct DoubleBuffer {
12 #[cfg(not(feature = "tokio-threads"))]
13 sender: Option<std::sync::mpsc::Sender<usize>>,
14 #[cfg(feature = "tokio-threads")]
15 sender: Option<tokio::sync::mpsc::Sender<usize>>,
16 toggle: bool,
17 size: usize,
18 fbuf0: DmaReadyFramebuffer,
19 fbuf1: DmaReadyFramebuffer,
20 mutex: Arc<Mutex<bool>>,
21}
22
23impl DoubleBuffer {
24 pub fn new(width: usize, height: usize) -> Self {
25 trace!("Creating new DoubleBuffer with raw framebuffers");
26
27 let fbuf0 = DmaReadyFramebuffer::new(width, height, true);
28 let fbuf1 = DmaReadyFramebuffer::new(width, height, true);
29
30 Self {
31 sender: None,
32 toggle: false,
33 size: width * height,
34 fbuf0,
35 fbuf1,
36 mutex: Arc::new(Mutex::new(true)),
37 }
38 }
39
40 pub fn start_thread(&mut self, #[cfg(not(test))] mut display: RenderTarget) {
41 debug!("Creating RenderTarget from card");
42
43 info!("Starting fb writer thread");
44 #[cfg(not(feature = "tokio-threads"))]
45 let (send, receive) = std::sync::mpsc::channel();
46 #[cfg(feature = "tokio-threads")]
47 let (send, mut receive) = tokio::sync::mpsc::channel(16);
48
49 self.sender = Some(send);
50 let mutex2 = self.mutex.clone();
51 let size = self.size;
52
53 #[cfg(not(feature = "tokio-threads"))]
54 std::thread::spawn(move || {
55 trace!("Framebuffer writer thread started for std runtime");
56 loop {
57 match receive.recv() {
58 Ok(ptr) => {
59 trace!("Received framebuffer pointer: {ptr}");
60 unsafe {
61 let _lock = mutex2.lock().unwrap();
62
63 let ptr = ptr as *mut u32;
64 let slice = std::slice::from_raw_parts_mut(ptr, size);
65
66 #[cfg(not(test))]
67 display.eat_framebuffer(slice).unwrap();
68 slice.fill(0); };
70 }
71 _ => {
72 debug!("Leaving framebuffer loop");
73 break;
74 }
75 }
76 }
77 });
78
79 #[cfg(feature = "tokio-threads")]
80 tokio::spawn(async move {
81 trace!("Framebuffer writer thread started for tokio runtime");
82 loop {
83 match receive.recv().await {
84 Some(ptr) => {
85 trace!("Received framebuffer pointer: {}", ptr);
86 unsafe {
87 let _lock = mutex2.lock().await;
88
89 let ptr = ptr as *mut u32;
90 let slice = std::slice::from_raw_parts_mut(ptr, size);
91
92 #[cfg(not(test))]
93 display.eat_framebuffer(slice).unwrap();
94 slice.fill(0); };
96 }
97 _ => {
98 debug!("Leaving framebuffer loop");
99 break;
100 }
101 }
102 }
103 });
104 }
105
106 pub fn swap_framebuffer(&mut self) -> &mut DmaReadyFramebuffer {
107 trace!("Swapping framebuffer from {}", self.toggle);
108 self.toggle = !self.toggle;
109
110 if self.toggle {
111 &mut self.fbuf0
112 } else {
113 &mut self.fbuf1
114 }
115 }
116
117 pub fn get_current_framebuffer(&mut self) -> &mut DmaReadyFramebuffer {
118 if self.toggle {
119 &mut self.fbuf0
120 } else {
121 &mut self.fbuf1
122 }
123 }
124
125 #[cfg(not(feature = "tokio-threads"))]
126 pub fn send_framebuffer(&mut self) {
127 {
128 let _lock = self.mutex.lock().unwrap();
129 std::mem::drop(_lock);
130 }
131
132 let fbuf = if self.toggle {
133 trace!(
134 "sending framebuffer 0 ({:?})",
135 self.fbuf0.framebuffer.as_ptr()
136 );
137 &mut self.fbuf0
138 } else {
139 trace!(
140 "sending framebuffer 1 ({:?})",
141 self.fbuf1.framebuffer.as_ptr()
142 );
143 &mut self.fbuf1
144 };
145
146 if let Some(sender) = &self.sender {
147 sender
148 .send(fbuf.framebuffer.as_ptr() as usize)
149 .inspect_err(|msg| {
150 error!("Failed to send framebuffer: {msg}");
151 })
152 .unwrap();
153 }
154 }
155
156 #[cfg(feature = "tokio-threads")]
157 pub async fn send_framebuffer(&mut self) {
158 trace!("Sending framebuffer in async context");
159 {
160 let _lock = self.mutex.lock().await;
161 std::mem::drop(_lock);
162 }
163
164 let fbuf = if self.toggle {
165 trace!(
166 "sending framebuffer 0 ({:?})",
167 self.fbuf0.framebuffer.as_ptr()
168 );
169 &mut self.fbuf0
170 } else {
171 trace!(
172 "sending framebuffer 1 ({:?})",
173 self.fbuf1.framebuffer.as_ptr()
174 );
175 &mut self.fbuf1
176 };
177
178 if let Some(sender) = &self.sender {
179 sender
180 .send(fbuf.framebuffer.as_ptr() as usize)
181 .await
182 .inspect_err(|msg| {
183 error!("Failed to send framebuffer: {}", msg);
184 })
185 .unwrap();
186 }
187 }
188}
189
190unsafe impl Send for DoubleBuffer {}
196
197#[cfg(test)]
198mod tests {
199 use embedded_graphics::prelude::RgbColor;
200
201 use super::*;
202 #[test]
207 fn test_doublebuffer_creation() {
208 const WIDTH: usize = 64;
209 const HEIGHT: usize = 64;
210
211 let db = DoubleBuffer::new(WIDTH, HEIGHT);
213
214 assert!(!db.toggle); assert!(db.sender.is_none()); }
218
219 #[test]
220 fn test_framebuffer_swapping() {
221 const WIDTH: usize = 64;
222 const HEIGHT: usize = 64;
223
224 let mut db = DoubleBuffer::new(WIDTH, HEIGHT);
226
227 let fb1 = db.swap_framebuffer();
229 use embedded_graphics_core::geometry::Point;
233 use embedded_graphics_core::pixelcolor::Bgr888;
234
235 fb1.set_pixel(Point::new(0, 0), Bgr888::new(255, 255, 255));
236
237 let fb2 = db.swap_framebuffer();
239
240 assert_eq!(fb2.get_pixel(Point { x: 0, y: 0 }), Some(Bgr888::BLACK)); let fb3 = db.swap_framebuffer();
245
246 assert_eq!(fb3.get_pixel(Point { x: 0, y: 0 }), Some(Bgr888::WHITE)); }
249
250 #[test]
258 fn test_toggle_behavior() {
259 const WIDTH: usize = 64;
260 const HEIGHT: usize = 64;
261
262 let mut db = DoubleBuffer::new(WIDTH, HEIGHT);
264
265 let initial_toggle = db.toggle;
267 assert!(!initial_toggle);
268
269 let _first_fb = db.swap_framebuffer();
271
272 let _second_fb = db.swap_framebuffer();
274
275 let _third_fb = db.swap_framebuffer();
277
278 assert!(db.toggle);
280 }
281}