wgpu_core/track/
metadata.rs

1//! The `ResourceMetadata` type.
2
3use alloc::vec::Vec;
4
5use bit_vec::BitVec;
6use wgt::strict_assert;
7
8/// A set of resources, holding a `Arc<T>` and epoch for each member.
9///
10/// Testing for membership is fast, and iterating over members is
11/// reasonably fast in practice. Storage consumption is proportional
12/// to the largest id index of any member, not to the number of
13/// members, but a bit vector tracks occupancy, so iteration touches
14/// only occupied elements.
15#[derive(Debug)]
16pub(super) struct ResourceMetadata<T: Clone> {
17    /// If the resource with index `i` is a member, `owned[i]` is `true`.
18    owned: BitVec<usize>,
19
20    /// A vector holding clones of members' `T`s.
21    resources: Vec<Option<T>>,
22}
23
24impl<T: Clone> ResourceMetadata<T> {
25    pub(super) fn new() -> Self {
26        Self {
27            owned: BitVec::default(),
28            resources: Vec::new(),
29        }
30    }
31
32    pub(super) fn set_size(&mut self, size: usize) {
33        self.resources.resize(size, None);
34        resize_bitvec(&mut self.owned, size);
35    }
36
37    pub(super) fn clear(&mut self) {
38        self.resources.clear();
39        self.owned.clear();
40    }
41
42    /// Ensures a given index is in bounds for all arrays and does
43    /// sanity checks of the presence of a refcount.
44    ///
45    /// In release mode this function is completely empty and is removed.
46    pub(super) fn tracker_assert_in_bounds(&self, index: usize) {
47        strict_assert!(index < self.owned.len());
48        strict_assert!(index < self.resources.len());
49        strict_assert!(if self.contains(index) {
50            self.resources[index].is_some()
51        } else {
52            true
53        });
54    }
55
56    /// Returns true if the tracker owns no resources.
57    ///
58    /// This is a O(n) operation.
59    pub(super) fn is_empty(&self) -> bool {
60        !self.owned.any()
61    }
62
63    /// Returns true if the set contains the resource with the given index.
64    pub(super) fn contains(&self, index: usize) -> bool {
65        self.owned.get(index).unwrap_or(false)
66    }
67
68    /// Returns true if the set contains the resource with the given index.
69    ///
70    /// # Safety
71    ///
72    /// The given `index` must be in bounds for this `ResourceMetadata`'s
73    /// existing tables. See `tracker_assert_in_bounds`.
74    #[inline(always)]
75    pub(super) unsafe fn contains_unchecked(&self, index: usize) -> bool {
76        unsafe { self.owned.get(index).unwrap_unchecked() }
77    }
78
79    /// Insert a resource into the set.
80    ///
81    /// Add the resource with the given index, epoch, and reference count to the
82    /// set.
83    ///
84    /// Returns a reference to the newly inserted resource.
85    /// (This allows avoiding a clone/reference count increase in many cases.)
86    ///
87    /// # Safety
88    ///
89    /// The given `index` must be in bounds for this `ResourceMetadata`'s
90    /// existing tables. See `tracker_assert_in_bounds`.
91    #[inline(always)]
92    pub(super) unsafe fn insert(&mut self, index: usize, resource: T) -> &T {
93        self.owned.set(index, true);
94        let resource_dst = unsafe { self.resources.get_unchecked_mut(index) };
95        resource_dst.insert(resource)
96    }
97
98    /// Get the resource with the given index.
99    ///
100    /// # Safety
101    ///
102    /// The given `index` must be in bounds for this `ResourceMetadata`'s
103    /// existing tables. See `tracker_assert_in_bounds`.
104    #[inline(always)]
105    pub(super) unsafe fn get_resource_unchecked(&self, index: usize) -> &T {
106        unsafe {
107            self.resources
108                .get_unchecked(index)
109                .as_ref()
110                .unwrap_unchecked()
111        }
112    }
113
114    /// Returns an iterator over the resources owned by `self`.
115    pub(super) fn owned_resources(&self) -> impl Iterator<Item = &T> + '_ {
116        if !self.owned.is_empty() {
117            self.tracker_assert_in_bounds(self.owned.len() - 1)
118        };
119        iterate_bitvec_indices(&self.owned).map(move |index| {
120            let resource = unsafe { self.resources.get_unchecked(index) };
121            resource.as_ref().unwrap()
122        })
123    }
124
125    /// Returns an iterator over the indices of all resources owned by `self`.
126    pub(super) fn owned_indices(&self) -> impl Iterator<Item = usize> + '_ {
127        if !self.owned.is_empty() {
128            self.tracker_assert_in_bounds(self.owned.len() - 1)
129        };
130        iterate_bitvec_indices(&self.owned)
131    }
132
133    /// Remove the resource with the given index from the set.
134    pub(super) unsafe fn remove(&mut self, index: usize) {
135        unsafe {
136            *self.resources.get_unchecked_mut(index) = None;
137        }
138        self.owned.set(index, false);
139    }
140}
141
142/// A source of resource metadata.
143///
144/// This is used to abstract over the various places
145/// trackers can get new resource metadata from.
146pub(super) enum ResourceMetadataProvider<'a, T: Clone> {
147    /// Comes directly from explicit values.
148    Direct { resource: &'a T },
149    /// Comes from another metadata tracker.
150    Indirect { metadata: &'a ResourceMetadata<T> },
151}
152impl<T: Clone> ResourceMetadataProvider<'_, T> {
153    /// Get a reference to the resource from this.
154    ///
155    /// # Safety
156    ///
157    /// - The index must be in bounds of the metadata tracker if this uses an indirect source.
158    #[inline(always)]
159    pub(super) unsafe fn get(&self, index: usize) -> &T {
160        match self {
161            ResourceMetadataProvider::Direct { resource } => resource,
162            ResourceMetadataProvider::Indirect { metadata } => {
163                metadata.tracker_assert_in_bounds(index);
164                {
165                    let resource = unsafe { metadata.resources.get_unchecked(index) }.as_ref();
166                    unsafe { resource.unwrap_unchecked() }
167                }
168            }
169        }
170    }
171}
172
173/// Resizes the given bitvec to the given size. I'm not sure why this is hard to do but it is.
174fn resize_bitvec<B: bit_vec::BitBlock>(vec: &mut BitVec<B>, size: usize) {
175    let owned_size_to_grow = size.checked_sub(vec.len());
176    if let Some(delta) = owned_size_to_grow {
177        if delta != 0 {
178            vec.grow(delta, false);
179        }
180    } else {
181        vec.truncate(size);
182    }
183}
184
185/// Produces an iterator that yields the indexes of all bits that are set in the bitvec.
186///
187/// Will skip entire usize's worth of bits if they are all false.
188fn iterate_bitvec_indices(ownership: &BitVec<usize>) -> impl Iterator<Item = usize> + '_ {
189    const BITS_PER_BLOCK: usize = usize::BITS as usize;
190
191    let size = ownership.len();
192
193    ownership
194        .blocks()
195        .enumerate()
196        .filter(|&(_, word)| word != 0)
197        .flat_map(move |(word_index, mut word)| {
198            let bit_start = word_index * BITS_PER_BLOCK;
199            let bit_end = (bit_start + BITS_PER_BLOCK).min(size);
200
201            (bit_start..bit_end).filter(move |_| {
202                let active = word & 0b1 != 0;
203                word >>= 1;
204
205                active
206            })
207        })
208}