iced_wgpu/
quad.rs

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