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