par_term/custom_shader_renderer/
textures.rs1use anyhow::{Context, Result};
7use std::path::Path;
8use wgpu::*;
9
10pub struct ChannelTexture {
12 #[allow(dead_code)]
14 pub texture: Texture,
15 pub view: TextureView,
17 pub sampler: Sampler,
19 pub width: u32,
21 pub height: u32,
23}
24
25impl ChannelTexture {
26 pub fn placeholder(device: &Device, queue: &Queue) -> Self {
31 let texture = device.create_texture(&TextureDescriptor {
32 label: Some("Channel Placeholder Texture"),
33 size: Extent3d {
34 width: 1,
35 height: 1,
36 depth_or_array_layers: 1,
37 },
38 mip_level_count: 1,
39 sample_count: 1,
40 dimension: TextureDimension::D2,
41 format: TextureFormat::Rgba8UnormSrgb,
42 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
43 view_formats: &[],
44 });
45
46 queue.write_texture(
48 TexelCopyTextureInfo {
49 texture: &texture,
50 mip_level: 0,
51 origin: Origin3d::ZERO,
52 aspect: TextureAspect::All,
53 },
54 &[0u8, 0, 0, 0], TexelCopyBufferLayout {
56 offset: 0,
57 bytes_per_row: Some(4),
58 rows_per_image: Some(1),
59 },
60 Extent3d {
61 width: 1,
62 height: 1,
63 depth_or_array_layers: 1,
64 },
65 );
66
67 let view = texture.create_view(&TextureViewDescriptor::default());
68 let sampler = device.create_sampler(&SamplerDescriptor {
69 label: Some("Channel Placeholder Sampler"),
70 address_mode_u: AddressMode::ClampToEdge,
71 address_mode_v: AddressMode::ClampToEdge,
72 address_mode_w: AddressMode::ClampToEdge,
73 mag_filter: FilterMode::Linear,
74 min_filter: FilterMode::Linear,
75 mipmap_filter: FilterMode::Linear,
76 ..Default::default()
77 });
78
79 Self {
80 texture,
81 view,
82 sampler,
83 width: 1,
84 height: 1,
85 }
86 }
87
88 pub fn from_file(device: &Device, queue: &Queue, path: &Path) -> Result<Self> {
100 let img = image::open(path)
102 .with_context(|| format!("Failed to open image: {}", path.display()))?
103 .to_rgba8();
104
105 let (width, height) = img.dimensions();
106
107 let texture = device.create_texture(&TextureDescriptor {
109 label: Some(&format!("Channel Texture: {}", path.display())),
110 size: Extent3d {
111 width,
112 height,
113 depth_or_array_layers: 1,
114 },
115 mip_level_count: 1,
116 sample_count: 1,
117 dimension: TextureDimension::D2,
118 format: TextureFormat::Rgba8UnormSrgb,
119 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
120 view_formats: &[],
121 });
122
123 queue.write_texture(
125 TexelCopyTextureInfo {
126 texture: &texture,
127 mip_level: 0,
128 origin: Origin3d::ZERO,
129 aspect: TextureAspect::All,
130 },
131 &img,
132 TexelCopyBufferLayout {
133 offset: 0,
134 bytes_per_row: Some(4 * width),
135 rows_per_image: Some(height),
136 },
137 Extent3d {
138 width,
139 height,
140 depth_or_array_layers: 1,
141 },
142 );
143
144 let view = texture.create_view(&TextureViewDescriptor::default());
145
146 let sampler = device.create_sampler(&SamplerDescriptor {
148 label: Some(&format!("Channel Sampler: {}", path.display())),
149 address_mode_u: AddressMode::Repeat,
150 address_mode_v: AddressMode::Repeat,
151 address_mode_w: AddressMode::Repeat,
152 mag_filter: FilterMode::Linear,
153 min_filter: FilterMode::Linear,
154 mipmap_filter: FilterMode::Linear,
155 ..Default::default()
156 });
157
158 log::info!(
159 "Loaded channel texture: {} ({}x{})",
160 path.display(),
161 width,
162 height
163 );
164
165 Ok(Self {
166 texture,
167 view,
168 sampler,
169 width,
170 height,
171 })
172 }
173
174 pub fn resolution(&self) -> [f32; 4] {
178 [self.width as f32, self.height as f32, 1.0, 0.0]
179 }
180}
181
182pub fn load_channel_textures(
192 device: &Device,
193 queue: &Queue,
194 paths: &[Option<std::path::PathBuf>; 4],
195) -> [ChannelTexture; 4] {
196 let load_or_placeholder = |path: &Option<std::path::PathBuf>, index: usize| -> ChannelTexture {
197 match path {
198 Some(p) => match ChannelTexture::from_file(device, queue, p) {
199 Ok(tex) => tex,
200 Err(e) => {
201 log::error!(
202 "Failed to load iChannel{} texture '{}': {}",
203 index + 1,
204 p.display(),
205 e
206 );
207 ChannelTexture::placeholder(device, queue)
208 }
209 },
210 None => ChannelTexture::placeholder(device, queue),
211 }
212 };
213
214 [
215 load_or_placeholder(&paths[0], 0),
216 load_or_placeholder(&paths[1], 1),
217 load_or_placeholder(&paths[2], 2),
218 load_or_placeholder(&paths[3], 3),
219 ]
220}