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