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