wgpu_core/
resource.rs

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