peter_engine/
mipmapping.rs

1// Based on: https://github.com/gfx-rs/wgpu/blob/trunk/examples/mipmap/src/main.rs
2
3static SHADER_SOURCE: &str = r#"
4struct MipMapVertexOutput {
5  @builtin(position) clip_position: vec4<f32>,
6  @location(0) uv: vec2<f32>,
7}
8
9@vertex
10fn mipmap_gen_vertex_shader_main(@builtin(vertex_index) vertex_index: u32) -> MipMapVertexOutput {
11  var out: MipMapVertexOutput;
12  let x = i32(vertex_index) / 2;
13  let y = i32(vertex_index) & 1;
14  let tc = vec2<f32>(
15    f32(x) * 2.0,
16    f32(y) * 2.0,
17  );
18  out.clip_position = vec4<f32>(
19    tc.x * 2.0 - 1.0,
20    1.0 - tc.y * 2.0,
21    0.0, 1.0,
22  );
23  out.uv = tc;
24  return out;
25}
26
27@group(0) @binding(0) var t_diffuse: texture_2d<f32>;
28@group(0) @binding(1) var s_diffuse: sampler;
29
30@fragment
31fn mipmap_gen_fragment_shader_main(in: MipMapVertexOutput) -> @location(0) vec4<f32> {
32  return textureSample(t_diffuse, s_diffuse, in.uv);
33}
34"#;
35
36pub struct MipMapGen {
37  pipeline: wgpu::RenderPipeline,
38  sampler:  wgpu::Sampler,
39}
40
41impl MipMapGen {
42  pub fn new(device: &wgpu::Device, texture_format: wgpu::TextureFormat) -> Self {
43    let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
44      label:  Some("mipmap_gen_shader_module"),
45      source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(SHADER_SOURCE)),
46    });
47    let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
48      entries: &[
49        wgpu::BindGroupLayoutEntry {
50          binding:    0,
51          visibility: wgpu::ShaderStages::FRAGMENT,
52          ty:         wgpu::BindingType::Texture {
53            multisampled:   false,
54            view_dimension: wgpu::TextureViewDimension::D2,
55            sample_type:    wgpu::TextureSampleType::Float { filterable: true },
56          },
57          count:      None,
58        },
59        wgpu::BindGroupLayoutEntry {
60          binding:    1,
61          visibility: wgpu::ShaderStages::FRAGMENT,
62          ty:         wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
63          count:      None,
64        },
65      ],
66      label:   Some("mipmap_gen_bind_group_layout"),
67    });
68    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
69      label:                Some("mipmap_gen_pipeline_layout"),
70      bind_group_layouts:   &[&bind_group_layout],
71      push_constant_ranges: &[],
72    });
73    let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
74      label:         Some("mipmap_gen_pipeline"),
75      layout:        Some(&pipeline_layout),
76      vertex:        wgpu::VertexState {
77        module:      &shader,
78        entry_point: "mipmap_gen_vertex_shader_main",
79        buffers:     &[],
80      },
81      fragment:      Some(wgpu::FragmentState {
82        module:      &shader,
83        entry_point: "mipmap_gen_fragment_shader_main",
84        targets:     &[Some(texture_format.into())],
85      }),
86      primitive:     wgpu::PrimitiveState {
87        topology: wgpu::PrimitiveTopology::TriangleList,
88        polygon_mode: wgpu::PolygonMode::Fill,
89        ..Default::default()
90      },
91      depth_stencil: None,
92      multisample:   wgpu::MultisampleState::default(),
93      multiview:     None,
94    });
95    let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
96      label: Some("mipmap_gen_sampler"),
97      address_mode_u: wgpu::AddressMode::ClampToEdge,
98      address_mode_v: wgpu::AddressMode::ClampToEdge,
99      address_mode_w: wgpu::AddressMode::ClampToEdge,
100      mag_filter: wgpu::FilterMode::Linear,
101      min_filter: wgpu::FilterMode::Linear,
102      mipmap_filter: wgpu::FilterMode::Nearest,
103      ..Default::default()
104    });
105    Self { pipeline, sampler }
106  }
107
108  pub fn generate_mipmaps(
109    &self,
110    encoder: &mut wgpu::CommandEncoder,
111    device: &wgpu::Device,
112    texture: &wgpu::Texture,
113    mip_count: u32,
114  ) {
115    let bind_group_layout = self.pipeline.get_bind_group_layout(0);
116    for target_mip in 1..mip_count {
117      let create_view = |base_mip_level| {
118        texture.create_view(&wgpu::TextureViewDescriptor {
119          label: Some("mipmap_read"),
120          format: None,
121          dimension: None,
122          aspect: wgpu::TextureAspect::All,
123          base_mip_level,
124          mip_level_count: Some(1),
125          base_array_layer: 0,
126          array_layer_count: None,
127        })
128      };
129      let read_view = create_view(target_mip - 1);
130      let write_view = create_view(target_mip);
131      let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
132        layout:  &bind_group_layout,
133        entries: &[
134          wgpu::BindGroupEntry {
135            binding:  0,
136            resource: wgpu::BindingResource::TextureView(&read_view),
137          },
138          wgpu::BindGroupEntry {
139            binding:  1,
140            resource: wgpu::BindingResource::Sampler(&self.sampler),
141          },
142        ],
143        label:   None,
144      });
145
146      let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
147        label:                    None,
148        color_attachments:        &[Some(wgpu::RenderPassColorAttachment {
149          view:           &write_view,
150          resolve_target: None,
151          ops:            wgpu::Operations {
152            load:  wgpu::LoadOp::Clear(wgpu::Color::WHITE),
153            store: true,
154          },
155        })],
156        depth_stencil_attachment: None,
157      });
158      rpass.set_pipeline(&self.pipeline);
159      rpass.set_bind_group(0, &bind_group, &[]);
160      rpass.draw(0..3, 0..1);
161    }
162  }
163}