<NME> swapchain.rs
<BEF> 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 = 4;

pub struct Swapchain<A: Allocator<B>, B: Buffer + TryInto<B>, D: Buffer = B> {
    allocator: A,
    _original_buffer_format: std::marker::PhantomData<B>,

    width: u32,
    height: u32,
    format: Format,

    slots: [Slot<D>; SLOT_CAP],
}

pub struct Slot<B: Buffer> {
    buffer: Arc<Option<B>>,
    acquired: Arc<AtomicBool>,
}

impl<B: Buffer> Default for Slot<B> {
    fn default() -> Self {
        Slot {
            buffer: Arc::new(None),
            acquired: Arc::new(AtomicBool::new(false)),
        }
    }
}

impl<B: Buffer> Clone for Slot<B> {
    fn clone(&self) -> Self {
        Slot {
            buffer: self.buffer.clone(),
            acquired: self.acquired.clone(),
        }
    }
}

impl<B: Buffer> Deref for Slot<B> {
    type Target = B;
    fn deref(&self) -> &B {
        Option::as_ref(&*self.buffer).unwrap()
    }
}

impl<B: Buffer> Drop for Slot<B> {
    fn drop(&mut self) {
        self.acquired.store(false, Ordering::AcqRel);
    }
}

struct InternalSlot<B: Buffer> {
    buffer: Option<B>,
    acquired: AtomicBool,
    age: AtomicU8,
    userdata: UserDataMap,
}

impl<B: Buffer> Slot<B> {
    /// Retrieve userdata for this slot.
    ConversionError(#[source] E2),
}

impl<A, B, D, E1, E2> Swapchain<A, B, D>
where
    A: Allocator<B, Error=E1>,
    B: Buffer + TryInto<D, Error=E2>,
    D: Buffer,
    E1: std::error::Error + 'static,
    E2: std::error::Error + 'static,
{
    pub fn new(allocator: A, width: u32, height: u32, format: Format) -> Swapchain<A, B, D> {
        Swapchain {
            allocator,
            _original_buffer_format: std::marker::PhantomData,
            userdata: UserDataMap::new(),
        }
    }
}
        }
    }

    pub fn acquire(&mut self) -> Result<Option<Slot<D>>, SwapchainError<E1, E2>> {
        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(

impl<B: Buffer> Drop for Slot<B> {
                    .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()));
            }

{
    /// Create a new swapchain with the desired allocator, dimensions and pixel format for the created buffers.
    pub fn new(
        allocator: A,
        width: u32,
        height: u32,
        fourcc: Fourcc,
        modifiers: Vec<Modifier>,
    ) -> Swapchain<A, B> {
        Swapchain {
            allocator,
            width,
            height,
            fourcc,
            modifiers,
            slots: Default::default(),
        }
    }

    /// Acquire a new slot from the swapchain, if one is still free.
    ///
    /// The swapchain has an internal maximum of four re-usable buffers.
    /// This function returns the first free one.
    pub fn acquire(&mut self) -> Result<Option<Slot<B>>, A::Error> {
        if let Some(free_slot) = self
            .slots
            .iter_mut()
            .find(|s| !s.acquired.swap(true, Ordering::SeqCst))
        {
            if free_slot.buffer.is_none() {
                let mut free_slot =
                    Arc::get_mut(free_slot).expect("Acquired was false, but Arc is not unique?");
                free_slot.buffer = Some(self.allocator.create_buffer(
                    self.width,
                    self.height,
                    self.fourcc,
                    &self.modifiers,
                )?);
            }
            assert!(free_slot.buffer.is_some());
            return Ok(Some(Slot(free_slot.clone())));
        }

        // no free slots
        Ok(None)
    }

    /// Mark a given buffer as submitted.
    ///
    /// This might effect internal data (e.g. buffer age) and may only be called,
    /// the buffer may not be used for rendering anymore.
    /// You may hold on to it, if you require keeping it alive.
    ///
    /// Buffers can always just be safely discarded by dropping them, but not
    /// calling this function before may affect performance characteristics
    /// (e.g. by not tracking the buffer age).
    pub fn submitted(&mut self, slot: &Slot<B>) {
        // don't mess up the state, if the user submitted and old buffer, after e.g. a resize
        if !self.slots.iter().any(|other| Arc::ptr_eq(&slot.0, other)) {
            return;
        }

        slot.0.age.store(1, Ordering::SeqCst);
        for other_slot in &mut self.slots {
            if !Arc::ptr_eq(other_slot, &slot.0) && other_slot.buffer.is_some() {
                let res = other_slot
                    .age
                    .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |age| {
                        if age > 0 {
                            age.checked_add(1)
                        } else {
                            Some(0)
                        }
                    });
                // If the age overflows the slot was not used for a long time. Lets clear it
                if res.is_err() {
                    *other_slot = Default::default();
                }
            }
        }
    }

    /// Change the dimensions of newly returned buffers.
    ///
    /// Already obtained buffers are unaffected and will be cleaned up on drop.
    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();
    }

    /// Remove all internally cached buffers to e.g. reset age values
    pub fn reset_buffers(&mut self) {
        for slot in &mut self.slots {
            if let Some(internal_slot) = Arc::get_mut(slot) {
                *internal_slot = InternalSlot {
                    buffer: internal_slot.buffer.take(),
                    ..Default::default()
                };
            } else {
                *slot = Default::default();
            }
        }
    }
}

