1use std::{iter::Peekable, mem, slice};
2
3use super::{Bounds, Hsla, Point};
4use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledContentMask, ScaledPixels};
5use collections::BTreeMap;
6use smallvec::SmallVec;
7
8// Exported to metal
9pub type PointF = Point<f32>;
10pub type LayerId = SmallVec<[u32; 16]>;
11
12#[derive(Debug)]
13pub struct Scene {
14 pub(crate) scale_factor: f32,
15 pub(crate) layers: BTreeMap<LayerId, SceneLayer>,
16}
17
18impl Scene {
19 pub fn new(scale_factor: f32) -> Scene {
20 Scene {
21 scale_factor,
22 layers: BTreeMap::new(),
23 }
24 }
25
26 pub fn take(&mut self) -> Scene {
27 Scene {
28 scale_factor: self.scale_factor,
29 layers: mem::take(&mut self.layers),
30 }
31 }
32
33 pub fn insert(&mut self, stacking_order: LayerId, primitive: impl Into<Primitive>) {
34 let layer = self.layers.entry(stacking_order).or_default();
35
36 let primitive = primitive.into();
37 match primitive {
38 Primitive::Quad(quad) => {
39 layer.quads.push(quad);
40 }
41 Primitive::MonochromeSprite(sprite) => {
42 layer.monochrome_sprites.push(sprite);
43 }
44 Primitive::PolychromeSprite(sprite) => {
45 layer.polychrome_sprites.push(sprite);
46 }
47 }
48 }
49
50 pub(crate) fn layers(&mut self) -> impl Iterator<Item = &mut SceneLayer> {
51 self.layers.values_mut()
52 }
53}
54
55#[derive(Debug, Default)]
56pub(crate) struct SceneLayer {
57 pub quads: Vec<Quad>,
58 pub monochrome_sprites: Vec<MonochromeSprite>,
59 pub polychrome_sprites: Vec<PolychromeSprite>,
60}
61
62impl SceneLayer {
63 pub fn batches(&mut self) -> impl Iterator<Item = PrimitiveBatch> {
64 self.quads.sort_unstable();
65 self.monochrome_sprites.sort_unstable();
66 self.polychrome_sprites.sort_unstable();
67 BatchIterator {
68 quads: &self.quads,
69 quads_start: 0,
70 quads_iter: self.quads.iter().peekable(),
71 monochrome_sprites: &self.monochrome_sprites,
72 monochrome_sprites_start: 0,
73 monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
74 polychrome_sprites: &self.polychrome_sprites,
75 polychrome_sprites_start: 0,
76 polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
77 }
78 }
79}
80
81struct BatchIterator<'a> {
82 quads: &'a [Quad],
83 quads_start: usize,
84 quads_iter: Peekable<slice::Iter<'a, Quad>>,
85 monochrome_sprites: &'a [MonochromeSprite],
86 monochrome_sprites_start: usize,
87 monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
88 polychrome_sprites: &'a [PolychromeSprite],
89 polychrome_sprites_start: usize,
90 polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
91}
92
93impl<'a> Iterator for BatchIterator<'a> {
94 type Item = PrimitiveBatch<'a>;
95
96 fn next(&mut self) -> Option<Self::Item> {
97 let mut kinds_and_orders = [
98 (PrimitiveKind::Quad, self.quads_iter.peek().map(|q| q.order)),
99 (
100 PrimitiveKind::MonochromeSprite,
101 self.monochrome_sprites_iter.peek().map(|s| s.order),
102 ),
103 (
104 PrimitiveKind::PolychromeSprite,
105 self.polychrome_sprites_iter.peek().map(|s| s.order),
106 ),
107 ];
108 kinds_and_orders.sort_by_key(|(_, order)| order.unwrap_or(u32::MAX));
109
110 let first = kinds_and_orders[0];
111 let second = kinds_and_orders[1];
112 let (batch_kind, max_order) = if first.1.is_some() {
113 (first.0, second.1.unwrap_or(u32::MAX))
114 } else {
115 return None;
116 };
117
118 match batch_kind {
119 PrimitiveKind::Quad => {
120 let quads_start = self.quads_start;
121 let quads_end = quads_start
122 + self
123 .quads_iter
124 .by_ref()
125 .take_while(|quad| quad.order <= max_order)
126 .count();
127 self.quads_start = quads_end;
128 Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
129 }
130 PrimitiveKind::MonochromeSprite => {
131 let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
132 let sprites_start = self.monochrome_sprites_start;
133 let sprites_end = sprites_start
134 + self
135 .monochrome_sprites_iter
136 .by_ref()
137 .take_while(|sprite| {
138 sprite.order <= max_order && sprite.tile.texture_id == texture_id
139 })
140 .count();
141 self.monochrome_sprites_start = sprites_end;
142 Some(PrimitiveBatch::MonochromeSprites {
143 texture_id,
144 sprites: &self.monochrome_sprites[sprites_start..sprites_end],
145 })
146 }
147 PrimitiveKind::PolychromeSprite => {
148 let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
149 let sprites_start = self.polychrome_sprites_start;
150 let sprites_end = sprites_start
151 + self
152 .polychrome_sprites_iter
153 .by_ref()
154 .take_while(|sprite| {
155 sprite.order <= max_order && sprite.tile.texture_id == texture_id
156 })
157 .count();
158 self.polychrome_sprites_start = sprites_end;
159 Some(PrimitiveBatch::PolychromeSprites {
160 texture_id,
161 sprites: &self.polychrome_sprites[sprites_start..sprites_end],
162 })
163 }
164 }
165 }
166}
167
168#[derive(Clone, Copy, Debug, Eq, PartialEq)]
169pub enum PrimitiveKind {
170 Quad,
171 MonochromeSprite,
172 PolychromeSprite,
173}
174
175#[derive(Clone, Debug)]
176pub enum Primitive {
177 Quad(Quad),
178 MonochromeSprite(MonochromeSprite),
179 PolychromeSprite(PolychromeSprite),
180}
181
182pub(crate) enum PrimitiveBatch<'a> {
183 Quads(&'a [Quad]),
184 MonochromeSprites {
185 texture_id: AtlasTextureId,
186 sprites: &'a [MonochromeSprite],
187 },
188 PolychromeSprites {
189 texture_id: AtlasTextureId,
190 sprites: &'a [PolychromeSprite],
191 },
192}
193
194#[derive(Debug, Copy, Clone, Eq, PartialEq)]
195#[repr(C)]
196pub struct Quad {
197 pub order: u32,
198 pub bounds: Bounds<ScaledPixels>,
199 pub clip_bounds: Bounds<ScaledPixels>,
200 pub clip_corner_radii: Corners<ScaledPixels>,
201 pub background: Hsla,
202 pub border_color: Hsla,
203 pub corner_radii: Corners<ScaledPixels>,
204 pub border_widths: Edges<ScaledPixels>,
205}
206
207impl Quad {
208 pub fn vertices(&self) -> impl Iterator<Item = Point<ScaledPixels>> {
209 let x1 = self.bounds.origin.x;
210 let y1 = self.bounds.origin.y;
211 let x2 = x1 + self.bounds.size.width;
212 let y2 = y1 + self.bounds.size.height;
213 [
214 Point::new(x1, y1),
215 Point::new(x2, y1),
216 Point::new(x2, y2),
217 Point::new(x1, y2),
218 ]
219 .into_iter()
220 }
221}
222
223impl Ord for Quad {
224 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
225 self.order.cmp(&other.order)
226 }
227}
228
229impl PartialOrd for Quad {
230 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
231 Some(self.cmp(other))
232 }
233}
234
235impl From<Quad> for Primitive {
236 fn from(quad: Quad) -> Self {
237 Primitive::Quad(quad)
238 }
239}
240
241#[derive(Clone, Debug, Eq, PartialEq)]
242#[repr(C)]
243pub struct MonochromeSprite {
244 pub order: u32,
245 pub bounds: Bounds<ScaledPixels>,
246 pub content_mask: ScaledContentMask,
247 pub color: Hsla,
248 pub tile: AtlasTile,
249}
250
251impl Ord for MonochromeSprite {
252 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
253 match self.order.cmp(&other.order) {
254 std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
255 order => order,
256 }
257 }
258}
259
260impl PartialOrd for MonochromeSprite {
261 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
262 Some(self.cmp(other))
263 }
264}
265
266impl From<MonochromeSprite> for Primitive {
267 fn from(sprite: MonochromeSprite) -> Self {
268 Primitive::MonochromeSprite(sprite)
269 }
270}
271
272#[derive(Clone, Debug, Eq, PartialEq)]
273#[repr(C)]
274pub struct PolychromeSprite {
275 pub order: u32,
276 pub bounds: Bounds<ScaledPixels>,
277 pub content_mask: ScaledContentMask,
278 pub tile: AtlasTile,
279 pub grayscale: bool,
280}
281
282impl Ord for PolychromeSprite {
283 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
284 match self.order.cmp(&other.order) {
285 std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
286 order => order,
287 }
288 }
289}
290
291impl PartialOrd for PolychromeSprite {
292 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
293 Some(self.cmp(other))
294 }
295}
296
297impl From<PolychromeSprite> for Primitive {
298 fn from(sprite: PolychromeSprite) -> Self {
299 Primitive::PolychromeSprite(sprite)
300 }
301}
302
303#[derive(Copy, Clone, Debug)]
304pub struct AtlasId(pub(crate) usize);