Skip to main content

wgpu_core/
resource.rs

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