#![allow(dead_code)] use anyhow::*; use eframe::wgpu; use image::GenericImageView; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum TextureColorSpace { Srgb, Linear, } impl TextureColorSpace { pub fn format(self) -> wgpu::TextureFormat { match self { Self::Srgb => wgpu::TextureFormat::Rgba8UnormSrgb, Self::Linear => wgpu::TextureFormat::Rgba8Unorm, } } } #[derive(Copy, Clone, Debug)] pub struct TextureSamplerDescriptor { pub address_mode_u: wgpu::AddressMode, pub address_mode_v: wgpu::AddressMode, pub mag_filter: wgpu::FilterMode, pub min_filter: wgpu::FilterMode, pub mipmap_filter: wgpu::MipmapFilterMode, } impl Default for TextureSamplerDescriptor { fn default() -> Self { Self { address_mode_u: wgpu::AddressMode::Repeat, address_mode_v: wgpu::AddressMode::Repeat, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::MipmapFilterMode::Linear, } } } pub struct Texture { pub texture: wgpu::Texture, pub view: wgpu::TextureView, pub sampler: wgpu::Sampler, pub width: u32, pub height: u32, } impl Texture { pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; pub fn create_depth_texture( device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, label: &str, ) -> Self { let size = wgpu::Extent3d { width: config.width, height: config.height, depth_or_array_layers: 1, }; let desc = wgpu::TextureDescriptor { label: Some(label), size, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: Self::DEPTH_FORMAT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, view_formats: &[], }; let texture = device.create_texture(&desc); let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let sampler = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::MipmapFilterMode::Nearest, compare: Some(wgpu::CompareFunction::LessEqual), lod_min_clamp: 0.0, lod_max_clamp: 200.0, ..Default::default() }); Self { texture, view, sampler, width: config.width, height: config.height, } } #[allow(dead_code)] pub fn from_bytes( device: &wgpu::Device, queue: &wgpu::Queue, bytes: &[u8], label: &str, ) -> Result { let img = image::load_from_memory(bytes)?; Self::from_image(device, queue, &img, Some(label)) } pub fn from_image( device: &wgpu::Device, queue: &wgpu::Queue, img: &image::DynamicImage, label: Option<&str>, ) -> Result { let dimensions = img.dimensions(); let rgba = img.to_rgba8(); let size = wgpu::Extent3d { width: dimensions.0, height: dimensions.1, depth_or_array_layers: 1, }; let texture = device.create_texture(&wgpu::TextureDescriptor { label, size, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: TextureColorSpace::Srgb.format(), usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, view_formats: &[], }); queue.write_texture( wgpu::TexelCopyTextureInfo { aspect: wgpu::TextureAspect::All, texture: &texture, mip_level: 0, origin: wgpu::Origin3d::ZERO, }, &rgba, wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(4 * dimensions.0), rows_per_image: Some(dimensions.1), }, size, ); let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let sampler = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::MipmapFilterMode::Nearest, ..Default::default() }); Ok(Self { texture, view, sampler, width: dimensions.0, height: dimensions.1, }) } pub fn from_rgba8( device: &wgpu::Device, queue: &wgpu::Queue, rgba: &[u8], width: u32, height: u32, label: &str, ) -> Result { Self::from_rgba8_with_sampler( device, queue, rgba, width, height, label, TextureColorSpace::Srgb, TextureSamplerDescriptor::default(), ) } #[allow(clippy::too_many_arguments)] pub fn from_rgba8_with_sampler( device: &wgpu::Device, queue: &wgpu::Queue, rgba: &[u8], width: u32, height: u32, label: &str, color_space: TextureColorSpace, sampler_desc: TextureSamplerDescriptor, ) -> Result { let size = wgpu::Extent3d { width, height, depth_or_array_layers: 1, }; let texture = device.create_texture(&wgpu::TextureDescriptor { label: Some(label), size, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: color_space.format(), usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, view_formats: &[], }); queue.write_texture( wgpu::TexelCopyTextureInfo { aspect: wgpu::TextureAspect::All, texture: &texture, mip_level: 0, origin: wgpu::Origin3d::ZERO, }, rgba, wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(4 * width), rows_per_image: Some(height), }, size, ); let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let sampler = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: sampler_desc.address_mode_u, address_mode_v: sampler_desc.address_mode_v, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: sampler_desc.mag_filter, min_filter: sampler_desc.min_filter, mipmap_filter: sampler_desc.mipmap_filter, ..Default::default() }); Ok(Self { texture, view, sampler, width, height, }) } }