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