1use rootvg_core::math::PhysicalSizeI32;
6use wgpu::PipelineCompilationOptions;
7
8#[derive(Debug)]
9pub struct MsaaPipeline {
10 format: wgpu::TextureFormat,
11 pipeline: wgpu::RenderPipeline,
12 constants: wgpu::BindGroup,
13 texture_layout: wgpu::BindGroupLayout,
14 sample_count: u32,
15 targets: Option<Targets>,
16}
17
18impl MsaaPipeline {
19 pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat, sample_count: u32) -> Self {
20 let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default());
21
22 let constant_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
23 label: Some("rootvg-msaa uniforms layout"),
24 entries: &[wgpu::BindGroupLayoutEntry {
25 binding: 0,
26 visibility: wgpu::ShaderStages::FRAGMENT,
27 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
28 count: None,
29 }],
30 });
31
32 let constant_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
33 label: Some("rootvg-msaa uniforms bind group"),
34 layout: &constant_layout,
35 entries: &[wgpu::BindGroupEntry {
36 binding: 0,
37 resource: wgpu::BindingResource::Sampler(&sampler),
38 }],
39 });
40
41 let texture_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
42 label: Some("rootvg-msaa texture layout"),
43 entries: &[wgpu::BindGroupLayoutEntry {
44 binding: 0,
45 visibility: wgpu::ShaderStages::FRAGMENT,
46 ty: wgpu::BindingType::Texture {
47 sample_type: wgpu::TextureSampleType::Float { filterable: false },
48 view_dimension: wgpu::TextureViewDimension::D2,
49 multisampled: false,
50 },
51 count: None,
52 }],
53 });
54
55 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
56 label: Some("rootvg-msaa pipeline layout"),
57 push_constant_ranges: &[],
58 bind_group_layouts: &[&constant_layout, &texture_layout],
59 });
60
61 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
62 label: Some("rootvg triangle blit_shader"),
63 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!(
64 "shader/msaa.wgsl"
65 ))),
66 });
67
68 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
69 label: Some("rootvg-msaa pipeline"),
70 layout: Some(&layout),
71 vertex: wgpu::VertexState {
72 module: &shader,
73 entry_point: "vs_main",
74 buffers: &[],
75 compilation_options: PipelineCompilationOptions::default(),
76 },
77 fragment: Some(wgpu::FragmentState {
78 module: &shader,
79 entry_point: "fs_main",
80 targets: &[Some(wgpu::ColorTargetState {
81 format,
82 blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
83 write_mask: wgpu::ColorWrites::ALL,
84 })],
85 compilation_options: PipelineCompilationOptions::default(),
86 }),
87 primitive: wgpu::PrimitiveState {
88 topology: wgpu::PrimitiveTopology::TriangleList,
89 front_face: wgpu::FrontFace::Cw,
90 ..Default::default()
91 },
92 depth_stencil: None,
93 multisample: wgpu::MultisampleState {
94 count: 1,
95 mask: !0,
96 alpha_to_coverage_enabled: false,
97 },
98 multiview: None,
99 });
100
101 Self {
102 format,
103 pipeline,
104 constants: constant_bind_group,
105 texture_layout,
106 sample_count,
107 targets: None,
108 }
109 }
110
111 pub fn targets(
112 &mut self,
113 device: &wgpu::Device,
114 size: PhysicalSizeI32,
115 ) -> (&wgpu::TextureView, &wgpu::TextureView) {
116 match &mut self.targets {
117 None => {
118 self.targets = Some(Targets::new(
119 device,
120 self.format,
121 &self.texture_layout,
122 self.sample_count,
123 size,
124 ));
125 }
126 Some(targets) => {
127 if targets.size != size {
128 self.targets = Some(Targets::new(
129 device,
130 self.format,
131 &self.texture_layout,
132 self.sample_count,
133 size,
134 ));
135 }
136 }
137 }
138
139 let targets = self.targets.as_ref().unwrap();
140
141 (&targets.attachment, &targets.resolve)
142 }
143
144 pub fn render_to_target(
145 &self,
146 target: &wgpu::TextureView,
147 clear_color: Option<wgpu::Color>,
148 encoder: &mut wgpu::CommandEncoder,
149 ) {
150 let load = if let Some(color) = clear_color {
151 wgpu::LoadOp::Clear(color)
152 } else {
153 wgpu::LoadOp::Load
154 };
155
156 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
157 label: Some("rootvg-msaa render pass"),
158 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
159 view: target,
160 resolve_target: None,
161 ops: wgpu::Operations {
162 load,
163 store: wgpu::StoreOp::Store,
164 },
165 })],
166 depth_stencil_attachment: None,
167 timestamp_writes: None,
168 occlusion_query_set: None,
169 });
170
171 render_pass.set_pipeline(&self.pipeline);
172 render_pass.set_bind_group(0, &self.constants, &[]);
173 render_pass.set_bind_group(1, &self.targets.as_ref().unwrap().bind_group, &[]);
174 render_pass.draw(0..6, 0..1);
175 }
176}
177
178#[derive(Debug)]
179struct Targets {
180 attachment: wgpu::TextureView,
181 resolve: wgpu::TextureView,
182 bind_group: wgpu::BindGroup,
183 size: PhysicalSizeI32,
184}
185
186impl Targets {
187 pub fn new(
188 device: &wgpu::Device,
189 format: wgpu::TextureFormat,
190 texture_layout: &wgpu::BindGroupLayout,
191 sample_count: u32,
192 size: PhysicalSizeI32,
193 ) -> Targets {
194 assert!(size.width > 0);
195 assert!(size.height > 0);
196
197 let extent = wgpu::Extent3d {
198 width: size.width as u32,
199 height: size.height as u32,
200 depth_or_array_layers: 1,
201 };
202
203 let attachment = device.create_texture(&wgpu::TextureDescriptor {
204 label: Some("rootvg-msaa attachment"),
205 size: extent,
206 mip_level_count: 1,
207 sample_count,
208 dimension: wgpu::TextureDimension::D2,
209 format,
210 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
211 view_formats: &[],
212 });
213
214 let resolve = device.create_texture(&wgpu::TextureDescriptor {
215 label: Some("rootvg-msaa resolve target"),
216 size: extent,
217 mip_level_count: 1,
218 sample_count: 1,
219 dimension: wgpu::TextureDimension::D2,
220 format,
221 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
222 view_formats: &[],
223 });
224
225 let attachment = attachment.create_view(&wgpu::TextureViewDescriptor::default());
226
227 let resolve = resolve.create_view(&wgpu::TextureViewDescriptor::default());
228
229 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
230 label: Some("rootvg-msaa texture bind group"),
231 layout: texture_layout,
232 entries: &[wgpu::BindGroupEntry {
233 binding: 0,
234 resource: wgpu::BindingResource::TextureView(&resolve),
235 }],
236 });
237
238 Targets {
239 attachment,
240 resolve,
241 bind_group,
242 size,
243 }
244 }
245}