wgpu_mipmap/backends/
copy.rs

1use crate::backends::RenderMipmapGenerator;
2use crate::core::*;
3use crate::util::get_mip_extent;
4
5/// Generates mipmaps for textures with sampled usage.
6pub struct CopyMipmapGenerator<'a> {
7    generator: &'a RenderMipmapGenerator,
8}
9
10impl<'a> CopyMipmapGenerator<'a> {
11    // Creates a new `CopyMipmapGenerator` from an existing `RenderMipmapGenerator`
12    /// Once created, it can be used repeatedly to generate mipmaps for any
13    /// texture supported by the render generator.
14    pub fn new(generator: &'a RenderMipmapGenerator) -> Self {
15        Self { generator }
16    }
17
18    /// Returns the texture usage `CopyMipmapGenerator` requires for mipmap
19    /// generation.
20    pub fn required_usage() -> wgpu::TextureUsage {
21        wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST
22    }
23}
24
25impl<'a> MipmapGenerator for CopyMipmapGenerator<'a> {
26    fn generate(
27        &self,
28        device: &wgpu::Device,
29        encoder: &mut wgpu::CommandEncoder,
30        texture: &wgpu::Texture,
31        texture_descriptor: &wgpu::TextureDescriptor,
32    ) -> Result<(), Error> {
33        // Create a temporary texture with half the resolution
34        // of the original texture, and one less mip level
35        // We'll generate mipmaps into this texture, then
36        // copy the results back into the mip levels of the original texture
37        let tmp_descriptor = wgpu::TextureDescriptor {
38            label: None,
39            size: get_mip_extent(&texture_descriptor.size, 1),
40            mip_level_count: texture_descriptor.mip_level_count - 1,
41            sample_count: texture_descriptor.sample_count,
42            dimension: texture_descriptor.dimension,
43            format: texture_descriptor.format,
44            usage: RenderMipmapGenerator::required_usage() | wgpu::TextureUsage::COPY_SRC,
45        };
46        let tmp_texture = device.create_texture(&tmp_descriptor);
47        self.generator.generate_src_dst(
48            device,
49            encoder,
50            &texture,
51            &tmp_texture,
52            texture_descriptor,
53            &tmp_descriptor,
54            1,
55        )?;
56        let mip_count = tmp_descriptor.mip_level_count;
57        for i in 0..mip_count {
58            encoder.copy_texture_to_texture(
59                wgpu::TextureCopyView {
60                    texture: &tmp_texture,
61                    mip_level: i,
62                    origin: wgpu::Origin3d::default(),
63                },
64                wgpu::TextureCopyView {
65                    texture: &texture,
66                    mip_level: i + 1,
67                    origin: wgpu::Origin3d::default(),
68                },
69                get_mip_extent(&tmp_descriptor.size, i),
70            );
71        }
72        Ok(())
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use crate::util::*;
80
81    fn init() {
82        let _ = env_logger::builder().is_test(true).try_init();
83    }
84
85    #[allow(dead_code)]
86    async fn generate_and_copy_to_cpu_render_slow(
87        buffer: &[u8],
88        texture_descriptor: &wgpu::TextureDescriptor<'_>,
89    ) -> Result<Vec<MipBuffer>, Error> {
90        let (_instance, _adaptor, device, queue) = wgpu_setup().await;
91
92        let generator = crate::backends::RenderMipmapGenerator::new_with_format_hints(
93            &device,
94            &[texture_descriptor.format],
95        );
96        let fallback = CopyMipmapGenerator::new(&generator);
97        Ok(
98            generate_and_copy_to_cpu(&device, &queue, &fallback, buffer, texture_descriptor)
99                .await?,
100        )
101    }
102
103    async fn generate_test(texture_descriptor: &wgpu::TextureDescriptor<'_>) -> Result<(), Error> {
104        let (_instance, _adapter, device, _queue) = wgpu_setup().await;
105        let render = crate::backends::RenderMipmapGenerator::new_with_format_hints(
106            &device,
107            &[texture_descriptor.format],
108        );
109        let generator = CopyMipmapGenerator::new(&render);
110        let texture = device.create_texture(&texture_descriptor);
111        let mut encoder = device.create_command_encoder(&Default::default());
112        generator.generate(&device, &mut encoder, &texture, &texture_descriptor)
113    }
114
115    #[test]
116    fn sanity_check() {
117        init();
118        // Generate texture data on the CPU
119        let size = 511;
120        let mip_level_count = 1 + (size as f32).log2() as u32;
121        // Create a texture
122        let format = wgpu::TextureFormat::R8Unorm;
123        let texture_extent = wgpu::Extent3d {
124            width: size,
125            height: size,
126            depth: 1,
127        };
128        let texture_descriptor = wgpu::TextureDescriptor {
129            size: texture_extent,
130            mip_level_count,
131            format,
132            sample_count: 1,
133            dimension: wgpu::TextureDimension::D2,
134            usage: CopyMipmapGenerator::required_usage(),
135            label: None,
136        };
137        futures::executor::block_on((|| async {
138            let res = generate_test(&texture_descriptor).await;
139            assert!(res.is_ok());
140        })());
141    }
142
143    #[test]
144    fn unsupported_format() {
145        init();
146        // Generate texture data on the CPU
147        let size = 511;
148        let mip_level_count = 1 + (size as f32).log2() as u32;
149        // Create a texture
150        let format = wgpu::TextureFormat::R8Unorm;
151        let texture_extent = wgpu::Extent3d {
152            width: size,
153            height: size,
154            depth: 1,
155        };
156        let texture_descriptor = wgpu::TextureDescriptor {
157            size: texture_extent,
158            mip_level_count,
159            format,
160            sample_count: 1,
161            dimension: wgpu::TextureDimension::D2,
162            usage: wgpu::TextureUsage::empty(),
163            label: None,
164        };
165        futures::executor::block_on((|| async {
166            let res = generate_test(&texture_descriptor).await;
167            assert!(res.is_err());
168            assert!(res.err() == Some(Error::UnsupportedUsage(texture_descriptor.usage)));
169        })());
170    }
171}