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
46#[derive(Clone)]
47pub struct Texture {
48 pub texture: wgpu::Texture,
49 pub view: wgpu::TextureView,
50 pub sampler: wgpu::Sampler, pub tex_params: TexParams,
55}
56
57impl Texture {
58 pub fn new(
59 device: &wgpu::Device,
60 width: u32,
61 height: u32,
62 format: wgpu::TextureFormat,
63 usage: wgpu::TextureUsages,
64 tex_params: TexParams,
65 ) -> Self {
66 debug!("New texture");
67 let mut texture_desc = wgpu::TextureDescriptor {
69 size: wgpu::Extent3d {
70 width,
71 height,
72 depth_or_array_layers: 1,
73 },
74 mip_level_count: 1,
75 sample_count: 1,
76 dimension: wgpu::TextureDimension::D2,
77 format,
78 usage,
80 label: None,
81 view_formats: &[],
82 };
83 tex_params.apply(&mut texture_desc);
84
85 let texture = device.create_texture(&texture_desc);
86 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
87 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
88 address_mode_u: wgpu::AddressMode::ClampToEdge,
89 address_mode_v: wgpu::AddressMode::ClampToEdge,
90 address_mode_w: wgpu::AddressMode::ClampToEdge,
91 mag_filter: wgpu::FilterMode::Linear,
92 min_filter: wgpu::FilterMode::Linear,
93 mipmap_filter: wgpu::FilterMode::Linear,
94 ..Default::default()
95 });
96
97 Self {
98 texture,
99 view,
100 sampler,
101 tex_params,
102 }
106 }
107
108 pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bytes: &[u8], label: &str) -> Self {
111 let img = image::load_from_memory(bytes).unwrap();
112 Self::from_image(device, queue, &img, Some(label))
113 }
114
115 pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, img: &image::DynamicImage, label: Option<&str>) -> Self {
116 let rgba = img.to_rgba8();
117 let dimensions = img.dimensions();
118
119 let size = wgpu::Extent3d {
120 width: dimensions.0,
121 height: dimensions.1,
122 depth_or_array_layers: 1,
123 };
124 let format = wgpu::TextureFormat::Rgba8UnormSrgb;
125 let desc = wgpu::TextureDescriptor {
126 label,
127 size,
128 mip_level_count: 1,
129 sample_count: 1,
130 dimension: wgpu::TextureDimension::D2,
131 format,
132 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
133 view_formats: &[],
134 };
135 let tex_params = TexParams::from_desc(&desc);
136 let texture = device.create_texture(&desc);
137
138 queue.write_texture(
139 wgpu::TexelCopyTextureInfo {
140 aspect: wgpu::TextureAspect::All,
141 texture: &texture,
142 mip_level: 0,
143 origin: wgpu::Origin3d::ZERO,
144 },
145 &rgba,
146 wgpu::TexelCopyBufferLayout {
147 offset: 0,
148 bytes_per_row: Some(4 * dimensions.0),
149 rows_per_image: Some(dimensions.1),
150 },
151 size,
152 );
153
154 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
155 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
156 address_mode_u: wgpu::AddressMode::ClampToEdge,
157 address_mode_v: wgpu::AddressMode::ClampToEdge,
158 address_mode_w: wgpu::AddressMode::ClampToEdge,
159 mag_filter: wgpu::FilterMode::Linear,
160 min_filter: wgpu::FilterMode::Nearest,
161 mipmap_filter: wgpu::FilterMode::Nearest,
162 ..Default::default()
163 });
164
165 Self {
166 texture,
167 view,
168 sampler,
169 tex_params, }
173 }
174
175 pub fn from_path(path: &str, device: &wgpu::Device, queue: &wgpu::Queue, is_srgb: bool) -> Self {
182 let img = image::ImageReader::open(path).unwrap().decode().unwrap();
184 Self::from_img(
185 &img.try_into().unwrap(),
186 device,
187 queue,
188 is_srgb,
189 true,
190 false, None,
192 None,
193 )
194 .block_on()
195 .unwrap()
196 }
197
198 #[allow(clippy::missing_errors_doc)]
202 #[allow(clippy::too_many_lines)]
203 #[allow(clippy::too_many_arguments)]
204 pub async fn from_img(
205 img: &DynImage,
206 device: &wgpu::Device,
207 queue: &wgpu::Queue,
208 is_srgb: bool,
209 generate_mipmaps: bool,
210 mipmap_generation_cpu: bool,
211 staging_buffer: Option<&Buffer>,
212 mipmaper: Option<&RenderMipmapGenerator>,
213 ) -> Result<Self, Box<dyn std::error::Error>> {
214 let dimensions = img.dimensions();
215 let nr_channels = img.color().channel_count();
216 let bytes_per_channel = img.color().bytes_per_pixel() / nr_channels;
217 assert!(bytes_per_channel == 1, "We are only supporting textures which have 1 byte per channel.");
218 let img_vec;
220 let img_buf = match nr_channels {
221 1 | 2 | 4 => img.as_bytes(),
222 3 => {
223 img_vec = img.to_rgba8().into_vec();
224 img_vec.as_bytes()
225 }
226 _ => panic!("Format with more than 4 channels not supported"),
227 };
228
229 let tex_format = Self::format_from_img(img, is_srgb);
230
231 let size = wgpu::Extent3d {
232 width: dimensions.0,
233 height: dimensions.1,
234 depth_or_array_layers: 1,
235 };
236 let mut nr_mip_maps = 1;
237 let mut usages = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST;
238 if generate_mipmaps {
239 nr_mip_maps = size.max_mips(wgpu::TextureDimension::D2);
240 }
241 if mipmaper.is_some() && generate_mipmaps {
242 usages |= RenderMipmapGenerator::required_usage();
243 }
244
245 let desc = wgpu::TextureDescriptor {
246 label: None,
247 size,
248 mip_level_count: nr_mip_maps,
249 sample_count: 1,
250 dimension: wgpu::TextureDimension::D2,
251 format: tex_format,
252 usage: usages,
253 view_formats: &[],
254 };
255 let tex_params = TexParams::from_desc(&desc);
256
257 let texture = device.create_texture(&desc); Self::upload_single_mip(&texture, device, queue, &desc, img_buf, staging_buffer, 0).await?;
260
261 if generate_mipmaps {
263 Self::generate_mipmaps(
264 img,
265 &texture,
266 device,
267 queue,
268 &desc,
269 nr_mip_maps,
270 mipmap_generation_cpu,
271 staging_buffer,
272 mipmaper,
273 )
274 .await?;
275 }
276
277 let view = texture.create_view(&wgpu::TextureViewDescriptor {
279 mip_level_count: Some(nr_mip_maps),
280 ..Default::default()
281 });
282 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
283 address_mode_u: wgpu::AddressMode::ClampToEdge,
284 address_mode_v: wgpu::AddressMode::ClampToEdge,
285 address_mode_w: wgpu::AddressMode::ClampToEdge,
286 mag_filter: wgpu::FilterMode::Linear,
287 min_filter: wgpu::FilterMode::Nearest,
288 mipmap_filter: wgpu::FilterMode::Nearest,
289 ..Default::default()
290 });
291
292 Ok(Self {
293 texture,
294 view,
295 sampler,
296 tex_params, })
300 }
301
302 #[allow(clippy::missing_errors_doc)]
305 #[allow(clippy::too_many_arguments)]
306 pub async fn update_from_img(
307 &mut self,
308 img: &DynImage,
309 device: &wgpu::Device,
310 queue: &wgpu::Queue,
311 is_srgb: bool,
312 generate_mipmaps: bool,
313 mipmap_generation_cpu: bool,
314 staging_buffer: Option<&Buffer>,
315 mipmaper: Option<&RenderMipmapGenerator>,
316 ) -> Result<(), Box<dyn std::error::Error>> {
317 let nr_channels = img.color().channel_count();
319 let bytes_per_channel = img.color().bytes_per_pixel() / nr_channels;
320 assert!(bytes_per_channel == 1, "We are only supporting textures which have 1 byte per channel.");
321
322 let img_vec;
326 let img_buf = match nr_channels {
327 1 | 2 | 4 => img.as_bytes(),
328 3 => {
329 img_vec = img.to_rgba8().into_vec();
330 img_vec.as_bytes()
331 }
332 _ => panic!("Format with more than 4 channels not supported"),
333 };
334
335 let size = Self::extent_from_img(img);
336 let tex_format = Self::format_from_img(img, is_srgb);
337 let mut nr_mip_maps = 1;
338 let mut usages = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST;
339 if generate_mipmaps {
340 nr_mip_maps = size.max_mips(wgpu::TextureDimension::D2);
341 }
342 if mipmaper.is_some() && generate_mipmaps {
343 usages |= RenderMipmapGenerator::required_usage();
344 }
345
346 let desc = wgpu::TextureDescriptor {
347 label: None,
348 size,
349 mip_level_count: nr_mip_maps,
350 sample_count: 1,
351 dimension: wgpu::TextureDimension::D2,
352 format: tex_format,
353 usage: usages,
354 view_formats: &[],
355 };
356
357 Self::upload_single_mip(&self.texture, device, queue, &desc, img_buf, staging_buffer, 0).await?;
358
359 if generate_mipmaps {
361 Self::generate_mipmaps(
362 img,
363 &self.texture,
364 device,
365 queue,
366 &desc,
367 nr_mip_maps,
368 mipmap_generation_cpu,
369 staging_buffer,
370 mipmaper,
371 )
372 .await?;
373 }
374
375 let view = self.texture.create_view(&wgpu::TextureViewDescriptor {
377 mip_level_count: Some(nr_mip_maps),
378 ..Default::default()
379 });
380
381 self.view = view;
383
384 Ok(())
385 }
386
387 #[allow(clippy::too_many_arguments)]
388 #[allow(clippy::missing_errors_doc)]
389 pub async fn generate_mipmaps(
390 img: &DynImage,
391 texture: &wgpu::Texture,
392 device: &wgpu::Device,
393 queue: &wgpu::Queue,
394 desc: &wgpu::TextureDescriptor<'_>,
395 nr_mip_maps: u32,
396 mipmap_generation_cpu: bool,
397 staging_buffer: Option<&Buffer>,
398 mipmaper: Option<&RenderMipmapGenerator>,
399 ) -> Result<(), Box<dyn std::error::Error>> {
400 let nr_channels = img.color().channel_count();
401 if mipmap_generation_cpu {
402 let mut img_mip = DynImage::new(1, 1, image::ColorType::L8);
405 for mip_lvl in 1..nr_mip_maps {
406 let mip_size = desc.mip_level_size(mip_lvl).unwrap();
407 let prev_img_mip = if mip_lvl == 1 { img } else { &img_mip };
408 img_mip = prev_img_mip.resize_exact(mip_size.width, mip_size.height, FilterType::Triangle);
409 debug!("mip lvl {mip_lvl} has size {mip_size:?}");
410
411 let img_mip_vec;
412 let img_mip_buf = match nr_channels {
413 1 | 2 | 4 => img_mip.as_bytes(),
414 3 => {
415 img_mip_vec = img_mip.to_rgba8().into_vec();
416 img_mip_vec.as_bytes()
417 }
418 _ => panic!("Format with more than 4 channels not supported"),
419 };
420
421 Self::upload_single_mip(texture, device, queue, desc, img_mip_buf, staging_buffer, mip_lvl).await?;
422 }
423 } else {
424 if let Some(mipmaper) = mipmaper {
426 let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor::default());
427 mipmaper.generate(device, &mut encoder, texture, desc).unwrap();
428 queue.submit(std::iter::once(encoder.finish()));
429 } else {
430 warn!("Couldn't generate mipmaps since the mipmapper was not provided");
431 }
432 }
433
434 Ok(())
435 }
436
437 pub fn extent_from_img(img: &DynImage) -> wgpu::Extent3d {
438 let dimensions = img.dimensions();
439 wgpu::Extent3d {
440 width: dimensions.0,
441 height: dimensions.1,
442 depth_or_array_layers: 1,
443 }
444 }
445
446 pub fn format_from_img(img: &DynImage, is_srgb: bool) -> wgpu::TextureFormat {
449 let nr_channels = img.color().channel_count();
450 let bytes_per_channel = img.color().bytes_per_pixel() / nr_channels;
451 assert!(bytes_per_channel == 1, "We are only supporting textures which have 1 byte per channel.");
452
453 let mut tex_format = match nr_channels {
455 1 => wgpu::TextureFormat::R8Unorm,
456 2 => wgpu::TextureFormat::Rg8Unorm,
457 3 | 4 => wgpu::TextureFormat::Rgba8Unorm,
458 _ => panic!("Format with more than 4 channels not supported"),
459 };
460 if is_srgb {
461 tex_format = tex_format.add_srgb_suffix();
462 }
463
464 tex_format
465 }
466
467 #[allow(clippy::missing_errors_doc)]
473 pub async fn upload_single_mip(
474 texture: &wgpu::Texture,
475 device: &wgpu::Device,
476 queue: &wgpu::Queue,
477 desc: &wgpu::TextureDescriptor<'_>,
478 data: &[u8],
479 staging_buffer: Option<&Buffer>,
480 mip: u32,
481 ) -> Result<(), Box<dyn std::error::Error>> {
482 let mut mip_size = desc.mip_level_size(mip).unwrap();
483 if desc.dimension != wgpu::TextureDimension::D3 {
485 mip_size.depth_or_array_layers = 1;
486 }
487
488 let block_size = desc.format.block_copy_size(None).unwrap_or(4);
492 let (block_width, block_height) = desc.format.block_dimensions();
493
494 let mip_physical = mip_size.physical_size(desc.format);
499
500 let width_blocks = mip_physical.width / block_width;
503 let height_blocks = mip_physical.height / block_height;
504
505 let bytes_per_row = width_blocks * block_size;
506 if let Some(staging_buffer) = staging_buffer {
512 warn!("Using slow CPU->GPU transfer for texture upload. Might use less memory that staging buffer using by wgpu but it will be slower.");
513
514 let bytes_per_row_unpadded = texture.format().block_copy_size(None).unwrap() * mip_size.width;
516 let bytes_per_row_padded = numerical::align(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
517
518 let slice_size = numerical::align(u32::try_from(data.len()).unwrap(), u32::try_from(wgpu::COPY_BUFFER_ALIGNMENT).unwrap());
522 {
523 let buffer_slice = staging_buffer.buffer.slice(0..u64::from(slice_size));
524 let (tx, rx) = futures::channel::oneshot::channel();
527 buffer_slice.map_async(wgpu::MapMode::Write, move |result| {
528 tx.send(result).unwrap();
529 });
530 let _ = device.poll(wgpu::PollType::Wait);
531 rx.await.unwrap()?;
532 let mut buf_data = buffer_slice.get_mapped_range_mut();
533
534 buf_data.get_mut(0..data.len()).unwrap().clone_from_slice(data);
536 }
537
538 staging_buffer.buffer.unmap();
540
541 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
543 encoder.copy_buffer_to_texture(
544 wgpu::TexelCopyBufferInfo {
545 buffer: &staging_buffer.buffer,
546 layout: wgpu::TexelCopyBufferLayout {
547 offset: 0,
548 bytes_per_row: Some(bytes_per_row_padded),
549 rows_per_image: Some(mip_size.height),
550 },
551 },
552 wgpu::TexelCopyTextureInfo {
553 aspect: wgpu::TextureAspect::All,
554 texture,
555 mip_level: mip,
556 origin: wgpu::Origin3d::ZERO,
557 },
558 wgpu::Extent3d {
559 width: mip_size.width,
560 height: mip_size.height,
561 depth_or_array_layers: 1,
562 },
563 );
564 queue.submit(Some(encoder.finish()));
565
566 } else {
571 queue.write_texture(
574 wgpu::TexelCopyTextureInfo {
575 texture,
576 mip_level: mip,
577 origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },
578 aspect: wgpu::TextureAspect::All,
579 },
580 data,
581 wgpu::TexelCopyBufferLayout {
582 offset: 0,
583 bytes_per_row: Some(bytes_per_row),
584 rows_per_image: Some(height_blocks),
585 },
586 mip_physical,
587 );
588 }
589
590 Ok(())
591 }
592
593 pub fn upload_all_mips(
599 texture: &wgpu::Texture,
600 device: &wgpu::Device,
601 queue: &wgpu::Queue,
602 desc: &wgpu::TextureDescriptor,
603 data: &[u8],
604 staging_buffer: Option<&Buffer>,
605 ) {
606 let block_size = desc.format.block_copy_size(None).unwrap_or(4);
610 let (block_width, block_height) = desc.format.block_dimensions();
611 let layer_iterations = desc.array_layer_count();
612
613 let (min_mip, max_mip) = (0, desc.mip_level_count);
614
615 let mut binary_offset = 0;
616 for layer in 0..layer_iterations {
617 for mip in min_mip..max_mip {
618 let mut mip_size = desc.mip_level_size(mip).unwrap();
619 if desc.dimension != wgpu::TextureDimension::D3 {
621 mip_size.depth_or_array_layers = 1;
622 }
623
624 let mip_physical = mip_size.physical_size(desc.format);
629
630 let width_blocks = mip_physical.width / block_width;
633 let height_blocks = mip_physical.height / block_height;
634
635 let bytes_per_row = width_blocks * block_size;
636 let data_size = bytes_per_row * height_blocks * mip_size.depth_or_array_layers;
637
638 let end_offset = binary_offset + data_size as usize;
639
640 if let Some(staging_buffer) = staging_buffer {
641 warn!("Using slow CPU->GPU transfer for texture upload. Might use less memory that staging buffer using by wgpu but it will be slower.");
642
643 let bytes_per_row_unpadded = texture.format().block_copy_size(None).unwrap() * mip_size.width;
645 let bytes_per_row_padded = numerical::align(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
646
647 let data_to_copy = &data[binary_offset..end_offset];
650 let slice_size = numerical::align(
652 u32::try_from(data_to_copy.len()).unwrap(),
653 u32::try_from(wgpu::COPY_BUFFER_ALIGNMENT).unwrap(),
654 );
655 {
656 let buffer_slice = staging_buffer.buffer.slice(0..u64::from(slice_size));
657 let (tx, rx) = futures::channel::oneshot::channel();
660 buffer_slice.map_async(wgpu::MapMode::Write, move |result| {
661 tx.send(result).unwrap();
662 });
663 let _ = device.poll(wgpu::PollType::Wait);
664 rx.block_on().unwrap().unwrap();
665 let mut buf_data = buffer_slice.get_mapped_range_mut();
666
667 buf_data.get_mut(0..data_to_copy.len()).unwrap().clone_from_slice(data_to_copy);
669 }
670
671 staging_buffer.buffer.unmap();
673
674 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
676 encoder.copy_buffer_to_texture(
677 wgpu::TexelCopyBufferInfo {
678 buffer: &staging_buffer.buffer,
679 layout: wgpu::TexelCopyBufferLayout {
680 offset: 0,
681 bytes_per_row: Some(bytes_per_row_padded),
682 rows_per_image: Some(mip_size.height),
683 },
684 },
685 wgpu::TexelCopyTextureInfo {
686 aspect: wgpu::TextureAspect::All,
687 texture,
688 mip_level: mip,
689 origin: wgpu::Origin3d::ZERO,
690 },
691 wgpu::Extent3d {
692 width: mip_size.width,
693 height: mip_size.height,
694 depth_or_array_layers: 1,
695 },
696 );
697 queue.submit(Some(encoder.finish()));
698
699 } else {
705 queue.write_texture(
708 wgpu::TexelCopyTextureInfo {
709 texture,
710 mip_level: mip,
711 origin: wgpu::Origin3d { x: 0, y: 0, z: layer },
712 aspect: wgpu::TextureAspect::All,
713 },
714 &data[binary_offset..end_offset],
715 wgpu::TexelCopyBufferLayout {
716 offset: 0,
717 bytes_per_row: Some(bytes_per_row),
718 rows_per_image: Some(height_blocks),
719 },
720 mip_physical,
721 );
722 }
723
724 binary_offset = end_offset;
725 }
726 }
727 }
728
729 pub fn upload_from_cpu_with_staging_buffer(
730 texture: &wgpu::Texture,
731 device: &wgpu::Device,
732 queue: &wgpu::Queue,
733 desc: &wgpu::TextureDescriptor,
734 data: &[u8],
735 staging_buffer: &Buffer,
736 mip_lvl: u32,
737 ) {
738 let mip_size = desc.mip_level_size(mip_lvl).unwrap();
739
740 {
743 let buffer_slice = staging_buffer.buffer.slice(0..data.len() as u64);
744 let (tx, rx) = futures::channel::oneshot::channel();
747 buffer_slice.map_async(wgpu::MapMode::Write, move |result| {
748 tx.send(result).unwrap();
749 });
750 let _ = device.poll(wgpu::PollType::Wait);
751 rx.block_on().unwrap().unwrap();
752 let mut buf_data = buffer_slice.get_mapped_range_mut();
753
754 buf_data.clone_from_slice(data);
756 }
757
758 staging_buffer.buffer.unmap();
760
761 let bytes_per_row_unpadded = texture.format().block_copy_size(None).unwrap() * mip_size.width;
763 let bytes_per_row_padded = numerical::align(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
764
765 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
767 encoder.copy_buffer_to_texture(
768 wgpu::TexelCopyBufferInfo {
769 buffer: &staging_buffer.buffer,
770 layout: wgpu::TexelCopyBufferLayout {
771 offset: 0,
772 bytes_per_row: Some(bytes_per_row_padded),
773 rows_per_image: Some(mip_size.height),
774 },
775 },
776 wgpu::TexelCopyTextureInfo {
777 aspect: wgpu::TextureAspect::All,
778 texture,
779 mip_level: mip_lvl,
780 origin: wgpu::Origin3d::ZERO,
781 },
782 wgpu::Extent3d {
783 width: mip_size.width,
784 height: mip_size.height,
785 depth_or_array_layers: 1,
786 },
787 );
788 queue.submit(Some(encoder.finish()));
789
790 let _ = device.poll(wgpu::PollType::Wait);
793 }
794
795 pub async fn download_to_cpu(&self, device: &wgpu::Device, queue: &wgpu::Queue, aspect: wgpu::TextureAspect) -> DynImage {
800 let bytes_per_row_unpadded = self.texture.format().block_copy_size(None).unwrap_or(4) * self.width();
802 let bytes_per_row_padded = numerical::align(bytes_per_row_unpadded, wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
803 let output_buffer_size = u64::from(bytes_per_row_padded * self.height());
804 let output_buffer_desc = wgpu::BufferDescriptor {
805 size: output_buffer_size,
806 usage: wgpu::BufferUsages::COPY_DST
807 | wgpu::BufferUsages::MAP_READ,
809 label: None,
810 mapped_at_creation: false,
811 };
812
813 let output_buffer = device.create_buffer(&output_buffer_desc);
814
815 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
817 encoder.copy_texture_to_buffer(
818 wgpu::TexelCopyTextureInfo {
819 aspect,
820 texture: &self.texture,
821 mip_level: 0,
822 origin: wgpu::Origin3d::ZERO,
823 },
824 wgpu::TexelCopyBufferInfo {
825 buffer: &output_buffer,
826 layout: wgpu::TexelCopyBufferLayout {
827 offset: 0,
828 bytes_per_row: Some(bytes_per_row_padded),
829 rows_per_image: Some(self.height()),
830 },
831 },
832 wgpu::Extent3d {
833 width: self.width(),
834 height: self.height(),
835 depth_or_array_layers: 1,
836 },
837 );
838 queue.submit(Some(encoder.finish()));
839
840 let img: Option<DynImage> = {
851 let buffer_slice = output_buffer.slice(..);
852
853 let (tx, rx) = futures_intrusive::channel::shared::oneshot_channel();
858 buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
859 tx.send(result).unwrap();
860 });
861 let _ = device.poll(wgpu::PollType::Wait);
862 rx.receive().await.unwrap().unwrap();
863
864 let data = buffer_slice.get_mapped_range();
865
866 let data_unpadded = Texture::remove_padding(data.as_bytes(), bytes_per_row_unpadded, bytes_per_row_padded, self.height());
869
870 let w = self.width();
873 let h = self.height();
874 match self.texture.format() {
875 TextureFormat::Rgba8Unorm => ImageBuffer::from_raw(w, h, data_unpadded.to_vec()).map(DynImage::ImageRgba8),
876 TextureFormat::Bgra8Unorm => {
877 let bgra_data = data_unpadded.to_vec();
878 let mut rgba_data = bgra_data.clone();
880 for chunk in rgba_data.chunks_exact_mut(4) {
881 chunk.swap(0, 2); }
883 ImageBuffer::from_raw(w, h, rgba_data).map(DynImage::ImageRgba8)
884 }
885 TextureFormat::Rgba32Float => ImageBuffer::from_raw(w, h, numerical::u8_to_f32_vec(&data_unpadded)).map(DynImage::ImageRgba32F),
886 TextureFormat::Depth32Float | TextureFormat::Depth32FloatStencil8 => {
887 ImageBuffer::from_raw(w, h, numerical::u8_to_f32_vec(&data_unpadded)).map(DynImage::ImageLuma32F)
888 }
889 x => panic!("Texture format not implemented! {x:?}"),
890 }
891 };
892 output_buffer.unmap();
893 img.unwrap()
894 }
895
896 pub async fn download_pixel_to_cpu(&self, device: &wgpu::Device, queue: &wgpu::Queue, aspect: wgpu::TextureAspect, x: u32, y: u32) -> DynImage {
897 let output_buffer_desc = wgpu::BufferDescriptor {
899 label: Some("ID Readback Buffer"),
900 size: 4, usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
902 mapped_at_creation: false,
903 };
904 let output_buffer = device.create_buffer(&output_buffer_desc);
905
906 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
907 label: Some("ID Readback Encoder"),
908 });
909
910 encoder.copy_texture_to_buffer(
914 wgpu::TexelCopyTextureInfo {
915 aspect,
916 texture: &self.texture,
917 mip_level: 0,
918 origin: wgpu::Origin3d { x, y, z: 0 },
919 },
920 wgpu::TexelCopyBufferInfo {
921 buffer: &output_buffer,
922 layout: wgpu::TexelCopyBufferLayout {
923 offset: 0,
924 bytes_per_row: None,
925 rows_per_image: None,
926 },
927 },
928 wgpu::Extent3d {
929 width: 1,
930 height: 1,
931 depth_or_array_layers: 1,
932 },
933 );
934
935 queue.submit(Some(encoder.finish()));
936
937 let pixel: Option<DynImage> = {
938 let buffer_slice = output_buffer.slice(..);
939
940 let (tx, rx) = futures_intrusive::channel::shared::oneshot_channel();
941 buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
942 tx.send(result).unwrap();
943 });
944 let _ = device.poll(wgpu::PollType::Wait);
945 rx.receive().await.unwrap().unwrap();
946
947 let data = buffer_slice.get_mapped_range();
948 match self.texture.format() {
949 TextureFormat::Rgba8Unorm => {
950 let single_pixel_bytes = *data.to_vec().first().unwrap();
952 ImageBuffer::from_raw(1, 1, [single_pixel_bytes].to_vec()).map(DynImage::ImageLuma8)
953 }
954 x => panic!("Texture format not implemented! {x:?}"),
955 }
956 };
957 output_buffer.unmap();
958 pixel.unwrap()
959 }
960
961 pub fn remove_padding(buffer: &[u8], bytes_per_row_unpadded: u32, bytes_per_row_padded: u32, nr_rows: u32) -> Cow<'_, [u8]> {
962 if bytes_per_row_padded == bytes_per_row_unpadded {
967 return Cow::Borrowed(buffer);
968 }
969
970 let mut unpadded_buffer = Vec::with_capacity((bytes_per_row_unpadded * nr_rows) as _);
971
972 for row in 0..nr_rows {
973 let offset = (bytes_per_row_padded * row) as usize;
974 unpadded_buffer.extend_from_slice(&buffer[offset..(offset + bytes_per_row_unpadded as usize)]);
975 }
976
977 unpadded_buffer.into()
978 }
979
980 pub fn create_bind_group_layout(device: &wgpu::Device, binding_tex: u32, binding_sampler: u32) -> wgpu::BindGroupLayout {
981 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
982 entries: &[
983 wgpu::BindGroupLayoutEntry {
984 binding: binding_tex, visibility: wgpu::ShaderStages::FRAGMENT,
986 ty: wgpu::BindingType::Texture {
987 multisampled: false,
988 view_dimension: wgpu::TextureViewDimension::D2,
989 sample_type: wgpu::TextureSampleType::Float { filterable: true },
990 },
991 count: None,
992 },
993 wgpu::BindGroupLayoutEntry {
994 binding: binding_sampler, visibility: wgpu::ShaderStages::FRAGMENT,
996 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
997 count: None,
998 },
999 ],
1000 label: Some("texture_bind_group_layout"),
1001 })
1002 }
1003 #[must_use]
1004 pub fn depth_linearize(&self, device: &wgpu::Device, queue: &wgpu::Queue, near: f32, far: f32) -> DynImage {
1005 assert!(
1007 !(self.texture.sample_count() > 1 && self.texture.format() == TextureFormat::Depth32Float),
1008 "InvalidSampleCount: Depth maps not supported for MSAA sample count {} (Use a config to set msaa_nr_samples as 1)",
1009 self.texture.sample_count()
1010 );
1011
1012 let aspect = wgpu::TextureAspect::DepthOnly;
1014 let dynamic_img = pollster::block_on(self.download_to_cpu(device, queue, aspect));
1015 let w = dynamic_img.width();
1016 let h = dynamic_img.height();
1017 let c = dynamic_img.channels();
1018 assert!(c == 1, "Depth maps should have only 1 channel");
1019
1020 let linearized_img = match dynamic_img {
1021 DynImage::ImageLuma32F(v) => {
1022 let img_vec_ndc = v.to_vec();
1023 let img_vec: Vec<f32> = img_vec_ndc.iter().map(|&x| numerical::linearize_depth_reverse_z(x, near, far)).collect();
1024 DynImage::ImageLuma32F(ImageBuffer::from_raw(w, h, img_vec).unwrap())
1025 }
1026 _ => panic!("Texture format not implemented for remap (Only for depths)!"),
1027 };
1028 linearized_img
1029 }
1030
1031 pub fn create_bind_group(&self, device: &wgpu::Device, binding_tex: u32, binding_sampler: u32) -> wgpu::BindGroup {
1032 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
1035 layout: &Self::create_bind_group_layout(device, binding_tex, binding_sampler),
1036 entries: &[
1037 wgpu::BindGroupEntry {
1038 binding: binding_tex,
1039 resource: wgpu::BindingResource::TextureView(&self.view),
1040 },
1041 wgpu::BindGroupEntry {
1042 binding: binding_sampler,
1043 resource: wgpu::BindingResource::Sampler(&self.sampler),
1044 },
1045 ],
1046 label: Some("bind_group"),
1047 });
1048 bind_group
1049 }
1050
1051 pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
1052 let format = self.texture.format();
1054 let usage = self.texture.usage();
1055 let mut new = Self::new(device, width, height, format, usage, self.tex_params);
1056 std::mem::swap(self, &mut new);
1057 }
1058
1059 pub fn create_default_texture(device: &wgpu::Device, queue: &wgpu::Queue) -> Self {
1061 let width = 4;
1068 let height = 4;
1069
1070 let mut img_data: Vec<u8> = Vec::new();
1071 for _ in 0..height {
1072 for _ in 0..width {
1073 img_data.push(255);
1075 img_data.push(0);
1076 img_data.push(0);
1077 img_data.push(0);
1078 }
1079 }
1080
1081 let size = wgpu::Extent3d {
1085 width,
1086 height,
1087 depth_or_array_layers: 1,
1088 };
1089 let format = wgpu::TextureFormat::Rgba8UnormSrgb;
1091 let desc = wgpu::TextureDescriptor {
1092 label: None,
1093 size,
1094 mip_level_count: 1,
1095 sample_count: 1,
1096 dimension: wgpu::TextureDimension::D2,
1097 format,
1098 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
1099 view_formats: &[],
1100 };
1101 let tex_params = TexParams::from_desc(&desc);
1102 let texture = device.create_texture_with_data(queue, &desc, wgpu::util::TextureDataOrder::LayerMajor, img_data.as_slice());
1103
1104 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
1105 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
1106 address_mode_u: wgpu::AddressMode::ClampToEdge,
1107 address_mode_v: wgpu::AddressMode::ClampToEdge,
1108 address_mode_w: wgpu::AddressMode::ClampToEdge,
1109 mag_filter: wgpu::FilterMode::Linear,
1110 min_filter: wgpu::FilterMode::Nearest,
1111 mipmap_filter: wgpu::FilterMode::Nearest,
1112 ..Default::default()
1113 });
1114
1115 Self {
1116 texture,
1117 view,
1118 sampler,
1119 tex_params, }
1122 }
1123
1124 pub fn create_default_cubemap(device: &wgpu::Device, queue: &wgpu::Queue) -> Self {
1125 let width = 4;
1132 let height = 4;
1133
1134 let mut img_data: Vec<u8> = Vec::new();
1135 for _ in 0..6 {
1136 for _ in 0..height {
1137 for _ in 0..width {
1138 img_data.push(255);
1140 img_data.push(0);
1141 img_data.push(0);
1142 img_data.push(0);
1143 }
1144 }
1145 }
1146
1147 let size = wgpu::Extent3d {
1148 width,
1149 height,
1150 depth_or_array_layers: 6,
1151 };
1152 let format = wgpu::TextureFormat::Rgba8UnormSrgb;
1154 let desc = wgpu::TextureDescriptor {
1155 label: None,
1156 size,
1157 mip_level_count: 1,
1158 sample_count: 1,
1159 dimension: wgpu::TextureDimension::D2,
1160 format,
1161 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
1162 view_formats: &[],
1163 };
1164 let tex_params = TexParams::from_desc(&desc);
1165 let texture = device.create_texture_with_data(queue, &desc, wgpu::util::TextureDataOrder::LayerMajor, img_data.as_slice());
1166
1167 let view = texture.create_view(&wgpu::TextureViewDescriptor {
1168 dimension: Some(wgpu::TextureViewDimension::Cube),
1169 ..Default::default()
1170 });
1171 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
1172 address_mode_u: wgpu::AddressMode::ClampToEdge,
1173 address_mode_v: wgpu::AddressMode::ClampToEdge,
1174 address_mode_w: wgpu::AddressMode::ClampToEdge,
1175 mag_filter: wgpu::FilterMode::Linear,
1176 min_filter: wgpu::FilterMode::Linear,
1177 mipmap_filter: wgpu::FilterMode::Linear,
1178 ..Default::default()
1179 });
1180
1181 Self {
1182 texture,
1183 view,
1184 sampler,
1185 tex_params, }
1188 }
1189
1190 pub fn width(&self) -> u32 {
1191 self.texture.width()
1192 }
1193 pub fn height(&self) -> u32 {
1194 self.texture.height()
1195 }
1196 pub fn extent(&self) -> wgpu::Extent3d {
1197 wgpu::Extent3d {
1198 width: self.width(),
1199 height: self.height(),
1200 depth_or_array_layers: 1,
1201 }
1202 }
1203 }