Skip to main content

wgpu_core/
resource.rs

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