wgpu_core/
resource.rs

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