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 pub scale_factor: u32,
22}
23impl Default for TexParams {
24 fn default() -> Self {
25 Self {
26 sample_count: 1,
27 mip_level_count: 1,
28 scale_factor: 1,
29 }
30 }
31}
32impl TexParams {
33 pub fn from_desc(desc: &wgpu::TextureDescriptor) -> Self {
34 Self {
35 sample_count: desc.sample_count,
36 mip_level_count: desc.mip_level_count,
37 scale_factor: 1,
38 }
39 }
40 pub fn apply(&self, desc: &mut wgpu::TextureDescriptor) {
41 desc.sample_count = self.sample_count;
42 desc.mip_level_count = self.mip_level_count;
43 }
44}
45
46pub struct Texture {
47 pub texture: wgpu::Texture,
48 pub view: wgpu::TextureView,
49 pub sampler: wgpu::Sampler, pub tex_params: TexParams,
54}
55
56impl Texture {
57 pub fn new(
58 device: &wgpu::Device,
59 width: u32,
60 height: u32,
61 format: wgpu::TextureFormat,
62 usage: wgpu::TextureUsages,
63 tex_params: TexParams,
64 ) -> Self {
65 debug!("New texture");
66 let mut texture_desc = wgpu::TextureDescriptor {
68 size: wgpu::Extent3d {
69 width,
70 height,
71 depth_or_array_layers: 1,
72 },
73 mip_level_count: 1,
74 sample_count: 1,
75 dimension: wgpu::TextureDimension::D2,
76 format,
77 usage,
79 label: None,
80 view_formats: &[],
81 };
82 tex_params.apply(&mut texture_desc);
83
84 let texture = device.create_texture(&texture_desc);
85 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
86 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
87 address_mode_u: wgpu::AddressMode::ClampToEdge,
88 address_mode_v: wgpu::AddressMode::ClampToEdge,
89 address_mode_w: wgpu::AddressMode::ClampToEdge,
90 mag_filter: wgpu::FilterMode::Linear,
91 min_filter: wgpu::FilterMode::Linear,
92 mipmap_filter: wgpu::FilterMode::Linear,
93 ..Default::default()
94 });
95
96 Self {
97 texture,
98 view,
99 sampler,
100 tex_params,
101 }
105 }
106
107 pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bytes: &[u8], label: &str) -> Self {
110 let img = image::load_from_memory(bytes).unwrap();
111 Self::from_image(device, queue, &img, Some(label))
112 }
113
114 pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, img: &image::DynamicImage, label: Option<&str>) -> Self {
115 let rgba = img.to_rgba8();
116 let dimensions = img.dimensions();
117
118 let size = wgpu::Extent3d {
119 width: dimensions.0,
120 height: dimensions.1,
121 depth_or_array_layers: 1,
122 };
123 let format = wgpu::TextureFormat::Rgba8UnormSrgb;
124 let desc = wgpu::TextureDescriptor {
125 label,
126 size,
127 mip_level_count: 1,
128 sample_count: 1,
129 dimension: wgpu::TextureDimension::D2,
130 format,
131 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
132 view_formats: &[],
133 };
134 let tex_params = TexParams::from_desc(&desc);
135 let texture = device.create_texture(&desc);
136
137 queue.write_texture(
138 wgpu::ImageCopyTexture {
139 aspect: wgpu::TextureAspect::All,
140 texture: &texture,
141 mip_level: 0,
142 origin: wgpu::Origin3d::ZERO,
143 },
144 &rgba,
145 wgpu::ImageDataLayout {
146 offset: 0,
147 bytes_per_row: Some(4 * dimensions.0),
148 rows_per_image: Some(dimensions.1),
149 },
150 size,
151 );
152
153 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
154 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
155 address_mode_u: wgpu::AddressMode::ClampToEdge,
156 address_mode_v: wgpu::AddressMode::ClampToEdge,
157 address_mode_w: wgpu::AddressMode::ClampToEdge,
158 mag_filter: wgpu::FilterMode::Linear,
159 min_filter: wgpu::FilterMode::Nearest,
160 mipmap_filter: wgpu::FilterMode::Nearest,
161 ..Default::default()
162 });
163
164 Self {
165 texture,
166 view,
167 sampler,
168 tex_params, }
172 }
173
174 pub fn from_path(path: &str, device: &wgpu::Device, queue: &wgpu::Queue, is_srgb: bool) -> Self {
181 let img = image::ImageReader::open(path).unwrap().decode().unwrap();
183 Self::from_img(
184 &img.try_into().unwrap(),
185 device,
186 queue,
187 is_srgb,
188 true,
189 false, None,
191 None,
192 )
193 }
194
195 #[allow(clippy::too_many_lines)]
199 #[allow(clippy::too_many_arguments)]
200 pub fn from_img(
201 img: &DynImage,
202 device: &wgpu::Device,
203 queue: &wgpu::Queue,
204 is_srgb: bool,
205 generate_mipmaps: bool,
206 mipmap_generation_cpu: bool,
207 staging_buffer: Option<&Buffer>,
208 mipmaper: Option<&RenderMipmapGenerator>,
209 ) -> Self {
210 let dimensions = img.dimensions();
211 let nr_channels = img.color().channel_count();
212 let bytes_per_channel = img.color().bytes_per_pixel() / nr_channels;
213 assert!(bytes_per_channel == 1, "We are only supporting textures which have 1 byte per channel.");
214 let img_vec;
216 let img_buf = match nr_channels {
217 1 | 2 | 4 => img.as_bytes(),
218 3 => {
219 img_vec = img.to_rgba8().into_vec();
220 img_vec.as_bytes()
221 }
222 _ => panic!("Format with more than 4 channels not supported"),
223 };
224
225 let tex_format = Self::format_from_img(img, is_srgb);
226
227 let size = wgpu::Extent3d {
228 width: dimensions.0,
229 height: dimensions.1,
230 depth_or_array_layers: 1,
231 };
232 let mut nr_mip_maps = 1;
233 let mut usages = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST;
234 if generate_mipmaps {
235 nr_mip_maps = size.max_mips(wgpu::TextureDimension::D2);
236 }
237 if mipmaper.is_some() && generate_mipmaps {
238 usages |= RenderMipmapGenerator::required_usage();
239 }
240
241 let desc = wgpu::TextureDescriptor {
242 label: None,
243 size,
244 mip_level_count: nr_mip_maps,
245 sample_count: 1,
246 dimension: wgpu::TextureDimension::D2,
247 format: tex_format,
248 usage: usages,
249 view_formats: &[],
250 };
251 let tex_params = TexParams::from_desc(&desc);
252
253 let texture = device.create_texture(&desc); Self::upload_single_mip(&texture, device, queue, &desc, img_buf, staging_buffer, 0);
256
257 if generate_mipmaps {
259 Self::generate_mipmaps(
260 img,
261 &texture,
262 device,
263 queue,
264 &desc,
265 nr_mip_maps,
266 mipmap_generation_cpu,
267 staging_buffer,
268 mipmaper,
269 );
270 }
271
272 let view = texture.create_view(&wgpu::TextureViewDescriptor {
274 mip_level_count: Some(nr_mip_maps),
275 ..Default::default()
276 });
277 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
278 address_mode_u: wgpu::AddressMode::ClampToEdge,
279 address_mode_v: wgpu::AddressMode::ClampToEdge,
280 address_mode_w: wgpu::AddressMode::ClampToEdge,
281 mag_filter: wgpu::FilterMode::Linear,
282 min_filter: wgpu::FilterMode::Nearest,
283 mipmap_filter: wgpu::FilterMode::Nearest,
284 ..Default::default()
285 });
286
287 Self {
288 texture,
289 view,
290 sampler,
291 tex_params, }
295 }
296
297 #[allow(clippy::too_many_arguments)]
300 pub fn update_from_img(
301 &mut self,
302 img: &DynImage,
303 device: &wgpu::Device,
304 queue: &wgpu::Queue,
305 is_srgb: bool,
306 generate_mipmaps: bool,
307 mipmap_generation_cpu: bool,
308 staging_buffer: Option<&Buffer>,
309 mipmaper: Option<&RenderMipmapGenerator>,
310 ) {
311 let nr_channels = img.color().channel_count();
313 let bytes_per_channel = img.color().bytes_per_pixel() / nr_channels;
314 assert!(bytes_per_channel == 1, "We are only supporting textures which have 1 byte per channel.");
315
316 let img_vec;
320 let img_buf = match nr_channels {
321 1 | 2 | 4 => img.as_bytes(),
322 3 => {
323 img_vec = img.to_rgba8().into_vec();
324 img_vec.as_bytes()
325 }
326 _ => panic!("Format with more than 4 channels not supported"),
327 };
328
329 let size = Self::extent_from_img(img);
330 let tex_format = Self::format_from_img(img, is_srgb);
331 let mut nr_mip_maps = 1;
332 let mut usages = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST;
333 if generate_mipmaps {
334 nr_mip_maps = size.max_mips(wgpu::TextureDimension::D2);
335 }
336 if mipmaper.is_some() && generate_mipmaps {
337 usages |= RenderMipmapGenerator::required_usage();
338 }
339
340 let desc = wgpu::TextureDescriptor {
341 label: None,
342 size,
343 mip_level_count: nr_mip_maps,
344 sample_count: 1,
345 dimension: wgpu::TextureDimension::D2,
346 format: tex_format,
347 usage: usages,
348 view_formats: &[],
349 };
350
351 Self::upload_single_mip(&self.texture, device, queue, &desc, img_buf, staging_buffer, 0);
352
353 if generate_mipmaps {
355 Self::generate_mipmaps(
356 img,
357 &self.texture,
358 device,
359 queue,
360 &desc,
361 nr_mip_maps,
362 mipmap_generation_cpu,
363 staging_buffer,
364 mipmaper,
365 );
366 }
367
368 let view = self.texture.create_view(&wgpu::TextureViewDescriptor {
370 mip_level_count: Some(nr_mip_maps),
371 ..Default::default()
372 });
373
374 self.view = view;
376 }
377
378 #[allow(clippy::too_many_arguments)]
379 pub fn generate_mipmaps(
380 img: &DynImage,
381 texture: &wgpu::Texture,
382 device: &wgpu::Device,
383 queue: &wgpu::Queue,
384 desc: &wgpu::TextureDescriptor,
385 nr_mip_maps: u32,
386 mipmap_generation_cpu: bool,
387 staging_buffer: Option<&Buffer>,
388 mipmaper: Option<&RenderMipmapGenerator>,
389 ) {
390 let nr_channels = img.color().channel_count();
391 if mipmap_generation_cpu {
392 let mut img_mip = DynImage::new(1, 1, image::ColorType::L8);
395 for mip_lvl in 1..nr_mip_maps {
396 let mip_size = desc.mip_level_size(mip_lvl).unwrap();
397 let prev_img_mip = if mip_lvl == 1 { img } else { &img_mip };
398 img_mip = prev_img_mip.resize_exact(mip_size.width, mip_size.height, FilterType::Triangle);
399 debug!("mip lvl {} has size {:?}", mip_lvl, mip_size);
400
401 let img_mip_vec;
402 let img_mip_buf = match nr_channels {
403 1 | 2 | 4 => img_mip.as_bytes(),
404 3 => {
405 img_mip_vec = img_mip.to_rgba8().into_vec();
406 img_mip_vec.as_bytes()
407 }
408 _ => panic!("Format with more than 4 channels not supported"),
409 };
410
411 Self::upload_single_mip(texture, device, queue, desc, img_mip_buf, staging_buffer, mip_lvl);
412 }
413 } else {
414 if let Some(mipmaper) = mipmaper {
416 let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor::default());
417 mipmaper.generate(device, &mut encoder, texture, desc).unwrap();
418 queue.submit(std::iter::once(encoder.finish()));
419 } else {
420 warn!("Couldn't generate mipmaps since the mipmapper was not provided");
421 }
422 }
423 }
424
425 pub fn extent_from_img(img: &DynImage) -> wgpu::Extent3d {
426 let dimensions = img.dimensions();
427 wgpu::Extent3d {
428 width: dimensions.0,
429 height: dimensions.1,
430 depth_or_array_layers: 1,
431 }
432 }
433
434 pub fn format_from_img(img: &DynImage, is_srgb: bool) -> wgpu::TextureFormat {
437 let nr_channels = img.color().channel_count();
438 let bytes_per_channel = img.color().bytes_per_pixel() / nr_channels;
439 assert!(bytes_per_channel == 1, "We are only supporting textures which have 1 byte per channel.");
440
441 let mut tex_format = match nr_channels {
443 1 => wgpu::TextureFormat::R8Unorm,
444 2 => wgpu::TextureFormat::Rg8Unorm,
445 3 | 4 => wgpu::TextureFormat::Rgba8Unorm,
446 _ => panic!("Format with more than 4 channels not supported"),
447 };
448 if is_srgb {
449 tex_format = tex_format.add_srgb_suffix();
450 }
451
452 tex_format
453 }
454
455 pub fn upload_single_mip(
460 texture: &wgpu::Texture,
461 device: &wgpu::Device,
462 queue: &wgpu::Queue,
463 desc: &wgpu::TextureDescriptor,
464 data: &[u8],
465 staging_buffer: Option<&Buffer>,
466 mip: u32,
467 ) {
468 let mut mip_size = desc.mip_level_size(mip).unwrap();
469 if desc.dimension != wgpu::TextureDimension::D3 {
471 mip_size.depth_or_array_layers = 1;
472 }
473
474 let block_size = desc.format.block_copy_size(None).unwrap_or(4);
478 let (block_width, block_height) = desc.format.block_dimensions();
479
480 let mip_physical = mip_size.physical_size(desc.format);
485
486 let width_blocks = mip_physical.width / block_width;
489 let height_blocks = mip_physical.height / block_height;
490
491 let bytes_per_row = width_blocks * block_size;
492 if let Some(staging_buffer) = staging_buffer {
498 warn!("Using slow CPU->GPU transfer for texture upload. Might use less memory that staging buffer using by wgpu but it will be slower.");
499
500 let bytes_per_row_unpadded = texture.format().block_copy_size(None).unwrap() * mip_size.width;
502 let bytes_per_row_padded = numerical::align(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
503
504 let slice_size = numerical::align(u32::try_from(data.len()).unwrap(), u32::try_from(wgpu::COPY_BUFFER_ALIGNMENT).unwrap());
508 {
509 let buffer_slice = staging_buffer.buffer.slice(0..u64::from(slice_size));
510 let (tx, rx) = futures::channel::oneshot::channel();
513 buffer_slice.map_async(wgpu::MapMode::Write, move |result| {
514 tx.send(result).unwrap();
515 });
516 device.poll(wgpu::Maintain::Wait);
517 rx.block_on().unwrap().unwrap();
518 let mut buf_data = buffer_slice.get_mapped_range_mut();
519
520 buf_data.get_mut(0..data.len()).unwrap().clone_from_slice(data);
522 }
523
524 staging_buffer.buffer.unmap();
526
527 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
529 encoder.copy_buffer_to_texture(
530 wgpu::ImageCopyBuffer {
531 buffer: &staging_buffer.buffer,
532 layout: wgpu::ImageDataLayout {
533 offset: 0,
534 bytes_per_row: Some(bytes_per_row_padded),
535 rows_per_image: Some(mip_size.height),
536 },
537 },
538 wgpu::ImageCopyTexture {
539 aspect: wgpu::TextureAspect::All,
540 texture,
541 mip_level: mip,
542 origin: wgpu::Origin3d::ZERO,
543 },
544 wgpu::Extent3d {
545 width: mip_size.width,
546 height: mip_size.height,
547 depth_or_array_layers: 1,
548 },
549 );
550 queue.submit(Some(encoder.finish()));
551
552 } else {
557 queue.write_texture(
560 wgpu::ImageCopyTexture {
561 texture,
562 mip_level: mip,
563 origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },
564 aspect: wgpu::TextureAspect::All,
565 },
566 data,
567 wgpu::ImageDataLayout {
568 offset: 0,
569 bytes_per_row: Some(bytes_per_row),
570 rows_per_image: Some(height_blocks),
571 },
572 mip_physical,
573 );
574 }
575
576 }
578
579 pub fn upload_all_mips(
585 texture: &wgpu::Texture,
586 device: &wgpu::Device,
587 queue: &wgpu::Queue,
588 desc: &wgpu::TextureDescriptor,
589 data: &[u8],
590 staging_buffer: Option<&Buffer>,
591 ) {
592 let block_size = desc.format.block_copy_size(None).unwrap_or(4);
596 let (block_width, block_height) = desc.format.block_dimensions();
597 let layer_iterations = desc.array_layer_count();
598
599 let (min_mip, max_mip) = (0, desc.mip_level_count);
600
601 let mut binary_offset = 0;
602 for layer in 0..layer_iterations {
603 for mip in min_mip..max_mip {
604 let mut mip_size = desc.mip_level_size(mip).unwrap();
605 if desc.dimension != wgpu::TextureDimension::D3 {
607 mip_size.depth_or_array_layers = 1;
608 }
609
610 let mip_physical = mip_size.physical_size(desc.format);
615
616 let width_blocks = mip_physical.width / block_width;
619 let height_blocks = mip_physical.height / block_height;
620
621 let bytes_per_row = width_blocks * block_size;
622 let data_size = bytes_per_row * height_blocks * mip_size.depth_or_array_layers;
623
624 let end_offset = binary_offset + data_size as usize;
625
626 if let Some(staging_buffer) = staging_buffer {
627 warn!("Using slow CPU->GPU transfer for texture upload. Might use less memory that staging buffer using by wgpu but it will be slower.");
628
629 let bytes_per_row_unpadded = texture.format().block_copy_size(None).unwrap() * mip_size.width;
631 let bytes_per_row_padded = numerical::align(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
632
633 let data_to_copy = &data[binary_offset..end_offset];
636 let slice_size = numerical::align(
638 u32::try_from(data_to_copy.len()).unwrap(),
639 u32::try_from(wgpu::COPY_BUFFER_ALIGNMENT).unwrap(),
640 );
641 {
642 let buffer_slice = staging_buffer.buffer.slice(0..u64::from(slice_size));
643 let (tx, rx) = futures::channel::oneshot::channel();
646 buffer_slice.map_async(wgpu::MapMode::Write, move |result| {
647 tx.send(result).unwrap();
648 });
649 device.poll(wgpu::Maintain::Wait);
650 rx.block_on().unwrap().unwrap();
651 let mut buf_data = buffer_slice.get_mapped_range_mut();
652
653 buf_data.get_mut(0..data_to_copy.len()).unwrap().clone_from_slice(data_to_copy);
655 }
656
657 staging_buffer.buffer.unmap();
659
660 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
662 encoder.copy_buffer_to_texture(
663 wgpu::ImageCopyBuffer {
664 buffer: &staging_buffer.buffer,
665 layout: wgpu::ImageDataLayout {
666 offset: 0,
667 bytes_per_row: Some(bytes_per_row_padded),
668 rows_per_image: Some(mip_size.height),
669 },
670 },
671 wgpu::ImageCopyTexture {
672 aspect: wgpu::TextureAspect::All,
673 texture,
674 mip_level: mip,
675 origin: wgpu::Origin3d::ZERO,
676 },
677 wgpu::Extent3d {
678 width: mip_size.width,
679 height: mip_size.height,
680 depth_or_array_layers: 1,
681 },
682 );
683 queue.submit(Some(encoder.finish()));
684
685 } else {
691 queue.write_texture(
694 wgpu::ImageCopyTexture {
695 texture,
696 mip_level: mip,
697 origin: wgpu::Origin3d { x: 0, y: 0, z: layer },
698 aspect: wgpu::TextureAspect::All,
699 },
700 &data[binary_offset..end_offset],
701 wgpu::ImageDataLayout {
702 offset: 0,
703 bytes_per_row: Some(bytes_per_row),
704 rows_per_image: Some(height_blocks),
705 },
706 mip_physical,
707 );
708 }
709
710 binary_offset = end_offset;
711 }
712 }
713 }
714
715 pub fn upload_from_cpu_with_staging_buffer(
716 texture: &wgpu::Texture,
717 device: &wgpu::Device,
718 queue: &wgpu::Queue,
719 desc: &wgpu::TextureDescriptor,
720 data: &[u8],
721 staging_buffer: &Buffer,
722 mip_lvl: u32,
723 ) {
724 let mip_size = desc.mip_level_size(mip_lvl).unwrap();
725
726 {
729 let buffer_slice = staging_buffer.buffer.slice(0..data.len() as u64);
730 let (tx, rx) = futures::channel::oneshot::channel();
733 buffer_slice.map_async(wgpu::MapMode::Write, move |result| {
734 tx.send(result).unwrap();
735 });
736 device.poll(wgpu::Maintain::Wait);
737 rx.block_on().unwrap().unwrap();
738 let mut buf_data = buffer_slice.get_mapped_range_mut();
739
740 buf_data.clone_from_slice(data);
742 }
743
744 staging_buffer.buffer.unmap();
746
747 let bytes_per_row_unpadded = texture.format().block_copy_size(None).unwrap() * mip_size.width;
749 let bytes_per_row_padded = numerical::align(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
750
751 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
753 encoder.copy_buffer_to_texture(
754 wgpu::ImageCopyBuffer {
755 buffer: &staging_buffer.buffer,
756 layout: wgpu::ImageDataLayout {
757 offset: 0,
758 bytes_per_row: Some(bytes_per_row_padded),
759 rows_per_image: Some(mip_size.height),
760 },
761 },
762 wgpu::ImageCopyTexture {
763 aspect: wgpu::TextureAspect::All,
764 texture,
765 mip_level: mip_lvl,
766 origin: wgpu::Origin3d::ZERO,
767 },
768 wgpu::Extent3d {
769 width: mip_size.width,
770 height: mip_size.height,
771 depth_or_array_layers: 1,
772 },
773 );
774 queue.submit(Some(encoder.finish()));
775
776 device.poll(wgpu::Maintain::Wait);
779 }
780
781 pub async fn download_to_cpu(&self, device: &wgpu::Device, queue: &wgpu::Queue, aspect: wgpu::TextureAspect) -> DynImage {
786 let bytes_per_row_unpadded = self.texture.format().block_copy_size(None).unwrap_or(4) * self.width();
788 let bytes_per_row_padded = numerical::align(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
789 let output_buffer_size = u64::from(bytes_per_row_padded * self.height());
790 let output_buffer_desc = wgpu::BufferDescriptor {
791 size: output_buffer_size,
792 usage: wgpu::BufferUsages::COPY_DST
793 | wgpu::BufferUsages::MAP_READ,
795 label: None,
796 mapped_at_creation: false,
797 };
798
799 let output_buffer = device.create_buffer(&output_buffer_desc);
800
801 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
803 encoder.copy_texture_to_buffer(
804 wgpu::ImageCopyTexture {
805 aspect,
806 texture: &self.texture,
807 mip_level: 0,
808 origin: wgpu::Origin3d::ZERO,
809 },
810 wgpu::ImageCopyBuffer {
811 buffer: &output_buffer,
812 layout: wgpu::ImageDataLayout {
813 offset: 0,
814 bytes_per_row: Some(bytes_per_row_padded),
815 rows_per_image: Some(self.height()),
816 },
817 },
818 wgpu::Extent3d {
819 width: self.width(),
820 height: self.height(),
821 depth_or_array_layers: 1,
822 },
823 );
824 queue.submit(Some(encoder.finish()));
825
826 let img: Option<DynImage> = {
837 let buffer_slice = output_buffer.slice(..);
838
839 let (tx, rx) = futures_intrusive::channel::shared::oneshot_channel();
844 buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
845 tx.send(result).unwrap();
846 });
847 device.poll(wgpu::Maintain::Wait);
848 rx.receive().await.unwrap().unwrap();
849
850 let data = buffer_slice.get_mapped_range();
851
852 let data_unpadded = Texture::remove_padding(data.as_bytes(), bytes_per_row_unpadded, bytes_per_row_padded, self.height());
855
856 let w = self.width();
859 let h = self.height();
860 match self.texture.format() {
861 TextureFormat::Rgba8Unorm => ImageBuffer::from_raw(w, h, data_unpadded.to_vec()).map(DynImage::ImageRgba8),
862 TextureFormat::Bgra8Unorm => {
863 let bgra_data = data_unpadded.to_vec();
864 let mut rgba_data = bgra_data.clone();
866 for chunk in rgba_data.chunks_exact_mut(4) {
867 chunk.swap(0, 2); }
869 ImageBuffer::from_raw(w, h, rgba_data).map(DynImage::ImageRgba8)
870 }
871 TextureFormat::Rgba32Float => ImageBuffer::from_raw(w, h, numerical::u8_to_f32_vec(&data_unpadded)).map(DynImage::ImageRgba32F),
872 TextureFormat::Depth32Float | TextureFormat::Depth32FloatStencil8 => {
873 ImageBuffer::from_raw(w, h, numerical::u8_to_f32_vec(&data_unpadded)).map(DynImage::ImageLuma32F)
874 }
875 x => panic!("Texture format not implemented! {x:?}"),
876 }
877 };
878 output_buffer.unmap();
879 img.unwrap()
880 }
881
882 pub async fn download_pixel_to_cpu(&self, device: &wgpu::Device, queue: &wgpu::Queue, aspect: wgpu::TextureAspect, x: u32, y: u32) -> DynImage {
883 let output_buffer_desc = wgpu::BufferDescriptor {
885 label: Some("ID Readback Buffer"),
886 size: 4, usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
888 mapped_at_creation: false,
889 };
890 let output_buffer = device.create_buffer(&output_buffer_desc);
891
892 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
893 label: Some("ID Readback Encoder"),
894 });
895
896 encoder.copy_texture_to_buffer(
900 wgpu::ImageCopyTexture {
901 aspect,
902 texture: &self.texture,
903 mip_level: 0,
904 origin: wgpu::Origin3d { x, y, z: 0 },
905 },
906 wgpu::ImageCopyBuffer {
907 buffer: &output_buffer,
908 layout: wgpu::ImageDataLayout {
909 offset: 0,
910 bytes_per_row: None,
911 rows_per_image: None,
912 },
913 },
914 wgpu::Extent3d {
915 width: 1,
916 height: 1,
917 depth_or_array_layers: 1,
918 },
919 );
920
921 queue.submit(Some(encoder.finish()));
922
923 let pixel: Option<DynImage> = {
924 let buffer_slice = output_buffer.slice(..);
925
926 let (tx, rx) = futures_intrusive::channel::shared::oneshot_channel();
927 buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
928 tx.send(result).unwrap();
929 });
930 device.poll(wgpu::Maintain::Wait);
931 rx.receive().await.unwrap().unwrap();
932
933 let data = buffer_slice.get_mapped_range();
934 match self.texture.format() {
935 TextureFormat::Rgba8Unorm => {
936 let single_pixel_bytes = *data.to_vec().first().unwrap();
938 ImageBuffer::from_raw(1, 1, [single_pixel_bytes].to_vec()).map(DynImage::ImageLuma8)
939 }
940 x => panic!("Texture format not implemented! {x:?}"),
941 }
942 };
943 output_buffer.unmap();
944 pixel.unwrap()
945 }
946
947 pub fn remove_padding(buffer: &[u8], bytes_per_row_unpadded: u32, bytes_per_row_padded: u32, nr_rows: u32) -> Cow<'_, [u8]> {
948 if bytes_per_row_padded == bytes_per_row_unpadded {
953 return Cow::Borrowed(buffer);
954 }
955
956 let mut unpadded_buffer = Vec::with_capacity((bytes_per_row_unpadded * nr_rows) as _);
957
958 for row in 0..nr_rows {
959 let offset = (bytes_per_row_padded * row) as usize;
960 unpadded_buffer.extend_from_slice(&buffer[offset..(offset + bytes_per_row_unpadded as usize)]);
961 }
962
963 unpadded_buffer.into()
964 }
965
966 pub fn create_bind_group_layout(device: &wgpu::Device, binding_tex: u32, binding_sampler: u32) -> wgpu::BindGroupLayout {
967 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
968 entries: &[
969 wgpu::BindGroupLayoutEntry {
970 binding: binding_tex, visibility: wgpu::ShaderStages::FRAGMENT,
972 ty: wgpu::BindingType::Texture {
973 multisampled: false,
974 view_dimension: wgpu::TextureViewDimension::D2,
975 sample_type: wgpu::TextureSampleType::Float { filterable: true },
976 },
977 count: None,
978 },
979 wgpu::BindGroupLayoutEntry {
980 binding: binding_sampler, visibility: wgpu::ShaderStages::FRAGMENT,
982 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
983 count: None,
984 },
985 ],
986 label: Some("texture_bind_group_layout"),
987 })
988 }
989 #[must_use]
990 pub fn depth_linearize(&self, device: &wgpu::Device, queue: &wgpu::Queue, near: f32, far: f32) -> DynImage {
991 assert!(
993 !(self.texture.sample_count() > 1 && self.texture.format() == TextureFormat::Depth32Float),
994 "InvalidSampleCount: Depth maps not supported for MSAA sample count {} (Use a config to set msaa_nr_samples as 1)",
995 self.texture.sample_count()
996 );
997
998 let aspect = wgpu::TextureAspect::DepthOnly;
1000 let dynamic_img = pollster::block_on(self.download_to_cpu(device, queue, aspect));
1001 let w = dynamic_img.width();
1002 let h = dynamic_img.height();
1003 let c = dynamic_img.channels();
1004 assert!(c == 1, "Depth maps should have only 1 channel");
1005
1006 let linearized_img = match dynamic_img {
1007 DynImage::ImageLuma32F(v) => {
1008 let img_vec_ndc = v.to_vec();
1009 let img_vec: Vec<f32> = img_vec_ndc.iter().map(|&x| numerical::linearize_depth_reverse_z(x, near, far)).collect();
1010 DynImage::ImageLuma32F(ImageBuffer::from_raw(w, h, img_vec).unwrap())
1011 }
1012 _ => panic!("Texture format not implemented for remap (Only for depths)!"),
1013 };
1014 linearized_img
1015 }
1016
1017 pub fn create_bind_group(&self, device: &wgpu::Device, binding_tex: u32, binding_sampler: u32) -> wgpu::BindGroup {
1018 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
1021 layout: &Self::create_bind_group_layout(device, binding_tex, binding_sampler),
1022 entries: &[
1023 wgpu::BindGroupEntry {
1024 binding: binding_tex,
1025 resource: wgpu::BindingResource::TextureView(&self.view),
1026 },
1027 wgpu::BindGroupEntry {
1028 binding: binding_sampler,
1029 resource: wgpu::BindingResource::Sampler(&self.sampler),
1030 },
1031 ],
1032 label: Some("bind_group"),
1033 });
1034 bind_group
1035 }
1036
1037 pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
1038 let format = self.texture.format();
1040 let usage = self.texture.usage();
1041 let mut new = Self::new(device, width, height, format, usage, self.tex_params);
1042 std::mem::swap(self, &mut new);
1043 }
1044
1045 pub fn create_default_texture(device: &wgpu::Device, queue: &wgpu::Queue) -> Self {
1047 let width = 4;
1054 let height = 4;
1055
1056 let mut img_data: Vec<u8> = Vec::new();
1057 for _ in 0..height {
1058 for _ in 0..width {
1059 img_data.push(255);
1061 img_data.push(0);
1062 img_data.push(0);
1063 img_data.push(0);
1064 }
1065 }
1066
1067 let size = wgpu::Extent3d {
1071 width,
1072 height,
1073 depth_or_array_layers: 1,
1074 };
1075 let format = wgpu::TextureFormat::Rgba8UnormSrgb;
1077 let desc = wgpu::TextureDescriptor {
1078 label: None,
1079 size,
1080 mip_level_count: 1,
1081 sample_count: 1,
1082 dimension: wgpu::TextureDimension::D2,
1083 format,
1084 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
1085 view_formats: &[],
1086 };
1087 let tex_params = TexParams::from_desc(&desc);
1088 let texture = device.create_texture_with_data(queue, &desc, wgpu::util::TextureDataOrder::LayerMajor, img_data.as_slice());
1089
1090 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
1091 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
1092 address_mode_u: wgpu::AddressMode::ClampToEdge,
1093 address_mode_v: wgpu::AddressMode::ClampToEdge,
1094 address_mode_w: wgpu::AddressMode::ClampToEdge,
1095 mag_filter: wgpu::FilterMode::Linear,
1096 min_filter: wgpu::FilterMode::Nearest,
1097 mipmap_filter: wgpu::FilterMode::Nearest,
1098 ..Default::default()
1099 });
1100
1101 Self {
1102 texture,
1103 view,
1104 sampler,
1105 tex_params, }
1108 }
1109
1110 pub fn create_default_cubemap(device: &wgpu::Device, queue: &wgpu::Queue) -> Self {
1111 let width = 4;
1118 let height = 4;
1119
1120 let mut img_data: Vec<u8> = Vec::new();
1121 for _ in 0..6 {
1122 for _ in 0..height {
1123 for _ in 0..width {
1124 img_data.push(255);
1126 img_data.push(0);
1127 img_data.push(0);
1128 img_data.push(0);
1129 }
1130 }
1131 }
1132
1133 let size = wgpu::Extent3d {
1134 width,
1135 height,
1136 depth_or_array_layers: 6,
1137 };
1138 let format = wgpu::TextureFormat::Rgba8UnormSrgb;
1140 let desc = wgpu::TextureDescriptor {
1141 label: None,
1142 size,
1143 mip_level_count: 1,
1144 sample_count: 1,
1145 dimension: wgpu::TextureDimension::D2,
1146 format,
1147 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
1148 view_formats: &[],
1149 };
1150 let tex_params = TexParams::from_desc(&desc);
1151 let texture = device.create_texture_with_data(queue, &desc, wgpu::util::TextureDataOrder::LayerMajor, img_data.as_slice());
1152
1153 let view = texture.create_view(&wgpu::TextureViewDescriptor {
1154 dimension: Some(wgpu::TextureViewDimension::Cube),
1155 ..Default::default()
1156 });
1157 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
1158 address_mode_u: wgpu::AddressMode::ClampToEdge,
1159 address_mode_v: wgpu::AddressMode::ClampToEdge,
1160 address_mode_w: wgpu::AddressMode::ClampToEdge,
1161 mag_filter: wgpu::FilterMode::Linear,
1162 min_filter: wgpu::FilterMode::Linear,
1163 mipmap_filter: wgpu::FilterMode::Linear,
1164 ..Default::default()
1165 });
1166
1167 Self {
1168 texture,
1169 view,
1170 sampler,
1171 tex_params, }
1174 }
1175
1176 pub fn width(&self) -> u32 {
1177 self.texture.width()
1178 }
1179 pub fn height(&self) -> u32 {
1180 self.texture.height()
1181 }
1182 pub fn extent(&self) -> wgpu::Extent3d {
1183 wgpu::Extent3d {
1184 width: self.width(),
1185 height: self.height(),
1186 depth_or_array_layers: 1,
1187 }
1188 }
1189 }