mod buffer;
mod range;
mod texture;
use crate::{
hub::Storage,
Backend,
BindGroupId,
Epoch,
FastHashMap,
Index,
RefCount,
SamplerId,
TextureViewId,
TypedId,
};
use std::{
borrow::Borrow,
collections::hash_map::Entry,
fmt::Debug,
marker::PhantomData,
ops::Range,
vec::Drain,
};
use buffer::BufferState;
use texture::TextureState;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Unit<U> {
init: U,
last: U,
}
impl<U: Copy> Unit<U> {
fn new(usage: U) -> Self {
Unit {
init: usage,
last: usage,
}
}
fn select(&self, stitch: Stitch) -> U {
match stitch {
Stitch::Init => self.init,
Stitch::Last => self.last,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Stitch {
Init,
Last,
}
pub trait ResourceState: Clone + Default {
type Id: Copy + Debug + TypedId;
type Selector: Debug;
type Usage: Debug;
fn query(&self, selector: Self::Selector) -> Option<Self::Usage>;
fn change(
&mut self,
id: Self::Id,
selector: Self::Selector,
usage: Self::Usage,
output: Option<&mut Vec<PendingTransition<Self>>>,
) -> Result<(), PendingTransition<Self>>;
fn merge(
&mut self,
id: Self::Id,
other: &Self,
stitch: Stitch,
output: Option<&mut Vec<PendingTransition<Self>>>,
) -> Result<(), PendingTransition<Self>>;
fn optimize(&mut self);
}
#[derive(Clone, Debug)]
struct Resource<S> {
ref_count: RefCount,
state: S,
epoch: Epoch,
}
#[derive(Debug)]
pub struct PendingTransition<S: ResourceState> {
pub id: S::Id,
pub selector: S::Selector,
pub usage: Range<S::Usage>,
}
#[derive(Debug)]
pub struct ResourceTracker<S: ResourceState> {
map: FastHashMap<Index, Resource<S>>,
temp: Vec<PendingTransition<S>>,
backend: Backend,
}
impl<S: ResourceState> ResourceTracker<S> {
pub fn new(backend: Backend) -> Self {
ResourceTracker {
map: FastHashMap::default(),
temp: Vec::new(),
backend,
}
}
pub fn remove(&mut self, id: S::Id) -> bool {
let (index, epoch, backend) = id.unzip();
debug_assert_eq!(backend, self.backend);
match self.map.remove(&index) {
Some(resource) => {
assert_eq!(resource.epoch, epoch);
true
}
None => false,
}
}
pub fn optimize(&mut self) {
for resource in self.map.values_mut() {
resource.state.optimize();
}
}
pub fn used<'a>(&'a self) -> impl 'a + Iterator<Item = S::Id> {
let backend = self.backend;
self.map
.iter()
.map(move |(&index, resource)| S::Id::zip(index, resource.epoch, backend))
}
fn clear(&mut self) {
self.map.clear();
}
pub fn init(
&mut self,
id: S::Id,
ref_count: &RefCount,
selector: S::Selector,
default: S::Usage,
) -> bool {
let mut state = S::default();
match state.change(id, selector, default, None) {
Ok(()) => (),
Err(_) => unreachable!(),
}
let (index, epoch, backend) = id.unzip();
debug_assert_eq!(backend, self.backend);
self.map
.insert(
index,
Resource {
ref_count: ref_count.clone(),
state,
epoch,
},
)
.is_none()
}
pub fn query(&mut self, id: S::Id, selector: S::Selector) -> Option<S::Usage> {
let (index, epoch, backend) = id.unzip();
debug_assert_eq!(backend, self.backend);
let res = self.map.get(&index)?;
assert_eq!(res.epoch, epoch);
res.state.query(selector)
}
fn get_or_insert<'a>(
self_backend: Backend,
map: &'a mut FastHashMap<Index, Resource<S>>,
id: S::Id,
ref_count: &RefCount,
) -> &'a mut Resource<S> {
let (index, epoch, backend) = id.unzip();
debug_assert_eq!(self_backend, backend);
match map.entry(index) {
Entry::Vacant(e) => e.insert(Resource {
ref_count: ref_count.clone(),
state: S::default(),
epoch,
}),
Entry::Occupied(e) => {
assert_eq!(e.get().epoch, epoch);
e.into_mut()
}
}
}
pub fn change_extend(
&mut self,
id: S::Id,
ref_count: &RefCount,
selector: S::Selector,
usage: S::Usage,
) -> Result<(), PendingTransition<S>> {
Self::get_or_insert(self.backend, &mut self.map, id, ref_count)
.state
.change(id, selector, usage, None)
}
pub fn change_replace(
&mut self,
id: S::Id,
ref_count: &RefCount,
selector: S::Selector,
usage: S::Usage,
) -> Drain<PendingTransition<S>> {
let res = Self::get_or_insert(self.backend, &mut self.map, id, ref_count);
res.state
.change(id, selector, usage, Some(&mut self.temp))
.ok(); self.temp.drain(..)
}
pub fn merge_extend(&mut self, other: &Self) -> Result<(), PendingTransition<S>> {
debug_assert_eq!(self.backend, other.backend);
for (&index, new) in other.map.iter() {
match self.map.entry(index) {
Entry::Vacant(e) => {
e.insert(new.clone());
}
Entry::Occupied(e) => {
assert_eq!(e.get().epoch, new.epoch);
let id = S::Id::zip(index, new.epoch, self.backend);
e.into_mut()
.state
.merge(id, &new.state, Stitch::Last, None)?;
}
}
}
Ok(())
}
pub fn merge_replace<'a>(
&'a mut self,
other: &'a Self,
stitch: Stitch,
) -> Drain<PendingTransition<S>> {
for (&index, new) in other.map.iter() {
match self.map.entry(index) {
Entry::Vacant(e) => {
e.insert(new.clone());
}
Entry::Occupied(e) => {
assert_eq!(e.get().epoch, new.epoch);
let id = S::Id::zip(index, new.epoch, self.backend);
e.into_mut()
.state
.merge(id, &new.state, stitch, Some(&mut self.temp))
.ok(); }
}
}
self.temp.drain(..)
}
pub fn use_extend<'a, T: 'a + Borrow<RefCount>>(
&mut self,
storage: &'a Storage<T, S::Id>,
id: S::Id,
selector: S::Selector,
usage: S::Usage,
) -> Result<&'a T, S::Usage> {
let item = &storage[id];
self.change_extend(id, item.borrow(), selector, usage)
.map(|()| item)
.map_err(|pending| pending.usage.start)
}
pub fn use_replace<'a, T: 'a + Borrow<RefCount>>(
&mut self,
storage: &'a Storage<T, S::Id>,
id: S::Id,
selector: S::Selector,
usage: S::Usage,
) -> (&'a T, Drain<PendingTransition<S>>) {
let item = &storage[id];
let drain = self.change_replace(id, item.borrow(), selector, usage);
(item, drain)
}
}
impl<I: Copy + Debug + TypedId> ResourceState for PhantomData<I> {
type Id = I;
type Selector = ();
type Usage = ();
fn query(&self, _selector: Self::Selector) -> Option<Self::Usage> {
Some(())
}
fn change(
&mut self,
_id: Self::Id,
_selector: Self::Selector,
_usage: Self::Usage,
_output: Option<&mut Vec<PendingTransition<Self>>>,
) -> Result<(), PendingTransition<Self>> {
Ok(())
}
fn merge(
&mut self,
_id: Self::Id,
_other: &Self,
_stitch: Stitch,
_output: Option<&mut Vec<PendingTransition<Self>>>,
) -> Result<(), PendingTransition<Self>> {
Ok(())
}
fn optimize(&mut self) {}
}
#[derive(Debug)]
pub struct TrackerSet {
pub buffers: ResourceTracker<BufferState>,
pub textures: ResourceTracker<TextureState>,
pub views: ResourceTracker<PhantomData<TextureViewId>>,
pub bind_groups: ResourceTracker<PhantomData<BindGroupId>>,
pub samplers: ResourceTracker<PhantomData<SamplerId>>,
}
impl TrackerSet {
pub fn new(backend: Backend) -> Self {
TrackerSet {
buffers: ResourceTracker::new(backend),
textures: ResourceTracker::new(backend),
views: ResourceTracker::new(backend),
bind_groups: ResourceTracker::new(backend),
samplers: ResourceTracker::new(backend),
}
}
pub fn clear(&mut self) {
self.buffers.clear();
self.textures.clear();
self.views.clear();
self.bind_groups.clear();
self.samplers.clear();
}
pub fn optimize(&mut self) {
self.buffers.optimize();
self.textures.optimize();
self.views.optimize();
self.bind_groups.optimize();
self.samplers.optimize();
}
pub fn merge_extend(&mut self, other: &Self) {
self.buffers.merge_extend(&other.buffers).unwrap();
self.textures.merge_extend(&other.textures).unwrap();
self.views.merge_extend(&other.views).unwrap();
self.bind_groups.merge_extend(&other.bind_groups).unwrap();
self.samplers.merge_extend(&other.samplers).unwrap();
}
pub fn backend(&self) -> Backend {
self.buffers.backend
}
}