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    binding_model::BindGroup,
21    device::{
22        queue, resource::DeferredDestroy, BufferMapPendingClosure, Device, DeviceError,
23        DeviceMismatch, HostMap, MissingDownlevelFlags, MissingFeatures,
24    },
25    init_tracker::{BufferInitTracker, TextureInitTracker},
26    lock::{rank, Mutex, RwLock},
27    ray_tracing::{BlasCompactReadyPendingClosure, BlasPrepareCompactError},
28    resource_log,
29    snatch::{SnatchGuard, Snatchable},
30    timestamp_normalization::TimestampNormalizationBindGroup,
31    track::{SharedTrackerIndexAllocator, TrackerIndex},
32    weak_vec::WeakVec,
33    Label, LabelHelpers, SubmissionIndex,
34};
35
36/// Information about the wgpu-core resource.
37///
38/// Each type representing a `wgpu-core` resource, like [`Device`],
39/// [`Buffer`], etc., contains a `ResourceInfo` which contains
40/// its latest submission index and label.
41///
42/// A resource may need to be retained for any of several reasons:
43/// and any lifetime logic will be handled by `Arc<Resource>` refcount
44///
45/// - The user may hold a reference to it (via a `wgpu::Buffer`, say).
46///
47/// - Other resources may depend on it (a texture view's backing
48///   texture, for example).
49///
50/// - It may be used by commands sent to the GPU that have not yet
51///   finished execution.
52///
53/// [`Device`]: crate::device::resource::Device
54/// [`Buffer`]: crate::resource::Buffer
55#[derive(Debug)]
56pub(crate) struct TrackingData {
57    tracker_index: TrackerIndex,
58    tracker_indices: Arc<SharedTrackerIndexAllocator>,
59}
60
61impl Drop for TrackingData {
62    fn drop(&mut self) {
63        self.tracker_indices.free(self.tracker_index);
64    }
65}
66
67impl TrackingData {
68    pub(crate) fn new(tracker_indices: Arc<SharedTrackerIndexAllocator>) -> Self {
69        Self {
70            tracker_index: tracker_indices.alloc(),
71            tracker_indices,
72        }
73    }
74
75    pub(crate) fn tracker_index(&self) -> TrackerIndex {
76        self.tracker_index
77    }
78}
79
80#[derive(Clone, Debug)]
81#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82pub struct ResourceErrorIdent {
83    r#type: Cow<'static, str>,
84    label: String,
85}
86
87impl fmt::Display for ResourceErrorIdent {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
89        write!(f, "{} with '{}' label", self.r#type, self.label)
90    }
91}
92
93pub trait ParentDevice: Labeled {
94    fn device(&self) -> &Arc<Device>;
95
96    fn is_equal(self: &Arc<Self>, other: &Arc<Self>) -> bool {
97        Arc::ptr_eq(self, other)
98    }
99
100    fn same_device_as<O: ParentDevice>(&self, other: &O) -> Result<(), DeviceError> {
101        if Arc::ptr_eq(self.device(), other.device()) {
102            Ok(())
103        } else {
104            Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
105                res: self.error_ident(),
106                res_device: self.device().error_ident(),
107                target: Some(other.error_ident()),
108                target_device: other.device().error_ident(),
109            })))
110        }
111    }
112
113    fn same_device(&self, device: &Device) -> Result<(), DeviceError> {
114        if core::ptr::eq(&**self.device(), device) {
115            Ok(())
116        } else {
117            Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
118                res: self.error_ident(),
119                res_device: self.device().error_ident(),
120                target: None,
121                target_device: device.error_ident(),
122            })))
123        }
124    }
125}
126
127#[macro_export]
128macro_rules! impl_parent_device {
129    ($ty:ident) => {
130        impl $crate::resource::ParentDevice for $ty {
131            fn device(&self) -> &Arc<Device> {
132                &self.device
133            }
134        }
135    };
136}
137
138/// Allow access to the hal resource as guarded by the `SnatchGuard`.
139pub trait RawResourceAccess: ParentDevice {
140    type DynResource: hal::DynResource + ?Sized;
141
142    /// Get access to the raw resource if it is not destroyed.
143    ///
144    /// Returns `None` if the resource has been destroyed. This method
145    /// does not allocate in either case.
146    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource>;
147
148    /// Get access to the raw resource if it is not destroyed.
149    ///
150    /// Returns a full error if the resource has been destroyed. This
151    /// method allocates a label in the error case.
152    fn try_raw<'a>(
153        &'a self,
154        guard: &'a SnatchGuard,
155    ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
156        self.raw(guard)
157            .ok_or_else(|| DestroyedResourceError(self.error_ident()))
158    }
159}
160
161pub trait ResourceType {
162    const TYPE: &'static str;
163}
164
165#[macro_export]
166macro_rules! impl_resource_type {
167    ($ty:ident) => {
168        impl $crate::resource::ResourceType for $ty {
169            const TYPE: &'static str = stringify!($ty);
170        }
171    };
172}
173
174pub trait Labeled: ResourceType {
175    /// Returns a string identifying this resource for logging and errors.
176    ///
177    /// It may be a user-provided string or it may be a placeholder from wgpu.
178    ///
179    /// It is non-empty unless the user-provided string was empty.
180    fn label(&self) -> &str;
181
182    fn error_ident(&self) -> ResourceErrorIdent {
183        ResourceErrorIdent {
184            r#type: Cow::Borrowed(Self::TYPE),
185            label: self.label().to_owned(),
186        }
187    }
188}
189
190#[macro_export]
191macro_rules! impl_labeled {
192    ($ty:ident) => {
193        impl $crate::resource::Labeled for $ty {
194            fn label(&self) -> &str {
195                &self.label
196            }
197        }
198    };
199}
200
201pub(crate) trait Trackable {
202    fn tracker_index(&self) -> TrackerIndex;
203}
204
205#[macro_export]
206macro_rules! impl_trackable {
207    ($ty:ident) => {
208        impl $crate::resource::Trackable for $ty {
209            fn tracker_index(&self) -> $crate::track::TrackerIndex {
210                self.tracking_data.tracker_index()
211            }
212        }
213    };
214}
215
216#[derive(Debug)]
217pub(crate) enum BufferMapState {
218    /// Mapped at creation.
219    Init { staging_buffer: StagingBuffer },
220    /// Waiting for GPU to be done before mapping
221    Waiting(BufferPendingMapping),
222    /// Mapped
223    Active {
224        mapping: hal::BufferMapping,
225        range: hal::MemoryRange,
226        host: HostMap,
227    },
228    /// Not mapped
229    Idle,
230}
231
232#[cfg(send_sync)]
233unsafe impl Send for BufferMapState {}
234#[cfg(send_sync)]
235unsafe impl Sync for BufferMapState {}
236
237#[cfg(send_sync)]
238pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + Send + 'static>;
239#[cfg(not(send_sync))]
240pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + 'static>;
241
242pub struct BufferMapOperation {
243    pub host: HostMap,
244    pub callback: Option<BufferMapCallback>,
245}
246
247impl fmt::Debug for BufferMapOperation {
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        f.debug_struct("BufferMapOperation")
250            .field("host", &self.host)
251            .field("callback", &self.callback.as_ref().map(|_| "?"))
252            .finish()
253    }
254}
255
256#[derive(Clone, Debug, Error)]
257#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
258#[non_exhaustive]
259pub enum BufferAccessError {
260    #[error(transparent)]
261    Device(#[from] DeviceError),
262    #[error("Buffer map failed")]
263    Failed,
264    #[error(transparent)]
265    DestroyedResource(#[from] DestroyedResourceError),
266    #[error("Buffer is already mapped")]
267    AlreadyMapped,
268    #[error("Buffer map is pending")]
269    MapAlreadyPending,
270    #[error(transparent)]
271    MissingBufferUsage(#[from] MissingBufferUsageError),
272    #[error("Buffer is not mapped")]
273    NotMapped,
274    #[error(
275        "Buffer map range must start aligned to `MAP_ALIGNMENT` and end to `COPY_BUFFER_ALIGNMENT`"
276    )]
277    UnalignedRange,
278    #[error("Buffer offset invalid: offset {offset} must be multiple of 8")]
279    UnalignedOffset { offset: wgt::BufferAddress },
280    #[error("Buffer range size invalid: range_size {range_size} must be multiple of 4")]
281    UnalignedRangeSize { range_size: wgt::BufferAddress },
282    #[error("Buffer access out of bounds: index {index} would underrun the buffer (limit: {min})")]
283    OutOfBoundsUnderrun {
284        index: wgt::BufferAddress,
285        min: wgt::BufferAddress,
286    },
287    #[error(
288        "Buffer access out of bounds: last index {index} would overrun the buffer (limit: {max})"
289    )]
290    OutOfBoundsOverrun {
291        index: wgt::BufferAddress,
292        max: wgt::BufferAddress,
293    },
294    #[error("Buffer map range start {start} is greater than end {end}")]
295    NegativeRange {
296        start: wgt::BufferAddress,
297        end: wgt::BufferAddress,
298    },
299    #[error("Buffer map aborted")]
300    MapAborted,
301    #[error(transparent)]
302    InvalidResource(#[from] InvalidResourceError),
303}
304
305impl WebGpuError for BufferAccessError {
306    fn webgpu_error_type(&self) -> ErrorType {
307        let e: &dyn WebGpuError = match self {
308            Self::Device(e) => e,
309            Self::InvalidResource(e) => e,
310            Self::DestroyedResource(e) => e,
311
312            Self::Failed
313            | Self::AlreadyMapped
314            | Self::MapAlreadyPending
315            | Self::MissingBufferUsage(_)
316            | Self::NotMapped
317            | Self::UnalignedRange
318            | Self::UnalignedOffset { .. }
319            | Self::UnalignedRangeSize { .. }
320            | Self::OutOfBoundsUnderrun { .. }
321            | Self::OutOfBoundsOverrun { .. }
322            | Self::NegativeRange { .. }
323            | Self::MapAborted => return ErrorType::Validation,
324        };
325        e.webgpu_error_type()
326    }
327}
328
329#[derive(Clone, Debug, Error)]
330#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
331#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
332pub struct MissingBufferUsageError {
333    pub(crate) res: ResourceErrorIdent,
334    pub(crate) actual: wgt::BufferUsages,
335    pub(crate) expected: wgt::BufferUsages,
336}
337
338impl WebGpuError for MissingBufferUsageError {
339    fn webgpu_error_type(&self) -> ErrorType {
340        ErrorType::Validation
341    }
342}
343
344#[derive(Clone, Debug, Error)]
345#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
346pub struct MissingTextureUsageError {
347    pub(crate) res: ResourceErrorIdent,
348    pub(crate) actual: wgt::TextureUsages,
349    pub(crate) expected: wgt::TextureUsages,
350}
351
352impl WebGpuError for MissingTextureUsageError {
353    fn webgpu_error_type(&self) -> ErrorType {
354        ErrorType::Validation
355    }
356}
357
358#[derive(Clone, Debug, Error)]
359#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
360#[error("{0} has been destroyed")]
361pub struct DestroyedResourceError(pub ResourceErrorIdent);
362
363impl WebGpuError for DestroyedResourceError {
364    fn webgpu_error_type(&self) -> ErrorType {
365        ErrorType::Validation
366    }
367}
368
369#[derive(Clone, Debug, Error)]
370#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
371#[error("{0} is invalid")]
372pub struct InvalidResourceError(pub ResourceErrorIdent);
373
374impl WebGpuError for InvalidResourceError {
375    fn webgpu_error_type(&self) -> ErrorType {
376        ErrorType::Validation
377    }
378}
379
380pub enum Fallible<T: ParentDevice> {
381    Valid(Arc<T>),
382    Invalid(Arc<String>),
383}
384
385impl<T: ParentDevice> Fallible<T> {
386    pub fn get(self) -> Result<Arc<T>, InvalidResourceError> {
387        match self {
388            Fallible::Valid(v) => Ok(v),
389            Fallible::Invalid(label) => Err(InvalidResourceError(ResourceErrorIdent {
390                r#type: Cow::Borrowed(T::TYPE),
391                label: (*label).clone(),
392            })),
393        }
394    }
395}
396
397impl<T: ParentDevice> Clone for Fallible<T> {
398    fn clone(&self) -> Self {
399        match self {
400            Self::Valid(v) => Self::Valid(v.clone()),
401            Self::Invalid(l) => Self::Invalid(l.clone()),
402        }
403    }
404}
405
406impl<T: ParentDevice> ResourceType for Fallible<T> {
407    const TYPE: &'static str = T::TYPE;
408}
409
410impl<T: ParentDevice + crate::storage::StorageItem> crate::storage::StorageItem for Fallible<T> {
411    type Marker = T::Marker;
412}
413
414pub type BufferAccessResult = Result<(), BufferAccessError>;
415
416#[derive(Debug)]
417pub(crate) struct BufferPendingMapping {
418    pub(crate) range: Range<wgt::BufferAddress>,
419    pub(crate) op: BufferMapOperation,
420    // hold the parent alive while the mapping is active
421    pub(crate) _parent_buffer: Arc<Buffer>,
422}
423
424pub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;
425
426#[derive(Debug)]
427pub struct Buffer {
428    pub(crate) raw: Snatchable<Box<dyn hal::DynBuffer>>,
429    pub(crate) device: Arc<Device>,
430    pub(crate) usage: wgt::BufferUsages,
431    pub(crate) size: wgt::BufferAddress,
432    pub(crate) initialization_status: RwLock<BufferInitTracker>,
433    /// The `label` from the descriptor used to create the resource.
434    pub(crate) label: String,
435    pub(crate) tracking_data: TrackingData,
436    pub(crate) map_state: Mutex<BufferMapState>,
437    pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
438    pub(crate) timestamp_normalization_bind_group: Snatchable<TimestampNormalizationBindGroup>,
439    pub(crate) indirect_validation_bind_groups: Snatchable<crate::indirect_validation::BindGroups>,
440}
441
442impl Drop for Buffer {
443    fn drop(&mut self) {
444        if let Some(raw) = self.timestamp_normalization_bind_group.take() {
445            raw.dispose(self.device.raw());
446        }
447
448        if let Some(raw) = self.indirect_validation_bind_groups.take() {
449            raw.dispose(self.device.raw());
450        }
451
452        if let Some(raw) = self.raw.take() {
453            resource_log!("Destroy raw {}", self.error_ident());
454            unsafe {
455                self.device.raw().destroy_buffer(raw);
456            }
457        }
458    }
459}
460
461impl RawResourceAccess for Buffer {
462    type DynResource = dyn hal::DynBuffer;
463
464    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
465        self.raw.get(guard).map(|b| b.as_ref())
466    }
467}
468
469impl Buffer {
470    pub(crate) fn check_destroyed(
471        &self,
472        guard: &SnatchGuard,
473    ) -> Result<(), DestroyedResourceError> {
474        self.raw
475            .get(guard)
476            .map(|_| ())
477            .ok_or_else(|| DestroyedResourceError(self.error_ident()))
478    }
479
480    /// Checks that the given buffer usage contains the required buffer usage,
481    /// returns an error otherwise.
482    pub(crate) fn check_usage(
483        &self,
484        expected: wgt::BufferUsages,
485    ) -> Result<(), MissingBufferUsageError> {
486        if self.usage.contains(expected) {
487            Ok(())
488        } else {
489            Err(MissingBufferUsageError {
490                res: self.error_ident(),
491                actual: self.usage,
492                expected,
493            })
494        }
495    }
496
497    /// Returns the mapping callback in case of error so that the callback can be fired outside
498    /// of the locks that are held in this function.
499    pub(crate) fn map_async(
500        self: &Arc<Self>,
501        offset: wgt::BufferAddress,
502        size: Option<wgt::BufferAddress>,
503        op: BufferMapOperation,
504    ) -> Result<SubmissionIndex, (BufferMapOperation, BufferAccessError)> {
505        let range_size = if let Some(size) = size {
506            size
507        } else {
508            self.size.saturating_sub(offset)
509        };
510
511        if offset % wgt::MAP_ALIGNMENT != 0 {
512            return Err((op, BufferAccessError::UnalignedOffset { offset }));
513        }
514        if range_size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
515            return Err((op, BufferAccessError::UnalignedRangeSize { range_size }));
516        }
517
518        let range = offset..(offset + range_size);
519
520        if range.start % wgt::MAP_ALIGNMENT != 0 || range.end % wgt::COPY_BUFFER_ALIGNMENT != 0 {
521            return Err((op, BufferAccessError::UnalignedRange));
522        }
523
524        let (pub_usage, internal_use) = match op.host {
525            HostMap::Read => (wgt::BufferUsages::MAP_READ, wgt::BufferUses::MAP_READ),
526            HostMap::Write => (wgt::BufferUsages::MAP_WRITE, wgt::BufferUses::MAP_WRITE),
527        };
528
529        if let Err(e) = self.check_usage(pub_usage) {
530            return Err((op, e.into()));
531        }
532
533        if range.start > range.end {
534            return Err((
535                op,
536                BufferAccessError::NegativeRange {
537                    start: range.start,
538                    end: range.end,
539                },
540            ));
541        }
542        if range.end > self.size {
543            return Err((
544                op,
545                BufferAccessError::OutOfBoundsOverrun {
546                    index: range.end,
547                    max: self.size,
548                },
549            ));
550        }
551
552        let device = &self.device;
553        if let Err(e) = device.check_is_valid() {
554            return Err((op, e.into()));
555        }
556
557        {
558            let snatch_guard = device.snatchable_lock.read();
559            if let Err(e) = self.check_destroyed(&snatch_guard) {
560                return Err((op, e.into()));
561            }
562        }
563
564        {
565            let map_state = &mut *self.map_state.lock();
566            *map_state = match *map_state {
567                BufferMapState::Init { .. } | BufferMapState::Active { .. } => {
568                    return Err((op, BufferAccessError::AlreadyMapped));
569                }
570                BufferMapState::Waiting(_) => {
571                    return Err((op, BufferAccessError::MapAlreadyPending));
572                }
573                BufferMapState::Idle => BufferMapState::Waiting(BufferPendingMapping {
574                    range,
575                    op,
576                    _parent_buffer: self.clone(),
577                }),
578            };
579        }
580
581        // TODO: we are ignoring the transition here, I think we need to add a barrier
582        // at the end of the submission
583        device
584            .trackers
585            .lock()
586            .buffers
587            .set_single(self, internal_use);
588
589        let submit_index = if let Some(queue) = device.get_queue() {
590            queue.lock_life().map(self).unwrap_or(0) // '0' means no wait is necessary
591        } else {
592            // We can safely unwrap below since we just set the `map_state` to `BufferMapState::Waiting`.
593            let (mut operation, status) = self.map(&device.snatchable_lock.read()).unwrap();
594            if let Some(callback) = operation.callback.take() {
595                callback(status);
596            }
597            0
598        };
599
600        Ok(submit_index)
601    }
602
603    /// This function returns [`None`] only if [`Self::map_state`] is not [`BufferMapState::Waiting`].
604    #[must_use]
605    pub(crate) fn map(&self, snatch_guard: &SnatchGuard) -> Option<BufferMapPendingClosure> {
606        // This _cannot_ be inlined into the match. If it is, the lock will be held
607        // open through the whole match, resulting in a deadlock when we try to re-lock
608        // the buffer back to active.
609        let mapping = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
610        let pending_mapping = match mapping {
611            BufferMapState::Waiting(pending_mapping) => pending_mapping,
612            // Mapping cancelled
613            BufferMapState::Idle => return None,
614            // Mapping queued at least twice by map -> unmap -> map
615            // and was already successfully mapped below
616            BufferMapState::Active { .. } => {
617                *self.map_state.lock() = mapping;
618                return None;
619            }
620            _ => panic!("No pending mapping."),
621        };
622        let status = if pending_mapping.range.start != pending_mapping.range.end {
623            let host = pending_mapping.op.host;
624            let size = pending_mapping.range.end - pending_mapping.range.start;
625            match crate::device::map_buffer(
626                self,
627                pending_mapping.range.start,
628                size,
629                host,
630                snatch_guard,
631            ) {
632                Ok(mapping) => {
633                    *self.map_state.lock() = BufferMapState::Active {
634                        mapping,
635                        range: pending_mapping.range.clone(),
636                        host,
637                    };
638                    Ok(())
639                }
640                Err(e) => Err(e),
641            }
642        } else {
643            *self.map_state.lock() = BufferMapState::Active {
644                mapping: hal::BufferMapping {
645                    ptr: NonNull::dangling(),
646                    is_coherent: true,
647                },
648                range: pending_mapping.range,
649                host: pending_mapping.op.host,
650            };
651            Ok(())
652        };
653        Some((pending_mapping.op, status))
654    }
655
656    // Note: This must not be called while holding a lock.
657    pub(crate) fn unmap(
658        self: &Arc<Self>,
659        #[cfg(feature = "trace")] buffer_id: crate::id::BufferId,
660    ) -> Result<(), BufferAccessError> {
661        if let Some((mut operation, status)) = self.unmap_inner(
662            #[cfg(feature = "trace")]
663            buffer_id,
664        )? {
665            if let Some(callback) = operation.callback.take() {
666                callback(status);
667            }
668        }
669
670        Ok(())
671    }
672
673    fn unmap_inner(
674        self: &Arc<Self>,
675        #[cfg(feature = "trace")] buffer_id: crate::id::BufferId,
676    ) -> Result<Option<BufferMapPendingClosure>, BufferAccessError> {
677        let device = &self.device;
678        let snatch_guard = device.snatchable_lock.read();
679        let raw_buf = self.try_raw(&snatch_guard)?;
680        match mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle) {
681            BufferMapState::Init { staging_buffer } => {
682                #[cfg(feature = "trace")]
683                if let Some(ref mut trace) = *device.trace.lock() {
684                    let data = trace.make_binary("bin", staging_buffer.get_data());
685                    trace.add(trace::Action::WriteBuffer {
686                        id: buffer_id,
687                        data,
688                        range: 0..self.size,
689                        queued: true,
690                    });
691                }
692
693                let staging_buffer = staging_buffer.flush();
694
695                if let Some(queue) = device.get_queue() {
696                    let region = wgt::BufferSize::new(self.size).map(|size| hal::BufferCopy {
697                        src_offset: 0,
698                        dst_offset: 0,
699                        size,
700                    });
701                    let transition_src = hal::BufferBarrier {
702                        buffer: staging_buffer.raw(),
703                        usage: hal::StateTransition {
704                            from: wgt::BufferUses::MAP_WRITE,
705                            to: wgt::BufferUses::COPY_SRC,
706                        },
707                    };
708                    let transition_dst = hal::BufferBarrier::<dyn hal::DynBuffer> {
709                        buffer: raw_buf,
710                        usage: hal::StateTransition {
711                            from: wgt::BufferUses::empty(),
712                            to: wgt::BufferUses::COPY_DST,
713                        },
714                    };
715                    let mut pending_writes = queue.pending_writes.lock();
716                    let encoder = pending_writes.activate();
717                    unsafe {
718                        encoder.transition_buffers(&[transition_src, transition_dst]);
719                        if self.size > 0 {
720                            encoder.copy_buffer_to_buffer(
721                                staging_buffer.raw(),
722                                raw_buf,
723                                region.as_slice(),
724                            );
725                        }
726                    }
727                    pending_writes.consume(staging_buffer);
728                    pending_writes.insert_buffer(self);
729                }
730            }
731            BufferMapState::Idle => {
732                return Err(BufferAccessError::NotMapped);
733            }
734            BufferMapState::Waiting(pending) => {
735                return Ok(Some((pending.op, Err(BufferAccessError::MapAborted))));
736            }
737            BufferMapState::Active {
738                mapping,
739                range,
740                host,
741            } => {
742                #[allow(clippy::collapsible_if)]
743                if host == HostMap::Write {
744                    #[cfg(feature = "trace")]
745                    if let Some(ref mut trace) = *device.trace.lock() {
746                        let size = range.end - range.start;
747                        let data = trace.make_binary("bin", unsafe {
748                            core::slice::from_raw_parts(mapping.ptr.as_ptr(), size as usize)
749                        });
750                        trace.add(trace::Action::WriteBuffer {
751                            id: buffer_id,
752                            data,
753                            range: range.clone(),
754                            queued: false,
755                        });
756                    }
757                    if !mapping.is_coherent {
758                        unsafe { device.raw().flush_mapped_ranges(raw_buf, &[range]) };
759                    }
760                }
761                unsafe { device.raw().unmap_buffer(raw_buf) };
762            }
763        }
764        Ok(None)
765    }
766
767    pub(crate) fn destroy(self: &Arc<Self>) {
768        let device = &self.device;
769
770        let temp = {
771            let mut snatch_guard = device.snatchable_lock.write();
772
773            let raw = match self.raw.snatch(&mut snatch_guard) {
774                Some(raw) => raw,
775                None => {
776                    // Per spec, it is valid to call `destroy` multiple times.
777                    return;
778                }
779            };
780
781            let timestamp_normalization_bind_group = self
782                .timestamp_normalization_bind_group
783                .snatch(&mut snatch_guard);
784
785            let indirect_validation_bind_groups = self
786                .indirect_validation_bind_groups
787                .snatch(&mut snatch_guard);
788
789            drop(snatch_guard);
790
791            let bind_groups = {
792                let mut guard = self.bind_groups.lock();
793                mem::take(&mut *guard)
794            };
795
796            queue::TempResource::DestroyedBuffer(DestroyedBuffer {
797                raw: ManuallyDrop::new(raw),
798                device: Arc::clone(&self.device),
799                label: self.label().to_owned(),
800                bind_groups,
801                timestamp_normalization_bind_group,
802                indirect_validation_bind_groups,
803            })
804        };
805
806        if let Some(queue) = device.get_queue() {
807            let mut pending_writes = queue.pending_writes.lock();
808            if pending_writes.contains_buffer(self) {
809                pending_writes.consume_temp(temp);
810            } else {
811                let mut life_lock = queue.lock_life();
812                let last_submit_index = life_lock.get_buffer_latest_submission_index(self);
813                if let Some(last_submit_index) = last_submit_index {
814                    life_lock.schedule_resource_destruction(temp, last_submit_index);
815                }
816            }
817        }
818    }
819}
820
821#[derive(Clone, Debug, Error)]
822#[non_exhaustive]
823pub enum CreateBufferError {
824    #[error(transparent)]
825    Device(#[from] DeviceError),
826    #[error("Failed to map buffer while creating: {0}")]
827    AccessError(#[from] BufferAccessError),
828    #[error("Buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
829    UnalignedSize,
830    #[error("Invalid usage flags {0:?}")]
831    InvalidUsage(wgt::BufferUsages),
832    #[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
833    UsageMismatch(wgt::BufferUsages),
834    #[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")]
835    MaxBufferSize { requested: u64, maximum: u64 },
836    #[error(transparent)]
837    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
838    #[error("Failed to create bind group for indirect buffer validation: {0}")]
839    IndirectValidationBindGroup(DeviceError),
840}
841
842crate::impl_resource_type!(Buffer);
843crate::impl_labeled!(Buffer);
844crate::impl_parent_device!(Buffer);
845crate::impl_storage_item!(Buffer);
846crate::impl_trackable!(Buffer);
847
848impl WebGpuError for CreateBufferError {
849    fn webgpu_error_type(&self) -> ErrorType {
850        let e: &dyn WebGpuError = match self {
851            Self::Device(e) => e,
852            Self::AccessError(e) => e,
853            Self::MissingDownlevelFlags(e) => e,
854            Self::IndirectValidationBindGroup(e) => e,
855
856            Self::UnalignedSize
857            | Self::InvalidUsage(_)
858            | Self::UsageMismatch(_)
859            | Self::MaxBufferSize { .. } => return ErrorType::Validation,
860        };
861        e.webgpu_error_type()
862    }
863}
864
865/// A buffer that has been marked as destroyed and is staged for actual deletion soon.
866#[derive(Debug)]
867pub struct DestroyedBuffer {
868    raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
869    device: Arc<Device>,
870    label: String,
871    bind_groups: WeakVec<BindGroup>,
872    timestamp_normalization_bind_group: Option<TimestampNormalizationBindGroup>,
873    indirect_validation_bind_groups: Option<crate::indirect_validation::BindGroups>,
874}
875
876impl DestroyedBuffer {
877    pub fn label(&self) -> &dyn fmt::Debug {
878        &self.label
879    }
880}
881
882impl Drop for DestroyedBuffer {
883    fn drop(&mut self) {
884        let mut deferred = self.device.deferred_destroy.lock();
885        deferred.push(DeferredDestroy::BindGroups(mem::take(
886            &mut self.bind_groups,
887        )));
888        drop(deferred);
889
890        if let Some(raw) = self.timestamp_normalization_bind_group.take() {
891            raw.dispose(self.device.raw());
892        }
893
894        if let Some(raw) = self.indirect_validation_bind_groups.take() {
895            raw.dispose(self.device.raw());
896        }
897
898        resource_log!("Destroy raw Buffer (destroyed) {:?}", self.label());
899        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
900        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
901        unsafe {
902            hal::DynDevice::destroy_buffer(self.device.raw(), raw);
903        }
904    }
905}
906
907#[cfg(send_sync)]
908unsafe impl Send for StagingBuffer {}
909#[cfg(send_sync)]
910unsafe impl Sync for StagingBuffer {}
911
912/// A temporary buffer, consumed by the command that uses it.
913///
914/// A [`StagingBuffer`] is designed for one-shot uploads of data to the GPU. It
915/// is always created mapped, and the command that uses it destroys the buffer
916/// when it is done.
917///
918/// [`StagingBuffer`]s can be created with [`queue_create_staging_buffer`] and
919/// used with [`queue_write_staging_buffer`]. They are also used internally by
920/// operations like [`queue_write_texture`] that need to upload data to the GPU,
921/// but that don't belong to any particular wgpu command buffer.
922///
923/// Used `StagingBuffer`s are accumulated in [`Device::pending_writes`], to be
924/// freed once their associated operation's queue submission has finished
925/// execution.
926///
927/// [`queue_create_staging_buffer`]: crate::global::Global::queue_create_staging_buffer
928/// [`queue_write_staging_buffer`]: crate::global::Global::queue_write_staging_buffer
929/// [`queue_write_texture`]: crate::global::Global::queue_write_texture
930/// [`Device::pending_writes`]: crate::device::Device
931#[derive(Debug)]
932pub struct StagingBuffer {
933    raw: Box<dyn hal::DynBuffer>,
934    device: Arc<Device>,
935    pub(crate) size: wgt::BufferSize,
936    is_coherent: bool,
937    ptr: NonNull<u8>,
938}
939
940impl StagingBuffer {
941    pub(crate) fn new(device: &Arc<Device>, size: wgt::BufferSize) -> Result<Self, DeviceError> {
942        profiling::scope!("StagingBuffer::new");
943        let stage_desc = hal::BufferDescriptor {
944            label: crate::hal_label(Some("(wgpu internal) Staging"), device.instance_flags),
945            size: size.get(),
946            usage: wgt::BufferUses::MAP_WRITE | wgt::BufferUses::COPY_SRC,
947            memory_flags: hal::MemoryFlags::TRANSIENT,
948        };
949
950        let raw = unsafe { device.raw().create_buffer(&stage_desc) }
951            .map_err(|e| device.handle_hal_error(e))?;
952        let mapping = unsafe { device.raw().map_buffer(raw.as_ref(), 0..size.get()) }
953            .map_err(|e| device.handle_hal_error(e))?;
954
955        let staging_buffer = StagingBuffer {
956            raw,
957            device: device.clone(),
958            size,
959            is_coherent: mapping.is_coherent,
960            ptr: mapping.ptr,
961        };
962
963        Ok(staging_buffer)
964    }
965
966    /// SAFETY: You must not call any functions of `self`
967    /// until you stopped using the returned pointer.
968    pub(crate) unsafe fn ptr(&self) -> NonNull<u8> {
969        self.ptr
970    }
971
972    #[cfg(feature = "trace")]
973    pub(crate) fn get_data(&self) -> &[u8] {
974        unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.size.get() as usize) }
975    }
976
977    pub(crate) fn write_zeros(&mut self) {
978        unsafe { core::ptr::write_bytes(self.ptr.as_ptr(), 0, self.size.get() as usize) };
979    }
980
981    pub(crate) fn write(&mut self, data: &[u8]) {
982        assert!(data.len() >= self.size.get() as usize);
983        // SAFETY: With the assert above, all of `copy_nonoverlapping`'s
984        // requirements are satisfied.
985        unsafe {
986            core::ptr::copy_nonoverlapping(
987                data.as_ptr(),
988                self.ptr.as_ptr(),
989                self.size.get() as usize,
990            );
991        }
992    }
993
994    /// SAFETY: The offsets and size must be in-bounds.
995    pub(crate) unsafe fn write_with_offset(
996        &mut self,
997        data: &[u8],
998        src_offset: isize,
999        dst_offset: isize,
1000        size: usize,
1001    ) {
1002        unsafe {
1003            core::ptr::copy_nonoverlapping(
1004                data.as_ptr().offset(src_offset),
1005                self.ptr.as_ptr().offset(dst_offset),
1006                size,
1007            );
1008        }
1009    }
1010
1011    pub(crate) fn flush(self) -> FlushedStagingBuffer {
1012        let device = self.device.raw();
1013        if !self.is_coherent {
1014            #[allow(clippy::single_range_in_vec_init)]
1015            unsafe {
1016                device.flush_mapped_ranges(self.raw.as_ref(), &[0..self.size.get()])
1017            };
1018        }
1019        unsafe { device.unmap_buffer(self.raw.as_ref()) };
1020
1021        let StagingBuffer {
1022            raw, device, size, ..
1023        } = self;
1024
1025        FlushedStagingBuffer {
1026            raw: ManuallyDrop::new(raw),
1027            device,
1028            size,
1029        }
1030    }
1031}
1032
1033crate::impl_resource_type!(StagingBuffer);
1034crate::impl_storage_item!(StagingBuffer);
1035
1036#[derive(Debug)]
1037pub struct FlushedStagingBuffer {
1038    raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1039    device: Arc<Device>,
1040    pub(crate) size: wgt::BufferSize,
1041}
1042
1043impl FlushedStagingBuffer {
1044    pub(crate) fn raw(&self) -> &dyn hal::DynBuffer {
1045        self.raw.as_ref()
1046    }
1047}
1048
1049impl Drop for FlushedStagingBuffer {
1050    fn drop(&mut self) {
1051        resource_log!("Destroy raw StagingBuffer");
1052        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
1053        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1054        unsafe { self.device.raw().destroy_buffer(raw) };
1055    }
1056}
1057
1058pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, Vec<wgt::TextureFormat>>;
1059
1060#[derive(Debug)]
1061pub(crate) enum TextureInner {
1062    Native {
1063        raw: Box<dyn hal::DynTexture>,
1064    },
1065    Surface {
1066        raw: Box<dyn hal::DynSurfaceTexture>,
1067    },
1068}
1069
1070impl TextureInner {
1071    pub(crate) fn raw(&self) -> &dyn hal::DynTexture {
1072        match self {
1073            Self::Native { raw } => raw.as_ref(),
1074            Self::Surface { raw, .. } => raw.as_ref().borrow(),
1075        }
1076    }
1077}
1078
1079#[derive(Debug)]
1080pub enum TextureClearMode {
1081    BufferCopy,
1082    // View for clear via RenderPass for every subsurface (mip/layer/slice)
1083    RenderPass {
1084        clear_views: SmallVec<[ManuallyDrop<Box<dyn hal::DynTextureView>>; 1]>,
1085        is_color: bool,
1086    },
1087    Surface {
1088        clear_view: ManuallyDrop<Box<dyn hal::DynTextureView>>,
1089    },
1090    // Texture can't be cleared, attempting to do so will cause panic.
1091    // (either because it is impossible for the type of texture or it is being destroyed)
1092    None,
1093}
1094
1095#[derive(Debug)]
1096pub struct Texture {
1097    pub(crate) inner: Snatchable<TextureInner>,
1098    pub(crate) device: Arc<Device>,
1099    pub(crate) desc: wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
1100    pub(crate) _hal_usage: wgt::TextureUses,
1101    pub(crate) format_features: wgt::TextureFormatFeatures,
1102    pub(crate) initialization_status: RwLock<TextureInitTracker>,
1103    pub(crate) full_range: TextureSelector,
1104    /// The `label` from the descriptor used to create the resource.
1105    pub(crate) label: String,
1106    pub(crate) tracking_data: TrackingData,
1107    pub(crate) clear_mode: RwLock<TextureClearMode>,
1108    pub(crate) views: Mutex<WeakVec<TextureView>>,
1109    pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
1110}
1111
1112impl Texture {
1113    pub(crate) fn new(
1114        device: &Arc<Device>,
1115        inner: TextureInner,
1116        hal_usage: wgt::TextureUses,
1117        desc: &TextureDescriptor,
1118        format_features: wgt::TextureFormatFeatures,
1119        clear_mode: TextureClearMode,
1120        init: bool,
1121    ) -> Self {
1122        Texture {
1123            inner: Snatchable::new(inner),
1124            device: device.clone(),
1125            desc: desc.map_label(|_| ()),
1126            _hal_usage: hal_usage,
1127            format_features,
1128            initialization_status: RwLock::new(
1129                rank::TEXTURE_INITIALIZATION_STATUS,
1130                if init {
1131                    TextureInitTracker::new(desc.mip_level_count, desc.array_layer_count())
1132                } else {
1133                    TextureInitTracker::new(desc.mip_level_count, 0)
1134                },
1135            ),
1136            full_range: TextureSelector {
1137                mips: 0..desc.mip_level_count,
1138                layers: 0..desc.array_layer_count(),
1139            },
1140            label: desc.label.to_string(),
1141            tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1142            clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, clear_mode),
1143            views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1144            bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1145        }
1146    }
1147
1148    /// Checks that the given texture usage contains the required texture usage,
1149    /// returns an error otherwise.
1150    pub(crate) fn check_usage(
1151        &self,
1152        expected: wgt::TextureUsages,
1153    ) -> Result<(), MissingTextureUsageError> {
1154        if self.desc.usage.contains(expected) {
1155            Ok(())
1156        } else {
1157            Err(MissingTextureUsageError {
1158                res: self.error_ident(),
1159                actual: self.desc.usage,
1160                expected,
1161            })
1162        }
1163    }
1164}
1165
1166impl Drop for Texture {
1167    fn drop(&mut self) {
1168        match *self.clear_mode.write() {
1169            TextureClearMode::Surface {
1170                ref mut clear_view, ..
1171            } => {
1172                // SAFETY: We are in the Drop impl and we don't use clear_view anymore after this point.
1173                let raw = unsafe { ManuallyDrop::take(clear_view) };
1174                unsafe {
1175                    self.device.raw().destroy_texture_view(raw);
1176                }
1177            }
1178            TextureClearMode::RenderPass {
1179                ref mut clear_views,
1180                ..
1181            } => {
1182                clear_views.iter_mut().for_each(|clear_view| {
1183                    // SAFETY: We are in the Drop impl and we don't use clear_view anymore after this point.
1184                    let raw = unsafe { ManuallyDrop::take(clear_view) };
1185                    unsafe {
1186                        self.device.raw().destroy_texture_view(raw);
1187                    }
1188                });
1189            }
1190            _ => {}
1191        };
1192
1193        if let Some(TextureInner::Native { raw }) = self.inner.take() {
1194            resource_log!("Destroy raw {}", self.error_ident());
1195            unsafe {
1196                self.device.raw().destroy_texture(raw);
1197            }
1198        }
1199    }
1200}
1201
1202impl RawResourceAccess for Texture {
1203    type DynResource = dyn hal::DynTexture;
1204
1205    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1206        self.inner.get(guard).map(|t| t.raw())
1207    }
1208}
1209
1210impl Texture {
1211    pub(crate) fn try_inner<'a>(
1212        &'a self,
1213        guard: &'a SnatchGuard,
1214    ) -> Result<&'a TextureInner, DestroyedResourceError> {
1215        self.inner
1216            .get(guard)
1217            .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1218    }
1219
1220    pub(crate) fn check_destroyed(
1221        &self,
1222        guard: &SnatchGuard,
1223    ) -> Result<(), DestroyedResourceError> {
1224        self.inner
1225            .get(guard)
1226            .map(|_| ())
1227            .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1228    }
1229
1230    pub(crate) fn get_clear_view<'a>(
1231        clear_mode: &'a TextureClearMode,
1232        desc: &'a wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
1233        mip_level: u32,
1234        depth_or_layer: u32,
1235    ) -> &'a dyn hal::DynTextureView {
1236        match *clear_mode {
1237            TextureClearMode::BufferCopy => {
1238                panic!("Given texture is cleared with buffer copies, not render passes")
1239            }
1240            TextureClearMode::None => {
1241                panic!("Given texture can't be cleared")
1242            }
1243            TextureClearMode::Surface { ref clear_view, .. } => clear_view.as_ref(),
1244            TextureClearMode::RenderPass {
1245                ref clear_views, ..
1246            } => {
1247                let index = if desc.dimension == wgt::TextureDimension::D3 {
1248                    (0..mip_level).fold(0, |acc, mip| {
1249                        acc + (desc.size.depth_or_array_layers >> mip).max(1)
1250                    })
1251                } else {
1252                    mip_level * desc.size.depth_or_array_layers
1253                } + depth_or_layer;
1254                clear_views[index as usize].as_ref()
1255            }
1256        }
1257    }
1258
1259    pub(crate) fn destroy(self: &Arc<Self>) {
1260        let device = &self.device;
1261
1262        let temp = {
1263            let raw = match self.inner.snatch(&mut device.snatchable_lock.write()) {
1264                Some(TextureInner::Native { raw }) => raw,
1265                Some(TextureInner::Surface { .. }) => {
1266                    return;
1267                }
1268                None => {
1269                    // Per spec, it is valid to call `destroy` multiple times.
1270                    return;
1271                }
1272            };
1273
1274            let views = {
1275                let mut guard = self.views.lock();
1276                mem::take(&mut *guard)
1277            };
1278
1279            let bind_groups = {
1280                let mut guard = self.bind_groups.lock();
1281                mem::take(&mut *guard)
1282            };
1283
1284            queue::TempResource::DestroyedTexture(DestroyedTexture {
1285                raw: ManuallyDrop::new(raw),
1286                views,
1287                clear_mode: mem::replace(&mut *self.clear_mode.write(), TextureClearMode::None),
1288                bind_groups,
1289                device: Arc::clone(&self.device),
1290                label: self.label().to_owned(),
1291            })
1292        };
1293
1294        if let Some(queue) = device.get_queue() {
1295            let mut pending_writes = queue.pending_writes.lock();
1296            if pending_writes.contains_texture(self) {
1297                pending_writes.consume_temp(temp);
1298            } else {
1299                let mut life_lock = queue.lock_life();
1300                let last_submit_index = life_lock.get_texture_latest_submission_index(self);
1301                if let Some(last_submit_index) = last_submit_index {
1302                    life_lock.schedule_resource_destruction(temp, last_submit_index);
1303                }
1304            }
1305        }
1306    }
1307}
1308
1309/// A texture that has been marked as destroyed and is staged for actual deletion soon.
1310#[derive(Debug)]
1311pub struct DestroyedTexture {
1312    raw: ManuallyDrop<Box<dyn hal::DynTexture>>,
1313    views: WeakVec<TextureView>,
1314    clear_mode: TextureClearMode,
1315    bind_groups: WeakVec<BindGroup>,
1316    device: Arc<Device>,
1317    label: String,
1318}
1319
1320impl DestroyedTexture {
1321    pub fn label(&self) -> &dyn fmt::Debug {
1322        &self.label
1323    }
1324}
1325
1326impl Drop for DestroyedTexture {
1327    fn drop(&mut self) {
1328        let device = &self.device;
1329
1330        let mut deferred = device.deferred_destroy.lock();
1331        deferred.push(DeferredDestroy::TextureViews(mem::take(&mut self.views)));
1332        deferred.push(DeferredDestroy::BindGroups(mem::take(
1333            &mut self.bind_groups,
1334        )));
1335        drop(deferred);
1336
1337        match mem::replace(&mut self.clear_mode, TextureClearMode::None) {
1338            TextureClearMode::RenderPass { clear_views, .. } => {
1339                for clear_view in clear_views {
1340                    let raw = ManuallyDrop::into_inner(clear_view);
1341                    unsafe { self.device.raw().destroy_texture_view(raw) };
1342                }
1343            }
1344            TextureClearMode::Surface { clear_view } => {
1345                let raw = ManuallyDrop::into_inner(clear_view);
1346                unsafe { self.device.raw().destroy_texture_view(raw) };
1347            }
1348            _ => (),
1349        }
1350
1351        resource_log!("Destroy raw Texture (destroyed) {:?}", self.label());
1352        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
1353        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1354        unsafe {
1355            self.device.raw().destroy_texture(raw);
1356        }
1357    }
1358}
1359
1360#[derive(Clone, Copy, Debug)]
1361pub enum TextureErrorDimension {
1362    X,
1363    Y,
1364    Z,
1365}
1366
1367#[derive(Clone, Debug, Error)]
1368#[non_exhaustive]
1369pub enum TextureDimensionError {
1370    #[error("Dimension {0:?} is zero")]
1371    Zero(TextureErrorDimension),
1372    #[error("Dimension {dim:?} value {given} exceeds the limit of {limit}")]
1373    LimitExceeded {
1374        dim: TextureErrorDimension,
1375        given: u32,
1376        limit: u32,
1377    },
1378    #[error("Sample count {0} is invalid")]
1379    InvalidSampleCount(u32),
1380    #[error("Width {width} is not a multiple of {format:?}'s block width ({block_width})")]
1381    NotMultipleOfBlockWidth {
1382        width: u32,
1383        block_width: u32,
1384        format: wgt::TextureFormat,
1385    },
1386    #[error("Height {height} is not a multiple of {format:?}'s block height ({block_height})")]
1387    NotMultipleOfBlockHeight {
1388        height: u32,
1389        block_height: u32,
1390        format: wgt::TextureFormat,
1391    },
1392    #[error(
1393        "Width {width} is not a multiple of {format:?}'s width multiple requirement ({multiple})"
1394    )]
1395    WidthNotMultipleOf {
1396        width: u32,
1397        multiple: u32,
1398        format: wgt::TextureFormat,
1399    },
1400    #[error("Height {height} is not a multiple of {format:?}'s height multiple requirement ({multiple})")]
1401    HeightNotMultipleOf {
1402        height: u32,
1403        multiple: u32,
1404        format: wgt::TextureFormat,
1405    },
1406    #[error("Multisampled texture depth or array layers must be 1, got {0}")]
1407    MultisampledDepthOrArrayLayer(u32),
1408}
1409
1410impl WebGpuError for TextureDimensionError {
1411    fn webgpu_error_type(&self) -> ErrorType {
1412        ErrorType::Validation
1413    }
1414}
1415
1416#[derive(Clone, Debug, Error)]
1417#[non_exhaustive]
1418pub enum CreateTextureError {
1419    #[error(transparent)]
1420    Device(#[from] DeviceError),
1421    #[error(transparent)]
1422    CreateTextureView(#[from] CreateTextureViewError),
1423    #[error("Invalid usage flags {0:?}")]
1424    InvalidUsage(wgt::TextureUsages),
1425    #[error(transparent)]
1426    InvalidDimension(#[from] TextureDimensionError),
1427    #[error("Depth texture ({1:?}) can't be created as {0:?}")]
1428    InvalidDepthDimension(wgt::TextureDimension, wgt::TextureFormat),
1429    #[error("Compressed texture ({1:?}) can't be created as {0:?}")]
1430    InvalidCompressedDimension(wgt::TextureDimension, wgt::TextureFormat),
1431    #[error(
1432        "Texture descriptor mip level count {requested} is invalid, maximum allowed is {maximum}"
1433    )]
1434    InvalidMipLevelCount { requested: u32, maximum: u32 },
1435    #[error(
1436        "Texture usages {0:?} are not allowed on a texture of type {1:?}{downlevel_suffix}",
1437        downlevel_suffix = if *.2 { " due to downlevel restrictions" } else { "" }
1438    )]
1439    InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat, bool),
1440    #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
1441    InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
1442    #[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")]
1443    InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),
1444    #[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")]
1445    InvalidMultisampledStorageBinding,
1446    #[error("Format {0:?} does not support multisampling")]
1447    InvalidMultisampledFormat(wgt::TextureFormat),
1448    #[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:?}.")]
1449    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
1450    #[error("Multisampled textures must have RENDER_ATTACHMENT usage")]
1451    MultisampledNotRenderAttachment,
1452    #[error("Texture format {0:?} can't be used due to missing features")]
1453    MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),
1454    #[error(transparent)]
1455    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1456}
1457
1458crate::impl_resource_type!(Texture);
1459crate::impl_labeled!(Texture);
1460crate::impl_parent_device!(Texture);
1461crate::impl_storage_item!(Texture);
1462crate::impl_trackable!(Texture);
1463
1464impl Borrow<TextureSelector> for Texture {
1465    fn borrow(&self) -> &TextureSelector {
1466        &self.full_range
1467    }
1468}
1469
1470impl WebGpuError for CreateTextureError {
1471    fn webgpu_error_type(&self) -> ErrorType {
1472        let e: &dyn WebGpuError = match self {
1473            Self::Device(e) => e,
1474            Self::CreateTextureView(e) => e,
1475            Self::InvalidDimension(e) => e,
1476            Self::MissingFeatures(_, e) => e,
1477            Self::MissingDownlevelFlags(e) => e,
1478
1479            Self::InvalidUsage(_)
1480            | Self::InvalidDepthDimension(_, _)
1481            | Self::InvalidCompressedDimension(_, _)
1482            | Self::InvalidMipLevelCount { .. }
1483            | Self::InvalidFormatUsages(_, _, _)
1484            | Self::InvalidViewFormat(_, _)
1485            | Self::InvalidDimensionUsages(_, _)
1486            | Self::InvalidMultisampledStorageBinding
1487            | Self::InvalidMultisampledFormat(_)
1488            | Self::InvalidSampleCount(..)
1489            | Self::MultisampledNotRenderAttachment => return ErrorType::Validation,
1490        };
1491        e.webgpu_error_type()
1492    }
1493}
1494
1495/// Describes a [`TextureView`].
1496#[derive(Clone, Debug, Default, Eq, PartialEq)]
1497#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1498#[cfg_attr(feature = "serde", serde(default))]
1499pub struct TextureViewDescriptor<'a> {
1500    /// Debug label of the texture view.
1501    ///
1502    /// This will show up in graphics debuggers for easy identification.
1503    pub label: Label<'a>,
1504    /// Format of the texture view, or `None` for the same format as the texture
1505    /// itself.
1506    ///
1507    /// At this time, it must be the same the underlying format of the texture.
1508    pub format: Option<wgt::TextureFormat>,
1509    /// The dimension of the texture view.
1510    ///
1511    /// - For 1D textures, this must be `D1`.
1512    /// - For 2D textures it must be one of `D2`, `D2Array`, `Cube`, or `CubeArray`.
1513    /// - For 3D textures it must be `D3`.
1514    pub dimension: Option<wgt::TextureViewDimension>,
1515    /// The allowed usage(s) for the texture view. Must be a subset of the usage flags of the texture.
1516    /// If not provided, defaults to the full set of usage flags of the texture.
1517    pub usage: Option<wgt::TextureUsages>,
1518    /// Range within the texture that is accessible via this view.
1519    pub range: wgt::ImageSubresourceRange,
1520}
1521
1522#[derive(Debug)]
1523pub(crate) struct HalTextureViewDescriptor {
1524    pub texture_format: wgt::TextureFormat,
1525    pub format: wgt::TextureFormat,
1526    pub usage: wgt::TextureUsages,
1527    pub dimension: wgt::TextureViewDimension,
1528    pub range: wgt::ImageSubresourceRange,
1529}
1530
1531impl HalTextureViewDescriptor {
1532    pub fn aspects(&self) -> hal::FormatAspects {
1533        hal::FormatAspects::new(self.texture_format, self.range.aspect)
1534    }
1535}
1536
1537#[derive(Debug, Copy, Clone, Error)]
1538pub enum TextureViewNotRenderableReason {
1539    #[error("The texture this view references doesn't include the RENDER_ATTACHMENT usage. Provided usages: {0:?}")]
1540    Usage(wgt::TextureUsages),
1541    #[error("The dimension of this texture view is not 2D. View dimension: {0:?}")]
1542    Dimension(wgt::TextureViewDimension),
1543    #[error("This texture view has more than one mipmap level. View mipmap levels: {0:?}")]
1544    MipLevelCount(u32),
1545    #[error("This texture view has more than one array layer. View array layers: {0:?}")]
1546    ArrayLayerCount(u32),
1547    #[error(
1548        "The aspects of this texture view are a subset of the aspects in the original texture. Aspects: {0:?}"
1549    )]
1550    Aspects(hal::FormatAspects),
1551}
1552
1553#[derive(Debug)]
1554pub struct TextureView {
1555    pub(crate) raw: Snatchable<Box<dyn hal::DynTextureView>>,
1556    // if it's a surface texture - it's none
1557    pub(crate) parent: Arc<Texture>,
1558    pub(crate) device: Arc<Device>,
1559    pub(crate) desc: HalTextureViewDescriptor,
1560    pub(crate) format_features: wgt::TextureFormatFeatures,
1561    /// This is `Err` only if the texture view is not renderable
1562    pub(crate) render_extent: Result<wgt::Extent3d, TextureViewNotRenderableReason>,
1563    pub(crate) samples: u32,
1564    pub(crate) selector: TextureSelector,
1565    /// The `label` from the descriptor used to create the resource.
1566    pub(crate) label: String,
1567    pub(crate) tracking_data: TrackingData,
1568}
1569
1570impl Drop for TextureView {
1571    fn drop(&mut self) {
1572        if let Some(raw) = self.raw.take() {
1573            resource_log!("Destroy raw {}", self.error_ident());
1574            unsafe {
1575                self.device.raw().destroy_texture_view(raw);
1576            }
1577        }
1578    }
1579}
1580
1581impl RawResourceAccess for TextureView {
1582    type DynResource = dyn hal::DynTextureView;
1583
1584    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1585        self.raw.get(guard).map(|it| it.as_ref())
1586    }
1587
1588    fn try_raw<'a>(
1589        &'a self,
1590        guard: &'a SnatchGuard,
1591    ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
1592        self.parent.check_destroyed(guard)?;
1593
1594        self.raw(guard)
1595            .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1596    }
1597}
1598
1599impl TextureView {
1600    /// Checks that the given texture usage contains the required texture usage,
1601    /// returns an error otherwise.
1602    pub(crate) fn check_usage(
1603        &self,
1604        expected: wgt::TextureUsages,
1605    ) -> Result<(), MissingTextureUsageError> {
1606        if self.desc.usage.contains(expected) {
1607            Ok(())
1608        } else {
1609            Err(MissingTextureUsageError {
1610                res: self.error_ident(),
1611                actual: self.desc.usage,
1612                expected,
1613            })
1614        }
1615    }
1616}
1617
1618#[derive(Clone, Debug, Error)]
1619#[non_exhaustive]
1620pub enum CreateTextureViewError {
1621    #[error(transparent)]
1622    Device(#[from] DeviceError),
1623    #[error(transparent)]
1624    DestroyedResource(#[from] DestroyedResourceError),
1625    #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`")]
1626    InvalidTextureViewDimension {
1627        view: wgt::TextureViewDimension,
1628        texture: wgt::TextureDimension,
1629    },
1630    #[error("Texture view format `{0:?}` is not renderable")]
1631    TextureViewFormatNotRenderable(wgt::TextureFormat),
1632    #[error("Texture view format `{0:?}` is not storage bindable")]
1633    TextureViewFormatNotStorage(wgt::TextureFormat),
1634    #[error("Invalid texture view usage `{view:?}` with texture of usage `{texture:?}`")]
1635    InvalidTextureViewUsage {
1636        view: wgt::TextureUsages,
1637        texture: wgt::TextureUsages,
1638    },
1639    #[error("Invalid texture view dimension `{0:?}` of a multisampled texture")]
1640    InvalidMultisampledTextureViewDimension(wgt::TextureViewDimension),
1641    #[error("Invalid texture depth `{depth}` for texture view of dimension `Cubemap`. Cubemap views must use images of size 6.")]
1642    InvalidCubemapTextureDepth { depth: u32 },
1643    #[error("Invalid texture depth `{depth}` for texture view of dimension `CubemapArray`. Cubemap views must use images with sizes which are a multiple of 6.")]
1644    InvalidCubemapArrayTextureDepth { depth: u32 },
1645    #[error("Source texture width and height must be equal for a texture view of dimension `Cube`/`CubeArray`")]
1646    InvalidCubeTextureViewSize,
1647    #[error("Mip level count is 0")]
1648    ZeroMipLevelCount,
1649    #[error("Array layer count is 0")]
1650    ZeroArrayLayerCount,
1651    #[error(
1652        "TextureView mip level count + base mip level {requested} must be <= Texture mip level count {total}"
1653    )]
1654    TooManyMipLevels { requested: u32, total: u32 },
1655    #[error("TextureView array layer count + base array layer {requested} must be <= Texture depth/array layer count {total}")]
1656    TooManyArrayLayers { requested: u32, total: u32 },
1657    #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")]
1658    InvalidArrayLayerCount {
1659        requested: u32,
1660        dim: wgt::TextureViewDimension,
1661    },
1662    #[error("Aspect {requested_aspect:?} is not in the source texture format {texture_format:?}")]
1663    InvalidAspect {
1664        texture_format: wgt::TextureFormat,
1665        requested_aspect: wgt::TextureAspect,
1666    },
1667    #[error("Unable to view texture {texture:?} as {view:?}")]
1668    FormatReinterpretation {
1669        texture: wgt::TextureFormat,
1670        view: wgt::TextureFormat,
1671    },
1672    #[error(transparent)]
1673    InvalidResource(#[from] InvalidResourceError),
1674    #[error(transparent)]
1675    MissingFeatures(#[from] MissingFeatures),
1676}
1677
1678impl WebGpuError for CreateTextureViewError {
1679    fn webgpu_error_type(&self) -> ErrorType {
1680        match self {
1681            Self::Device(e) => e.webgpu_error_type(),
1682
1683            Self::InvalidTextureViewDimension { .. }
1684            | Self::InvalidResource(_)
1685            | Self::InvalidMultisampledTextureViewDimension(_)
1686            | Self::InvalidCubemapTextureDepth { .. }
1687            | Self::InvalidCubemapArrayTextureDepth { .. }
1688            | Self::InvalidCubeTextureViewSize
1689            | Self::ZeroMipLevelCount
1690            | Self::ZeroArrayLayerCount
1691            | Self::TooManyMipLevels { .. }
1692            | Self::TooManyArrayLayers { .. }
1693            | Self::InvalidArrayLayerCount { .. }
1694            | Self::InvalidAspect { .. }
1695            | Self::FormatReinterpretation { .. }
1696            | Self::DestroyedResource(_)
1697            | Self::TextureViewFormatNotRenderable(_)
1698            | Self::TextureViewFormatNotStorage(_)
1699            | Self::InvalidTextureViewUsage { .. }
1700            | Self::MissingFeatures(_) => ErrorType::Validation,
1701        }
1702    }
1703}
1704
1705#[derive(Clone, Debug, Error)]
1706#[non_exhaustive]
1707pub enum TextureViewDestroyError {}
1708
1709crate::impl_resource_type!(TextureView);
1710crate::impl_labeled!(TextureView);
1711crate::impl_parent_device!(TextureView);
1712crate::impl_storage_item!(TextureView);
1713crate::impl_trackable!(TextureView);
1714
1715/// Describes a [`Sampler`]
1716#[derive(Clone, Debug, PartialEq)]
1717#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1718pub struct SamplerDescriptor<'a> {
1719    /// Debug label of the sampler.
1720    ///
1721    /// This will show up in graphics debuggers for easy identification.
1722    pub label: Label<'a>,
1723    /// How to deal with out of bounds accesses in the u (i.e. x) direction
1724    pub address_modes: [wgt::AddressMode; 3],
1725    /// How to filter the texture when it needs to be magnified (made larger)
1726    pub mag_filter: wgt::FilterMode,
1727    /// How to filter the texture when it needs to be minified (made smaller)
1728    pub min_filter: wgt::FilterMode,
1729    /// How to filter between mip map levels
1730    pub mipmap_filter: wgt::FilterMode,
1731    /// Minimum level of detail (i.e. mip level) to use
1732    pub lod_min_clamp: f32,
1733    /// Maximum level of detail (i.e. mip level) to use
1734    pub lod_max_clamp: f32,
1735    /// If this is enabled, this is a comparison sampler using the given comparison function.
1736    pub compare: Option<wgt::CompareFunction>,
1737    /// Must be at least 1. If this is not 1, all filter modes must be linear.
1738    pub anisotropy_clamp: u16,
1739    /// Border color to use when address_mode is
1740    /// [`AddressMode::ClampToBorder`](wgt::AddressMode::ClampToBorder)
1741    pub border_color: Option<wgt::SamplerBorderColor>,
1742}
1743
1744#[derive(Debug)]
1745pub struct Sampler {
1746    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynSampler>>,
1747    pub(crate) device: Arc<Device>,
1748    /// The `label` from the descriptor used to create the resource.
1749    pub(crate) label: String,
1750    pub(crate) tracking_data: TrackingData,
1751    /// `true` if this is a comparison sampler
1752    pub(crate) comparison: bool,
1753    /// `true` if this is a filtering sampler
1754    pub(crate) filtering: bool,
1755}
1756
1757impl Drop for Sampler {
1758    fn drop(&mut self) {
1759        resource_log!("Destroy raw {}", self.error_ident());
1760        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
1761        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1762        unsafe {
1763            self.device.raw().destroy_sampler(raw);
1764        }
1765    }
1766}
1767
1768impl Sampler {
1769    pub(crate) fn raw(&self) -> &dyn hal::DynSampler {
1770        self.raw.as_ref()
1771    }
1772}
1773
1774#[derive(Copy, Clone)]
1775pub enum SamplerFilterErrorType {
1776    MagFilter,
1777    MinFilter,
1778    MipmapFilter,
1779}
1780
1781impl fmt::Debug for SamplerFilterErrorType {
1782    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1783        match *self {
1784            SamplerFilterErrorType::MagFilter => write!(f, "magFilter"),
1785            SamplerFilterErrorType::MinFilter => write!(f, "minFilter"),
1786            SamplerFilterErrorType::MipmapFilter => write!(f, "mipmapFilter"),
1787        }
1788    }
1789}
1790
1791#[derive(Clone, Debug, Error)]
1792#[non_exhaustive]
1793pub enum CreateSamplerError {
1794    #[error(transparent)]
1795    Device(#[from] DeviceError),
1796    #[error("Invalid lodMinClamp: {0}. Must be greater or equal to 0.0")]
1797    InvalidLodMinClamp(f32),
1798    #[error("Invalid lodMaxClamp: {lod_max_clamp}. Must be greater or equal to lodMinClamp (which is {lod_min_clamp}).")]
1799    InvalidLodMaxClamp {
1800        lod_min_clamp: f32,
1801        lod_max_clamp: f32,
1802    },
1803    #[error("Invalid anisotropic clamp: {0}. Must be at least 1.")]
1804    InvalidAnisotropy(u16),
1805    #[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.")]
1806    InvalidFilterModeWithAnisotropy {
1807        filter_type: SamplerFilterErrorType,
1808        filter_mode: wgt::FilterMode,
1809        anisotropic_clamp: u16,
1810    },
1811    #[error(transparent)]
1812    MissingFeatures(#[from] MissingFeatures),
1813}
1814
1815crate::impl_resource_type!(Sampler);
1816crate::impl_labeled!(Sampler);
1817crate::impl_parent_device!(Sampler);
1818crate::impl_storage_item!(Sampler);
1819crate::impl_trackable!(Sampler);
1820
1821impl WebGpuError for CreateSamplerError {
1822    fn webgpu_error_type(&self) -> ErrorType {
1823        let e: &dyn WebGpuError = match self {
1824            Self::Device(e) => e,
1825            Self::MissingFeatures(e) => e,
1826
1827            Self::InvalidLodMinClamp(_)
1828            | Self::InvalidLodMaxClamp { .. }
1829            | Self::InvalidAnisotropy(_)
1830            | Self::InvalidFilterModeWithAnisotropy { .. } => return ErrorType::Validation,
1831        };
1832        e.webgpu_error_type()
1833    }
1834}
1835
1836#[derive(Clone, Debug, Error)]
1837#[non_exhaustive]
1838pub enum CreateQuerySetError {
1839    #[error(transparent)]
1840    Device(#[from] DeviceError),
1841    #[error("QuerySets cannot be made with zero queries")]
1842    ZeroCount,
1843    #[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")]
1844    TooManyQueries { count: u32, maximum: u32 },
1845    #[error(transparent)]
1846    MissingFeatures(#[from] MissingFeatures),
1847}
1848
1849impl WebGpuError for CreateQuerySetError {
1850    fn webgpu_error_type(&self) -> ErrorType {
1851        let e: &dyn WebGpuError = match self {
1852            Self::Device(e) => e,
1853            Self::MissingFeatures(e) => e,
1854
1855            Self::TooManyQueries { .. } | Self::ZeroCount => return ErrorType::Validation,
1856        };
1857        e.webgpu_error_type()
1858    }
1859}
1860
1861pub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor<Label<'a>>;
1862
1863#[derive(Debug)]
1864pub struct QuerySet {
1865    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynQuerySet>>,
1866    pub(crate) device: Arc<Device>,
1867    /// The `label` from the descriptor used to create the resource.
1868    pub(crate) label: String,
1869    pub(crate) tracking_data: TrackingData,
1870    pub(crate) desc: wgt::QuerySetDescriptor<()>,
1871}
1872
1873impl Drop for QuerySet {
1874    fn drop(&mut self) {
1875        resource_log!("Destroy raw {}", self.error_ident());
1876        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
1877        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1878        unsafe {
1879            self.device.raw().destroy_query_set(raw);
1880        }
1881    }
1882}
1883
1884crate::impl_resource_type!(QuerySet);
1885crate::impl_labeled!(QuerySet);
1886crate::impl_parent_device!(QuerySet);
1887crate::impl_storage_item!(QuerySet);
1888crate::impl_trackable!(QuerySet);
1889
1890impl QuerySet {
1891    pub(crate) fn raw(&self) -> &dyn hal::DynQuerySet {
1892        self.raw.as_ref()
1893    }
1894}
1895
1896pub type BlasDescriptor<'a> = wgt::CreateBlasDescriptor<Label<'a>>;
1897pub type TlasDescriptor<'a> = wgt::CreateTlasDescriptor<Label<'a>>;
1898
1899pub type BlasPrepareCompactResult = Result<(), BlasPrepareCompactError>;
1900
1901#[cfg(send_sync)]
1902pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + Send + 'static>;
1903#[cfg(not(send_sync))]
1904pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + 'static>;
1905
1906pub(crate) struct BlasPendingCompact {
1907    pub(crate) op: Option<BlasCompactCallback>,
1908    // hold the parent alive while the mapping is active
1909    pub(crate) _parent_blas: Arc<Blas>,
1910}
1911
1912impl fmt::Debug for BlasPendingCompact {
1913    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1914        f.debug_struct("BlasPendingCompact")
1915            .field("op", &())
1916            .field("_parent_blas", &self._parent_blas)
1917            .finish()
1918    }
1919}
1920
1921#[derive(Debug)]
1922pub(crate) enum BlasCompactState {
1923    /// Created from a compact operation.
1924    Compacted,
1925    /// Waiting for GPU to be done before mapping to get compacted size
1926    Waiting(BlasPendingCompact),
1927    /// Ready to be compacted
1928    Ready { size: wgt::BufferAddress },
1929    /// Ready to prepare to compact.
1930    Idle,
1931}
1932
1933#[cfg(send_sync)]
1934unsafe impl Send for BlasCompactState {}
1935#[cfg(send_sync)]
1936unsafe impl Sync for BlasCompactState {}
1937
1938#[derive(Debug)]
1939pub struct Blas {
1940    pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
1941    pub(crate) device: Arc<Device>,
1942    pub(crate) size_info: hal::AccelerationStructureBuildSizes,
1943    pub(crate) sizes: wgt::BlasGeometrySizeDescriptors,
1944    pub(crate) flags: wgt::AccelerationStructureFlags,
1945    pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
1946    pub(crate) built_index: RwLock<Option<NonZeroU64>>,
1947    pub(crate) handle: u64,
1948    /// The `label` from the descriptor used to create the resource.
1949    pub(crate) label: String,
1950    pub(crate) tracking_data: TrackingData,
1951    pub(crate) compaction_buffer: Option<ManuallyDrop<Box<dyn hal::DynBuffer>>>,
1952    pub(crate) compacted_state: Mutex<BlasCompactState>,
1953}
1954
1955impl Drop for Blas {
1956    fn drop(&mut self) {
1957        resource_log!("Destroy raw {}", self.error_ident());
1958        // SAFETY: We are in the Drop impl, and we don't use self.raw or self.compaction_buffer anymore after this point.
1959        if let Some(raw) = self.raw.take() {
1960            unsafe {
1961                self.device.raw().destroy_acceleration_structure(raw);
1962            }
1963        }
1964        if let Some(mut raw) = self.compaction_buffer.take() {
1965            unsafe {
1966                self.device
1967                    .raw()
1968                    .destroy_buffer(ManuallyDrop::take(&mut raw))
1969            }
1970        }
1971    }
1972}
1973
1974impl RawResourceAccess for Blas {
1975    type DynResource = dyn hal::DynAccelerationStructure;
1976
1977    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1978        self.raw.get(guard).map(|it| it.as_ref())
1979    }
1980}
1981
1982impl Blas {
1983    pub(crate) fn prepare_compact_async(
1984        self: &Arc<Self>,
1985        op: Option<BlasCompactCallback>,
1986    ) -> Result<SubmissionIndex, (Option<BlasCompactCallback>, BlasPrepareCompactError)> {
1987        let device = &self.device;
1988        if let Err(e) = device.check_is_valid() {
1989            return Err((op, e.into()));
1990        }
1991
1992        if self.built_index.read().is_none() {
1993            return Err((op, BlasPrepareCompactError::NotBuilt));
1994        }
1995
1996        if !self
1997            .flags
1998            .contains(wgt::AccelerationStructureFlags::ALLOW_COMPACTION)
1999        {
2000            return Err((op, BlasPrepareCompactError::CompactionUnsupported));
2001        }
2002
2003        let mut state = self.compacted_state.lock();
2004        *state = match *state {
2005            BlasCompactState::Compacted => {
2006                return Err((op, BlasPrepareCompactError::DoubleCompaction))
2007            }
2008            BlasCompactState::Waiting(_) => {
2009                return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
2010            }
2011            BlasCompactState::Ready { .. } => {
2012                return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
2013            }
2014            BlasCompactState::Idle => BlasCompactState::Waiting(BlasPendingCompact {
2015                op,
2016                _parent_blas: self.clone(),
2017            }),
2018        };
2019
2020        let submit_index = if let Some(queue) = device.get_queue() {
2021            queue.lock_life().prepare_compact(self).unwrap_or(0) // '0' means no wait is necessary
2022        } else {
2023            // We can safely unwrap below since we just set the `compacted_state` to `BlasCompactState::Waiting`.
2024            let (mut callback, status) = self.read_back_compact_size().unwrap();
2025            if let Some(callback) = callback.take() {
2026                callback(status);
2027            }
2028            0
2029        };
2030
2031        Ok(submit_index)
2032    }
2033
2034    /// This function returns [`None`] only if [`Self::compacted_state`] is not [`BlasCompactState::Waiting`].
2035    #[must_use]
2036    pub(crate) fn read_back_compact_size(&self) -> Option<BlasCompactReadyPendingClosure> {
2037        let mut state = self.compacted_state.lock();
2038        let pending_compact = match mem::replace(&mut *state, BlasCompactState::Idle) {
2039            BlasCompactState::Waiting(pending_mapping) => pending_mapping,
2040            // Compaction cancelled e.g. by rebuild
2041            BlasCompactState::Idle => return None,
2042            BlasCompactState::Ready { .. } => {
2043                unreachable!("This should be validated out by `prepare_for_compaction`")
2044            }
2045            _ => panic!("No pending mapping."),
2046        };
2047        let status = {
2048            let compaction_buffer = self.compaction_buffer.as_ref().unwrap().as_ref();
2049            unsafe {
2050                let map_res = self.device.raw().map_buffer(
2051                    compaction_buffer,
2052                    0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress,
2053                );
2054                match map_res {
2055                    Ok(mapping) => {
2056                        if !mapping.is_coherent {
2057                            // Clippy complains about this because it might not be intended, but
2058                            // this is intentional.
2059                            #[expect(clippy::single_range_in_vec_init)]
2060                            self.device.raw().flush_mapped_ranges(
2061                                compaction_buffer,
2062                                &[0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress],
2063                            );
2064                        }
2065                        let size = core::ptr::read_unaligned(
2066                            mapping.ptr.as_ptr().cast::<wgt::BufferAddress>(),
2067                        );
2068                        self.device.raw().unmap_buffer(compaction_buffer);
2069                        if self.size_info.acceleration_structure_size != 0 {
2070                            debug_assert_ne!(size, 0);
2071                        }
2072                        *state = BlasCompactState::Ready { size };
2073                        Ok(())
2074                    }
2075                    Err(err) => Err(BlasPrepareCompactError::from(DeviceError::from_hal(err))),
2076                }
2077            }
2078        };
2079        Some((pending_compact.op, status))
2080    }
2081}
2082
2083crate::impl_resource_type!(Blas);
2084crate::impl_labeled!(Blas);
2085crate::impl_parent_device!(Blas);
2086crate::impl_storage_item!(Blas);
2087crate::impl_trackable!(Blas);
2088
2089#[derive(Debug)]
2090pub struct Tlas {
2091    pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
2092    pub(crate) device: Arc<Device>,
2093    pub(crate) size_info: hal::AccelerationStructureBuildSizes,
2094    pub(crate) max_instance_count: u32,
2095    pub(crate) flags: wgt::AccelerationStructureFlags,
2096    pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
2097    pub(crate) built_index: RwLock<Option<NonZeroU64>>,
2098    pub(crate) dependencies: RwLock<Vec<Arc<Blas>>>,
2099    pub(crate) instance_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,
2100    /// The `label` from the descriptor used to create the resource.
2101    pub(crate) label: String,
2102    pub(crate) tracking_data: TrackingData,
2103}
2104
2105impl Drop for Tlas {
2106    fn drop(&mut self) {
2107        unsafe {
2108            resource_log!("Destroy raw {}", self.error_ident());
2109            if let Some(structure) = self.raw.take() {
2110                self.device.raw().destroy_acceleration_structure(structure);
2111            }
2112            let buffer = ManuallyDrop::take(&mut self.instance_buffer);
2113            self.device.raw().destroy_buffer(buffer);
2114        }
2115    }
2116}
2117
2118impl RawResourceAccess for Tlas {
2119    type DynResource = dyn hal::DynAccelerationStructure;
2120
2121    fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2122        self.raw.get(guard).map(|raw| raw.as_ref())
2123    }
2124}
2125
2126crate::impl_resource_type!(Tlas);
2127crate::impl_labeled!(Tlas);
2128crate::impl_parent_device!(Tlas);
2129crate::impl_storage_item!(Tlas);
2130crate::impl_trackable!(Tlas);