flow_ngin/data_structures/
texture.rs1use anyhow::*;
8use image::{GenericImageView, ImageFormat, load_from_memory_with_format};
9
10use crate::pipelines::mipmapper::Mipmapper;
11
12#[derive(Clone, Debug)]
19pub struct Texture {
20 #[allow(unused)]
21 pub texture: wgpu::Texture,
22 pub view: wgpu::TextureView,
23 pub sampler: Option<wgpu::Sampler>,
24}
25
26impl Texture {
27 pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
29
30 pub fn create_msaa_texture(
41 device: &wgpu::Device,
42 config: &wgpu::SurfaceConfiguration,
43 sample_count: u32,
44 ) -> wgpu::TextureView {
45 let texture = device.create_texture(&wgpu::TextureDescriptor {
46 label: Some("MSAA Color Texture"),
47 size: wgpu::Extent3d {
48 width: config.width.max(1),
49 height: config.height.max(1),
50 depth_or_array_layers: 1,
51 },
52 mip_level_count: 1,
53 sample_count,
54 dimension: wgpu::TextureDimension::D2,
55 format: config.format,
56 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
57 view_formats: &[],
58 });
59 texture.create_view(&wgpu::TextureViewDescriptor::default())
60 }
61
62 pub fn create_depth_texture(device: &wgpu::Device, size: [u32; 2], label: &str, sample_count: u32) -> Self {
63 let size = wgpu::Extent3d {
64 width: size[0].max(1),
65 height: size[1].max(1),
66 depth_or_array_layers: 1,
67 };
68 let desc = wgpu::TextureDescriptor {
69 label: Some(label),
70 size,
71 mip_level_count: 1,
72 sample_count,
73 dimension: wgpu::TextureDimension::D2,
74 format: Self::DEPTH_FORMAT,
75 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
76 view_formats: &[Self::DEPTH_FORMAT],
77 };
78 let texture = device.create_texture(&desc);
79 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
80 let sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor {
81 address_mode_u: wgpu::AddressMode::Repeat,
82 address_mode_v: wgpu::AddressMode::Repeat,
83 address_mode_w: wgpu::AddressMode::Repeat,
84 mag_filter: wgpu::FilterMode::Linear,
85 min_filter: wgpu::FilterMode::Linear,
86 mipmap_filter: wgpu::MipmapFilterMode::Nearest,
87 compare: Some(wgpu::CompareFunction::LessEqual),
88 lod_min_clamp: 0.0,
89 lod_max_clamp: 100.0,
90 ..Default::default()
91 }));
92
93 Self {
94 texture,
95 view,
96 sampler,
97 }
98 }
99
100 pub fn create_default_normal_map(
105 width: u32,
106 height: u32,
107 device: &wgpu::Device,
108 queue: &wgpu::Queue,
109 ) -> Texture {
110 let size = wgpu::Extent3d {
111 width: width,
112 height: height,
113 depth_or_array_layers: 1,
114 };
115
116 let data: Vec<u8> = [127, 127, 255, 255]
118 .iter()
119 .cycle()
120 .take(width as usize * height as usize * 4)
121 .map(|&u| u)
122 .collect();
123
124 let texture = device.create_texture(&wgpu::TextureDescriptor {
125 label: Some("default normal map"),
126 size,
127 mip_level_count: 1,
128 sample_count: 1,
129 dimension: wgpu::TextureDimension::D2,
130 format: wgpu::TextureFormat::Rgba8Unorm,
131 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
132 view_formats: &[],
133 });
134
135 queue.write_texture(
136 wgpu::TexelCopyTextureInfo {
137 aspect: wgpu::TextureAspect::All,
138 texture: &texture,
139 mip_level: 0,
140 origin: wgpu::Origin3d::ZERO,
141 },
142 &data,
143 wgpu::TexelCopyBufferLayout {
144 offset: 0,
145 bytes_per_row: Some(width * 4),
146 rows_per_image: Some(height),
147 },
148 size,
149 );
150
151 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
152 let sampler = Some(create_default_sampler(device));
153 Texture {
154 texture,
155 view,
156 sampler,
157 }
158 }
159
160 pub fn from_bytes(
169 device: &wgpu::Device,
170 queue: &wgpu::Queue,
171 bytes: &[u8],
172 label: &str,
173 format: Option<&str>,
174 is_normal_map: bool,
175 ) -> Result<Self> {
176 let img = match format {
177 None => image::load_from_memory(bytes)?,
178 Some(fmt) => {
179 load_from_memory_with_format(bytes, ImageFormat::from_extension(fmt).unwrap())?
180 }
181 };
182 Self::from_image(device, queue, &img, Some(label), is_normal_map)
183 }
184
185 pub fn from_color(rgba: [u8; 4], device: &wgpu::Device, queue: &wgpu::Queue) -> Texture {
187 let size = wgpu::Extent3d {
188 width: 1,
189 height: 1,
190 depth_or_array_layers: 1,
191 };
192
193 let texture = device.create_texture(&wgpu::TextureDescriptor {
194 label: Some("solid color texture"),
195 size,
196 mip_level_count: 1,
197 sample_count: 1,
198 dimension: wgpu::TextureDimension::D2,
199 format: wgpu::TextureFormat::Rgba8UnormSrgb,
200 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
201 view_formats: &[],
202 });
203
204 queue.write_texture(
205 wgpu::TexelCopyTextureInfo {
206 aspect: wgpu::TextureAspect::All,
207 texture: &texture,
208 mip_level: 0,
209 origin: wgpu::Origin3d::ZERO,
210 },
211 &rgba,
212 wgpu::TexelCopyBufferLayout {
213 offset: 0,
214 bytes_per_row: Some(4),
215 rows_per_image: Some(1),
216 },
217 size,
218 );
219
220 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
221 let sampler = Some(create_default_sampler(device));
222 Texture {
223 texture,
224 view,
225 sampler,
226 }
227 }
228
229 pub fn from_image(
230 device: &wgpu::Device,
231 queue: &wgpu::Queue,
232 img: &image::DynamicImage,
233 label: Option<&str>,
234 is_normal_map: bool,
235 ) -> Result<Self> {
236 let dimensions = img.dimensions();
237 let rgba = img.to_rgba8();
238
239 let mip_level_count = dimensions.0.min(dimensions.1).max(1).ilog2() + 1;
240
241 let size = wgpu::Extent3d {
242 width: dimensions.0,
243 height: dimensions.1,
244 depth_or_array_layers: 1,
245 };
246 let format = if is_normal_map {
247 wgpu::TextureFormat::Rgba8Unorm
248 } else {
249 wgpu::TextureFormat::Rgba8UnormSrgb
250 };
251 let texture = device.create_texture(&wgpu::TextureDescriptor {
252 label,
253 size,
254 mip_level_count,
255 sample_count: 1,
256 dimension: wgpu::TextureDimension::D2,
257 format,
258 usage: wgpu::TextureUsages::TEXTURE_BINDING
259 | wgpu::TextureUsages::COPY_DST
260 | wgpu::TextureUsages::COPY_SRC,
261 view_formats: &[],
262 });
263
264 queue.write_texture(
265 wgpu::TexelCopyTextureInfo {
266 aspect: wgpu::TextureAspect::All,
267 texture: &texture,
268 mip_level: 0,
269 origin: wgpu::Origin3d::ZERO,
270 },
271 &rgba,
272 wgpu::TexelCopyBufferLayout {
273 offset: 0,
274 bytes_per_row: Some(4 * dimensions.0),
275 rows_per_image: Some(dimensions.1),
276 },
277 size,
278 );
279
280 let mipmapper = Mipmapper::new(device);
281 mipmapper.generate_mipmaps(device, queue, &texture)?;
282
283 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
284 let sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor {
285 address_mode_u: wgpu::AddressMode::Repeat,
286 address_mode_v: wgpu::AddressMode::Repeat,
287 address_mode_w: wgpu::AddressMode::Repeat,
288 mag_filter: wgpu::FilterMode::Linear,
289 min_filter: wgpu::FilterMode::Linear,
290 mipmap_filter: wgpu::MipmapFilterMode::Linear,
291 ..Default::default()
292 }));
293
294 Ok(Self {
295 texture,
296 view,
297 sampler,
298 })
299 }
300}
301
302pub fn create_default_sampler(device: &wgpu::Device) -> wgpu::Sampler {
303 device.create_sampler(&wgpu::SamplerDescriptor {
304 address_mode_u: wgpu::AddressMode::Repeat,
305 address_mode_v: wgpu::AddressMode::Repeat,
306 address_mode_w: wgpu::AddressMode::Repeat,
307 mag_filter: wgpu::FilterMode::Linear,
308 min_filter: wgpu::FilterMode::Linear,
309 mipmap_filter: wgpu::MipmapFilterMode::Linear,
310 ..Default::default()
311 })
312}