1use gloss_img::DynImage;
2use image::imageops::FilterType;
3use image::{EncodableLayout, GenericImageView, ImageBuffer};
5use log::{debug, warn};
6use pollster::FutureExt;
7use std::borrow::Cow;
8use wgpu::{util::DeviceExt, CommandEncoderDescriptor, TextureFormat}; use gloss_utils::numerical;
12
13use crate::{buffer::Buffer, mipmap::RenderMipmapGenerator};
14
15#[derive(Clone, Copy)]
18pub struct TexParams {
19 pub sample_count: u32,
20 pub mip_level_count: u32,
21}
22impl Default for TexParams {
23 fn default() -> Self {
24 Self {
25 sample_count: 1,
26 mip_level_count: 1,
27 }
28 }
29}
30impl TexParams {
31 pub fn from_desc(desc: &wgpu::TextureDescriptor) -> Self {
32 Self {
33 sample_count: desc.sample_count,
34 mip_level_count: desc.mip_level_count,
35 }
36 }
37 pub fn apply(&self, desc: &mut wgpu::TextureDescriptor) {
38 desc.sample_count = self.sample_count;
39 desc.mip_level_count = self.mip_level_count;
40 }
41}
42
43pub struct Texture {
44 pub texture: wgpu::Texture,
45 pub view: wgpu::TextureView,
46 pub sampler: wgpu::Sampler, pub tex_params: TexParams,
51}
52
53impl Texture {
54 pub fn new(
55 device: &wgpu::Device,
56 width: u32,
57 height: u32,
58 format: wgpu::TextureFormat,
59 usage: wgpu::TextureUsages,
60 tex_params: TexParams,
61 ) -> Self {
62 debug!("New texture");
63 let mut texture_desc = wgpu::TextureDescriptor {
65 size: wgpu::Extent3d {
66 width,
67 height,
68 depth_or_array_layers: 1,
69 },
70 mip_level_count: 1,
71 sample_count: 1,
72 dimension: wgpu::TextureDimension::D2,
73 format,
74 usage,
76 label: None,
77 view_formats: &[],
78 };
79 tex_params.apply(&mut texture_desc);
80
81 let texture = device.create_texture(&texture_desc);
82 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
83 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
84 address_mode_u: wgpu::AddressMode::ClampToEdge,
85 address_mode_v: wgpu::AddressMode::ClampToEdge,
86 address_mode_w: wgpu::AddressMode::ClampToEdge,
87 mag_filter: wgpu::FilterMode::Linear,
88 min_filter: wgpu::FilterMode::Linear,
89 mipmap_filter: wgpu::FilterMode::Linear,
90 ..Default::default()
91 });
92
93 Self {
94 texture,
95 view,
96 sampler,
97 tex_params,
98 }
102 }
103
104 pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bytes: &[u8], label: &str) -> Self {
107 let img = image::load_from_memory(bytes).unwrap();
108 Self::from_image(device, queue, &img, Some(label))
109 }
110
111 pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, img: &image::DynamicImage, label: Option<&str>) -> Self {
112 let rgba = img.to_rgba8();
113 let dimensions = img.dimensions();
114
115 let size = wgpu::Extent3d {
116 width: dimensions.0,
117 height: dimensions.1,
118 depth_or_array_layers: 1,
119 };
120 let format = wgpu::TextureFormat::Rgba8UnormSrgb;
121 let desc = wgpu::TextureDescriptor {
122 label,
123 size,
124 mip_level_count: 1,
125 sample_count: 1,
126 dimension: wgpu::TextureDimension::D2,
127 format,
128 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
129 view_formats: &[],
130 };
131 let tex_params = TexParams::from_desc(&desc);
132 let texture = device.create_texture(&desc);
133
134 queue.write_texture(
135 wgpu::ImageCopyTexture {
136 aspect: wgpu::TextureAspect::All,
137 texture: &texture,
138 mip_level: 0,
139 origin: wgpu::Origin3d::ZERO,
140 },
141 &rgba,
142 wgpu::ImageDataLayout {
143 offset: 0,
144 bytes_per_row: Some(4 * dimensions.0),
145 rows_per_image: Some(dimensions.1),
146 },
147 size,
148 );
149
150 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
151 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
152 address_mode_u: wgpu::AddressMode::ClampToEdge,
153 address_mode_v: wgpu::AddressMode::ClampToEdge,
154 address_mode_w: wgpu::AddressMode::ClampToEdge,
155 mag_filter: wgpu::FilterMode::Linear,
156 min_filter: wgpu::FilterMode::Nearest,
157 mipmap_filter: wgpu::FilterMode::Nearest,
158 ..Default::default()
159 });
160
161 Self {
162 texture,
163 view,
164 sampler,
165 tex_params, }
169 }
170
171 pub fn from_path(path: &str, device: &wgpu::Device, queue: &wgpu::Queue, is_srgb: bool) -> Self {
178 let img = image::ImageReader::open(path).unwrap().decode().unwrap();
180 Self::from_img(
181 &img.try_into().unwrap(),
182 device,
183 queue,
184 is_srgb,
185 true,
186 false, None,
188 None,
189 )
190 }
191
192 #[allow(clippy::too_many_lines)]
196 #[allow(clippy::too_many_arguments)]
197 pub fn from_img(
198 img: &DynImage,
199 device: &wgpu::Device,
200 queue: &wgpu::Queue,
201 is_srgb: bool,
202 generate_mipmaps: bool,
203 mipmap_generation_cpu: bool,
204 staging_buffer: Option<&Buffer>,
205 mipmaper: Option<&RenderMipmapGenerator>,
206 ) -> Self {
207 let dimensions = img.dimensions();
208 let nr_channels = img.color().channel_count();
209 let bytes_per_channel = img.color().bytes_per_pixel() / nr_channels;
210 assert!(bytes_per_channel == 1, "We are only supporting textures which have 1 byte per channel.");
211 let img_vec;
213 let img_buf = match nr_channels {
214 1 | 2 | 4 => img.as_bytes(),
215 3 => {
216 img_vec = img.to_rgba8().into_vec();
217 img_vec.as_bytes()
218 }
219 _ => panic!("Format with more than 4 channels not supported"),
220 };
221
222 let tex_format = Self::format_from_img(img, is_srgb);
223
224 let size = wgpu::Extent3d {
225 width: dimensions.0,
226 height: dimensions.1,
227 depth_or_array_layers: 1,
228 };
229 let mut nr_mip_maps = 1;
230 let mut usages = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST;
231 if generate_mipmaps {
232 nr_mip_maps = size.max_mips(wgpu::TextureDimension::D2);
233 }
234 if mipmaper.is_some() && generate_mipmaps {
235 usages |= RenderMipmapGenerator::required_usage();
236 }
237
238 let desc = wgpu::TextureDescriptor {
239 label: None,
240 size,
241 mip_level_count: nr_mip_maps,
242 sample_count: 1,
243 dimension: wgpu::TextureDimension::D2,
244 format: tex_format,
245 usage: usages,
246 view_formats: &[],
247 };
248 let tex_params = TexParams::from_desc(&desc);
249
250 let texture = device.create_texture(&desc); Self::upload_single_mip(&texture, device, queue, &desc, img_buf, staging_buffer, 0);
253
254 if generate_mipmaps {
256 Self::generate_mipmaps(
257 img,
258 &texture,
259 device,
260 queue,
261 &desc,
262 nr_mip_maps,
263 mipmap_generation_cpu,
264 staging_buffer,
265 mipmaper,
266 );
267 }
268
269 let view = texture.create_view(&wgpu::TextureViewDescriptor {
271 mip_level_count: Some(nr_mip_maps),
272 ..Default::default()
273 });
274 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
275 address_mode_u: wgpu::AddressMode::ClampToEdge,
276 address_mode_v: wgpu::AddressMode::ClampToEdge,
277 address_mode_w: wgpu::AddressMode::ClampToEdge,
278 mag_filter: wgpu::FilterMode::Linear,
279 min_filter: wgpu::FilterMode::Nearest,
280 mipmap_filter: wgpu::FilterMode::Nearest,
281 ..Default::default()
282 });
283
284 Self {
285 texture,
286 view,
287 sampler,
288 tex_params, }
292 }
293
294 #[allow(clippy::too_many_arguments)]
297 pub fn update_from_img(
298 &mut self,
299 img: &DynImage,
300 device: &wgpu::Device,
301 queue: &wgpu::Queue,
302 is_srgb: bool,
303 generate_mipmaps: bool,
304 mipmap_generation_cpu: bool,
305 staging_buffer: Option<&Buffer>,
306 mipmaper: Option<&RenderMipmapGenerator>,
307 ) {
308 let nr_channels = img.color().channel_count();
310 let bytes_per_channel = img.color().bytes_per_pixel() / nr_channels;
311 assert!(bytes_per_channel == 1, "We are only supporting textures which have 1 byte per channel.");
312
313 let img_vec;
317 let img_buf = match nr_channels {
318 1 | 2 | 4 => img.as_bytes(),
319 3 => {
320 img_vec = img.to_rgba8().into_vec();
321 img_vec.as_bytes()
322 }
323 _ => panic!("Format with more than 4 channels not supported"),
324 };
325
326 let size = Self::extent_from_img(img);
327 let tex_format = Self::format_from_img(img, is_srgb);
328 let mut nr_mip_maps = 1;
329 let mut usages = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST;
330 if generate_mipmaps {
331 nr_mip_maps = size.max_mips(wgpu::TextureDimension::D2);
332 }
333 if mipmaper.is_some() && generate_mipmaps {
334 usages |= RenderMipmapGenerator::required_usage();
335 }
336
337 let desc = wgpu::TextureDescriptor {
338 label: None,
339 size,
340 mip_level_count: nr_mip_maps,
341 sample_count: 1,
342 dimension: wgpu::TextureDimension::D2,
343 format: tex_format,
344 usage: usages,
345 view_formats: &[],
346 };
347
348 Self::upload_single_mip(&self.texture, device, queue, &desc, img_buf, staging_buffer, 0);
349
350 if generate_mipmaps {
352 Self::generate_mipmaps(
353 img,
354 &self.texture,
355 device,
356 queue,
357 &desc,
358 nr_mip_maps,
359 mipmap_generation_cpu,
360 staging_buffer,
361 mipmaper,
362 );
363 }
364
365 let view = self.texture.create_view(&wgpu::TextureViewDescriptor {
367 mip_level_count: Some(nr_mip_maps),
368 ..Default::default()
369 });
370
371 self.view = view;
373 }
374
375 #[allow(clippy::too_many_arguments)]
376 pub fn generate_mipmaps(
377 img: &DynImage,
378 texture: &wgpu::Texture,
379 device: &wgpu::Device,
380 queue: &wgpu::Queue,
381 desc: &wgpu::TextureDescriptor,
382 nr_mip_maps: u32,
383 mipmap_generation_cpu: bool,
384 staging_buffer: Option<&Buffer>,
385 mipmaper: Option<&RenderMipmapGenerator>,
386 ) {
387 let nr_channels = img.color().channel_count();
388 if mipmap_generation_cpu {
389 let mut img_mip = DynImage::new(1, 1, image::ColorType::L8);
392 for mip_lvl in 1..nr_mip_maps {
393 let mip_size = desc.mip_level_size(mip_lvl).unwrap();
394 let prev_img_mip = if mip_lvl == 1 { img } else { &img_mip };
395 img_mip = prev_img_mip.resize_exact(mip_size.width, mip_size.height, FilterType::Triangle);
396 debug!("mip lvl {} has size {:?}", mip_lvl, mip_size);
397
398 let img_mip_vec;
399 let img_mip_buf = match nr_channels {
400 1 | 2 | 4 => img_mip.as_bytes(),
401 3 => {
402 img_mip_vec = img_mip.to_rgba8().into_vec();
403 img_mip_vec.as_bytes()
404 }
405 _ => panic!("Format with more than 4 channels not supported"),
406 };
407
408 Self::upload_single_mip(texture, device, queue, desc, img_mip_buf, staging_buffer, mip_lvl);
409 }
410 } else {
411 if let Some(mipmaper) = mipmaper {
413 let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor::default());
414 mipmaper.generate(device, &mut encoder, texture, desc).unwrap();
415 queue.submit(std::iter::once(encoder.finish()));
416 } else {
417 warn!("Couldn't generate mipmaps since the mipmapper was not provided");
418 }
419 }
420 }
421
422 pub fn extent_from_img(img: &DynImage) -> wgpu::Extent3d {
423 let dimensions = img.dimensions();
424 wgpu::Extent3d {
425 width: dimensions.0,
426 height: dimensions.1,
427 depth_or_array_layers: 1,
428 }
429 }
430
431 pub fn format_from_img(img: &DynImage, is_srgb: bool) -> wgpu::TextureFormat {
434 let nr_channels = img.color().channel_count();
435 let bytes_per_channel = img.color().bytes_per_pixel() / nr_channels;
436 assert!(bytes_per_channel == 1, "We are only supporting textures which have 1 byte per channel.");
437
438 let mut tex_format = match nr_channels {
440 1 => wgpu::TextureFormat::R8Unorm,
441 2 => wgpu::TextureFormat::Rg8Unorm,
442 3 | 4 => wgpu::TextureFormat::Rgba8Unorm,
443 _ => panic!("Format with more than 4 channels not supported"),
444 };
445 if is_srgb {
446 tex_format = tex_format.add_srgb_suffix();
447 }
448
449 tex_format
450 }
451
452 pub fn upload_single_mip(
457 texture: &wgpu::Texture,
458 device: &wgpu::Device,
459 queue: &wgpu::Queue,
460 desc: &wgpu::TextureDescriptor,
461 data: &[u8],
462 staging_buffer: Option<&Buffer>,
463 mip: u32,
464 ) {
465 let mut mip_size = desc.mip_level_size(mip).unwrap();
466 if desc.dimension != wgpu::TextureDimension::D3 {
468 mip_size.depth_or_array_layers = 1;
469 }
470
471 let block_size = desc.format.block_copy_size(None).unwrap_or(4);
475 let (block_width, block_height) = desc.format.block_dimensions();
476
477 let mip_physical = mip_size.physical_size(desc.format);
482
483 let width_blocks = mip_physical.width / block_width;
486 let height_blocks = mip_physical.height / block_height;
487
488 let bytes_per_row = width_blocks * block_size;
489 if let Some(staging_buffer) = staging_buffer {
495 warn!("Using slow CPU->GPU transfer for texture upload. Might use less memory that staging buffer using by wgpu but it will be slower.");
496
497 let bytes_per_row_unpadded = texture.format().block_copy_size(None).unwrap() * mip_size.width;
499 let bytes_per_row_padded = numerical::align(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
500
501 let slice_size = numerical::align(u32::try_from(data.len()).unwrap(), u32::try_from(wgpu::COPY_BUFFER_ALIGNMENT).unwrap());
505 {
506 let buffer_slice = staging_buffer.buffer.slice(0..u64::from(slice_size));
507 let (tx, rx) = futures::channel::oneshot::channel();
510 buffer_slice.map_async(wgpu::MapMode::Write, move |result| {
511 tx.send(result).unwrap();
512 });
513 device.poll(wgpu::Maintain::Wait);
514 rx.block_on().unwrap().unwrap();
515 let mut buf_data = buffer_slice.get_mapped_range_mut();
516
517 buf_data.get_mut(0..data.len()).unwrap().clone_from_slice(data);
519 }
520
521 staging_buffer.buffer.unmap();
523
524 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
526 encoder.copy_buffer_to_texture(
527 wgpu::ImageCopyBuffer {
528 buffer: &staging_buffer.buffer,
529 layout: wgpu::ImageDataLayout {
530 offset: 0,
531 bytes_per_row: Some(bytes_per_row_padded),
532 rows_per_image: Some(mip_size.height),
533 },
534 },
535 wgpu::ImageCopyTexture {
536 aspect: wgpu::TextureAspect::All,
537 texture,
538 mip_level: mip,
539 origin: wgpu::Origin3d::ZERO,
540 },
541 wgpu::Extent3d {
542 width: mip_size.width,
543 height: mip_size.height,
544 depth_or_array_layers: 1,
545 },
546 );
547 queue.submit(Some(encoder.finish()));
548
549 } else {
554 queue.write_texture(
557 wgpu::ImageCopyTexture {
558 texture,
559 mip_level: mip,
560 origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },
561 aspect: wgpu::TextureAspect::All,
562 },
563 data,
564 wgpu::ImageDataLayout {
565 offset: 0,
566 bytes_per_row: Some(bytes_per_row),
567 rows_per_image: Some(height_blocks),
568 },
569 mip_physical,
570 );
571 }
572
573 }
575
576 pub fn upload_all_mips(
582 texture: &wgpu::Texture,
583 device: &wgpu::Device,
584 queue: &wgpu::Queue,
585 desc: &wgpu::TextureDescriptor,
586 data: &[u8],
587 staging_buffer: Option<&Buffer>,
588 ) {
589 let block_size = desc.format.block_copy_size(None).unwrap_or(4);
593 let (block_width, block_height) = desc.format.block_dimensions();
594 let layer_iterations = desc.array_layer_count();
595
596 let (min_mip, max_mip) = (0, desc.mip_level_count);
597
598 let mut binary_offset = 0;
599 for layer in 0..layer_iterations {
600 for mip in min_mip..max_mip {
601 let mut mip_size = desc.mip_level_size(mip).unwrap();
602 if desc.dimension != wgpu::TextureDimension::D3 {
604 mip_size.depth_or_array_layers = 1;
605 }
606
607 let mip_physical = mip_size.physical_size(desc.format);
612
613 let width_blocks = mip_physical.width / block_width;
616 let height_blocks = mip_physical.height / block_height;
617
618 let bytes_per_row = width_blocks * block_size;
619 let data_size = bytes_per_row * height_blocks * mip_size.depth_or_array_layers;
620
621 let end_offset = binary_offset + data_size as usize;
622
623 if let Some(staging_buffer) = staging_buffer {
624 warn!("Using slow CPU->GPU transfer for texture upload. Might use less memory that staging buffer using by wgpu but it will be slower.");
625
626 let bytes_per_row_unpadded = texture.format().block_copy_size(None).unwrap() * mip_size.width;
628 let bytes_per_row_padded = numerical::align(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
629
630 let data_to_copy = &data[binary_offset..end_offset];
633 let slice_size = numerical::align(
635 u32::try_from(data_to_copy.len()).unwrap(),
636 u32::try_from(wgpu::COPY_BUFFER_ALIGNMENT).unwrap(),
637 );
638 {
639 let buffer_slice = staging_buffer.buffer.slice(0..u64::from(slice_size));
640 let (tx, rx) = futures::channel::oneshot::channel();
643 buffer_slice.map_async(wgpu::MapMode::Write, move |result| {
644 tx.send(result).unwrap();
645 });
646 device.poll(wgpu::Maintain::Wait);
647 rx.block_on().unwrap().unwrap();
648 let mut buf_data = buffer_slice.get_mapped_range_mut();
649
650 buf_data.get_mut(0..data_to_copy.len()).unwrap().clone_from_slice(data_to_copy);
652 }
653
654 staging_buffer.buffer.unmap();
656
657 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
659 encoder.copy_buffer_to_texture(
660 wgpu::ImageCopyBuffer {
661 buffer: &staging_buffer.buffer,
662 layout: wgpu::ImageDataLayout {
663 offset: 0,
664 bytes_per_row: Some(bytes_per_row_padded),
665 rows_per_image: Some(mip_size.height),
666 },
667 },
668 wgpu::ImageCopyTexture {
669 aspect: wgpu::TextureAspect::All,
670 texture,
671 mip_level: mip,
672 origin: wgpu::Origin3d::ZERO,
673 },
674 wgpu::Extent3d {
675 width: mip_size.width,
676 height: mip_size.height,
677 depth_or_array_layers: 1,
678 },
679 );
680 queue.submit(Some(encoder.finish()));
681
682 } else {
688 queue.write_texture(
691 wgpu::ImageCopyTexture {
692 texture,
693 mip_level: mip,
694 origin: wgpu::Origin3d { x: 0, y: 0, z: layer },
695 aspect: wgpu::TextureAspect::All,
696 },
697 &data[binary_offset..end_offset],
698 wgpu::ImageDataLayout {
699 offset: 0,
700 bytes_per_row: Some(bytes_per_row),
701 rows_per_image: Some(height_blocks),
702 },
703 mip_physical,
704 );
705 }
706
707 binary_offset = end_offset;
708 }
709 }
710 }
711
712 pub fn upload_from_cpu_with_staging_buffer(
713 texture: &wgpu::Texture,
714 device: &wgpu::Device,
715 queue: &wgpu::Queue,
716 desc: &wgpu::TextureDescriptor,
717 data: &[u8],
718 staging_buffer: &Buffer,
719 mip_lvl: u32,
720 ) {
721 let mip_size = desc.mip_level_size(mip_lvl).unwrap();
722
723 {
726 let buffer_slice = staging_buffer.buffer.slice(0..data.len() as u64);
727 let (tx, rx) = futures::channel::oneshot::channel();
730 buffer_slice.map_async(wgpu::MapMode::Write, move |result| {
731 tx.send(result).unwrap();
732 });
733 device.poll(wgpu::Maintain::Wait);
734 rx.block_on().unwrap().unwrap();
735 let mut buf_data = buffer_slice.get_mapped_range_mut();
736
737 buf_data.clone_from_slice(data);
739 }
740
741 staging_buffer.buffer.unmap();
743
744 let bytes_per_row_unpadded = texture.format().block_copy_size(None).unwrap() * mip_size.width;
746 let bytes_per_row_padded = numerical::align(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
747
748 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
750 encoder.copy_buffer_to_texture(
751 wgpu::ImageCopyBuffer {
752 buffer: &staging_buffer.buffer,
753 layout: wgpu::ImageDataLayout {
754 offset: 0,
755 bytes_per_row: Some(bytes_per_row_padded),
756 rows_per_image: Some(mip_size.height),
757 },
758 },
759 wgpu::ImageCopyTexture {
760 aspect: wgpu::TextureAspect::All,
761 texture,
762 mip_level: mip_lvl,
763 origin: wgpu::Origin3d::ZERO,
764 },
765 wgpu::Extent3d {
766 width: mip_size.width,
767 height: mip_size.height,
768 depth_or_array_layers: 1,
769 },
770 );
771 queue.submit(Some(encoder.finish()));
772
773 device.poll(wgpu::Maintain::Wait);
776 }
777
778 pub async fn download_to_cpu(&self, device: &wgpu::Device, queue: &wgpu::Queue) -> DynImage {
779 let bytes_per_row_unpadded = self.texture.format().block_copy_size(None).unwrap() * self.width();
781 let bytes_per_row_padded = numerical::align(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
782 let output_buffer_size = u64::from(bytes_per_row_padded * self.height());
783 let output_buffer_desc = wgpu::BufferDescriptor {
784 size: output_buffer_size,
785 usage: wgpu::BufferUsages::COPY_DST
786 | wgpu::BufferUsages::MAP_READ,
788 label: None,
789 mapped_at_creation: false,
790 };
791
792 let output_buffer = device.create_buffer(&output_buffer_desc);
793
794 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
796 encoder.copy_texture_to_buffer(
797 wgpu::ImageCopyTexture {
798 aspect: wgpu::TextureAspect::All,
799 texture: &self.texture,
800 mip_level: 0,
801 origin: wgpu::Origin3d::ZERO,
802 },
803 wgpu::ImageCopyBuffer {
804 buffer: &output_buffer,
805 layout: wgpu::ImageDataLayout {
806 offset: 0,
807 bytes_per_row: Some(bytes_per_row_padded),
808 rows_per_image: Some(self.height()),
809 },
810 },
811 wgpu::Extent3d {
812 width: self.width(),
813 height: self.height(),
814 depth_or_array_layers: 1,
815 },
816 );
817 queue.submit(Some(encoder.finish()));
818
819 let img: Option<DynImage> = {
830 let buffer_slice = output_buffer.slice(..);
831
832 let (tx, rx) = futures_intrusive::channel::shared::oneshot_channel();
837 buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
838 tx.send(result).unwrap();
839 });
840 device.poll(wgpu::Maintain::Wait);
841 rx.receive().await.unwrap().unwrap();
842
843 let data = buffer_slice.get_mapped_range();
844
845 let data_unpadded = Texture::remove_padding(data.as_bytes(), bytes_per_row_unpadded, bytes_per_row_padded, self.height());
848
849 let w = self.width();
852 let h = self.height();
853 match self.texture.format() {
854 TextureFormat::Rgba8Unorm => ImageBuffer::from_raw(w, h, data_unpadded.to_vec()).map(DynImage::ImageRgba8),
855 TextureFormat::Bgra8Unorm => {
856 let bgra_data = data_unpadded.to_vec();
857 let mut rgba_data = bgra_data.clone();
859 for chunk in rgba_data.chunks_exact_mut(4) {
860 chunk.swap(0, 2); }
862 ImageBuffer::from_raw(w, h, rgba_data).map(DynImage::ImageRgba8)
863 }
864 TextureFormat::Rgba32Float => ImageBuffer::from_raw(w, h, numerical::u8_to_f32_vec(&data_unpadded)).map(DynImage::ImageRgba32F),
865 TextureFormat::Depth32Float => ImageBuffer::from_raw(w, h, numerical::u8_to_f32_vec(&data_unpadded)).map(DynImage::ImageLuma32F),
866 x => panic!("Texture format not implemented! {x:?}"),
867 }
868 };
869 output_buffer.unmap();
870 img.unwrap()
871 }
872
873 pub fn remove_padding(buffer: &[u8], bytes_per_row_unpadded: u32, bytes_per_row_padded: u32, nr_rows: u32) -> Cow<'_, [u8]> {
874 if bytes_per_row_padded == bytes_per_row_unpadded {
879 return Cow::Borrowed(buffer);
880 }
881
882 let mut unpadded_buffer = Vec::with_capacity((bytes_per_row_unpadded * nr_rows) as _);
883
884 for row in 0..nr_rows {
885 let offset = (bytes_per_row_padded * row) as usize;
886 unpadded_buffer.extend_from_slice(&buffer[offset..(offset + bytes_per_row_unpadded as usize)]);
887 }
888
889 unpadded_buffer.into()
890 }
891
892 pub fn create_bind_group_layout(device: &wgpu::Device, binding_tex: u32, binding_sampler: u32) -> wgpu::BindGroupLayout {
893 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
894 entries: &[
895 wgpu::BindGroupLayoutEntry {
896 binding: binding_tex, visibility: wgpu::ShaderStages::FRAGMENT,
898 ty: wgpu::BindingType::Texture {
899 multisampled: false,
900 view_dimension: wgpu::TextureViewDimension::D2,
901 sample_type: wgpu::TextureSampleType::Float { filterable: true },
902 },
903 count: None,
904 },
905 wgpu::BindGroupLayoutEntry {
906 binding: binding_sampler, visibility: wgpu::ShaderStages::FRAGMENT,
908 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
909 count: None,
910 },
911 ],
912 label: Some("texture_bind_group_layout"),
913 })
914 }
915 #[must_use]
916 pub fn depth_linearize(&self, device: &wgpu::Device, queue: &wgpu::Queue, near: f32, far: f32) -> DynImage {
917 assert!(
919 !(self.texture.sample_count() > 1 && self.texture.format() == TextureFormat::Depth32Float),
920 "InvalidSampleCount: Depth maps not supported for MSAA sample count {} (Use a config to set msaa_nr_samples as 1)",
921 self.texture.sample_count()
922 );
923
924 let dynamic_img = pollster::block_on(self.download_to_cpu(device, queue));
925 let w = dynamic_img.width();
926 let h = dynamic_img.height();
927 let c = dynamic_img.channels();
928 assert!(c == 1, "Depth maps should have only 1 channel");
929
930 let linearized_img = match dynamic_img {
931 DynImage::ImageLuma32F(v) => {
932 let img_vec_ndc = v.to_vec();
933 let img_vec: Vec<f32> = img_vec_ndc.iter().map(|&x| numerical::linearize_depth_reverse_z(x, near, far)).collect();
934 DynImage::ImageLuma32F(ImageBuffer::from_raw(w, h, img_vec).unwrap())
935 }
936 _ => panic!("Texture format not implemented for remap (Only for depths)!"),
937 };
938 linearized_img
939 }
940
941 pub fn create_bind_group(&self, device: &wgpu::Device, binding_tex: u32, binding_sampler: u32) -> wgpu::BindGroup {
942 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
945 layout: &Self::create_bind_group_layout(device, binding_tex, binding_sampler),
946 entries: &[
947 wgpu::BindGroupEntry {
948 binding: binding_tex,
949 resource: wgpu::BindingResource::TextureView(&self.view),
950 },
951 wgpu::BindGroupEntry {
952 binding: binding_sampler,
953 resource: wgpu::BindingResource::Sampler(&self.sampler),
954 },
955 ],
956 label: Some("bind_group"),
957 });
958 bind_group
959 }
960
961 pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
962 let format = self.texture.format();
964 let usage = self.texture.usage();
965 let mut new = Self::new(device, width, height, format, usage, self.tex_params);
966 std::mem::swap(self, &mut new);
967 }
968
969 pub fn create_default_texture(device: &wgpu::Device, queue: &wgpu::Queue) -> Self {
971 let width = 4;
978 let height = 4;
979
980 let mut img_data: Vec<u8> = Vec::new();
981 for _ in 0..height {
982 for _ in 0..width {
983 img_data.push(255);
985 img_data.push(0);
986 img_data.push(0);
987 img_data.push(0);
988 }
989 }
990
991 let size = wgpu::Extent3d {
995 width,
996 height,
997 depth_or_array_layers: 1,
998 };
999 let format = wgpu::TextureFormat::Rgba8UnormSrgb;
1001 let desc = wgpu::TextureDescriptor {
1002 label: None,
1003 size,
1004 mip_level_count: 1,
1005 sample_count: 1,
1006 dimension: wgpu::TextureDimension::D2,
1007 format,
1008 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
1009 view_formats: &[],
1010 };
1011 let tex_params = TexParams::from_desc(&desc);
1012 let texture = device.create_texture_with_data(queue, &desc, wgpu::util::TextureDataOrder::LayerMajor, img_data.as_slice());
1013
1014 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
1015 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
1016 address_mode_u: wgpu::AddressMode::ClampToEdge,
1017 address_mode_v: wgpu::AddressMode::ClampToEdge,
1018 address_mode_w: wgpu::AddressMode::ClampToEdge,
1019 mag_filter: wgpu::FilterMode::Linear,
1020 min_filter: wgpu::FilterMode::Nearest,
1021 mipmap_filter: wgpu::FilterMode::Nearest,
1022 ..Default::default()
1023 });
1024
1025 Self {
1026 texture,
1027 view,
1028 sampler,
1029 tex_params, }
1032 }
1033
1034 pub fn create_default_cubemap(device: &wgpu::Device, queue: &wgpu::Queue) -> Self {
1035 let width = 4;
1042 let height = 4;
1043
1044 let mut img_data: Vec<u8> = Vec::new();
1045 for _ in 0..6 {
1046 for _ in 0..height {
1047 for _ in 0..width {
1048 img_data.push(255);
1050 img_data.push(0);
1051 img_data.push(0);
1052 img_data.push(0);
1053 }
1054 }
1055 }
1056
1057 let size = wgpu::Extent3d {
1058 width,
1059 height,
1060 depth_or_array_layers: 6,
1061 };
1062 let format = wgpu::TextureFormat::Rgba8UnormSrgb;
1064 let desc = wgpu::TextureDescriptor {
1065 label: None,
1066 size,
1067 mip_level_count: 1,
1068 sample_count: 1,
1069 dimension: wgpu::TextureDimension::D2,
1070 format,
1071 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
1072 view_formats: &[],
1073 };
1074 let tex_params = TexParams::from_desc(&desc);
1075 let texture = device.create_texture_with_data(queue, &desc, wgpu::util::TextureDataOrder::LayerMajor, img_data.as_slice());
1076
1077 let view = texture.create_view(&wgpu::TextureViewDescriptor {
1078 dimension: Some(wgpu::TextureViewDimension::Cube),
1079 ..Default::default()
1080 });
1081 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
1082 address_mode_u: wgpu::AddressMode::ClampToEdge,
1083 address_mode_v: wgpu::AddressMode::ClampToEdge,
1084 address_mode_w: wgpu::AddressMode::ClampToEdge,
1085 mag_filter: wgpu::FilterMode::Linear,
1086 min_filter: wgpu::FilterMode::Linear,
1087 mipmap_filter: wgpu::FilterMode::Linear,
1088 ..Default::default()
1089 });
1090
1091 Self {
1092 texture,
1093 view,
1094 sampler,
1095 tex_params, }
1098 }
1099
1100 pub fn width(&self) -> u32 {
1101 self.texture.width()
1102 }
1103 pub fn height(&self) -> u32 {
1104 self.texture.height()
1105 }
1106 pub fn extent(&self) -> wgpu::Extent3d {
1107 wgpu::Extent3d {
1108 width: self.width(),
1109 height: self.height(),
1110 depth_or_array_layers: 1,
1111 }
1112 }
1113 }