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 resolve_target: Some(&targets.resolve),
171 ops: wgpu::Operations {
172 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
173 store: wgpu::StoreOp::Store,
174 },
175 })],
176 depth_stencil_attachment: None,
177 timestamp_writes: None,
178 occlusion_query_set: None,
179 })
180 }
181}
182
183#[derive(Debug, Clone)]
184struct Targets {
185 attachment: wgpu::TextureView,
186 resolve: wgpu::TextureView,
187 bind_group: wgpu::BindGroup,
188 size: Size<u32>,
189}
190
191impl Targets {
192 pub fn new(
193 device: &wgpu::Device,
194 format: wgpu::TextureFormat,
195 texture_layout: &wgpu::BindGroupLayout,
196 sample_count: u32,
197 size: Size<u32>,
198 ) -> Targets {
199 let extent = wgpu::Extent3d {
200 width: size.width,
201 height: size.height,
202 depth_or_array_layers: 1,
203 };
204
205 let attachment = device.create_texture(&wgpu::TextureDescriptor {
206 label: Some("iced_wgpu::triangle::msaa attachment"),
207 size: extent,
208 mip_level_count: 1,
209 sample_count,
210 dimension: wgpu::TextureDimension::D2,
211 format,
212 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
213 view_formats: &[],
214 });
215
216 let resolve = device.create_texture(&wgpu::TextureDescriptor {
217 label: Some("iced_wgpu::triangle::msaa resolve target"),
218 size: extent,
219 mip_level_count: 1,
220 sample_count: 1,
221 dimension: wgpu::TextureDimension::D2,
222 format,
223 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
224 | wgpu::TextureUsages::TEXTURE_BINDING,
225 view_formats: &[],
226 });
227
228 let attachment =
229 attachment.create_view(&wgpu::TextureViewDescriptor::default());
230
231 let resolve =
232 resolve.create_view(&wgpu::TextureViewDescriptor::default());
233
234 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
235 label: Some("iced_wgpu::triangle::msaa texture bind group"),
236 layout: texture_layout,
237 entries: &[wgpu::BindGroupEntry {
238 binding: 0,
239 resource: wgpu::BindingResource::TextureView(&resolve),
240 }],
241 });
242
243 Targets {
244 attachment,
245 resolve,
246 bind_group,
247 size,
248 }
249 }
250}
251
252#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
253#[repr(C)]
254struct Ratio {
255 u: f32,
256 v: f32,
257 _padding: [f32; 2],
260}
261
262pub struct State {
263 ratio: wgpu::Buffer,
264 constants: wgpu::BindGroup,
265 last_ratio: Option<Ratio>,
266}
267
268impl State {
269 pub fn new(device: &wgpu::Device, pipeline: &Pipeline) -> Self {
270 let ratio = device.create_buffer(&wgpu::BufferDescriptor {
271 label: Some("iced_wgpu::triangle::msaa ratio"),
272 size: std::mem::size_of::<Ratio>() as u64,
273 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM,
274 mapped_at_creation: false,
275 });
276
277 let constants = device.create_bind_group(&wgpu::BindGroupDescriptor {
278 label: Some("iced_wgpu::triangle::msaa uniforms bind group"),
279 layout: &pipeline.constant_layout,
280 entries: &[
281 wgpu::BindGroupEntry {
282 binding: 0,
283 resource: wgpu::BindingResource::Sampler(&pipeline.sampler),
284 },
285 wgpu::BindGroupEntry {
286 binding: 1,
287 resource: ratio.as_entire_binding(),
288 },
289 ],
290 });
291
292 Self {
293 ratio,
294 constants,
295 last_ratio: None,
296 }
297 }
298
299 pub fn prepare(
300 &mut self,
301 device: &wgpu::Device,
302 encoder: &mut wgpu::CommandEncoder,
303 belt: &mut wgpu::util::StagingBelt,
304 pipeline: &Pipeline,
305 region_size: Size<u32>,
306 ) -> Transformation {
307 let targets = pipeline.targets(device, region_size);
308
309 let ratio = Ratio {
310 u: region_size.width as f32 / targets.size.width as f32,
311 v: region_size.height as f32 / targets.size.height as f32,
312 _padding: [0.0; 2],
313 };
314
315 if Some(ratio) != self.last_ratio {
316 belt.write_buffer(
317 encoder,
318 &self.ratio,
319 0,
320 NonZeroU64::new(std::mem::size_of::<Ratio>() as u64)
321 .expect("non-empty ratio"),
322 device,
323 )
324 .copy_from_slice(bytemuck::bytes_of(&ratio));
325
326 self.last_ratio = Some(ratio);
327 }
328
329 Transformation::orthographic(targets.size.width, targets.size.height)
330 }
331
332 pub fn render(
333 &self,
334 pipeline: &Pipeline,
335 encoder: &mut wgpu::CommandEncoder,
336 target: &wgpu::TextureView,
337 ) {
338 let mut render_pass =
339 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
340 label: Some("iced_wgpu::triangle::msaa render pass"),
341 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
342 view: target,
343 resolve_target: None,
344 ops: wgpu::Operations {
345 load: wgpu::LoadOp::Load,
346 store: wgpu::StoreOp::Store,
347 },
348 })],
349 depth_stencil_attachment: None,
350 timestamp_writes: None,
351 occlusion_query_set: None,
352 });
353
354 render_pass.set_pipeline(&pipeline.raw);
355 render_pass.set_bind_group(0, &self.constants, &[]);
356 render_pass.set_bind_group(
357 1,
358 &pipeline
359 .targets
360 .read()
361 .expect("Read MSAA targets")
362 .as_ref()
363 .unwrap()
364 .bind_group,
365 &[],
366 );
367 render_pass.draw(0..6, 0..1);
368 }
369}