wgpu_core/
resource.rs

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