1static 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}