1use embedded_graphics_core::pixelcolor::Rgb565;
19use embedded_graphics_framebuf::{FrameBuf, backends::DMACapableFrameBufferBackend};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct DisplayRegion {
24 pub x: usize,
25 pub y: usize,
26 pub width: usize,
27 pub height: usize,
28}
29
30impl DisplayRegion {
31 pub const fn new(x: usize, y: usize, width: usize, height: usize) -> Self {
32 Self {
33 x,
34 y,
35 width,
36 height,
37 }
38 }
39}
40
41pub trait HardwareAccelerator {
43 fn fill_rect(&mut self, x: u16, y: u16, w: u16, h: u16, color: Rgb565) -> bool;
45
46 fn blit(
48 &mut self,
49 src: &[Rgb565],
50 src_stride: usize,
51 dst_x: u16,
52 dst_y: u16,
53 w: u16,
54 h: u16,
55 ) -> bool;
56}
57
58#[derive(Debug, Clone, Copy, Default)]
60pub struct CpuAccelerator;
61
62impl HardwareAccelerator for CpuAccelerator {
63 #[inline(always)]
64 fn fill_rect(&mut self, _x: u16, _y: u16, _w: u16, _h: u16, _color: Rgb565) -> bool {
65 false }
67
68 #[inline(always)]
69 fn blit(
70 &mut self,
71 _src: &[Rgb565],
72 _src_stride: usize,
73 _dst_x: u16,
74 _dst_y: u16,
75 _w: u16,
76 _h: u16,
77 ) -> bool {
78 false }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub enum DisplayError {
85 Busy,
87 HardwareError,
89 InvalidBuffer,
91}
92
93pub struct TransferError<FB>
98where
99 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
100{
101 pub framebuffer: FrameBuf<Rgb565, FB>,
103 pub error: DisplayError,
105}
106
107impl<FB> core::fmt::Debug for TransferError<FB>
108where
109 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
110{
111 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112 f.debug_struct("TransferError")
113 .field("error", &self.error)
114 .finish_non_exhaustive()
115 }
116}
117
118pub trait DmaTransfer {
127 type Buffer;
129
130 fn is_done(&self) -> bool;
132
133 fn wait(self) -> Self::Buffer;
138}
139
140pub trait AsyncDmaTransfer: DmaTransfer {
145 type WaitFuture: core::future::Future<Output = Self::Buffer>;
147
148 fn wait_async(self) -> Self::WaitFuture;
150}
151
152pub trait DisplayBackend<const W: usize, const H: usize, FB>
173where
174 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
175{
176 type Transfer: DmaTransfer<Buffer = FrameBuf<Rgb565, FB>>;
178
179 fn start_dma_transfer(
187 &mut self,
188 framebuffer: FrameBuf<Rgb565, FB>,
189 ) -> Result<Self::Transfer, TransferError<FB>>;
190
191 fn start_dma_transfer_region(
196 &mut self,
197 framebuffer: FrameBuf<Rgb565, FB>,
198 _region: DisplayRegion,
199 ) -> Result<Self::Transfer, TransferError<FB>> {
200 self.start_dma_transfer(framebuffer)
201 }
202
203 #[cfg(feature = "dma2d")]
207 fn hardware_clear_depth(&mut self, _zbuffer: &mut [crate::ZDepth]) -> bool {
208 false
209 }
210}
211
212pub struct CompletedTransfer<FB>
219where
220 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
221{
222 framebuffer: Option<FrameBuf<Rgb565, FB>>,
223}
224
225impl<FB> DmaTransfer for CompletedTransfer<FB>
226where
227 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
228{
229 type Buffer = FrameBuf<Rgb565, FB>;
230
231 fn is_done(&self) -> bool {
232 true
233 }
234
235 fn wait(mut self) -> FrameBuf<Rgb565, FB> {
236 self.framebuffer
237 .take()
238 .expect("CompletedTransfer polled after completion")
239 }
240}
241
242impl<FB> core::future::Future for CompletedTransfer<FB>
243where
244 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
245{
246 type Output = FrameBuf<Rgb565, FB>;
247
248 fn poll(
249 self: core::pin::Pin<&mut Self>,
250 _cx: &mut core::task::Context<'_>,
251 ) -> core::task::Poll<Self::Output> {
252 let buf = unsafe { self.get_unchecked_mut() }
253 .framebuffer
254 .take()
255 .expect("CompletedTransfer polled after completion");
256 core::task::Poll::Ready(buf)
257 }
258}
259
260impl<FB> AsyncDmaTransfer for CompletedTransfer<FB>
261where
262 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
263{
264 type WaitFuture = Self;
265
266 fn wait_async(self) -> Self::WaitFuture {
267 self
268 }
269}
270
271pub struct SimulatorBackend;
279
280impl SimulatorBackend {
281 pub fn new() -> Self {
282 Self
283 }
284}
285
286impl Default for SimulatorBackend {
287 fn default() -> Self {
288 Self::new()
289 }
290}
291
292impl<const W: usize, const H: usize, FB> DisplayBackend<W, H, FB> for SimulatorBackend
293where
294 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
295{
296 type Transfer = CompletedTransfer<FB>;
297
298 fn start_dma_transfer(
299 &mut self,
300 framebuffer: FrameBuf<Rgb565, FB>,
301 ) -> Result<CompletedTransfer<FB>, TransferError<FB>> {
302 Ok(CompletedTransfer {
303 framebuffer: Some(framebuffer),
304 })
305 }
306}
307
308#[cfg(test)]
311mod tests {
312 extern crate std;
313 use super::*;
314 use core::cell::Cell;
315 use embedded_graphics_core::pixelcolor::RgbColor;
316 use embedded_graphics_framebuf::backends::EndianCorrectedBuffer;
317 use std::vec;
318
319 type TestBackend = EndianCorrectedBuffer<'static, Rgb565>;
320
321 fn make_fb<const W: usize, const H: usize>() -> FrameBuf<Rgb565, TestBackend> {
322 use embedded_graphics_framebuf::backends::EndianCorrection;
323 let data: &'static mut [Rgb565] = vec![Rgb565::BLACK; W * H].leak();
324 FrameBuf::new(
325 EndianCorrectedBuffer::new(data, EndianCorrection::ToLittleEndian),
326 W,
327 H,
328 )
329 }
330
331 struct RegionTransfer<FB: DMACapableFrameBufferBackend<Color = Rgb565>> {
334 framebuffer: Option<FrameBuf<Rgb565, FB>>,
335 }
336
337 impl<FB: DMACapableFrameBufferBackend<Color = Rgb565>> DmaTransfer for RegionTransfer<FB> {
338 type Buffer = FrameBuf<Rgb565, FB>;
339 fn is_done(&self) -> bool {
340 true
341 }
342 fn wait(mut self) -> FrameBuf<Rgb565, FB> {
343 self.framebuffer.take().unwrap()
344 }
345 }
346
347 impl<FB: DMACapableFrameBufferBackend<Color = Rgb565>> core::future::Future for RegionTransfer<FB> {
348 type Output = FrameBuf<Rgb565, FB>;
349 fn poll(
350 self: core::pin::Pin<&mut Self>,
351 _cx: &mut core::task::Context<'_>,
352 ) -> core::task::Poll<Self::Output> {
353 core::task::Poll::Ready(
354 unsafe { self.get_unchecked_mut() }
355 .framebuffer
356 .take()
357 .unwrap(),
358 )
359 }
360 }
361
362 impl<FB: DMACapableFrameBufferBackend<Color = Rgb565>> AsyncDmaTransfer for RegionTransfer<FB> {
363 type WaitFuture = Self;
364 fn wait_async(self) -> Self::WaitFuture {
365 self
366 }
367 }
368
369 struct RegionTrackingBackend {
370 region_calls: Cell<usize>,
371 }
372
373 impl RegionTrackingBackend {
374 fn new() -> Self {
375 Self {
376 region_calls: Cell::new(0),
377 }
378 }
379 }
380
381 impl<const W: usize, const H: usize, FB> DisplayBackend<W, H, FB> for RegionTrackingBackend
382 where
383 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
384 {
385 type Transfer = RegionTransfer<FB>;
386
387 fn start_dma_transfer(
388 &mut self,
389 framebuffer: FrameBuf<Rgb565, FB>,
390 ) -> Result<RegionTransfer<FB>, TransferError<FB>> {
391 Ok(RegionTransfer {
392 framebuffer: Some(framebuffer),
393 })
394 }
395
396 fn start_dma_transfer_region(
397 &mut self,
398 framebuffer: FrameBuf<Rgb565, FB>,
399 _region: DisplayRegion,
400 ) -> Result<RegionTransfer<FB>, TransferError<FB>> {
401 self.region_calls.set(self.region_calls.get() + 1);
402 Ok(RegionTransfer {
403 framebuffer: Some(framebuffer),
404 })
405 }
406 }
407
408 #[test]
411 fn test_simulator_backend_transfer_completes_immediately() {
412 let mut backend = SimulatorBackend::new();
413 let fb = make_fb::<2, 2>();
414 let xfer = <SimulatorBackend as DisplayBackend<2, 2, TestBackend>>::start_dma_transfer(
415 &mut backend,
416 fb,
417 )
418 .unwrap();
419 assert!(xfer.is_done());
420 let _fb = xfer.wait();
421 }
422
423 #[test]
424 fn test_simulator_backend_returns_buffer_on_wait() {
425 let mut backend = SimulatorBackend::new();
426 let fb = make_fb::<4, 4>();
427 let xfer = <SimulatorBackend as DisplayBackend<4, 4, TestBackend>>::start_dma_transfer(
428 &mut backend,
429 fb,
430 )
431 .unwrap();
432 let fb_back = xfer.wait();
434 assert_eq!(fb_back.width(), 4);
435 assert_eq!(fb_back.height(), 4);
436 }
437
438 #[test]
439 fn test_region_tracking_backend_counts_region_transfers() {
440 let mut backend = RegionTrackingBackend::new();
441 let fb = make_fb::<2, 2>();
442 let region = DisplayRegion::new(0, 0, 1, 1);
443 let xfer = <RegionTrackingBackend as DisplayBackend<2, 2, TestBackend>>::start_dma_transfer_region(
444 &mut backend,
445 fb,
446 region,
447 )
448 .unwrap();
449 assert_eq!(backend.region_calls.get(), 1);
450 let _ = xfer.wait();
451 }
452
453 #[test]
454 fn test_full_transfer_does_not_increment_region_counter() {
455 let mut backend = RegionTrackingBackend::new();
456 let fb = make_fb::<2, 2>();
457 let xfer =
458 <RegionTrackingBackend as DisplayBackend<2, 2, TestBackend>>::start_dma_transfer(
459 &mut backend,
460 fb,
461 )
462 .unwrap();
463 assert_eq!(backend.region_calls.get(), 0);
464 let _ = xfer.wait();
465 }
466}