use std::sync::Mutex;
use std::sync::atomic::{AtomicU64, Ordering};
use crate::Shared;
#[derive(Debug)]
pub(crate) enum Retired {
Texture(wgpu::Texture),
Buffer(wgpu::Buffer),
}
impl Retired {
fn destroy(self) {
match self {
Self::Texture(texture) => texture.destroy(),
Self::Buffer(buffer) => buffer.destroy(),
}
}
}
#[derive(Debug, Default)]
pub(crate) struct Retirement {
state: Mutex<RetirementState>,
generation: AtomicU64,
}
#[derive(Debug, Default)]
struct RetirementState {
open: usize,
waiting: Vec<Retired>,
retired_bytes: u64,
}
#[derive(Debug)]
pub(crate) struct EncodeScope {
retirement: Shared<Retirement>,
}
impl Retirement {
fn lock(&self) -> std::sync::MutexGuard<'_, RetirementState> {
self.state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub(crate) fn retire(&self, resources: impl IntoIterator<Item = (Retired, u64)>) {
let mut now = Vec::new();
{
let mut state = self.lock();
for (resource, bytes) in resources {
state.retired_bytes = state.retired_bytes.saturating_add(bytes);
if state.open == 0 {
now.push(resource);
} else {
state.waiting.push(resource);
}
}
}
for resource in now {
resource.destroy();
}
}
pub(crate) fn open(self: &Shared<Self>) -> EncodeScope {
let mut state = self.lock();
state.open = state.open.saturating_add(1);
drop(state);
EncodeScope {
retirement: Shared::clone(self),
}
}
pub(crate) fn generation(&self) -> u64 {
self.generation.load(Ordering::Acquire)
}
pub(crate) fn trusts(&self, generation: u64) -> bool {
generation == self.generation()
}
pub(crate) fn doubt(&self) {
self.generation.fetch_add(1, Ordering::AcqRel);
}
pub(crate) fn retired_bytes(&self) -> u64 {
self.lock().retired_bytes
}
}
impl Drop for EncodeScope {
fn drop(&mut self) {
let waiting = {
let mut state = self.retirement.lock();
state.open = state.open.saturating_sub(1);
if state.open == 0 {
std::mem::take(&mut state.waiting)
} else {
Vec::new()
}
};
for resource in waiting {
resource.destroy();
}
}
}