use std::convert::TryInto; use std::sync::{Arc, atomic::{AtomicBool, Ordering}}; use std::ops::Deref; use crate::backend::allocator::{Allocator, Buffer, Format}; pub const SLOT_CAP: usize = 3; pub struct Swapchain, B: Buffer + TryInto, D: Buffer = B> { allocator: A, _original_buffer_format: std::marker::PhantomData, width: u32, height: u32, format: Format, slots: [Slot; SLOT_CAP], } pub struct Slot { buffer: Arc>, acquired: Arc, } impl Default for Slot { fn default() -> Self { Slot { buffer: Arc::new(None), acquired: Arc::new(AtomicBool::new(false)), } } } impl Clone for Slot { fn clone(&self) -> Self { Slot { buffer: self.buffer.clone(), acquired: self.acquired.clone(), } } } impl Deref for Slot { type Target = B; fn deref(&self) -> &B { Option::as_ref(&*self.buffer).unwrap() } } impl Drop for Slot { fn drop(&mut self) { self.acquired.store(false, Ordering::AcqRel); } } #[derive(Debug, thiserror::Error)] pub enum SwapchainError where E1: std::error::Error + 'static, E2: std::error::Error + 'static, { #[error("Failed to allocate a new buffer: {0}")] AllocationError(#[source] E1), #[error("Failed to convert a new buffer: {0}")] ConversionError(#[source] E2), } impl Swapchain where A: Allocator, B: Buffer + TryInto, D: Buffer, E1: std::error::Error + 'static, E2: std::error::Error + 'static, { pub fn new(allocator: A, width: u32, height: u32, format: Format) -> Swapchain { Swapchain { allocator, _original_buffer_format: std::marker::PhantomData, width, height, format, slots: Default::default(), } } pub fn acquire(&mut self) -> Result>, SwapchainError> { if let Some(free_slot) = self.slots.iter_mut().filter(|s| !s.acquired.load(Ordering::SeqCst)).next() { if free_slot.buffer.is_none() { free_slot.buffer = Arc::new(Some( self.allocator .create_buffer(self.width, self.height, self.format).map_err(SwapchainError::AllocationError)? .try_into().map_err(SwapchainError::ConversionError)? )); } assert!(!free_slot.buffer.is_some()); if !free_slot.acquired.swap(true, Ordering::AcqRel) { return Ok(Some(free_slot.clone())); } } // no free slots Ok(None) } pub fn resize(&mut self, width: u32, height: u32) { if self.width == width && self.height == height { return; } self.width = width; self.height = height; self.slots = Default::default(); } }