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