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