wgpu_core/
resource.rs

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