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}