1use crate::core::{Size, Transformation};
2use crate::graphics;
3
4use std::num::NonZeroU64;
5use std::sync::{Arc, RwLock};
6
7#[derive(Debug, Clone)]
8pub struct Pipeline {
9 format: wgpu::TextureFormat,
10 sampler: wgpu::Sampler,
11 raw: wgpu::RenderPipeline,
12 constant_layout: wgpu::BindGroupLayout,
13 texture_layout: wgpu::BindGroupLayout,
14 sample_count: u32,
15 targets: Arc<RwLock<Option<Targets>>>,
16}
17
18impl Pipeline {
19 pub fn new(
20 device: &wgpu::Device,
21 format: wgpu::TextureFormat,
22 antialiasing: graphics::Antialiasing,
23 ) -> Pipeline {
24 let sampler =
25 device.create_sampler(&wgpu::SamplerDescriptor::default());
26
27 let constant_layout =
28 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
29 label: Some("iced_wgpu::triangle:msaa uniforms layout"),
30 entries: &[
31 wgpu::BindGroupLayoutEntry {
32 binding: 0,
33 visibility: wgpu::ShaderStages::FRAGMENT,
34 ty: wgpu::BindingType::Sampler(
35 wgpu::SamplerBindingType::NonFiltering,
36 ),
37 count: None,
38 },
39 wgpu::BindGroupLayoutEntry {
40 binding: 1,
41 visibility: wgpu::ShaderStages::VERTEX,
42 ty: wgpu::BindingType::Buffer {
43 ty: wgpu::BufferBindingType::Uniform,
44 has_dynamic_offset: false,
45 min_binding_size: None,
46 },
47 count: None,
48 },
49 ],
50 });
51
52 let texture_layout =
53 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
54 label: Some("iced_wgpu::triangle::msaa texture layout"),
55 entries: &[wgpu::BindGroupLayoutEntry {
56 binding: 0,
57 visibility: wgpu::ShaderStages::FRAGMENT,
58 ty: wgpu::BindingType::Texture {
59 sample_type: wgpu::TextureSampleType::Float {
60 filterable: false,
61 },
62 view_dimension: wgpu::TextureViewDimension::D2,
63 multisampled: false,
64 },
65 count: None,
66 }],
67 });
68
69 let layout =
70 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
71 label: Some("iced_wgpu::triangle::msaa pipeline layout"),
72 push_constant_ranges: &[],
73 bind_group_layouts: &[&constant_layout, &texture_layout],
74 });
75
76 let shader =
77 device.create_shader_module(wgpu::ShaderModuleDescriptor {
78 label: Some("iced_wgpu triangle blit_shader"),
79 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
80 include_str!("../shader/blit.wgsl"),
81 )),
82 });
83
84 let pipeline =
85 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
86 label: Some("iced_wgpu::triangle::msaa pipeline"),
87 layout: Some(&layout),
88 vertex: wgpu::VertexState {
89 module: &shader,
90 entry_point: Some("vs_main"),
91 buffers: &[],
92 compilation_options:
93 wgpu::PipelineCompilationOptions::default(),
94 },
95 fragment: Some(wgpu::FragmentState {
96 module: &shader,
97 entry_point: Some("fs_main"),
98 targets: &[Some(wgpu::ColorTargetState {
99 format,
100 blend: Some(
101 wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING,
102 ),
103 write_mask: wgpu::ColorWrites::ALL,
104 })],
105 compilation_options:
106 wgpu::PipelineCompilationOptions::default(),
107 }),
108 primitive: wgpu::PrimitiveState {
109 topology: wgpu::PrimitiveTopology::TriangleList,
110 front_face: wgpu::FrontFace::Cw,
111 ..Default::default()
112 },
113 depth_stencil: None,
114 multisample: wgpu::MultisampleState {
115 count: 1,
116 mask: !0,
117 alpha_to_coverage_enabled: false,
118 },
119 multiview: None,
120 cache: None,
121 });
122
123 Self {
124 format,
125 sampler,
126 raw: pipeline,
127 constant_layout,
128 texture_layout,
129 sample_count: antialiasing.sample_count(),
130 targets: Arc::new(RwLock::new(None)),
131 }
132 }
133
134 fn targets(
135 &self,
136 device: &wgpu::Device,
137 region_size: Size<u32>,
138 ) -> Targets {
139 let mut targets = self.targets.write().expect("Write MSAA targets");
140
141 match targets.as_mut() {
142 Some(targets)
143 if region_size.width <= targets.size.width
144 && region_size.height <= targets.size.height => {}
145 _ => {
146 *targets = Some(Targets::new(
147 device,
148 self.format,
149 &self.texture_layout,
150 self.sample_count,
151 region_size,
152 ));
153 }
154 }
155
156 targets.as_ref().unwrap().clone()
157 }
158
159 pub fn render_pass<'a>(
160 &self,
161 encoder: &'a mut wgpu::CommandEncoder,
162 ) -> wgpu::RenderPass<'a> {
163 let targets = self.targets.read().expect("Read MSAA targets");
164 let targets = targets.as_ref().unwrap();
165
166 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
167 label: Some("iced_wgpu.triangle.render_pass"),
168 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
169 view: &targets.attachment,
170 depth_slice: None,
171 resolve_target: Some(&targets.resolve),
172 ops: wgpu::Operations {
173 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
174 store: wgpu::StoreOp::Store,
175 },
176 })],
177 depth_stencil_attachment: None,
178 timestamp_writes: None,
179 occlusion_query_set: None,
180 })
181 }
182}
183
184#[derive(Debug, Clone)]
185struct Targets {
186 attachment: wgpu::TextureView,
187 resolve: wgpu::TextureView,
188 bind_group: wgpu::BindGroup,
189 size: Size<u32>,
190}
191
192impl Targets {
193 pub fn new(
194 device: &wgpu::Device,
195 format: wgpu::TextureFormat,
196 texture_layout: &wgpu::BindGroupLayout,
197 sample_count: u32,
198 size: Size<u32>,
199 ) -> Targets {
200 let extent = wgpu::Extent3d {
201 width: size.width,
202 height: size.height,
203 depth_or_array_layers: 1,
204 };
205
206 let attachment = device.create_texture(&wgpu::TextureDescriptor {
207 label: Some("iced_wgpu::triangle::msaa attachment"),
208 size: extent,
209 mip_level_count: 1,
210 sample_count,
211 dimension: wgpu::TextureDimension::D2,
212 format,
213 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
214 view_formats: &[],
215 });
216
217 let resolve = device.create_texture(&wgpu::TextureDescriptor {
218 label: Some("iced_wgpu::triangle::msaa resolve target"),
219 size: extent,
220 mip_level_count: 1,
221 sample_count: 1,
222 dimension: wgpu::TextureDimension::D2,
223 format,
224 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
225 | wgpu::TextureUsages::TEXTURE_BINDING,
226 view_formats: &[],
227 });
228
229 let attachment =
230 attachment.create_view(&wgpu::TextureViewDescriptor::default());
231
232 let resolve =
233 resolve.create_view(&wgpu::TextureViewDescriptor::default());
234
235 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
236 label: Some("iced_wgpu::triangle::msaa texture bind group"),
237 layout: texture_layout,
238 entries: &[wgpu::BindGroupEntry {
239 binding: 0,
240 resource: wgpu::BindingResource::TextureView(&resolve),
241 }],
242 });
243
244 Targets {
245 attachment,
246 resolve,
247 bind_group,
248 size,
249 }
250 }
251}
252
253#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
254#[repr(C)]
255struct Ratio {
256 u: f32,
257 v: f32,
258 _padding: [f32; 2],
261}
262
263pub struct State {
264 ratio: wgpu::Buffer,
265 constants: wgpu::BindGroup,
266 last_ratio: Option<Ratio>,
267}
268
269impl State {
270 pub fn new(device: &wgpu::Device, pipeline: &Pipeline) -> Self {
271 let ratio = device.create_buffer(&wgpu::BufferDescriptor {
272 label: Some("iced_wgpu::triangle::msaa ratio"),
273 size: std::mem::size_of::<Ratio>() as u64,
274 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM,
275 mapped_at_creation: false,
276 });
277
278 let constants = device.create_bind_group(&wgpu::BindGroupDescriptor {
279 label: Some("iced_wgpu::triangle::msaa uniforms bind group"),
280 layout: &pipeline.constant_layout,
281 entries: &[
282 wgpu::BindGroupEntry {
283 binding: 0,
284 resource: wgpu::BindingResource::Sampler(&pipeline.sampler),
285 },
286 wgpu::BindGroupEntry {
287 binding: 1,
288 resource: ratio.as_entire_binding(),
289 },
290 ],
291 });
292
293 Self {
294 ratio,
295 constants,
296 last_ratio: None,
297 }
298 }
299
300 pub fn prepare(
301 &mut self,
302 device: &wgpu::Device,
303 encoder: &mut wgpu::CommandEncoder,
304 belt: &mut wgpu::util::StagingBelt,
305 pipeline: &Pipeline,
306 region_size: Size<u32>,
307 ) -> Transformation {
308 let targets = pipeline.targets(device, region_size);
309
310 let ratio = Ratio {
311 u: region_size.width as f32 / targets.size.width as f32,
312 v: region_size.height as f32 / targets.size.height as f32,
313 _padding: [0.0; 2],
314 };
315
316 if Some(ratio) != self.last_ratio {
317 belt.write_buffer(
318 encoder,
319 &self.ratio,
320 0,
321 NonZeroU64::new(std::mem::size_of::<Ratio>() as u64)
322 .expect("non-empty ratio"),
323 device,
324 )
325 .copy_from_slice(bytemuck::bytes_of(&ratio));
326
327 self.last_ratio = Some(ratio);
328 }
329
330 Transformation::orthographic(targets.size.width, targets.size.height)
331 }
332
333 pub fn render(
334 &self,
335 pipeline: &Pipeline,
336 encoder: &mut wgpu::CommandEncoder,
337 target: &wgpu::TextureView,
338 ) {
339 let mut render_pass =
340 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
341 label: Some("iced_wgpu::triangle::msaa render pass"),
342 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
343 view: target,
344 depth_slice: None,
345 resolve_target: None,
346 ops: wgpu::Operations {
347 load: wgpu::LoadOp::Load,
348 store: wgpu::StoreOp::Store,
349 },
350 })],
351 depth_stencil_attachment: None,
352 timestamp_writes: None,
353 occlusion_query_set: None,
354 });
355
356 render_pass.set_pipeline(&pipeline.raw);
357 render_pass.set_bind_group(0, &self.constants, &[]);
358 render_pass.set_bind_group(
359 1,
360 &pipeline
361 .targets
362 .read()
363 .expect("Read MSAA targets")
364 .as_ref()
365 .unwrap()
366 .bind_group,
367 &[],
368 );
369 render_pass.draw(0..6, 0..1);
370 }
371}