wgpu_core/
storage.rs

1use std::{marker::PhantomData, mem, ops};
2
3use wgt::Backend;
4
5use crate::{id, Epoch, Index};
6
7/// An entry in a `Storage::map` table.
8#[derive(Debug)]
9pub(crate) enum Element<T> {
10    /// There are no live ids with this index.
11    Vacant,
12
13    /// There is one live id with this index, allocated at the given
14    /// epoch.
15    Occupied(T, Epoch),
16
17    /// Like `Occupied`, but an error occurred when creating the
18    /// resource.
19    ///
20    /// The given `String` is the resource's descriptor label.
21    Error(Epoch, String),
22}
23
24#[derive(Clone, Debug, Default)]
25pub struct StorageReport {
26    pub num_occupied: usize,
27    pub num_vacant: usize,
28    pub num_error: usize,
29    pub element_size: usize,
30}
31
32impl StorageReport {
33    pub fn is_empty(&self) -> bool {
34        self.num_occupied + self.num_vacant + self.num_error == 0
35    }
36}
37
38#[derive(Clone, Debug)]
39pub(crate) struct InvalidId;
40
41/// A table of `T` values indexed by the id type `I`.
42///
43/// The table is represented as a vector indexed by the ids' index
44/// values, so you should use an id allocator like `IdentityManager`
45/// that keeps the index values dense and close to zero.
46#[derive(Debug)]
47pub struct Storage<T, I: id::TypedId> {
48    pub(crate) map: Vec<Element<T>>,
49    pub(crate) kind: &'static str,
50    pub(crate) _phantom: PhantomData<I>,
51}
52
53impl<T, I: id::TypedId> ops::Index<id::Valid<I>> for Storage<T, I> {
54    type Output = T;
55    fn index(&self, id: id::Valid<I>) -> &T {
56        self.get(id.0).unwrap()
57    }
58}
59
60impl<T, I: id::TypedId> ops::IndexMut<id::Valid<I>> for Storage<T, I> {
61    fn index_mut(&mut self, id: id::Valid<I>) -> &mut T {
62        self.get_mut(id.0).unwrap()
63    }
64}
65
66impl<T, I: id::TypedId> Storage<T, I> {
67    pub(crate) fn contains(&self, id: I) -> bool {
68        let (index, epoch, _) = id.unzip();
69        match self.map.get(index as usize) {
70            Some(&Element::Vacant) => false,
71            Some(&Element::Occupied(_, storage_epoch) | &Element::Error(storage_epoch, _)) => {
72                storage_epoch == epoch
73            }
74            None => false,
75        }
76    }
77
78    /// Attempts to get a reference to an item behind a potentially invalid ID.
79    ///
80    /// Returns [`None`] if there is an epoch mismatch, or the entry is empty.
81    ///
82    /// This function is primarily intended for the `as_hal` family of functions
83    /// where you may need to fallibly get a object backed by an id that could
84    /// be in a different hub.
85    pub(crate) fn try_get(&self, id: I) -> Result<Option<&T>, InvalidId> {
86        let (index, epoch, _) = id.unzip();
87        let (result, storage_epoch) = match self.map.get(index as usize) {
88            Some(&Element::Occupied(ref v, epoch)) => (Ok(Some(v)), epoch),
89            Some(&Element::Vacant) => return Ok(None),
90            Some(&Element::Error(epoch, ..)) => (Err(InvalidId), epoch),
91            None => return Err(InvalidId),
92        };
93        assert_eq!(
94            epoch, storage_epoch,
95            "{}[{}] is no longer alive",
96            self.kind, index
97        );
98        result
99    }
100
101    /// Get a reference to an item behind a potentially invalid ID.
102    /// Panics if there is an epoch mismatch, or the entry is empty.
103    pub(crate) fn get(&self, id: I) -> Result<&T, InvalidId> {
104        let (index, epoch, _) = id.unzip();
105        let (result, storage_epoch) = match self.map.get(index as usize) {
106            Some(&Element::Occupied(ref v, epoch)) => (Ok(v), epoch),
107            Some(&Element::Vacant) => panic!("{}[{}] does not exist", self.kind, index),
108            Some(&Element::Error(epoch, ..)) => (Err(InvalidId), epoch),
109            None => return Err(InvalidId),
110        };
111        assert_eq!(
112            epoch, storage_epoch,
113            "{}[{}] is no longer alive",
114            self.kind, index
115        );
116        result
117    }
118
119    /// Get a mutable reference to an item behind a potentially invalid ID.
120    /// Panics if there is an epoch mismatch, or the entry is empty.
121    pub(crate) fn get_mut(&mut self, id: I) -> Result<&mut T, InvalidId> {
122        let (index, epoch, _) = id.unzip();
123        let (result, storage_epoch) = match self.map.get_mut(index as usize) {
124            Some(&mut Element::Occupied(ref mut v, epoch)) => (Ok(v), epoch),
125            Some(&mut Element::Vacant) | None => panic!("{}[{}] does not exist", self.kind, index),
126            Some(&mut Element::Error(epoch, ..)) => (Err(InvalidId), epoch),
127        };
128        assert_eq!(
129            epoch, storage_epoch,
130            "{}[{}] is no longer alive",
131            self.kind, index
132        );
133        result
134    }
135
136    pub(crate) unsafe fn get_unchecked(&self, id: u32) -> &T {
137        match self.map[id as usize] {
138            Element::Occupied(ref v, _) => v,
139            Element::Vacant => panic!("{}[{}] does not exist", self.kind, id),
140            Element::Error(_, _) => panic!(""),
141        }
142    }
143
144    pub(crate) fn label_for_invalid_id(&self, id: I) -> &str {
145        let (index, _, _) = id.unzip();
146        match self.map.get(index as usize) {
147            Some(&Element::Error(_, ref label)) => label,
148            _ => "",
149        }
150    }
151
152    fn insert_impl(&mut self, index: usize, element: Element<T>) {
153        if index >= self.map.len() {
154            self.map.resize_with(index + 1, || Element::Vacant);
155        }
156        match std::mem::replace(&mut self.map[index], element) {
157            Element::Vacant => {}
158            _ => panic!("Index {index:?} is already occupied"),
159        }
160    }
161
162    pub(crate) fn insert(&mut self, id: I, value: T) {
163        let (index, epoch, _) = id.unzip();
164        self.insert_impl(index as usize, Element::Occupied(value, epoch))
165    }
166
167    pub(crate) fn insert_error(&mut self, id: I, label: &str) {
168        let (index, epoch, _) = id.unzip();
169        self.insert_impl(index as usize, Element::Error(epoch, label.to_string()))
170    }
171
172    pub(crate) fn force_replace(&mut self, id: I, value: T) {
173        let (index, epoch, _) = id.unzip();
174        self.map[index as usize] = Element::Occupied(value, epoch);
175    }
176
177    pub(crate) fn remove(&mut self, id: I) -> Option<T> {
178        let (index, epoch, _) = id.unzip();
179        match std::mem::replace(&mut self.map[index as usize], Element::Vacant) {
180            Element::Occupied(value, storage_epoch) => {
181                assert_eq!(epoch, storage_epoch);
182                Some(value)
183            }
184            Element::Error(..) => None,
185            Element::Vacant => panic!("Cannot remove a vacant resource"),
186        }
187    }
188
189    // Prevents panic on out of range access, allows Vacant elements.
190    pub(crate) fn _try_remove(&mut self, id: I) -> Option<T> {
191        let (index, epoch, _) = id.unzip();
192        if index as usize >= self.map.len() {
193            None
194        } else if let Element::Occupied(value, storage_epoch) =
195            std::mem::replace(&mut self.map[index as usize], Element::Vacant)
196        {
197            assert_eq!(epoch, storage_epoch);
198            Some(value)
199        } else {
200            None
201        }
202    }
203
204    pub(crate) fn iter(&self, backend: Backend) -> impl Iterator<Item = (I, &T)> {
205        self.map
206            .iter()
207            .enumerate()
208            .filter_map(move |(index, x)| match *x {
209                Element::Occupied(ref value, storage_epoch) => {
210                    Some((I::zip(index as Index, storage_epoch, backend), value))
211                }
212                _ => None,
213            })
214    }
215
216    pub(crate) fn len(&self) -> usize {
217        self.map.len()
218    }
219
220    pub(crate) fn generate_report(&self) -> StorageReport {
221        let mut report = StorageReport {
222            element_size: mem::size_of::<T>(),
223            ..Default::default()
224        };
225        for element in self.map.iter() {
226            match *element {
227                Element::Occupied(..) => report.num_occupied += 1,
228                Element::Vacant => report.num_vacant += 1,
229                Element::Error(..) => report.num_error += 1,
230            }
231        }
232        report
233    }
234}