use std::sync::Arc;
#[derive(Clone)]
pub struct Texture(pub(crate) Arc<AnyTexture>);
pub(crate) struct AnyTexture {
pub format: wgpu::TextureFormat,
pub texture: wgpu::Texture,
pub view: wgpu::TextureView,
}
impl Drop for AnyTexture {
fn drop(&mut self) {
self.texture.destroy();
}
}
impl Texture {
pub fn create_empty(
device: &wgpu::Device,
width: u32,
height: u32,
format: wgpu::TextureFormat,
) -> Self {
let mip_level_count = Self::calculate_mip_levels(width, height);
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("atlas_texture"),
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::COPY_DST
| wgpu::TextureUsages::COPY_SRC
| wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("atlas_texture_view"),
format: Some(format),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
mip_level_count: Some(mip_level_count),
base_array_layer: 0,
array_layer_count: Some(1),
usage: Some(wgpu::TextureUsages::TEXTURE_BINDING),
});
Self(Arc::new(AnyTexture {
format,
texture,
view,
}))
}
fn calculate_mip_levels(width: u32, height: u32) -> u32 {
let max_dim = width.max(height);
(max_dim as f32).log2().floor() as u32 + 1
}
pub fn generate_mipmaps(&self, device: &wgpu::Device, queue: &wgpu::Queue) {
let texture = &self.0.texture;
let mip_level_count = texture.mip_level_count();
if mip_level_count <= 1 {
return;
}
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("mipmap_generation"),
});
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("mipmap_shader"),
source: wgpu::ShaderSource::Wgsl(include_str!("shaders/mipmap.wgsl").into()),
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("mipmap_pipeline"),
layout: None,
vertex: wgpu::VertexState {
module: &shader,
entry_point: Some("vs_main"),
compilation_options: wgpu::PipelineCompilationOptions::default(),
buffers: &[],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("fs_main"),
compilation_options: wgpu::PipelineCompilationOptions::default(),
targets: &[Some(wgpu::ColorTargetState {
format: self.0.format,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
});
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("mipmap_sampler"),
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::MipmapFilterMode::Linear,
..Default::default()
});
for mip_level in 1..mip_level_count {
let src_mip = mip_level - 1;
let dst_mip = mip_level;
let src_view = texture.create_view(&wgpu::TextureViewDescriptor {
label: Some(&format!("mipmap_src_view_{}", src_mip)),
format: Some(self.0.format),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: src_mip,
mip_level_count: Some(1),
base_array_layer: 0,
array_layer_count: Some(1),
usage: Some(wgpu::TextureUsages::TEXTURE_BINDING),
});
let dst_view = texture.create_view(&wgpu::TextureViewDescriptor {
label: Some(&format!("mipmap_dst_view_{}", dst_mip)),
format: Some(self.0.format),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: dst_mip,
mip_level_count: Some(1),
base_array_layer: 0,
array_layer_count: Some(1),
usage: Some(wgpu::TextureUsages::RENDER_ATTACHMENT),
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some(&format!("mipmap_bind_group_{}", mip_level)),
layout: &pipeline.get_bind_group_layout(0),
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&src_view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&sampler),
},
],
});
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some(&format!("mimap_pass_{}", mip_level)),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &dst_view,
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
render_pass.set_pipeline(&pipeline);
render_pass.set_bind_group(0, &bind_group, &[]);
render_pass.draw(0..6, 0..1);
}
queue.submit(Some(encoder.finish()));
}
}