<MSG> swapchain: allow slots to contain userdata

<DFF> @@ -1,55 +1,72 @@
 use std::convert::TryInto;
-use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
+use std::sync::{Arc, Mutex, MutexGuard, atomic::{AtomicBool, Ordering}};
 use std::ops::Deref;
 
 use crate::backend::allocator::{Allocator, Buffer, Format};
 
 pub const SLOT_CAP: usize = 4;
 
-pub struct Swapchain<A: Allocator<B>, B: Buffer + TryInto<B>, D: Buffer = B> {
-    allocator: A,
+pub struct Swapchain<A: Allocator<B>, B: Buffer + TryInto<B>, U: 'static, D: Buffer = B> {
+    pub allocator: A,
     _original_buffer_format: std::marker::PhantomData<B>,
 
     width: u32,
     height: u32,
     format: Format,
 
-    slots: [Slot<D>; SLOT_CAP],
+    slots: [Slot<D, U>; SLOT_CAP],
 }
 
-pub struct Slot<B: Buffer> {
+pub struct Slot<B: Buffer, U: 'static> {
     buffer: Arc<Option<B>>,
     acquired: Arc<AtomicBool>,
+    userdata: Arc<Mutex<Option<U>>>,
 }
 
-impl<B: Buffer> Default for Slot<B> {
+impl<B: Buffer, U: 'static> Slot<B, U> {
+    pub fn set_userdata(&self, data: U) -> Option<U> {
+        self.userdata.lock().unwrap().replace(data)
+    }
+
+    pub fn userdata(&self) -> MutexGuard<'_, Option<U>> {
+        self.userdata.lock().unwrap()
+    }
+
+    pub fn clear_userdata(&self) -> Option<U> {
+        self.userdata.lock().unwrap().take()
+    }
+}
+
+impl<B: Buffer, U: 'static> Default for Slot<B, U> {
     fn default() -> Self {
         Slot {
             buffer: Arc::new(None),
             acquired: Arc::new(AtomicBool::new(false)),
+            userdata: Arc::new(Mutex::new(None)),
         }
     }
 }
 
-impl<B: Buffer> Clone for Slot<B> {
+impl<B: Buffer, U: 'static> Clone for Slot<B, U> {
     fn clone(&self) -> Self {
         Slot {
             buffer: self.buffer.clone(),
             acquired: self.acquired.clone(),
+            userdata: self.userdata.clone(),
         }
     }
 }
 
-impl<B: Buffer> Deref for Slot<B> {
+impl<B: Buffer, U: 'static> Deref for Slot<B, U> {
     type Target = B;
     fn deref(&self) -> &B {
         Option::as_ref(&*self.buffer).unwrap()
     }
 }
 
-impl<B: Buffer> Drop for Slot<B> {
+impl<B: Buffer, U: 'static> Drop for Slot<B, U> {
     fn drop(&mut self) {
-        self.acquired.store(false, Ordering::AcqRel);
+        self.acquired.store(false, Ordering::SeqCst);
     }
 }
 
@@ -65,15 +82,16 @@ where
     ConversionError(#[source] E2),
 }
 
-impl<A, B, D, E1, E2> Swapchain<A, B, D>
+impl<A, B, D, U, E1, E2> Swapchain<A, B, U, D>
 where
     A: Allocator<B, Error=E1>,
     B: Buffer + TryInto<D, Error=E2>,
     D: Buffer,
     E1: std::error::Error + 'static,
     E2: std::error::Error + 'static,
+    U: 'static
 {
-    pub fn new(allocator: A, width: u32, height: u32, format: Format) -> Swapchain<A, B, D> {
+    pub fn new(allocator: A, width: u32, height: u32, format: Format) -> Swapchain<A, B, U, D> {
         Swapchain {
             allocator,
             _original_buffer_format: std::marker::PhantomData,
@@ -84,7 +102,7 @@ where
         }
     }
 
-    pub fn acquire(&mut self) -> Result<Option<Slot<D>>, SwapchainError<E1, E2>> {
+    pub fn acquire(&mut self) -> Result<Option<Slot<D, U>>, SwapchainError<E1, E2>> {
         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(
@@ -93,9 +111,9 @@ where
                     .try_into().map_err(SwapchainError::ConversionError)?
                 ));
             }
-            assert!(!free_slot.buffer.is_some());
+            assert!(free_slot.buffer.is_some());
 
-            if !free_slot.acquired.swap(true, Ordering::AcqRel) {
+            if !free_slot.acquired.swap(true, Ordering::SeqCst) {
                 return Ok(Some(free_slot.clone()));
             }
 

swapchain: allow slots to contain userdata