easy_wgpu/
texture.rs

1use gloss_img::DynImage;
2use image::imageops::FilterType;
3// use image::GenericImage;
4use image::{EncodableLayout, GenericImageView, ImageBuffer};
5use log::{debug, warn};
6use pollster::FutureExt;
7use std::borrow::Cow;
8use wgpu::{util::DeviceExt, CommandEncoderDescriptor, TextureFormat}; //enabled create_texture_with_data
9
10// use gloss_utils::gloss_image;
11use gloss_utils::numerical;
12
13use crate::{buffer::Buffer, mipmap::RenderMipmapGenerator};
14
15//aditional parameters for texture creation that usually you can leave as
16// default
17#[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, //TODO should be optional or rather we should create a nearest and linear sampler as a global per frame uniform
47    // pub width: u32,
48    // pub height: u32,
49    // pub bind_group: Option<wgpu::BindGroup>, //cannot lazily create because it depends on the binding locations
50    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 format = wgpu::TextureFormat::Rgba8UnormSrgb;
64        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: wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::RENDER_ATTACHMENT,
75            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            // width,
99            // height,
100            // bind_group: None,
101        }
102    }
103
104    /// # Panics
105    /// Will panic if bytes cannot be decoded into a image representation
106    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, /* width: dimensions.0,
166                         * height: dimensions.1,
167                         * bind_group: None, */
168        }
169    }
170
171    /// reads image from format and into this texture
172    /// if `is_srgb` is set then the reading will perform a conversion from
173    /// gamma space to linear space when sampling the texture in a shader
174    /// When writing to the texture, the opposite conversion takes place.
175    /// # Panics
176    /// Will panic if the path cannot be found
177    pub fn from_path(path: &str, device: &wgpu::Device, queue: &wgpu::Queue, is_srgb: bool) -> Self {
178        //read to cpu
179        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, //TODO what do we set as default here?
187            None,
188            None,
189        )
190    }
191
192    /// # Panics
193    /// Will panic if textures that have more than 1 byte per channel or more
194    /// than 4 channels.
195    #[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        //convert 3 channels to 4 channels and keep 2 channels as 2 channels
212        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); //create with all mips but upload only 1 mip
251
252        Self::upload_single_mip(&texture, device, queue, &desc, img_buf, staging_buffer, 0);
253
254        //mipmaps
255        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::default());
270        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, /* width: dimensions.0,
289                         * height: dimensions.1,
290                         * bind_group: None, */
291        }
292    }
293
294    /// # Panics
295    /// Will panic if the image has more than 1 byte per channel
296    #[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 dimensions = img.dimensions();
309        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        // TODO refactor this into its own func because there is a lot of duplication
314        // with the from_img function convert 3 channels to 4 channels and keep
315        // 2 channels as 2 channels
316        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        //mipmaps
351        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 = texture.create_view(&wgpu::TextureViewDescriptor::default());
366        let view = self.texture.create_view(&wgpu::TextureViewDescriptor {
367            mip_level_count: Some(nr_mip_maps),
368            ..Default::default()
369        });
370
371        //update
372        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            //CPU generation
390            //similar to https://github.com/DGriffin91/bevy_mod_mipmap_generator/blob/main/src/lib.rs
391            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            //GPU mipmaps generation
412            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    /// # Panics
432    /// Will panic if the image has more than 1 byte per channel
433    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        //get a format for the texture
439        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    /// Basically the same as `device.create_texture_with_data` but without the
453    /// creation part and the data is assumed to contain only one mip # Panics
454    /// Will panic if the data does not fit in the defined mipmaps described in
455    /// textureDescriptor
456    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        // copying layers separately
467        if desc.dimension != wgpu::TextureDimension::D3 {
468            mip_size.depth_or_array_layers = 1;
469        }
470
471        // Will return None only if it's a combined depth-stencil format
472        // If so, default to 4, validation will fail later anyway since the depth or
473        // stencil aspect needs to be written to individually
474        let block_size = desc.format.block_copy_size(None).unwrap_or(4);
475        let (block_width, block_height) = desc.format.block_dimensions();
476
477        // When uploading mips of compressed textures and the mip is supposed to be
478        // a size that isn't a multiple of the block size, the mip needs to be uploaded
479        // as its "physical size" which is the size rounded up to the nearest block
480        // size.
481        let mip_physical = mip_size.physical_size(desc.format);
482
483        // All these calculations are performed on the physical size as that's the
484        // data that exists in the buffer.
485        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        // let data_size = bytes_per_row * height_blocks *
490        // mip_size.depth_or_array_layers;
491
492        // let end_offset = binary_offset + data_size as usize;
493
494        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            //get some metadata
498            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            //map buffer and copy into it
502            // https://docs.rs/wgpu/latest/wgpu/struct.Buffer.html#mapping-buffers
503            //the mapping range has to be aligned to COPY_BUFFER_ALIGNMENT(4 bytes)
504            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                // NOTE: We have to create the mapping THEN device.poll() before await
508                // the future. Otherwise the application will freeze.
509                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                //copy into it
518                buf_data.get_mut(0..data.len()).unwrap().clone_from_slice(data);
519            }
520
521            //finish
522            staging_buffer.buffer.unmap();
523
524            //copy from buffer to texture
525            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            //wait to finish because we might be reusing the staging buffer for
550            // something else later TODO maybe this is not needed
551            // since the mapping will block either way if the buffer is still in
552            // use device.poll(wgpu::Maintain::Wait);
553        } else {
554            //Use wgpu write_texture which schedules internally the transfer to happen
555            // later
556            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        //-=---------------------
574    }
575
576    /// Basically the same as `device.create_texture_with_data` but without the
577    /// creation part Assumes the data contains info for all mips
578    /// # Panics
579    /// Will panic if the data does not fit in the defined mipmaps described in
580    /// textureDescriptor
581    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        // Will return None only if it's a combined depth-stencil format
590        // If so, default to 4, validation will fail later anyway since the depth or
591        // stencil aspect needs to be written to individually
592        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                // copying layers separately
603                if desc.dimension != wgpu::TextureDimension::D3 {
604                    mip_size.depth_or_array_layers = 1;
605                }
606
607                // When uploading mips of compressed textures and the mip is supposed to be
608                // a size that isn't a multiple of the block size, the mip needs to be uploaded
609                // as its "physical size" which is the size rounded up to the nearest block
610                // size.
611                let mip_physical = mip_size.physical_size(desc.format);
612
613                // All these calculations are performed on the physical size as that's the
614                // data that exists in the buffer.
615                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                    //get some metadata
627                    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                    //map buffer and copy into it
631                    // https://docs.rs/wgpu/latest/wgpu/struct.Buffer.html#mapping-buffers
632                    let data_to_copy = &data[binary_offset..end_offset];
633                    //the mapping range has to be aligned to COPY_BUFFER_ALIGNMENT(4 bytes)
634                    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                        // NOTE: We have to create the mapping THEN device.poll() before await
641                        // the future. Otherwise the application will freeze.
642                        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                        //copy into it
651                        buf_data.get_mut(0..data_to_copy.len()).unwrap().clone_from_slice(data_to_copy);
652                    }
653
654                    //finish
655                    staging_buffer.buffer.unmap();
656
657                    //copy from buffer to texture
658                    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                    //wait to finish because we might be reusing the staging
683                    // buffer for something else later
684                    // TODO maybe this is not needed since the mapping will
685                    // block either way if the buffer is still in use
686                    // device.poll(wgpu::Maintain::Wait);
687                } else {
688                    //Use wgpu write_texture which schedules internally the transfer to happen
689                    // later
690                    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        //map buffer and copy into it
724        // https://docs.rs/wgpu/latest/wgpu/struct.Buffer.html#mapping-buffers
725        {
726            let buffer_slice = staging_buffer.buffer.slice(0..data.len() as u64);
727            // NOTE: We have to create the mapping THEN device.poll() before await
728            // the future. Otherwise the application will freeze.
729            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            //copy into it
738            buf_data.clone_from_slice(data);
739        }
740
741        //finish
742        staging_buffer.buffer.unmap();
743
744        //get some metadata
745        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        //copy from buffer to texture
749        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        //wait to finish because we might be reusing the staging buffer for something
774        // else later
775        device.poll(wgpu::Maintain::Wait);
776    }
777
778    pub async fn download_to_cpu(&self, device: &wgpu::Device, queue: &wgpu::Queue) -> DynImage {
779        // create buffer
780        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        // this tells wpgu that we want to read this buffer from the cpu
787        | 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        //copy from texture to buffer
795        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        // map and get to cpu
820        // We need to scope the mapping variables so that we can unmap the buffer
821
822        // let mut buffer = DynImage::new(self.width(), self.height(),
823        // self.texture.format()); let mut buffer = match self.texture.format()
824        // {     TextureFormat::Rgba8Unorm => DynImage::new_rgba8(self.width(),
825        // self.height()),     TextureFormat::Depth32Float =>
826        // DynImage::new_luma32f(self.width(), self.height()),     _ => panic!("
827        // Texture format not implemented!"), };
828
829        let img: Option<DynImage> = {
830            let buffer_slice = output_buffer.slice(..);
831
832            // NOTE: We have to create the mapping THEN device.poll() before await
833            // the future. Otherwise the application will freeze.
834            //TODO maybe change the future_intrusive to futures. Future_intrusive seems to
835            // give some issues on wasm
836            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            //TODO remove padding and copy into image
846            // https://github.com/rerun-io/rerun/blob/93146b6d04f8f494258901c8b892eee0bb31b1a8/crates/re_renderer/src/texture_info.rs#L57
847            let data_unpadded = Texture::remove_padding(data.as_bytes(), bytes_per_row_unpadded, bytes_per_row_padded, self.height());
848
849            // let copy_from = data_unpadded.as_bytes();
850            // buffer.copy_from_bytes(self.width(), self.height(), copy_from);
851            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                    // Convert BGRA to RGBA by swapping channels
858                    let mut rgba_data = bgra_data.clone();
859                    for chunk in rgba_data.chunks_exact_mut(4) {
860                        chunk.swap(0, 2); // Swap B and R
861                    }
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        // re_tracing::profile_function!();
875
876        // assert_eq!(buffer.len() as wgpu::BufferAddress, self.buffer_size_padded);
877
878        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, //matches with the @binding in the shader
897                    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, //matches with the @binding in the shader
907                    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        //panics if depth map retrieval is attempted with MSAA sample count set to > 1
918        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        //create bind group
943        //recreate the bind group
944        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        //essentially creates a whole new texture with the same format and usage
963        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    //make a default 4x4 texture that can be used as a dummy texture
970    pub fn create_default_texture(device: &wgpu::Device, queue: &wgpu::Queue) -> Self {
971        // //read to cpu
972        // let img = ImageReader::open(path).unwrap().decode().unwrap();
973        // let rgba = img.to_rgba8();
974
975        //we make a 4x4 texture because some gbus don't allow 1x1 or 2x2 so 4x4 seems
976        // to be the minimum allowed
977        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                //assume 4 channels
984                img_data.push(255);
985                img_data.push(0);
986                img_data.push(0);
987                img_data.push(0);
988            }
989        }
990
991        // let rgba = img.to_rgba8();
992        // let dimensions = img.dimensions();
993
994        let size = wgpu::Extent3d {
995            width,
996            height,
997            depth_or_array_layers: 1,
998        };
999        // let format = wgpu::TextureFormat::Rgba8UnormSrgb;
1000        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, /* width,
1030                         * height, */
1031        }
1032    }
1033
1034    pub fn create_default_cubemap(device: &wgpu::Device, queue: &wgpu::Queue) -> Self {
1035        // //read to cpu
1036        // let img = ImageReader::open(path).unwrap().decode().unwrap();
1037        // let rgba = img.to_rgba8();
1038
1039        //we make a 4x4 texture because some gbus don't allow 1x1 or 2x2 so 4x4 seems
1040        // to be the minimum allowed
1041        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                    //assume 4 channels
1049                    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;
1063        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, /* width,
1096                         * height, */
1097        }
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    // pub fn clone(&self) -> Self {
1114    //     Self {
1115    //         texture: self.texture,
1116    //         view: (),
1117    //         sampler: (),
1118    //         width: (),
1119    //         height: (),
1120    //     }
1121    // }
1122}