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