use crate::backends::RenderMipmapGenerator;
use crate::core::*;
use crate::util::get_mip_extent;
pub struct CopyMipmapGenerator<'a> {
generator: &'a RenderMipmapGenerator,
}
impl<'a> CopyMipmapGenerator<'a> {
pub fn new(generator: &'a RenderMipmapGenerator) -> Self {
Self { generator }
}
pub fn required_usage() -> wgpu::TextureUsage {
wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST
}
}
impl<'a> MipmapGenerator for CopyMipmapGenerator<'a> {
fn generate(
&self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
texture: &wgpu::Texture,
texture_descriptor: &wgpu::TextureDescriptor,
) -> Result<(), Error> {
let tmp_descriptor = wgpu::TextureDescriptor {
label: None,
size: get_mip_extent(&texture_descriptor.size, 1),
mip_level_count: texture_descriptor.mip_level_count - 1,
sample_count: texture_descriptor.sample_count,
dimension: texture_descriptor.dimension,
format: texture_descriptor.format,
usage: RenderMipmapGenerator::required_usage() | wgpu::TextureUsage::COPY_SRC,
};
let tmp_texture = device.create_texture(&tmp_descriptor);
self.generator.generate_src_dst(
device,
encoder,
&texture,
&tmp_texture,
texture_descriptor,
&tmp_descriptor,
1,
)?;
let mip_count = tmp_descriptor.mip_level_count;
for i in 0..mip_count {
encoder.copy_texture_to_texture(
wgpu::TextureCopyView {
texture: &tmp_texture,
mip_level: i,
origin: wgpu::Origin3d::default(),
},
wgpu::TextureCopyView {
texture: &texture,
mip_level: i + 1,
origin: wgpu::Origin3d::default(),
},
get_mip_extent(&tmp_descriptor.size, i),
);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::*;
fn init() {
let _ = env_logger::builder().is_test(true).try_init();
}
#[allow(dead_code)]
async fn generate_and_copy_to_cpu_render_slow(
buffer: &[u8],
texture_descriptor: &wgpu::TextureDescriptor<'_>,
) -> Result<Vec<MipBuffer>, Error> {
let (_instance, _adaptor, device, queue) = wgpu_setup().await;
let generator = crate::backends::RenderMipmapGenerator::new_with_format_hints(
&device,
&[texture_descriptor.format],
);
let fallback = CopyMipmapGenerator::new(&generator);
Ok(
generate_and_copy_to_cpu(&device, &queue, &fallback, buffer, texture_descriptor)
.await?,
)
}
async fn generate_test(texture_descriptor: &wgpu::TextureDescriptor<'_>) -> Result<(), Error> {
let (_instance, _adapter, device, _queue) = wgpu_setup().await;
let render = crate::backends::RenderMipmapGenerator::new_with_format_hints(
&device,
&[texture_descriptor.format],
);
let generator = CopyMipmapGenerator::new(&render);
let texture = device.create_texture(&texture_descriptor);
let mut encoder = device.create_command_encoder(&Default::default());
generator.generate(&device, &mut encoder, &texture, &texture_descriptor)
}
#[test]
fn sanity_check() {
init();
let size = 511;
let mip_level_count = 1 + (size as f32).log2() as u32;
let format = wgpu::TextureFormat::R8Unorm;
let texture_extent = wgpu::Extent3d {
width: size,
height: size,
depth: 1,
};
let texture_descriptor = wgpu::TextureDescriptor {
size: texture_extent,
mip_level_count,
format,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
usage: CopyMipmapGenerator::required_usage(),
label: None,
};
futures::executor::block_on((|| async {
let res = generate_test(&texture_descriptor).await;
assert!(res.is_ok());
})());
}
#[test]
fn unsupported_format() {
init();
let size = 511;
let mip_level_count = 1 + (size as f32).log2() as u32;
let format = wgpu::TextureFormat::R8Unorm;
let texture_extent = wgpu::Extent3d {
width: size,
height: size,
depth: 1,
};
let texture_descriptor = wgpu::TextureDescriptor {
size: texture_extent,
mip_level_count,
format,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
usage: wgpu::TextureUsage::empty(),
label: None,
};
futures::executor::block_on((|| async {
let res = generate_test(&texture_descriptor).await;
assert!(res.is_err());
assert!(res.err() == Some(Error::UnsupportedUsage(texture_descriptor.usage)));
})());
}
}