Skip to main content

pebble/assets/
storage.rs

1use slotmap::{SecondaryMap, SlotMap, new_key_type};
2use std::collections::HashMap;
3
4new_key_type! {
5    /// Untyped slot-map key for an asset entry.
6    ///
7    /// Prefer the typed [`Handle<T>`](crate::assets::handle::Handle) over this
8    /// in most code. `RawAssetHandle` is used internally by the storage and
9    /// sync systems.
10    pub struct RawAssetHandle;
11}
12
13/// Storage for raw CPU-side assets of type `T`.
14///
15/// Assets are inserted by name and looked up by either name or
16/// [`RawAssetHandle`]. When an asset is inserted or updated its handle is
17/// pushed onto the *dirty queue*, which the sync system drains each tick to
18/// upload changed assets to the GPU.
19pub struct Assets<T: 'static + Send + Sync> {
20    storage: SlotMap<RawAssetHandle, T>,
21    handles: HashMap<String, RawAssetHandle>,
22    queue: Vec<RawAssetHandle>,
23    removed: Vec<RawAssetHandle>,
24}
25
26impl<T: 'static + Send + Sync> Assets<T> {
27    pub fn new() -> Self {
28        Self {
29            storage: SlotMap::with_key(),
30            handles: HashMap::new(),
31            queue: Vec::new(),
32            removed: Vec::new(),
33        }
34    }
35
36    /// Insert `asset` under `name`, returning its handle.
37    ///
38    /// If an asset with the same name already exists it is replaced and the
39    /// old entry is removed from the slot-map and dirty queue.
40    pub fn insert(&mut self, name: &str, asset: T) -> RawAssetHandle {
41        let handle = self.storage.insert(asset);
42        self.queue.push(handle);
43
44        if let Some(old) = self.handles.insert(name.to_string(), handle) {
45            self.storage.remove(old);
46            self.queue.retain(|h| *h != old);
47        }
48        handle
49    }
50
51    /// Look up an asset by its raw handle.
52    pub fn get(&self, handle: RawAssetHandle) -> Option<&T> {
53        self.storage.get(handle)
54    }
55
56    /// Mutably look up an asset by its raw handle.
57    pub fn get_mut(&mut self, handle: RawAssetHandle) -> Option<&mut T> {
58        self.storage.get_mut(handle)
59    }
60
61    /// Look up an asset by its name.
62    pub fn get_by_name(&self, name: &str) -> Option<&T> {
63        self.handles
64            .get(name)
65            .and_then(|&handle| self.storage.get(handle))
66    }
67
68    /// Mutably look up an asset by its name.
69    pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut T> {
70        let handle = self.handles.get(name).copied()?;
71        self.storage.get_mut(handle)
72    }
73
74    /// Look up an asset handle by its name.
75    pub fn get_handle_by_name(&self, name: &str) -> Option<RawAssetHandle> {
76        self.handles.get(name).copied()
77    }
78
79    /// Drain and return all handles currently in the dirty queue.
80    ///
81    /// Called by the asset sync system each tick.
82    pub fn take_dirty(&mut self) -> Vec<RawAssetHandle> {
83        std::mem::take(&mut self.queue)
84    }
85
86    /// Remove an asset by handle, returning the value if it existed.
87    pub fn remove(&mut self, handle: RawAssetHandle) -> Option<T> {
88        let value = self.storage.remove(handle)?;
89
90        self.handles.retain(|_, h| *h != handle);
91        self.queue.retain(|h| *h != handle);
92        self.removed.push(handle);
93
94        Some(value)
95    }
96
97    /// Remove an asset by name, returning the value if it existed.
98    pub fn remove_by_name(&mut self, name: &str) -> Option<T> {
99        let handle = self.handles.remove(name)?;
100        self.queue.retain(|h| *h != handle);
101        self.removed.push(handle);
102        self.storage.remove(handle)
103    }
104
105    /// Drain and return all handles removed since the last call.
106    ///
107    /// Called by the asset sync system each tick to evict stale processed assets.
108    pub fn take_removed(&mut self) -> Vec<RawAssetHandle> {
109        std::mem::take(&mut self.removed)
110    }
111
112    /// Returns `true` if the dirty queue is empty.
113    pub fn dirty_is_empty(&self) -> bool {
114        self.queue.is_empty()
115    }
116
117    /// Returns the number of handles currently in the dirty queue.
118    pub fn dirty_len(&self) -> usize {
119        self.queue.len()
120    }
121
122    /// Push `handles` back onto the dirty queue so they are retried next tick.
123    pub fn requeue(&mut self, handles: Vec<RawAssetHandle>) {
124        self.queue.extend(handles);
125    }
126
127    /// Iterate over all assets by handle.
128    pub fn iter(&self) -> impl Iterator<Item = (RawAssetHandle, &T)> {
129        self.storage.iter()
130    }
131
132    /// Mutably iterate over all assets by handle.
133    pub fn iter_mut(&mut self) -> impl Iterator<Item = (RawAssetHandle, &mut T)> {
134        self.storage.iter_mut()
135    }
136
137    /// Iterate over `(name, handle)` pairs for every named asset.
138    pub fn names(&self) -> impl Iterator<Item = (&str, RawAssetHandle)> {
139        self.handles
140            .iter()
141            .map(|(name, &handle)| (name.as_str(), handle))
142    }
143
144    pub fn name_for_handle(&self, handle: RawAssetHandle) -> Option<&str> {
145        self.handles
146            .iter()
147            .find(|(_, h)| **h == handle)
148            .map(|(name, _)| name.as_str())
149    }
150}
151
152impl<'a, T: 'static + Send + Sync> IntoIterator for &'a Assets<T> {
153    type Item = (RawAssetHandle, &'a T);
154    type IntoIter = slotmap::basic::Iter<'a, RawAssetHandle, T>;
155
156    fn into_iter(self) -> Self::IntoIter {
157        self.storage.iter()
158    }
159}
160
161impl<'a, T: 'static + Send + Sync> IntoIterator for &'a mut Assets<T> {
162    type Item = (RawAssetHandle, &'a mut T);
163    type IntoIter = slotmap::basic::IterMut<'a, RawAssetHandle, T>;
164
165    fn into_iter(self) -> Self::IntoIter {
166        self.storage.iter_mut()
167    }
168}
169
170/// Storage for backend-processed (GPU) assets indexed by the same
171/// [`RawAssetHandle`] as their source in [`Assets`].
172///
173/// Populated by the asset sync system after a successful [`Asset::upload`](crate::assets::upload::Asset::upload).
174pub struct ProcessedAssets<T: 'static + Send + Sync> {
175    storage: SecondaryMap<RawAssetHandle, T>,
176    pub(crate) names: HashMap<String, RawAssetHandle>,
177}
178
179impl<T: 'static + Send + Sync> ProcessedAssets<T> {
180    pub fn new() -> Self {
181        Self {
182            storage: SecondaryMap::new(),
183            names: HashMap::new(),
184        }
185    }
186
187    /// Store a processed asset, returning the previous value if one existed.
188    pub fn insert(&mut self, handle: RawAssetHandle, asset: T) -> Option<T> {
189        self.storage.insert(handle, asset)
190    }
191
192    /// Look up a processed asset by handle.
193    pub fn get(&self, handle: RawAssetHandle) -> Option<&T> {
194        self.storage.get(handle)
195    }
196
197    /// Mutably look up a processed asset by handle.
198    pub fn get_mut(&mut self, handle: RawAssetHandle) -> Option<&mut T> {
199        self.storage.get_mut(handle)
200    }
201
202    /// Remove a processed asset by handle, returning the value if it existed.
203    pub fn remove(&mut self, handle: RawAssetHandle) -> Option<T> {
204        self.names.retain(|_, h| *h != handle);
205        self.storage.remove(handle)
206    }
207
208    /// Returns `true` if a processed asset exists for `handle`.
209    pub fn contains(&self, handle: RawAssetHandle) -> bool {
210        self.storage.contains_key(handle)
211    }
212
213    /// Iterate over all processed assets by handle.
214    pub fn iter(&self) -> impl Iterator<Item = (RawAssetHandle, &T)> {
215        self.storage.iter()
216    }
217
218    /// Mutably iterate over all processed assets by handle.
219    pub fn iter_mut(&mut self) -> impl Iterator<Item = (RawAssetHandle, &mut T)> {
220        self.storage.iter_mut()
221    }
222
223    pub fn get_by_name(&self, name: &str) -> Option<&T> {
224        let handle = self.names.get(name)?;
225        self.storage.get(*handle)
226    }
227}
228
229impl<'a, T: 'static + Send + Sync> IntoIterator for &'a ProcessedAssets<T> {
230    type Item = (RawAssetHandle, &'a T);
231    type IntoIter = slotmap::secondary::Iter<'a, RawAssetHandle, T>;
232
233    fn into_iter(self) -> Self::IntoIter {
234        self.storage.iter()
235    }
236}
237
238impl<'a, T: 'static + Send + Sync> IntoIterator for &'a mut ProcessedAssets<T> {
239    type Item = (RawAssetHandle, &'a mut T);
240    type IntoIter = slotmap::secondary::IterMut<'a, RawAssetHandle, T>;
241
242    fn into_iter(self) -> Self::IntoIter {
243        self.storage.iter_mut()
244    }
245}