use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex, PoisonError};
use std::thread::ThreadId;
use super::LiveImageId;
pub struct DeviceTextures<H> {
passes: Arc<Mutex<Vec<Published<H>>>>,
}
impl<H> DeviceTextures<H> {
pub fn new() -> Self {
Self {
passes: Arc::new(Mutex::new(Vec::new())),
}
}
#[doc(hidden)]
pub fn passes(&self) -> usize {
self.lock().len()
}
fn lock(&self) -> std::sync::MutexGuard<'_, Vec<Published<H>>> {
self.passes.lock().unwrap_or_else(PoisonError::into_inner)
}
}
impl<H> Default for DeviceTextures<H> {
fn default() -> Self {
Self::new()
}
}
impl<H> Clone for DeviceTextures<H> {
fn clone(&self) -> Self {
Self {
passes: Arc::clone(&self.passes),
}
}
}
impl<H> std::fmt::Debug for DeviceTextures<H> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DeviceTextures")
.field("passes", &self.passes())
.finish()
}
}
struct Published<H> {
pass: u64,
thread: ThreadId,
held: Vec<Held<H>>,
}
pub(crate) struct Held<H> {
pub(crate) id: LiveImageId,
pub(crate) size: (u32, u32),
pub(crate) generation: u64,
pub(crate) texture: H,
}
pub(crate) struct Siblings<H> {
table: DeviceTextures<H>,
pass: u64,
listed: bool,
}
impl<H: Clone> Siblings<H> {
pub(crate) fn join(table: &DeviceTextures<H>) -> Self {
static NEXT: AtomicU64 = AtomicU64::new(1);
Self {
table: table.clone(),
pass: NEXT.fetch_add(1, Ordering::Relaxed),
listed: false,
}
}
pub(crate) fn table(&self) -> &DeviceTextures<H> {
&self.table
}
pub(crate) fn publish(&mut self, held: Vec<Held<H>>) {
if held.is_empty() && !self.listed {
return;
}
self.listed = !held.is_empty();
let mut passes = self.table.lock();
let at = passes.iter().position(|p| p.pass == self.pass);
match (at, held.is_empty()) {
(Some(at), true) => {
passes.swap_remove(at);
}
(Some(at), false) => {
passes[at].thread = std::thread::current().id();
passes[at].held = held;
}
(None, true) => {}
(None, false) => passes.push(Published {
pass: self.pass,
thread: std::thread::current().id(),
held,
}),
}
}
pub(crate) fn find(&self, id: LiveImageId, size: (u32, u32), generation: u64) -> Option<H> {
let thread = std::thread::current().id();
let passes = self.table.lock();
passes
.iter()
.filter(|p| p.pass != self.pass && p.thread == thread)
.flat_map(|p| &p.held)
.find(|h| h.id == id && h.size == size && h.generation == generation)
.map(|h| h.texture.clone())
}
}
impl<H> Drop for Siblings<H> {
fn drop(&mut self) {
self.table.lock().retain(|p| p.pass != self.pass);
}
}