teksilo_render/
image_manager.rs1use std::collections::HashMap;
15
16use crate::mipmap::build_mip_chain;
17
18#[derive(Default)]
20pub struct ImageManager {
21 images: HashMap<String, ImageEntry>,
22}
23
24struct ImageEntry {
25 _texture: wgpu::Texture,
26 bind_group: wgpu::BindGroup,
27}
28
29impl ImageManager {
30 pub fn new() -> Self {
31 Self::default()
32 }
33
34 #[allow(clippy::too_many_arguments)]
37 pub fn register_image(
38 &mut self,
39 name: &str,
40 width: u32,
41 height: u32,
42 pixels: &[u8],
43 device: &wgpu::Device,
44 queue: &wgpu::Queue,
45 bind_group_layout: &wgpu::BindGroupLayout,
46 ) {
47 if width == 0 || height == 0 {
48 return;
49 }
50
51 let mips = build_mip_chain(pixels, width, height);
53
54 let texture = device.create_texture(&wgpu::TextureDescriptor {
55 label: Some("image_texture"),
56 size: wgpu::Extent3d {
57 width,
58 height,
59 depth_or_array_layers: 1,
60 },
61 mip_level_count: 1 + mips.len() as u32,
62 sample_count: 1,
63 dimension: wgpu::TextureDimension::D2,
64 format: wgpu::TextureFormat::Rgba8UnormSrgb,
65 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
66 view_formats: &[],
67 });
68
69 let upload = |level: u32, w: u32, h: u32, data: &[u8]| {
73 queue.write_texture(
74 wgpu::TexelCopyTextureInfo {
75 texture: &texture,
76 mip_level: level,
77 origin: wgpu::Origin3d::ZERO,
78 aspect: wgpu::TextureAspect::All,
79 },
80 data,
81 wgpu::TexelCopyBufferLayout {
82 offset: 0,
83 bytes_per_row: Some(w * 4),
84 rows_per_image: Some(h),
85 },
86 wgpu::Extent3d {
87 width: w,
88 height: h,
89 depth_or_array_layers: 1,
90 },
91 );
92 };
93 upload(0, width, height, pixels);
94 for (level, (w, h, data)) in mips.iter().enumerate() {
95 upload(level as u32 + 1, *w, *h, data);
96 }
97
98 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
99 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
105 mag_filter: wgpu::FilterMode::Linear,
106 min_filter: wgpu::FilterMode::Linear,
107 mipmap_filter: wgpu::MipmapFilterMode::Linear,
108 ..Default::default()
109 });
110
111 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
112 label: Some("image_bind_group"),
113 layout: bind_group_layout,
114 entries: &[
115 wgpu::BindGroupEntry {
116 binding: 0,
117 resource: wgpu::BindingResource::TextureView(&view),
118 },
119 wgpu::BindGroupEntry {
120 binding: 1,
121 resource: wgpu::BindingResource::Sampler(&sampler),
122 },
123 ],
124 });
125
126 self.images.insert(
127 name.to_string(),
128 ImageEntry {
129 _texture: texture,
130 bind_group,
131 },
132 );
133 }
134
135 pub fn get_bind_group(&self, name: &str) -> Option<&wgpu::BindGroup> {
137 self.images.get(name).map(|e| &e.bind_group)
138 }
139
140 pub fn contains(&self, name: &str) -> bool {
142 self.images.contains_key(name)
143 }
144
145 pub fn remove(&mut self, name: &str) {
147 self.images.remove(name);
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn image_manager_new_is_empty() {
157 let mgr = ImageManager::new();
158 assert!(!mgr.contains("test"));
159 assert!(mgr.get_bind_group("test").is_none());
160 }
161}