wgpu_core/
identity.rs

1use parking_lot::Mutex;
2use wgt::Backend;
3
4use crate::{id, Epoch, Index};
5use std::fmt::Debug;
6
7/// A simple structure to allocate [`Id`] identifiers.
8///
9/// Calling [`alloc`] returns a fresh, never-before-seen id. Calling [`free`]
10/// marks an id as dead; it will never be returned again by `alloc`.
11///
12/// Use `IdentityManager::default` to construct new instances.
13///
14/// `IdentityManager` returns `Id`s whose index values are suitable for use as
15/// indices into a `Storage<T>` that holds those ids' referents:
16///
17/// - Every live id has a distinct index value. Each live id's index selects a
18///   distinct element in the vector.
19///
20/// - `IdentityManager` prefers low index numbers. If you size your vector to
21///   accommodate the indices produced here, the vector's length will reflect
22///   the highwater mark of actual occupancy.
23///
24/// - `IdentityManager` reuses the index values of freed ids before returning
25///   ids with new index values. Freed vector entries get reused.
26///
27/// See the module-level documentation for an overview of how this
28/// fits together.
29///
30/// [`Id`]: crate::id::Id
31/// [`Backend`]: wgt::Backend;
32/// [`alloc`]: IdentityManager::alloc
33/// [`free`]: IdentityManager::free
34#[derive(Debug, Default)]
35pub struct IdentityManager {
36    /// Available index values. If empty, then `epochs.len()` is the next index
37    /// to allocate.
38    free: Vec<Index>,
39
40    /// The next or currently-live epoch value associated with each `Id` index.
41    ///
42    /// If there is a live id with index `i`, then `epochs[i]` is its epoch; any
43    /// id with the same index but an older epoch is dead.
44    ///
45    /// If index `i` is currently unused, `epochs[i]` is the epoch to use in its
46    /// next `Id`.
47    epochs: Vec<Epoch>,
48}
49
50impl IdentityManager {
51    /// Allocate a fresh, never-before-seen id with the given `backend`.
52    ///
53    /// The backend is incorporated into the id, so that ids allocated with
54    /// different `backend` values are always distinct.
55    pub fn alloc<I: id::TypedId>(&mut self, backend: Backend) -> I {
56        match self.free.pop() {
57            Some(index) => I::zip(index, self.epochs[index as usize], backend),
58            None => {
59                let epoch = 1;
60                let id = I::zip(self.epochs.len() as Index, epoch, backend);
61                self.epochs.push(epoch);
62                id
63            }
64        }
65    }
66
67    /// Free `id`. It will never be returned from `alloc` again.
68    pub fn free<I: id::TypedId + Debug>(&mut self, id: I) {
69        let (index, epoch, _backend) = id.unzip();
70        let pe = &mut self.epochs[index as usize];
71        assert_eq!(*pe, epoch);
72        // If the epoch reaches EOL, the index doesn't go
73        // into the free list, will never be reused again.
74        if epoch < id::EPOCH_MASK {
75            *pe = epoch + 1;
76            self.free.push(index);
77        }
78    }
79}
80
81/// A type that can build true ids from proto-ids, and free true ids.
82///
83/// For some implementations, the true id is based on the proto-id.
84/// The caller is responsible for providing well-allocated proto-ids.
85///
86/// For other implementations, the proto-id carries no information
87/// (it's `()`, say), and this `IdentityHandler` type takes care of
88/// allocating a fresh true id.
89///
90/// See the module-level documentation for details.
91pub trait IdentityHandler<I>: Debug {
92    /// The type of proto-id consumed by this filter, to produce a true id.
93    type Input: Clone + Debug;
94
95    /// Given a proto-id value `id`, return a true id for `backend`.
96    fn process(&self, id: Self::Input, backend: Backend) -> I;
97
98    /// Free the true id `id`.
99    fn free(&self, id: I);
100}
101
102impl<I: id::TypedId + Debug> IdentityHandler<I> for Mutex<IdentityManager> {
103    type Input = ();
104    fn process(&self, _id: Self::Input, backend: Backend) -> I {
105        self.lock().alloc(backend)
106    }
107    fn free(&self, id: I) {
108        self.lock().free(id)
109    }
110}
111
112/// A type that can produce [`IdentityHandler`] filters for ids of type `I`.
113///
114/// See the module-level documentation for details.
115pub trait IdentityHandlerFactory<I> {
116    /// The type of filter this factory constructs.
117    ///
118    /// "Filter" and "handler" seem to both mean the same thing here:
119    /// something that can produce true ids from proto-ids.
120    type Filter: IdentityHandler<I>;
121
122    /// Create an [`IdentityHandler<I>`] implementation that can
123    /// transform proto-ids into ids of type `I`.
124    ///
125    /// [`IdentityHandler<I>`]: IdentityHandler
126    fn spawn(&self) -> Self::Filter;
127}
128
129/// A global identity handler factory based on [`IdentityManager`].
130///
131/// Each of this type's `IdentityHandlerFactory<I>::spawn` methods
132/// returns a `Mutex<IdentityManager<I>>`, which allocates fresh `I`
133/// ids itself, and takes `()` as its proto-id type.
134#[derive(Debug)]
135pub struct IdentityManagerFactory;
136
137impl<I: id::TypedId + Debug> IdentityHandlerFactory<I> for IdentityManagerFactory {
138    type Filter = Mutex<IdentityManager>;
139    fn spawn(&self) -> Self::Filter {
140        Mutex::new(IdentityManager::default())
141    }
142}
143
144/// A factory that can build [`IdentityHandler`]s for all resource
145/// types.
146pub trait GlobalIdentityHandlerFactory:
147    IdentityHandlerFactory<id::AdapterId>
148    + IdentityHandlerFactory<id::DeviceId>
149    + IdentityHandlerFactory<id::PipelineLayoutId>
150    + IdentityHandlerFactory<id::ShaderModuleId>
151    + IdentityHandlerFactory<id::BindGroupLayoutId>
152    + IdentityHandlerFactory<id::BindGroupId>
153    + IdentityHandlerFactory<id::CommandBufferId>
154    + IdentityHandlerFactory<id::RenderBundleId>
155    + IdentityHandlerFactory<id::RenderPipelineId>
156    + IdentityHandlerFactory<id::ComputePipelineId>
157    + IdentityHandlerFactory<id::QuerySetId>
158    + IdentityHandlerFactory<id::BufferId>
159    + IdentityHandlerFactory<id::StagingBufferId>
160    + IdentityHandlerFactory<id::TextureId>
161    + IdentityHandlerFactory<id::TextureViewId>
162    + IdentityHandlerFactory<id::SamplerId>
163    + IdentityHandlerFactory<id::SurfaceId>
164{
165}
166
167impl GlobalIdentityHandlerFactory for IdentityManagerFactory {}
168
169pub type Input<G, I> = <<G as IdentityHandlerFactory<I>>::Filter as IdentityHandler<I>>::Input;
170
171#[test]
172fn test_epoch_end_of_life() {
173    use id::TypedId as _;
174    let mut man = IdentityManager::default();
175    man.epochs.push(id::EPOCH_MASK);
176    man.free.push(0);
177    let id1 = man.alloc::<id::BufferId>(Backend::Empty);
178    assert_eq!(id1.unzip().0, 0);
179    man.free(id1);
180    let id2 = man.alloc::<id::BufferId>(Backend::Empty);
181    // confirm that the index 0 is no longer re-used
182    assert_eq!(id2.unzip().0, 1);
183}