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}