wgpu_core/track/
buffer.rs

1//! Buffer Trackers
2//!
3//! Buffers are represented by a single state for the whole resource,
4//! a 16 bit bitflag of buffer usages. Because there is only ever
5//! one subresource, they have no selector.
6
7use alloc::{
8    sync::{Arc, Weak},
9    vec::Vec,
10};
11
12use hal::BufferBarrier;
13use wgt::{strict_assert, strict_assert_eq, BufferUses};
14
15use super::{PendingTransition, TrackerIndex};
16use crate::{
17    resource::{Buffer, Trackable},
18    snatch::SnatchGuard,
19    track::{
20        invalid_resource_state, skip_barrier, ResourceMetadata, ResourceMetadataProvider,
21        ResourceUsageCompatibilityError, ResourceUses,
22    },
23};
24
25impl ResourceUses for BufferUses {
26    const EXCLUSIVE: Self = Self::EXCLUSIVE;
27
28    type Selector = ();
29
30    fn bits(self) -> u16 {
31        Self::bits(&self)
32    }
33
34    fn any_exclusive(self) -> bool {
35        self.intersects(Self::EXCLUSIVE)
36    }
37}
38
39/// Stores a bind group's buffers + their usages (within the bind group).
40#[derive(Debug)]
41pub(crate) struct BufferBindGroupState {
42    buffers: Vec<(Arc<Buffer>, BufferUses)>,
43}
44impl BufferBindGroupState {
45    pub fn new() -> Self {
46        Self {
47            buffers: Vec::new(),
48        }
49    }
50
51    /// Optimize the buffer bind group state by sorting it by ID.
52    ///
53    /// When this list of states is merged into a tracker, the memory
54    /// accesses will be in a constant ascending order.
55    pub(crate) fn optimize(&mut self) {
56        self.buffers
57            .sort_unstable_by_key(|(b, _)| b.tracker_index());
58    }
59
60    /// Returns an iterator over the tracked buffers. May contain duplicates.
61    pub fn used_resources(&self) -> impl Iterator<Item = &Arc<Buffer>> {
62        self.buffers.iter().map(|(b, _)| b)
63    }
64
65    /// Adds the given resource with the given state.
66    pub fn insert_single(&mut self, buffer: Arc<Buffer>, state: BufferUses) {
67        self.buffers.push((buffer, state));
68    }
69}
70
71/// Stores all buffer state within a single usage scope.
72#[derive(Debug)]
73pub(crate) struct BufferUsageScope {
74    state: Vec<BufferUses>,
75    metadata: ResourceMetadata<Arc<Buffer>>,
76    ordered_uses_mask: BufferUses,
77}
78
79impl Default for BufferUsageScope {
80    fn default() -> Self {
81        Self {
82            state: Vec::new(),
83            metadata: ResourceMetadata::new(),
84            ordered_uses_mask: BufferUses::empty(),
85        }
86    }
87}
88
89impl BufferUsageScope {
90    fn tracker_assert_in_bounds(&self, index: usize) {
91        strict_assert!(index < self.state.len());
92        self.metadata.tracker_assert_in_bounds(index);
93    }
94    pub fn clear(&mut self) {
95        self.state.clear();
96        self.metadata.clear();
97    }
98
99    /// Sets the size of all the vectors inside the tracker.
100    ///
101    /// Must be called with the highest possible Buffer ID before
102    /// all unsafe functions are called.
103    pub fn set_size(&mut self, size: usize) {
104        self.state.resize(size, BufferUses::empty());
105        self.metadata.set_size(size);
106    }
107
108    pub fn set_ordered_uses_mask(&mut self, ordered_uses_mask: BufferUses) {
109        self.ordered_uses_mask = ordered_uses_mask;
110    }
111
112    /// Extend the vectors to let the given index be valid.
113    fn allow_index(&mut self, index: usize) {
114        if index >= self.state.len() {
115            self.set_size(index + 1);
116        }
117    }
118
119    /// Merge the list of buffer states in the given bind group into this usage scope.
120    ///
121    /// If any of the resulting states is invalid, stops the merge and returns a usage
122    /// conflict with the details of the invalid state.
123    ///
124    /// Because bind groups do not check if the union of all their states is valid,
125    /// this method is allowed to return Err on the first bind group bound.
126    ///
127    /// # Safety
128    ///
129    /// [`Self::set_size`] must be called with the maximum possible Buffer ID before this
130    /// method is called.
131    pub unsafe fn merge_bind_group(
132        &mut self,
133        bind_group: &BufferBindGroupState,
134    ) -> Result<(), ResourceUsageCompatibilityError> {
135        for &(ref resource, state) in bind_group.buffers.iter() {
136            let index = resource.tracker_index().as_usize();
137
138            unsafe {
139                self.insert_or_merge(
140                    index as _,
141                    index,
142                    BufferStateProvider::Direct { state },
143                    ResourceMetadataProvider::Direct { resource },
144                )?
145            };
146        }
147
148        Ok(())
149    }
150
151    /// Merge the list of buffer states in the given usage scope into this UsageScope.
152    ///
153    /// If any of the resulting states is invalid, stops the merge and returns a usage
154    /// conflict with the details of the invalid state.
155    ///
156    /// If the given tracker uses IDs higher than the length of internal vectors,
157    /// the vectors will be extended. A call to set_size is not needed.
158    pub fn merge_usage_scope(
159        &mut self,
160        scope: &Self,
161    ) -> Result<(), ResourceUsageCompatibilityError> {
162        let incoming_size = scope.state.len();
163        if incoming_size > self.state.len() {
164            self.set_size(incoming_size);
165        }
166
167        for index in scope.metadata.owned_indices() {
168            self.tracker_assert_in_bounds(index);
169            scope.tracker_assert_in_bounds(index);
170
171            unsafe {
172                self.insert_or_merge(
173                    index as u32,
174                    index,
175                    BufferStateProvider::Indirect {
176                        state: &scope.state,
177                    },
178                    ResourceMetadataProvider::Indirect {
179                        metadata: &scope.metadata,
180                    },
181                )?;
182            };
183        }
184
185        Ok(())
186    }
187
188    /// Merge a single state into the UsageScope.
189    ///
190    /// If the resulting state is invalid, returns a usage
191    /// conflict with the details of the invalid state.
192    ///
193    /// If the ID is higher than the length of internal vectors,
194    /// the vectors will be extended. A call to set_size is not needed.
195    pub fn merge_single(
196        &mut self,
197        buffer: &Arc<Buffer>,
198        new_state: BufferUses,
199    ) -> Result<(), ResourceUsageCompatibilityError> {
200        let index = buffer.tracker_index().as_usize();
201
202        self.allow_index(index);
203
204        self.tracker_assert_in_bounds(index);
205
206        unsafe {
207            self.insert_or_merge(
208                index as _,
209                index,
210                BufferStateProvider::Direct { state: new_state },
211                ResourceMetadataProvider::Direct { resource: buffer },
212            )?;
213        }
214
215        Ok(())
216    }
217
218    /// Does an insertion operation if the index isn't tracked
219    /// in the current metadata, otherwise merges the given state
220    /// with the current state. If the merging would cause
221    /// a conflict, returns that usage conflict.
222    ///
223    /// # Safety
224    ///
225    /// Indexes must be valid indexes into all arrays passed in
226    /// to this function, either directly or via metadata or provider structs.
227    #[inline(always)]
228    unsafe fn insert_or_merge(
229        &mut self,
230        index32: u32,
231        index: usize,
232        state_provider: BufferStateProvider<'_>,
233        metadata_provider: ResourceMetadataProvider<'_, Arc<Buffer>>,
234    ) -> Result<(), ResourceUsageCompatibilityError> {
235        let currently_owned = unsafe { self.metadata.contains_unchecked(index) };
236
237        if !currently_owned {
238            unsafe {
239                insert(
240                    None,
241                    &mut self.state,
242                    &mut self.metadata,
243                    index,
244                    state_provider,
245                    None,
246                    metadata_provider,
247                )
248            };
249            return Ok(());
250        }
251
252        unsafe {
253            merge(
254                &mut self.state,
255                index32,
256                index,
257                state_provider,
258                metadata_provider,
259            )
260        }
261    }
262
263    /// Removes the indicated usage from the scope.
264    ///
265    /// Note that multiple uses of the same type get merged. It is only
266    /// safe to remove a usage if you are certain you aren't going to
267    /// erase another usage you don't know about.
268    pub fn remove_usage(&mut self, buffer: &Buffer, usage: BufferUses) {
269        let index = buffer.tracker_index().as_usize();
270        if self.metadata.contains(index) {
271            // SAFETY: If the buffer is part of this usage scope, then the index
272            // is in range.
273            unsafe {
274                *self.state.get_unchecked_mut(index) &= !usage;
275            }
276        }
277    }
278}
279
280/// Stores all buffer state within a command buffer.
281pub(crate) struct BufferTracker {
282    start: Vec<BufferUses>,
283    end: Vec<BufferUses>,
284
285    metadata: ResourceMetadata<Arc<Buffer>>,
286
287    temp: Vec<PendingTransition<BufferUses>>,
288
289    ordered_uses_mask: BufferUses,
290}
291
292impl BufferTracker {
293    pub fn new(ordered_uses_mask: BufferUses) -> Self {
294        Self {
295            start: Vec::new(),
296            end: Vec::new(),
297
298            metadata: ResourceMetadata::new(),
299
300            temp: Vec::new(),
301
302            ordered_uses_mask,
303        }
304    }
305
306    fn tracker_assert_in_bounds(&self, index: usize) {
307        strict_assert!(index < self.start.len());
308        strict_assert!(index < self.end.len());
309        self.metadata.tracker_assert_in_bounds(index);
310    }
311
312    /// Sets the size of all the vectors inside the tracker.
313    ///
314    /// Must be called with the highest possible Buffer ID before
315    /// all unsafe functions are called.
316    pub fn set_size(&mut self, size: usize) {
317        self.start.resize(size, BufferUses::empty());
318        self.end.resize(size, BufferUses::empty());
319
320        self.metadata.set_size(size);
321    }
322
323    /// Extend the vectors to let the given index be valid.
324    fn allow_index(&mut self, index: usize) {
325        if index >= self.start.len() {
326            self.set_size(index + 1);
327        }
328    }
329
330    /// Returns true if the given buffer is tracked.
331    pub fn contains(&self, buffer: &Buffer) -> bool {
332        self.metadata.contains(buffer.tracker_index().as_usize())
333    }
334
335    /// Returns a list of all buffers tracked.
336    pub fn used_resources(&self) -> impl Iterator<Item = &Arc<Buffer>> + '_ {
337        self.metadata.owned_resources()
338    }
339
340    /// Drains all currently pending transitions.
341    pub fn drain_transitions<'a, 'b: 'a>(
342        &'b mut self,
343        snatch_guard: &'a SnatchGuard<'a>,
344    ) -> impl Iterator<Item = BufferBarrier<'a, dyn hal::DynBuffer>> {
345        let buffer_barriers = self.temp.drain(..).map(|pending| {
346            let buf = unsafe { self.metadata.get_resource_unchecked(pending.id as _) };
347            pending.into_hal(buf, snatch_guard)
348        });
349        buffer_barriers
350    }
351
352    /// Sets the state of a single buffer.
353    ///
354    /// If a transition is needed to get the buffer into the given state, that transition
355    /// is returned. No more than one transition is needed.
356    ///
357    /// If the ID is higher than the length of internal vectors,
358    /// the vectors will be extended. A call to set_size is not needed.
359    pub fn set_single(
360        &mut self,
361        buffer: &Arc<Buffer>,
362        state: BufferUses,
363    ) -> Option<PendingTransition<BufferUses>> {
364        let index: usize = buffer.tracker_index().as_usize();
365
366        self.allow_index(index);
367
368        self.tracker_assert_in_bounds(index);
369
370        unsafe {
371            self.insert_or_barrier_update(
372                index,
373                BufferStateProvider::Direct { state },
374                None,
375                ResourceMetadataProvider::Direct { resource: buffer },
376            )
377        };
378
379        strict_assert!(self.temp.len() <= 1);
380
381        self.temp.pop()
382    }
383
384    /// Sets the given state for all buffers in the given tracker.
385    ///
386    /// If a transition is needed to get the buffers into the needed state,
387    /// those transitions are stored within the tracker. A subsequent
388    /// call to [`Self::drain_transitions`] is needed to get those transitions.
389    ///
390    /// If the ID is higher than the length of internal vectors,
391    /// the vectors will be extended. A call to set_size is not needed.
392    pub fn set_from_tracker(&mut self, tracker: &Self) {
393        let incoming_size = tracker.start.len();
394        if incoming_size > self.start.len() {
395            self.set_size(incoming_size);
396        }
397
398        for index in tracker.metadata.owned_indices() {
399            self.tracker_assert_in_bounds(index);
400            tracker.tracker_assert_in_bounds(index);
401            unsafe {
402                self.insert_or_barrier_update(
403                    index,
404                    BufferStateProvider::Indirect {
405                        state: &tracker.start,
406                    },
407                    Some(BufferStateProvider::Indirect {
408                        state: &tracker.end,
409                    }),
410                    ResourceMetadataProvider::Indirect {
411                        metadata: &tracker.metadata,
412                    },
413                )
414            }
415        }
416    }
417
418    /// Sets the given state for all buffers in the given UsageScope.
419    ///
420    /// If a transition is needed to get the buffers into the needed state,
421    /// those transitions are stored within the tracker. A subsequent
422    /// call to [`Self::drain_transitions`] is needed to get those transitions.
423    ///
424    /// If the ID is higher than the length of internal vectors,
425    /// the vectors will be extended. A call to set_size is not needed.
426    pub fn set_from_usage_scope(&mut self, scope: &BufferUsageScope) {
427        let incoming_size = scope.state.len();
428        if incoming_size > self.start.len() {
429            self.set_size(incoming_size);
430        }
431
432        for index in scope.metadata.owned_indices() {
433            self.tracker_assert_in_bounds(index);
434            scope.tracker_assert_in_bounds(index);
435            unsafe {
436                self.insert_or_barrier_update(
437                    index,
438                    BufferStateProvider::Indirect {
439                        state: &scope.state,
440                    },
441                    None,
442                    ResourceMetadataProvider::Indirect {
443                        metadata: &scope.metadata,
444                    },
445                )
446            }
447        }
448    }
449
450    /// Iterates through all buffers in the given bind group and adopts
451    /// the state given for those buffers in the UsageScope. It also
452    /// removes all touched buffers from the usage scope.
453    ///
454    /// If a transition is needed to get the buffers into the needed state,
455    /// those transitions are stored within the tracker. A subsequent
456    /// call to [`Self::drain_transitions`] is needed to get those transitions.
457    ///
458    /// This is a really funky method used by Compute Passes to generate
459    /// barriers after a call to dispatch without needing to iterate
460    /// over all elements in the usage scope. We use each the
461    /// a given iterator of ids as a source of which IDs to look at.
462    /// All the IDs must have first been added to the usage scope.
463    ///
464    /// # Panics
465    ///
466    /// If a resource identified by `index_source` is not found in the usage
467    /// scope.
468    pub fn set_and_remove_from_usage_scope_sparse(
469        &mut self,
470        scope: &mut BufferUsageScope,
471        index_source: impl IntoIterator<Item = TrackerIndex>,
472    ) {
473        let incoming_size = scope.state.len();
474        if incoming_size > self.start.len() {
475            self.set_size(incoming_size);
476        }
477
478        for index in index_source {
479            let index = index.as_usize();
480
481            scope.tracker_assert_in_bounds(index);
482
483            if unsafe { !scope.metadata.contains_unchecked(index) } {
484                continue;
485            }
486
487            // SAFETY: we checked that the index is in bounds for the scope, and
488            // called `set_size` to ensure it is valid for `self`.
489            unsafe {
490                self.insert_or_barrier_update(
491                    index,
492                    BufferStateProvider::Indirect {
493                        state: &scope.state,
494                    },
495                    None,
496                    ResourceMetadataProvider::Indirect {
497                        metadata: &scope.metadata,
498                    },
499                )
500            };
501
502            unsafe { scope.metadata.remove(index) };
503        }
504    }
505
506    /// If the resource isn't tracked
507    /// - Inserts the given resource.
508    /// - Uses the `start_state_provider` to populate `start_states`
509    /// - Uses either `end_state_provider` or `start_state_provider`
510    ///   to populate `current_states`.
511    ///
512    /// If the resource is tracked
513    /// - Inserts barriers from the state in `current_states`
514    ///   to the state provided by `start_state_provider`.
515    /// - Updates the `current_states` with either the state from
516    ///   `end_state_provider` or `start_state_provider`.
517    ///
518    /// Any barriers are added to the barrier vector.
519    ///
520    /// # Safety
521    ///
522    /// Indexes must be valid indexes into all arrays passed in
523    /// to this function, either directly or via metadata or provider structs.
524    #[inline(always)]
525    unsafe fn insert_or_barrier_update(
526        &mut self,
527        index: usize,
528        start_state_provider: BufferStateProvider<'_>,
529        end_state_provider: Option<BufferStateProvider<'_>>,
530        metadata_provider: ResourceMetadataProvider<'_, Arc<Buffer>>,
531    ) {
532        let currently_owned = unsafe { self.metadata.contains_unchecked(index) };
533
534        if !currently_owned {
535            unsafe {
536                insert(
537                    Some(&mut self.start),
538                    &mut self.end,
539                    &mut self.metadata,
540                    index,
541                    start_state_provider,
542                    end_state_provider,
543                    metadata_provider,
544                )
545            };
546            return;
547        }
548
549        let update_state_provider =
550            end_state_provider.unwrap_or_else(|| start_state_provider.clone());
551        unsafe {
552            barrier(
553                &mut self.end,
554                index,
555                start_state_provider,
556                &mut self.temp,
557                self.ordered_uses_mask,
558            )
559        };
560
561        unsafe { update(&mut self.end, index, update_state_provider) };
562    }
563}
564
565/// Stores all buffer state within a device.
566pub(crate) struct DeviceBufferTracker {
567    current_states: Vec<BufferUses>,
568    metadata: ResourceMetadata<Weak<Buffer>>,
569    temp: Vec<PendingTransition<BufferUses>>,
570    ordered_uses_mask: BufferUses,
571}
572
573impl DeviceBufferTracker {
574    pub fn new(ordered_uses_mask: BufferUses) -> Self {
575        Self {
576            current_states: Vec::new(),
577            metadata: ResourceMetadata::new(),
578            temp: Vec::new(),
579            ordered_uses_mask,
580        }
581    }
582
583    fn tracker_assert_in_bounds(&self, index: usize) {
584        strict_assert!(index < self.current_states.len());
585        self.metadata.tracker_assert_in_bounds(index);
586    }
587
588    /// Extend the vectors to let the given index be valid.
589    fn allow_index(&mut self, index: usize) {
590        if index >= self.current_states.len() {
591            self.current_states.resize(index + 1, BufferUses::empty());
592            self.metadata.set_size(index + 1);
593        }
594    }
595
596    /// Returns a list of all buffers tracked.
597    pub fn used_resources(&self) -> impl Iterator<Item = &Weak<Buffer>> + '_ {
598        self.metadata.owned_resources()
599    }
600
601    /// Inserts a single buffer and its state into the resource tracker.
602    ///
603    /// If the resource already exists in the tracker, it will be overwritten.
604    pub fn insert_single(&mut self, buffer: &Arc<Buffer>, state: BufferUses) {
605        let index = buffer.tracker_index().as_usize();
606
607        self.allow_index(index);
608
609        self.tracker_assert_in_bounds(index);
610
611        unsafe {
612            insert(
613                None,
614                &mut self.current_states,
615                &mut self.metadata,
616                index,
617                BufferStateProvider::Direct { state },
618                None,
619                ResourceMetadataProvider::Direct {
620                    resource: &Arc::downgrade(buffer),
621                },
622            )
623        }
624    }
625
626    /// Sets the state of a single buffer.
627    ///
628    /// If a transition is needed to get the buffer into the given state, that transition
629    /// is returned. No more than one transition is needed.
630    pub fn set_single(
631        &mut self,
632        buffer: &Arc<Buffer>,
633        state: BufferUses,
634    ) -> Option<PendingTransition<BufferUses>> {
635        let index: usize = buffer.tracker_index().as_usize();
636
637        self.tracker_assert_in_bounds(index);
638
639        let start_state_provider = BufferStateProvider::Direct { state };
640
641        unsafe {
642            barrier(
643                &mut self.current_states,
644                index,
645                start_state_provider.clone(),
646                &mut self.temp,
647                self.ordered_uses_mask,
648            )
649        };
650        unsafe { update(&mut self.current_states, index, start_state_provider) };
651
652        strict_assert!(self.temp.len() <= 1);
653
654        self.temp.pop()
655    }
656
657    /// Sets the given state for all buffers in the given tracker.
658    ///
659    /// If a transition is needed to get the buffers into the needed state,
660    /// those transitions are returned.
661    pub fn set_from_tracker_and_drain_transitions<'a, 'b: 'a>(
662        &'a mut self,
663        tracker: &'a BufferTracker,
664        snatch_guard: &'b SnatchGuard<'b>,
665    ) -> impl Iterator<Item = BufferBarrier<'a, dyn hal::DynBuffer>> {
666        for index in tracker.metadata.owned_indices() {
667            self.tracker_assert_in_bounds(index);
668
669            let start_state_provider = BufferStateProvider::Indirect {
670                state: &tracker.start,
671            };
672            let end_state_provider = BufferStateProvider::Indirect {
673                state: &tracker.end,
674            };
675            unsafe {
676                barrier(
677                    &mut self.current_states,
678                    index,
679                    start_state_provider,
680                    &mut self.temp,
681                    self.ordered_uses_mask,
682                )
683            };
684            unsafe { update(&mut self.current_states, index, end_state_provider) };
685        }
686
687        self.temp.drain(..).map(|pending| {
688            let buf = unsafe { tracker.metadata.get_resource_unchecked(pending.id as _) };
689            pending.into_hal(buf, snatch_guard)
690        })
691    }
692}
693
694/// Source of Buffer State.
695#[derive(Debug, Clone)]
696enum BufferStateProvider<'a> {
697    /// Get a state that was provided directly.
698    Direct { state: BufferUses },
699    /// Get a state from an an array of states.
700    Indirect { state: &'a [BufferUses] },
701}
702impl BufferStateProvider<'_> {
703    /// Gets the state from the provider, given a resource ID index.
704    ///
705    /// # Safety
706    ///
707    /// Index must be in bounds for the indirect source iff this is in the indirect state.
708    #[inline(always)]
709    unsafe fn get_state(&self, index: usize) -> BufferUses {
710        match *self {
711            BufferStateProvider::Direct { state } => state,
712            BufferStateProvider::Indirect { state } => {
713                strict_assert!(index < state.len());
714                *unsafe { state.get_unchecked(index) }
715            }
716        }
717    }
718}
719
720#[inline(always)]
721unsafe fn insert<T: Clone>(
722    start_states: Option<&mut [BufferUses]>,
723    current_states: &mut [BufferUses],
724    resource_metadata: &mut ResourceMetadata<T>,
725    index: usize,
726    start_state_provider: BufferStateProvider<'_>,
727    end_state_provider: Option<BufferStateProvider<'_>>,
728    metadata_provider: ResourceMetadataProvider<'_, T>,
729) {
730    let new_start_state = unsafe { start_state_provider.get_state(index) };
731    let new_end_state =
732        end_state_provider.map_or(new_start_state, |p| unsafe { p.get_state(index) });
733
734    // This should only ever happen with a wgpu bug, but let's just double
735    // check that resource states don't have any conflicts.
736    strict_assert_eq!(invalid_resource_state(new_start_state), false);
737    strict_assert_eq!(invalid_resource_state(new_end_state), false);
738
739    unsafe {
740        if let Some(&mut ref mut start_state) = start_states {
741            *start_state.get_unchecked_mut(index) = new_start_state;
742        }
743        *current_states.get_unchecked_mut(index) = new_end_state;
744
745        let resource = metadata_provider.get(index);
746        resource_metadata.insert(index, resource.clone());
747    }
748}
749
750#[inline(always)]
751unsafe fn merge(
752    current_states: &mut [BufferUses],
753    _index32: u32,
754    index: usize,
755    state_provider: BufferStateProvider<'_>,
756    metadata_provider: ResourceMetadataProvider<'_, Arc<Buffer>>,
757) -> Result<(), ResourceUsageCompatibilityError> {
758    let current_state = unsafe { current_states.get_unchecked_mut(index) };
759    let new_state = unsafe { state_provider.get_state(index) };
760
761    let merged_state = *current_state | new_state;
762
763    if invalid_resource_state(merged_state) {
764        return Err(ResourceUsageCompatibilityError::from_buffer(
765            unsafe { metadata_provider.get(index) },
766            *current_state,
767            new_state,
768        ));
769    }
770
771    *current_state = merged_state;
772
773    Ok(())
774}
775
776#[inline(always)]
777unsafe fn barrier(
778    current_states: &mut [BufferUses],
779    index: usize,
780    state_provider: BufferStateProvider<'_>,
781    barriers: &mut Vec<PendingTransition<BufferUses>>,
782    ordered_uses_mask: BufferUses,
783) {
784    let current_state = unsafe { *current_states.get_unchecked(index) };
785    let new_state = unsafe { state_provider.get_state(index) };
786
787    if skip_barrier(current_state, ordered_uses_mask, new_state) {
788        return;
789    }
790
791    barriers.push(PendingTransition {
792        id: index as _,
793        selector: (),
794        usage: hal::StateTransition {
795            from: current_state,
796            to: new_state,
797        },
798    });
799}
800
801#[inline(always)]
802unsafe fn update(
803    current_states: &mut [BufferUses],
804    index: usize,
805    state_provider: BufferStateProvider<'_>,
806) {
807    let current_state = unsafe { current_states.get_unchecked_mut(index) };
808    let new_state = unsafe { state_provider.get_state(index) };
809
810    *current_state = new_state;
811}