li_wgpu_core/
resource.rs

1use crate::{
2    device::{DeviceError, HostMap, MissingDownlevelFlags, MissingFeatures},
3    global::Global,
4    hal_api::HalApi,
5    hub::Token,
6    id::{AdapterId, DeviceId, SurfaceId, TextureId, Valid},
7    identity::GlobalIdentityHandlerFactory,
8    init_tracker::{BufferInitTracker, TextureInitTracker},
9    track::TextureSelector,
10    validation::MissingBufferUsageError,
11    Label, LifeGuard, RefCount, Stored,
12};
13
14use smallvec::SmallVec;
15use thiserror::Error;
16
17use std::{borrow::Borrow, ops::Range, ptr::NonNull};
18
19pub trait Resource {
20    const TYPE: &'static str;
21    fn life_guard(&self) -> &LifeGuard;
22    fn label(&self) -> &str {
23        #[cfg(debug_assertions)]
24        return &self.life_guard().label;
25        #[cfg(not(debug_assertions))]
26        return "";
27    }
28}
29
30/// The status code provided to the buffer mapping callback.
31///
32/// This is very similar to `BufferAccessResult`, except that this is FFI-friendly.
33#[repr(C)]
34#[derive(Debug)]
35pub enum BufferMapAsyncStatus {
36    /// The Buffer is sucessfully mapped, `get_mapped_range` can be called.
37    ///
38    /// All other variants of this enum represent failures to map the buffer.
39    Success,
40    /// The buffer is already mapped.
41    ///
42    /// While this is treated as an error, it does not prevent mapped range from being accessed.
43    AlreadyMapped,
44    /// Mapping was already requested.
45    MapAlreadyPending,
46    /// An unknown error.
47    Error,
48    /// Mapping was aborted (by unmapping or destroying the buffer before mapping
49    /// happened).
50    Aborted,
51    /// The context is Lost.
52    ContextLost,
53    /// The buffer is in an invalid state.
54    Invalid,
55    /// The range isn't fully contained in the buffer.
56    InvalidRange,
57    /// The range isn't properly aligned.
58    InvalidAlignment,
59    /// Incompatible usage flags.
60    InvalidUsageFlags,
61}
62
63pub(crate) enum BufferMapState<A: hal::Api> {
64    /// Mapped at creation.
65    Init {
66        ptr: NonNull<u8>,
67        stage_buffer: A::Buffer,
68        needs_flush: bool,
69    },
70    /// Waiting for GPU to be done before mapping
71    Waiting(BufferPendingMapping),
72    /// Mapped
73    Active {
74        ptr: NonNull<u8>,
75        range: hal::MemoryRange,
76        host: HostMap,
77    },
78    /// Not mapped
79    Idle,
80}
81
82#[cfg(any(
83    not(target_arch = "wasm32"),
84    all(
85        feature = "fragile-send-sync-non-atomic-wasm",
86        not(target_feature = "atomics")
87    )
88))]
89unsafe impl<A: hal::Api> Send for BufferMapState<A> {}
90#[cfg(any(
91    not(target_arch = "wasm32"),
92    all(
93        feature = "fragile-send-sync-non-atomic-wasm",
94        not(target_feature = "atomics")
95    )
96))]
97unsafe impl<A: hal::Api> Sync for BufferMapState<A> {}
98
99#[repr(C)]
100pub struct BufferMapCallbackC {
101    pub callback: unsafe extern "C" fn(status: BufferMapAsyncStatus, user_data: *mut u8),
102    pub user_data: *mut u8,
103}
104
105#[cfg(any(
106    not(target_arch = "wasm32"),
107    all(
108        feature = "fragile-send-sync-non-atomic-wasm",
109        not(target_feature = "atomics")
110    )
111))]
112unsafe impl Send for BufferMapCallbackC {}
113
114pub struct BufferMapCallback {
115    // We wrap this so creating the enum in the C variant can be unsafe,
116    // allowing our call function to be safe.
117    inner: Option<BufferMapCallbackInner>,
118}
119
120#[cfg(any(
121    not(target_arch = "wasm32"),
122    all(
123        feature = "fragile-send-sync-non-atomic-wasm",
124        not(target_feature = "atomics")
125    )
126))]
127type BufferMapCallbackCallback = Box<dyn FnOnce(BufferAccessResult) + Send + 'static>;
128#[cfg(not(any(
129    not(target_arch = "wasm32"),
130    all(
131        feature = "fragile-send-sync-non-atomic-wasm",
132        not(target_feature = "atomics")
133    )
134)))]
135type BufferMapCallbackCallback = Box<dyn FnOnce(BufferAccessResult) + 'static>;
136
137enum BufferMapCallbackInner {
138    Rust { callback: BufferMapCallbackCallback },
139    C { inner: BufferMapCallbackC },
140}
141
142impl BufferMapCallback {
143    pub fn from_rust(callback: BufferMapCallbackCallback) -> Self {
144        Self {
145            inner: Some(BufferMapCallbackInner::Rust { callback }),
146        }
147    }
148
149    /// # Safety
150    ///
151    /// - The callback pointer must be valid to call with the provided user_data
152    ///   pointer.
153    ///
154    /// - Both pointers must point to valid memory until the callback is
155    ///   invoked, which may happen at an unspecified time.
156    pub unsafe fn from_c(inner: BufferMapCallbackC) -> Self {
157        Self {
158            inner: Some(BufferMapCallbackInner::C { inner }),
159        }
160    }
161
162    pub(crate) fn call(mut self, result: BufferAccessResult) {
163        match self.inner.take() {
164            Some(BufferMapCallbackInner::Rust { callback }) => {
165                callback(result);
166            }
167            // SAFETY: the contract of the call to from_c says that this unsafe is sound.
168            Some(BufferMapCallbackInner::C { inner }) => unsafe {
169                let status = match result {
170                    Ok(()) => BufferMapAsyncStatus::Success,
171                    Err(BufferAccessError::Device(_)) => BufferMapAsyncStatus::ContextLost,
172                    Err(BufferAccessError::Invalid) | Err(BufferAccessError::Destroyed) => {
173                        BufferMapAsyncStatus::Invalid
174                    }
175                    Err(BufferAccessError::AlreadyMapped) => BufferMapAsyncStatus::AlreadyMapped,
176                    Err(BufferAccessError::MapAlreadyPending) => {
177                        BufferMapAsyncStatus::MapAlreadyPending
178                    }
179                    Err(BufferAccessError::MissingBufferUsage(_)) => {
180                        BufferMapAsyncStatus::InvalidUsageFlags
181                    }
182                    Err(BufferAccessError::UnalignedRange)
183                    | Err(BufferAccessError::UnalignedRangeSize { .. })
184                    | Err(BufferAccessError::UnalignedOffset { .. }) => {
185                        BufferMapAsyncStatus::InvalidAlignment
186                    }
187                    Err(BufferAccessError::OutOfBoundsUnderrun { .. })
188                    | Err(BufferAccessError::OutOfBoundsOverrun { .. })
189                    | Err(BufferAccessError::NegativeRange { .. }) => {
190                        BufferMapAsyncStatus::InvalidRange
191                    }
192                    Err(_) => BufferMapAsyncStatus::Error,
193                };
194
195                (inner.callback)(status, inner.user_data);
196            },
197            None => {
198                panic!("Map callback invoked twice");
199            }
200        }
201    }
202}
203
204impl Drop for BufferMapCallback {
205    fn drop(&mut self) {
206        if self.inner.is_some() {
207            panic!("Map callback was leaked");
208        }
209    }
210}
211
212pub struct BufferMapOperation {
213    pub host: HostMap,
214    pub callback: BufferMapCallback,
215}
216
217#[derive(Clone, Debug, Error)]
218#[non_exhaustive]
219pub enum BufferAccessError {
220    #[error(transparent)]
221    Device(#[from] DeviceError),
222    #[error("Buffer map failed")]
223    Failed,
224    #[error("Buffer is invalid")]
225    Invalid,
226    #[error("Buffer is destroyed")]
227    Destroyed,
228    #[error("Buffer is already mapped")]
229    AlreadyMapped,
230    #[error("Buffer map is pending")]
231    MapAlreadyPending,
232    #[error(transparent)]
233    MissingBufferUsage(#[from] MissingBufferUsageError),
234    #[error("Buffer is not mapped")]
235    NotMapped,
236    #[error(
237        "Buffer map range must start aligned to `MAP_ALIGNMENT` and end to `COPY_BUFFER_ALIGNMENT`"
238    )]
239    UnalignedRange,
240    #[error("Buffer offset invalid: offset {offset} must be multiple of 8")]
241    UnalignedOffset { offset: wgt::BufferAddress },
242    #[error("Buffer range size invalid: range_size {range_size} must be multiple of 4")]
243    UnalignedRangeSize { range_size: wgt::BufferAddress },
244    #[error("Buffer access out of bounds: index {index} would underrun the buffer (limit: {min})")]
245    OutOfBoundsUnderrun {
246        index: wgt::BufferAddress,
247        min: wgt::BufferAddress,
248    },
249    #[error(
250        "Buffer access out of bounds: last index {index} would overrun the buffer (limit: {max})"
251    )]
252    OutOfBoundsOverrun {
253        index: wgt::BufferAddress,
254        max: wgt::BufferAddress,
255    },
256    #[error("Buffer map range start {start} is greater than end {end}")]
257    NegativeRange {
258        start: wgt::BufferAddress,
259        end: wgt::BufferAddress,
260    },
261    #[error("Buffer map aborted")]
262    MapAborted,
263}
264
265pub type BufferAccessResult = Result<(), BufferAccessError>;
266pub(crate) struct BufferPendingMapping {
267    pub range: Range<wgt::BufferAddress>,
268    pub op: BufferMapOperation,
269    // hold the parent alive while the mapping is active
270    pub _parent_ref_count: RefCount,
271}
272
273pub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;
274
275pub struct Buffer<A: hal::Api> {
276    pub(crate) raw: Option<A::Buffer>,
277    pub(crate) device_id: Stored<DeviceId>,
278    pub(crate) usage: wgt::BufferUsages,
279    pub(crate) size: wgt::BufferAddress,
280    pub(crate) initialization_status: BufferInitTracker,
281    pub(crate) sync_mapped_writes: Option<hal::MemoryRange>,
282    pub(crate) life_guard: LifeGuard,
283    pub(crate) map_state: BufferMapState<A>,
284}
285
286#[derive(Clone, Debug, Error)]
287#[non_exhaustive]
288pub enum CreateBufferError {
289    #[error(transparent)]
290    Device(#[from] DeviceError),
291    #[error("Failed to map buffer while creating: {0}")]
292    AccessError(#[from] BufferAccessError),
293    #[error("Buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
294    UnalignedSize,
295    #[error("Invalid usage flags {0:?}")]
296    InvalidUsage(wgt::BufferUsages),
297    #[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
298    UsageMismatch(wgt::BufferUsages),
299    #[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")]
300    MaxBufferSize { requested: u64, maximum: u64 },
301    #[error(transparent)]
302    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
303}
304
305impl<A: hal::Api> Resource for Buffer<A> {
306    const TYPE: &'static str = "Buffer";
307
308    fn life_guard(&self) -> &LifeGuard {
309        &self.life_guard
310    }
311}
312
313/// A temporary buffer, consumed by the command that uses it.
314///
315/// A [`StagingBuffer`] is designed for one-shot uploads of data to the GPU. It
316/// is always created mapped, and the command that uses it destroys the buffer
317/// when it is done.
318///
319/// [`StagingBuffer`]s can be created with [`queue_create_staging_buffer`] and
320/// used with [`queue_write_staging_buffer`]. They are also used internally by
321/// operations like [`queue_write_texture`] that need to upload data to the GPU,
322/// but that don't belong to any particular wgpu command buffer.
323///
324/// Used `StagingBuffer`s are accumulated in [`Device::pending_writes`], to be
325/// freed once their associated operation's queue submission has finished
326/// execution.
327///
328/// [`queue_create_staging_buffer`]: Global::queue_create_staging_buffer
329/// [`queue_write_staging_buffer`]: Global::queue_write_staging_buffer
330/// [`queue_write_texture`]: Global::queue_write_texture
331/// [`Device::pending_writes`]: crate::device::Device
332pub struct StagingBuffer<A: hal::Api> {
333    pub(crate) raw: A::Buffer,
334    pub(crate) size: wgt::BufferAddress,
335    pub(crate) is_coherent: bool,
336}
337
338impl<A: hal::Api> Resource for StagingBuffer<A> {
339    const TYPE: &'static str = "StagingBuffer";
340
341    fn life_guard(&self) -> &LifeGuard {
342        unreachable!()
343    }
344
345    fn label(&self) -> &str {
346        "<StagingBuffer>"
347    }
348}
349
350pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, Vec<wgt::TextureFormat>>;
351
352#[derive(Debug)]
353pub(crate) enum TextureInner<A: hal::Api> {
354    Native {
355        raw: Option<A::Texture>,
356    },
357    Surface {
358        raw: A::SurfaceTexture,
359        parent_id: Valid<SurfaceId>,
360        has_work: bool,
361    },
362}
363
364impl<A: hal::Api> TextureInner<A> {
365    pub fn as_raw(&self) -> Option<&A::Texture> {
366        match *self {
367            Self::Native { raw: Some(ref tex) } => Some(tex),
368            Self::Native { raw: None } => None,
369            Self::Surface { ref raw, .. } => Some(raw.borrow()),
370        }
371    }
372}
373
374#[derive(Debug)]
375pub enum TextureClearMode<A: hal::Api> {
376    BufferCopy,
377    // View for clear via RenderPass for every subsurface (mip/layer/slice)
378    RenderPass {
379        clear_views: SmallVec<[A::TextureView; 1]>,
380        is_color: bool,
381    },
382    // Texture can't be cleared, attempting to do so will cause panic.
383    // (either because it is impossible for the type of texture or it is being destroyed)
384    None,
385}
386
387impl<A: hal::Api> TextureClearMode<A> {
388    pub(crate) fn destroy_clear_views(self, device: &A::Device) {
389        if let TextureClearMode::RenderPass { clear_views, .. } = self {
390            for clear_view in clear_views {
391                unsafe {
392                    hal::Device::destroy_texture_view(device, clear_view);
393                }
394            }
395        }
396    }
397}
398
399#[derive(Debug)]
400pub struct Texture<A: hal::Api> {
401    pub(crate) inner: TextureInner<A>,
402    pub(crate) device_id: Stored<DeviceId>,
403    pub(crate) desc: wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
404    pub(crate) hal_usage: hal::TextureUses,
405    pub(crate) format_features: wgt::TextureFormatFeatures,
406    pub(crate) initialization_status: TextureInitTracker,
407    pub(crate) full_range: TextureSelector,
408    pub(crate) life_guard: LifeGuard,
409    pub(crate) clear_mode: TextureClearMode<A>,
410}
411
412impl<A: hal::Api> Texture<A> {
413    pub(crate) fn get_clear_view(&self, mip_level: u32, depth_or_layer: u32) -> &A::TextureView {
414        match self.clear_mode {
415            TextureClearMode::BufferCopy => {
416                panic!("Given texture is cleared with buffer copies, not render passes")
417            }
418            TextureClearMode::None => {
419                panic!("Given texture can't be cleared")
420            }
421            TextureClearMode::RenderPass {
422                ref clear_views, ..
423            } => {
424                let index = if self.desc.dimension == wgt::TextureDimension::D3 {
425                    (0..mip_level).fold(0, |acc, mip| {
426                        acc + (self.desc.size.depth_or_array_layers >> mip).max(1)
427                    })
428                } else {
429                    mip_level * self.desc.size.depth_or_array_layers
430                } + depth_or_layer;
431                &clear_views[index as usize]
432            }
433        }
434    }
435}
436
437impl<G: GlobalIdentityHandlerFactory> Global<G> {
438    /// # Safety
439    ///
440    /// - The raw texture handle must not be manually destroyed
441    pub unsafe fn texture_as_hal<A: HalApi, F: FnOnce(Option<&A::Texture>)>(
442        &self,
443        id: TextureId,
444        hal_texture_callback: F,
445    ) {
446        profiling::scope!("Texture::as_hal");
447
448        let hub = A::hub(self);
449        let mut token = Token::root();
450        let (guard, _) = hub.textures.read(&mut token);
451        let texture = guard.try_get(id).ok().flatten();
452        let hal_texture = texture.and_then(|tex| tex.inner.as_raw());
453
454        hal_texture_callback(hal_texture);
455    }
456
457    /// # Safety
458    ///
459    /// - The raw adapter handle must not be manually destroyed
460    pub unsafe fn adapter_as_hal<A: HalApi, F: FnOnce(Option<&A::Adapter>) -> R, R>(
461        &self,
462        id: AdapterId,
463        hal_adapter_callback: F,
464    ) -> R {
465        profiling::scope!("Adapter::as_hal");
466
467        let hub = A::hub(self);
468        let mut token = Token::root();
469
470        let (guard, _) = hub.adapters.read(&mut token);
471        let adapter = guard.try_get(id).ok().flatten();
472        let hal_adapter = adapter.map(|adapter| &adapter.raw.adapter);
473
474        hal_adapter_callback(hal_adapter)
475    }
476
477    /// # Safety
478    ///
479    /// - The raw device handle must not be manually destroyed
480    pub unsafe fn device_as_hal<A: HalApi, F: FnOnce(Option<&A::Device>) -> R, R>(
481        &self,
482        id: DeviceId,
483        hal_device_callback: F,
484    ) -> R {
485        profiling::scope!("Device::as_hal");
486
487        let hub = A::hub(self);
488        let mut token = Token::root();
489        let (guard, _) = hub.devices.read(&mut token);
490        let device = guard.try_get(id).ok().flatten();
491        let hal_device = device.map(|device| &device.raw);
492
493        hal_device_callback(hal_device)
494    }
495
496    /// # Safety
497    /// - The raw surface handle must not be manually destroyed
498    pub unsafe fn surface_as_hal_mut<A: HalApi, F: FnOnce(Option<&mut A::Surface>) -> R, R>(
499        &self,
500        id: SurfaceId,
501        hal_surface_callback: F,
502    ) -> R {
503        profiling::scope!("Surface::as_hal_mut");
504
505        let mut token = Token::root();
506        let (mut guard, _) = self.surfaces.write(&mut token);
507        let surface = guard.get_mut(id).ok();
508        let hal_surface = surface
509            .and_then(|surface| A::get_surface_mut(surface))
510            .map(|surface| &mut surface.raw);
511
512        hal_surface_callback(hal_surface)
513    }
514}
515
516#[derive(Clone, Copy, Debug)]
517pub enum TextureErrorDimension {
518    X,
519    Y,
520    Z,
521}
522
523#[derive(Clone, Debug, Error)]
524#[non_exhaustive]
525pub enum TextureDimensionError {
526    #[error("Dimension {0:?} is zero")]
527    Zero(TextureErrorDimension),
528    #[error("Dimension {dim:?} value {given} exceeds the limit of {limit}")]
529    LimitExceeded {
530        dim: TextureErrorDimension,
531        given: u32,
532        limit: u32,
533    },
534    #[error("Sample count {0} is invalid")]
535    InvalidSampleCount(u32),
536    #[error("Width {width} is not a multiple of {format:?}'s block width ({block_width})")]
537    NotMultipleOfBlockWidth {
538        width: u32,
539        block_width: u32,
540        format: wgt::TextureFormat,
541    },
542    #[error("Height {height} is not a multiple of {format:?}'s block height ({block_height})")]
543    NotMultipleOfBlockHeight {
544        height: u32,
545        block_height: u32,
546        format: wgt::TextureFormat,
547    },
548    #[error("Multisampled texture depth or array layers must be 1, got {0}")]
549    MultisampledDepthOrArrayLayer(u32),
550}
551
552#[derive(Clone, Debug, Error)]
553#[non_exhaustive]
554pub enum CreateTextureError {
555    #[error(transparent)]
556    Device(#[from] DeviceError),
557    #[error("Invalid usage flags {0:?}")]
558    InvalidUsage(wgt::TextureUsages),
559    #[error(transparent)]
560    InvalidDimension(#[from] TextureDimensionError),
561    #[error("Depth texture ({1:?}) can't be created as {0:?}")]
562    InvalidDepthDimension(wgt::TextureDimension, wgt::TextureFormat),
563    #[error("Compressed texture ({1:?}) can't be created as {0:?}")]
564    InvalidCompressedDimension(wgt::TextureDimension, wgt::TextureFormat),
565    #[error(
566        "Texture descriptor mip level count {requested} is invalid, maximum allowed is {maximum}"
567    )]
568    InvalidMipLevelCount { requested: u32, maximum: u32 },
569    #[error(
570        "Texture usages {0:?} are not allowed on a texture of type {1:?}{}",
571        if *.2 { " due to downlevel restrictions" } else { "" }
572    )]
573    InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat, bool),
574    #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
575    InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
576    #[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")]
577    InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),
578    #[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")]
579    InvalidMultisampledStorageBinding,
580    #[error("Format {0:?} does not support multisampling")]
581    InvalidMultisampledFormat(wgt::TextureFormat),
582    #[error("Sample count {0} is not supported by format {1:?} on this device. It may be supported by your adapter through the TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES feature.")]
583    InvalidSampleCount(u32, wgt::TextureFormat),
584    #[error("Multisampled textures must have RENDER_ATTACHMENT usage")]
585    MultisampledNotRenderAttachment,
586    #[error("Texture format {0:?} can't be used due to missing features")]
587    MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),
588    #[error(transparent)]
589    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
590}
591
592impl<A: hal::Api> Resource for Texture<A> {
593    const TYPE: &'static str = "Texture";
594
595    fn life_guard(&self) -> &LifeGuard {
596        &self.life_guard
597    }
598}
599
600impl<A: hal::Api> Borrow<TextureSelector> for Texture<A> {
601    fn borrow(&self) -> &TextureSelector {
602        &self.full_range
603    }
604}
605
606/// Describes a [`TextureView`].
607#[derive(Clone, Debug, Default, Eq, PartialEq)]
608#[cfg_attr(feature = "trace", derive(serde::Serialize))]
609#[cfg_attr(feature = "replay", derive(serde::Deserialize), serde(default))]
610pub struct TextureViewDescriptor<'a> {
611    /// Debug label of the texture view.
612    ///
613    /// This will show up in graphics debuggers for easy identification.
614    pub label: Label<'a>,
615    /// Format of the texture view, or `None` for the same format as the texture
616    /// itself.
617    ///
618    /// At this time, it must be the same the underlying format of the texture.
619    pub format: Option<wgt::TextureFormat>,
620    /// The dimension of the texture view.
621    ///
622    /// - For 1D textures, this must be `D1`.
623    /// - For 2D textures it must be one of `D2`, `D2Array`, `Cube`, or `CubeArray`.
624    /// - For 3D textures it must be `D3`.
625    pub dimension: Option<wgt::TextureViewDimension>,
626    /// Range within the texture that is accessible via this view.
627    pub range: wgt::ImageSubresourceRange,
628}
629
630#[derive(Debug)]
631pub(crate) struct HalTextureViewDescriptor {
632    pub format: wgt::TextureFormat,
633    pub dimension: wgt::TextureViewDimension,
634    pub range: wgt::ImageSubresourceRange,
635}
636
637impl HalTextureViewDescriptor {
638    pub fn aspects(&self) -> hal::FormatAspects {
639        hal::FormatAspects::new(self.format, self.range.aspect)
640    }
641}
642
643#[derive(Debug, Copy, Clone, Error)]
644pub enum TextureViewNotRenderableReason {
645    #[error("The texture this view references doesn't include the RENDER_ATTACHMENT usage. Provided usages: {0:?}")]
646    Usage(wgt::TextureUsages),
647    #[error("The dimension of this texture view is not 2D. View dimension: {0:?}")]
648    Dimension(wgt::TextureViewDimension),
649    #[error("This texture view has more than one mipmap level. View mipmap levels: {0:?}")]
650    MipLevelCount(u32),
651    #[error("This texture view has more than one array layer. View array layers: {0:?}")]
652    ArrayLayerCount(u32),
653    #[error(
654        "The aspects of this texture view are a subset of the aspects in the original texture. Aspects: {0:?}"
655    )]
656    Aspects(hal::FormatAspects),
657}
658
659#[derive(Debug)]
660pub struct TextureView<A: hal::Api> {
661    pub(crate) raw: A::TextureView,
662    // The parent's refcount is held alive, but the parent may still be deleted
663    // if it's a surface texture. TODO: make this cleaner.
664    pub(crate) parent_id: Stored<TextureId>,
665    pub(crate) device_id: Stored<DeviceId>,
666    //TODO: store device_id for quick access?
667    pub(crate) desc: HalTextureViewDescriptor,
668    pub(crate) format_features: wgt::TextureFormatFeatures,
669    /// This is `Err` only if the texture view is not renderable
670    pub(crate) render_extent: Result<wgt::Extent3d, TextureViewNotRenderableReason>,
671    pub(crate) samples: u32,
672    pub(crate) selector: TextureSelector,
673    pub(crate) life_guard: LifeGuard,
674}
675
676#[derive(Clone, Debug, Error)]
677#[non_exhaustive]
678pub enum CreateTextureViewError {
679    #[error("Parent texture is invalid or destroyed")]
680    InvalidTexture,
681    #[error("Not enough memory left")]
682    OutOfMemory,
683    #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`")]
684    InvalidTextureViewDimension {
685        view: wgt::TextureViewDimension,
686        texture: wgt::TextureDimension,
687    },
688    #[error("Invalid texture view dimension `{0:?}` of a multisampled texture")]
689    InvalidMultisampledTextureViewDimension(wgt::TextureViewDimension),
690    #[error("Invalid texture depth `{depth}` for texture view of dimension `Cubemap`. Cubemap views must use images of size 6.")]
691    InvalidCubemapTextureDepth { depth: u32 },
692    #[error("Invalid texture depth `{depth}` for texture view of dimension `CubemapArray`. Cubemap views must use images with sizes which are a multiple of 6.")]
693    InvalidCubemapArrayTextureDepth { depth: u32 },
694    #[error("Source texture width and height must be equal for a texture view of dimension `Cube`/`CubeArray`")]
695    InvalidCubeTextureViewSize,
696    #[error("Mip level count is 0")]
697    ZeroMipLevelCount,
698    #[error("Array layer count is 0")]
699    ZeroArrayLayerCount,
700    #[error(
701        "TextureView mip level count + base mip level {requested} must be <= Texture mip level count {total}"
702    )]
703    TooManyMipLevels { requested: u32, total: u32 },
704    #[error("TextureView array layer count + base array layer {requested} must be <= Texture depth/array layer count {total}")]
705    TooManyArrayLayers { requested: u32, total: u32 },
706    #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")]
707    InvalidArrayLayerCount {
708        requested: u32,
709        dim: wgt::TextureViewDimension,
710    },
711    #[error("Aspect {requested_aspect:?} is not in the source texture format {texture_format:?}")]
712    InvalidAspect {
713        texture_format: wgt::TextureFormat,
714        requested_aspect: wgt::TextureAspect,
715    },
716    #[error("Unable to view texture {texture:?} as {view:?}")]
717    FormatReinterpretation {
718        texture: wgt::TextureFormat,
719        view: wgt::TextureFormat,
720    },
721}
722
723#[derive(Clone, Debug, Error)]
724#[non_exhaustive]
725pub enum TextureViewDestroyError {}
726
727impl<A: hal::Api> Resource for TextureView<A> {
728    const TYPE: &'static str = "TextureView";
729
730    fn life_guard(&self) -> &LifeGuard {
731        &self.life_guard
732    }
733}
734
735/// Describes a [`Sampler`]
736#[derive(Clone, Debug, PartialEq)]
737#[cfg_attr(feature = "trace", derive(serde::Serialize))]
738#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
739pub struct SamplerDescriptor<'a> {
740    /// Debug label of the sampler.
741    ///
742    /// This will show up in graphics debuggers for easy identification.
743    pub label: Label<'a>,
744    /// How to deal with out of bounds accesses in the u (i.e. x) direction
745    pub address_modes: [wgt::AddressMode; 3],
746    /// How to filter the texture when it needs to be magnified (made larger)
747    pub mag_filter: wgt::FilterMode,
748    /// How to filter the texture when it needs to be minified (made smaller)
749    pub min_filter: wgt::FilterMode,
750    /// How to filter between mip map levels
751    pub mipmap_filter: wgt::FilterMode,
752    /// Minimum level of detail (i.e. mip level) to use
753    pub lod_min_clamp: f32,
754    /// Maximum level of detail (i.e. mip level) to use
755    pub lod_max_clamp: f32,
756    /// If this is enabled, this is a comparison sampler using the given comparison function.
757    pub compare: Option<wgt::CompareFunction>,
758    /// Must be at least 1. If this is not 1, all filter modes must be linear.
759    pub anisotropy_clamp: u16,
760    /// Border color to use when address_mode is
761    /// [`AddressMode::ClampToBorder`](wgt::AddressMode::ClampToBorder)
762    pub border_color: Option<wgt::SamplerBorderColor>,
763}
764
765#[derive(Debug)]
766pub struct Sampler<A: hal::Api> {
767    pub(crate) raw: A::Sampler,
768    pub(crate) device_id: Stored<DeviceId>,
769    pub(crate) life_guard: LifeGuard,
770    /// `true` if this is a comparison sampler
771    pub(crate) comparison: bool,
772    /// `true` if this is a filtering sampler
773    pub(crate) filtering: bool,
774}
775
776#[derive(Copy, Clone)]
777pub enum SamplerFilterErrorType {
778    MagFilter,
779    MinFilter,
780    MipmapFilter,
781}
782
783impl std::fmt::Debug for SamplerFilterErrorType {
784    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
785        match *self {
786            SamplerFilterErrorType::MagFilter => write!(f, "magFilter"),
787            SamplerFilterErrorType::MinFilter => write!(f, "minFilter"),
788            SamplerFilterErrorType::MipmapFilter => write!(f, "mipmapFilter"),
789        }
790    }
791}
792
793#[derive(Clone, Debug, Error)]
794#[non_exhaustive]
795pub enum CreateSamplerError {
796    #[error(transparent)]
797    Device(#[from] DeviceError),
798    #[error("Invalid lodMinClamp: {0}. Must be greater or equal to 0.0")]
799    InvalidLodMinClamp(f32),
800    #[error("Invalid lodMaxClamp: {lod_max_clamp}. Must be greater or equal to lodMinClamp (which is {lod_min_clamp}).")]
801    InvalidLodMaxClamp {
802        lod_min_clamp: f32,
803        lod_max_clamp: f32,
804    },
805    #[error("Invalid anisotropic clamp: {0}. Must be at least 1.")]
806    InvalidAnisotropy(u16),
807    #[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.")]
808    InvalidFilterModeWithAnisotropy {
809        filter_type: SamplerFilterErrorType,
810        filter_mode: wgt::FilterMode,
811        anisotropic_clamp: u16,
812    },
813    #[error("Cannot create any more samplers")]
814    TooManyObjects,
815    /// AddressMode::ClampToBorder requires feature ADDRESS_MODE_CLAMP_TO_BORDER.
816    #[error(transparent)]
817    MissingFeatures(#[from] MissingFeatures),
818}
819
820impl<A: hal::Api> Resource for Sampler<A> {
821    const TYPE: &'static str = "Sampler";
822
823    fn life_guard(&self) -> &LifeGuard {
824        &self.life_guard
825    }
826}
827
828#[derive(Clone, Debug, Error)]
829#[non_exhaustive]
830pub enum CreateQuerySetError {
831    #[error(transparent)]
832    Device(#[from] DeviceError),
833    #[error("QuerySets cannot be made with zero queries")]
834    ZeroCount,
835    #[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")]
836    TooManyQueries { count: u32, maximum: u32 },
837    #[error(transparent)]
838    MissingFeatures(#[from] MissingFeatures),
839}
840
841pub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor<Label<'a>>;
842
843#[derive(Debug)]
844pub struct QuerySet<A: hal::Api> {
845    pub(crate) raw: A::QuerySet,
846    pub(crate) device_id: Stored<DeviceId>,
847    pub(crate) life_guard: LifeGuard,
848    pub(crate) desc: wgt::QuerySetDescriptor<()>,
849}
850
851impl<A: hal::Api> Resource for QuerySet<A> {
852    const TYPE: &'static str = "QuerySet";
853
854    fn life_guard(&self) -> &LifeGuard {
855        &self.life_guard
856    }
857}
858
859#[derive(Clone, Debug, Error)]
860#[non_exhaustive]
861pub enum DestroyError {
862    #[error("Resource is invalid")]
863    Invalid,
864    #[error("Resource is already destroyed")]
865    AlreadyDestroyed,
866}