use std::hash::Hash;
use uuid::Uuid;
use crate::{context::Context, RenderTexture};
#[derive(Clone, Debug)]
pub struct Texture {
id: Uuid,
texture: wgpu::Texture,
base_mip_level: u32,
mip_level_count: u32,
sample_count: u32,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct TextureBinding {
pub(crate) texture: Texture,
pub(crate) binding_type: wgpu::BindingType,
}
impl Texture {
pub fn new(desc: &wgpu::TextureDescriptor, context: &Context) -> Self {
let texture = context.device().create_texture(desc);
Self {
id: Uuid::new_v4(),
texture,
base_mip_level: 0,
mip_level_count: desc.mip_level_count,
sample_count: desc.sample_count,
}
}
pub fn with_data(
desc: &wgpu::TextureDescriptor,
data: &[u8],
bytes_per_row: Option<u32>,
context: &Context,
) -> Self {
let texture = context.device().create_texture(desc);
context.queue().write_texture(
texture.as_image_copy(),
data,
wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row,
rows_per_image: None,
},
desc.size,
);
Self {
id: Uuid::new_v4(),
texture,
base_mip_level: 0,
mip_level_count: desc.mip_level_count,
sample_count: desc.sample_count,
}
}
pub fn size(&self) -> wgpu::Extent3d {
self.texture.size()
}
pub fn dimension(&self) -> wgpu::TextureDimension {
self.texture.dimension()
}
fn sample_type(&self) -> wgpu::TextureSampleType {
match self.texture.format() {
wgpu::TextureFormat::R8Unorm
| wgpu::TextureFormat::R8Snorm
| wgpu::TextureFormat::Rg8Unorm
| wgpu::TextureFormat::Rg8Snorm
| wgpu::TextureFormat::Rgba8Unorm
| wgpu::TextureFormat::Rgba8Snorm
| wgpu::TextureFormat::Rgba8UnormSrgb
| wgpu::TextureFormat::Bgra8Unorm
| wgpu::TextureFormat::Bgra8UnormSrgb
| wgpu::TextureFormat::R16Float
| wgpu::TextureFormat::Rgba16Float
| wgpu::TextureFormat::Rgb10a2Unorm => {
wgpu::TextureSampleType::Float { filterable: true }
}
wgpu::TextureFormat::R8Uint
| wgpu::TextureFormat::Rg8Uint
| wgpu::TextureFormat::Rgba8Uint
| wgpu::TextureFormat::R16Uint
| wgpu::TextureFormat::Rg16Uint
| wgpu::TextureFormat::Rgba16Uint
| wgpu::TextureFormat::R32Uint
| wgpu::TextureFormat::Rg32Uint
| wgpu::TextureFormat::Rgba32Uint => wgpu::TextureSampleType::Uint,
wgpu::TextureFormat::R8Sint
| wgpu::TextureFormat::Rg8Sint
| wgpu::TextureFormat::Rgba8Sint
| wgpu::TextureFormat::R16Sint
| wgpu::TextureFormat::Rg16Sint
| wgpu::TextureFormat::Rgba16Sint
| wgpu::TextureFormat::R32Sint
| wgpu::TextureFormat::Rg32Sint
| wgpu::TextureFormat::Rgba32Sint => wgpu::TextureSampleType::Sint,
_ => wgpu::TextureSampleType::Float { filterable: false },
}
}
pub fn view(&self, base_mip_level: u32, mip_level_count: u32) -> Texture {
Self {
id: self.id.clone(),
texture: self.texture.clone(),
base_mip_level,
mip_level_count,
sample_count: self.sample_count,
}
}
pub fn as_render_texture(&self, context: &Context) -> RenderTexture {
RenderTexture {
view: self.get_or_build(context),
format: self.texture.format(),
}
}
#[must_use]
pub fn texture_binding(&self) -> TextureBinding {
let view_dimension = match self.texture.dimension() {
wgpu::TextureDimension::D1 => wgpu::TextureViewDimension::D1,
wgpu::TextureDimension::D2 => wgpu::TextureViewDimension::D2,
wgpu::TextureDimension::D3 => wgpu::TextureViewDimension::D3,
};
TextureBinding {
texture: self.clone(),
binding_type: wgpu::BindingType::Texture {
sample_type: self.sample_type(),
view_dimension,
multisampled: self.sample_count > 1,
},
}
}
#[must_use]
pub fn storage_binding(&self) -> TextureBinding {
let view_dimension = match self.texture.dimension() {
wgpu::TextureDimension::D1 => wgpu::TextureViewDimension::D1,
wgpu::TextureDimension::D2 => wgpu::TextureViewDimension::D2,
wgpu::TextureDimension::D3 => wgpu::TextureViewDimension::D3,
};
TextureBinding {
texture: self.clone(),
binding_type: wgpu::BindingType::StorageTexture {
access: wgpu::StorageTextureAccess::WriteOnly,
format: self.texture.format(),
view_dimension,
},
}
}
pub(crate) fn get_or_build(&self, context: &Context) -> wgpu::TextureView {
let mut texture_view_cache = context.caches.texture_view_cache.borrow_mut();
texture_view_cache
.get_or_insert_with(self.clone(), || {
self.texture.create_view(&wgpu::TextureViewDescriptor {
label: None,
format: None,
dimension: None,
aspect: wgpu::TextureAspect::All,
base_mip_level: self.base_mip_level,
mip_level_count: Some(self.mip_level_count),
base_array_layer: 0,
array_layer_count: None,
usage: None,
})
})
.clone()
}
}
impl Hash for Texture {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
self.base_mip_level.hash(state);
self.mip_level_count.hash(state);
}
}
impl PartialEq for Texture {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
&& self.base_mip_level == other.base_mip_level
&& self.mip_level_count == other.mip_level_count
}
}
impl Eq for Texture {}