wgpu_core/
resource.rs

1use alloc::{borrow::Cow, borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec::Vec};
2use core::{
3    borrow::Borrow,
4    fmt,
5    mem::{self, size_of, ManuallyDrop},
6    num::NonZeroU64,
7    ops::Range,
8    ptr::NonNull,
9};
10use smallvec::SmallVec;
11use thiserror::Error;
12use wgt::{
13    error::{ErrorType, WebGpuError},
14    TextureSelector,
15};
16
17#[cfg(feature = "trace")]
18use crate::device::trace;
19use crate::{
20    binding_model::{BindGroup, BindingError},
21    device::{
22        queue, resource::DeferredDestroy, BufferMapPendingClosure, Device, DeviceError,
23        DeviceMismatch, HostMap, MissingDownlevelFlags, MissingFeatures,
24    },
25    hal_label,
26    init_tracker::{BufferInitTracker, TextureInitTracker},
27    lock::{rank, Mutex, RwLock},
28    ray_tracing::{BlasCompactReadyPendingClosure, BlasPrepareCompactError},
29    resource_log,
30    snatch::{SnatchGuard, Snatchable},
31    timestamp_normalization::TimestampNormalizationBindGroup,
32    track::{SharedTrackerIndexAllocator, TrackerIndex},
33    weak_vec::WeakVec,
34    Label, LabelHelpers, SubmissionIndex,
35};
36
37/// Information about the wgpu-core resource.
38///
39/// Each type representing a `wgpu-core` resource, like [`Device`],
40/// [`Buffer`], etc., contains a `ResourceInfo` which contains
41/// its latest submission index and label.
42///
43/// A resource may need to be retained for any of several reasons:
44/// and any lifetime logic will be handled by `Arc<Resource>` refcount
45///
46/// - The user may hold a reference to it (via a `wgpu::Buffer`, say).
47///
48/// - Other resources may depend on it (a texture view's backing
49///   texture, for example).
50///
51/// - It may be used by commands sent to the GPU that have not yet
52///   finished execution.
53///
54/// [`Device`]: crate::device::resource::Device
55/// [`Buffer`]: crate::resource::Buffer
56#[derive(Debug)]
57pub(crate) struct TrackingData {
58    tracker_index: TrackerIndex,
59    tracker_indices: Arc<SharedTrackerIndexAllocator>,
60}
61
62impl Drop for TrackingData {
63    fn drop(&mut self) {
64        self.tracker_indices.free(self.tracker_index);
65    }
66}
67
68impl TrackingData {
69    pub(crate) fn new(tracker_indices: Arc<SharedTrackerIndexAllocator>) -> Self {
70        Self {
71            tracker_index: tracker_indices.alloc(),
72            tracker_indices,
73        }
74    }
75
76    pub(crate) fn tracker_index(&self) -> TrackerIndex {
77        self.tracker_index
78    }
79}
80
81#[derive(Clone, Debug)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
83pub struct ResourceErrorIdent {
84    r#type: Cow<'static, str>,
85    label: String,
86}
87
88impl fmt::Display for ResourceErrorIdent {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
90        write!(f, "{} with '{}' label", self.r#type, self.label)
91    }
92}
93
94pub trait ParentDevice: Labeled {
95    fn device(&self) -> &Arc<Device>;
96
97    fn is_equal(self: &Arc<Self>, other: &Arc<Self>) -> bool {
98        Arc::ptr_eq(self, other)
99    }
100
101    fn same_device_as<O: ParentDevice>(&self, other: &O) -> Result<(), DeviceError> {
102        if Arc::ptr_eq(self.device(), other.device()) {
103            Ok(())
104        } else {
105            Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
106                res: self.error_ident(),
107                res_device: self.device().error_ident(),
108                target: Some(other.error_ident()),
109                target_device: other.device().error_ident(),
110            })))
111        }
112    }
113
114    fn same_device(&self, device: &Device) -> Result<(), DeviceError> {
115        if core::ptr::eq(&**self.device(), device) {
116            Ok(())
117        } else {
118            Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
119                res: self.error_ident(),
120                res_device: self.device().error_ident(),
121                target: None,
122                target_device: device.error_ident(),
123            })))
124        }
125    }
126}
127
128#[macro_export]
129macro_rules! impl_parent_device {
130    ($ty:ident) => {
131        impl $crate::resource::ParentDevice for $ty {
132            fn device(&self) -> &Arc<Device> {
133                &self.device
134            }
135        }
136    };
137}
138
139/// Allow access to the hal resource as guarded by the `SnatchGuard`.
140pub trait RawResourceAccess: ParentDevice {
141    type DynResource: hal::DynResource + ?Sized;
142
143    /// Get access to the raw resource if it is not destroyed.
144    ///
145    /// Returns `None` if the resource has been destroyed. This method
146    /// does not allocate in either case.
147    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource>;
148
149    /// Get access to the raw resource if it is not destroyed.
150    ///
151    /// Returns a full error if the resource has been destroyed. This
152    /// method allocates a label in the error case.
153    fn try_raw<'a>(
154        &'a self,
155        guard: &'a SnatchGuard,
156    ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
157        self.raw(guard)
158            .ok_or_else(|| DestroyedResourceError(self.error_ident()))
159    }
160}
161
162pub trait ResourceType {
163    const TYPE: &'static str;
164}
165
166#[macro_export]
167macro_rules! impl_resource_type {
168    ($ty:ident) => {
169        impl $crate::resource::ResourceType for $ty {
170            const TYPE: &'static str = stringify!($ty);
171        }
172    };
173}
174
175pub trait Labeled: ResourceType {
176    /// Returns a string identifying this resource for logging and errors.
177    ///
178    /// It may be a user-provided string or it may be a placeholder from wgpu.
179    ///
180    /// It is non-empty unless the user-provided string was empty.
181    fn label(&self) -> &str;
182
183    fn error_ident(&self) -> ResourceErrorIdent {
184        ResourceErrorIdent {
185            r#type: Cow::Borrowed(Self::TYPE),
186            label: self.label().to_owned(),
187        }
188    }
189}
190
191#[macro_export]
192macro_rules! impl_labeled {
193    ($ty:ident) => {
194        impl $crate::resource::Labeled for $ty {
195            fn label(&self) -> &str {
196                &self.label
197            }
198        }
199    };
200}
201
202pub(crate) trait Trackable {
203    fn tracker_index(&self) -> TrackerIndex;
204}
205
206#[macro_export]
207macro_rules! impl_trackable {
208    ($ty:ident) => {
209        impl $crate::resource::Trackable for $ty {
210            fn tracker_index(&self) -> $crate::track::TrackerIndex {
211                self.tracking_data.tracker_index()
212            }
213        }
214    };
215}
216
217#[derive(Debug)]
218pub(crate) enum BufferMapState {
219    /// Mapped at creation.
220    Init { staging_buffer: StagingBuffer },
221    /// Waiting for GPU to be done before mapping
222    Waiting(BufferPendingMapping),
223    /// Mapped
224    Active {
225        mapping: hal::BufferMapping,
226        range: hal::MemoryRange,
227        host: HostMap,
228    },
229    /// Not mapped
230    Idle,
231}
232
233#[cfg(send_sync)]
234unsafe impl Send for BufferMapState {}
235#[cfg(send_sync)]
236unsafe impl Sync for BufferMapState {}
237
238#[cfg(send_sync)]
239pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + Send + 'static>;
240#[cfg(not(send_sync))]
241pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + 'static>;
242
243pub struct BufferMapOperation {
244    pub host: HostMap,
245    pub callback: Option<BufferMapCallback>,
246}
247
248impl fmt::Debug for BufferMapOperation {
249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250        f.debug_struct("BufferMapOperation")
251            .field("host", &self.host)
252            .field("callback", &self.callback.as_ref().map(|_| "?"))
253            .finish()
254    }
255}
256
257#[derive(Clone, Debug, Error)]
258#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
259#[non_exhaustive]
260pub enum BufferAccessError {
261    #[error(transparent)]
262    Device(#[from] DeviceError),
263    #[error("Buffer map failed")]
264    Failed,
265    #[error(transparent)]
266    DestroyedResource(#[from] DestroyedResourceError),
267    #[error("Buffer is already mapped")]
268    AlreadyMapped,
269    #[error("Buffer map is pending")]
270    MapAlreadyPending,
271    #[error(transparent)]
272    MissingBufferUsage(#[from] MissingBufferUsageError),
273    #[error("Buffer is not mapped")]
274    NotMapped,
275    #[error(
276        "Buffer map range must start aligned to `MAP_ALIGNMENT` and end to `COPY_BUFFER_ALIGNMENT`"
277    )]
278    UnalignedRange,
279    #[error("Buffer offset invalid: offset {offset} must be multiple of 8")]
280    UnalignedOffset { offset: wgt::BufferAddress },
281    #[error("Buffer range size invalid: range_size {range_size} must be multiple of 4")]
282    UnalignedRangeSize { range_size: wgt::BufferAddress },
283    #[error("Buffer access out of bounds: index {index} would underrun the buffer (limit: {min})")]
284    OutOfBoundsStartOffsetUnderrun {
285        index: wgt::BufferAddress,
286        min: wgt::BufferAddress,
287    },
288    #[error(
289        "Buffer access out of bounds: start offset {index} would overrun the buffer (limit: {max})"
290    )]
291    OutOfBoundsStartOffsetOverrun {
292        index: wgt::BufferAddress,
293        max: wgt::BufferAddress,
294    },
295    #[error(
296        "Buffer access out of bounds: start offset {index} + size {size} would overrun the buffer (limit: {max})"
297    )]
298    OutOfBoundsEndOffsetOverrun {
299        index: wgt::BufferAddress,
300        size: wgt::BufferAddress,
301        max: wgt::BufferAddress,
302    },
303    #[error("Buffer map aborted")]
304    MapAborted,
305    #[error(transparent)]
306    InvalidResource(#[from] InvalidResourceError),
307    #[error("Map start offset ({offset}) is out-of-bounds for buffer of size {buffer_size}")]
308    MapStartOffsetOverrun {
309        offset: wgt::BufferAddress,
310        buffer_size: wgt::BufferAddress,
311    },
312    #[error(
313        "Map end offset (start at {} + size of {}) is out-of-bounds for buffer of size {}",
314        offset,
315        size,
316        buffer_size
317    )]
318    MapEndOffsetOverrun {
319        offset: wgt::BufferAddress,
320        size: wgt::BufferAddress,
321        buffer_size: wgt::BufferAddress,
322    },
323}
324
325impl WebGpuError for BufferAccessError {
326    fn webgpu_error_type(&self) -> ErrorType {
327        match self {
328            Self::Device(e) => e.webgpu_error_type(),
329            Self::InvalidResource(e) => e.webgpu_error_type(),
330            Self::DestroyedResource(e) => e.webgpu_error_type(),
331
332            Self::Failed
333            | Self::AlreadyMapped
334            | Self::MapAlreadyPending
335            | Self::MissingBufferUsage(_)
336            | Self::NotMapped
337            | Self::UnalignedRange
338            | Self::UnalignedOffset { .. }
339            | Self::UnalignedRangeSize { .. }
340            | Self::OutOfBoundsStartOffsetUnderrun { .. }
341            | Self::OutOfBoundsStartOffsetOverrun { .. }
342            | Self::OutOfBoundsEndOffsetOverrun { .. }
343            | Self::MapAborted
344            | Self::MapStartOffsetOverrun { .. }
345            | Self::MapEndOffsetOverrun { .. } => ErrorType::Validation,
346        }
347    }
348}
349
350#[derive(Clone, Debug, Error)]
351#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
352#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
353pub struct MissingBufferUsageError {
354    pub(crate) res: ResourceErrorIdent,
355    pub(crate) actual: wgt::BufferUsages,
356    pub(crate) expected: wgt::BufferUsages,
357}
358
359impl WebGpuError for MissingBufferUsageError {
360    fn webgpu_error_type(&self) -> ErrorType {
361        ErrorType::Validation
362    }
363}
364
365#[derive(Clone, Debug, Error)]
366#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
367pub struct MissingTextureUsageError {
368    pub(crate) res: ResourceErrorIdent,
369    pub(crate) actual: wgt::TextureUsages,
370    pub(crate) expected: wgt::TextureUsages,
371}
372
373impl WebGpuError for MissingTextureUsageError {
374    fn webgpu_error_type(&self) -> ErrorType {
375        ErrorType::Validation
376    }
377}
378
379#[derive(Clone, Debug, Error)]
380#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
381#[error("{0} has been destroyed")]
382pub struct DestroyedResourceError(pub ResourceErrorIdent);
383
384impl WebGpuError for DestroyedResourceError {
385    fn webgpu_error_type(&self) -> ErrorType {
386        ErrorType::Validation
387    }
388}
389
390#[derive(Clone, Debug, Error)]
391#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
392#[error("{0} is invalid")]
393pub struct InvalidResourceError(pub ResourceErrorIdent);
394
395impl WebGpuError for InvalidResourceError {
396    fn webgpu_error_type(&self) -> ErrorType {
397        ErrorType::Validation
398    }
399}
400
401pub enum Fallible<T: ParentDevice> {
402    Valid(Arc<T>),
403    Invalid(Arc<String>),
404}
405
406impl<T: ParentDevice> Fallible<T> {
407    pub fn get(self) -> Result<Arc<T>, InvalidResourceError> {
408        match self {
409            Fallible::Valid(v) => Ok(v),
410            Fallible::Invalid(label) => Err(InvalidResourceError(ResourceErrorIdent {
411                r#type: Cow::Borrowed(T::TYPE),
412                label: (*label).clone(),
413            })),
414        }
415    }
416}
417
418impl<T: ParentDevice> Clone for Fallible<T> {
419    fn clone(&self) -> Self {
420        match self {
421            Self::Valid(v) => Self::Valid(v.clone()),
422            Self::Invalid(l) => Self::Invalid(l.clone()),
423        }
424    }
425}
426
427impl<T: ParentDevice> ResourceType for Fallible<T> {
428    const TYPE: &'static str = T::TYPE;
429}
430
431impl<T: ParentDevice + crate::storage::StorageItem> crate::storage::StorageItem for Fallible<T> {
432    type Marker = T::Marker;
433}
434
435pub type BufferAccessResult = Result<(), BufferAccessError>;
436
437#[derive(Debug)]
438pub(crate) struct BufferPendingMapping {
439    pub(crate) range: Range<wgt::BufferAddress>,
440    pub(crate) op: BufferMapOperation,
441    // hold the parent alive while the mapping is active
442    pub(crate) _parent_buffer: Arc<Buffer>,
443}
444
445pub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;
446
447#[derive(Debug)]
448pub struct Buffer {
449    pub(crate) raw: Snatchable<Box<dyn hal::DynBuffer>>,
450    pub(crate) device: Arc<Device>,
451    pub(crate) usage: wgt::BufferUsages,
452    pub(crate) size: wgt::BufferAddress,
453    pub(crate) initialization_status: RwLock<BufferInitTracker>,
454    /// The `label` from the descriptor used to create the resource.
455    pub(crate) label: String,
456    pub(crate) tracking_data: TrackingData,
457    pub(crate) map_state: Mutex<BufferMapState>,
458    // Bind groups that reference this buffer. May contain duplicates.
459    pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
460    pub(crate) timestamp_normalization_bind_group: Snatchable<TimestampNormalizationBindGroup>,
461    pub(crate) indirect_validation_bind_groups: Snatchable<crate::indirect_validation::BindGroups>,
462}
463
464impl Drop for Buffer {
465    fn drop(&mut self) {
466        if let Some(raw) = self.timestamp_normalization_bind_group.take() {
467            raw.dispose(self.device.raw());
468        }
469
470        if let Some(raw) = self.indirect_validation_bind_groups.take() {
471            raw.dispose(self.device.raw());
472        }
473
474        if let Some(raw) = self.raw.take() {
475            resource_log!("Destroy raw {}", self.error_ident());
476            unsafe {
477                self.device.raw().destroy_buffer(raw);
478            }
479        }
480    }
481}
482
483impl RawResourceAccess for Buffer {
484    type DynResource = dyn hal::DynBuffer;
485
486    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
487        self.raw.get(guard).map(|b| b.as_ref())
488    }
489}
490
491impl Buffer {
492    pub(crate) fn check_destroyed(
493        &self,
494        guard: &SnatchGuard,
495    ) -> Result<(), DestroyedResourceError> {
496        self.raw
497            .get(guard)
498            .map(|_| ())
499            .ok_or_else(|| DestroyedResourceError(self.error_ident()))
500    }
501
502    /// Checks that the given buffer usage contains the required buffer usage,
503    /// returns an error otherwise.
504    pub(crate) fn check_usage(
505        &self,
506        expected: wgt::BufferUsages,
507    ) -> Result<(), MissingBufferUsageError> {
508        if self.usage.contains(expected) {
509            Ok(())
510        } else {
511            Err(MissingBufferUsageError {
512                res: self.error_ident(),
513                actual: self.usage,
514                expected,
515            })
516        }
517    }
518
519    /// Resolve the size of a binding for buffer with `offset` and `size`.
520    ///
521    /// If `size` is `None`, then the remainder of the buffer starting from
522    /// `offset` is used.
523    ///
524    /// If the binding would overflow the buffer, then an error is returned.
525    ///
526    /// Zero-size bindings are permitted here for historical reasons. Although
527    /// zero-size bindings are permitted by WebGPU, they are not permitted by
528    /// some backends. See [`Buffer::binding`] and
529    /// [#3170](https://github.com/gfx-rs/wgpu/issues/3170).
530    pub fn resolve_binding_size(
531        &self,
532        offset: wgt::BufferAddress,
533        binding_size: Option<wgt::BufferSize>,
534    ) -> Result<u64, BindingError> {
535        let buffer_size = self.size;
536
537        match binding_size {
538            Some(binding_size) => match offset.checked_add(binding_size.get()) {
539                Some(end) if end <= buffer_size => Ok(binding_size.get()),
540                _ => Err(BindingError::BindingRangeTooLarge {
541                    buffer: self.error_ident(),
542                    offset,
543                    binding_size: binding_size.get(),
544                    buffer_size,
545                }),
546            },
547            None => {
548                buffer_size
549                    .checked_sub(offset)
550                    .ok_or_else(|| BindingError::BindingOffsetTooLarge {
551                        buffer: self.error_ident(),
552                        offset,
553                        buffer_size,
554                    })
555            }
556        }
557    }
558
559    /// Create a new [`hal::BufferBinding`] for the buffer with `offset` and
560    /// `binding_size`.
561    ///
562    /// If `binding_size` is `None`, then the remainder of the buffer starting
563    /// from `offset` is used.
564    ///
565    /// If the binding would overflow the buffer, then an error is returned.
566    ///
567    /// A zero-size binding at the end of the buffer is permitted here for historical reasons. Although
568    /// zero-size bindings are permitted by WebGPU, they are not permitted by
569    /// some backends. The zero-size binding need to be quashed or remapped to a
570    /// non-zero size, either universally in wgpu-core, or in specific backends
571    /// that do not support them. See
572    /// [#3170](https://github.com/gfx-rs/wgpu/issues/3170).
573    ///
574    /// Although it seems like it would be simpler and safer to use the resolved
575    /// size in the returned [`hal::BufferBinding`], doing this (and removing
576    /// redundant logic in backends to resolve the implicit size) was observed
577    /// to cause problems in certain CTS tests, so an implicit size
578    /// specification is preserved in the output.
579    pub fn binding<'a>(
580        &'a self,
581        offset: wgt::BufferAddress,
582        binding_size: Option<wgt::BufferSize>,
583        snatch_guard: &'a SnatchGuard,
584    ) -> Result<(hal::BufferBinding<'a, dyn hal::DynBuffer>, u64), BindingError> {
585        let buf_raw = self.try_raw(snatch_guard)?;
586        let resolved_size = self.resolve_binding_size(offset, binding_size)?;
587        // SAFETY: The offset and size passed to hal::BufferBinding::new_unchecked must
588        // define a binding contained within the buffer.
589        Ok((
590            hal::BufferBinding::new_unchecked(buf_raw, offset, binding_size),
591            resolved_size,
592        ))
593    }
594
595    /// Schedule buffer mapping.
596    ///
597    /// `op.callback` is guaranteed to be called, regardless of the outcome.
598    pub fn map_async(
599        self: &Arc<Self>,
600        offset: wgt::BufferAddress,
601        size: Option<wgt::BufferAddress>,
602        op: BufferMapOperation,
603    ) -> Result<SubmissionIndex, BufferAccessError> {
604        self.try_map_async(offset, size, op)
605            .map_err(|(mut operation, err)| {
606                if let Some(callback) = operation.callback.take() {
607                    callback(Err(err.clone()));
608                }
609                err
610            })
611    }
612
613    /// Try to schedule buffer mapping.
614    ///
615    /// The outcome of this function is one of the following:
616    /// - If there is a queue, and nothing pending in the queue that uses the
617    ///   buffer in question, the buffer is added to `Queue::ready_to_map`, and
618    ///   will be mapped the next time `Device::maintain` is called. The
619    ///   queue assumes responsibility for calling the callback, and this
620    ///   function returns `Ok(0)`, but the buffer has not yet been mapped.
621    /// - If there is a queue, and something is pending in the queue that uses
622    ///   the buffer in question, the buffer is scheduled for mapping after that
623    ///   submission completes. The queue assumes responsibility for calling the
624    ///   callback, and this function returns `Ok(index)` with the index of the
625    ///   submission that must complete. The buffer has not yet been mapped.
626    /// - If there is no queue, the buffer is mapped and the callback is called
627    ///   immediately. The return value is `Ok(0)`.
628    /// - Regardless of the queue state, if there is an error that terminates
629    ///   the buffer mapping attempt, this function returns the callback along
630    ///   with the error, and the caller is responsible for calling the
631    ///   callback.
632    ///
633    /// A return value of `Ok(0)` means that mapping does not need to wait on the queue, but
634    /// it does not mean that the buffer has already been mapped.
635    fn try_map_async(
636        self: &Arc<Self>,
637        offset: wgt::BufferAddress,
638        size: Option<wgt::BufferAddress>,
639        op: BufferMapOperation,
640    ) -> Result<SubmissionIndex, (BufferMapOperation, BufferAccessError)> {
641        let range_size = if let Some(size) = size {
642            size
643        } else {
644            self.size.saturating_sub(offset)
645        };
646
647        if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {
648            return Err((op, BufferAccessError::UnalignedOffset { offset }));
649        }
650        if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
651            return Err((op, BufferAccessError::UnalignedRangeSize { range_size }));
652        }
653
654        if offset > self.size {
655            return Err((
656                op,
657                BufferAccessError::MapStartOffsetOverrun {
658                    offset,
659                    buffer_size: self.size,
660                },
661            ));
662        }
663        // NOTE: Should never underflow because of our earlier check.
664        if range_size > self.size - offset {
665            return Err((
666                op,
667                BufferAccessError::MapEndOffsetOverrun {
668                    offset,
669                    size: range_size,
670                    buffer_size: self.size,
671                },
672            ));
673        }
674        let end_offset = offset + range_size;
675
676        if !offset.is_multiple_of(wgt::MAP_ALIGNMENT)
677            || !end_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT)
678        {
679            return Err((op, BufferAccessError::UnalignedRange));
680        }
681
682        let (pub_usage, internal_use) = match op.host {
683            HostMap::Read => (wgt::BufferUsages::MAP_READ, wgt::BufferUses::MAP_READ),
684            HostMap::Write => (wgt::BufferUsages::MAP_WRITE, wgt::BufferUses::MAP_WRITE),
685        };
686
687        if let Err(e) = self.check_usage(pub_usage) {
688            return Err((op, e.into()));
689        }
690
691        let device = &self.device;
692        if let Err(e) = device.check_is_valid() {
693            return Err((op, e.into()));
694        }
695
696        let submit_index = {
697            let snatch_guard = device.snatchable_lock.read();
698            if let Err(e) = self.check_destroyed(&snatch_guard) {
699                return Err((op, e.into()));
700            }
701
702            {
703                let map_state = &mut *self.map_state.lock();
704                *map_state = match *map_state {
705                    BufferMapState::Init { .. } | BufferMapState::Active { .. } => {
706                        return Err((op, BufferAccessError::AlreadyMapped));
707                    }
708                    BufferMapState::Waiting(_) => {
709                        return Err((op, BufferAccessError::MapAlreadyPending));
710                    }
711                    BufferMapState::Idle => BufferMapState::Waiting(BufferPendingMapping {
712                        range: offset..end_offset,
713                        op,
714                        _parent_buffer: self.clone(),
715                    }),
716                };
717            }
718
719            if let Some(queue) = device.get_queue().as_ref() {
720                match queue.flush_writes_for_buffer(self, snatch_guard) {
721                    Err(err) => {
722                        let state = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
723                        let BufferMapState::Waiting(BufferPendingMapping { op, .. }) = state else {
724                            unreachable!();
725                        };
726                        return Err((op, err));
727                    }
728                    Ok(()) => {
729                        // Schedule the buffer map in the  lifetime tracker.
730                        //
731                        // This call searches for use of the buffer by pending submissions.
732                        // If we just flushed pending writes, that search is redundant; we
733                        // already know that mapping needs to wait for the latest submission
734                        // and could implement a special case to directly attach it to that
735                        // submission. However, the queue is searched in reverse, so finding
736                        // that the buffer is used by the latest submission will be fast.
737                        Some(queue.lock_life().map(self).unwrap_or(0))
738                    }
739                }
740            } else {
741                None
742            }
743        };
744
745        // At this point, `submit_index` is:
746        // - `Some(index)`, if there is a submission the mapping operation must wait for.
747        // - `Some(0)`, if we have a queue and there is no submission to wait for.
748        // - `None`, if we don't have a queue.
749        //
750        // TODO(https://github.com/gfx-rs/wgpu/issues/9306): we are ignoring the transition
751        // here, I think we need to add a barrier at the end of the submission
752        device
753            .trackers
754            .lock()
755            .buffers
756            .set_single(self, internal_use);
757
758        if let Some(index) = submit_index {
759            Ok(index)
760        } else {
761            // We don't have a queue, so go ahead and map the buffer.
762            // We can safely unwrap below since we just set the `map_state` to `BufferMapState::Waiting`.
763            let (mut operation, status) = self.map(&device.snatchable_lock.read()).unwrap();
764            if let Some(callback) = operation.callback.take() {
765                callback(status);
766            }
767            Ok(0)
768        }
769    }
770
771    pub fn get_mapped_range(
772        self: &Arc<Self>,
773        offset: wgt::BufferAddress,
774        size: Option<wgt::BufferAddress>,
775    ) -> Result<(NonNull<u8>, u64), BufferAccessError> {
776        {
777            let snatch_guard = self.device.snatchable_lock.read();
778            self.check_destroyed(&snatch_guard)?;
779        }
780
781        let range_size = if let Some(size) = size {
782            size
783        } else {
784            self.size.saturating_sub(offset)
785        };
786
787        if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {
788            return Err(BufferAccessError::UnalignedOffset { offset });
789        }
790        if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
791            return Err(BufferAccessError::UnalignedRangeSize { range_size });
792        }
793        let map_state = &*self.map_state.lock();
794        match *map_state {
795            BufferMapState::Init { ref staging_buffer } => {
796                if offset > self.size {
797                    return Err(BufferAccessError::MapStartOffsetOverrun {
798                        offset,
799                        buffer_size: self.size,
800                    });
801                }
802                // NOTE: Should never underflow because of our earlier check.
803                if range_size > self.size - offset {
804                    return Err(BufferAccessError::MapEndOffsetOverrun {
805                        offset,
806                        size: range_size,
807                        buffer_size: self.size,
808                    });
809                }
810                let ptr = unsafe { staging_buffer.ptr() };
811                let ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().offset(offset as isize)) };
812                Ok((ptr, range_size))
813            }
814            BufferMapState::Active {
815                ref mapping,
816                ref range,
817                ..
818            } => {
819                if offset > range.end {
820                    return Err(BufferAccessError::OutOfBoundsStartOffsetOverrun {
821                        index: offset,
822                        max: range.end,
823                    });
824                }
825                if offset < range.start {
826                    return Err(BufferAccessError::OutOfBoundsStartOffsetUnderrun {
827                        index: offset,
828                        min: range.start,
829                    });
830                }
831                if range_size > range.end - offset {
832                    return Err(BufferAccessError::OutOfBoundsEndOffsetOverrun {
833                        index: offset,
834                        size: range_size,
835                        max: range.end,
836                    });
837                }
838                // ptr points to the beginning of the range we mapped in map_async
839                // rather than the beginning of the buffer.
840                let relative_offset = (offset - range.start) as isize;
841                unsafe {
842                    Ok((
843                        NonNull::new_unchecked(mapping.ptr.as_ptr().offset(relative_offset)),
844                        range_size,
845                    ))
846                }
847            }
848            BufferMapState::Idle | BufferMapState::Waiting(_) => Err(BufferAccessError::NotMapped),
849        }
850    }
851    /// This function returns [`None`] only if [`Self::map_state`] is not [`BufferMapState::Waiting`].
852    /// Other errors are returned within `BufferMapPendingClosure`.
853    #[must_use]
854    pub(crate) fn map(&self, snatch_guard: &SnatchGuard) -> Option<BufferMapPendingClosure> {
855        // This _cannot_ be inlined into the match. If it is, the lock will be held
856        // open through the whole match, resulting in a deadlock when we try to re-lock
857        // the buffer back to active.
858        let mapping = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
859        let pending_mapping = match mapping {
860            BufferMapState::Waiting(pending_mapping) => pending_mapping,
861            // Mapping cancelled
862            BufferMapState::Idle => return None,
863            // Mapping queued at least twice by map -> unmap -> map
864            // and was already successfully mapped below
865            BufferMapState::Active { .. } => {
866                *self.map_state.lock() = mapping;
867                return None;
868            }
869            _ => panic!("No pending mapping."),
870        };
871        let status = if pending_mapping.range.start != pending_mapping.range.end {
872            let host = pending_mapping.op.host;
873            let size = pending_mapping.range.end - pending_mapping.range.start;
874            match crate::device::map_buffer(
875                self,
876                pending_mapping.range.start,
877                size,
878                host,
879                snatch_guard,
880            ) {
881                Ok(mapping) => {
882                    *self.map_state.lock() = BufferMapState::Active {
883                        mapping,
884                        range: pending_mapping.range.clone(),
885                        host,
886                    };
887                    Ok(())
888                }
889                Err(e) => Err(e),
890            }
891        } else {
892            *self.map_state.lock() = BufferMapState::Active {
893                mapping: hal::BufferMapping {
894                    ptr: NonNull::dangling(),
895                    is_coherent: true,
896                },
897                range: pending_mapping.range,
898                host: pending_mapping.op.host,
899            };
900            Ok(())
901        };
902        Some((pending_mapping.op, status))
903    }
904
905    // Note: This must not be called while holding a lock.
906    pub fn unmap(self: &Arc<Self>) -> Result<(), BufferAccessError> {
907        if let Some((mut operation, status)) = self.unmap_inner()? {
908            if let Some(callback) = operation.callback.take() {
909                callback(status);
910            }
911        }
912
913        Ok(())
914    }
915
916    fn unmap_inner(self: &Arc<Self>) -> Result<Option<BufferMapPendingClosure>, BufferAccessError> {
917        let device = &self.device;
918        let snatch_guard = device.snatchable_lock.read();
919        let raw_buf = self.try_raw(&snatch_guard)?;
920        let map_state = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
921        match map_state {
922            BufferMapState::Init { staging_buffer } => {
923                #[cfg(feature = "trace")]
924                if let Some(ref mut trace) = *device.trace.lock() {
925                    use crate::device::trace::{DataKind, IntoTrace};
926
927                    let data = trace.make_binary(DataKind::Bin, staging_buffer.get_data());
928                    trace.add(trace::Action::WriteBuffer {
929                        id: self.to_trace(),
930                        data,
931                        // NOTE: `self.size` here corresponds to `data`'s actual length.
932                        offset: 0,
933                        size: self.size,
934                        queued: true,
935                    });
936                }
937
938                let staging_buffer = staging_buffer.flush();
939
940                if let Some(queue) = device.get_queue() {
941                    let region = wgt::BufferSize::new(self.size).map(|size| hal::BufferCopy {
942                        src_offset: 0,
943                        dst_offset: 0,
944                        size,
945                    });
946                    let transition_src = hal::BufferBarrier {
947                        buffer: staging_buffer.raw(),
948                        usage: hal::StateTransition {
949                            from: wgt::BufferUses::MAP_WRITE,
950                            to: wgt::BufferUses::COPY_SRC,
951                        },
952                    };
953                    let transition_dst = hal::BufferBarrier::<dyn hal::DynBuffer> {
954                        buffer: raw_buf,
955                        usage: hal::StateTransition {
956                            from: wgt::BufferUses::empty(),
957                            to: wgt::BufferUses::COPY_DST,
958                        },
959                    };
960                    let mut pending_writes = queue.pending_writes.lock();
961                    let encoder = pending_writes.activate();
962                    unsafe {
963                        encoder.transition_buffers(&[transition_src, transition_dst]);
964                        if self.size > 0 {
965                            encoder.copy_buffer_to_buffer(
966                                staging_buffer.raw(),
967                                raw_buf,
968                                region.as_slice(),
969                            );
970                        }
971                    }
972                    pending_writes.consume(staging_buffer);
973                    pending_writes.insert_buffer(self);
974                }
975            }
976            BufferMapState::Idle => {
977                return Err(BufferAccessError::NotMapped);
978            }
979            BufferMapState::Waiting(pending) => {
980                return Ok(Some((pending.op, Err(BufferAccessError::MapAborted))));
981            }
982            BufferMapState::Active {
983                mapping,
984                range,
985                host,
986            } => {
987                if host == HostMap::Write {
988                    #[cfg(feature = "trace")]
989                    if let Some(ref mut trace) = *device.trace.lock() {
990                        use crate::device::trace::{DataKind, IntoTrace};
991
992                        let size = range.end - range.start;
993                        let data = trace.make_binary(DataKind::Bin, unsafe {
994                            core::slice::from_raw_parts(mapping.ptr.as_ptr(), size as usize)
995                        });
996                        trace.add(trace::Action::WriteBuffer {
997                            id: self.to_trace(),
998                            data,
999                            offset: range.start,
1000                            size,
1001                            queued: false,
1002                        });
1003                    }
1004                    if !mapping.is_coherent {
1005                        unsafe { device.raw().flush_mapped_ranges(raw_buf, &[range]) };
1006                    }
1007                }
1008                unsafe { device.raw().unmap_buffer(raw_buf) };
1009            }
1010        }
1011        Ok(None)
1012    }
1013
1014    pub fn destroy(self: &Arc<Self>) {
1015        let device = &self.device;
1016
1017        let temp = {
1018            let mut snatch_guard = device.snatchable_lock.write();
1019
1020            let raw = match self.raw.snatch(&mut snatch_guard) {
1021                Some(raw) => raw,
1022                None => {
1023                    // Per spec, it is valid to call `destroy` multiple times.
1024                    return;
1025                }
1026            };
1027
1028            let timestamp_normalization_bind_group = self
1029                .timestamp_normalization_bind_group
1030                .snatch(&mut snatch_guard);
1031
1032            let indirect_validation_bind_groups = self
1033                .indirect_validation_bind_groups
1034                .snatch(&mut snatch_guard);
1035
1036            drop(snatch_guard);
1037
1038            let bind_groups = {
1039                let mut guard = self.bind_groups.lock();
1040                mem::take(&mut *guard)
1041            };
1042
1043            queue::TempResource::DestroyedBuffer(DestroyedBuffer {
1044                raw: ManuallyDrop::new(raw),
1045                device: Arc::clone(&self.device),
1046                label: self.label().to_owned(),
1047                bind_groups,
1048                timestamp_normalization_bind_group,
1049                indirect_validation_bind_groups,
1050            })
1051        };
1052
1053        let Some(queue) = device.get_queue() else {
1054            return;
1055        };
1056
1057        {
1058            let mut pending_writes = queue.pending_writes.lock();
1059            if pending_writes.contains_buffer(self) {
1060                pending_writes.consume_temp(temp);
1061                return;
1062            }
1063        }
1064
1065        let mut life_lock = queue.lock_life();
1066        let last_submit_index = life_lock.get_buffer_latest_submission_index(self);
1067        if let Some(last_submit_index) = last_submit_index {
1068            life_lock.schedule_resource_destruction(temp, last_submit_index);
1069        }
1070    }
1071}
1072
1073#[derive(Clone, Debug, Error)]
1074#[non_exhaustive]
1075pub enum CreateBufferError {
1076    #[error(transparent)]
1077    Device(#[from] DeviceError),
1078    #[error("Failed to map buffer while creating: {0}")]
1079    AccessError(#[from] BufferAccessError),
1080    #[error("Buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
1081    UnalignedSize,
1082    #[error("Invalid usage flags {0:?}")]
1083    InvalidUsage(wgt::BufferUsages),
1084    #[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
1085    UsageMismatch(wgt::BufferUsages),
1086    #[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")]
1087    MaxBufferSize { requested: u64, maximum: u64 },
1088    #[error(transparent)]
1089    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1090    #[error(transparent)]
1091    MissingFeatures(#[from] MissingFeatures),
1092    #[error("Failed to create bind group for indirect buffer validation: {0}")]
1093    IndirectValidationBindGroup(DeviceError),
1094}
1095
1096crate::impl_resource_type!(Buffer);
1097crate::impl_labeled!(Buffer);
1098crate::impl_parent_device!(Buffer);
1099crate::impl_storage_item!(Buffer);
1100crate::impl_trackable!(Buffer);
1101
1102impl WebGpuError for CreateBufferError {
1103    fn webgpu_error_type(&self) -> ErrorType {
1104        match self {
1105            Self::Device(e) => e.webgpu_error_type(),
1106            Self::AccessError(e) => e.webgpu_error_type(),
1107            Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1108            Self::IndirectValidationBindGroup(e) => e.webgpu_error_type(),
1109            Self::MissingFeatures(e) => e.webgpu_error_type(),
1110
1111            Self::UnalignedSize
1112            | Self::InvalidUsage(_)
1113            | Self::UsageMismatch(_)
1114            | Self::MaxBufferSize { .. } => ErrorType::Validation,
1115        }
1116    }
1117}
1118
1119/// A buffer that has been marked as destroyed and is staged for actual deletion soon.
1120#[derive(Debug)]
1121pub struct DestroyedBuffer {
1122    raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1123    device: Arc<Device>,
1124    label: String,
1125    bind_groups: WeakVec<BindGroup>,
1126    timestamp_normalization_bind_group: Option<TimestampNormalizationBindGroup>,
1127    indirect_validation_bind_groups: Option<crate::indirect_validation::BindGroups>,
1128}
1129
1130impl DestroyedBuffer {
1131    pub fn label(&self) -> &dyn fmt::Debug {
1132        &self.label
1133    }
1134}
1135
1136impl Drop for DestroyedBuffer {
1137    fn drop(&mut self) {
1138        let mut deferred = self.device.deferred_destroy.lock();
1139        deferred.push(DeferredDestroy::BindGroups(mem::take(
1140            &mut self.bind_groups,
1141        )));
1142        drop(deferred);
1143
1144        if let Some(raw) = self.timestamp_normalization_bind_group.take() {
1145            raw.dispose(self.device.raw());
1146        }
1147
1148        if let Some(raw) = self.indirect_validation_bind_groups.take() {
1149            raw.dispose(self.device.raw());
1150        }
1151
1152        resource_log!("Destroy raw Buffer (destroyed) {:?}", self.label());
1153        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
1154        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1155        unsafe {
1156            hal::DynDevice::destroy_buffer(self.device.raw(), raw);
1157        }
1158    }
1159}
1160
1161#[cfg(send_sync)]
1162unsafe impl Send for StagingBuffer {}
1163#[cfg(send_sync)]
1164unsafe impl Sync for StagingBuffer {}
1165
1166/// A temporary buffer, consumed by the command that uses it.
1167///
1168/// A [`StagingBuffer`] is designed for one-shot uploads of data to the GPU. It
1169/// is always created mapped, and the command that uses it destroys the buffer
1170/// when it is done.
1171///
1172/// [`StagingBuffer`]s can be created with [`queue_create_staging_buffer`] and
1173/// used with [`queue_write_staging_buffer`]. They are also used internally by
1174/// operations like [`queue_write_texture`] that need to upload data to the GPU,
1175/// but that don't belong to any particular wgpu command buffer.
1176///
1177/// Used `StagingBuffer`s are accumulated in [`Device::pending_writes`], to be
1178/// freed once their associated operation's queue submission has finished
1179/// execution.
1180///
1181/// [`queue_create_staging_buffer`]: crate::global::Global::queue_create_staging_buffer
1182/// [`queue_write_staging_buffer`]: crate::global::Global::queue_write_staging_buffer
1183/// [`queue_write_texture`]: crate::global::Global::queue_write_texture
1184/// [`Device::pending_writes`]: crate::device::Device
1185#[derive(Debug)]
1186pub struct StagingBuffer {
1187    raw: Box<dyn hal::DynBuffer>,
1188    device: Arc<Device>,
1189    pub(crate) size: wgt::BufferSize,
1190    is_coherent: bool,
1191    ptr: NonNull<u8>,
1192}
1193
1194impl StagingBuffer {
1195    pub(crate) fn new(device: &Arc<Device>, size: wgt::BufferSize) -> Result<Self, DeviceError> {
1196        profiling::scope!("StagingBuffer::new");
1197        let stage_desc = hal::BufferDescriptor {
1198            label: hal_label(Some("(wgpu internal) Staging"), device.instance_flags),
1199            size: size.get(),
1200            usage: wgt::BufferUses::MAP_WRITE | wgt::BufferUses::COPY_SRC,
1201            memory_flags: hal::MemoryFlags::TRANSIENT,
1202        };
1203
1204        let raw = unsafe { device.raw().create_buffer(&stage_desc) }
1205            .map_err(|e| device.handle_hal_error(e))?;
1206        let mapping = unsafe { device.raw().map_buffer(raw.as_ref(), 0..size.get()) }
1207            .map_err(|e| device.handle_hal_error(e))?;
1208
1209        let staging_buffer = StagingBuffer {
1210            raw,
1211            device: device.clone(),
1212            size,
1213            is_coherent: mapping.is_coherent,
1214            ptr: mapping.ptr,
1215        };
1216
1217        Ok(staging_buffer)
1218    }
1219
1220    /// SAFETY: You must not call any functions of `self`
1221    /// until you stopped using the returned pointer.
1222    pub(crate) unsafe fn ptr(&self) -> NonNull<u8> {
1223        self.ptr
1224    }
1225
1226    #[cfg(feature = "trace")]
1227    pub(crate) fn get_data(&self) -> &[u8] {
1228        unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.size.get() as usize) }
1229    }
1230
1231    pub(crate) fn write_zeros(&mut self) {
1232        unsafe { core::ptr::write_bytes(self.ptr.as_ptr(), 0, self.size.get() as usize) };
1233    }
1234
1235    pub(crate) fn write(&mut self, data: &[u8]) {
1236        assert!(data.len() >= self.size.get() as usize);
1237        // SAFETY: With the assert above, all of `copy_nonoverlapping`'s
1238        // requirements are satisfied.
1239        unsafe {
1240            core::ptr::copy_nonoverlapping(
1241                data.as_ptr(),
1242                self.ptr.as_ptr(),
1243                self.size.get() as usize,
1244            );
1245        }
1246    }
1247
1248    /// SAFETY: The offsets and size must be in-bounds.
1249    pub(crate) unsafe fn write_with_offset(
1250        &mut self,
1251        data: &[u8],
1252        src_offset: isize,
1253        dst_offset: isize,
1254        size: usize,
1255    ) {
1256        unsafe {
1257            debug_assert!(
1258                (src_offset + size as isize) as usize <= data.len(),
1259                "src_offset + size must be in-bounds: src_offset = {}, size = {}, data.len() = {}",
1260                src_offset,
1261                size,
1262                data.len()
1263            );
1264            core::ptr::copy_nonoverlapping(
1265                data.as_ptr().offset(src_offset),
1266                self.ptr.as_ptr().offset(dst_offset),
1267                size,
1268            );
1269        }
1270    }
1271
1272    pub(crate) fn flush(self) -> FlushedStagingBuffer {
1273        let device = self.device.raw();
1274        if !self.is_coherent {
1275            #[allow(clippy::single_range_in_vec_init)]
1276            unsafe {
1277                device.flush_mapped_ranges(self.raw.as_ref(), &[0..self.size.get()])
1278            };
1279        }
1280        unsafe { device.unmap_buffer(self.raw.as_ref()) };
1281
1282        let StagingBuffer {
1283            raw, device, size, ..
1284        } = self;
1285
1286        FlushedStagingBuffer {
1287            raw: ManuallyDrop::new(raw),
1288            device,
1289            size,
1290        }
1291    }
1292}
1293
1294crate::impl_resource_type!(StagingBuffer);
1295crate::impl_storage_item!(StagingBuffer);
1296
1297#[derive(Debug)]
1298pub struct FlushedStagingBuffer {
1299    raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1300    device: Arc<Device>,
1301    pub(crate) size: wgt::BufferSize,
1302}
1303
1304impl FlushedStagingBuffer {
1305    pub(crate) fn raw(&self) -> &dyn hal::DynBuffer {
1306        self.raw.as_ref()
1307    }
1308}
1309
1310impl Drop for FlushedStagingBuffer {
1311    fn drop(&mut self) {
1312        resource_log!("Destroy raw StagingBuffer");
1313        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
1314        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1315        unsafe { self.device.raw().destroy_buffer(raw) };
1316    }
1317}
1318
1319pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, Vec<wgt::TextureFormat>>;
1320
1321#[derive(Debug)]
1322pub(crate) enum TextureInner {
1323    Native {
1324        raw: Box<dyn hal::DynTexture>,
1325    },
1326    Surface {
1327        raw: Box<dyn hal::DynSurfaceTexture>,
1328    },
1329}
1330
1331impl TextureInner {
1332    pub(crate) fn raw(&self) -> &dyn hal::DynTexture {
1333        match self {
1334            Self::Native { raw } => raw.as_ref(),
1335            Self::Surface { raw, .. } => raw.as_ref().borrow(),
1336        }
1337    }
1338}
1339
1340#[derive(Debug)]
1341pub enum TextureClearMode {
1342    BufferCopy,
1343    // View for clear via RenderPass for every subsurface (mip/layer/slice)
1344    RenderPass {
1345        clear_views: SmallVec<[ManuallyDrop<Box<dyn hal::DynTextureView>>; 1]>,
1346        is_color: bool,
1347    },
1348    Surface {
1349        clear_view: ManuallyDrop<Box<dyn hal::DynTextureView>>,
1350    },
1351    // Texture can't be cleared, attempting to do so will cause panic.
1352    // (either because it is impossible for the type of texture or it is being destroyed)
1353    None,
1354}
1355
1356#[derive(Debug)]
1357pub struct Texture {
1358    pub(crate) inner: Snatchable<TextureInner>,
1359    pub(crate) device: Arc<Device>,
1360    pub(crate) desc: wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
1361    pub(crate) _hal_usage: wgt::TextureUses,
1362    pub(crate) format_features: wgt::TextureFormatFeatures,
1363    pub(crate) initialization_status: RwLock<TextureInitTracker>,
1364    pub(crate) full_range: TextureSelector,
1365    /// The `label` from the descriptor used to create the resource.
1366    pub(crate) label: String,
1367    pub(crate) tracking_data: TrackingData,
1368    pub(crate) clear_mode: RwLock<TextureClearMode>,
1369    pub(crate) views: Mutex<WeakVec<TextureView>>,
1370    // Bind groups that reference this texture. May contain duplicates.
1371    pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
1372}
1373
1374impl Texture {
1375    pub(crate) fn new(
1376        device: &Arc<Device>,
1377        inner: TextureInner,
1378        hal_usage: wgt::TextureUses,
1379        desc: &TextureDescriptor,
1380        format_features: wgt::TextureFormatFeatures,
1381        clear_mode: TextureClearMode,
1382        init: bool,
1383    ) -> Self {
1384        Texture {
1385            inner: Snatchable::new(inner),
1386            device: device.clone(),
1387            desc: desc.map_label(|_| ()),
1388            _hal_usage: hal_usage,
1389            format_features,
1390            initialization_status: RwLock::new(
1391                rank::TEXTURE_INITIALIZATION_STATUS,
1392                if init {
1393                    TextureInitTracker::new(desc.mip_level_count, desc.array_layer_count())
1394                } else {
1395                    TextureInitTracker::new(desc.mip_level_count, 0)
1396                },
1397            ),
1398            full_range: TextureSelector {
1399                mips: 0..desc.mip_level_count,
1400                layers: 0..desc.array_layer_count(),
1401            },
1402            label: desc.label.to_string(),
1403            tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1404            clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, clear_mode),
1405            views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1406            bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1407        }
1408    }
1409
1410    /// Checks that the given texture usage contains the required texture usage,
1411    /// returns an error otherwise.
1412    pub(crate) fn check_usage(
1413        &self,
1414        expected: wgt::TextureUsages,
1415    ) -> Result<(), MissingTextureUsageError> {
1416        if self.desc.usage.contains(expected) {
1417            Ok(())
1418        } else {
1419            Err(MissingTextureUsageError {
1420                res: self.error_ident(),
1421                actual: self.desc.usage,
1422                expected,
1423            })
1424        }
1425    }
1426}
1427
1428impl Drop for Texture {
1429    fn drop(&mut self) {
1430        match *self.clear_mode.write() {
1431            TextureClearMode::Surface {
1432                ref mut clear_view, ..
1433            } => {
1434                // SAFETY: We are in the Drop impl and we don't use clear_view anymore after this point.
1435                let raw = unsafe { ManuallyDrop::take(clear_view) };
1436                unsafe {
1437                    self.device.raw().destroy_texture_view(raw);
1438                }
1439            }
1440            TextureClearMode::RenderPass {
1441                ref mut clear_views,
1442                ..
1443            } => {
1444                clear_views.iter_mut().for_each(|clear_view| {
1445                    // SAFETY: We are in the Drop impl and we don't use clear_view anymore after this point.
1446                    let raw = unsafe { ManuallyDrop::take(clear_view) };
1447                    unsafe {
1448                        self.device.raw().destroy_texture_view(raw);
1449                    }
1450                });
1451            }
1452            _ => {}
1453        };
1454
1455        if let Some(TextureInner::Native { raw }) = self.inner.take() {
1456            resource_log!("Destroy raw {}", self.error_ident());
1457            unsafe {
1458                self.device.raw().destroy_texture(raw);
1459            }
1460        }
1461    }
1462}
1463
1464impl RawResourceAccess for Texture {
1465    type DynResource = dyn hal::DynTexture;
1466
1467    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1468        self.inner.get(guard).map(|t| t.raw())
1469    }
1470}
1471
1472impl Texture {
1473    pub(crate) fn try_inner<'a>(
1474        &'a self,
1475        guard: &'a SnatchGuard,
1476    ) -> Result<&'a TextureInner, DestroyedResourceError> {
1477        self.inner
1478            .get(guard)
1479            .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1480    }
1481
1482    pub(crate) fn check_destroyed(
1483        &self,
1484        guard: &SnatchGuard,
1485    ) -> Result<(), DestroyedResourceError> {
1486        self.inner
1487            .get(guard)
1488            .map(|_| ())
1489            .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1490    }
1491
1492    pub(crate) fn get_clear_view<'a>(
1493        clear_mode: &'a TextureClearMode,
1494        desc: &'a wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
1495        mip_level: u32,
1496        depth_or_layer: u32,
1497    ) -> &'a dyn hal::DynTextureView {
1498        match *clear_mode {
1499            TextureClearMode::BufferCopy => {
1500                panic!("Given texture is cleared with buffer copies, not render passes")
1501            }
1502            TextureClearMode::None => {
1503                panic!("Given texture can't be cleared")
1504            }
1505            TextureClearMode::Surface { ref clear_view, .. } => clear_view.as_ref(),
1506            TextureClearMode::RenderPass {
1507                ref clear_views, ..
1508            } => {
1509                let index = if desc.dimension == wgt::TextureDimension::D3 {
1510                    (0..mip_level).fold(0, |acc, mip| {
1511                        acc + (desc.size.depth_or_array_layers >> mip).max(1)
1512                    })
1513                } else {
1514                    mip_level * desc.size.depth_or_array_layers
1515                } + depth_or_layer;
1516                clear_views[index as usize].as_ref()
1517            }
1518        }
1519    }
1520
1521    pub fn destroy(self: &Arc<Self>) {
1522        let device = &self.device;
1523
1524        let temp = {
1525            let raw = match self.inner.snatch(&mut device.snatchable_lock.write()) {
1526                Some(TextureInner::Native { raw }) => raw,
1527                Some(TextureInner::Surface { .. }) => {
1528                    return;
1529                }
1530                None => {
1531                    // Per spec, it is valid to call `destroy` multiple times.
1532                    return;
1533                }
1534            };
1535
1536            let views = {
1537                let mut guard = self.views.lock();
1538                mem::take(&mut *guard)
1539            };
1540
1541            let bind_groups = {
1542                let mut guard = self.bind_groups.lock();
1543                mem::take(&mut *guard)
1544            };
1545
1546            queue::TempResource::DestroyedTexture(DestroyedTexture {
1547                raw: ManuallyDrop::new(raw),
1548                views,
1549                clear_mode: mem::replace(&mut *self.clear_mode.write(), TextureClearMode::None),
1550                bind_groups,
1551                device: Arc::clone(&self.device),
1552                label: self.label().to_owned(),
1553            })
1554        };
1555
1556        let Some(queue) = device.get_queue() else {
1557            return;
1558        };
1559
1560        {
1561            let mut pending_writes = queue.pending_writes.lock();
1562            if pending_writes.contains_texture(self) {
1563                pending_writes.consume_temp(temp);
1564                return;
1565            }
1566        }
1567
1568        let mut life_lock = queue.lock_life();
1569        let last_submit_index = life_lock.get_texture_latest_submission_index(self);
1570        if let Some(last_submit_index) = last_submit_index {
1571            life_lock.schedule_resource_destruction(temp, last_submit_index);
1572        }
1573    }
1574}
1575
1576/// A texture that has been marked as destroyed and is staged for actual deletion soon.
1577#[derive(Debug)]
1578pub struct DestroyedTexture {
1579    raw: ManuallyDrop<Box<dyn hal::DynTexture>>,
1580    views: WeakVec<TextureView>,
1581    clear_mode: TextureClearMode,
1582    bind_groups: WeakVec<BindGroup>,
1583    device: Arc<Device>,
1584    label: String,
1585}
1586
1587impl DestroyedTexture {
1588    pub fn label(&self) -> &dyn fmt::Debug {
1589        &self.label
1590    }
1591}
1592
1593impl Drop for DestroyedTexture {
1594    fn drop(&mut self) {
1595        let device = &self.device;
1596
1597        let mut deferred = device.deferred_destroy.lock();
1598        deferred.push(DeferredDestroy::TextureViews(mem::take(&mut self.views)));
1599        deferred.push(DeferredDestroy::BindGroups(mem::take(
1600            &mut self.bind_groups,
1601        )));
1602        drop(deferred);
1603
1604        match mem::replace(&mut self.clear_mode, TextureClearMode::None) {
1605            TextureClearMode::RenderPass { clear_views, .. } => {
1606                for clear_view in clear_views {
1607                    let raw = ManuallyDrop::into_inner(clear_view);
1608                    unsafe { self.device.raw().destroy_texture_view(raw) };
1609                }
1610            }
1611            TextureClearMode::Surface { clear_view } => {
1612                let raw = ManuallyDrop::into_inner(clear_view);
1613                unsafe { self.device.raw().destroy_texture_view(raw) };
1614            }
1615            _ => (),
1616        }
1617
1618        resource_log!("Destroy raw Texture (destroyed) {:?}", self.label());
1619        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
1620        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1621        unsafe {
1622            self.device.raw().destroy_texture(raw);
1623        }
1624    }
1625}
1626
1627#[derive(Clone, Copy, Debug)]
1628pub enum TextureErrorDimension {
1629    X,
1630    Y,
1631    Z,
1632}
1633
1634#[derive(Clone, Debug, Error)]
1635#[non_exhaustive]
1636pub enum TextureDimensionError {
1637    #[error("Dimension {0:?} is zero")]
1638    Zero(TextureErrorDimension),
1639    #[error("Dimension {dim:?} value {given} exceeds the limit of {limit}")]
1640    LimitExceeded {
1641        dim: TextureErrorDimension,
1642        given: u32,
1643        limit: u32,
1644    },
1645    #[error("Sample count {0} is invalid")]
1646    InvalidSampleCount(u32),
1647    #[error("Width {width} is not a multiple of {format:?}'s block width ({block_width})")]
1648    NotMultipleOfBlockWidth {
1649        width: u32,
1650        block_width: u32,
1651        format: wgt::TextureFormat,
1652    },
1653    #[error("Height {height} is not a multiple of {format:?}'s block height ({block_height})")]
1654    NotMultipleOfBlockHeight {
1655        height: u32,
1656        block_height: u32,
1657        format: wgt::TextureFormat,
1658    },
1659    #[error(
1660        "Width {width} is not a multiple of {format:?}'s width multiple requirement ({multiple})"
1661    )]
1662    WidthNotMultipleOf {
1663        width: u32,
1664        multiple: u32,
1665        format: wgt::TextureFormat,
1666    },
1667    #[error("Height {height} is not a multiple of {format:?}'s height multiple requirement ({multiple})")]
1668    HeightNotMultipleOf {
1669        height: u32,
1670        multiple: u32,
1671        format: wgt::TextureFormat,
1672    },
1673    #[error("Multisampled texture depth or array layers must be 1, got {0}")]
1674    MultisampledDepthOrArrayLayer(u32),
1675}
1676
1677impl WebGpuError for TextureDimensionError {
1678    fn webgpu_error_type(&self) -> ErrorType {
1679        ErrorType::Validation
1680    }
1681}
1682
1683#[derive(Clone, Debug, Error)]
1684#[non_exhaustive]
1685pub enum CreateTextureError {
1686    #[error(transparent)]
1687    Device(#[from] DeviceError),
1688    #[error(transparent)]
1689    CreateTextureView(#[from] CreateTextureViewError),
1690    #[error("Invalid usage flags {0:?}")]
1691    InvalidUsage(wgt::TextureUsages),
1692    #[error("Texture usage {0:?} is not compatible with texture usage {1:?}")]
1693    IncompatibleUsage(wgt::TextureUsages, wgt::TextureUsages),
1694    #[error(transparent)]
1695    InvalidDimension(#[from] TextureDimensionError),
1696    #[error("Depth texture ({1:?}) can't be created as {0:?}")]
1697    InvalidDepthDimension(wgt::TextureDimension, wgt::TextureFormat),
1698    #[error("Compressed texture ({1:?}) can't be created as {0:?}")]
1699    InvalidCompressedDimension(wgt::TextureDimension, wgt::TextureFormat),
1700    #[error(
1701        "Texture descriptor mip level count {requested} is invalid, maximum allowed is {maximum}"
1702    )]
1703    InvalidMipLevelCount { requested: u32, maximum: u32 },
1704    #[error(
1705        "Texture usages {0:?} are not allowed on a texture of type {1:?}{downlevel_suffix}",
1706        downlevel_suffix = if *.2 { " due to downlevel restrictions" } else { "" }
1707    )]
1708    InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat, bool),
1709    #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
1710    InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
1711    #[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")]
1712    InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),
1713    #[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")]
1714    InvalidMultisampledStorageBinding,
1715    #[error("Format {0:?} does not support multisampling")]
1716    InvalidMultisampledFormat(wgt::TextureFormat),
1717    #[error("Sample count {0} is not supported by format {1:?} on this device. The WebGPU spec guarantees {2:?} samples are supported by this format. With the TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES feature your device supports {3:?}.")]
1718    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
1719    #[error("Multisampled textures must have RENDER_ATTACHMENT usage")]
1720    MultisampledNotRenderAttachment,
1721    #[error("Texture format {0:?} can't be used due to missing features")]
1722    MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),
1723    #[error(transparent)]
1724    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1725}
1726
1727crate::impl_resource_type!(Texture);
1728crate::impl_labeled!(Texture);
1729crate::impl_parent_device!(Texture);
1730crate::impl_storage_item!(Texture);
1731crate::impl_trackable!(Texture);
1732
1733impl Borrow<TextureSelector> for Texture {
1734    fn borrow(&self) -> &TextureSelector {
1735        &self.full_range
1736    }
1737}
1738
1739impl WebGpuError for CreateTextureError {
1740    fn webgpu_error_type(&self) -> ErrorType {
1741        match self {
1742            Self::Device(e) => e.webgpu_error_type(),
1743            Self::CreateTextureView(e) => e.webgpu_error_type(),
1744            Self::InvalidDimension(e) => e.webgpu_error_type(),
1745            Self::MissingFeatures(_, e) => e.webgpu_error_type(),
1746            Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1747
1748            Self::InvalidUsage(_)
1749            | Self::IncompatibleUsage(_, _)
1750            | Self::InvalidDepthDimension(_, _)
1751            | Self::InvalidCompressedDimension(_, _)
1752            | Self::InvalidMipLevelCount { .. }
1753            | Self::InvalidFormatUsages(_, _, _)
1754            | Self::InvalidViewFormat(_, _)
1755            | Self::InvalidDimensionUsages(_, _)
1756            | Self::InvalidMultisampledStorageBinding
1757            | Self::InvalidMultisampledFormat(_)
1758            | Self::InvalidSampleCount(..)
1759            | Self::MultisampledNotRenderAttachment => ErrorType::Validation,
1760        }
1761    }
1762}
1763
1764/// Describes a [`TextureView`].
1765#[derive(Clone, Debug, Default, Eq, PartialEq)]
1766#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1767#[cfg_attr(feature = "serde", serde(default))]
1768pub struct TextureViewDescriptor<'a> {
1769    /// Debug label of the texture view.
1770    ///
1771    /// This will show up in graphics debuggers for easy identification.
1772    pub label: Label<'a>,
1773    /// Format of the texture view, or `None` for the same format as the texture
1774    /// itself.
1775    ///
1776    /// At this time, it must be the same the underlying format of the texture.
1777    pub format: Option<wgt::TextureFormat>,
1778    /// The dimension of the texture view.
1779    ///
1780    /// - For 1D textures, this must be `D1`.
1781    /// - For 2D textures it must be one of `D2`, `D2Array`, `Cube`, or `CubeArray`.
1782    /// - For 3D textures it must be `D3`.
1783    pub dimension: Option<wgt::TextureViewDimension>,
1784    /// The allowed usage(s) for the texture view. Must be a subset of the usage flags of the texture.
1785    /// If not provided, defaults to the full set of usage flags of the texture.
1786    pub usage: Option<wgt::TextureUsages>,
1787    /// Range within the texture that is accessible via this view.
1788    pub range: wgt::ImageSubresourceRange,
1789}
1790
1791#[derive(Debug)]
1792pub(crate) struct HalTextureViewDescriptor {
1793    pub texture_format: wgt::TextureFormat,
1794    pub format: wgt::TextureFormat,
1795    pub usage: wgt::TextureUsages,
1796    pub dimension: wgt::TextureViewDimension,
1797    pub range: wgt::ImageSubresourceRange,
1798}
1799
1800impl HalTextureViewDescriptor {
1801    pub fn aspects(&self) -> hal::FormatAspects {
1802        hal::FormatAspects::new(self.texture_format, self.range.aspect)
1803    }
1804}
1805
1806#[derive(Debug, Copy, Clone, Error)]
1807pub enum TextureViewNotRenderableReason {
1808    #[error("The texture this view references doesn't include the RENDER_ATTACHMENT usage. Provided usages: {0:?}")]
1809    Usage(wgt::TextureUsages),
1810    #[error("The dimension of this texture view is not 2D. View dimension: {0:?}")]
1811    Dimension(wgt::TextureViewDimension),
1812    #[error("This texture view has more than one mipmap level. View mipmap levels: {0:?}")]
1813    MipLevelCount(u32),
1814    #[error("This texture view has more than one array layer. View array layers: {0:?}")]
1815    ArrayLayerCount(u32),
1816    #[error(
1817        "The aspects of this texture view are a subset of the aspects in the original texture. Aspects: {0:?}"
1818    )]
1819    Aspects(hal::FormatAspects),
1820}
1821
1822#[derive(Debug)]
1823pub struct TextureView {
1824    pub(crate) raw: Snatchable<Box<dyn hal::DynTextureView>>,
1825    // if it's a surface texture - it's none
1826    pub(crate) parent: Arc<Texture>,
1827    pub(crate) device: Arc<Device>,
1828    pub(crate) desc: HalTextureViewDescriptor,
1829    pub(crate) format_features: wgt::TextureFormatFeatures,
1830    /// This is `Err` only if the texture view is not renderable
1831    pub(crate) render_extent: Result<wgt::Extent3d, TextureViewNotRenderableReason>,
1832    pub(crate) samples: u32,
1833    pub(crate) selector: TextureSelector,
1834    /// The `label` from the descriptor used to create the resource.
1835    pub(crate) label: String,
1836}
1837
1838impl Drop for TextureView {
1839    fn drop(&mut self) {
1840        if let Some(raw) = self.raw.take() {
1841            resource_log!("Destroy raw {}", self.error_ident());
1842            unsafe {
1843                self.device.raw().destroy_texture_view(raw);
1844            }
1845        }
1846    }
1847}
1848
1849impl RawResourceAccess for TextureView {
1850    type DynResource = dyn hal::DynTextureView;
1851
1852    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1853        self.raw.get(guard).map(|it| it.as_ref())
1854    }
1855
1856    fn try_raw<'a>(
1857        &'a self,
1858        guard: &'a SnatchGuard,
1859    ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
1860        self.parent.check_destroyed(guard)?;
1861
1862        self.raw(guard)
1863            .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1864    }
1865}
1866
1867impl TextureView {
1868    /// Checks that the given texture usage contains the required texture usage,
1869    /// returns an error otherwise.
1870    pub(crate) fn check_usage(
1871        &self,
1872        expected: wgt::TextureUsages,
1873    ) -> Result<(), MissingTextureUsageError> {
1874        if self.desc.usage.contains(expected) {
1875            Ok(())
1876        } else {
1877            Err(MissingTextureUsageError {
1878                res: self.error_ident(),
1879                actual: self.desc.usage,
1880                expected,
1881            })
1882        }
1883    }
1884}
1885
1886#[derive(Clone, Debug, Error)]
1887#[non_exhaustive]
1888pub enum CreateTextureViewError {
1889    #[error(transparent)]
1890    Device(#[from] DeviceError),
1891    #[error(transparent)]
1892    DestroyedResource(#[from] DestroyedResourceError),
1893    #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`")]
1894    InvalidTextureViewDimension {
1895        view: wgt::TextureViewDimension,
1896        texture: wgt::TextureDimension,
1897    },
1898    #[error("Texture view format `{0:?}` cannot be used as a render attachment. Make sure the format supports RENDER_ATTACHMENT usage and required device features are enabled.")]
1899    TextureViewFormatNotRenderable(wgt::TextureFormat),
1900    #[error("Texture view format `{0:?}` cannot be used as a storage binding. Make sure the format supports STORAGE usage and required device features are enabled.")]
1901    TextureViewFormatNotStorage(wgt::TextureFormat),
1902    #[error("Texture view usages (`{view:?}`) must be a subset of the texture's original usages (`{texture:?}`)")]
1903    InvalidTextureViewUsage {
1904        view: wgt::TextureUsages,
1905        texture: wgt::TextureUsages,
1906    },
1907    #[error("Texture view dimension `{0:?}` cannot be used with a multisampled texture")]
1908    InvalidMultisampledTextureViewDimension(wgt::TextureViewDimension),
1909    #[error(
1910        "TextureView has an arrayLayerCount of {depth}. Views of type `Cube` must have arrayLayerCount of 6."
1911    )]
1912    InvalidCubemapTextureDepth { depth: u32 },
1913    #[error("TextureView has an arrayLayerCount of {depth}. Views of type `CubeArray` must have an arrayLayerCount that is a multiple of 6.")]
1914    InvalidCubemapArrayTextureDepth { depth: u32 },
1915    #[error("Source texture width and height must be equal for a texture view of dimension `Cube`/`CubeArray`")]
1916    InvalidCubeTextureViewSize,
1917    #[error("Mip level count is 0")]
1918    ZeroMipLevelCount,
1919    #[error("Array layer count is 0")]
1920    ZeroArrayLayerCount,
1921    #[error(
1922        "`TextureView` starts at mip level {base_mip_level} and spans {mip_level_count} mip \
1923        levels, but the texture view only has {total} total mip level(s)"
1924    )]
1925    TooManyMipLevels {
1926        base_mip_level: u32,
1927        mip_level_count: u32,
1928        total: u32,
1929    },
1930    #[error(
1931        "`TextureView` starts at array layer {base_array_layer} and spans {array_layer_count}) \
1932        array layers, but the texture view only has {total} total layer(s)"
1933    )]
1934    TooManyArrayLayers {
1935        base_array_layer: u32,
1936        array_layer_count: u32,
1937        total: u32,
1938    },
1939    #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")]
1940    InvalidArrayLayerCount {
1941        requested: u32,
1942        dim: wgt::TextureViewDimension,
1943    },
1944    #[error(
1945        "Aspect {requested_aspect:?} is not a valid aspect of the source texture format {texture_format:?}"
1946    )]
1947    InvalidAspect {
1948        texture_format: wgt::TextureFormat,
1949        requested_aspect: wgt::TextureAspect,
1950    },
1951    #[error(
1952        "Trying to create a view of format {view:?} of a texture with format {texture:?}, \
1953         but this view format is not present in the texture's viewFormat array"
1954    )]
1955    FormatReinterpretation {
1956        texture: wgt::TextureFormat,
1957        view: wgt::TextureFormat,
1958    },
1959    #[error(transparent)]
1960    InvalidResource(#[from] InvalidResourceError),
1961    #[error(transparent)]
1962    MissingFeatures(#[from] MissingFeatures),
1963}
1964
1965impl WebGpuError for CreateTextureViewError {
1966    fn webgpu_error_type(&self) -> ErrorType {
1967        match self {
1968            Self::Device(e) => e.webgpu_error_type(),
1969
1970            Self::InvalidTextureViewDimension { .. }
1971            | Self::InvalidResource(_)
1972            | Self::InvalidMultisampledTextureViewDimension(_)
1973            | Self::InvalidCubemapTextureDepth { .. }
1974            | Self::InvalidCubemapArrayTextureDepth { .. }
1975            | Self::InvalidCubeTextureViewSize
1976            | Self::ZeroMipLevelCount
1977            | Self::ZeroArrayLayerCount
1978            | Self::TooManyMipLevels { .. }
1979            | Self::TooManyArrayLayers { .. }
1980            | Self::InvalidArrayLayerCount { .. }
1981            | Self::InvalidAspect { .. }
1982            | Self::FormatReinterpretation { .. }
1983            | Self::DestroyedResource(_)
1984            | Self::TextureViewFormatNotRenderable(_)
1985            | Self::TextureViewFormatNotStorage(_)
1986            | Self::InvalidTextureViewUsage { .. }
1987            | Self::MissingFeatures(_) => ErrorType::Validation,
1988        }
1989    }
1990}
1991
1992crate::impl_resource_type!(TextureView);
1993crate::impl_labeled!(TextureView);
1994crate::impl_parent_device!(TextureView);
1995crate::impl_storage_item!(TextureView);
1996
1997pub type ExternalTextureDescriptor<'a> = wgt::ExternalTextureDescriptor<Label<'a>>;
1998
1999#[derive(Debug)]
2000pub struct ExternalTexture {
2001    pub(crate) device: Arc<Device>,
2002    /// Between 1 and 3 (inclusive) planes of texture data.
2003    pub(crate) planes: arrayvec::ArrayVec<Arc<TextureView>, 3>,
2004    /// Buffer containing a [`crate::device::resource::ExternalTextureParams`]
2005    /// describing the external texture.
2006    pub(crate) params: Arc<Buffer>,
2007    /// The `label` from the descriptor used to create the resource.
2008    pub(crate) label: String,
2009    pub(crate) tracking_data: TrackingData,
2010}
2011
2012impl Drop for ExternalTexture {
2013    fn drop(&mut self) {
2014        resource_log!("Destroy raw {}", self.error_ident());
2015    }
2016}
2017
2018impl ExternalTexture {
2019    pub fn destroy(self: &Arc<Self>) {
2020        self.params.destroy();
2021    }
2022}
2023
2024#[derive(Clone, Debug, Error)]
2025#[non_exhaustive]
2026pub enum CreateExternalTextureError {
2027    #[error(transparent)]
2028    Device(#[from] DeviceError),
2029    #[error(transparent)]
2030    MissingFeatures(#[from] MissingFeatures),
2031    #[error(transparent)]
2032    InvalidResource(#[from] InvalidResourceError),
2033    #[error(transparent)]
2034    CreateBuffer(#[from] CreateBufferError),
2035    #[error(transparent)]
2036    QueueWrite(#[from] queue::QueueWriteError),
2037    #[error("External texture format {format:?} expects {expected} planes, but given {provided}")]
2038    IncorrectPlaneCount {
2039        format: wgt::ExternalTextureFormat,
2040        expected: usize,
2041        provided: usize,
2042    },
2043    #[error("External texture planes cannot be multisampled, but given view with samples = {0}")]
2044    InvalidPlaneMultisample(u32),
2045    #[error("External texture planes expect a filterable float sample type, but given view with format {format:?} (sample type {sample_type:?})")]
2046    InvalidPlaneSampleType {
2047        format: wgt::TextureFormat,
2048        sample_type: wgt::TextureSampleType,
2049    },
2050    #[error("External texture planes expect 2D dimension, but given view with dimension = {0:?}")]
2051    InvalidPlaneDimension(wgt::TextureViewDimension),
2052    #[error(transparent)]
2053    MissingTextureUsage(#[from] MissingTextureUsageError),
2054    #[error("External texture format {format:?} plane {plane} expects format with {expected} components but given view with format {provided:?} ({} components)",
2055        provided.components())]
2056    InvalidPlaneFormat {
2057        format: wgt::ExternalTextureFormat,
2058        plane: usize,
2059        expected: u8,
2060        provided: wgt::TextureFormat,
2061    },
2062}
2063
2064impl WebGpuError for CreateExternalTextureError {
2065    fn webgpu_error_type(&self) -> ErrorType {
2066        match self {
2067            CreateExternalTextureError::Device(e) => e.webgpu_error_type(),
2068            CreateExternalTextureError::MissingFeatures(e) => e.webgpu_error_type(),
2069            CreateExternalTextureError::InvalidResource(e) => e.webgpu_error_type(),
2070            CreateExternalTextureError::CreateBuffer(e) => e.webgpu_error_type(),
2071            CreateExternalTextureError::QueueWrite(e) => e.webgpu_error_type(),
2072            CreateExternalTextureError::MissingTextureUsage(e) => e.webgpu_error_type(),
2073            CreateExternalTextureError::IncorrectPlaneCount { .. }
2074            | CreateExternalTextureError::InvalidPlaneMultisample(_)
2075            | CreateExternalTextureError::InvalidPlaneSampleType { .. }
2076            | CreateExternalTextureError::InvalidPlaneDimension(_)
2077            | CreateExternalTextureError::InvalidPlaneFormat { .. } => ErrorType::Validation,
2078        }
2079    }
2080}
2081
2082crate::impl_resource_type!(ExternalTexture);
2083crate::impl_labeled!(ExternalTexture);
2084crate::impl_parent_device!(ExternalTexture);
2085crate::impl_storage_item!(ExternalTexture);
2086crate::impl_trackable!(ExternalTexture);
2087
2088/// Describes a [`Sampler`]
2089#[derive(Clone, Debug, PartialEq)]
2090#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2091pub struct SamplerDescriptor<'a> {
2092    /// Debug label of the sampler.
2093    ///
2094    /// This will show up in graphics debuggers for easy identification.
2095    pub label: Label<'a>,
2096    /// How to deal with out of bounds accesses in the u (i.e. x) direction
2097    pub address_modes: [wgt::AddressMode; 3],
2098    /// How to filter the texture when it needs to be magnified (made larger)
2099    pub mag_filter: wgt::FilterMode,
2100    /// How to filter the texture when it needs to be minified (made smaller)
2101    pub min_filter: wgt::FilterMode,
2102    /// How to filter between mip map levels
2103    pub mipmap_filter: wgt::MipmapFilterMode,
2104    /// Minimum level of detail (i.e. mip level) to use
2105    pub lod_min_clamp: f32,
2106    /// Maximum level of detail (i.e. mip level) to use
2107    pub lod_max_clamp: f32,
2108    /// If this is enabled, this is a comparison sampler using the given comparison function.
2109    pub compare: Option<wgt::CompareFunction>,
2110    /// Must be at least 1. If this is not 1, all filter modes must be linear.
2111    pub anisotropy_clamp: u16,
2112    /// Border color to use when address_mode is
2113    /// [`AddressMode::ClampToBorder`](wgt::AddressMode::ClampToBorder)
2114    pub border_color: Option<wgt::SamplerBorderColor>,
2115}
2116
2117#[derive(Debug)]
2118pub struct Sampler {
2119    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynSampler>>,
2120    pub(crate) device: Arc<Device>,
2121    /// The `label` from the descriptor used to create the resource.
2122    pub(crate) label: String,
2123    pub(crate) tracking_data: TrackingData,
2124    /// `true` if this is a comparison sampler
2125    pub(crate) comparison: bool,
2126    /// `true` if this is a filtering sampler
2127    pub(crate) filtering: bool,
2128}
2129
2130impl Drop for Sampler {
2131    fn drop(&mut self) {
2132        resource_log!("Destroy raw {}", self.error_ident());
2133        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
2134        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
2135        unsafe {
2136            self.device.raw().destroy_sampler(raw);
2137        }
2138    }
2139}
2140
2141impl Sampler {
2142    pub(crate) fn raw(&self) -> &dyn hal::DynSampler {
2143        self.raw.as_ref()
2144    }
2145}
2146
2147#[derive(Copy, Clone)]
2148pub enum SamplerFilterErrorType {
2149    MagFilter,
2150    MinFilter,
2151    MipmapFilter,
2152}
2153
2154impl fmt::Debug for SamplerFilterErrorType {
2155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2156        match *self {
2157            SamplerFilterErrorType::MagFilter => write!(f, "magFilter"),
2158            SamplerFilterErrorType::MinFilter => write!(f, "minFilter"),
2159            SamplerFilterErrorType::MipmapFilter => write!(f, "mipmapFilter"),
2160        }
2161    }
2162}
2163
2164#[derive(Clone, Debug, Error)]
2165#[non_exhaustive]
2166pub enum CreateSamplerError {
2167    #[error(transparent)]
2168    Device(#[from] DeviceError),
2169    #[error("Invalid lodMinClamp: {0}. Must be greater or equal to 0.0")]
2170    InvalidLodMinClamp(f32),
2171    #[error("Invalid lodMaxClamp: {lod_max_clamp}. Must be greater or equal to lodMinClamp (which is {lod_min_clamp}).")]
2172    InvalidLodMaxClamp {
2173        lod_min_clamp: f32,
2174        lod_max_clamp: f32,
2175    },
2176    #[error("Invalid anisotropic clamp: {0}. Must be at least 1.")]
2177    InvalidAnisotropy(u16),
2178    #[error("Invalid filter mode for {filter_type:?}: {filter_mode:?}. When anistropic clamp is not 1 (it is {anisotropic_clamp}), all filter modes must be linear.")]
2179    InvalidFilterModeWithAnisotropy {
2180        filter_type: SamplerFilterErrorType,
2181        filter_mode: wgt::FilterMode,
2182        anisotropic_clamp: u16,
2183    },
2184    #[error("Invalid filter mode for {filter_type:?}: {filter_mode:?}. When anistropic clamp is not 1 (it is {anisotropic_clamp}), all filter modes must be linear.")]
2185    InvalidMipmapFilterModeWithAnisotropy {
2186        filter_type: SamplerFilterErrorType,
2187        filter_mode: wgt::MipmapFilterMode,
2188        anisotropic_clamp: u16,
2189    },
2190    #[error(transparent)]
2191    MissingFeatures(#[from] MissingFeatures),
2192}
2193
2194crate::impl_resource_type!(Sampler);
2195crate::impl_labeled!(Sampler);
2196crate::impl_parent_device!(Sampler);
2197crate::impl_storage_item!(Sampler);
2198crate::impl_trackable!(Sampler);
2199
2200impl WebGpuError for CreateSamplerError {
2201    fn webgpu_error_type(&self) -> ErrorType {
2202        match self {
2203            Self::Device(e) => e.webgpu_error_type(),
2204            Self::MissingFeatures(e) => e.webgpu_error_type(),
2205
2206            Self::InvalidLodMinClamp(_)
2207            | Self::InvalidLodMaxClamp { .. }
2208            | Self::InvalidAnisotropy(_)
2209            | Self::InvalidFilterModeWithAnisotropy { .. }
2210            | Self::InvalidMipmapFilterModeWithAnisotropy { .. } => ErrorType::Validation,
2211        }
2212    }
2213}
2214
2215#[derive(Clone, Debug, Error)]
2216#[non_exhaustive]
2217pub enum CreateQuerySetError {
2218    #[error(transparent)]
2219    Device(#[from] DeviceError),
2220    #[error("QuerySets cannot be made with zero queries")]
2221    ZeroCount,
2222    #[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")]
2223    TooManyQueries { count: u32, maximum: u32 },
2224    #[error(transparent)]
2225    MissingFeatures(#[from] MissingFeatures),
2226}
2227
2228impl WebGpuError for CreateQuerySetError {
2229    fn webgpu_error_type(&self) -> ErrorType {
2230        match self {
2231            Self::Device(e) => e.webgpu_error_type(),
2232            Self::MissingFeatures(e) => e.webgpu_error_type(),
2233
2234            Self::TooManyQueries { .. } | Self::ZeroCount => ErrorType::Validation,
2235        }
2236    }
2237}
2238
2239pub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor<Label<'a>>;
2240
2241#[derive(Debug)]
2242pub struct QuerySet {
2243    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynQuerySet>>,
2244    pub(crate) device: Arc<Device>,
2245    /// The `label` from the descriptor used to create the resource.
2246    pub(crate) label: String,
2247    pub(crate) tracking_data: TrackingData,
2248    pub(crate) desc: wgt::QuerySetDescriptor<()>,
2249}
2250
2251impl Drop for QuerySet {
2252    fn drop(&mut self) {
2253        resource_log!("Destroy raw {}", self.error_ident());
2254        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
2255        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
2256        unsafe {
2257            self.device.raw().destroy_query_set(raw);
2258        }
2259    }
2260}
2261
2262crate::impl_resource_type!(QuerySet);
2263crate::impl_labeled!(QuerySet);
2264crate::impl_parent_device!(QuerySet);
2265crate::impl_storage_item!(QuerySet);
2266crate::impl_trackable!(QuerySet);
2267
2268impl QuerySet {
2269    pub(crate) fn raw(&self) -> &dyn hal::DynQuerySet {
2270        self.raw.as_ref()
2271    }
2272}
2273
2274pub type BlasDescriptor<'a> = wgt::CreateBlasDescriptor<Label<'a>>;
2275pub type TlasDescriptor<'a> = wgt::CreateTlasDescriptor<Label<'a>>;
2276
2277pub type BlasPrepareCompactResult = Result<(), BlasPrepareCompactError>;
2278
2279#[cfg(send_sync)]
2280pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + Send + 'static>;
2281#[cfg(not(send_sync))]
2282pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + 'static>;
2283
2284pub(crate) struct BlasPendingCompact {
2285    pub(crate) op: Option<BlasCompactCallback>,
2286    // hold the parent alive while the mapping is active
2287    pub(crate) _parent_blas: Arc<Blas>,
2288}
2289
2290impl fmt::Debug for BlasPendingCompact {
2291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2292        f.debug_struct("BlasPendingCompact")
2293            .field("op", &())
2294            .field("_parent_blas", &self._parent_blas)
2295            .finish()
2296    }
2297}
2298
2299#[derive(Debug)]
2300pub(crate) enum BlasCompactState {
2301    /// Created from a compact operation.
2302    Compacted,
2303    /// Waiting for GPU to be done before mapping to get compacted size
2304    Waiting(BlasPendingCompact),
2305    /// Ready to be compacted
2306    Ready { size: wgt::BufferAddress },
2307    /// Ready to prepare to compact.
2308    Idle,
2309}
2310
2311#[cfg(send_sync)]
2312unsafe impl Send for BlasCompactState {}
2313#[cfg(send_sync)]
2314unsafe impl Sync for BlasCompactState {}
2315
2316#[derive(Debug)]
2317pub struct Blas {
2318    pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
2319    pub(crate) device: Arc<Device>,
2320    pub(crate) size_info: hal::AccelerationStructureBuildSizes,
2321    pub(crate) sizes: wgt::BlasGeometrySizeDescriptors,
2322    pub(crate) flags: wgt::AccelerationStructureFlags,
2323    pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
2324    pub(crate) built_index: RwLock<Option<NonZeroU64>>,
2325    pub(crate) handle: u64,
2326    /// The `label` from the descriptor used to create the resource.
2327    pub(crate) label: String,
2328    pub(crate) tracking_data: TrackingData,
2329    pub(crate) compaction_buffer: Option<ManuallyDrop<Box<dyn hal::DynBuffer>>>,
2330    pub(crate) compacted_state: Mutex<BlasCompactState>,
2331}
2332
2333impl Drop for Blas {
2334    fn drop(&mut self) {
2335        resource_log!("Destroy raw {}", self.error_ident());
2336        // SAFETY: We are in the Drop impl, and we don't use self.raw or self.compaction_buffer anymore after this point.
2337        if let Some(raw) = self.raw.take() {
2338            unsafe {
2339                self.device.raw().destroy_acceleration_structure(raw);
2340            }
2341        }
2342        if let Some(mut raw) = self.compaction_buffer.take() {
2343            unsafe {
2344                self.device
2345                    .raw()
2346                    .destroy_buffer(ManuallyDrop::take(&mut raw))
2347            }
2348        }
2349    }
2350}
2351
2352impl RawResourceAccess for Blas {
2353    type DynResource = dyn hal::DynAccelerationStructure;
2354
2355    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2356        self.raw.get(guard).map(|it| it.as_ref())
2357    }
2358}
2359
2360impl Blas {
2361    pub(crate) fn prepare_compact_async(
2362        self: &Arc<Self>,
2363        op: Option<BlasCompactCallback>,
2364    ) -> Result<SubmissionIndex, (Option<BlasCompactCallback>, BlasPrepareCompactError)> {
2365        let device = &self.device;
2366        if let Err(e) = device.check_is_valid() {
2367            return Err((op, e.into()));
2368        }
2369
2370        if self.built_index.read().is_none() {
2371            return Err((op, BlasPrepareCompactError::NotBuilt));
2372        }
2373
2374        if !self
2375            .flags
2376            .contains(wgt::AccelerationStructureFlags::ALLOW_COMPACTION)
2377        {
2378            return Err((op, BlasPrepareCompactError::CompactionUnsupported));
2379        }
2380
2381        let mut state = self.compacted_state.lock();
2382        *state = match *state {
2383            BlasCompactState::Compacted => {
2384                return Err((op, BlasPrepareCompactError::DoubleCompaction))
2385            }
2386            BlasCompactState::Waiting(_) => {
2387                return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
2388            }
2389            BlasCompactState::Ready { .. } => {
2390                return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
2391            }
2392            BlasCompactState::Idle => BlasCompactState::Waiting(BlasPendingCompact {
2393                op,
2394                _parent_blas: self.clone(),
2395            }),
2396        };
2397
2398        let submit_index = if let Some(queue) = device.get_queue() {
2399            queue.lock_life().prepare_compact(self).unwrap_or(0) // '0' means no wait is necessary
2400        } else {
2401            // We can safely unwrap below since we just set the `compacted_state` to `BlasCompactState::Waiting`.
2402            let (mut callback, status) = self.read_back_compact_size().unwrap();
2403            if let Some(callback) = callback.take() {
2404                callback(status);
2405            }
2406            0
2407        };
2408
2409        Ok(submit_index)
2410    }
2411
2412    /// This function returns [`None`] only if [`Self::compacted_state`] is not [`BlasCompactState::Waiting`].
2413    #[must_use]
2414    pub(crate) fn read_back_compact_size(&self) -> Option<BlasCompactReadyPendingClosure> {
2415        let mut state = self.compacted_state.lock();
2416        let pending_compact = match mem::replace(&mut *state, BlasCompactState::Idle) {
2417            BlasCompactState::Waiting(pending_mapping) => pending_mapping,
2418            // Compaction cancelled e.g. by rebuild
2419            BlasCompactState::Idle => return None,
2420            BlasCompactState::Ready { .. } => {
2421                unreachable!("This should be validated out by `prepare_for_compaction`")
2422            }
2423            _ => panic!("No pending mapping."),
2424        };
2425        let status = {
2426            let compaction_buffer = self.compaction_buffer.as_ref().unwrap().as_ref();
2427            unsafe {
2428                let map_res = self.device.raw().map_buffer(
2429                    compaction_buffer,
2430                    0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress,
2431                );
2432                match map_res {
2433                    Ok(mapping) => {
2434                        if !mapping.is_coherent {
2435                            #[expect(clippy::single_range_in_vec_init, reason = "intentional")]
2436                            self.device.raw().invalidate_mapped_ranges(
2437                                compaction_buffer,
2438                                &[0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress],
2439                            );
2440                        }
2441                        let size = core::ptr::read_unaligned(
2442                            mapping.ptr.as_ptr().cast::<wgt::BufferAddress>(),
2443                        );
2444                        self.device.raw().unmap_buffer(compaction_buffer);
2445                        if self.size_info.acceleration_structure_size != 0 {
2446                            debug_assert_ne!(size, 0);
2447                        }
2448                        *state = BlasCompactState::Ready { size };
2449                        Ok(())
2450                    }
2451                    Err(err) => Err(BlasPrepareCompactError::from(DeviceError::from_hal(err))),
2452                }
2453            }
2454        };
2455        Some((pending_compact.op, status))
2456    }
2457}
2458
2459crate::impl_resource_type!(Blas);
2460crate::impl_labeled!(Blas);
2461crate::impl_parent_device!(Blas);
2462crate::impl_storage_item!(Blas);
2463crate::impl_trackable!(Blas);
2464
2465#[derive(Debug)]
2466pub struct Tlas {
2467    pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
2468    pub(crate) device: Arc<Device>,
2469    pub(crate) size_info: hal::AccelerationStructureBuildSizes,
2470    pub(crate) max_instance_count: u32,
2471    pub(crate) flags: wgt::AccelerationStructureFlags,
2472    pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
2473    pub(crate) built_index: RwLock<Option<NonZeroU64>>,
2474    pub(crate) dependencies: RwLock<Vec<Arc<Blas>>>,
2475    pub(crate) instance_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,
2476    /// The `label` from the descriptor used to create the resource.
2477    pub(crate) label: String,
2478    pub(crate) tracking_data: TrackingData,
2479}
2480
2481impl Drop for Tlas {
2482    fn drop(&mut self) {
2483        unsafe {
2484            resource_log!("Destroy raw {}", self.error_ident());
2485            if let Some(structure) = self.raw.take() {
2486                self.device.raw().destroy_acceleration_structure(structure);
2487            }
2488            let buffer = ManuallyDrop::take(&mut self.instance_buffer);
2489            self.device.raw().destroy_buffer(buffer);
2490        }
2491    }
2492}
2493
2494impl RawResourceAccess for Tlas {
2495    type DynResource = dyn hal::DynAccelerationStructure;
2496
2497    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2498        self.raw.get(guard).map(|raw| raw.as_ref())
2499    }
2500}
2501
2502crate::impl_resource_type!(Tlas);
2503crate::impl_labeled!(Tlas);
2504crate::impl_parent_device!(Tlas);
2505crate::impl_storage_item!(Tlas);
2506crate::impl_trackable!(Tlas);