use parking_lot::Mutex;
use wgt::Backend;
use crate::{
id::{self, TypedId},
Epoch, Index,
};
use std::{fmt::Debug, marker::PhantomData, sync::Arc};
#[derive(Debug)]
pub(super) struct IdentityValues {
free: Vec<(Index, Epoch)>,
next_index: Index,
count: usize,
}
impl IdentityValues {
pub fn alloc<I: TypedId>(&mut self, backend: Backend) -> I {
self.count += 1;
match self.free.pop() {
Some((index, epoch)) => I::zip(index, epoch + 1, backend),
None => {
let index = self.next_index;
self.next_index += 1;
let epoch = 1;
I::zip(index, epoch, backend)
}
}
}
pub fn mark_as_used<I: TypedId>(&mut self, id: I) -> I {
self.count += 1;
id
}
pub fn release<I: TypedId>(&mut self, id: I) {
let (index, epoch, _backend) = id.unzip();
self.free.push((index, epoch));
self.count -= 1;
}
pub fn count(&self) -> usize {
self.count
}
}
#[derive(Debug)]
pub struct IdentityManager<I: TypedId> {
pub(super) values: Mutex<IdentityValues>,
_phantom: PhantomData<I>,
}
impl<I: TypedId> IdentityManager<I> {
pub fn process(&self, backend: Backend) -> I {
self.values.lock().alloc(backend)
}
pub fn mark_as_used(&self, id: I) -> I {
self.values.lock().mark_as_used(id)
}
pub fn free(&self, id: I) {
self.values.lock().release(id)
}
}
impl<I: TypedId> IdentityManager<I> {
pub fn new() -> Self {
Self {
values: Mutex::new(IdentityValues {
free: Vec::new(),
next_index: 0,
count: 0,
}),
_phantom: PhantomData,
}
}
}
pub trait IdentityHandlerFactory<I: TypedId> {
type Input: Copy;
fn spawn(&self) -> Arc<IdentityManager<I>> {
Arc::new(IdentityManager::new())
}
fn autogenerate_ids() -> bool;
fn input_to_id(id_in: Self::Input) -> I;
}
#[derive(Debug)]
pub struct IdentityManagerFactory;
impl<I: TypedId> IdentityHandlerFactory<I> for IdentityManagerFactory {
type Input = ();
fn autogenerate_ids() -> bool {
true
}
fn input_to_id(_id_in: Self::Input) -> I {
unreachable!("It should not be called")
}
}
pub trait GlobalIdentityHandlerFactory:
IdentityHandlerFactory<id::AdapterId>
+ IdentityHandlerFactory<id::DeviceId>
+ IdentityHandlerFactory<id::PipelineLayoutId>
+ IdentityHandlerFactory<id::ShaderModuleId>
+ IdentityHandlerFactory<id::BindGroupLayoutId>
+ IdentityHandlerFactory<id::BindGroupId>
+ IdentityHandlerFactory<id::CommandBufferId>
+ IdentityHandlerFactory<id::RenderBundleId>
+ IdentityHandlerFactory<id::RenderPipelineId>
+ IdentityHandlerFactory<id::ComputePipelineId>
+ IdentityHandlerFactory<id::QuerySetId>
+ IdentityHandlerFactory<id::BufferId>
+ IdentityHandlerFactory<id::StagingBufferId>
+ IdentityHandlerFactory<id::TextureId>
+ IdentityHandlerFactory<id::TextureViewId>
+ IdentityHandlerFactory<id::SamplerId>
+ IdentityHandlerFactory<id::SurfaceId>
{
}
impl GlobalIdentityHandlerFactory for IdentityManagerFactory {}
pub type Input<G, I> = <G as IdentityHandlerFactory<I>>::Input;
#[test]
fn test_epoch_end_of_life() {
use id::TypedId as _;
let man = IdentityManager::<id::BufferId>::new();
let forced_id = man.mark_as_used(id::BufferId::zip(0, 1, Backend::Empty));
assert_eq!(forced_id.unzip().0, 0);
let id1 = man.process(Backend::Empty);
assert_eq!(id1.unzip(), (0, 1, Backend::Empty));
man.free(id1);
let id2 = man.process(Backend::Empty);
assert_eq!(id2.unzip(), (0, 2, Backend::Empty));
}