wgpu_core/
registry.rs

1use std::marker::PhantomData;
2
3use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
4use wgt::Backend;
5
6use crate::{
7    hub::{Access, Token},
8    id,
9    identity::{IdentityHandler, IdentityHandlerFactory},
10    resource::Resource,
11    storage::Storage,
12};
13
14#[derive(Debug)]
15pub struct Registry<T: Resource, I: id::TypedId, F: IdentityHandlerFactory<I>> {
16    identity: F::Filter,
17    pub(crate) data: RwLock<Storage<T, I>>,
18    backend: Backend,
19}
20
21impl<T: Resource, I: id::TypedId, F: IdentityHandlerFactory<I>> Registry<T, I, F> {
22    pub(crate) fn new(backend: Backend, factory: &F) -> Self {
23        Self {
24            identity: factory.spawn(),
25            data: RwLock::new(Storage {
26                map: Vec::new(),
27                kind: T::TYPE,
28                _phantom: PhantomData,
29            }),
30            backend,
31        }
32    }
33
34    pub(crate) fn without_backend(factory: &F, kind: &'static str) -> Self {
35        Self {
36            identity: factory.spawn(),
37            data: RwLock::new(Storage {
38                map: Vec::new(),
39                kind,
40                _phantom: PhantomData,
41            }),
42            backend: Backend::Empty,
43        }
44    }
45}
46
47#[must_use]
48pub(crate) struct FutureId<'a, I: id::TypedId, T> {
49    id: I,
50    data: &'a RwLock<Storage<T, I>>,
51}
52
53impl<I: id::TypedId + Copy, T> FutureId<'_, I, T> {
54    #[cfg(feature = "trace")]
55    pub fn id(&self) -> I {
56        self.id
57    }
58
59    pub fn into_id(self) -> I {
60        self.id
61    }
62
63    pub fn assign<'a, A: Access<T>>(self, value: T, _: &'a mut Token<A>) -> id::Valid<I> {
64        self.data.write().insert(self.id, value);
65        id::Valid(self.id)
66    }
67
68    pub fn assign_error<'a, A: Access<T>>(self, label: &str, _: &'a mut Token<A>) -> I {
69        self.data.write().insert_error(self.id, label);
70        self.id
71    }
72}
73
74impl<T: Resource, I: id::TypedId + Copy, F: IdentityHandlerFactory<I>> Registry<T, I, F> {
75    pub(crate) fn prepare(
76        &self,
77        id_in: <F::Filter as IdentityHandler<I>>::Input,
78    ) -> FutureId<I, T> {
79        FutureId {
80            id: self.identity.process(id_in, self.backend),
81            data: &self.data,
82        }
83    }
84
85    /// Acquire read access to this `Registry`'s contents.
86    ///
87    /// The caller must present a mutable reference to a `Token<A>`,
88    /// for some type `A` that comes before this `Registry`'s resource
89    /// type `T` in the lock ordering. A `Token<Root>` grants
90    /// permission to lock any field; see [`Token::root`].
91    ///
92    /// Once the read lock is acquired, return a new `Token<T>`, along
93    /// with a read guard for this `Registry`'s [`Storage`], which can
94    /// be indexed by id to get at the actual resources.
95    ///
96    /// The borrow checker ensures that the caller cannot again access
97    /// its `Token<A>` until it has dropped both the guard and the
98    /// `Token<T>`.
99    ///
100    /// See the [`Hub`] type for more details on locking.
101    ///
102    /// [`Hub`]: crate::hub::Hub
103    pub(crate) fn read<'a, A: Access<T>>(
104        &'a self,
105        _token: &'a mut Token<A>,
106    ) -> (RwLockReadGuard<'a, Storage<T, I>>, Token<'a, T>) {
107        (self.data.read(), Token::new())
108    }
109
110    /// Acquire write access to this `Registry`'s contents.
111    ///
112    /// The caller must present a mutable reference to a `Token<A>`,
113    /// for some type `A` that comes before this `Registry`'s resource
114    /// type `T` in the lock ordering. A `Token<Root>` grants
115    /// permission to lock any field; see [`Token::root`].
116    ///
117    /// Once the lock is acquired, return a new `Token<T>`, along with
118    /// a write guard for this `Registry`'s [`Storage`], which can be
119    /// indexed by id to get at the actual resources.
120    ///
121    /// The borrow checker ensures that the caller cannot again access
122    /// its `Token<A>` until it has dropped both the guard and the
123    /// `Token<T>`.
124    ///
125    /// See the [`Hub`] type for more details on locking.
126    ///
127    /// [`Hub`]: crate::hub::Hub
128    pub(crate) fn write<'a, A: Access<T>>(
129        &'a self,
130        _token: &'a mut Token<A>,
131    ) -> (RwLockWriteGuard<'a, Storage<T, I>>, Token<'a, T>) {
132        (self.data.write(), Token::new())
133    }
134
135    /// Unregister the resource at `id`.
136    ///
137    /// The caller must prove that it already holds a write lock for
138    /// this `Registry` by passing a mutable reference to this
139    /// `Registry`'s storage, obtained from the write guard returned
140    /// by a previous call to [`write`], as the `guard` parameter.
141    pub fn unregister_locked(&self, id: I, guard: &mut Storage<T, I>) -> Option<T> {
142        let value = guard.remove(id);
143        //Note: careful about the order here!
144        self.identity.free(id);
145        //Returning None is legal if it's an error ID
146        value
147    }
148
149    /// Unregister the resource at `id` and return its value, if any.
150    ///
151    /// The caller must present a mutable reference to a `Token<A>`,
152    /// for some type `A` that comes before this `Registry`'s resource
153    /// type `T` in the lock ordering.
154    ///
155    /// This returns a `Token<T>`, but it's almost useless, because it
156    /// doesn't return a lock guard to go with it: its only effect is
157    /// to make the token you passed to this function inaccessible.
158    /// However, the `Token<T>` can be used to satisfy some functions'
159    /// bureacratic expectations that you will have one available.
160    ///
161    /// The borrow checker ensures that the caller cannot again access
162    /// its `Token<A>` until it has dropped both the guard and the
163    /// `Token<T>`.
164    ///
165    /// See the [`Hub`] type for more details on locking.
166    ///
167    /// [`Hub`]: crate::hub::Hub
168    pub(crate) fn unregister<'a, A: Access<T>>(
169        &self,
170        id: I,
171        _token: &'a mut Token<A>,
172    ) -> (Option<T>, Token<'a, T>) {
173        let value = self.data.write().remove(id);
174        //Note: careful about the order here!
175        self.identity.free(id);
176        //Returning None is legal if it's an error ID
177        (value, Token::new())
178    }
179
180    pub fn label_for_resource(&self, id: I) -> String {
181        let guard = self.data.read();
182
183        let type_name = guard.kind;
184        match guard.get(id) {
185            Ok(res) => {
186                let label = res.label();
187                if label.is_empty() {
188                    format!("<{}-{:?}>", type_name, id.unzip())
189                } else {
190                    label.to_string()
191                }
192            }
193            Err(_) => format!(
194                "<Invalid-{} label={}>",
195                type_name,
196                guard.label_for_invalid_id(id)
197            ),
198        }
199    }
200}