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
111/// Storage for backend-processed (GPU) assets indexed by the same
112/// [`RawAssetHandle`] as their source in [`Assets`].
113///
114/// Populated by the asset sync system after a successful [`Asset::upload`](crate::assets::upload::Asset::upload).
115pub struct ProcessedAssets<T: 'static + Send + Sync> {
116    storage: SecondaryMap<RawAssetHandle, T>,
117}
118
119impl<T: 'static + Send + Sync> ProcessedAssets<T> {
120    pub fn new() -> Self {
121        Self {
122            storage: SecondaryMap::new(),
123        }
124    }
125
126    /// Store a processed asset, returning the previous value if one existed.
127    pub fn insert(&mut self, handle: RawAssetHandle, asset: T) -> Option<T> {
128        self.storage.insert(handle, asset)
129    }
130
131    /// Look up a processed asset by handle.
132    pub fn get(&self, handle: RawAssetHandle) -> Option<&T> {
133        self.storage.get(handle)
134    }
135
136    /// Mutably look up a processed asset by handle.
137    pub fn get_mut(&mut self, handle: RawAssetHandle) -> Option<&mut T> {
138        self.storage.get_mut(handle)
139    }
140
141    /// Remove a processed asset by handle, returning the value if it existed.
142    pub fn remove(&mut self, handle: RawAssetHandle) -> Option<T> {
143        self.storage.remove(handle)
144    }
145
146    /// Returns `true` if a processed asset exists for `handle`.
147    pub fn contains(&self, handle: RawAssetHandle) -> bool {
148        self.storage.contains_key(handle)
149    }
150}