Skip to main content

wgpu_core/
resource.rs

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