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.storage.remove(handle)
98    }
99
100    /// Returns `true` if the dirty queue is empty.
101    pub fn dirty_is_empty(&self) -> bool {
102        self.queue.is_empty()
103    }
104
105    /// Returns the number of handles currently in the dirty queue.
106    pub fn dirty_len(&self) -> usize {
107        self.queue.len()
108    }
109
110    /// Push `handles` back onto the dirty queue so they are retried next tick.
111    pub fn requeue(&mut self, handles: Vec<RawAssetHandle>) {
112        self.queue.extend(handles);
113    }
114
115    /// Iterate over all assets by handle.
116    pub fn iter(&self) -> impl Iterator<Item = (RawAssetHandle, &T)> {
117        self.storage.iter()
118    }
119
120    /// Mutably iterate over all assets by handle.
121    pub fn iter_mut(&mut self) -> impl Iterator<Item = (RawAssetHandle, &mut T)> {
122        self.storage.iter_mut()
123    }
124
125    /// Iterate over `(name, handle)` pairs for every named asset.
126    pub fn names(&self) -> impl Iterator<Item = (&str, RawAssetHandle)> {
127        self.handles
128            .iter()
129            .map(|(name, &handle)| (name.as_str(), handle))
130    }
131}
132
133impl<'a, T: 'static + Send + Sync> IntoIterator for &'a Assets<T> {
134    type Item = (RawAssetHandle, &'a T);
135    type IntoIter = slotmap::basic::Iter<'a, RawAssetHandle, T>;
136
137    fn into_iter(self) -> Self::IntoIter {
138        self.storage.iter()
139    }
140}
141
142impl<'a, T: 'static + Send + Sync> IntoIterator for &'a mut Assets<T> {
143    type Item = (RawAssetHandle, &'a mut T);
144    type IntoIter = slotmap::basic::IterMut<'a, RawAssetHandle, T>;
145
146    fn into_iter(self) -> Self::IntoIter {
147        self.storage.iter_mut()
148    }
149}
150
151/// Storage for backend-processed (GPU) assets indexed by the same
152/// [`RawAssetHandle`] as their source in [`Assets`].
153///
154/// Populated by the asset sync system after a successful [`Asset::upload`](crate::assets::upload::Asset::upload).
155pub struct ProcessedAssets<T: 'static + Send + Sync> {
156    storage: SecondaryMap<RawAssetHandle, T>,
157}
158
159impl<T: 'static + Send + Sync> ProcessedAssets<T> {
160    pub fn new() -> Self {
161        Self {
162            storage: SecondaryMap::new(),
163        }
164    }
165
166    /// Store a processed asset, returning the previous value if one existed.
167    pub fn insert(&mut self, handle: RawAssetHandle, asset: T) -> Option<T> {
168        self.storage.insert(handle, asset)
169    }
170
171    /// Look up a processed asset by handle.
172    pub fn get(&self, handle: RawAssetHandle) -> Option<&T> {
173        self.storage.get(handle)
174    }
175
176    /// Mutably look up a processed asset by handle.
177    pub fn get_mut(&mut self, handle: RawAssetHandle) -> Option<&mut T> {
178        self.storage.get_mut(handle)
179    }
180
181    /// Remove a processed asset by handle, returning the value if it existed.
182    pub fn remove(&mut self, handle: RawAssetHandle) -> Option<T> {
183        self.storage.remove(handle)
184    }
185
186    /// Returns `true` if a processed asset exists for `handle`.
187    pub fn contains(&self, handle: RawAssetHandle) -> bool {
188        self.storage.contains_key(handle)
189    }
190
191    /// Iterate over all processed assets by handle.
192    pub fn iter(&self) -> impl Iterator<Item = (RawAssetHandle, &T)> {
193        self.storage.iter()
194    }
195
196    /// Mutably iterate over all processed assets by handle.
197    pub fn iter_mut(&mut self) -> impl Iterator<Item = (RawAssetHandle, &mut T)> {
198        self.storage.iter_mut()
199    }
200}
201
202impl<'a, T: 'static + Send + Sync> IntoIterator for &'a ProcessedAssets<T> {
203    type Item = (RawAssetHandle, &'a T);
204    type IntoIter = slotmap::secondary::Iter<'a, RawAssetHandle, T>;
205
206    fn into_iter(self) -> Self::IntoIter {
207        self.storage.iter()
208    }
209}
210
211impl<'a, T: 'static + Send + Sync> IntoIterator for &'a mut ProcessedAssets<T> {
212    type Item = (RawAssetHandle, &'a mut T);
213    type IntoIter = slotmap::secondary::IterMut<'a, RawAssetHandle, T>;
214
215    fn into_iter(self) -> Self::IntoIter {
216        self.storage.iter_mut()
217    }
218}