par_term_render/custom_shader_renderer/
textures.rs1use anyhow::{Context, Result};
7use std::path::Path;
8use wgpu::*;
9
10pub struct ChannelTexture {
12 #[allow(dead_code)]
15 pub texture: Option<Texture>,
16 pub view: TextureView,
18 pub sampler: Sampler,
20 pub width: u32,
22 pub height: u32,
24}
25
26impl ChannelTexture {
27 pub fn placeholder(device: &Device, queue: &Queue) -> Self {
32 let texture = device.create_texture(&TextureDescriptor {
33 label: Some("Channel Placeholder Texture"),
34 size: Extent3d {
35 width: 1,
36 height: 1,
37 depth_or_array_layers: 1,
38 },
39 mip_level_count: 1,
40 sample_count: 1,
41 dimension: TextureDimension::D2,
42 format: TextureFormat::Rgba8UnormSrgb,
43 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
44 view_formats: &[],
45 });
46
47 queue.write_texture(
49 TexelCopyTextureInfo {
50 texture: &texture,
51 mip_level: 0,
52 origin: Origin3d::ZERO,
53 aspect: TextureAspect::All,
54 },
55 &[0u8, 0, 0, 0], TexelCopyBufferLayout {
57 offset: 0,
58 bytes_per_row: Some(4),
59 rows_per_image: Some(1),
60 },
61 Extent3d {
62 width: 1,
63 height: 1,
64 depth_or_array_layers: 1,
65 },
66 );
67
68 let view = texture.create_view(&TextureViewDescriptor::default());
69 let sampler = device.create_sampler(&SamplerDescriptor {
70 label: Some("Channel Placeholder Sampler"),
71 address_mode_u: AddressMode::ClampToEdge,
72 address_mode_v: AddressMode::ClampToEdge,
73 address_mode_w: AddressMode::ClampToEdge,
74 mag_filter: FilterMode::Linear,
75 min_filter: FilterMode::Linear,
76 mipmap_filter: FilterMode::Linear,
77 ..Default::default()
78 });
79
80 Self {
81 texture: Some(texture),
82 view,
83 sampler,
84 width: 1,
85 height: 1,
86 }
87 }
88
89 pub fn from_view(view: TextureView, sampler: Sampler, width: u32, height: u32) -> Self {
101 Self {
102 texture: None,
103 view,
104 sampler,
105 width,
106 height,
107 }
108 }
109
110 pub fn from_view_and_texture(
122 view: TextureView,
123 sampler: Sampler,
124 width: u32,
125 height: u32,
126 texture: Texture,
127 ) -> Self {
128 Self {
129 texture: Some(texture),
130 view,
131 sampler,
132 width,
133 height,
134 }
135 }
136
137 pub fn from_file(device: &Device, queue: &Queue, path: &Path) -> Result<Self> {
149 let img = image::open(path)
151 .with_context(|| format!("Failed to open image: {}", path.display()))?
152 .to_rgba8();
153
154 let (width, height) = img.dimensions();
155
156 let texture = device.create_texture(&TextureDescriptor {
158 label: Some(&format!("Channel Texture: {}", path.display())),
159 size: Extent3d {
160 width,
161 height,
162 depth_or_array_layers: 1,
163 },
164 mip_level_count: 1,
165 sample_count: 1,
166 dimension: TextureDimension::D2,
167 format: TextureFormat::Rgba8UnormSrgb,
168 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
169 view_formats: &[],
170 });
171
172 queue.write_texture(
174 TexelCopyTextureInfo {
175 texture: &texture,
176 mip_level: 0,
177 origin: Origin3d::ZERO,
178 aspect: TextureAspect::All,
179 },
180 &img,
181 TexelCopyBufferLayout {
182 offset: 0,
183 bytes_per_row: Some(4 * width),
184 rows_per_image: Some(height),
185 },
186 Extent3d {
187 width,
188 height,
189 depth_or_array_layers: 1,
190 },
191 );
192
193 let view = texture.create_view(&TextureViewDescriptor::default());
194
195 let sampler = device.create_sampler(&SamplerDescriptor {
197 label: Some(&format!("Channel Sampler: {}", path.display())),
198 address_mode_u: AddressMode::Repeat,
199 address_mode_v: AddressMode::Repeat,
200 address_mode_w: AddressMode::Repeat,
201 mag_filter: FilterMode::Linear,
202 min_filter: FilterMode::Linear,
203 mipmap_filter: FilterMode::Linear,
204 ..Default::default()
205 });
206
207 log::info!(
208 "Loaded channel texture: {} ({}x{})",
209 path.display(),
210 width,
211 height
212 );
213
214 Ok(Self {
215 texture: Some(texture),
216 view,
217 sampler,
218 width,
219 height,
220 })
221 }
222
223 pub fn resolution(&self) -> [f32; 4] {
227 [self.width as f32, self.height as f32, 1.0, 0.0]
228 }
229}
230
231pub fn load_channel_textures(
241 device: &Device,
242 queue: &Queue,
243 paths: &[Option<std::path::PathBuf>; 4],
244) -> [ChannelTexture; 4] {
245 let load_or_placeholder = |path: &Option<std::path::PathBuf>, index: usize| -> ChannelTexture {
246 match path {
247 Some(p) => match ChannelTexture::from_file(device, queue, p) {
248 Ok(tex) => tex,
249 Err(e) => {
250 log::error!(
251 "Failed to load iChannel{} texture '{}': {}",
252 index,
253 p.display(),
254 e
255 );
256 ChannelTexture::placeholder(device, queue)
257 }
258 },
259 None => ChannelTexture::placeholder(device, queue),
260 }
261 };
262
263 [
264 load_or_placeholder(&paths[0], 0),
265 load_or_placeholder(&paths[1], 1),
266 load_or_placeholder(&paths[2], 2),
267 load_or_placeholder(&paths[3], 3),
268 ]
269}