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