1use bytemuck::{Pod, Zeroable};
2
3use crate::ShapeRecord;
4
5#[repr(C)]
7#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
8pub struct ShapeRecordBody {
9 pub rect: [f32; 4],
11 pub color: [f32; 4],
13 pub stroke_width: f32,
15 pub flags: u32,
17 pub brush: u32,
19 pub placement: u32,
21 pub arc_geometry: [f32; 4],
23}
24
25#[repr(C)]
27#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
28pub struct ShapeRecordCurve {
29 pub radii: [f32; 4],
31 pub arc_normalized: [f32; 4],
33}
34
35#[derive(Clone, Debug, Default, PartialEq)]
40pub struct ShapeRecords {
41 bodies: Vec<ShapeRecordBody>,
42 curves: Vec<ShapeRecordCurve>,
43 sources: Vec<[f32; 4]>,
44}
45
46impl ShapeRecords {
47 pub(crate) fn with_capacity(capacity: usize) -> Self {
48 Self {
49 bodies: Vec::with_capacity(capacity),
50 curves: Vec::with_capacity(capacity),
51 sources: Vec::with_capacity(capacity),
52 }
53 }
54
55 pub fn len(&self) -> usize {
57 self.bodies.len()
58 }
59
60 pub fn is_empty(&self) -> bool {
62 self.bodies.is_empty()
63 }
64
65 pub fn bodies(&self) -> &[ShapeRecordBody] {
67 &self.bodies
68 }
69
70 pub fn curves(&self) -> &[ShapeRecordCurve] {
72 &self.curves
73 }
74
75 pub fn get(&self, index: usize) -> Option<ShapeRecord> {
77 self.bodies
78 .get(index)
79 .map(|body| reconstruct(body, &self.curves[index], self.sources[index]))
80 }
81
82 pub fn iter(&self) -> impl ExactSizeIterator<Item = ShapeRecord> + DoubleEndedIterator + '_ {
84 self.bodies
85 .iter()
86 .zip(&self.curves)
87 .zip(&self.sources)
88 .map(|((body, curve), &source)| reconstruct(body, curve, source))
89 }
90
91 pub(crate) fn capacity(&self) -> usize {
92 self.bodies
93 .capacity()
94 .min(self.curves.capacity())
95 .min(self.sources.capacity())
96 }
97
98 pub(crate) fn heap_bytes(&self) -> usize {
99 self.bodies.capacity() * std::mem::size_of::<ShapeRecordBody>()
100 + self.curves.capacity() * std::mem::size_of::<ShapeRecordCurve>()
101 + self.sources.capacity() * std::mem::size_of::<[f32; 4]>()
102 }
103
104 pub(crate) fn source_bytes(&self) -> &[u8] {
105 bytemuck::cast_slice(&self.sources)
106 }
107
108 pub(crate) fn clear(&mut self) {
109 self.bodies.clear();
110 self.curves.clear();
111 self.sources.clear();
112 }
113
114 pub(crate) fn reserve(&mut self, additional: usize) {
115 self.bodies.reserve(additional);
116 self.curves.reserve(additional);
117 self.sources.reserve(additional);
118 }
119
120 pub(crate) fn push(
121 &mut self,
122 body: ShapeRecordBody,
123 curve: ShapeRecordCurve,
124 source: [f32; 4],
125 ) {
126 self.bodies.push(body);
127 self.curves.push(curve);
128 self.sources.push(source);
129 }
130}
131
132fn reconstruct(body: &ShapeRecordBody, curve: &ShapeRecordCurve, source: [f32; 4]) -> ShapeRecord {
133 ShapeRecord {
134 rect: body.rect,
135 radii: curve.radii,
136 color: body.color,
137 stroke_width: body.stroke_width,
138 flags: body.flags,
139 brush: body.brush,
140 reserved: body.placement,
141 arc: [
142 body.arc_geometry[0],
143 body.arc_geometry[1],
144 source[0],
145 source[1],
146 ],
147 arc_band: [
148 source[2],
149 source[3],
150 body.arc_geometry[2],
151 body.arc_geometry[3],
152 ],
153 arc_normalized: curve.arc_normalized,
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 fn sample() -> ShapeRecord {
162 ShapeRecord {
163 rect: [1.0, 2.0, 3.0, 4.0],
164 radii: [5.0, 6.0, 7.0, 8.0],
165 color: [0.2, 0.4, 0.6, 0.8],
166 stroke_width: 9.0,
167 flags: 10,
168 brush: 11,
169 reserved: 12,
170 arc: [13.0, 14.0, 15.0, 16.0],
171 arc_band: [17.0, 18.0, 19.0, 20.0],
172 arc_normalized: [21.0, 22.0, 23.0, 24.0],
173 }
174 }
175
176 fn append_sample(records: &mut ShapeRecords) {
177 records.push(
178 ShapeRecordBody {
179 rect: [1.0, 2.0, 3.0, 4.0],
180 color: [0.2, 0.4, 0.6, 0.8],
181 stroke_width: 9.0,
182 flags: 10,
183 brush: 11,
184 placement: 12,
185 arc_geometry: [13.0, 14.0, 19.0, 20.0],
186 },
187 ShapeRecordCurve {
188 radii: [5.0, 6.0, 7.0, 8.0],
189 arc_normalized: [21.0, 22.0, 23.0, 24.0],
190 },
191 [15.0, 16.0, 17.0, 18.0],
192 );
193 }
194
195 #[test]
196 fn columns_preserve_every_record_bit_and_gpu_field() {
197 let record = sample();
198 let mut special = record;
199 special.arc = [
200 f32::NEG_INFINITY,
201 -0.0,
202 f32::from_bits(0x7fc0_0021),
203 f32::INFINITY,
204 ];
205 special.arc_band = [-0.0, f32::from_bits(0xffc0_0001), 1.0, 2.0];
206 let mut records = ShapeRecords::default();
207 assert!(records.is_empty());
208 assert_eq!(records.get(0), None);
209 append_sample(&mut records);
210 let mut body = records.bodies()[0];
211 body.arc_geometry = [f32::NEG_INFINITY, -0.0, 1.0, 2.0];
212 records.push(
213 body,
214 records.curves()[0],
215 [
216 special.arc[2],
217 special.arc[3],
218 special.arc_band[0],
219 special.arc_band[1],
220 ],
221 );
222 assert_eq!(records.len(), 2);
223 assert_eq!(records.iter().len(), 2);
224 for (actual, expected) in records.iter().zip([record, special]) {
225 assert_eq!(bytemuck::bytes_of(&actual), bytemuck::bytes_of(&expected));
226 }
227 assert_eq!(records.get(0), Some(record));
228 assert_eq!(records.get(2), None);
229 assert_eq!(records.iter().rev().nth(1), Some(record));
230 assert_eq!(records.bodies()[0].arc_geometry, [13.0, 14.0, 19.0, 20.0]);
231 assert_eq!(records.curves()[0].radii, record.radii);
232 assert_eq!(records.curves()[0].arc_normalized, record.arc_normalized);
233 assert_eq!(std::mem::size_of::<ShapeRecordBody>(), 64);
234 assert_eq!(std::mem::size_of::<ShapeRecordCurve>(), 32);
235 assert_eq!(
236 &records.source_bytes()[..16],
237 bytemuck::bytes_of(&[15.0f32, 16.0, 17.0, 18.0])
238 );
239 }
240
241 #[test]
242 fn clearing_and_reserving_keep_columns_aligned_without_reallocating() {
243 let mut records = ShapeRecords::with_capacity(4);
244 append_sample(&mut records);
245 let capacity = records.capacity();
246 let bytes = records.heap_bytes();
247 let bodies = records.bodies().as_ptr();
248 let curves = records.curves().as_ptr();
249 records.clear();
250 records.reserve(2);
251 append_sample(&mut records);
252 assert_eq!(records.capacity(), capacity);
253 assert_eq!(records.heap_bytes(), bytes);
254 assert_eq!(records.bodies().as_ptr(), bodies);
255 assert_eq!(records.curves().as_ptr(), curves);
256 assert_eq!(records.len(), records.curves().len());
257 assert_eq!(records.get(0), Some(sample()));
258 assert_eq!(records.clone(), records);
259 }
260}