1use crate::{ClippedShape, Galley, Mesh, Primitive, Shape};
4
5#[derive(Clone, Copy, PartialEq)]
7enum ElementSize {
8 Unknown,
9 Homogeneous(usize),
10 Heterogenous,
11}
12
13impl Default for ElementSize {
14 fn default() -> Self {
15 Self::Unknown
16 }
17}
18
19#[derive(Clone, Copy, Default, PartialEq)]
21pub struct AllocInfo {
22 element_size: ElementSize,
23 num_allocs: usize,
24 num_elements: usize,
25 num_bytes: usize,
26}
27
28impl<T> From<&[T]> for AllocInfo {
29 fn from(slice: &[T]) -> Self {
30 Self::from_slice(slice)
31 }
32}
33
34impl std::ops::Add for AllocInfo {
35 type Output = Self;
36
37 fn add(self, rhs: Self) -> Self {
38 use ElementSize::{Heterogenous, Homogeneous, Unknown};
39 let element_size = match (self.element_size, rhs.element_size) {
40 (Heterogenous, _) | (_, Heterogenous) => Heterogenous,
41 (Unknown, other) | (other, Unknown) => other,
42 (Homogeneous(lhs), Homogeneous(rhs)) if lhs == rhs => Homogeneous(lhs),
43 _ => Heterogenous,
44 };
45
46 Self {
47 element_size,
48 num_allocs: self.num_allocs + rhs.num_allocs,
49 num_elements: self.num_elements + rhs.num_elements,
50 num_bytes: self.num_bytes + rhs.num_bytes,
51 }
52 }
53}
54
55impl std::ops::AddAssign for AllocInfo {
56 fn add_assign(&mut self, rhs: Self) {
57 *self = *self + rhs;
58 }
59}
60
61impl std::iter::Sum for AllocInfo {
62 fn sum<I>(iter: I) -> Self
63 where
64 I: Iterator<Item = Self>,
65 {
66 let mut sum = Self::default();
67 for value in iter {
68 sum += value;
69 }
70 sum
71 }
72}
73
74impl AllocInfo {
75 pub fn from_galley(galley: &Galley) -> Self {
89 Self::from_slice(galley.text().as_bytes())
90 + Self::from_slice(&galley.rows)
91 + galley.rows.iter().map(Self::from_galley_row).sum()
92 }
93
94 fn from_galley_row(row: &crate::text::PlacedRow) -> Self {
95 Self::from_mesh(&row.visuals.mesh) + Self::from_slice(&row.glyphs)
96 }
97
98 pub fn from_mesh(mesh: &Mesh) -> Self {
99 Self::from_slice(&mesh.indices) + Self::from_slice(&mesh.vertices)
100 }
101
102 pub fn from_slice<T>(slice: &[T]) -> Self {
103 use std::mem::size_of;
104 let element_size = size_of::<T>();
105 Self {
106 element_size: ElementSize::Homogeneous(element_size),
107 num_allocs: 1,
108 num_elements: slice.len(),
109 num_bytes: std::mem::size_of_val(slice),
110 }
111 }
112
113 pub fn num_elements(&self) -> usize {
114 assert!(
115 self.element_size != ElementSize::Heterogenous,
116 "Heterogenous element size"
117 );
118 self.num_elements
119 }
120
121 pub fn num_allocs(&self) -> usize {
122 self.num_allocs
123 }
124
125 pub fn num_bytes(&self) -> usize {
126 self.num_bytes
127 }
128
129 pub fn megabytes(&self) -> String {
130 megabytes(self.num_bytes())
131 }
132
133 pub fn format(&self, what: &str) -> String {
134 if self.num_allocs() == 0 {
135 format!("{:6} {:16}", 0, what)
136 } else if self.num_allocs() == 1 {
137 format!(
138 "{:6} {:16} {} 1 allocation",
139 self.num_elements,
140 what,
141 self.megabytes()
142 )
143 } else if self.element_size != ElementSize::Heterogenous {
144 format!(
145 "{:6} {:16} {} {:3} allocations",
146 self.num_elements(),
147 what,
148 self.megabytes(),
149 self.num_allocs()
150 )
151 } else {
152 format!(
153 "{:6} {:16} {} {:3} allocations",
154 "",
155 what,
156 self.megabytes(),
157 self.num_allocs()
158 )
159 }
160 }
161}
162
163#[derive(Clone, Copy, Default)]
165pub struct PaintStats {
166 pub shapes: AllocInfo,
167 pub shape_text: AllocInfo,
168 pub shape_path: AllocInfo,
169 pub shape_mesh: AllocInfo,
170 pub shape_vec: AllocInfo,
171 pub num_callbacks: usize,
172
173 pub text_shape_vertices: AllocInfo,
174 pub text_shape_indices: AllocInfo,
175
176 pub clipped_primitives: AllocInfo,
178 pub vertices: AllocInfo,
179 pub indices: AllocInfo,
180}
181
182impl PaintStats {
183 pub fn from_shapes(shapes: &[ClippedShape]) -> Self {
184 let mut stats = Self::default();
185 stats.shape_path.element_size = ElementSize::Heterogenous; stats.shape_vec.element_size = ElementSize::Heterogenous; stats.shapes = AllocInfo::from_slice(shapes);
189 for ClippedShape { shape, .. } in shapes {
190 stats.add(shape);
191 }
192 stats
193 }
194
195 fn add(&mut self, shape: &Shape) {
196 match shape {
197 Shape::Vec(shapes) => {
198 self.shapes += AllocInfo::from_slice(shapes);
200 self.shape_vec += AllocInfo::from_slice(shapes);
201 for shape in shapes {
202 self.add(shape);
203 }
204 }
205 Shape::Noop
206 | Shape::Circle { .. }
207 | Shape::Ellipse { .. }
208 | Shape::LineSegment { .. }
209 | Shape::Rect { .. }
210 | Shape::CubicBezier(_)
211 | Shape::QuadraticBezier(_) => {}
212 Shape::Path(path_shape) => {
213 self.shape_path += AllocInfo::from_slice(&path_shape.points);
214 }
215 Shape::Text(text_shape) => {
216 self.shape_text += AllocInfo::from_galley(&text_shape.galley);
217
218 for row in &text_shape.galley.rows {
219 self.text_shape_indices += AllocInfo::from_slice(&row.visuals.mesh.indices);
220 self.text_shape_vertices += AllocInfo::from_slice(&row.visuals.mesh.vertices);
221 }
222 }
223 Shape::Mesh(mesh) => {
224 self.shape_mesh += AllocInfo::from_mesh(mesh);
225 }
226 Shape::Callback(_) => {
227 self.num_callbacks += 1;
228 }
229 }
230 }
231
232 pub fn with_clipped_primitives(
233 mut self,
234 clipped_primitives: &[crate::ClippedPrimitive],
235 ) -> Self {
236 self.clipped_primitives += AllocInfo::from_slice(clipped_primitives);
237 for clipped_primitive in clipped_primitives {
238 if let Primitive::Mesh(mesh) = &clipped_primitive.primitive {
239 self.vertices += AllocInfo::from_slice(&mesh.vertices);
240 self.indices += AllocInfo::from_slice(&mesh.indices);
241 }
242 }
243 self
244 }
245}
246
247fn megabytes(size: usize) -> String {
248 format!("{:.2} MB", size as f64 / 1e6)
249}