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}