Files
iris/src/render/texture.rs
2025-08-23 22:16:00 -04:00

115 lines
3.3 KiB
Rust

use image::{DynamicImage, EncodableLayout};
use wgpu::{util::DeviceExt, *};
use crate::layout::{TextureUpdate, Textures};
pub struct GpuTextures {
device: Device,
queue: Queue,
views: Vec<TextureView>,
samplers: Vec<Sampler>,
null_view: TextureView,
no_views: Vec<TextureView>,
}
impl GpuTextures {
pub fn update(&mut self, textures: &mut Textures) -> bool {
let mut changed = false;
for update in textures.updates() {
changed = true;
match update {
TextureUpdate::Push(image) => self.push(image),
TextureUpdate::Set(i, image) => self.set(i, image),
TextureUpdate::Free(i) => self.free(i),
}
}
changed
}
fn set(&mut self, i: u32, image: &DynamicImage) {
let view = self.create_view(image);
self.views[i as usize] = view;
}
fn free(&mut self, i: u32) {
self.views[i as usize] = self.null_view.clone();
}
fn push(&mut self, image: &DynamicImage) {
let view = self.create_view(image);
self.views.push(view);
}
fn create_view(&self, image: &DynamicImage) -> TextureView {
let image = image.to_rgba8();
let (width, height) = image.dimensions();
let texture = self.device.create_texture_with_data(
&self.queue,
&TextureDescriptor {
label: None,
size: Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8Unorm,
usage: TextureUsages::TEXTURE_BINDING,
view_formats: &[],
},
wgt::TextureDataOrder::MipMajor,
image.as_bytes(),
);
texture.create_view(&TextureViewDescriptor::default())
}
pub fn new(device: &Device, queue: &Queue) -> Self {
let null_view = null_texture_view(device);
Self {
device: device.clone(),
queue: queue.clone(),
views: Vec::new(),
samplers: vec![default_sampler(device)],
no_views: vec![null_view.clone()],
null_view,
}
}
pub fn views(&self) -> Vec<&TextureView> {
if self.views.is_empty() {
&self.no_views
} else {
&self.views
}
.iter()
.by_ref()
.collect()
}
pub fn samplers(&self) -> Vec<&Sampler> {
self.samplers.iter().by_ref().collect()
}
}
pub fn null_texture_view(device: &Device) -> TextureView {
device
.create_texture(&TextureDescriptor {
label: Some("null"),
size: Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8Unorm,
usage: TextureUsages::TEXTURE_BINDING,
view_formats: &[],
})
.create_view(&TextureViewDescriptor::default())
}
pub fn default_sampler(device: &Device) -> Sampler {
device.create_sampler(&SamplerDescriptor::default())
}