1mod gradient;
2mod solid;
3
4use gradient::Gradient;
5use solid::Solid;
6
7use crate::core::{Background, Rectangle, Transformation};
8use crate::graphics;
9use crate::graphics::color;
10
11use bytemuck::{Pod, Zeroable};
12
13use std::mem;
14
15const INITIAL_INSTANCES: usize = 2_000;
16
17#[derive(Clone, Copy, Debug, Pod, Zeroable)]
19#[repr(C)]
20pub struct Quad {
21 pub position: [f32; 2],
23
24 pub size: [f32; 2],
26
27 pub border_color: color::Packed,
29
30 pub border_radius: [f32; 4],
32
33 pub border_width: f32,
35
36 pub shadow_color: color::Packed,
38
39 pub shadow_offset: [f32; 2],
41
42 pub shadow_blur_radius: f32,
44}
45
46#[derive(Debug)]
47pub struct Pipeline {
48 solid: solid::Pipeline,
49 gradient: gradient::Pipeline,
50 constant_layout: wgpu::BindGroupLayout,
51 layers: Vec<Layer>,
52 prepare_layer: usize,
53}
54
55impl Pipeline {
56 pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Pipeline {
57 let constant_layout =
58 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
59 label: Some("iced_wgpu::quad uniforms layout"),
60 entries: &[wgpu::BindGroupLayoutEntry {
61 binding: 0,
62 visibility: wgpu::ShaderStages::VERTEX,
63 ty: wgpu::BindingType::Buffer {
64 ty: wgpu::BufferBindingType::Uniform,
65 has_dynamic_offset: false,
66 min_binding_size: wgpu::BufferSize::new(
67 mem::size_of::<Uniforms>() as wgpu::BufferAddress,
68 ),
69 },
70 count: None,
71 }],
72 });
73
74 Self {
75 solid: solid::Pipeline::new(device, format, &constant_layout),
76 gradient: gradient::Pipeline::new(device, format, &constant_layout),
77 layers: Vec::new(),
78 prepare_layer: 0,
79 constant_layout,
80 }
81 }
82
83 pub fn prepare(
84 &mut self,
85 device: &wgpu::Device,
86 encoder: &mut wgpu::CommandEncoder,
87 belt: &mut wgpu::util::StagingBelt,
88 quads: &Batch,
89 transformation: Transformation,
90 scale: f32,
91 ) {
92 if self.layers.len() <= self.prepare_layer {
93 self.layers.push(Layer::new(device, &self.constant_layout));
94 }
95
96 let layer = &mut self.layers[self.prepare_layer];
97 layer.prepare(device, encoder, belt, quads, transformation, scale);
98
99 self.prepare_layer += 1;
100 }
101
102 pub fn render<'a>(
103 &'a self,
104 layer: usize,
105 bounds: Rectangle<u32>,
106 quads: &Batch,
107 render_pass: &mut wgpu::RenderPass<'a>,
108 ) {
109 if let Some(layer) = self.layers.get(layer) {
110 render_pass.set_scissor_rect(
111 bounds.x,
112 bounds.y,
113 bounds.width,
114 bounds.height,
115 );
116
117 let mut solid_offset = 0;
118 let mut gradient_offset = 0;
119
120 for (kind, count) in &quads.order {
121 match kind {
122 Kind::Solid => {
123 self.solid.render(
124 render_pass,
125 &layer.constants,
126 &layer.solid,
127 solid_offset..(solid_offset + count),
128 );
129
130 solid_offset += count;
131 }
132 Kind::Gradient => {
133 self.gradient.render(
134 render_pass,
135 &layer.constants,
136 &layer.gradient,
137 gradient_offset..(gradient_offset + count),
138 );
139
140 gradient_offset += count;
141 }
142 }
143 }
144 }
145 }
146
147 pub fn end_frame(&mut self) {
148 self.prepare_layer = 0;
149 }
150}
151
152#[derive(Debug)]
153pub struct Layer {
154 constants: wgpu::BindGroup,
155 constants_buffer: wgpu::Buffer,
156 solid: solid::Layer,
157 gradient: gradient::Layer,
158}
159
160impl Layer {
161 pub fn new(
162 device: &wgpu::Device,
163 constant_layout: &wgpu::BindGroupLayout,
164 ) -> Self {
165 let constants_buffer = device.create_buffer(&wgpu::BufferDescriptor {
166 label: Some("iced_wgpu::quad uniforms buffer"),
167 size: mem::size_of::<Uniforms>() as wgpu::BufferAddress,
168 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
169 mapped_at_creation: false,
170 });
171
172 let constants = device.create_bind_group(&wgpu::BindGroupDescriptor {
173 label: Some("iced_wgpu::quad uniforms bind group"),
174 layout: constant_layout,
175 entries: &[wgpu::BindGroupEntry {
176 binding: 0,
177 resource: constants_buffer.as_entire_binding(),
178 }],
179 });
180
181 Self {
182 constants,
183 constants_buffer,
184 solid: solid::Layer::new(device),
185 gradient: gradient::Layer::new(device),
186 }
187 }
188
189 pub fn prepare(
190 &mut self,
191 device: &wgpu::Device,
192 encoder: &mut wgpu::CommandEncoder,
193 belt: &mut wgpu::util::StagingBelt,
194 quads: &Batch,
195 transformation: Transformation,
196 scale: f32,
197 ) {
198 self.update(device, encoder, belt, transformation, scale);
199
200 if !quads.solids.is_empty() {
201 self.solid.prepare(device, encoder, belt, &quads.solids);
202 }
203
204 if !quads.gradients.is_empty() {
205 self.gradient
206 .prepare(device, encoder, belt, &quads.gradients);
207 }
208 }
209
210 pub fn update(
211 &mut self,
212 device: &wgpu::Device,
213 encoder: &mut wgpu::CommandEncoder,
214 belt: &mut wgpu::util::StagingBelt,
215 transformation: Transformation,
216 scale: f32,
217 ) {
218 let uniforms = Uniforms::new(transformation, scale);
219 let bytes = bytemuck::bytes_of(&uniforms);
220
221 belt.write_buffer(
222 encoder,
223 &self.constants_buffer,
224 0,
225 (bytes.len() as u64).try_into().expect("Sized uniforms"),
226 device,
227 )
228 .copy_from_slice(bytes);
229 }
230}
231
232#[derive(Default, Debug)]
234pub struct Batch {
235 solids: Vec<Solid>,
237
238 gradients: Vec<Gradient>,
240
241 order: Order,
243}
244
245type Order = Vec<(Kind, usize)>;
247
248impl Batch {
249 pub fn is_empty(&self) -> bool {
251 self.solids.is_empty() && self.gradients.is_empty()
252 }
253
254 pub fn add(&mut self, quad: Quad, background: &Background) {
256 let kind = match background {
257 Background::Color(color) => {
258 self.solids.push(Solid {
259 color: color::pack(*color),
260 quad,
261 });
262
263 Kind::Solid
264 }
265 Background::Gradient(gradient) => {
266 self.gradients.push(Gradient {
267 gradient: graphics::gradient::pack(
268 gradient,
269 Rectangle::new(quad.position.into(), quad.size.into()),
270 ),
271 quad,
272 });
273
274 Kind::Gradient
275 }
276 };
277
278 match self.order.last_mut() {
279 Some((last_kind, count)) if kind == *last_kind => {
280 *count += 1;
281 }
282 _ => {
283 self.order.push((kind, 1));
284 }
285 }
286 }
287
288 pub fn clear(&mut self) {
289 self.solids.clear();
290 self.gradients.clear();
291 self.order.clear();
292 }
293}
294
295#[derive(Debug, Copy, Clone, PartialEq, Eq)]
296enum Kind {
298 Solid,
300 Gradient,
302}
303
304fn color_target_state(
305 format: wgpu::TextureFormat,
306) -> [Option<wgpu::ColorTargetState>; 1] {
307 [Some(wgpu::ColorTargetState {
308 format,
309 blend: Some(wgpu::BlendState {
310 color: wgpu::BlendComponent {
311 src_factor: wgpu::BlendFactor::SrcAlpha,
312 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
313 operation: wgpu::BlendOperation::Add,
314 },
315 alpha: wgpu::BlendComponent {
316 src_factor: wgpu::BlendFactor::One,
317 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
318 operation: wgpu::BlendOperation::Add,
319 },
320 }),
321 write_mask: wgpu::ColorWrites::ALL,
322 })]
323}
324
325#[repr(C)]
326#[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
327struct Uniforms {
328 transform: [f32; 16],
329 scale: f32,
330 _padding: [f32; 3],
333}
334
335impl Uniforms {
336 fn new(transformation: Transformation, scale: f32) -> Uniforms {
337 Self {
338 transform: *transformation.as_ref(),
339 scale,
340 _padding: [0.0; 3],
341 }
342 }
343}
344
345impl Default for Uniforms {
346 fn default() -> Self {
347 Self {
348 transform: *Transformation::IDENTITY.as_ref(),
349 scale: 1.0,
350 _padding: [0.0; 3],
351 }
352 }
353}