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