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