wgpu_core/track/
mod.rs

1/*! Resource State and Lifetime Trackers
2
3These structures are responsible for keeping track of resource state,
4generating barriers where needednd making sure resources are kept
5alive until the trackers die.
6
7## General Architecture
8
9Tracking is some of the hottest code in the entire codebase, so the trackers
10are designed to be as cache efficient as possible. They store resource state
11in flat vectors, storing metadata SOA style, one vector per type of metadata.
12
13A lot of the tracker code is deeply unsafe, using unchecked accesses all over
14to make performance as good as possible. However, for all unsafe accesses, there
15is a corresponding debug assert the checks if that access is valid. This helps
16get bugs caught fast, while still letting users not need to pay for the bounds
17checks.
18
19In wgpu, each resource ID includes a bitfield holding an index.
20Indices are allocated and re-used, so they will always be as low as
21reasonably possible. This allows us to use IDs to index into an array
22of tracking information.
23
24## Statefulness
25
26There are two main types of trackers, stateful and stateless.
27
28Stateful trackers are for buffers and textures. They both have
29resource state attached to them which needs to be used to generate
30automatic synchronization. Because of the different requirements of
31buffers and textures, they have two separate tracking structures.
32
33Stateless trackers only store metadata and own the given resource.
34
35## Use Case
36
37Within each type of tracker, the trackers are further split into 3 different
38use cases, Bind Group, Usage Scopend a full Tracker.
39
40Bind Group trackers are just a list of different resources, their refcount,
41and how they are used. Textures are used via a selector and a usage type.
42Buffers by just a usage type. Stateless resources don't have a usage type.
43
44Usage Scope trackers are only for stateful resources. These trackers represent
45a single [`UsageScope`] in the spec. When a use is added to a usage scope,
46it is merged with all other uses of that resource in that scope. If there
47is a usage conflict, merging will fail and an error will be reported.
48
49Full trackers represent a before and after state of a resource. These
50are used for tracking on the device and on command buffers. The before
51state represents the state the resource is first used as in the command buffer,
52the after state is the state the command buffer leaves the resource in.
53These double ended buffers can then be used to generate the needed transitions
54between command buffers.
55
56## Dense Datastructure with Sparse Data
57
58This tracking system is based on having completely dense data, but trackers do
59not always contain every resource. Some resources (or even most resources) go
60unused in any given command buffer. So to help speed up the process of iterating
61through possibly thousands of resources, we use a bit vector to represent if
62a resource is in the buffer or not. This allows us extremely efficient memory
63utilizations well as being able to bail out of whole blocks of 32-64 resources
64with a single usize comparison with zero. In practice this means that merging
65partially resident buffers is extremely quick.
66
67The main advantage of this dense datastructure is that we can do merging
68of trackers in an extremely efficient fashion that results in us doing linear
69scans down a couple of buffers. CPUs and their caches absolutely eat this up.
70
71## Stateful Resource Operations
72
73All operations on stateful trackers boil down to one of four operations:
74- `insert(tracker, new_state)` adds a resource with a given state to the tracker
75  for the first time.
76- `merge(tracker, new_state)` merges this new state with the previous state, checking
77  for usage conflicts.
78- `barrier(tracker, new_state)` compares the given state to the existing state and
79  generates the needed barriers.
80- `update(tracker, new_state)` takes the given new state and overrides the old state.
81
82This allows us to compose the operations to form the various kinds of tracker merges
83that need to happen in the codebase. For each resource in the given merger, the following
84operation applies:
85
86```text
87UsageScope <- Resource = insert(scope, usage) OR merge(scope, usage)
88UsageScope <- UsageScope = insert(scope, scope) OR merge(scope, scope)
89CommandBuffer <- UsageScope = insert(buffer.start, buffer.end, scope)
90                              OR barrier(buffer.end, scope) + update(buffer.end, scope)
91Device <- CommandBuffer = insert(device.start, device.end, buffer.start, buffer.end)
92                          OR barrier(device.end, buffer.start) + update(device.end, buffer.end)
93```
94
95[`UsageScope`]: https://gpuweb.github.io/gpuweb/#programming-model-synchronization
96*/
97
98mod blas;
99mod buffer;
100mod metadata;
101mod query_set;
102mod range;
103mod stateless;
104mod texture;
105
106use crate::{
107    binding_model, command,
108    lock::{rank, Mutex},
109    pipeline,
110    resource::{self, Labeled, RawResourceAccess, ResourceErrorIdent, Trackable},
111    snatch::SnatchGuard,
112    track::blas::BlasTracker,
113};
114
115use alloc::{sync::Arc, vec::Vec};
116use bitflags::Flags;
117use core::{fmt, mem, ops};
118
119use thiserror::Error;
120
121pub(crate) use crate::track::query_set::QuerySetTracker;
122pub(crate) use buffer::{
123    BufferBindGroupState, BufferTracker, BufferUsageScope, DeviceBufferTracker,
124};
125use metadata::{ResourceMetadata, ResourceMetadataProvider};
126pub(crate) use stateless::StatelessTracker;
127pub(crate) use texture::{
128    DeviceTextureTracker, TextureTracker, TextureTrackerSetSingle, TextureUsageScope,
129    TextureViewBindGroupState,
130};
131use wgt::{
132    error::{ErrorType, WebGpuError},
133    strict_assert_ne,
134};
135
136#[repr(transparent)]
137#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
138pub(crate) struct TrackerIndex(u32);
139
140impl TrackerIndex {
141    pub fn as_usize(self) -> usize {
142        self.0 as usize
143    }
144}
145
146/// wgpu-core internally use some array-like storage for tracking resources.
147/// To that end, there needs to be a uniquely assigned index for each live resource
148/// of a certain type. This index is separate from the resource ID for various reasons:
149/// - There can be multiple resource IDs pointing the the same resource.
150/// - IDs of dead handles can be recycled while resources are internally held alive (and tracked).
151/// - The plan is to remove IDs in the long run
152///   ([#5121](https://github.com/gfx-rs/wgpu/issues/5121)).
153///
154/// In order to produce these tracker indices, there is a shared TrackerIndexAllocator
155/// per resource type. Indices have the same lifetime as the internal resource they
156/// are associated to (alloc happens when creating the resource and free is called when
157/// the resource is dropped).
158struct TrackerIndexAllocator {
159    unused: Vec<TrackerIndex>,
160    next_index: TrackerIndex,
161}
162
163impl TrackerIndexAllocator {
164    pub fn new() -> Self {
165        TrackerIndexAllocator {
166            unused: Vec::new(),
167            next_index: TrackerIndex(0),
168        }
169    }
170
171    pub fn alloc(&mut self) -> TrackerIndex {
172        if let Some(index) = self.unused.pop() {
173            return index;
174        }
175
176        let index = self.next_index;
177        self.next_index.0 += 1;
178
179        index
180    }
181
182    pub fn free(&mut self, index: TrackerIndex) {
183        self.unused.push(index);
184    }
185
186    // This is used to pre-allocate the tracker storage.
187    pub fn size(&self) -> usize {
188        self.next_index.0 as usize
189    }
190}
191
192impl fmt::Debug for TrackerIndexAllocator {
193    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
194        Ok(())
195    }
196}
197
198/// See TrackerIndexAllocator.
199#[derive(Debug)]
200pub(crate) struct SharedTrackerIndexAllocator {
201    inner: Mutex<TrackerIndexAllocator>,
202}
203
204impl SharedTrackerIndexAllocator {
205    pub fn new() -> Self {
206        SharedTrackerIndexAllocator {
207            inner: Mutex::new(
208                rank::SHARED_TRACKER_INDEX_ALLOCATOR_INNER,
209                TrackerIndexAllocator::new(),
210            ),
211        }
212    }
213
214    pub fn alloc(&self) -> TrackerIndex {
215        self.inner.lock().alloc()
216    }
217
218    pub fn free(&self, index: TrackerIndex) {
219        self.inner.lock().free(index);
220    }
221
222    pub fn size(&self) -> usize {
223        self.inner.lock().size()
224    }
225}
226
227pub(crate) struct TrackerIndexAllocators {
228    pub buffers: Arc<SharedTrackerIndexAllocator>,
229    pub textures: Arc<SharedTrackerIndexAllocator>,
230    pub external_textures: Arc<SharedTrackerIndexAllocator>,
231    pub samplers: Arc<SharedTrackerIndexAllocator>,
232    pub bind_groups: Arc<SharedTrackerIndexAllocator>,
233    pub compute_pipelines: Arc<SharedTrackerIndexAllocator>,
234    pub render_pipelines: Arc<SharedTrackerIndexAllocator>,
235    pub bundles: Arc<SharedTrackerIndexAllocator>,
236    pub query_sets: Arc<SharedTrackerIndexAllocator>,
237    pub blas_s: Arc<SharedTrackerIndexAllocator>,
238    pub tlas_s: Arc<SharedTrackerIndexAllocator>,
239}
240
241impl TrackerIndexAllocators {
242    pub fn new() -> Self {
243        TrackerIndexAllocators {
244            buffers: Arc::new(SharedTrackerIndexAllocator::new()),
245            textures: Arc::new(SharedTrackerIndexAllocator::new()),
246            external_textures: Arc::new(SharedTrackerIndexAllocator::new()),
247            samplers: Arc::new(SharedTrackerIndexAllocator::new()),
248            bind_groups: Arc::new(SharedTrackerIndexAllocator::new()),
249            compute_pipelines: Arc::new(SharedTrackerIndexAllocator::new()),
250            render_pipelines: Arc::new(SharedTrackerIndexAllocator::new()),
251            bundles: Arc::new(SharedTrackerIndexAllocator::new()),
252            query_sets: Arc::new(SharedTrackerIndexAllocator::new()),
253            blas_s: Arc::new(SharedTrackerIndexAllocator::new()),
254            tlas_s: Arc::new(SharedTrackerIndexAllocator::new()),
255        }
256    }
257}
258
259/// A structure containing all the information about a particular resource
260/// transition. User code should be able to generate a pipeline barrier
261/// based on the contents.
262#[derive(Debug, PartialEq)]
263pub(crate) struct PendingTransition<S: ResourceUses> {
264    pub id: u32,
265    pub selector: S::Selector,
266    pub usage: hal::StateTransition<S>,
267}
268
269pub(crate) type PendingTransitionList = Vec<PendingTransition<wgt::TextureUses>>;
270
271impl PendingTransition<wgt::BufferUses> {
272    /// Produce the hal barrier corresponding to the transition.
273    pub fn into_hal<'a>(
274        self,
275        buf: &'a resource::Buffer,
276        snatch_guard: &'a SnatchGuard<'a>,
277    ) -> hal::BufferBarrier<'a, dyn hal::DynBuffer> {
278        let buffer = buf.raw(snatch_guard).expect("Buffer is destroyed");
279        hal::BufferBarrier {
280            buffer,
281            usage: self.usage,
282        }
283    }
284}
285
286impl PendingTransition<wgt::TextureUses> {
287    /// Produce the hal barrier corresponding to the transition.
288    pub fn into_hal(
289        self,
290        texture: &dyn hal::DynTexture,
291    ) -> hal::TextureBarrier<'_, dyn hal::DynTexture> {
292        // These showing up in a barrier is always a bug
293        strict_assert_ne!(self.usage.from, wgt::TextureUses::UNKNOWN);
294        strict_assert_ne!(self.usage.to, wgt::TextureUses::UNKNOWN);
295
296        let mip_count = self.selector.mips.end - self.selector.mips.start;
297        strict_assert_ne!(mip_count, 0);
298        let layer_count = self.selector.layers.end - self.selector.layers.start;
299        strict_assert_ne!(layer_count, 0);
300
301        hal::TextureBarrier {
302            texture,
303            range: wgt::ImageSubresourceRange {
304                aspect: wgt::TextureAspect::All,
305                base_mip_level: self.selector.mips.start,
306                mip_level_count: Some(mip_count),
307                base_array_layer: self.selector.layers.start,
308                array_layer_count: Some(layer_count),
309            },
310            usage: self.usage,
311        }
312    }
313}
314
315/// The uses that a resource or subresource can be in.
316pub(crate) trait ResourceUses:
317    fmt::Debug + ops::BitAnd<Output = Self> + ops::BitOr<Output = Self> + PartialEq + Sized + Copy
318{
319    /// All flags that are exclusive.
320    const EXCLUSIVE: Self;
321
322    /// The selector used by this resource.
323    type Selector: fmt::Debug;
324
325    /// Turn the resource into a pile of bits.
326    fn bits(self) -> u16;
327    /// Returns true if any of the uses are exclusive.
328    fn any_exclusive(self) -> bool;
329}
330
331/// Returns true if the given states violates the usage scope rule
332/// of any(inclusive) XOR one(exclusive)
333fn invalid_resource_state<T: ResourceUses>(state: T) -> bool {
334    // Is power of two also means "is one bit set". We check for this as if
335    // we're in any exclusive state, we must only be in a single state.
336    state.any_exclusive() && !state.bits().is_power_of_two()
337}
338
339/// Returns true if the transition from one state to another does not require
340/// a barrier.
341fn skip_barrier<F: Flags>(old_state: F, ordered_uses_mask: F, new_state: F) -> bool {
342    // If the state didn't change and all the usages are ordered, the hardware
343    // will guarantee the order of accesses, so we do not need to issue a barrier at all
344    old_state.bits() == new_state.bits() && ordered_uses_mask.contains(old_state)
345}
346
347#[derive(Clone, Debug, Error)]
348pub enum ResourceUsageCompatibilityError {
349    #[error("Attempted to use {res} with {invalid_use}.")]
350    Buffer {
351        res: ResourceErrorIdent,
352        invalid_use: InvalidUse<wgt::BufferUses>,
353    },
354    #[error(
355        "Attempted to use {res} (mips {mip_levels:?} layers {array_layers:?}) with {invalid_use}."
356    )]
357    Texture {
358        res: ResourceErrorIdent,
359        mip_levels: ops::Range<u32>,
360        array_layers: ops::Range<u32>,
361        invalid_use: InvalidUse<wgt::TextureUses>,
362    },
363}
364
365impl WebGpuError for ResourceUsageCompatibilityError {
366    fn webgpu_error_type(&self) -> ErrorType {
367        ErrorType::Validation
368    }
369}
370
371impl ResourceUsageCompatibilityError {
372    fn from_buffer(
373        buffer: &resource::Buffer,
374        current_state: wgt::BufferUses,
375        new_state: wgt::BufferUses,
376    ) -> Self {
377        Self::Buffer {
378            res: buffer.error_ident(),
379            invalid_use: InvalidUse {
380                current_state,
381                new_state,
382            },
383        }
384    }
385
386    fn from_texture(
387        texture: &resource::Texture,
388        selector: wgt::TextureSelector,
389        current_state: wgt::TextureUses,
390        new_state: wgt::TextureUses,
391    ) -> Self {
392        Self::Texture {
393            res: texture.error_ident(),
394            mip_levels: selector.mips,
395            array_layers: selector.layers,
396            invalid_use: InvalidUse {
397                current_state,
398                new_state,
399            },
400        }
401    }
402}
403
404/// Pretty print helper that shows helpful descriptions of a conflicting usage.
405#[derive(Clone, Debug, Eq, PartialEq)]
406pub struct InvalidUse<T> {
407    current_state: T,
408    new_state: T,
409}
410
411impl<T: ResourceUses> fmt::Display for InvalidUse<T> {
412    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413        let current = self.current_state;
414        let new = self.new_state;
415
416        let current_exclusive = current & T::EXCLUSIVE;
417        let new_exclusive = new & T::EXCLUSIVE;
418
419        let exclusive = current_exclusive | new_exclusive;
420
421        // The text starts with "tried to use X resource with {self}"
422        write!(
423            f,
424            "conflicting usages. Current usage {current:?} and new usage {new:?}. \
425            {exclusive:?} is an exclusive usage and cannot be used with any other \
426            usages within the usage scope (renderpass or compute dispatch)"
427        )
428    }
429}
430
431/// All the usages that a bind group contains. The uses are not deduplicated in any way
432/// and may include conflicting uses. This is fully compliant by the WebGPU spec.
433///
434/// All bind group states are sorted by their ID so that when adding to a tracker,
435/// they are added in the most efficient order possible (ascending order).
436#[derive(Debug)]
437pub(crate) struct BindGroupStates {
438    pub buffers: BufferBindGroupState,
439    pub views: TextureViewBindGroupState,
440    pub external_textures: StatelessTracker<resource::ExternalTexture>,
441    pub samplers: StatelessTracker<resource::Sampler>,
442    pub acceleration_structures: StatelessTracker<resource::Tlas>,
443}
444
445impl BindGroupStates {
446    pub fn new() -> Self {
447        Self {
448            buffers: BufferBindGroupState::new(),
449            views: TextureViewBindGroupState::new(),
450            external_textures: StatelessTracker::new(),
451            samplers: StatelessTracker::new(),
452            acceleration_structures: StatelessTracker::new(),
453        }
454    }
455
456    /// Optimize the bind group states by sorting them by ID.
457    ///
458    /// When this list of states is merged into a tracker, the memory
459    /// accesses will be in a constant ascending order.
460    pub fn optimize(&mut self) {
461        self.buffers.optimize();
462        // Views are stateless, however, `TextureViewBindGroupState`
463        // is special as it will be merged with other texture trackers.
464        self.views.optimize();
465        // Samplers and Tlas's are stateless and don't need to be optimized
466        // since the tracker is never merged with any other tracker.
467    }
468}
469
470/// This is a render bundle specific usage scope. It includes stateless resources
471/// that are not normally included in a usage scope, but are used by render bundles
472/// and need to be owned by the render bundles.
473#[derive(Debug)]
474pub(crate) struct RenderBundleScope {
475    pub buffers: BufferUsageScope,
476    pub textures: TextureUsageScope,
477    // Don't need to track views and samplers, they are never used directly, only by bind groups.
478    pub bind_groups: StatelessTracker<binding_model::BindGroup>,
479    pub render_pipelines: StatelessTracker<pipeline::RenderPipeline>,
480}
481
482impl RenderBundleScope {
483    /// Create the render bundle scope and pull the maximum IDs from the hubs.
484    pub fn new() -> Self {
485        Self {
486            buffers: BufferUsageScope::default(),
487            textures: TextureUsageScope::default(),
488            bind_groups: StatelessTracker::new(),
489            render_pipelines: StatelessTracker::new(),
490        }
491    }
492
493    /// Merge the inner contents of a bind group into the render bundle tracker.
494    ///
495    /// Only stateful things are merged in herell other resources are owned
496    /// indirectly by the bind group.
497    ///
498    /// # Safety
499    ///
500    /// The maximum ID given by each bind group resource must be less than the
501    /// length of the storage given at the call to `new`.
502    pub unsafe fn merge_bind_group(
503        &mut self,
504        bind_group: &BindGroupStates,
505    ) -> Result<(), ResourceUsageCompatibilityError> {
506        unsafe { self.buffers.merge_bind_group(&bind_group.buffers)? };
507        unsafe { self.textures.merge_bind_group(&bind_group.views)? };
508
509        Ok(())
510    }
511}
512
513/// A pool for storing the memory used by [`UsageScope`]s. We take and store this memory when the
514/// scope is dropped to avoid reallocating. The memory required only grows and allocation cost is
515/// significant when a large number of resources have been used.
516pub(crate) type UsageScopePool = Mutex<Vec<(BufferUsageScope, TextureUsageScope)>>;
517
518/// A usage scope tracker. Only needs to store stateful resources as stateless
519/// resources cannot possibly have a usage conflict.
520#[derive(Debug)]
521pub(crate) struct UsageScope<'a> {
522    pub pool: &'a UsageScopePool,
523    pub buffers: BufferUsageScope,
524    pub textures: TextureUsageScope,
525}
526
527impl<'a> Drop for UsageScope<'a> {
528    fn drop(&mut self) {
529        // clear vecs and push into pool
530        self.buffers.clear();
531        self.textures.clear();
532        self.pool
533            .lock()
534            .push((mem::take(&mut self.buffers), mem::take(&mut self.textures)));
535    }
536}
537
538impl UsageScope<'static> {
539    pub fn new_pooled<'d>(
540        pool: &'d UsageScopePool,
541        tracker_indices: &TrackerIndexAllocators,
542        ordered_buffer_usages: wgt::BufferUses,
543        ordered_texture_usages: wgt::TextureUses,
544    ) -> UsageScope<'d> {
545        let pooled = pool.lock().pop().unwrap_or_default();
546
547        let mut scope = UsageScope::<'d> {
548            pool,
549            buffers: pooled.0,
550            textures: pooled.1,
551        };
552
553        scope.buffers.set_size(tracker_indices.buffers.size());
554        scope.buffers.set_ordered_uses_mask(ordered_buffer_usages);
555        scope.textures.set_size(tracker_indices.textures.size());
556        scope.textures.set_ordered_uses_mask(ordered_texture_usages);
557        scope
558    }
559}
560
561impl<'a> UsageScope<'a> {
562    /// Merge the inner contents of a bind group into the usage scope.
563    ///
564    /// Only stateful things are merged in herell other resources are owned
565    /// indirectly by the bind group.
566    ///
567    /// # Safety
568    ///
569    /// The maximum ID given by each bind group resource must be less than the
570    /// length of the storage given at the call to `new`.
571    pub unsafe fn merge_bind_group(
572        &mut self,
573        bind_group: &BindGroupStates,
574    ) -> Result<(), ResourceUsageCompatibilityError> {
575        unsafe {
576            self.buffers.merge_bind_group(&bind_group.buffers)?;
577            self.textures.merge_bind_group(&bind_group.views)?;
578        }
579
580        Ok(())
581    }
582
583    /// Merge the inner contents of a bind group into the usage scope.
584    ///
585    /// Only stateful things are merged in herell other resources are owned
586    /// indirectly by a bind group or are merged directly into the command buffer tracker.
587    ///
588    /// # Safety
589    ///
590    /// The maximum ID given by each bind group resource must be less than the
591    /// length of the storage given at the call to `new`.
592    pub unsafe fn merge_render_bundle(
593        &mut self,
594        render_bundle: &RenderBundleScope,
595    ) -> Result<(), ResourceUsageCompatibilityError> {
596        self.buffers.merge_usage_scope(&render_bundle.buffers)?;
597        self.textures.merge_usage_scope(&render_bundle.textures)?;
598
599        Ok(())
600    }
601}
602
603/// A tracker used by Device.
604pub(crate) struct DeviceTracker {
605    pub buffers: DeviceBufferTracker,
606    pub textures: DeviceTextureTracker,
607}
608
609impl DeviceTracker {
610    pub fn new(
611        ordered_buffer_usages: wgt::BufferUses,
612        ordered_texture_usages: wgt::TextureUses,
613    ) -> Self {
614        Self {
615            buffers: DeviceBufferTracker::new(ordered_buffer_usages),
616            textures: DeviceTextureTracker::new(ordered_texture_usages),
617        }
618    }
619}
620
621/// A full double sided tracker used by CommandBuffers.
622pub(crate) struct Tracker {
623    /// Buffers used within this command buffer.
624    ///
625    /// For compute passes, this only includes buffers actually used by the
626    /// pipeline (contrast with the `bind_groups` member).
627    pub buffers: BufferTracker,
628
629    /// Textures used within this command buffer.
630    ///
631    /// For compute passes, this only includes textures actually used by the
632    /// pipeline (contrast with the `bind_groups` member).
633    pub textures: TextureTracker,
634
635    pub blas_s: BlasTracker,
636    pub tlas_s: StatelessTracker<resource::Tlas>,
637    pub views: StatelessTracker<resource::TextureView>,
638
639    /// Contains all bind groups that were passed in any call to
640    /// `set_bind_group` on the encoder.
641    ///
642    /// WebGPU requires that submission fails if any resource in any of these
643    /// bind groups is destroyed, even if the resource is not actually used by
644    /// the pipeline (e.g. because the pipeline does not use the bound slot, or
645    /// because the bind group was replaced by a subsequent call to
646    /// `set_bind_group`).
647    pub bind_groups: StatelessTracker<binding_model::BindGroup>,
648
649    pub compute_pipelines: StatelessTracker<pipeline::ComputePipeline>,
650    pub render_pipelines: StatelessTracker<pipeline::RenderPipeline>,
651    pub bundles: StatelessTracker<command::RenderBundle>,
652    pub query_sets: QuerySetTracker,
653}
654
655impl Tracker {
656    pub fn new(
657        ordered_buffer_usages: wgt::BufferUses,
658        ordered_texture_usages: wgt::TextureUses,
659    ) -> Self {
660        Self {
661            buffers: BufferTracker::new(ordered_buffer_usages),
662            textures: TextureTracker::new(ordered_texture_usages),
663            blas_s: BlasTracker::new(),
664            tlas_s: StatelessTracker::new(),
665            views: StatelessTracker::new(),
666            bind_groups: StatelessTracker::new(),
667            compute_pipelines: StatelessTracker::new(),
668            render_pipelines: StatelessTracker::new(),
669            bundles: StatelessTracker::new(),
670            query_sets: QuerySetTracker::new(),
671        }
672    }
673
674    /// Iterates through all resources in the given bind group and adopts
675    /// the state given for those resources in the UsageScope. It also
676    /// removes all touched resources from the usage scope.
677    ///
678    /// If a transition is needed to get the resources into the needed
679    /// state, those transitions are stored within the tracker. A
680    /// subsequent call to [`BufferTracker::drain_transitions`] or
681    /// [`TextureTracker::drain_transitions`] is needed to get those transitions.
682    ///
683    /// This is a really funky method used by Compute Passes to generate
684    /// barriers after a call to dispatch without needing to iterate
685    /// over all elements in the usage scope. We use each the
686    /// bind group as a source of which IDs to look at. The bind groups
687    /// must have first been added to the usage scope.
688    ///
689    /// Only stateful things are merged in here, all other resources are owned
690    /// indirectly by the bind group.
691    ///
692    /// # Panics
693    ///
694    /// If a resource in the `bind_group` is not found in the usage scope.
695    pub fn set_and_remove_from_usage_scope_sparse(
696        &mut self,
697        scope: &mut UsageScope,
698        bind_group: &BindGroupStates,
699    ) {
700        self.buffers.set_and_remove_from_usage_scope_sparse(
701            &mut scope.buffers,
702            bind_group
703                .buffers
704                .used_resources()
705                .map(|b| b.tracker_index()),
706        );
707        self.textures
708            .set_and_remove_from_usage_scope_sparse(&mut scope.textures, &bind_group.views);
709    }
710}