wgpu_core/
resource.rs

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