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