use crate::display_backend::{DisplayBackend, DisplayError};
use embedded_graphics_framebuf::{
backends::{DMACapableFrameBufferBackend, EndianCorrectedBuffer, EndianCorrection},
FrameBuf,
};
use embedded_graphics_core::pixelcolor::Rgb565;
pub struct SwapChain<const W: usize, const H: usize, FB, B>
where
FB: DMACapableFrameBufferBackend<Color = Rgb565>,
B: DisplayBackend<W, H, FB>,
{
front: FrameBuf<Rgb565, FB>,
back: FrameBuf<Rgb565, FB>,
backend: B,
frame_count: u64,
}
pub type StandardSwapChain<const W: usize, const H: usize, B> =
SwapChain<W, H, EndianCorrectedBuffer<'static, Rgb565>, B>;
impl<const W: usize, const H: usize, B> StandardSwapChain<W, H, B>
where
B: DisplayBackend<W, H, EndianCorrectedBuffer<'static, Rgb565>>,
{
pub fn from_static_slices(
front_data: &'static mut [Rgb565],
back_data: &'static mut [Rgb565],
big_endian: bool,
backend: B,
) -> Self {
let front_backend = if big_endian {
EndianCorrectedBuffer::new(front_data, EndianCorrection::ToBigEndian)
} else {
EndianCorrectedBuffer::new(front_data, EndianCorrection::ToLittleEndian)
};
let back_backend = if big_endian {
EndianCorrectedBuffer::new(back_data, EndianCorrection::ToBigEndian)
} else {
EndianCorrectedBuffer::new(back_data, EndianCorrection::ToLittleEndian)
};
Self {
front: FrameBuf::new(front_backend, W, H),
back: FrameBuf::new(back_backend, W, H),
backend,
frame_count: 0,
}
}
}
impl<const W: usize, const H: usize, FB, B> SwapChain<W, H, FB, B>
where
FB: DMACapableFrameBufferBackend<Color = Rgb565>,
B: DisplayBackend<W, H, FB>,
{
pub fn get_back_buffer(&mut self) -> &mut FrameBuf<Rgb565, FB> {
&mut self.back
}
pub fn get_front_buffer(&self) -> &FrameBuf<Rgb565, FB> {
&self.front
}
pub fn present(&mut self) -> Result<(), DisplayError> {
self.backend.wait_for_dma();
core::mem::swap(&mut self.front, &mut self.back);
self.backend.start_dma_transfer(&self.front)?;
self.frame_count += 1;
Ok(())
}
pub fn try_present(&mut self) -> Result<(), DisplayError> {
if !self.backend.is_dma_ready() {
return Err(DisplayError::Busy);
}
core::mem::swap(&mut self.front, &mut self.back);
self.backend.start_dma_transfer(&self.front)?;
self.frame_count += 1;
Ok(())
}
pub fn wait_for_vsync(&mut self) {
self.backend.wait_for_dma();
}
pub fn is_ready(&self) -> bool {
self.backend.is_dma_ready()
}
pub fn frame_count(&self) -> u64 {
self.frame_count
}
pub fn reset_frame_count(&mut self) {
self.frame_count = 0;
}
pub fn dimensions(&self) -> (usize, usize) {
(W, H)
}
}
#[cfg(test)]
mod tests {
extern crate std;
use super::*;
use crate::display_backend::SimulatorBackend;
use embedded_graphics_core::pixelcolor::RgbColor;
use std::vec;
fn create_static_buffer<const SIZE: usize>() -> &'static mut [Rgb565] {
let vec = vec![Rgb565::BLACK; SIZE];
vec.leak()
}
#[test]
fn test_swapchain_creation() {
let fb0 = create_static_buffer::<{ 320 * 240 }>();
let fb1 = create_static_buffer::<{ 320 * 240 }>();
let swap_chain = StandardSwapChain::<320, 240, _>::from_static_slices(
fb0,
fb1,
false,
SimulatorBackend::new(),
);
assert_eq!(swap_chain.dimensions(), (320, 240));
assert_eq!(swap_chain.frame_count(), 0);
assert!(swap_chain.is_ready());
}
#[test]
fn test_swapchain_present() {
let fb0 = create_static_buffer::<{ 320 * 240 }>();
let fb1 = create_static_buffer::<{ 320 * 240 }>();
let mut swap_chain = StandardSwapChain::<320, 240, _>::from_static_slices(
fb0,
fb1,
false,
SimulatorBackend::new(),
);
assert!(swap_chain.present().is_ok());
assert_eq!(swap_chain.frame_count(), 1);
}
#[test]
fn test_swapchain_multiple_presents() {
let fb0 = create_static_buffer::<{ 320 * 240 }>();
let fb1 = create_static_buffer::<{ 320 * 240 }>();
let mut swap_chain = StandardSwapChain::<320, 240, _>::from_static_slices(
fb0,
fb1,
false,
SimulatorBackend::new(),
);
for _ in 0..5 {
assert!(swap_chain.present().is_ok());
}
assert_eq!(swap_chain.frame_count(), 5);
}
#[test]
fn test_swapchain_try_present() {
let fb0 = create_static_buffer::<{ 320 * 240 }>();
let fb1 = create_static_buffer::<{ 320 * 240 }>();
let mut swap_chain = StandardSwapChain::<320, 240, _>::from_static_slices(
fb0,
fb1,
false,
SimulatorBackend::new(),
);
assert!(swap_chain.try_present().is_ok());
assert_eq!(swap_chain.frame_count(), 1);
}
#[test]
fn test_swapchain_frame_counter() {
let fb0 = create_static_buffer::<{ 320 * 240 }>();
let fb1 = create_static_buffer::<{ 320 * 240 }>();
let mut swap_chain = StandardSwapChain::<320, 240, _>::from_static_slices(
fb0,
fb1,
false,
SimulatorBackend::new(),
);
assert_eq!(swap_chain.frame_count(), 0);
swap_chain.present().unwrap();
assert_eq!(swap_chain.frame_count(), 1);
swap_chain.present().unwrap();
assert_eq!(swap_chain.frame_count(), 2);
swap_chain.reset_frame_count();
assert_eq!(swap_chain.frame_count(), 0);
}
}