Skip to main content

wgpu_core/
resource.rs

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