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