1use std::rc::Rc;
2
3use crate::{
4 renderer::sdf_sprite::AlphaSdfParams, Aabb, BindableTexture, Color, GraphicsContext,
5 GrowableBuffer, VertexT,
6};
7use wgpu::BufferUsages;
8
9use crate::ui::{
10 element::{ComputedBounds, DivComputed, SdfTextureRegion, Section, TextureRegion},
11 layout::GlyphBoundsAndUv,
12 Corners, Div, DivTexture, ElementWithComputed, SdfFont, TextSection,
13};
14
15use crate::utils::rc_addr_as_u64;
16
17#[repr(C)]
18#[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
19pub struct RectRaw {
20 pub bounds: Aabb,
21 pub color: Color,
22 pub border_radius: Corners<f32>,
23 pub border_color: Color,
24 border_width: f32,
26 border_softness: f32,
27 shadow_width: f32,
28 shadow_curve: f32,
29 shadow_color: Color,
30}
31
32impl VertexT for RectRaw {
33 const ATTRIBUTES: &'static [wgpu::VertexFormat] = &[
34 wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, ];
41}
42
43impl RectRaw {
44 fn new(div: &Div, computed: &DivComputed) -> Self {
45 RectRaw {
46 bounds: bounds_from_computed(&computed.bounds),
47 color: div.color,
48 border_radius: div.border.radius,
49 border_color: div.border.color,
50 border_width: div.border.width,
51 border_softness: div.border.softness,
52 shadow_width: div.shadow.width,
53 shadow_curve: div.shadow.curve_param,
54 shadow_color: div.shadow.color,
55 }
56 }
57}
58
59#[inline(always)]
60fn bounds_from_computed(computed: &ComputedBounds) -> Aabb {
61 let pos = computed.pos.as_vec2();
62 let size = computed.size.as_vec2();
63 Aabb::new(pos, pos + size)
64}
65
66#[repr(C)]
67#[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
68pub struct TexturedRectRaw {
69 pub rect: RectRaw,
70 pub uv: Aabb,
71}
72
73impl VertexT for TexturedRectRaw {
74 const ATTRIBUTES: &'static [wgpu::VertexFormat] = &[
75 wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, ];
83}
84
85#[repr(C)]
86#[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
87pub struct AlphaSdfRectRaw {
88 pub bounds: Aabb,
89 pub color: Color,
90 pub params: AlphaSdfParams,
91 pub uv: Aabb,
92}
93
94impl VertexT for AlphaSdfRectRaw {
95 const ATTRIBUTES: &'static [wgpu::VertexFormat] = &[
96 wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, ];
102}
103
104#[repr(C)]
105#[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
106pub struct GlyphRaw {
107 pub bounds: Aabb,
108 pub color: Color,
109 pub uv: Aabb,
110 pub shadow_intensity: f32,
111}
112
113impl VertexT for GlyphRaw {
114 const ATTRIBUTES: &'static [wgpu::VertexFormat] = &[
115 wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32x4, wgpu::VertexFormat::Float32, ];
120}
121
122#[derive(Debug)]
123pub struct Batch {
124 pub key: u64,
127 pub range: std::ops::Range<usize>,
128 pub kind: BatchKind,
129}
130
131#[derive(Debug)]
132pub enum BatchKind {
133 Rect,
134 TexturedRect(Rc<BindableTexture>),
135 AlphaSdfRect(Rc<BindableTexture>),
136 Glyph(Rc<SdfFont>),
137}
138
139#[derive(Debug, Default)]
140pub struct ElementBatches {
141 pub rects: Vec<RectRaw>,
142 pub textured_rects: Vec<TexturedRectRaw>,
143 pub alpha_sdf_rects: Vec<AlphaSdfRectRaw>,
144 pub glyphs: Vec<GlyphRaw>,
145 pub batches: Vec<Batch>,
146}
147
148pub enum PrimElement<'a> {
149 Rect(&'a (Div, DivComputed)),
150 TexturedRect(&'a (Div, DivComputed), &'a TextureRegion),
151 AlphaSdfRect(&'a (Div, DivComputed), &'a SdfTextureRegion),
152 Text(&'a TextSection, &'a [GlyphBoundsAndUv]),
153}
154
155impl<'a> PrimElement<'a> {
156 fn batch_key(&self) -> u64 {
157 match self {
158 PrimElement::Rect(_) => 0,
159 PrimElement::TexturedRect(_, texture) => rc_addr_as_u64(&texture.texture),
160 PrimElement::Text(text, _) => rc_addr_as_u64(&text.font),
161 PrimElement::AlphaSdfRect(_, sdf_texture) => {
162 rc_addr_as_u64(&sdf_texture.region.texture) ^ 21891209983212317
163 }
165 }
166 }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
175pub struct StackingLevel {
176 z_index: i16,
177 text_level: u16,
182 nesting_level: u16,
183}
184
185impl StackingLevel {
186 pub fn new(z_index: i16, text_level: u16, nesting_level: u16) -> Self {
187 StackingLevel {
188 nesting_level,
189 text_level,
190 z_index,
191 }
192 }
193 pub const ZERO: StackingLevel = StackingLevel {
194 z_index: 0,
195 text_level: 0,
196 nesting_level: 0,
197 };
198}
199
200impl PartialOrd for StackingLevel {
201 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
202 match self.z_index.partial_cmp(&other.z_index) {
203 Some(core::cmp::Ordering::Equal) => {}
204 ord => return ord,
205 }
206 match self.text_level.partial_cmp(&other.text_level) {
207 Some(core::cmp::Ordering::Equal) => {}
208 ord => return ord,
209 }
210 self.nesting_level.partial_cmp(&other.nesting_level)
211 }
212}
213
214impl Ord for StackingLevel {
215 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
216 match self.z_index.cmp(&other.z_index) {
217 core::cmp::Ordering::Equal => {}
218 ord => return ord,
219 }
220 match self.text_level.cmp(&other.text_level) {
221 core::cmp::Ordering::Equal => {}
222 ord => return ord,
223 }
224 self.nesting_level.cmp(&other.nesting_level)
225 }
226}
227
228impl ElementWithComputed {
229 pub fn get_batches(&self) -> ElementBatches {
230 get_batches(&[&self])
231 }
232
233 fn collect_prim_elements<'a>(
235 &'a self,
236 mut level: StackingLevel,
237 prim_elements: &mut Vec<(StackingLevel, PrimElement<'a>)>,
238 ) {
239 level.nesting_level += 1;
240
241 match self {
242 ElementWithComputed::Div(div) => {
243 level.z_index += div.0.z_index;
244
245 if div.0.color != Color::TRANSPARENT {
247 let prim = match &div.0.texture {
248 DivTexture::None => PrimElement::Rect(div),
249 DivTexture::Texture(texture) => PrimElement::TexturedRect(div, texture),
250 DivTexture::AlphaSdfTexture(sdf_texture) => {
251 PrimElement::AlphaSdfRect(div, sdf_texture)
252 }
253 };
254
255 prim_elements.push((level, prim));
256 }
257
258 for ch in div.0.children.iter() {
259 ch.element().collect_prim_elements(level, prim_elements);
260 }
261 }
262 ElementWithComputed::Text(text) => {
263 level.text_level += 1;
264
265 let mut i: usize = 0;
266 for section in text.0.sections.iter() {
267 match section {
268 Section::Text(text_section) => {
269 let glyph_range = text.1.text_section_glyphs[i].clone();
270 i += 1;
271 let glyphs = &text.1.glyphs[glyph_range];
272 let prim = PrimElement::Text(text_section, glyphs);
273 prim_elements.push((level, prim));
274 }
275 Section::Element { element, .. } => {
276 element
277 .element()
278 .collect_prim_elements(level, prim_elements);
279 }
280 }
281 }
282 }
283 }
284 }
285}
286
287pub fn get_batches(elements: &[&ElementWithComputed]) -> ElementBatches {
288 let mut prim_elements: Vec<(StackingLevel, PrimElement)> = vec![];
290 for element in elements {
291 element.collect_prim_elements(StackingLevel::ZERO, &mut prim_elements);
292 }
293
294 prim_elements.sort_by(|a, b| a.0.cmp(&b.0));
296
297 let mut rects: Vec<RectRaw> = vec![];
299 let mut textured_rects: Vec<TexturedRectRaw> = vec![];
300 let mut alpha_sdf_rects: Vec<AlphaSdfRectRaw> = vec![];
301 let mut glyphs: Vec<GlyphRaw> = vec![];
302 let mut batches: Vec<Batch> = vec![];
303
304 for (_level, element) in prim_elements {
305 let key = element.batch_key();
306
307 let add_new_batch = match batches.last_mut() {
308 Some(batch) => {
309 if batch.key != key {
310 let batch_end = match batch.kind {
312 BatchKind::Rect => rects.len(),
313 BatchKind::TexturedRect(_) => textured_rects.len(),
314 BatchKind::Glyph(_) => glyphs.len(),
315 BatchKind::AlphaSdfRect(_) => alpha_sdf_rects.len(),
316 };
317 batch.range.end = batch_end;
318 true
319 } else {
320 false
322 }
323 }
324 None => true,
325 };
326
327 if add_new_batch {
329 let batch = match &element {
330 PrimElement::Rect(_) => Batch {
331 key,
332 range: rects.len()..rects.len(),
333 kind: BatchKind::Rect,
334 },
335 PrimElement::TexturedRect(_, texture) => Batch {
336 key,
337 range: textured_rects.len()..textured_rects.len(),
338 kind: BatchKind::TexturedRect(texture.texture.clone()),
339 },
340 PrimElement::AlphaSdfRect(_, sdf_texture) => Batch {
341 key,
342 range: alpha_sdf_rects.len()..alpha_sdf_rects.len(),
343 kind: BatchKind::AlphaSdfRect(sdf_texture.region.texture.clone()),
344 },
345 PrimElement::Text(section, _) => Batch {
346 key,
347 range: glyphs.len()..glyphs.len(),
348 kind: BatchKind::Glyph(section.font.clone()),
349 },
350 };
351 batches.push(batch);
352 }
353
354 match element {
356 PrimElement::Rect((div, computed)) => {
357 let rect = RectRaw::new(div, computed);
358 rects.push(rect);
359 }
360 PrimElement::TexturedRect((div, computed), texture) => {
361 let rect = RectRaw::new(div, computed);
362 let textured_rect = TexturedRectRaw {
363 rect,
364 uv: texture.uv,
365 };
366 textured_rects.push(textured_rect);
367 }
368 PrimElement::AlphaSdfRect((div, computed), sdf_texture) => {
369 let alpha_sdf_rect = AlphaSdfRectRaw {
370 bounds: bounds_from_computed(&computed.bounds),
371 color: div.color,
372 params: sdf_texture.params,
373 uv: sdf_texture.region.uv,
374 };
375 alpha_sdf_rects.push(alpha_sdf_rect);
376 }
377 PrimElement::Text(section, text_glyphs) => {
378 for g in text_glyphs {
379 let glyph_raw = GlyphRaw {
380 bounds: g.bounds.into(),
381 color: section.color,
382 uv: g.uv,
383 shadow_intensity: section.shadow_intensity,
384 };
385 glyphs.push(glyph_raw);
386 }
387 }
388 }
389 }
390
391 if let Some(batch) = batches.last_mut() {
393 let batch_end = match batch.kind {
394 BatchKind::Rect => rects.len(),
395 BatchKind::TexturedRect(_) => textured_rects.len(),
396 BatchKind::AlphaSdfRect(_) => alpha_sdf_rects.len(),
397 BatchKind::Glyph(_) => glyphs.len(),
398 };
399 batch.range.end = batch_end;
400 }
401
402 ElementBatches {
403 rects,
404 textured_rects,
405 glyphs,
406 batches,
407 alpha_sdf_rects,
408 }
409}
410
411#[derive(Debug)]
412pub struct ElementBatchesGR {
413 pub rects: GrowableBuffer<RectRaw>,
414 pub textured_rects: GrowableBuffer<TexturedRectRaw>,
415 pub alpha_sdf_rects: GrowableBuffer<AlphaSdfRectRaw>,
416 pub glyphs: GrowableBuffer<GlyphRaw>,
417}
418
419impl ElementBatchesGR {
420 pub fn new(batches: &ElementBatches, device: &wgpu::Device) -> ElementBatchesGR {
421 let rects: GrowableBuffer<RectRaw> =
422 GrowableBuffer::new_from_data(device, BufferUsages::VERTEX, &batches.rects);
423 let textured_rects =
424 GrowableBuffer::new_from_data(device, BufferUsages::VERTEX, &batches.textured_rects);
425 let alpha_sdf_rects =
426 GrowableBuffer::new_from_data(device, BufferUsages::VERTEX, &batches.alpha_sdf_rects);
427 let glyphs = GrowableBuffer::new_from_data(device, BufferUsages::VERTEX, &batches.glyphs);
428
429 ElementBatchesGR {
430 rects,
431 textured_rects,
432 glyphs,
433 alpha_sdf_rects,
434 }
435 }
436
437 pub fn prepare(
438 &mut self,
439 batches: &ElementBatches,
440 device: &wgpu::Device,
441 queue: &wgpu::Queue,
442 ) {
443 self.rects.prepare(&batches.rects, device, queue);
444 self.textured_rects
445 .prepare(&batches.textured_rects, device, queue);
446 self.glyphs.prepare(&batches.glyphs, device, queue);
447 }
448}