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: u16 {
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 stencil usage.
235 const DEPTH_STENCIL_READ = 1 << 6;
236 /// Read-write depth stencil usage
237 const DEPTH_STENCIL_WRITE = 1 << 7;
238 /// Read-only storage texture usage. Corresponds to a UAV in d3d, so is exclusive, despite being read only.
239 /// cbindgen:ignore
240 const STORAGE_READ_ONLY = 1 << 8;
241 /// Write-only storage texture usage.
242 /// cbindgen:ignore
243 const STORAGE_WRITE_ONLY = 1 << 9;
244 /// Read-write storage texture usage.
245 /// cbindgen:ignore
246 const STORAGE_READ_WRITE = 1 << 10;
247 /// Image atomic enabled storage.
248 /// cbindgen:ignore
249 const STORAGE_ATOMIC = 1 << 11;
250 /// 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.
251 const TRANSIENT = 1 << 12;
252 /// The combination of states that a texture may be in _at the same time_.
253 /// cbindgen:ignore
254 const INCLUSIVE = Self::COPY_SRC.bits() | Self::RESOURCE.bits() | Self::DEPTH_STENCIL_READ.bits() | Self::STORAGE_READ_ONLY.bits();
255 /// The combination of states that a texture must exclusively be in.
256 /// cbindgen:ignore
257 const EXCLUSIVE = Self::COPY_DST.bits() | Self::COLOR_TARGET.bits() | Self::DEPTH_STENCIL_WRITE.bits() | Self::STORAGE_WRITE_ONLY.bits() | Self::STORAGE_READ_WRITE.bits() | Self::STORAGE_ATOMIC.bits() | Self::PRESENT.bits();
258
259 /// Flag used by the wgpu-core texture tracker to say a texture is in different states for every sub-resource
260 const COMPLEX = 1 << 13;
261 /// Flag used by the wgpu-core texture tracker to say that the tracker does not know the state of the sub-resource.
262 /// This is different from UNINITIALIZED as that says the tracker does know, but the texture has not been initialized.
263 const UNKNOWN = 1 << 14;
264 }
265}
266
267/// A texture transition for use with `CommandEncoder::transition_resources`.
268#[derive(Clone, Debug)]
269#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
270pub struct TextureTransition<T> {
271 /// The texture to transition.
272 pub texture: T,
273 /// An optional selector to transition only part of the texture.
274 ///
275 /// If None, the entire texture will be transitioned.
276 pub selector: Option<TextureSelector>,
277 /// The new state to transition to.
278 pub state: TextureUses,
279}
280
281/// Specifies a particular set of subresources in a texture.
282#[derive(Clone, Debug, PartialEq, Eq)]
283#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
284pub struct TextureSelector {
285 /// Range of mips to use.
286 pub mips: Range<u32>,
287 /// Range of layers to use.
288 pub layers: Range<u32>,
289}
290
291/// Specific type of a sample in a texture binding.
292///
293/// Corresponds to [WebGPU `GPUTextureSampleType`](
294/// https://gpuweb.github.io/gpuweb/#enumdef-gputexturesampletype).
295#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
296#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
297pub enum TextureSampleType {
298 /// Sampling returns floats.
299 ///
300 /// Example WGSL syntax:
301 /// ```rust,ignore
302 /// @group(0) @binding(0)
303 /// var t: texture_2d<f32>;
304 /// ```
305 ///
306 /// Example GLSL syntax:
307 /// ```cpp,ignore
308 /// layout(binding = 0)
309 /// uniform texture2D t;
310 /// ```
311 Float {
312 /// If this is `false`, the texture can't be sampled with
313 /// a filtering sampler.
314 ///
315 /// Even if this is `true`, it's possible to sample with
316 /// a **non-filtering** sampler.
317 filterable: bool,
318 },
319 /// Sampling does the depth reference comparison.
320 ///
321 /// This is also compatible with a non-filtering sampler.
322 ///
323 /// Example WGSL syntax:
324 /// ```rust,ignore
325 /// @group(0) @binding(0)
326 /// var t: texture_depth_2d;
327 /// ```
328 ///
329 /// Example GLSL syntax:
330 /// ```cpp,ignore
331 /// layout(binding = 0)
332 /// uniform texture2DShadow t;
333 /// ```
334 Depth,
335 /// Sampling returns signed integers.
336 ///
337 /// Example WGSL syntax:
338 /// ```rust,ignore
339 /// @group(0) @binding(0)
340 /// var t: texture_2d<i32>;
341 /// ```
342 ///
343 /// Example GLSL syntax:
344 /// ```cpp,ignore
345 /// layout(binding = 0)
346 /// uniform itexture2D t;
347 /// ```
348 Sint,
349 /// Sampling returns unsigned integers.
350 ///
351 /// Example WGSL syntax:
352 /// ```rust,ignore
353 /// @group(0) @binding(0)
354 /// var t: texture_2d<u32>;
355 /// ```
356 ///
357 /// Example GLSL syntax:
358 /// ```cpp,ignore
359 /// layout(binding = 0)
360 /// uniform utexture2D t;
361 /// ```
362 Uint,
363}
364
365impl Default for TextureSampleType {
366 fn default() -> Self {
367 Self::Float { filterable: true }
368 }
369}
370
371/// Specific type of a sample in a texture binding.
372///
373/// For use in [`BindingType::StorageTexture`].
374///
375/// Corresponds to [WebGPU `GPUStorageTextureAccess`](
376/// https://gpuweb.github.io/gpuweb/#enumdef-gpustoragetextureaccess).
377#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
378#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
379#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
380pub enum StorageTextureAccess {
381 /// The texture can only be written in the shader and it:
382 /// - may or may not be annotated with `write` (WGSL).
383 /// - must be annotated with `writeonly` (GLSL).
384 ///
385 /// Example WGSL syntax:
386 /// ```rust,ignore
387 /// @group(0) @binding(0)
388 /// var my_storage_image: texture_storage_2d<r32float, write>;
389 /// ```
390 ///
391 /// Example GLSL syntax:
392 /// ```cpp,ignore
393 /// layout(set=0, binding=0, r32f) writeonly uniform image2D myStorageImage;
394 /// ```
395 WriteOnly,
396 /// The texture can only be read in the shader and it must be annotated with `read` (WGSL) or
397 /// `readonly` (GLSL).
398 ///
399 /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] must be enabled to use this access
400 /// mode. This is a native-only extension.
401 ///
402 /// Example WGSL syntax:
403 /// ```rust,ignore
404 /// @group(0) @binding(0)
405 /// var my_storage_image: texture_storage_2d<r32float, read>;
406 /// ```
407 ///
408 /// Example GLSL syntax:
409 /// ```cpp,ignore
410 /// layout(set=0, binding=0, r32f) readonly uniform image2D myStorageImage;
411 /// ```
412 ReadOnly,
413 /// The texture can be both read and written in the shader and must be annotated with
414 /// `read_write` in WGSL.
415 ///
416 /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] must be enabled to use this access
417 /// mode. This is a nonstandard, native-only extension.
418 ///
419 /// Example WGSL syntax:
420 /// ```rust,ignore
421 /// @group(0) @binding(0)
422 /// var my_storage_image: texture_storage_2d<r32float, read_write>;
423 /// ```
424 ///
425 /// Example GLSL syntax:
426 /// ```cpp,ignore
427 /// layout(set=0, binding=0, r32f) uniform image2D myStorageImage;
428 /// ```
429 ReadWrite,
430 /// The texture can be both read and written in the shader via atomics and must be annotated
431 /// with `read_write` in WGSL.
432 ///
433 /// [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] must be enabled to use this access
434 /// mode. This is a nonstandard, native-only extension.
435 ///
436 /// Example WGSL syntax:
437 /// ```rust,ignore
438 /// @group(0) @binding(0)
439 /// var my_storage_image: texture_storage_2d<r32uint, atomic>;
440 /// ```
441 Atomic,
442}
443
444/// Describes a [`TextureView`].
445///
446/// For use with [`Texture::create_view()`].
447///
448/// Corresponds to [WebGPU `GPUTextureViewDescriptor`](
449/// https://gpuweb.github.io/gpuweb/#dictdef-gputextureviewdescriptor).
450///
451#[doc = link_to_wgpu_item!(struct TextureView)]
452#[doc = link_to_wgpu_docs!(["`Texture::create_view()`"]: "struct.Texture.html#method.create_view")]
453#[derive(Clone, Debug, Default, Eq, PartialEq)]
454#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
455pub struct TextureViewDescriptor<L> {
456 /// Debug label of the texture view. This will show up in graphics debuggers for easy identification.
457 pub label: L,
458 /// Format of the texture view. Either must be the same as the texture format or in the list
459 /// of `view_formats` in the texture's descriptor.
460 pub format: Option<TextureFormat>,
461 /// The dimension of the texture view. For 1D textures, this must be `D1`. For 2D textures it must be one of
462 /// `D2`, `D2Array`, `Cube`, and `CubeArray`. For 3D textures it must be `D3`
463 pub dimension: Option<TextureViewDimension>,
464 /// The allowed usage(s) for the texture view. Must be a subset of the usage flags of the texture.
465 /// If not provided, defaults to the full set of usage flags of the texture.
466 pub usage: Option<TextureUsages>,
467 /// Aspect of the texture. Color textures must be [`TextureAspect::All`].
468 pub aspect: TextureAspect,
469 /// Base mip level.
470 pub base_mip_level: u32,
471 /// Mip level count.
472 /// If `Some(count)`, `base_mip_level + count` must be less or equal to underlying texture mip count.
473 /// If `None`, considered to include the rest of the mipmap levels, but at least 1 in total.
474 pub mip_level_count: Option<u32>,
475 /// Base array layer.
476 pub base_array_layer: u32,
477 /// Layer count.
478 /// If `Some(count)`, `base_array_layer + count` must be less or equal to the underlying array count.
479 /// If `None`, considered to include the rest of the array layers, but at least 1 in total.
480 pub array_layer_count: Option<u32>,
481}
482
483impl<L> TextureViewDescriptor<L> {
484 /// Takes a closure and maps the label of the texture view descriptor into another.
485 #[must_use]
486 pub fn map_label<'a, K>(&'a self, fun: impl FnOnce(&'a L) -> K) -> TextureViewDescriptor<K> {
487 TextureViewDescriptor {
488 label: fun(&self.label),
489 format: self.format,
490 dimension: self.dimension,
491 usage: self.usage,
492 aspect: self.aspect,
493 base_mip_level: self.base_mip_level,
494 mip_level_count: self.mip_level_count,
495 base_array_layer: self.base_array_layer,
496 array_layer_count: self.array_layer_count,
497 }
498 }
499}
500
501/// Describes a [`Texture`](../wgpu/struct.Texture.html).
502///
503/// Corresponds to [WebGPU `GPUTextureDescriptor`](
504/// https://gpuweb.github.io/gpuweb/#dictdef-gputexturedescriptor).
505#[repr(C)]
506#[derive(Clone, Debug, PartialEq, Eq, Hash)]
507#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
508pub struct TextureDescriptor<L, V> {
509 /// Debug label of the texture. This will show up in graphics debuggers for easy identification.
510 pub label: L,
511 /// Size of the texture. All components must be greater than zero. For a
512 /// regular 1D/2D texture, the unused sizes will be 1. For 2DArray textures,
513 /// Z is the number of 2D textures in that array.
514 pub size: Extent3d,
515 /// Mip count of texture. For a texture with no extra mips, this must be 1.
516 pub mip_level_count: u32,
517 /// Sample count of texture. If this is not 1, texture must have [`BindingType::Texture::multisampled`] set to true.
518 pub sample_count: u32,
519 /// Dimensions of the texture.
520 pub dimension: TextureDimension,
521 /// Format of the texture.
522 pub format: TextureFormat,
523 /// Allowed usages of the texture. If used in other ways, the operation will panic.
524 pub usage: TextureUsages,
525 /// Specifies what view formats will be allowed when calling `Texture::create_view` on this texture.
526 ///
527 /// View formats of the same format as the texture are always allowed.
528 ///
529 /// Note: currently, only the srgb-ness is allowed to change. (ex: `Rgba8Unorm` texture + `Rgba8UnormSrgb` view)
530 pub view_formats: V,
531}
532
533impl<L, V> TextureDescriptor<L, V> {
534 /// Takes a closure and maps the label of the texture descriptor into another.
535 #[must_use]
536 pub fn map_label<'a, K>(&'a self, fun: impl FnOnce(&'a L) -> K) -> TextureDescriptor<K, V>
537 where
538 V: Clone,
539 {
540 TextureDescriptor {
541 label: fun(&self.label),
542 size: self.size,
543 mip_level_count: self.mip_level_count,
544 sample_count: self.sample_count,
545 dimension: self.dimension,
546 format: self.format,
547 usage: self.usage,
548 view_formats: self.view_formats.clone(),
549 }
550 }
551
552 /// Maps the label and view formats of the texture descriptor into another.
553 #[must_use]
554 pub fn map_label_and_view_formats<'a, K, M>(
555 &'a self,
556 l_fun: impl FnOnce(&'a L) -> K,
557 v_fun: impl FnOnce(&'a V) -> M,
558 ) -> TextureDescriptor<K, M> {
559 TextureDescriptor {
560 label: l_fun(&self.label),
561 size: self.size,
562 mip_level_count: self.mip_level_count,
563 sample_count: self.sample_count,
564 dimension: self.dimension,
565 format: self.format,
566 usage: self.usage,
567 view_formats: v_fun(&self.view_formats),
568 }
569 }
570
571 /// Calculates the extent at a given mip level.
572 ///
573 /// If the given mip level is larger than possible, returns None.
574 ///
575 /// Treats the depth as part of the mipmaps. If calculating
576 /// for a 2DArray texture, which does not mipmap depth, set depth to 1.
577 ///
578 /// ```rust
579 /// # use wgpu_types as wgpu;
580 /// # type TextureDescriptor<'a> = wgpu::TextureDescriptor<(), &'a [wgpu::TextureFormat]>;
581 /// let desc = TextureDescriptor {
582 /// label: (),
583 /// size: wgpu::Extent3d { width: 100, height: 60, depth_or_array_layers: 1 },
584 /// mip_level_count: 7,
585 /// sample_count: 1,
586 /// dimension: wgpu::TextureDimension::D3,
587 /// format: wgpu::TextureFormat::Rgba8Sint,
588 /// usage: wgpu::TextureUsages::empty(),
589 /// view_formats: &[],
590 /// };
591 ///
592 /// assert_eq!(desc.mip_level_size(0), Some(wgpu::Extent3d { width: 100, height: 60, depth_or_array_layers: 1 }));
593 /// assert_eq!(desc.mip_level_size(1), Some(wgpu::Extent3d { width: 50, height: 30, depth_or_array_layers: 1 }));
594 /// assert_eq!(desc.mip_level_size(2), Some(wgpu::Extent3d { width: 25, height: 15, depth_or_array_layers: 1 }));
595 /// assert_eq!(desc.mip_level_size(3), Some(wgpu::Extent3d { width: 12, height: 7, depth_or_array_layers: 1 }));
596 /// assert_eq!(desc.mip_level_size(4), Some(wgpu::Extent3d { width: 6, height: 3, depth_or_array_layers: 1 }));
597 /// assert_eq!(desc.mip_level_size(5), Some(wgpu::Extent3d { width: 3, height: 1, depth_or_array_layers: 1 }));
598 /// assert_eq!(desc.mip_level_size(6), Some(wgpu::Extent3d { width: 1, height: 1, depth_or_array_layers: 1 }));
599 /// assert_eq!(desc.mip_level_size(7), None);
600 /// ```
601 #[must_use]
602 pub fn mip_level_size(&self, level: u32) -> Option<Extent3d> {
603 if level >= self.mip_level_count {
604 return None;
605 }
606
607 Some(self.size.mip_level_size(level, self.dimension))
608 }
609
610 /// Computes the render extent of this texture.
611 ///
612 /// This is a low-level helper exported for use by wgpu-core.
613 ///
614 /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-compute-render-extent>
615 ///
616 /// # Panics
617 ///
618 /// If the mip level is out of range.
619 #[doc(hidden)]
620 #[must_use]
621 pub fn compute_render_extent(&self, mip_level: u32, plane: Option<u32>) -> Extent3d {
622 let Extent3d {
623 width,
624 height,
625 depth_or_array_layers: _,
626 } = self.mip_level_size(mip_level).expect("invalid mip level");
627
628 let (w_subsampling, h_subsampling) = self.format.subsampling_factors(plane);
629
630 let width = width / w_subsampling;
631 let height = height / h_subsampling;
632
633 Extent3d {
634 width,
635 height,
636 depth_or_array_layers: 1,
637 }
638 }
639
640 /// Returns the number of array layers.
641 ///
642 /// <https://gpuweb.github.io/gpuweb/#abstract-opdef-array-layer-count>
643 #[must_use]
644 pub fn array_layer_count(&self) -> u32 {
645 match self.dimension {
646 TextureDimension::D1 | TextureDimension::D3 => 1,
647 TextureDimension::D2 => self.size.depth_or_array_layers,
648 }
649 }
650}
651
652/// Describes a `Sampler`.
653///
654/// For use with `Device::create_sampler`.
655///
656/// Corresponds to [WebGPU `GPUSamplerDescriptor`](
657/// https://gpuweb.github.io/gpuweb/#dictdef-gpusamplerdescriptor).
658#[derive(Clone, Debug, PartialEq)]
659#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
660pub struct SamplerDescriptor<L> {
661 /// Debug label of the sampler. This will show up in graphics debuggers for easy identification.
662 pub label: L,
663 /// How to deal with out of bounds accesses in the u (i.e. x) direction
664 pub address_mode_u: AddressMode,
665 /// How to deal with out of bounds accesses in the v (i.e. y) direction
666 pub address_mode_v: AddressMode,
667 /// How to deal with out of bounds accesses in the w (i.e. z) direction
668 pub address_mode_w: AddressMode,
669 /// How to filter the texture when it needs to be magnified (made larger)
670 pub mag_filter: FilterMode,
671 /// How to filter the texture when it needs to be minified (made smaller)
672 pub min_filter: FilterMode,
673 /// How to filter between mip map levels
674 pub mipmap_filter: MipmapFilterMode,
675 /// Minimum level of detail (i.e. mip level) to use
676 pub lod_min_clamp: f32,
677 /// Maximum level of detail (i.e. mip level) to use
678 pub lod_max_clamp: f32,
679 /// If this is enabled, this is a comparison sampler using the given comparison function.
680 pub compare: Option<crate::CompareFunction>,
681 /// Must be at least 1. If this is not 1, all filter modes must be linear.
682 pub anisotropy_clamp: u16,
683 /// Border color to use when `address_mode` is [`AddressMode::ClampToBorder`]
684 pub border_color: Option<SamplerBorderColor>,
685}
686
687impl<L: Default> Default for SamplerDescriptor<L> {
688 fn default() -> Self {
689 Self {
690 label: Default::default(),
691 address_mode_u: Default::default(),
692 address_mode_v: Default::default(),
693 address_mode_w: Default::default(),
694 mag_filter: Default::default(),
695 min_filter: Default::default(),
696 mipmap_filter: Default::default(),
697 lod_min_clamp: 0.0,
698 lod_max_clamp: 32.0,
699 compare: None,
700 anisotropy_clamp: 1,
701 border_color: None,
702 }
703 }
704}
705
706impl<L> SamplerDescriptor<L> {
707 /// Takes a closure and maps the label of the sampler descriptor into another.
708 #[must_use]
709 pub fn map_label<'a, K>(&'a self, fun: impl FnOnce(&'a L) -> K) -> SamplerDescriptor<K> {
710 SamplerDescriptor {
711 label: fun(&self.label),
712 address_mode_u: self.address_mode_u,
713 address_mode_v: self.address_mode_v,
714 address_mode_w: self.address_mode_w,
715 mag_filter: self.mag_filter,
716 min_filter: self.min_filter,
717 mipmap_filter: self.mipmap_filter,
718 lod_min_clamp: self.lod_min_clamp,
719 lod_max_clamp: self.lod_max_clamp,
720 compare: self.compare,
721 anisotropy_clamp: self.anisotropy_clamp,
722 border_color: self.border_color,
723 }
724 }
725}
726
727/// How edges should be handled in texture addressing.
728///
729/// Corresponds to [WebGPU `GPUAddressMode`](
730/// https://gpuweb.github.io/gpuweb/#enumdef-gpuaddressmode).
731#[repr(C)]
732#[derive(Copy, Clone, Debug, ConstDefault!, Hash, Eq, PartialEq)]
733#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
734#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
735pub enum AddressMode {
736 /// Clamp the value to the edge of the texture
737 ///
738 /// -0.25 -> 0.0
739 /// 1.25 -> 1.0
740 #[custom(default)]
741 ClampToEdge = 0,
742 /// Repeat the texture in a tiling fashion
743 ///
744 /// -0.25 -> 0.75
745 /// 1.25 -> 0.25
746 Repeat = 1,
747 /// Repeat the texture, mirroring it every repeat
748 ///
749 /// -0.25 -> 0.25
750 /// 1.25 -> 0.75
751 MirrorRepeat = 2,
752 /// Clamp the value to the border of the texture
753 /// Requires feature [`Features::ADDRESS_MODE_CLAMP_TO_BORDER`]
754 ///
755 /// -0.25 -> border
756 /// 1.25 -> border
757 ClampToBorder = 3,
758}
759
760/// Texel mixing mode when sampling between texels.
761///
762/// Corresponds to [WebGPU `GPUFilterMode`](
763/// https://gpuweb.github.io/gpuweb/#enumdef-gpufiltermode).
764#[repr(C)]
765#[derive(Copy, Clone, Debug, ConstDefault!, Hash, Eq, PartialEq)]
766#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
767#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
768pub enum FilterMode {
769 /// Nearest neighbor sampling.
770 ///
771 /// This creates a pixelated effect.
772 #[custom(default)]
773 Nearest = 0,
774 /// Linear Interpolation
775 ///
776 /// This makes textures smooth but blurry.
777 Linear = 1,
778}
779
780/// Texel mixing mode when sampling between texels.
781///
782/// Corresponds to [WebGPU `GPUMipmapFilterMode`](
783/// https://gpuweb.github.io/gpuweb/#enumdef-gpumipmapfiltermode).
784#[repr(C)]
785#[derive(Copy, Clone, Debug, ConstDefault!, Hash, Eq, PartialEq)]
786#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
787#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
788pub enum MipmapFilterMode {
789 /// Nearest neighbor sampling.
790 ///
791 /// Return the value of the texel nearest to the texture coordinates.
792 #[custom(default)]
793 Nearest = 0,
794 /// Linear Interpolation
795 ///
796 /// Select two texels in each dimension and return a linear interpolation between their values.
797 Linear = 1,
798}
799
800/// Color variation to use when sampler addressing mode is [`AddressMode::ClampToBorder`]
801#[repr(C)]
802#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
803#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
804pub enum SamplerBorderColor {
805 /// [0, 0, 0, 0]
806 TransparentBlack,
807 /// [0, 0, 0, 1]
808 OpaqueBlack,
809 /// [1, 1, 1, 1]
810 OpaqueWhite,
811
812 /// On the Metal backend, this is equivalent to `TransparentBlack` for
813 /// textures that have an alpha component, and equivalent to `OpaqueBlack`
814 /// for textures that do not have an alpha component. On other backends,
815 /// this is equivalent to `TransparentBlack`. Requires
816 /// [`Features::ADDRESS_MODE_CLAMP_TO_ZERO`]. Not supported on the web.
817 Zero,
818}
819
820/// Layout of a texture in a buffer's memory.
821///
822/// The bytes per row and rows per image can be hard to figure out so here are some examples:
823///
824/// | Resolution | Format | Bytes per block | Pixels per block | Bytes per row | Rows per image |
825/// |------------|--------|-----------------|------------------|----------------------------------------|------------------------------|
826/// | 256x256 | RGBA8 | 4 | 1 * 1 * 1 | 256 * 4 = Some(1024) | None |
827/// | 32x16x8 | RGBA8 | 4 | 1 * 1 * 1 | 32 * 4 = 128 padded to 256 = Some(256) | None |
828/// | 256x256 | BC3 | 16 | 4 * 4 * 1 | 16 * (256 / 4) = 1024 = Some(1024) | None |
829/// | 64x64x8 | BC3 | 16 | 4 * 4 * 1 | 16 * (64 / 4) = 256 = Some(256) | 64 / 4 = 16 = Some(16) |
830///
831/// Corresponds to [WebGPU `GPUTexelCopyBufferLayout`](
832/// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagedatalayout).
833#[repr(C)]
834#[derive(Clone, Copy, Debug, ConstDefault!)]
835#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
836pub struct TexelCopyBufferLayout {
837 /// Offset into the buffer that is the start of the texture. Must be a multiple of texture block size.
838 /// For non-compressed textures, this is 1.
839 pub offset: crate::BufferAddress,
840 /// Bytes per "row" in an image.
841 ///
842 /// A row is one row of pixels or of compressed blocks in the x direction.
843 ///
844 /// 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)
845 ///
846 /// Must be a multiple of 256 for [`CommandEncoder::copy_buffer_to_texture`][CEcbtt]
847 /// and [`CommandEncoder::copy_texture_to_buffer`][CEcttb]. You must manually pad the
848 /// buffer as if the image width is a multiple of 256. An image of size (500, 500) can be
849 /// written to a buffer of size (512, 500) with `bytes_per_row` of 512,
850 ///
851 /// [`Queue::write_texture`][Qwt] does not have this requirement.
852 ///
853 /// Must be a multiple of the texture block size. For non-compressed textures, this is 1.
854 ///
855 #[doc = link_to_wgpu_docs!(["CEcbtt"]: "struct.CommandEncoder.html#method.copy_buffer_to_texture")]
856 #[doc = link_to_wgpu_docs!(["CEcttb"]: "struct.CommandEncoder.html#method.copy_texture_to_buffer")]
857 #[doc = link_to_wgpu_docs!(["Qwt"]: "struct.Queue.html#method.write_texture")]
858 pub bytes_per_row: Option<u32>,
859 /// "Rows" that make up a single "image".
860 ///
861 /// A row is one row of pixels or of compressed blocks in the x direction.
862 ///
863 /// An image is one layer in the z direction of a 3D image or 2DArray texture.
864 ///
865 /// The amount of rows per image may be larger than the actual amount of rows of data.
866 ///
867 /// Required if there are multiple images (i.e. the depth is more than one).
868 pub rows_per_image: Option<u32>,
869}
870
871/// View of a buffer which can be used to copy to/from a texture.
872///
873/// Corresponds to [WebGPU `GPUTexelCopyBufferInfo`](
874/// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopybuffer).
875#[repr(C)]
876#[derive(Copy, Clone, Debug)]
877#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
878pub struct TexelCopyBufferInfo<B> {
879 /// The buffer to be copied to/from.
880 pub buffer: B,
881 /// The layout of the texture data in this buffer.
882 pub layout: TexelCopyBufferLayout,
883}
884
885/// View of a texture which can be used to copy to/from a buffer/texture.
886///
887/// Corresponds to [WebGPU `GPUTexelCopyTextureInfo`](
888/// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopytexture).
889#[repr(C)]
890#[derive(Copy, Clone, Debug)]
891#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
892pub struct TexelCopyTextureInfo<T> {
893 /// The texture to be copied to/from.
894 pub texture: T,
895 /// The target mip level of the texture.
896 pub mip_level: u32,
897 /// The base texel of the texture in the selected `mip_level`. Together
898 /// with the `copy_size` argument to copy functions, defines the
899 /// sub-region of the texture to copy.
900 #[cfg_attr(feature = "serde", serde(default))]
901 pub origin: Origin3d,
902 /// The copy aspect.
903 #[cfg_attr(feature = "serde", serde(default))]
904 pub aspect: TextureAspect,
905}
906
907impl<T> TexelCopyTextureInfo<T> {
908 /// Adds color space and premultiplied alpha information to make this
909 /// descriptor tagged.
910 pub fn to_tagged(
911 self,
912 color_space: PredefinedColorSpace,
913 premultiplied_alpha: bool,
914 ) -> CopyExternalImageDestInfo<T> {
915 CopyExternalImageDestInfo {
916 texture: self.texture,
917 mip_level: self.mip_level,
918 origin: self.origin,
919 aspect: self.aspect,
920 color_space,
921 premultiplied_alpha,
922 }
923 }
924}
925
926/// Subresource range within an image
927#[repr(C)]
928#[derive(Clone, Copy, Debug, ConstDefault!, Eq, PartialEq)]
929#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
930#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
931pub struct ImageSubresourceRange {
932 /// Aspect of the texture. Color textures must be [`TextureAspect::All`][TAA].
933 ///
934 #[doc = link_to_wgpu_docs!(["TAA"]: "enum.TextureAspect.html#variant.All")]
935 pub aspect: TextureAspect,
936 /// Base mip level.
937 pub base_mip_level: u32,
938 /// Mip level count.
939 /// If `Some(count)`, `base_mip_level + count` must be less or equal to underlying texture mip count.
940 /// If `None`, considered to include the rest of the mipmap levels, but at least 1 in total.
941 pub mip_level_count: Option<u32>,
942 /// Base array layer.
943 pub base_array_layer: u32,
944 /// Layer count.
945 /// If `Some(count)`, `base_array_layer + count` must be less or equal to the underlying array count.
946 /// If `None`, considered to include the rest of the array layers, but at least 1 in total.
947 pub array_layer_count: Option<u32>,
948}
949
950impl ImageSubresourceRange {
951 /// Returns if the given range represents a full resource, with a texture of the given
952 /// layer count and mip count.
953 ///
954 /// ```rust
955 /// # use wgpu_types as wgpu;
956 ///
957 /// let range_none = wgpu::ImageSubresourceRange {
958 /// aspect: wgpu::TextureAspect::All,
959 /// base_mip_level: 0,
960 /// mip_level_count: None,
961 /// base_array_layer: 0,
962 /// array_layer_count: None,
963 /// };
964 /// assert_eq!(range_none.is_full_resource(wgpu::TextureFormat::Stencil8, 5, 10), true);
965 ///
966 /// let range_some = wgpu::ImageSubresourceRange {
967 /// aspect: wgpu::TextureAspect::All,
968 /// base_mip_level: 0,
969 /// mip_level_count: Some(5),
970 /// base_array_layer: 0,
971 /// array_layer_count: Some(10),
972 /// };
973 /// assert_eq!(range_some.is_full_resource(wgpu::TextureFormat::Stencil8, 5, 10), true);
974 ///
975 /// let range_mixed = wgpu::ImageSubresourceRange {
976 /// aspect: wgpu::TextureAspect::StencilOnly,
977 /// base_mip_level: 0,
978 /// // Only partial resource
979 /// mip_level_count: Some(3),
980 /// base_array_layer: 0,
981 /// array_layer_count: None,
982 /// };
983 /// assert_eq!(range_mixed.is_full_resource(wgpu::TextureFormat::Stencil8, 5, 10), false);
984 /// ```
985 #[must_use]
986 pub fn is_full_resource(
987 &self,
988 format: TextureFormat,
989 mip_levels: u32,
990 array_layers: u32,
991 ) -> bool {
992 // Mip level count and array layer count need to deal with both the None and Some(count) case.
993 let mip_level_count = self.mip_level_count.unwrap_or(mip_levels);
994 let array_layer_count = self.array_layer_count.unwrap_or(array_layers);
995
996 let aspect_eq = Some(format) == format.aspect_specific_format(self.aspect);
997
998 let base_mip_level_eq = self.base_mip_level == 0;
999 let mip_level_count_eq = mip_level_count == mip_levels;
1000
1001 let base_array_layer_eq = self.base_array_layer == 0;
1002 let array_layer_count_eq = array_layer_count == array_layers;
1003
1004 aspect_eq
1005 && base_mip_level_eq
1006 && mip_level_count_eq
1007 && base_array_layer_eq
1008 && array_layer_count_eq
1009 }
1010
1011 /// Returns the mip level range of a subresource range describes for a specific texture.
1012 #[must_use]
1013 pub fn mip_range(&self, mip_level_count: u32) -> Range<u32> {
1014 self.base_mip_level..match self.mip_level_count {
1015 Some(mip_level_count) => self.base_mip_level.saturating_add(mip_level_count),
1016 None => mip_level_count,
1017 }
1018 }
1019
1020 /// Returns the layer range of a subresource range describes for a specific texture.
1021 #[must_use]
1022 pub fn layer_range(&self, array_layer_count: u32) -> Range<u32> {
1023 self.base_array_layer..match self.array_layer_count {
1024 Some(array_layer_count) => self.base_array_layer.saturating_add(array_layer_count),
1025 None => array_layer_count,
1026 }
1027 }
1028}
1029
1030#[cfg(test)]
1031mod tests {
1032 use super::*;
1033 use crate::Extent3d;
1034
1035 #[test]
1036 fn test_physical_size() {
1037 let format = TextureFormat::Bc1RgbaUnormSrgb; // 4x4 blocks
1038 assert_eq!(
1039 Extent3d {
1040 width: 7,
1041 height: 7,
1042 depth_or_array_layers: 1
1043 }
1044 .physical_size(format),
1045 Extent3d {
1046 width: 8,
1047 height: 8,
1048 depth_or_array_layers: 1
1049 }
1050 );
1051 // Doesn't change, already aligned
1052 assert_eq!(
1053 Extent3d {
1054 width: 8,
1055 height: 8,
1056 depth_or_array_layers: 1
1057 }
1058 .physical_size(format),
1059 Extent3d {
1060 width: 8,
1061 height: 8,
1062 depth_or_array_layers: 1
1063 }
1064 );
1065 let format = TextureFormat::Astc {
1066 block: AstcBlock::B8x5,
1067 channel: AstcChannel::Unorm,
1068 }; // 8x5 blocks
1069 assert_eq!(
1070 Extent3d {
1071 width: 7,
1072 height: 7,
1073 depth_or_array_layers: 1
1074 }
1075 .physical_size(format),
1076 Extent3d {
1077 width: 8,
1078 height: 10,
1079 depth_or_array_layers: 1
1080 }
1081 );
1082 }
1083
1084 #[test]
1085 fn test_max_mips() {
1086 // 1D
1087 assert_eq!(
1088 Extent3d {
1089 width: 240,
1090 height: 1,
1091 depth_or_array_layers: 1
1092 }
1093 .max_mips(TextureDimension::D1),
1094 1
1095 );
1096 // 2D
1097 assert_eq!(
1098 Extent3d {
1099 width: 1,
1100 height: 1,
1101 depth_or_array_layers: 1
1102 }
1103 .max_mips(TextureDimension::D2),
1104 1
1105 );
1106 assert_eq!(
1107 Extent3d {
1108 width: 60,
1109 height: 60,
1110 depth_or_array_layers: 1
1111 }
1112 .max_mips(TextureDimension::D2),
1113 6
1114 );
1115 assert_eq!(
1116 Extent3d {
1117 width: 240,
1118 height: 1,
1119 depth_or_array_layers: 1000
1120 }
1121 .max_mips(TextureDimension::D2),
1122 8
1123 );
1124 // 3D
1125 assert_eq!(
1126 Extent3d {
1127 width: 16,
1128 height: 30,
1129 depth_or_array_layers: 60
1130 }
1131 .max_mips(TextureDimension::D3),
1132 6
1133 );
1134 }
1135}