1use std::num::NonZeroU64;
4use wgpu::util::DeviceExt;
5
6#[repr(C)]
8#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
9pub struct SsaaUniforms {
10 pub ssaa_factor: u32,
11 _padding: [u32; 3],
12}
13
14pub struct SsaaPass {
16 pipeline: wgpu::RenderPipeline,
17 bind_group_layout: wgpu::BindGroupLayout,
18 uniform_buffer: wgpu::Buffer,
19 sampler: wgpu::Sampler,
20 ssaa_factor: u32,
21}
22
23impl SsaaPass {
24 #[must_use]
26 pub fn new(device: &wgpu::Device, output_format: wgpu::TextureFormat) -> Self {
27 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
29 label: Some("SSAA Downsample Bind Group Layout"),
30 entries: &[
31 wgpu::BindGroupLayoutEntry {
33 binding: 0,
34 visibility: wgpu::ShaderStages::FRAGMENT,
35 ty: wgpu::BindingType::Texture {
36 sample_type: wgpu::TextureSampleType::Float { filterable: true },
37 view_dimension: wgpu::TextureViewDimension::D2,
38 multisampled: false,
39 },
40 count: None,
41 },
42 wgpu::BindGroupLayoutEntry {
44 binding: 1,
45 visibility: wgpu::ShaderStages::FRAGMENT,
46 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
47 count: None,
48 },
49 wgpu::BindGroupLayoutEntry {
51 binding: 2,
52 visibility: wgpu::ShaderStages::FRAGMENT,
53 ty: wgpu::BindingType::Buffer {
54 ty: wgpu::BufferBindingType::Uniform,
55 has_dynamic_offset: false,
56 min_binding_size: NonZeroU64::new(16),
57 },
58 count: None,
59 },
60 ],
61 });
62
63 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
65 label: Some("SSAA Downsample Shader"),
66 source: wgpu::ShaderSource::Wgsl(include_str!("shaders/ssaa_downsample.wgsl").into()),
67 });
68
69 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
71 label: Some("SSAA Downsample Pipeline Layout"),
72 bind_group_layouts: &[&bind_group_layout],
73 push_constant_ranges: &[],
74 });
75
76 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
78 label: Some("SSAA Downsample Pipeline"),
79 layout: Some(&pipeline_layout),
80 vertex: wgpu::VertexState {
81 module: &shader,
82 entry_point: Some("vs_main"),
83 buffers: &[],
84 compilation_options: wgpu::PipelineCompilationOptions::default(),
85 },
86 fragment: Some(wgpu::FragmentState {
87 module: &shader,
88 entry_point: Some("fs_main"),
89 targets: &[Some(wgpu::ColorTargetState {
90 format: output_format,
91 blend: None,
92 write_mask: wgpu::ColorWrites::ALL,
93 })],
94 compilation_options: wgpu::PipelineCompilationOptions::default(),
95 }),
96 primitive: wgpu::PrimitiveState {
97 topology: wgpu::PrimitiveTopology::TriangleList,
98 ..Default::default()
99 },
100 depth_stencil: None,
101 multisample: wgpu::MultisampleState::default(),
102 multiview: None,
103 cache: None,
104 });
105
106 let uniforms = SsaaUniforms {
108 ssaa_factor: 1,
109 _padding: [0; 3],
110 };
111 let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
112 label: Some("SSAA Uniform Buffer"),
113 contents: bytemuck::cast_slice(&[uniforms]),
114 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
115 });
116
117 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
119 label: Some("SSAA Sampler"),
120 mag_filter: wgpu::FilterMode::Linear,
121 min_filter: wgpu::FilterMode::Linear,
122 ..Default::default()
123 });
124
125 Self {
126 pipeline,
127 bind_group_layout,
128 uniform_buffer,
129 sampler,
130 ssaa_factor: 1,
131 }
132 }
133
134 pub fn set_ssaa_factor(&mut self, queue: &wgpu::Queue, factor: u32) {
136 let factor = factor.clamp(1, 4);
138 if factor != self.ssaa_factor {
139 self.ssaa_factor = factor;
140 let uniforms = SsaaUniforms {
141 ssaa_factor: factor,
142 _padding: [0; 3],
143 };
144 queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
145 }
146 }
147
148 #[must_use]
150 pub fn ssaa_factor(&self) -> u32 {
151 self.ssaa_factor
152 }
153
154 #[must_use]
156 pub fn create_bind_group(
157 &self,
158 device: &wgpu::Device,
159 input_view: &wgpu::TextureView,
160 ) -> wgpu::BindGroup {
161 device.create_bind_group(&wgpu::BindGroupDescriptor {
162 label: Some("SSAA Bind Group"),
163 layout: &self.bind_group_layout,
164 entries: &[
165 wgpu::BindGroupEntry {
166 binding: 0,
167 resource: wgpu::BindingResource::TextureView(input_view),
168 },
169 wgpu::BindGroupEntry {
170 binding: 1,
171 resource: wgpu::BindingResource::Sampler(&self.sampler),
172 },
173 wgpu::BindGroupEntry {
174 binding: 2,
175 resource: self.uniform_buffer.as_entire_binding(),
176 },
177 ],
178 })
179 }
180
181 pub fn render(
183 &self,
184 encoder: &mut wgpu::CommandEncoder,
185 output_view: &wgpu::TextureView,
186 bind_group: &wgpu::BindGroup,
187 ) {
188 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
189 label: Some("SSAA Downsample Pass"),
190 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
191 view: output_view,
192 resolve_target: None,
193 ops: wgpu::Operations {
194 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
195 store: wgpu::StoreOp::Store,
196 },
197 depth_slice: None,
198 })],
199 depth_stencil_attachment: None,
200 ..Default::default()
201 });
202
203 render_pass.set_pipeline(&self.pipeline);
204 render_pass.set_bind_group(0, bind_group, &[]);
205 render_pass.draw(0..3, 0..1); }
207
208 pub fn render_to_target(
211 &self,
212 device: &wgpu::Device,
213 encoder: &mut wgpu::CommandEncoder,
214 input_view: &wgpu::TextureView,
215 output_view: &wgpu::TextureView,
216 ) {
217 let bind_group = self.create_bind_group(device, input_view);
218 self.render(encoder, output_view, &bind_group);
219 }
220}