Skip to main content

wgpu_types/
texture.rs

1use core::ops::Range;
2
3use macro_rules_attribute::derive;
4
5use crate::{link_to_wgpu_docs, link_to_wgpu_item, ConstDefault, Extent3d, Origin3d};
6
7#[cfg(any(feature = "serde", test))]
8use serde::{Deserialize, Serialize};
9
10#[cfg(doc)]
11use crate::{BindingType, Features};
12
13mod external_image;
14mod external_texture;
15mod format;
16
17pub use external_image::*;
18pub use external_texture::*;
19pub use format::*;
20
21/// Dimensionality of a texture.
22///
23/// Corresponds to [WebGPU `GPUTextureDimension`](
24/// https://gpuweb.github.io/gpuweb/#enumdef-gputexturedimension).
25#[repr(C)]
26#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28pub enum TextureDimension {
29    /// 1D texture
30    #[cfg_attr(feature = "serde", serde(rename = "1d"))]
31    D1,
32    /// 2D texture
33    #[cfg_attr(feature = "serde", serde(rename = "2d"))]
34    D2,
35    /// 3D texture
36    #[cfg_attr(feature = "serde", serde(rename = "3d"))]
37    D3,
38}
39
40/// Order in which texture data is laid out in memory.
41#[derive(Clone, Copy, ConstDefault!, Debug, PartialEq, Eq, Hash)]
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
43pub enum TextureDataOrder {
44    /// The texture is laid out densely in memory as:
45    ///
46    /// ```text
47    /// Layer0Mip0 Layer0Mip1 Layer0Mip2
48    /// Layer1Mip0 Layer1Mip1 Layer1Mip2
49    /// Layer2Mip0 Layer2Mip1 Layer2Mip2
50    /// ````
51    ///
52    /// This is the layout used by dds files.
53    #[custom(default)]
54    LayerMajor,
55    /// The texture is laid out densely in memory as:
56    ///
57    /// ```text
58    /// Layer0Mip0 Layer1Mip0 Layer2Mip0
59    /// Layer0Mip1 Layer1Mip1 Layer2Mip1
60    /// Layer0Mip2 Layer1Mip2 Layer2Mip2
61    /// ```
62    ///
63    /// This is the layout used by ktx and ktx2 files.
64    MipMajor,
65}
66
67/// Dimensions of a particular texture view.
68///
69/// Corresponds to [WebGPU `GPUTextureViewDimension`](
70/// https://gpuweb.github.io/gpuweb/#enumdef-gputextureviewdimension).
71#[repr(C)]
72#[derive(Copy, Clone, Debug, ConstDefault!, Hash, Eq, PartialEq)]
73#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
74pub enum TextureViewDimension {
75    /// A one dimensional texture. `texture_1d` in WGSL and `texture1D` in GLSL.
76    #[cfg_attr(feature = "serde", serde(rename = "1d"))]
77    D1,
78    /// A two dimensional texture. `texture_2d` in WGSL and `texture2D` in GLSL.
79    #[cfg_attr(feature = "serde", serde(rename = "2d"))]
80    #[custom(default)]
81    D2,
82    /// A two dimensional array texture. `texture_2d_array` in WGSL and `texture2DArray` in GLSL.
83    #[cfg_attr(feature = "serde", serde(rename = "2d-array"))]
84    D2Array,
85    /// A cubemap texture. `texture_cube` in WGSL and `textureCube` in GLSL.
86    #[cfg_attr(feature = "serde", serde(rename = "cube"))]
87    Cube,
88    /// A cubemap array texture. `texture_cube_array` in WGSL and `textureCubeArray` in GLSL.
89    #[cfg_attr(feature = "serde", serde(rename = "cube-array"))]
90    CubeArray,
91    /// A three dimensional texture. `texture_3d` in WGSL and `texture3D` in GLSL.
92    #[cfg_attr(feature = "serde", serde(rename = "3d"))]
93    D3,
94}
95
96impl TextureViewDimension {
97    /// Get the texture dimension required of this texture view dimension.
98    #[must_use]
99    pub fn compatible_texture_dimension(self) -> TextureDimension {
100        match self {
101            Self::D1 => TextureDimension::D1,
102            Self::D2 | Self::D2Array | Self::Cube | Self::CubeArray => TextureDimension::D2,
103            Self::D3 => TextureDimension::D3,
104        }
105    }
106}
107
108/// Selects a subset of the data a [`Texture`] holds.
109///
110/// Used in [texture views](TextureViewDescriptor) and
111/// [texture copy operations](TexelCopyTextureInfo).
112///
113/// Corresponds to [WebGPU `GPUTextureAspect`](
114/// https://gpuweb.github.io/gpuweb/#enumdef-gputextureaspect).
115///
116#[doc = link_to_wgpu_item!(struct Texture)]
117#[repr(C)]
118#[derive(Copy, Clone, Debug, ConstDefault!, Hash, Eq, PartialEq)]
119#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
120#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
121pub enum TextureAspect {
122    /// Depth, Stencil, and Color.
123    #[custom(default)]
124    All,
125    /// Stencil.
126    StencilOnly,
127    /// Depth.
128    DepthOnly,
129    /// Plane 0.
130    Plane0,
131    /// Plane 1.
132    Plane1,
133    /// Plane 2.
134    Plane2,
135}
136
137impl TextureAspect {
138    /// Returns the texture aspect for a given plane.
139    #[must_use]
140    pub fn from_plane(plane: u32) -> Option<Self> {
141        Some(match plane {
142            0 => Self::Plane0,
143            1 => Self::Plane1,
144            2 => Self::Plane2,
145            _ => return None,
146        })
147    }
148
149    /// Returns the plane for a given texture aspect.
150    #[must_use]
151    pub fn to_plane(&self) -> Option<u32> {
152        match self {
153            TextureAspect::Plane0 => Some(0),
154            TextureAspect::Plane1 => Some(1),
155            TextureAspect::Plane2 => Some(2),
156            _ => None,
157        }
158    }
159}
160
161bitflags::bitflags! {
162    /// Different ways that you can use a texture.
163    ///
164    /// The usages determine what kind of memory the texture is allocated from and what
165    /// actions the texture can partake in.
166    ///
167    /// Corresponds to [WebGPU `GPUTextureUsageFlags`](
168    /// https://gpuweb.github.io/gpuweb/#typedefdef-gputextureusageflags).
169    #[repr(transparent)]
170    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
171    #[cfg_attr(feature = "serde", serde(transparent))]
172    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
173    pub struct TextureUsages: u32 {
174        //
175        // ---- Start numbering at 1 << 0 ----
176        //
177        // WebGPU features:
178        //
179        /// Allows a texture to be the source in a [`CommandEncoder::copy_texture_to_buffer`] or
180        /// [`CommandEncoder::copy_texture_to_texture`] operation.
181        const COPY_SRC = 1 << 0;
182        /// Allows a texture to be the destination in a  [`CommandEncoder::copy_buffer_to_texture`],
183        /// [`CommandEncoder::copy_texture_to_texture`], or [`Queue::write_texture`] operation.
184        const COPY_DST = 1 << 1;
185        /// Allows a texture to be a [`BindingType::Texture`] in a bind group.
186        const TEXTURE_BINDING = 1 << 2;
187        /// Allows a texture to be a [`BindingType::StorageTexture`] in a bind group.
188        const STORAGE_BINDING = 1 << 3;
189        /// Allows a texture to be an output attachment of a render pass.
190        ///
191        /// Consider adding [`TextureUsages::TRANSIENT_ATTACHMENT`] if the contents are not reused.
192        const RENDER_ATTACHMENT = 1 << 4;
193
194        /// Specifies the contents of this texture will not be used in another pass to potentially reduce memory usage and bandwidth.
195        ///
196        /// No-op on platforms on platforms that do not benefit from transient textures.
197        /// Generally mobile and Apple chips care about this.
198        ///
199        /// Incompatible with ALL other usages except [`TextureUsages::RENDER_ATTACHMENT`] and requires it.
200        ///
201        /// Requires [`LoadOp::Clear`] or [`LoadOp::DontCare`] (if it is available) and [`StoreOp::Discard`].
202        const TRANSIENT_ATTACHMENT = 1 << 5;
203
204        //
205        // ---- Restart Numbering for Native Features ---
206        //
207        // Native Features:
208        //
209        /// Allows a texture to be used with image atomics. Requires [`Features::TEXTURE_ATOMIC`].
210        const STORAGE_ATOMIC = 1 << 16;
211    }
212}
213
214bitflags::bitflags! {
215    /// Similar to `TextureUsages`, but used only for `CommandEncoder::transition_resources`.
216    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
217    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
218    #[cfg_attr(feature = "serde", serde(transparent))]
219    pub struct TextureUses: u32 {
220        /// The texture is in unknown state.
221        const UNINITIALIZED = 1 << 0;
222        /// Ready to present image to the surface.
223        const PRESENT = 1 << 1;
224        /// The source of a hardware copy.
225        /// cbindgen:ignore
226        const COPY_SRC = 1 << 2;
227        /// The destination of a hardware copy.
228        /// cbindgen:ignore
229        const COPY_DST = 1 << 3;
230        /// Read-only sampled or fetched resource.
231        const RESOURCE = 1 << 4;
232        /// The color target of a renderpass.
233        const COLOR_TARGET = 1 << 5;
234        /// Read-only depth usage.
235        const DEPTH_READ = 1 << 6;
236        /// Read-write depth usage
237        const DEPTH_WRITE = 1 << 7;
238        /// Read-only stencil usage.
239        const STENCIL_READ = 1 << 8;
240        /// Read-write stencil usage
241        const STENCIL_WRITE = 1 << 9;
242        /// Read-only storage texture usage. Corresponds to a UAV in d3d, so is exclusive, despite being read only.
243        /// cbindgen:ignore
244        const STORAGE_READ_ONLY = 1 << 10;
245        /// Write-only storage texture usage.
246        /// cbindgen:ignore
247        const STORAGE_WRITE_ONLY = 1 << 11;
248        /// Read-write storage texture usage.
249        /// cbindgen:ignore
250        const STORAGE_READ_WRITE = 1 << 12;
251        /// Image atomic enabled storage.
252        /// cbindgen:ignore
253        const STORAGE_ATOMIC = 1 << 13;
254        /// Transient texture that may not have any backing memory. Not a resource state stored in the trackers, only used for passing down usages to create_texture.
255        const TRANSIENT = 1 << 14;
256        /// The combination of states that a texture may be in _at the same time_.
257        /// cbindgen:ignore
258        const INCLUSIVE = Self::COPY_SRC.bits() | Self::RESOURCE.bits() | Self::DEPTH_READ.bits()| Self::STENCIL_READ.bits() | Self::STORAGE_READ_ONLY.bits();
259        /// The combination of states that a texture must exclusively be in.
260        /// cbindgen:ignore
261        const EXCLUSIVE = Self::COPY_DST.bits() | Self::COLOR_TARGET.bits() | Self::STORAGE_WRITE_ONLY.bits() | Self::STORAGE_READ_WRITE.bits() | Self::STORAGE_ATOMIC.bits() | Self::PRESENT.bits();
262
263        /// Flag used by the wgpu-core texture tracker to say a texture is in different states for every sub-resource
264        const COMPLEX = 1 << 15;
265        /// Flag used by the wgpu-core texture tracker to say that the tracker does not know the state of the sub-resource.
266        /// This is different from UNINITIALIZED as that says the tracker does know, but the texture has not been initialized.
267        const UNKNOWN = 1 << 16;
268
269        /// Flag used by texture tracker to say the read-only depth aspect of texture is sampled.
270        const DEPTH_SAMPLED = 1 << 17;
271        /// Flag used by texture tracker to say the read-only stencil aspect of texture is sampled.
272        const STENCIL_SAMPLED = 1 << 18;
273    }
274}
275
276/// A texture transition for use with `CommandEncoder::transition_resources`.
277#[derive(Clone, Debug)]
278#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
279pub struct TextureTransition<T> {
280    /// The texture to transition.
281    pub texture: T,
282    /// An optional selector to transition only part of the texture.
283    ///
284    /// If None, the entire texture will be transitioned.
285    pub selector: Option<TextureSelector>,
286    /// The new state to transition to.
287    pub state: TextureUses,
288}
289
290/// Specifies a particular set of subresources in a texture.
291#[derive(Clone, Debug, PartialEq, Eq)]
292#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
293pub struct TextureSelector {
294    /// Range of mips to use.
295    pub mips: Range<u32>,
296    /// Range of layers to use.
297    pub layers: Range<u32>,
298}
299
300/// Specific type of a sample in a texture binding.
301///
302/// Corresponds to [WebGPU `GPUTextureSampleType`](
303/// https://gpuweb.github.io/gpuweb/#enumdef-gputexturesampletype).
304#[repr(C)]
305#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
306#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
307pub enum TextureSampleType {
308    /// Sampling returns floats.
309    ///
310    /// Example WGSL syntax:
311    /// ```rust,ignore
312    /// @group(0) @binding(0)
313    /// var t: texture_2d<f32>;
314    /// ```
315    ///
316    /// Example GLSL syntax:
317    /// ```cpp,ignore
318    /// layout(binding = 0)
319    /// uniform texture2D t;
320    /// ```
321    Float {
322        /// If this is `false`, the texture can't be sampled with
323        /// a filtering sampler.
324        ///
325        /// Even if this is `true`, it's possible to sample with
326        /// a **non-filtering** sampler.
327        filterable: bool,
328    },
329    /// Sampling does the depth reference comparison.
330    ///
331    /// This is also compatible with a non-filtering sampler.
332    ///
333    /// Example WGSL syntax:
334    /// ```rust,ignore
335    /// @group(0) @binding(0)
336    /// var t: texture_depth_2d;
337    /// ```
338    ///
339    /// Example GLSL syntax:
340    /// ```cpp,ignore
341    /// layout(binding = 0)
342    /// uniform texture2DShadow t;
343    /// ```
344    Depth,
345    /// Sampling returns signed integers.
346    ///
347    /// Example WGSL syntax:
348    /// ```rust,ignore
349    /// @group(0) @binding(0)
350    /// var t: texture_2d<i32>;
351    /// ```
352    ///
353    /// Example GLSL syntax:
354    /// ```cpp,ignore
355    /// layout(binding = 0)
356    /// uniform itexture2D t;
357    /// ```
358    Sint,
359    /// Sampling returns unsigned integers.
360    ///
361    /// Example WGSL syntax:
362    /// ```rust,ignore
363    /// @group(0) @binding(0)
364    /// var t: texture_2d<u32>;
365    /// ```
366    ///
367    /// Example GLSL syntax:
368    /// ```cpp,ignore
369    /// layout(binding = 0)
370    /// uniform utexture2D t;
371    /// ```
372    Uint,
373}
374
375impl Default for TextureSampleType {
376    fn default() -> Self {
377        Self::Float { filterable: true }
378    }
379}
380
381/// Specific type of a sample in a texture binding.
382///
383/// For use in [`BindingType::StorageTexture`].
384///
385/// Corresponds to [WebGPU `GPUStorageTextureAccess`](
386/// https://gpuweb.github.io/gpuweb/#enumdef-gpustoragetextureaccess).
387#[repr(C)]
388#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
389#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
390#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
391pub enum StorageTextureAccess {
392    /// The texture can only be written in the shader and it:
393    /// - may or may not be annotated with `write` (WGSL).
394    /// - must be annotated with `writeonly` (GLSL).
395    ///
396    /// Example WGSL syntax:
397    /// ```rust,ignore
398    /// @group(0) @binding(0)
399    /// var my_storage_image: texture_storage_2d<r32float, write>;
400    /// ```
401    ///
402    /// Example GLSL syntax:
403    /// ```cpp,ignore
404    /// layout(set=0, binding=0, r32f) writeonly uniform image2D myStorageImage;
405    /// ```
406    WriteOnly,
407    /// The texture can only be read in the shader and it must be annotated with `read` (WGSL) or
408    /// `readonly` (GLSL).
409    ///
410    /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] must be enabled to use this access
411    /// mode. This is a native-only extension.
412    ///
413    /// Example WGSL syntax:
414    /// ```rust,ignore
415    /// @group(0) @binding(0)
416    /// var my_storage_image: texture_storage_2d<r32float, read>;
417    /// ```
418    ///
419    /// Example GLSL syntax:
420    /// ```cpp,ignore
421    /// layout(set=0, binding=0, r32f) readonly uniform image2D myStorageImage;
422    /// ```
423    ReadOnly,
424    /// The texture can be both read and written in the shader and must be annotated with
425    /// `read_write` in WGSL.
426    ///
427    /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] must be enabled to use this access
428    /// mode.  This is a nonstandard, native-only extension.
429    ///
430    /// Example WGSL syntax:
431    /// ```rust,ignore
432    /// @group(0) @binding(0)
433    /// var my_storage_image: texture_storage_2d<r32float, read_write>;
434    /// ```
435    ///
436    /// Example GLSL syntax:
437    /// ```cpp,ignore
438    /// layout(set=0, binding=0, r32f) uniform image2D myStorageImage;
439    /// ```
440    ReadWrite,
441    /// The texture can be both read and written in the shader via atomics and must be annotated
442    /// with `read_write` in WGSL.
443    ///
444    /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] must be enabled to use this access
445    /// mode.  This is a nonstandard, native-only extension.
446    ///
447    /// Example WGSL syntax:
448    /// ```rust,ignore
449    /// @group(0) @binding(0)
450    /// var my_storage_image: texture_storage_2d<r32uint, atomic>;
451    /// ```
452    Atomic,
453}
454
455/// Specifies the component swizzle for a channel.
456///
457/// Used in [`TextureComponentSwizzle`]
458#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
459#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
460pub enum ComponentSwizzle {
461    /// Force its value to 0.
462    Zero,
463    /// Force its value to 1.
464    One,
465    /// Take its value from the red channel of the texture.
466    R,
467    /// Take its value from the green channel of the texture.
468    G,
469    /// Take its value from the blue channel of the texture.
470    B,
471    /// Take its value from the alpha channel of the texture.
472    A,
473}
474
475/// Specifies the texture component swizzle for each channel.
476///
477/// Used in [`TextureViewDescriptor::swizzle`].
478///
479/// Example:
480/// ```rust
481/// # use wgpu_types::{TextureComponentSwizzle, ComponentSwizzle};
482/// // The swizzle maps `xgxr` to `rg01`, or maps `rgba` to `ag01`
483/// TextureComponentSwizzle {
484///     r: ComponentSwizzle::A,
485///     g: ComponentSwizzle::G,
486///     b: ComponentSwizzle::Zero,
487///     a: ComponentSwizzle::One,
488/// };
489/// ```
490#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
491#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
492pub struct TextureComponentSwizzle {
493    /// Replace the red channel with the [`ComponentSwizzle`].
494    pub r: ComponentSwizzle,
495    /// Replace the green channel with the [`ComponentSwizzle`].
496    pub g: ComponentSwizzle,
497    /// Replace the blue channel with the [`ComponentSwizzle`].
498    pub b: ComponentSwizzle,
499    /// Replace the alpha channel with the [`ComponentSwizzle`].
500    pub a: ComponentSwizzle,
501}
502
503impl Default for TextureComponentSwizzle {
504    fn default() -> Self {
505        Self {
506            r: ComponentSwizzle::R,
507            g: ComponentSwizzle::G,
508            b: ComponentSwizzle::B,
509            a: ComponentSwizzle::A,
510        }
511    }
512}
513
514impl TextureComponentSwizzle {
515    fn select(&self, component: ComponentSwizzle) -> ComponentSwizzle {
516        match component {
517            ComponentSwizzle::Zero => ComponentSwizzle::Zero,
518            ComponentSwizzle::One => ComponentSwizzle::One,
519            ComponentSwizzle::R => self.r,
520            ComponentSwizzle::G => self.g,
521            ComponentSwizzle::B => self.b,
522            ComponentSwizzle::A => self.a,
523        }
524    }
525
526    /// Computes a swizzle that when applied, is equivalent to applying `self` then `other`,
527    /// like the order of WGSL swizzles (`value.rgba.rgba`).
528    pub fn compose(&self, other: Self) -> Self {
529        Self {
530            r: self.select(other.r),
531            g: self.select(other.g),
532            b: self.select(other.b),
533            a: self.select(other.a),
534        }
535    }
536}
537
538/// Describes a [`TextureView`].
539///
540/// For use with [`Texture::create_view()`].
541///
542/// Corresponds to [WebGPU `GPUTextureViewDescriptor`](
543/// https://gpuweb.github.io/gpuweb/#dictdef-gputextureviewdescriptor).
544///
545#[doc = link_to_wgpu_item!(struct TextureView)]
546#[doc = link_to_wgpu_docs!(["`Texture::create_view()`"]: "struct.Texture.html#method.create_view")]
547#[derive(Clone, Debug, Default, Eq, PartialEq)]
548#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
549pub struct TextureViewDescriptor<L> {
550    /// Debug label of the texture view. This will show up in graphics debuggers for easy identification.
551    pub label: L,
552    /// Format of the texture view. Either must be the same as the texture format or in the list
553    /// of `view_formats` in the texture's descriptor.
554    pub format: Option<TextureFormat>,
555    /// The dimension of the texture view. For 1D textures, this must be `D1`. For 2D textures it must be one of
556    /// `D2`, `D2Array`, `Cube`, and `CubeArray`. For 3D textures it must be `D3`
557    pub dimension: Option<TextureViewDimension>,
558    /// The allowed usage(s) for the texture view. Must be a subset of the usage flags of the texture.
559    /// If not provided, defaults to the full set of usage flags of the texture.
560    pub usage: Option<TextureUsages>,
561    /// Aspect of the texture. Color textures must be [`TextureAspect::All`].
562    pub aspect: TextureAspect,
563    /// Base mip level.
564    pub base_mip_level: u32,
565    /// Mip level count.
566    /// If `Some(count)`, `base_mip_level + count` must be less or equal to underlying texture mip count.
567    /// If `None`, considered to include the rest of the mipmap levels, but at least 1 in total.
568    pub mip_level_count: Option<u32>,
569    /// Base array layer.
570    pub base_array_layer: u32,
571    /// Layer count.
572    /// If `Some(count)`, `base_array_layer + count` must be less or equal to the underlying array count.
573    /// If `None`, considered to include the rest of the array layers, but at least 1 in total.
574    pub array_layer_count: Option<u32>,
575    /// Texture component swizzle.
576    /// When the texture view is accessed by a shader, the red/green/blue/alpha channels are replaced
577    /// by the value corresponding to the component specified in [`TextureComponentSwizzle`].
578    ///
579    /// This requires [`Features::TEXTURE_COMPONENT_SWIZZLE`] if it is not identity swizzle.
580    pub swizzle: TextureComponentSwizzle,
581}
582
583impl<L> TextureViewDescriptor<L> {
584    /// Takes a closure and maps the label of the texture view descriptor into another.
585    #[must_use]
586    pub fn map_label<'a, K>(&'a self, fun: impl FnOnce(&'a L) -> K) -> TextureViewDescriptor<K> {
587        TextureViewDescriptor {
588            label: fun(&self.label),
589            format: self.format,
590            dimension: self.dimension,
591            usage: self.usage,
592            aspect: self.aspect,
593            base_mip_level: self.base_mip_level,
594            mip_level_count: self.mip_level_count,
595            base_array_layer: self.base_array_layer,
596            array_layer_count: self.array_layer_count,
597            swizzle: self.swizzle,
598        }
599    }
600}
601
602/// Describes a [`Texture`](../wgpu/struct.Texture.html).
603///
604/// Corresponds to [WebGPU `GPUTextureDescriptor`](
605/// https://gpuweb.github.io/gpuweb/#dictdef-gputexturedescriptor).
606#[repr(C)]
607#[derive(Clone, Debug, PartialEq, Eq, Hash)]
608#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
609pub struct TextureDescriptor<L, V> {
610    /// Debug label of the texture. This will show up in graphics debuggers for easy identification.
611    pub label: L,
612    /// Size of the texture. All components must be greater than zero. For a
613    /// regular 1D/2D texture, the unused sizes will be 1. For 2DArray textures,
614    /// Z is the number of 2D textures in that array.
615    pub size: Extent3d,
616    /// Mip count of texture. For a texture with no extra mips, this must be 1.
617    pub mip_level_count: u32,
618    /// Sample count of texture. If this is not 1, texture must have [`BindingType::Texture::multisampled`] set to true.
619    pub sample_count: u32,
620    /// Dimensions of the texture.
621    pub dimension: TextureDimension,
622    /// Format of the texture.
623    pub format: TextureFormat,
624    /// Allowed usages of the texture. If used in other ways, the operation will panic.
625    pub usage: TextureUsages,
626    /// Specifies what view formats will be allowed when calling `Texture::create_view` on this texture.
627    ///
628    /// View formats of the same format as the texture are always allowed.
629    ///
630    /// Note: currently, only the srgb-ness is allowed to change. (ex: `Rgba8Unorm` texture + `Rgba8UnormSrgb` view)
631    pub view_formats: V,
632}
633
634impl<L, V> TextureDescriptor<L, V> {
635    /// Takes a closure and maps the label of the texture descriptor into another.
636    #[must_use]
637    pub fn map_label<'a, K>(&'a self, fun: impl FnOnce(&'a L) -> K) -> TextureDescriptor<K, V>
638    where
639        V: Clone,
640    {
641        TextureDescriptor {
642            label: fun(&self.label),
643            size: self.size,
644            mip_level_count: self.mip_level_count,
645            sample_count: self.sample_count,
646            dimension: self.dimension,
647            format: self.format,
648            usage: self.usage,
649            view_formats: self.view_formats.clone(),
650        }
651    }
652
653    /// Maps the label and view formats of the texture descriptor into another.
654    #[must_use]
655    pub fn map_label_and_view_formats<'a, K, M>(
656        &'a self,
657        l_fun: impl FnOnce(&'a L) -> K,
658        v_fun: impl FnOnce(&'a V) -> M,
659    ) -> TextureDescriptor<K, M> {
660        TextureDescriptor {
661            label: l_fun(&self.label),
662            size: self.size,
663            mip_level_count: self.mip_level_count,
664            sample_count: self.sample_count,
665            dimension: self.dimension,
666            format: self.format,
667            usage: self.usage,
668            view_formats: v_fun(&self.view_formats),
669        }
670    }
671
672    /// Calculates the extent at a given mip level.
673    ///
674    /// If the given mip level is larger than possible, returns None.
675    ///
676    /// Treats the depth as part of the mipmaps. If calculating
677    /// for a 2DArray texture, which does not mipmap depth, set depth to 1.
678    ///
679    /// ```rust
680    /// # use wgpu_types as wgpu;
681    /// # type TextureDescriptor<'a> = wgpu::TextureDescriptor<(), &'a [wgpu::TextureFormat]>;
682    /// let desc  = TextureDescriptor {
683    ///   label: (),
684    ///   size: wgpu::Extent3d { width: 100, height: 60, depth_or_array_layers: 1 },
685    ///   mip_level_count: 7,
686    ///   sample_count: 1,
687    ///   dimension: wgpu::TextureDimension::D3,
688    ///   format: wgpu::TextureFormat::Rgba8Sint,
689    ///   usage: wgpu::TextureUsages::empty(),
690    ///   view_formats: &[],
691    /// };
692    ///
693    /// assert_eq!(desc.mip_level_size(0), Some(wgpu::Extent3d { width: 100, height: 60, depth_or_array_layers: 1 }));
694    /// assert_eq!(desc.mip_level_size(1), Some(wgpu::Extent3d { width: 50, height: 30, depth_or_array_layers: 1 }));
695    /// assert_eq!(desc.mip_level_size(2), Some(wgpu::Extent3d { width: 25, height: 15, depth_or_array_layers: 1 }));
696    /// assert_eq!(desc.mip_level_size(3), Some(wgpu::Extent3d { width: 12, height: 7, depth_or_array_layers: 1 }));
697    /// assert_eq!(desc.mip_level_size(4), Some(wgpu::Extent3d { width: 6, height: 3, depth_or_array_layers: 1 }));
698    /// assert_eq!(desc.mip_level_size(5), Some(wgpu::Extent3d { width: 3, height: 1, depth_or_array_layers: 1 }));
699    /// assert_eq!(desc.mip_level_size(6), Some(wgpu::Extent3d { width: 1, height: 1, depth_or_array_layers: 1 }));
700    /// assert_eq!(desc.mip_level_size(7), None);
701    /// ```
702    #[must_use]
703    pub fn mip_level_size(&self, level: u32) -> Option<Extent3d> {
704        if level >= self.mip_level_count {
705            return None;
706        }
707
708        Some(self.size.mip_level_size(level, self.dimension))
709    }
710
711    /// Computes the render extent of this texture.
712    ///
713    /// This is a low-level helper exported for use by wgpu-core.
714    ///
715    /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-compute-render-extent>
716    ///
717    /// # Panics
718    ///
719    /// If the mip level is out of range.
720    #[doc(hidden)]
721    #[must_use]
722    pub fn compute_render_extent(&self, mip_level: u32, plane: Option<u32>) -> Extent3d {
723        let Extent3d {
724            width,
725            height,
726            depth_or_array_layers: _,
727        } = self.mip_level_size(mip_level).expect("invalid mip level");
728
729        let (w_subsampling, h_subsampling) = self.format.subsampling_factors(plane);
730
731        let width = width / w_subsampling;
732        let height = height / h_subsampling;
733
734        Extent3d {
735            width,
736            height,
737            depth_or_array_layers: 1,
738        }
739    }
740
741    /// Returns the number of array layers.
742    ///
743    /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-array-layer-count>
744    #[must_use]
745    pub fn array_layer_count(&self) -> u32 {
746        match self.dimension {
747            TextureDimension::D1 | TextureDimension::D3 => 1,
748            TextureDimension::D2 => self.size.depth_or_array_layers,
749        }
750    }
751
752    /// Returns the theoretical memory footprint of a texture.
753    ///
754    /// Actual memory usage may greatly exceed this value due to alignment and padding.
755    #[must_use]
756    pub fn theoretical_memory_footprint(&self) -> u64 {
757        (0..self.mip_level_count).fold(0, |acc, level| {
758            acc.saturating_add(
759                self.format.theoretical_memory_footprint(
760                    self.mip_level_size(level)
761                        .expect("mipmap level should be inbounds"),
762                ),
763            )
764        })
765    }
766}
767
768/// Describes a `Sampler`.
769///
770/// For use with `Device::create_sampler`.
771///
772/// Corresponds to [WebGPU `GPUSamplerDescriptor`](
773/// https://gpuweb.github.io/gpuweb/#dictdef-gpusamplerdescriptor).
774#[derive(Clone, Debug, PartialEq)]
775#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
776pub struct SamplerDescriptor<L> {
777    /// Debug label of the sampler. This will show up in graphics debuggers for easy identification.
778    pub label: L,
779    /// How to deal with out of bounds accesses in the u (i.e. x) direction
780    pub address_mode_u: AddressMode,
781    /// How to deal with out of bounds accesses in the v (i.e. y) direction
782    pub address_mode_v: AddressMode,
783    /// How to deal with out of bounds accesses in the w (i.e. z) direction
784    pub address_mode_w: AddressMode,
785    /// How to filter the texture when it needs to be magnified (made larger)
786    pub mag_filter: FilterMode,
787    /// How to filter the texture when it needs to be minified (made smaller)
788    pub min_filter: FilterMode,
789    /// How to filter between mip map levels
790    pub mipmap_filter: MipmapFilterMode,
791    /// Minimum level of detail (i.e. mip level) to use
792    pub lod_min_clamp: f32,
793    /// Maximum level of detail (i.e. mip level) to use
794    pub lod_max_clamp: f32,
795    /// If this is enabled, this is a comparison sampler using the given comparison function.
796    pub compare: Option<crate::CompareFunction>,
797    /// Must be at least 1. If this is not 1, all filter modes must be linear.
798    pub anisotropy_clamp: u16,
799    /// Border color to use when `address_mode` is [`AddressMode::ClampToBorder`]
800    pub border_color: Option<SamplerBorderColor>,
801}
802
803impl<L: Default> Default for SamplerDescriptor<L> {
804    fn default() -> Self {
805        Self {
806            label: Default::default(),
807            address_mode_u: Default::default(),
808            address_mode_v: Default::default(),
809            address_mode_w: Default::default(),
810            mag_filter: Default::default(),
811            min_filter: Default::default(),
812            mipmap_filter: Default::default(),
813            lod_min_clamp: 0.0,
814            lod_max_clamp: 32.0,
815            compare: None,
816            anisotropy_clamp: 1,
817            border_color: None,
818        }
819    }
820}
821
822impl<L> SamplerDescriptor<L> {
823    /// Takes a closure and maps the label of the sampler descriptor into another.
824    #[must_use]
825    pub fn map_label<'a, K>(&'a self, fun: impl FnOnce(&'a L) -> K) -> SamplerDescriptor<K> {
826        SamplerDescriptor {
827            label: fun(&self.label),
828            address_mode_u: self.address_mode_u,
829            address_mode_v: self.address_mode_v,
830            address_mode_w: self.address_mode_w,
831            mag_filter: self.mag_filter,
832            min_filter: self.min_filter,
833            mipmap_filter: self.mipmap_filter,
834            lod_min_clamp: self.lod_min_clamp,
835            lod_max_clamp: self.lod_max_clamp,
836            compare: self.compare,
837            anisotropy_clamp: self.anisotropy_clamp,
838            border_color: self.border_color,
839        }
840    }
841}
842
843/// How edges should be handled in texture addressing.
844///
845/// Corresponds to [WebGPU `GPUAddressMode`](
846/// https://gpuweb.github.io/gpuweb/#enumdef-gpuaddressmode).
847#[repr(C)]
848#[derive(Copy, Clone, Debug, ConstDefault!, Hash, Eq, PartialEq)]
849#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
850#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
851pub enum AddressMode {
852    /// Clamp the value to the edge of the texture
853    ///
854    /// -0.25 -> 0.0
855    /// 1.25  -> 1.0
856    #[custom(default)]
857    ClampToEdge = 0,
858    /// Repeat the texture in a tiling fashion
859    ///
860    /// -0.25 -> 0.75
861    /// 1.25 -> 0.25
862    Repeat = 1,
863    /// Repeat the texture, mirroring it every repeat
864    ///
865    /// -0.25 -> 0.25
866    /// 1.25 -> 0.75
867    MirrorRepeat = 2,
868    /// Clamp the value to the border of the texture
869    /// Requires feature [`Features::ADDRESS_MODE_CLAMP_TO_BORDER`]
870    ///
871    /// -0.25 -> border
872    /// 1.25 -> border
873    ClampToBorder = 3,
874}
875
876/// Texel mixing mode when sampling between texels.
877///
878/// Corresponds to [WebGPU `GPUFilterMode`](
879/// https://gpuweb.github.io/gpuweb/#enumdef-gpufiltermode).
880#[repr(C)]
881#[derive(Copy, Clone, Debug, ConstDefault!, Hash, Eq, PartialEq)]
882#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
883#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
884pub enum FilterMode {
885    /// Nearest neighbor sampling.
886    ///
887    /// This creates a pixelated effect.
888    #[custom(default)]
889    Nearest = 0,
890    /// Linear Interpolation
891    ///
892    /// This makes textures smooth but blurry.
893    Linear = 1,
894}
895
896/// Texel mixing mode when sampling between texels.
897///
898/// Corresponds to [WebGPU `GPUMipmapFilterMode`](
899/// https://gpuweb.github.io/gpuweb/#enumdef-gpumipmapfiltermode).
900#[repr(C)]
901#[derive(Copy, Clone, Debug, ConstDefault!, Hash, Eq, PartialEq)]
902#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
903#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
904pub enum MipmapFilterMode {
905    /// Nearest neighbor sampling.
906    ///
907    /// Return the value of the texel nearest to the texture coordinates.
908    #[custom(default)]
909    Nearest = 0,
910    /// Linear Interpolation
911    ///
912    /// Select two texels in each dimension and return a linear interpolation between their values.
913    Linear = 1,
914}
915
916/// Color variation to use when sampler addressing mode is [`AddressMode::ClampToBorder`]
917#[repr(C)]
918#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
919#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
920pub enum SamplerBorderColor {
921    /// [0, 0, 0, 0]
922    TransparentBlack,
923    /// [0, 0, 0, 1]
924    OpaqueBlack,
925    /// [1, 1, 1, 1]
926    OpaqueWhite,
927
928    /// On the Metal backend, this is equivalent to `TransparentBlack` for
929    /// textures that have an alpha component, and equivalent to `OpaqueBlack`
930    /// for textures that do not have an alpha component. On other backends,
931    /// this is equivalent to `TransparentBlack`. Requires
932    /// [`Features::ADDRESS_MODE_CLAMP_TO_ZERO`]. Not supported on the web.
933    Zero,
934}
935
936/// Layout of a texture in a buffer's memory.
937///
938/// The bytes per row and rows per image can be hard to figure out so here are some examples:
939///
940/// | Resolution | Format | Bytes per block | Pixels per block | Bytes per row                          | Rows per image               |
941/// |------------|--------|-----------------|------------------|----------------------------------------|------------------------------|
942/// | 256x256    | RGBA8  | 4               | 1 * 1 * 1        | 256 * 4 = Some(1024)                   | None                         |
943/// | 32x16x8    | RGBA8  | 4               | 1 * 1 * 1        | 32 * 4 = 128 padded to 256 = Some(256) | None                         |
944/// | 256x256    | BC3    | 16              | 4 * 4 * 1        | 16 * (256 / 4) = 1024 = Some(1024)     | None                         |
945/// | 64x64x8    | BC3    | 16              | 4 * 4 * 1        | 16 * (64 / 4) = 256 = Some(256)        | 64 / 4 = 16 = Some(16)       |
946///
947/// Corresponds to [WebGPU `GPUTexelCopyBufferLayout`](
948/// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagedatalayout).
949#[repr(C)]
950#[derive(Clone, Copy, Debug, ConstDefault!)]
951#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
952pub struct TexelCopyBufferLayout {
953    /// Offset into the buffer that is the start of the texture. Must be a multiple of texture block size.
954    /// For non-compressed textures, this is 1.
955    pub offset: crate::BufferAddress,
956    /// Bytes per "row" in an image.
957    ///
958    /// A row is one row of pixels or of compressed blocks in the x direction.
959    ///
960    /// This value is required if there are multiple rows (i.e. height or depth is more than one pixel or pixel block for compressed textures)
961    ///
962    /// Must be a multiple of 256 for [`CommandEncoder::copy_buffer_to_texture`][CEcbtt]
963    /// and [`CommandEncoder::copy_texture_to_buffer`][CEcttb]. You must manually pad the
964    /// buffer as if the image width is a multiple of 256. An image of size (500, 500) can be
965    /// written to a buffer of size (512, 500) with `bytes_per_row` of 512,
966    ///
967    /// [`Queue::write_texture`][Qwt] does not have this requirement.
968    ///
969    /// Must be a multiple of the texture block size. For non-compressed textures, this is 1.
970    ///
971    #[doc = link_to_wgpu_docs!(["CEcbtt"]: "struct.CommandEncoder.html#method.copy_buffer_to_texture")]
972    #[doc = link_to_wgpu_docs!(["CEcttb"]: "struct.CommandEncoder.html#method.copy_texture_to_buffer")]
973    #[doc = link_to_wgpu_docs!(["Qwt"]: "struct.Queue.html#method.write_texture")]
974    pub bytes_per_row: Option<u32>,
975    /// "Rows" that make up a single "image".
976    ///
977    /// A row is one row of pixels or of compressed blocks in the x direction.
978    ///
979    /// An image is one layer in the z direction of a 3D image or 2DArray texture.
980    ///
981    /// The amount of rows per image may be larger than the actual amount of rows of data.
982    ///
983    /// Required if there are multiple images (i.e. the depth is more than one).
984    pub rows_per_image: Option<u32>,
985}
986
987/// View of a buffer which can be used to copy to/from a texture.
988///
989/// Corresponds to [WebGPU `GPUTexelCopyBufferInfo`](
990/// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopybuffer).
991#[repr(C)]
992#[derive(Copy, Clone, Debug)]
993#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
994pub struct TexelCopyBufferInfo<B> {
995    /// The buffer to be copied to/from.
996    pub buffer: B,
997    /// The layout of the texture data in this buffer.
998    pub layout: TexelCopyBufferLayout,
999}
1000
1001/// View of a texture which can be used to copy to/from a buffer/texture.
1002///
1003/// Corresponds to [WebGPU `GPUTexelCopyTextureInfo`](
1004/// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopytexture).
1005#[repr(C)]
1006#[derive(Copy, Clone, Debug)]
1007#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1008pub struct TexelCopyTextureInfo<T> {
1009    /// The texture to be copied to/from.
1010    pub texture: T,
1011    /// The target mip level of the texture.
1012    pub mip_level: u32,
1013    /// The base texel of the texture in the selected `mip_level`. Together
1014    /// with the `copy_size` argument to copy functions, defines the
1015    /// sub-region of the texture to copy.
1016    #[cfg_attr(feature = "serde", serde(default))]
1017    pub origin: Origin3d,
1018    /// The copy aspect.
1019    #[cfg_attr(feature = "serde", serde(default))]
1020    pub aspect: TextureAspect,
1021}
1022
1023impl<T> TexelCopyTextureInfo<T> {
1024    /// Adds color space and premultiplied alpha information to make this
1025    /// descriptor tagged.
1026    pub fn to_tagged(
1027        self,
1028        color_space: PredefinedColorSpace,
1029        premultiplied_alpha: bool,
1030    ) -> CopyExternalImageDestInfo<T> {
1031        CopyExternalImageDestInfo {
1032            texture: self.texture,
1033            mip_level: self.mip_level,
1034            origin: self.origin,
1035            aspect: self.aspect,
1036            color_space,
1037            premultiplied_alpha,
1038        }
1039    }
1040}
1041
1042/// Subresource range within an image
1043#[repr(C)]
1044#[derive(Clone, Copy, Debug, ConstDefault!, Eq, PartialEq)]
1045#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1046#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1047pub struct ImageSubresourceRange {
1048    /// Aspect of the texture. Color textures must be [`TextureAspect::All`][TAA].
1049    ///
1050    #[doc = link_to_wgpu_docs!(["TAA"]: "enum.TextureAspect.html#variant.All")]
1051    pub aspect: TextureAspect,
1052    /// Base mip level.
1053    pub base_mip_level: u32,
1054    /// Mip level count.
1055    /// If `Some(count)`, `base_mip_level + count` must be less or equal to underlying texture mip count.
1056    /// If `None`, considered to include the rest of the mipmap levels, but at least 1 in total.
1057    pub mip_level_count: Option<u32>,
1058    /// Base array layer.
1059    pub base_array_layer: u32,
1060    /// Layer count.
1061    /// If `Some(count)`, `base_array_layer + count` must be less or equal to the underlying array count.
1062    /// If `None`, considered to include the rest of the array layers, but at least 1 in total.
1063    pub array_layer_count: Option<u32>,
1064}
1065
1066impl ImageSubresourceRange {
1067    /// Returns if the given range represents a full resource, with a texture of the given
1068    /// layer count and mip count.
1069    ///
1070    /// ```rust
1071    /// # use wgpu_types as wgpu;
1072    ///
1073    /// let range_none = wgpu::ImageSubresourceRange {
1074    ///     aspect: wgpu::TextureAspect::All,
1075    ///     base_mip_level: 0,
1076    ///     mip_level_count: None,
1077    ///     base_array_layer: 0,
1078    ///     array_layer_count: None,
1079    /// };
1080    /// assert_eq!(range_none.is_full_resource(wgpu::TextureFormat::Stencil8, 5, 10), true);
1081    ///
1082    /// let range_some = wgpu::ImageSubresourceRange {
1083    ///     aspect: wgpu::TextureAspect::All,
1084    ///     base_mip_level: 0,
1085    ///     mip_level_count: Some(5),
1086    ///     base_array_layer: 0,
1087    ///     array_layer_count: Some(10),
1088    /// };
1089    /// assert_eq!(range_some.is_full_resource(wgpu::TextureFormat::Stencil8, 5, 10), true);
1090    ///
1091    /// let range_mixed = wgpu::ImageSubresourceRange {
1092    ///     aspect: wgpu::TextureAspect::StencilOnly,
1093    ///     base_mip_level: 0,
1094    ///     // Only partial resource
1095    ///     mip_level_count: Some(3),
1096    ///     base_array_layer: 0,
1097    ///     array_layer_count: None,
1098    /// };
1099    /// assert_eq!(range_mixed.is_full_resource(wgpu::TextureFormat::Stencil8, 5, 10), false);
1100    /// ```
1101    #[must_use]
1102    pub fn is_full_resource(
1103        &self,
1104        format: TextureFormat,
1105        mip_levels: u32,
1106        array_layers: u32,
1107    ) -> bool {
1108        // Mip level count and array layer count need to deal with both the None and Some(count) case.
1109        let mip_level_count = self.mip_level_count.unwrap_or(mip_levels);
1110        let array_layer_count = self.array_layer_count.unwrap_or(array_layers);
1111
1112        let aspect_eq = Some(format) == format.aspect_specific_format(self.aspect);
1113
1114        let base_mip_level_eq = self.base_mip_level == 0;
1115        let mip_level_count_eq = mip_level_count == mip_levels;
1116
1117        let base_array_layer_eq = self.base_array_layer == 0;
1118        let array_layer_count_eq = array_layer_count == array_layers;
1119
1120        aspect_eq
1121            && base_mip_level_eq
1122            && mip_level_count_eq
1123            && base_array_layer_eq
1124            && array_layer_count_eq
1125    }
1126
1127    /// Returns the mip level range of a subresource range describes for a specific texture.
1128    #[must_use]
1129    pub fn mip_range(&self, mip_level_count: u32) -> Range<u32> {
1130        self.base_mip_level..match self.mip_level_count {
1131            Some(mip_level_count) => self.base_mip_level.saturating_add(mip_level_count),
1132            None => mip_level_count,
1133        }
1134    }
1135
1136    /// Returns the layer range of a subresource range describes for a specific texture.
1137    #[must_use]
1138    pub fn layer_range(&self, array_layer_count: u32) -> Range<u32> {
1139        self.base_array_layer..match self.array_layer_count {
1140            Some(array_layer_count) => self.base_array_layer.saturating_add(array_layer_count),
1141            None => array_layer_count,
1142        }
1143    }
1144}
1145
1146#[cfg(test)]
1147mod tests {
1148    use super::*;
1149    use crate::Extent3d;
1150
1151    #[test]
1152    fn test_physical_size() {
1153        let format = TextureFormat::Bc1RgbaUnormSrgb; // 4x4 blocks
1154        assert_eq!(
1155            Extent3d {
1156                width: 7,
1157                height: 7,
1158                depth_or_array_layers: 1
1159            }
1160            .physical_size(format),
1161            Extent3d {
1162                width: 8,
1163                height: 8,
1164                depth_or_array_layers: 1
1165            }
1166        );
1167        // Doesn't change, already aligned
1168        assert_eq!(
1169            Extent3d {
1170                width: 8,
1171                height: 8,
1172                depth_or_array_layers: 1
1173            }
1174            .physical_size(format),
1175            Extent3d {
1176                width: 8,
1177                height: 8,
1178                depth_or_array_layers: 1
1179            }
1180        );
1181        let format = TextureFormat::Astc {
1182            block: AstcBlock::B8x5,
1183            channel: AstcChannel::Unorm,
1184        }; // 8x5 blocks
1185        assert_eq!(
1186            Extent3d {
1187                width: 7,
1188                height: 7,
1189                depth_or_array_layers: 1
1190            }
1191            .physical_size(format),
1192            Extent3d {
1193                width: 8,
1194                height: 10,
1195                depth_or_array_layers: 1
1196            }
1197        );
1198    }
1199
1200    #[test]
1201    fn test_max_mips() {
1202        // 1D
1203        assert_eq!(
1204            Extent3d {
1205                width: 240,
1206                height: 1,
1207                depth_or_array_layers: 1
1208            }
1209            .max_mips(TextureDimension::D1),
1210            1
1211        );
1212        // 2D
1213        assert_eq!(
1214            Extent3d {
1215                width: 1,
1216                height: 1,
1217                depth_or_array_layers: 1
1218            }
1219            .max_mips(TextureDimension::D2),
1220            1
1221        );
1222        assert_eq!(
1223            Extent3d {
1224                width: 60,
1225                height: 60,
1226                depth_or_array_layers: 1
1227            }
1228            .max_mips(TextureDimension::D2),
1229            6
1230        );
1231        assert_eq!(
1232            Extent3d {
1233                width: 240,
1234                height: 1,
1235                depth_or_array_layers: 1000
1236            }
1237            .max_mips(TextureDimension::D2),
1238            8
1239        );
1240        // 3D
1241        assert_eq!(
1242            Extent3d {
1243                width: 16,
1244                height: 30,
1245                depth_or_array_layers: 60
1246            }
1247            .max_mips(TextureDimension::D3),
1248            6
1249        );
1250    }
1251}