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 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#[derive(Default, Debug)]
239pub struct Batch {
240 solids: Vec<Solid>,
242
243 gradients: Vec<Gradient>,
245
246 order: Order,
248}
249
250type Order = Vec<(Kind, usize)>;
252
253impl Batch {
254 pub fn is_empty(&self) -> bool {
256 self.solids.is_empty() && self.gradients.is_empty()
257 }
258
259 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)]
307enum Kind {
309 Solid,
311 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 _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}