1use std::{iter::Peekable, mem};
2
3use super::{Bounds, Hsla, Pixels, Point};
4use crate::{AtlasTextureId, AtlasTile, Corners, Edges};
5use collections::BTreeMap;
6use smallvec::SmallVec;
7
8// Exported to metal
9pub type PointF = Point<f32>;
10pub type StackingOrder = SmallVec<[u32; 16]>;
11
12#[derive(Debug)]
13pub struct Scene {
14 pub(crate) scale_factor: f32,
15 pub(crate) layers: BTreeMap<StackingOrder, 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: StackingOrder, 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(mut quad) => {
39 quad.scale(self.scale_factor);
40 layer.quads.push(quad);
41 }
42 Primitive::Sprite(sprite) => {
43 layer.sprites.push(sprite);
44 }
45 }
46 }
47
48 pub(crate) fn layers(&mut self) -> impl Iterator<Item = &mut SceneLayer> {
49 self.layers.values_mut()
50 }
51}
52
53#[derive(Debug, Default)]
54pub(crate) struct SceneLayer {
55 pub quads: Vec<Quad>,
56 pub sprites: Vec<MonochromeSprite>,
57}
58
59impl SceneLayer {
60 pub fn batches(&mut self) -> impl Iterator<Item = PrimitiveBatch> {
61 self.quads.sort_unstable();
62 self.sprites.sort_unstable();
63
64 BatchIterator::new(
65 &self.quads,
66 self.quads.iter().peekable(),
67 &self.sprites,
68 self.sprites.iter().peekable(),
69 )
70 }
71}
72
73struct BatchIterator<'a, Q, S>
74where
75 Q: Iterator<Item = &'a Quad>,
76 S: Iterator<Item = &'a MonochromeSprite>,
77{
78 quads: &'a [Quad],
79 sprites: &'a [MonochromeSprite],
80 quads_start: usize,
81 sprites_start: usize,
82 quads_iter: Peekable<Q>,
83 sprites_iter: Peekable<S>,
84}
85
86impl<'a, Q: 'a, S: 'a> Iterator for BatchIterator<'a, Q, S>
87where
88 Q: Iterator<Item = &'a Quad>,
89 S: Iterator<Item = &'a MonochromeSprite>,
90{
91 type Item = PrimitiveBatch<'a>;
92
93 fn next(&mut self) -> Option<Self::Item> {
94 let mut kinds_and_orders = [
95 (PrimitiveKind::Quad, self.quads_iter.peek().map(|q| q.order)),
96 (
97 PrimitiveKind::Sprite,
98 self.sprites_iter.peek().map(|s| s.order),
99 ),
100 ];
101 kinds_and_orders.sort_by_key(|(_, order)| order.unwrap_or(u32::MAX));
102
103 let first = kinds_and_orders[0];
104 let second = kinds_and_orders[1];
105 let (batch_kind, max_order) = if first.1.is_some() {
106 (first.0, second.1.unwrap_or(u32::MAX))
107 } else {
108 return None;
109 };
110
111 match batch_kind {
112 PrimitiveKind::Quad => {
113 let quads_start = self.quads_start;
114 let quads_end = quads_start
115 + self
116 .quads_iter
117 .by_ref()
118 .take_while(|quad| quad.order <= max_order)
119 .count();
120 self.quads_start = quads_end;
121 Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
122 }
123 PrimitiveKind::Sprite => {
124 let texture_id = self.sprites_iter.peek().unwrap().tile.texture_id;
125 let sprites_start = self.sprites_start;
126 let sprites_end = sprites_start
127 + self
128 .sprites_iter
129 .by_ref()
130 .take_while(|sprite| {
131 sprite.order <= max_order && sprite.tile.texture_id == texture_id
132 })
133 .count();
134 self.sprites_start = sprites_end;
135 Some(PrimitiveBatch::Sprites {
136 texture_id,
137 sprites: &self.sprites[sprites_start..sprites_end],
138 })
139 }
140 }
141 }
142}
143
144impl<'a, Q: 'a, S: 'a> BatchIterator<'a, Q, S>
145where
146 Q: Iterator<Item = &'a Quad>,
147 S: Iterator<Item = &'a MonochromeSprite>,
148{
149 fn new(
150 quads: &'a [Quad],
151 quads_iter: Peekable<Q>,
152 sprites: &'a [MonochromeSprite],
153 sprites_iter: Peekable<S>,
154 ) -> Self {
155 Self {
156 quads,
157 quads_start: 0,
158 quads_iter,
159 sprites,
160 sprites_start: 0,
161 sprites_iter,
162 }
163 }
164}
165
166#[derive(Clone, Copy, Debug, Eq, PartialEq)]
167pub enum PrimitiveKind {
168 Quad,
169 Sprite,
170}
171
172#[derive(Clone, Debug)]
173pub enum Primitive {
174 Quad(Quad),
175 Sprite(MonochromeSprite),
176}
177
178pub(crate) enum PrimitiveBatch<'a> {
179 Quads(&'a [Quad]),
180 Sprites {
181 texture_id: AtlasTextureId,
182 sprites: &'a [MonochromeSprite],
183 },
184}
185
186#[derive(Debug, Copy, Clone, Eq, PartialEq)]
187#[repr(C)]
188pub struct Quad {
189 pub order: u32,
190 pub bounds: Bounds<Pixels>,
191 pub clip_bounds: Bounds<Pixels>,
192 pub clip_corner_radii: Corners<Pixels>,
193 pub background: Hsla,
194 pub border_color: Hsla,
195 pub corner_radii: Corners<Pixels>,
196 pub border_widths: Edges<Pixels>,
197}
198
199impl Quad {
200 pub fn vertices(&self) -> impl Iterator<Item = Point<Pixels>> {
201 let x1 = self.bounds.origin.x;
202 let y1 = self.bounds.origin.y;
203 let x2 = x1 + self.bounds.size.width;
204 let y2 = y1 + self.bounds.size.height;
205 [
206 Point::new(x1, y1),
207 Point::new(x2, y1),
208 Point::new(x2, y2),
209 Point::new(x1, y2),
210 ]
211 .into_iter()
212 }
213
214 pub fn scale(&mut self, factor: f32) {
215 self.bounds *= factor;
216 self.clip_bounds *= factor;
217 self.clip_corner_radii *= factor;
218 self.corner_radii *= factor;
219 self.border_widths *= factor;
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<Pixels>,
246 pub color: Hsla,
247 pub tile: AtlasTile,
248}
249
250impl Ord for MonochromeSprite {
251 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
252 match self.order.cmp(&other.order) {
253 std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
254 order => order,
255 }
256 }
257}
258
259impl PartialOrd for MonochromeSprite {
260 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
261 Some(self.cmp(other))
262 }
263}
264
265impl From<MonochromeSprite> for Primitive {
266 fn from(sprite: MonochromeSprite) -> Self {
267 Primitive::Sprite(sprite)
268 }
269}
270
271#[derive(Copy, Clone, Debug)]
272pub struct AtlasId(pub(crate) usize);