1use crate::display_backend::{DisplayBackend, DisplayError, DisplayRegion, DmaTransfer};
22use embedded_graphics_core::pixelcolor::Rgb565;
23use embedded_graphics_framebuf::{
24 FrameBuf,
25 backends::{DMACapableFrameBufferBackend, EndianCorrectedBuffer, EndianCorrection},
26};
27
28enum FrontState<FB, Xfer> {
33 Idle(FB),
34 InFlight(Xfer),
35}
36
37impl<FB, Xfer: DmaTransfer<Buffer = FB>> FrontState<FB, Xfer> {
38 fn recover(self) -> FB {
40 match self {
41 FrontState::Idle(fb) => fb,
42 FrontState::InFlight(xfer) => xfer.wait(),
43 }
44 }
45
46 fn is_ready(&self) -> bool {
48 match self {
49 FrontState::Idle(_) => true,
50 FrontState::InFlight(xfer) => xfer.is_done(),
51 }
52 }
53}
54
55impl<FB, Xfer: crate::display_backend::AsyncDmaTransfer<Buffer = FB>> FrontState<FB, Xfer> {
56 async fn recover_async(self) -> FB {
58 match self {
59 FrontState::Idle(fb) => fb,
60 FrontState::InFlight(xfer) => xfer.wait_async().await,
61 }
62 }
63}
64
65pub struct SwapChain<const W: usize, const H: usize, FB, B>
81where
82 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
83 B: DisplayBackend<W, H, FB>,
84{
85 back: FrameBuf<Rgb565, FB>,
87 front: Option<FrontState<FrameBuf<Rgb565, FB>, B::Transfer>>,
89 backend: B,
90 frame_count: u64,
91}
92
93pub type StandardSwapChain<const W: usize, const H: usize, B> =
98 SwapChain<W, H, EndianCorrectedBuffer<'static, Rgb565>, B>;
99
100impl<const W: usize, const H: usize, B> StandardSwapChain<W, H, B>
103where
104 B: DisplayBackend<W, H, EndianCorrectedBuffer<'static, Rgb565>>,
105{
106 pub fn from_static_slices(
129 front_data: &'static mut [Rgb565],
130 back_data: &'static mut [Rgb565],
131 big_endian: bool,
132 backend: B,
133 ) -> Self {
134 let mk_buf = |data: &'static mut [Rgb565]| {
135 let correction = if big_endian {
136 EndianCorrection::ToBigEndian
137 } else {
138 EndianCorrection::ToLittleEndian
139 };
140 EndianCorrectedBuffer::new(data, correction)
141 };
142
143 let front_fb = FrameBuf::new(mk_buf(front_data), W, H);
144 let back_fb = FrameBuf::new(mk_buf(back_data), W, H);
145
146 Self {
147 back: back_fb,
148 front: Some(FrontState::Idle(front_fb)),
149 backend,
150 frame_count: 0,
151 }
152 }
153}
154
155impl<const W: usize, const H: usize, FB, B> SwapChain<W, H, FB, B>
158where
159 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
160 B: DisplayBackend<W, H, FB>,
161{
162 pub fn get_back_buffer(&mut self) -> &mut FrameBuf<Rgb565, FB> {
167 &mut self.back
168 }
169
170 pub fn get_front_buffer(&self) -> Option<&FrameBuf<Rgb565, FB>> {
174 match &self.front {
175 Some(FrontState::Idle(fb)) => Some(fb),
176 _ => None,
177 }
178 }
179
180 pub fn present(&mut self) -> Result<(), DisplayError> {
189 self.present_impl(|backend, fb| backend.start_dma_transfer(fb))
190 }
191
192 pub fn try_present(&mut self) -> Result<(), DisplayError> {
197 if !self.is_ready() {
198 return Err(DisplayError::Busy);
199 }
200 self.present_impl(|backend, fb| backend.start_dma_transfer(fb))
201 }
202
203 pub fn present_region(&mut self, region: DisplayRegion) -> Result<(), DisplayError> {
208 self.present_impl(|backend, fb| backend.start_dma_transfer_region(fb, region))
209 }
210
211 pub fn try_present_region(&mut self, region: DisplayRegion) -> Result<(), DisplayError> {
215 if !self.is_ready() {
216 return Err(DisplayError::Busy);
217 }
218 self.present_impl(|backend, fb| backend.start_dma_transfer_region(fb, region))
219 }
220
221 pub fn wait_for_vsync(&mut self) {
226 if let Some(state) = self.front.take() {
227 let fb = state.recover();
228 self.front = Some(FrontState::Idle(fb));
229 }
230 }
231
232 pub async fn wait_for_vsync_async(&mut self)
234 where
235 B::Transfer: crate::display_backend::AsyncDmaTransfer<Buffer = FrameBuf<Rgb565, FB>>,
236 {
237 if let Some(state) = self.front.take() {
238 let fb = state.recover_async().await;
239 self.front = Some(FrontState::Idle(fb));
240 }
241 }
242
243 pub async fn present_async(&mut self) -> Result<(), DisplayError>
245 where
246 B::Transfer: crate::display_backend::AsyncDmaTransfer<Buffer = FrameBuf<Rgb565, FB>>,
247 {
248 let old_front = if let Some(state) = self.front.take() {
250 state.recover_async().await
251 } else {
252 panic!("SwapChain front buffer missing — double present?");
253 };
254
255 let new_front = core::mem::replace(&mut self.back, old_front);
257
258 match self.backend.start_dma_transfer(new_front) {
260 Ok(transfer) => {
261 self.front = Some(FrontState::InFlight(transfer));
262 self.frame_count += 1;
263 Ok(())
264 }
265 Err(e) => {
266 let recovered_front = core::mem::replace(&mut self.back, e.framebuffer);
267 self.front = Some(FrontState::Idle(recovered_front));
268 Err(e.error)
269 }
270 }
271 }
272
273 pub async fn present_region_async(&mut self, region: DisplayRegion) -> Result<(), DisplayError>
275 where
276 B::Transfer: crate::display_backend::AsyncDmaTransfer<Buffer = FrameBuf<Rgb565, FB>>,
277 {
278 let old_front = if let Some(state) = self.front.take() {
280 state.recover_async().await
281 } else {
282 panic!("SwapChain front buffer missing — double present?");
283 };
284
285 let new_front = core::mem::replace(&mut self.back, old_front);
287
288 match self.backend.start_dma_transfer_region(new_front, region) {
290 Ok(transfer) => {
291 self.front = Some(FrontState::InFlight(transfer));
292 self.frame_count += 1;
293 Ok(())
294 }
295 Err(e) => {
296 let recovered_front = core::mem::replace(&mut self.back, e.framebuffer);
297 self.front = Some(FrontState::Idle(recovered_front));
298 Err(e.error)
299 }
300 }
301 }
302
303 pub fn is_ready(&self) -> bool {
306 self.front.as_ref().is_none_or(|s| s.is_ready())
307 }
308
309 pub fn frame_count(&self) -> u64 {
312 self.frame_count
313 }
314
315 pub fn reset_frame_count(&mut self) {
317 self.frame_count = 0;
318 }
319
320 pub fn dimensions(&self) -> (usize, usize) {
322 (W, H)
323 }
324
325 fn present_impl<F>(&mut self, start_fn: F) -> Result<(), DisplayError>
332 where
333 F: FnOnce(
334 &mut B,
335 FrameBuf<Rgb565, FB>,
336 ) -> Result<B::Transfer, crate::display_backend::TransferError<FB>>,
337 {
338 let old_front = self
340 .front
341 .take()
342 .expect("SwapChain front buffer missing — double present?")
343 .recover();
344
345 let new_front = core::mem::replace(&mut self.back, old_front);
348
349 match start_fn(&mut self.backend, new_front) {
351 Ok(transfer) => {
352 self.front = Some(FrontState::InFlight(transfer));
353 self.frame_count += 1;
354 Ok(())
355 }
356 Err(e) => {
357 let recovered_front = core::mem::replace(&mut self.back, e.framebuffer);
360 self.front = Some(FrontState::Idle(recovered_front));
361 Err(e.error)
362 }
363 }
364 }
365}
366
367#[cfg(feature = "triple-buffering")]
379pub struct TripleSwapChain<const W: usize, const H: usize, FB, B>
380where
381 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
382 B: DisplayBackend<W, H, FB>,
383{
384 display: Option<FrontState<FrameBuf<Rgb565, FB>, B::Transfer>>,
385 ready: FrameBuf<Rgb565, FB>,
386 render: FrameBuf<Rgb565, FB>,
387 backend: B,
388 frame_count: u64,
389}
390
391#[cfg(feature = "triple-buffering")]
392pub type StandardTripleSwapChain<const W: usize, const H: usize, B> =
393 TripleSwapChain<W, H, EndianCorrectedBuffer<'static, Rgb565>, B>;
394
395#[cfg(feature = "triple-buffering")]
396impl<const W: usize, const H: usize, B> StandardTripleSwapChain<W, H, B>
397where
398 B: DisplayBackend<W, H, EndianCorrectedBuffer<'static, Rgb565>>,
399{
400 pub fn from_static_slices(
401 display_data: &'static mut [Rgb565],
402 ready_data: &'static mut [Rgb565],
403 render_data: &'static mut [Rgb565],
404 big_endian: bool,
405 backend: B,
406 ) -> Self {
407 let mk = |data: &'static mut [Rgb565]| {
408 let correction = if big_endian {
409 EndianCorrection::ToBigEndian
410 } else {
411 EndianCorrection::ToLittleEndian
412 };
413 FrameBuf::new(EndianCorrectedBuffer::new(data, correction), W, H)
414 };
415 Self {
416 display: Some(FrontState::Idle(mk(display_data))),
417 ready: mk(ready_data),
418 render: mk(render_data),
419 backend,
420 frame_count: 0,
421 }
422 }
423}
424
425#[cfg(feature = "triple-buffering")]
426impl<const W: usize, const H: usize, FB, B> TripleSwapChain<W, H, FB, B>
427where
428 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
429 B: DisplayBackend<W, H, FB>,
430{
431 pub fn get_render_buffer(&mut self) -> &mut FrameBuf<Rgb565, FB> {
433 &mut self.render
434 }
435
436 pub fn present(&mut self) -> Result<(), DisplayError> {
442 self.present_impl(|backend, fb| backend.start_dma_transfer(fb))
443 }
444
445 pub async fn present_async(&mut self) -> Result<(), DisplayError>
447 where
448 B::Transfer: crate::display_backend::AsyncDmaTransfer<Buffer = FrameBuf<Rgb565, FB>>,
449 {
450 let old_display = if let Some(state) = self.display.take() {
452 state.recover_async().await
453 } else {
454 panic!("TripleSwapChain display buffer missing");
455 };
456
457 let rendered = core::mem::replace(&mut self.render, old_display);
459
460 match self.backend.start_dma_transfer(rendered) {
462 Ok(transfer) => {
463 self.display = Some(FrontState::InFlight(transfer));
464 core::mem::swap(&mut self.ready, &mut self.render);
466 self.frame_count += 1;
467 Ok(())
468 }
469 Err(e) => {
470 let old_display = core::mem::replace(&mut self.render, e.framebuffer);
471 self.display = Some(FrontState::Idle(old_display));
472 Err(e.error)
473 }
474 }
475 }
476
477 pub fn try_present(&mut self) -> Result<(), DisplayError> {
482 let ready = self.display.as_ref().is_none_or(|s| s.is_ready());
483 if !ready {
484 return Err(DisplayError::Busy);
485 }
486 self.present_impl(|backend, fb| backend.start_dma_transfer(fb))
487 }
488
489 pub fn frame_count(&self) -> u64 {
490 self.frame_count
491 }
492
493 fn present_impl<F>(&mut self, start_fn: F) -> Result<(), DisplayError>
494 where
495 F: FnOnce(
496 &mut B,
497 FrameBuf<Rgb565, FB>,
498 ) -> Result<B::Transfer, crate::display_backend::TransferError<FB>>,
499 {
500 let old_display = self
502 .display
503 .take()
504 .expect("TripleSwapChain display buffer missing")
505 .recover();
506
507 let rendered = core::mem::replace(&mut self.render, old_display);
509
510 match start_fn(&mut self.backend, rendered) {
512 Ok(transfer) => {
513 self.display = Some(FrontState::InFlight(transfer));
514 core::mem::swap(&mut self.ready, &mut self.render);
516 self.frame_count += 1;
517 Ok(())
518 }
519 Err(e) => {
520 let old_display = core::mem::replace(&mut self.render, e.framebuffer);
522 self.display = Some(FrontState::Idle(old_display));
523 Err(e.error)
524 }
525 }
526 }
527}
528
529#[cfg(test)]
532mod tests {
533 extern crate std;
534 use super::*;
535 use crate::display_backend::{DmaTransfer, SimulatorBackend, TransferError};
536 use core::cell::Cell;
537 use embedded_graphics_core::pixelcolor::RgbColor;
538 use std::vec;
539
540 fn make_static_slice(n: usize) -> &'static mut [Rgb565] {
541 vec![Rgb565::BLACK; n].leak()
542 }
543
544 struct TrackingTransfer<FB: DMACapableFrameBufferBackend<Color = Rgb565>> {
550 framebuffer: Option<FrameBuf<Rgb565, FB>>,
551 }
552
553 impl<FB: DMACapableFrameBufferBackend<Color = Rgb565>> DmaTransfer for TrackingTransfer<FB> {
554 type Buffer = FrameBuf<Rgb565, FB>;
555 fn is_done(&self) -> bool {
556 true
557 }
558 fn wait(mut self) -> FrameBuf<Rgb565, FB> {
559 self.framebuffer.take().unwrap()
560 }
561 }
562
563 impl<FB: DMACapableFrameBufferBackend<Color = Rgb565>> core::future::Future
564 for TrackingTransfer<FB>
565 {
566 type Output = FrameBuf<Rgb565, FB>;
567 fn poll(
568 self: core::pin::Pin<&mut Self>,
569 _cx: &mut core::task::Context<'_>,
570 ) -> core::task::Poll<Self::Output> {
571 core::task::Poll::Ready(
572 unsafe { self.get_unchecked_mut() }
573 .framebuffer
574 .take()
575 .unwrap(),
576 )
577 }
578 }
579
580 impl<FB: DMACapableFrameBufferBackend<Color = Rgb565>> crate::display_backend::AsyncDmaTransfer
581 for TrackingTransfer<FB>
582 {
583 type WaitFuture = Self;
584 fn wait_async(self) -> Self::WaitFuture {
585 self
586 }
587 }
588
589 struct TrackingBackend {
590 region_present_count: Cell<usize>,
591 }
592
593 impl TrackingBackend {
594 fn new() -> Self {
595 Self {
596 region_present_count: Cell::new(0),
597 }
598 }
599 }
600
601 impl<const W: usize, const H: usize, FB> DisplayBackend<W, H, FB> for TrackingBackend
602 where
603 FB: DMACapableFrameBufferBackend<Color = Rgb565>,
604 {
605 type Transfer = TrackingTransfer<FB>;
606
607 fn start_dma_transfer(
608 &mut self,
609 framebuffer: FrameBuf<Rgb565, FB>,
610 ) -> Result<TrackingTransfer<FB>, TransferError<FB>> {
611 Ok(TrackingTransfer {
612 framebuffer: Some(framebuffer),
613 })
614 }
615
616 fn start_dma_transfer_region(
617 &mut self,
618 framebuffer: FrameBuf<Rgb565, FB>,
619 _region: DisplayRegion,
620 ) -> Result<TrackingTransfer<FB>, TransferError<FB>> {
621 self.region_present_count
622 .set(self.region_present_count.get() + 1);
623 Ok(TrackingTransfer {
624 framebuffer: Some(framebuffer),
625 })
626 }
627 }
628
629 fn make_swap_chain<B>(backend: B) -> StandardSwapChain<320, 240, B>
632 where
633 B: DisplayBackend<320, 240, EndianCorrectedBuffer<'static, Rgb565>>,
634 {
635 StandardSwapChain::<320, 240, _>::from_static_slices(
636 make_static_slice(320 * 240),
637 make_static_slice(320 * 240),
638 false,
639 backend,
640 )
641 }
642
643 #[test]
646 fn test_swapchain_creation() {
647 let sc = make_swap_chain(SimulatorBackend::new());
648 assert_eq!(sc.dimensions(), (320, 240));
649 assert_eq!(sc.frame_count(), 0);
650 assert!(sc.is_ready());
651 }
652
653 #[test]
654 fn test_swapchain_present() {
655 let mut sc = make_swap_chain(SimulatorBackend::new());
656 assert!(sc.present().is_ok());
657 assert_eq!(sc.frame_count(), 1);
658 }
659
660 #[test]
661 fn test_swapchain_multiple_presents() {
662 let mut sc = make_swap_chain(SimulatorBackend::new());
663 for _ in 0..5 {
664 assert!(sc.present().is_ok());
665 }
666 assert_eq!(sc.frame_count(), 5);
667 }
668
669 #[test]
670 fn test_swapchain_try_present() {
671 let mut sc = make_swap_chain(SimulatorBackend::new());
672 assert!(sc.try_present().is_ok());
673 assert_eq!(sc.frame_count(), 1);
674 }
675
676 #[test]
677 fn test_swapchain_frame_counter() {
678 let mut sc = make_swap_chain(SimulatorBackend::new());
679 assert_eq!(sc.frame_count(), 0);
680 sc.present().unwrap();
681 assert_eq!(sc.frame_count(), 1);
682 sc.present().unwrap();
683 assert_eq!(sc.frame_count(), 2);
684 sc.reset_frame_count();
685 assert_eq!(sc.frame_count(), 0);
686 }
687
688 #[test]
689 fn test_swapchain_get_back_buffer_always_available() {
690 let mut sc = make_swap_chain(SimulatorBackend::new());
691 sc.present().unwrap();
692 let _back = sc.get_back_buffer();
694 }
695
696 #[test]
697 fn test_swapchain_wait_for_vsync() {
698 let mut sc = make_swap_chain(SimulatorBackend::new());
699 sc.present().unwrap();
700 sc.wait_for_vsync();
701 assert!(sc.is_ready());
703 }
704
705 #[test]
706 fn test_swapchain_present_region() {
707 let fb0 = make_static_slice(64 * 64);
708 let fb1 = make_static_slice(64 * 64);
709 let mut sc = StandardSwapChain::<64, 64, _>::from_static_slices(
710 fb0,
711 fb1,
712 false,
713 TrackingBackend::new(),
714 );
715 sc.present_region(DisplayRegion::new(0, 0, 8, 8)).unwrap();
716 assert_eq!(sc.backend.region_present_count.get(), 1);
717 }
718
719 #[test]
720 fn test_swapchain_is_ready_after_simulator_present() {
721 let mut sc = make_swap_chain(SimulatorBackend::new());
722 sc.present().unwrap();
724 assert!(sc.is_ready());
725 }
726
727 #[cfg(feature = "triple-buffering")]
730 #[test]
731 fn test_triple_swapchain_present() {
732 let fb0 = make_static_slice(64 * 64);
733 let fb1 = make_static_slice(64 * 64);
734 let fb2 = make_static_slice(64 * 64);
735 let mut sc = StandardTripleSwapChain::<64, 64, _>::from_static_slices(
736 fb0,
737 fb1,
738 fb2,
739 false,
740 SimulatorBackend::new(),
741 );
742 assert_eq!(sc.frame_count(), 0);
743 sc.present().unwrap();
744 assert_eq!(sc.frame_count(), 1);
745 }
746}