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