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(
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#[derive(Default, Debug)]
248pub struct Batch {
249 solids: Vec<Solid>,
251
252 gradients: Vec<Gradient>,
254
255 order: Order,
257}
258
259type Order = Vec<(Kind, usize)>;
261
262impl Batch {
263 pub fn is_empty(&self) -> bool {
265 self.solids.is_empty() && self.gradients.is_empty()
266 }
267
268 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)]
310enum Kind {
312 Solid,
314 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 _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}