use core::ops::Range;
use crate::{link_to_wgpu_docs, link_to_wgpu_item, Extent3d, Origin3d};
#[cfg(any(feature = "serde", test))]
use serde::{Deserialize, Serialize};
#[cfg(doc)]
use crate::{BindingType, Features};
mod external_image;
mod external_texture;
mod format;
pub use external_image::*;
pub use external_texture::*;
pub use format::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TextureDimension {
#[cfg_attr(feature = "serde", serde(rename = "1d"))]
D1,
#[cfg_attr(feature = "serde", serde(rename = "2d"))]
D2,
#[cfg_attr(feature = "serde", serde(rename = "3d"))]
D3,
}
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]
pub enum TextureDataOrder {
#[default]
LayerMajor,
MipMajor,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TextureViewDimension {
#[cfg_attr(feature = "serde", serde(rename = "1d"))]
D1,
#[cfg_attr(feature = "serde", serde(rename = "2d"))]
#[default]
D2,
#[cfg_attr(feature = "serde", serde(rename = "2d-array"))]
D2Array,
#[cfg_attr(feature = "serde", serde(rename = "cube"))]
Cube,
#[cfg_attr(feature = "serde", serde(rename = "cube-array"))]
CubeArray,
#[cfg_attr(feature = "serde", serde(rename = "3d"))]
D3,
}
impl TextureViewDimension {
#[must_use]
pub fn compatible_texture_dimension(self) -> TextureDimension {
match self {
Self::D1 => TextureDimension::D1,
Self::D2 | Self::D2Array | Self::Cube | Self::CubeArray => TextureDimension::D2,
Self::D3 => TextureDimension::D3,
}
}
}
#[doc = link_to_wgpu_item!(struct Texture)]
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum TextureAspect {
#[default]
All,
StencilOnly,
DepthOnly,
Plane0,
Plane1,
Plane2,
}
impl TextureAspect {
#[must_use]
pub fn from_plane(plane: u32) -> Option<Self> {
Some(match plane {
0 => Self::Plane0,
1 => Self::Plane1,
2 => Self::Plane2,
_ => return None,
})
}
#[must_use]
pub fn to_plane(&self) -> Option<u32> {
match self {
TextureAspect::Plane0 => Some(0),
TextureAspect::Plane1 => Some(1),
TextureAspect::Plane2 => Some(2),
_ => None,
}
}
}
bitflags::bitflags! {
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct TextureUsages: u32 {
const COPY_SRC = 1 << 0;
const COPY_DST = 1 << 1;
const TEXTURE_BINDING = 1 << 2;
const STORAGE_BINDING = 1 << 3;
const RENDER_ATTACHMENT = 1 << 4;
const STORAGE_ATOMIC = 1 << 16;
const TRANSIENT = 1 << 17;
}
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct TextureUses: u16 {
const UNINITIALIZED = 1 << 0;
const PRESENT = 1 << 1;
const COPY_SRC = 1 << 2;
const COPY_DST = 1 << 3;
const RESOURCE = 1 << 4;
const COLOR_TARGET = 1 << 5;
const DEPTH_STENCIL_READ = 1 << 6;
const DEPTH_STENCIL_WRITE = 1 << 7;
const STORAGE_READ_ONLY = 1 << 8;
const STORAGE_WRITE_ONLY = 1 << 9;
const STORAGE_READ_WRITE = 1 << 10;
const STORAGE_ATOMIC = 1 << 11;
const TRANSIENT = 1 << 12;
const INCLUSIVE = Self::COPY_SRC.bits() | Self::RESOURCE.bits() | Self::DEPTH_STENCIL_READ.bits() | Self::STORAGE_READ_ONLY.bits();
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();
const COMPLEX = 1 << 13;
const UNKNOWN = 1 << 14;
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TextureTransition<T> {
pub texture: T,
pub selector: Option<TextureSelector>,
pub state: TextureUses,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TextureSelector {
pub mips: Range<u32>,
pub layers: Range<u32>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TextureSampleType {
Float {
filterable: bool,
},
Depth,
Sint,
Uint,
}
impl Default for TextureSampleType {
fn default() -> Self {
Self::Float { filterable: true }
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum StorageTextureAccess {
WriteOnly,
ReadOnly,
ReadWrite,
Atomic,
}
#[doc = link_to_wgpu_item!(struct TextureView)]
#[doc = link_to_wgpu_docs!(["`Texture::create_view()`"]: "struct.Texture.html#method.create_view")]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct TextureViewDescriptor<L> {
pub label: L,
pub format: Option<TextureFormat>,
pub dimension: Option<TextureViewDimension>,
pub usage: Option<TextureUsages>,
pub aspect: TextureAspect,
pub base_mip_level: u32,
pub mip_level_count: Option<u32>,
pub base_array_layer: u32,
pub array_layer_count: Option<u32>,
}
#[repr(C)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TextureDescriptor<L, V> {
pub label: L,
pub size: Extent3d,
pub mip_level_count: u32,
pub sample_count: u32,
pub dimension: TextureDimension,
pub format: TextureFormat,
pub usage: TextureUsages,
pub view_formats: V,
}
impl<L, V> TextureDescriptor<L, V> {
#[must_use]
pub fn map_label<K>(&self, fun: impl FnOnce(&L) -> K) -> TextureDescriptor<K, V>
where
V: Clone,
{
TextureDescriptor {
label: fun(&self.label),
size: self.size,
mip_level_count: self.mip_level_count,
sample_count: self.sample_count,
dimension: self.dimension,
format: self.format,
usage: self.usage,
view_formats: self.view_formats.clone(),
}
}
#[must_use]
pub fn map_label_and_view_formats<K, M>(
&self,
l_fun: impl FnOnce(&L) -> K,
v_fun: impl FnOnce(V) -> M,
) -> TextureDescriptor<K, M>
where
V: Clone,
{
TextureDescriptor {
label: l_fun(&self.label),
size: self.size,
mip_level_count: self.mip_level_count,
sample_count: self.sample_count,
dimension: self.dimension,
format: self.format,
usage: self.usage,
view_formats: v_fun(self.view_formats.clone()),
}
}
#[must_use]
pub fn mip_level_size(&self, level: u32) -> Option<Extent3d> {
if level >= self.mip_level_count {
return None;
}
Some(self.size.mip_level_size(level, self.dimension))
}
#[doc(hidden)]
#[must_use]
pub fn compute_render_extent(&self, mip_level: u32, plane: Option<u32>) -> Extent3d {
let Extent3d {
width,
height,
depth_or_array_layers: _,
} = self.mip_level_size(mip_level).expect("invalid mip level");
let (w_subsampling, h_subsampling) = self.format.subsampling_factors(plane);
let width = width / w_subsampling;
let height = height / h_subsampling;
Extent3d {
width,
height,
depth_or_array_layers: 1,
}
}
#[must_use]
pub fn array_layer_count(&self) -> u32 {
match self.dimension {
TextureDimension::D1 | TextureDimension::D3 => 1,
TextureDimension::D2 => self.size.depth_or_array_layers,
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SamplerDescriptor<L> {
pub label: L,
pub address_mode_u: AddressMode,
pub address_mode_v: AddressMode,
pub address_mode_w: AddressMode,
pub mag_filter: FilterMode,
pub min_filter: FilterMode,
pub mipmap_filter: MipmapFilterMode,
pub lod_min_clamp: f32,
pub lod_max_clamp: f32,
pub compare: Option<crate::CompareFunction>,
pub anisotropy_clamp: u16,
pub border_color: Option<SamplerBorderColor>,
}
impl<L: Default> Default for SamplerDescriptor<L> {
fn default() -> Self {
Self {
label: Default::default(),
address_mode_u: Default::default(),
address_mode_v: Default::default(),
address_mode_w: Default::default(),
mag_filter: Default::default(),
min_filter: Default::default(),
mipmap_filter: Default::default(),
lod_min_clamp: 0.0,
lod_max_clamp: 32.0,
compare: None,
anisotropy_clamp: 1,
border_color: None,
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum AddressMode {
#[default]
ClampToEdge = 0,
Repeat = 1,
MirrorRepeat = 2,
ClampToBorder = 3,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum FilterMode {
#[default]
Nearest = 0,
Linear = 1,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum MipmapFilterMode {
#[default]
Nearest = 0,
Linear = 1,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SamplerBorderColor {
TransparentBlack,
OpaqueBlack,
OpaqueWhite,
Zero,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TexelCopyBufferLayout {
pub offset: crate::BufferAddress,
#[doc = link_to_wgpu_docs!(["CEcbtt"]: "struct.CommandEncoder.html#method.copy_buffer_to_texture")]
#[doc = link_to_wgpu_docs!(["CEcttb"]: "struct.CommandEncoder.html#method.copy_texture_to_buffer")]
#[doc = link_to_wgpu_docs!(["Qwt"]: "struct.Queue.html#method.write_texture")]
pub bytes_per_row: Option<u32>,
pub rows_per_image: Option<u32>,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TexelCopyBufferInfo<B> {
pub buffer: B,
pub layout: TexelCopyBufferLayout,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TexelCopyTextureInfo<T> {
pub texture: T,
pub mip_level: u32,
#[cfg_attr(feature = "serde", serde(default))]
pub origin: Origin3d,
#[cfg_attr(feature = "serde", serde(default))]
pub aspect: TextureAspect,
}
impl<T> TexelCopyTextureInfo<T> {
pub fn to_tagged(
self,
color_space: PredefinedColorSpace,
premultiplied_alpha: bool,
) -> CopyExternalImageDestInfo<T> {
CopyExternalImageDestInfo {
texture: self.texture,
mip_level: self.mip_level,
origin: self.origin,
aspect: self.aspect,
color_space,
premultiplied_alpha,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct ImageSubresourceRange {
#[doc = link_to_wgpu_docs!(["TAA"]: "enum.TextureAspect.html#variant.All")]
pub aspect: TextureAspect,
pub base_mip_level: u32,
pub mip_level_count: Option<u32>,
pub base_array_layer: u32,
pub array_layer_count: Option<u32>,
}
impl ImageSubresourceRange {
#[must_use]
pub fn is_full_resource(
&self,
format: TextureFormat,
mip_levels: u32,
array_layers: u32,
) -> bool {
let mip_level_count = self.mip_level_count.unwrap_or(mip_levels);
let array_layer_count = self.array_layer_count.unwrap_or(array_layers);
let aspect_eq = Some(format) == format.aspect_specific_format(self.aspect);
let base_mip_level_eq = self.base_mip_level == 0;
let mip_level_count_eq = mip_level_count == mip_levels;
let base_array_layer_eq = self.base_array_layer == 0;
let array_layer_count_eq = array_layer_count == array_layers;
aspect_eq
&& base_mip_level_eq
&& mip_level_count_eq
&& base_array_layer_eq
&& array_layer_count_eq
}
#[must_use]
pub fn mip_range(&self, mip_level_count: u32) -> Range<u32> {
self.base_mip_level..match self.mip_level_count {
Some(mip_level_count) => self.base_mip_level + mip_level_count,
None => mip_level_count,
}
}
#[must_use]
pub fn layer_range(&self, array_layer_count: u32) -> Range<u32> {
self.base_array_layer..match self.array_layer_count {
Some(array_layer_count) => self.base_array_layer + array_layer_count,
None => array_layer_count,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Extent3d;
#[test]
fn test_physical_size() {
let format = TextureFormat::Bc1RgbaUnormSrgb; assert_eq!(
Extent3d {
width: 7,
height: 7,
depth_or_array_layers: 1
}
.physical_size(format),
Extent3d {
width: 8,
height: 8,
depth_or_array_layers: 1
}
);
assert_eq!(
Extent3d {
width: 8,
height: 8,
depth_or_array_layers: 1
}
.physical_size(format),
Extent3d {
width: 8,
height: 8,
depth_or_array_layers: 1
}
);
let format = TextureFormat::Astc {
block: AstcBlock::B8x5,
channel: AstcChannel::Unorm,
}; assert_eq!(
Extent3d {
width: 7,
height: 7,
depth_or_array_layers: 1
}
.physical_size(format),
Extent3d {
width: 8,
height: 10,
depth_or_array_layers: 1
}
);
}
#[test]
fn test_max_mips() {
assert_eq!(
Extent3d {
width: 240,
height: 1,
depth_or_array_layers: 1
}
.max_mips(TextureDimension::D1),
1
);
assert_eq!(
Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1
}
.max_mips(TextureDimension::D2),
1
);
assert_eq!(
Extent3d {
width: 60,
height: 60,
depth_or_array_layers: 1
}
.max_mips(TextureDimension::D2),
6
);
assert_eq!(
Extent3d {
width: 240,
height: 1,
depth_or_array_layers: 1000
}
.max_mips(TextureDimension::D2),
8
);
assert_eq!(
Extent3d {
width: 16,
height: 30,
depth_or_array_layers: 60
}
.max_mips(TextureDimension::D3),
6
);
}
}