use crate::rendering::texture::Texture; use std::collections::HashMap; use tracing::debug; use wgpu::{ include_wgsl, BindGroupDescriptor, BindGroupEntry, BindingResource, Color, ColorTargetState, ColorWrites, CommandEncoderDescriptor, Device, FilterMode, FragmentState, LoadOp, Operations, Queue, RenderPassColorAttachment, RenderPassDescriptor, RenderPipeline, RenderPipelineDescriptor, Sampler, SamplerDescriptor, ShaderModule, StoreOp, TextureFormat, TextureViewDescriptor, VertexState, }; pub struct MipGenerator { shader: ShaderModule, sampler: Sampler, pipelines: HashMap, } impl MipGenerator { pub fn new(device: &Device) -> Self { debug!("initializing MipGenerator, compiling shader module"); let shader = device.create_shader_module(include_wgsl!("../shaders/text_quad_mips.wgsl")); Self { shader, sampler: device.create_sampler(&SamplerDescriptor { min_filter: FilterMode::Linear, label: Some("MipGenerator sampler"), ..Default::default() }), pipelines: HashMap::new(), } } pub fn generate_mips(&mut self, texture: &Texture, device: &Device, queue: &Queue) { let pipeline = self .pipelines .entry(texture.texture.format()) .or_insert_with(|| { device.create_render_pipeline(&RenderPipelineDescriptor { label: Some("MipGenerator format pipeline"), layout: None, vertex: VertexState { module: &self.shader, entry_point: Some("vs"), compilation_options: Default::default(), buffers: &[], }, primitive: Default::default(), depth_stencil: None, multisample: Default::default(), fragment: Some(FragmentState { module: &self.shader, entry_point: Some("fs"), compilation_options: Default::default(), targets: &[Some(ColorTargetState { format: texture.texture.format(), blend: None, write_mask: ColorWrites::default(), })], }), multiview: None, cache: None, }) }); let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { label: Some("MipGenerator command encoder"), }); let mut width = texture.texture.width(); let mut height = texture.texture.height(); let mut base_mip_level = 0; while width > 1 || height > 1 { width = 1.max(width / 2); height = 1.max(height / 2); let bind_group = device.create_bind_group(&BindGroupDescriptor { label: Some("MipGenerator bind group"), layout: &pipeline.get_bind_group_layout(0), entries: &[ BindGroupEntry { binding: 0, resource: BindingResource::Sampler(&self.sampler), }, BindGroupEntry { binding: 1, resource: BindingResource::TextureView(&texture.texture.create_view( &TextureViewDescriptor { base_mip_level, mip_level_count: Some(1), ..Default::default() }, )), }, ], }); base_mip_level += 1; let texture_view = texture.texture.create_view(&TextureViewDescriptor { base_mip_level, mip_level_count: Some(1), ..Default::default() }); let render_pass_descriptor = RenderPassDescriptor { label: Some("MipGenerator render pass"), color_attachments: &[Some(RenderPassColorAttachment { view: &texture_view, resolve_target: None, ops: Operations { load: LoadOp::Clear(Color { r: 0.0, g: 0.0, b: 0.0, a: 1.0, }), store: StoreOp::Store, }, })], depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, }; let mut pass = encoder.begin_render_pass(&render_pass_descriptor); pass.set_pipeline(pipeline); pass.set_bind_group(0, Some(&bind_group), &[]); pass.draw(0..6, 0..1); } let command_buffer = encoder.finish(); queue.submit(std::iter::once(command_buffer)); } }