1use std::{
4 any::{Any, TypeId},
5 collections::HashMap,
6};
7
8use bevy_ecs::prelude::Resource;
9
10pub type FrameItemId = (usize, usize);
12
13pub struct FrameItem<T> {
15 pub id: FrameItemId,
17 pub order: usize,
19 pub value: T,
21}
22
23#[derive(Debug, Clone, Copy)]
25pub struct FrameItemMetadata<'a> {
26 pub id: FrameItemId,
28 pub order: usize,
30 pub type_id: TypeId,
32 pub type_name: &'a str,
34}
35
36trait ErasedItems: Any + Send + Sync {
37 fn clear(&mut self);
38 fn len(&self) -> usize;
39 fn metadata_at(&self, index: usize) -> FrameItemMetadata<'_>;
40}
41
42impl<T: Send + Sync + 'static> ErasedItems for Vec<FrameItem<T>> {
43 fn clear(&mut self) {
44 Vec::clear(self);
45 }
46
47 fn len(&self) -> usize {
48 Vec::len(self)
49 }
50
51 fn metadata_at(&self, index: usize) -> FrameItemMetadata<'_> {
52 let item = &self[index];
53 FrameItemMetadata {
54 id: item.id,
55 order: item.order,
56 type_id: TypeId::of::<T>(),
57 type_name: std::any::type_name::<T>(),
58 }
59 }
60}
61
62#[derive(Resource, Default)]
68pub struct EvaluatedFrame {
69 batches: HashMap<TypeId, Box<dyn ErasedItems>>,
70 len: usize,
71}
72
73impl EvaluatedFrame {
74 pub fn new() -> Self {
76 Self::default()
77 }
78
79 pub fn len(&self) -> usize {
81 self.len
82 }
83
84 pub fn is_empty(&self) -> bool {
86 self.len == 0
87 }
88
89 pub fn clear(&mut self) {
91 for batch in self.batches.values_mut() {
92 batch.clear();
93 }
94 self.len = 0;
95 }
96
97 pub fn push<T: Send + Sync + 'static>(&mut self, id: FrameItemId, value: T) {
99 let order = self.len;
100 self.batch_mut().push(FrameItem { id, order, value });
101 self.len += 1;
102 }
103
104 pub fn update<T: Send + Sync + 'static>(
106 &mut self,
107 items: impl IntoIterator<Item = (FrameItemId, T)>,
108 ) {
109 self.clear();
110 let batch = self.batch_mut::<T>();
111 batch.extend(
112 items
113 .into_iter()
114 .enumerate()
115 .map(|(order, (id, value))| FrameItem { id, order, value }),
116 );
117 self.len = batch.len();
118 }
119
120 pub fn items<T: 'static>(&self) -> &[FrameItem<T>] {
122 let Some(batch) = self.batches.get(&TypeId::of::<T>()) else {
123 return &[];
124 };
125 let batch: &dyn Any = batch.as_ref();
126 batch
127 .downcast_ref::<Vec<FrameItem<T>>>()
128 .expect("frame batch type must match its TypeId")
129 .as_slice()
130 }
131
132 pub fn item_metadata(&self) -> impl Iterator<Item = FrameItemMetadata<'_>> + '_ {
137 self.batches
138 .values()
139 .flat_map(|batch| (0..batch.len()).map(move |index| batch.metadata_at(index)))
140 }
141
142 fn batch_mut<T: Send + Sync + 'static>(&mut self) -> &mut Vec<FrameItem<T>> {
143 let batch = self
144 .batches
145 .entry(TypeId::of::<T>())
146 .or_insert_with(|| Box::new(Vec::<FrameItem<T>>::new()));
147 let batch: &mut dyn Any = batch.as_mut();
148 batch
149 .downcast_mut::<Vec<FrameItem<T>>>()
150 .expect("frame batch type must match its TypeId")
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn typed_batches_preserve_global_insertion_order() {
160 let mut frame = EvaluatedFrame::new();
161 frame.push((0, 0), 10_u32);
162 frame.push((1, 0), String::from("middle"));
163 frame.push((2, 0), 20_u32);
164
165 assert_eq!(frame.len(), 3);
166 assert!(!frame.is_empty());
167 let numbers = frame.items::<u32>();
168 assert_eq!(numbers.len(), 2);
169 assert_eq!(
170 (numbers[0].id, numbers[0].order, numbers[0].value),
171 ((0, 0), 0, 10)
172 );
173 assert_eq!(
174 (numbers[1].id, numbers[1].order, numbers[1].value),
175 ((2, 0), 2, 20)
176 );
177 let strings = frame.items::<String>();
178 assert_eq!(strings.len(), 1);
179 assert_eq!(strings[0].id, (1, 0));
180 assert_eq!(strings[0].order, 1);
181 assert_eq!(strings[0].value, "middle");
182 }
183
184 #[test]
185 fn metadata_reports_all_payload_types_without_retained_empty_batches() {
186 let mut frame = EvaluatedFrame::new();
187 frame.push((0, 0), 10_u32);
188 frame.push((0, 1), String::from("text"));
189 let mut metadata = frame.item_metadata().collect::<Vec<_>>();
190 metadata.sort_by_key(|item| item.order);
191 assert_eq!(metadata.len(), 2);
192 assert_eq!(metadata[0].id, (0, 0));
193 assert_eq!(metadata[0].type_id, TypeId::of::<u32>());
194 assert_eq!(metadata[1].id, (0, 1));
195 assert_eq!(metadata[1].type_name, std::any::type_name::<String>());
196 frame.clear();
197 assert_eq!(frame.item_metadata().count(), 0);
198 frame.push((1, 0), 20_u32);
199 let metadata = frame.item_metadata().collect::<Vec<_>>();
200 assert_eq!(metadata.len(), 1);
201 assert_eq!(metadata[0].type_id, TypeId::of::<u32>());
202 assert_eq!(metadata[0].type_name, "u32");
203 }
204
205 #[test]
206 fn update_replaces_every_type_and_restarts_order() {
207 let mut frame = EvaluatedFrame::new();
208 frame.push((0, 0), 10_u32);
209 frame.push((1, 0), String::from("old"));
210
211 frame.update([
212 ((2, 0), String::from("new")),
213 ((3, 0), String::from("next")),
214 ]);
215
216 assert_eq!(frame.len(), 2);
217 assert!(frame.items::<u32>().is_empty());
218 let strings = frame.items::<String>();
219 assert_eq!(strings.len(), 2);
220 assert_eq!(strings[0].id, (2, 0));
221 assert_eq!(strings[0].order, 0);
222 assert_eq!(strings[0].value, "new");
223 assert_eq!(strings[1].id, (3, 0));
224 assert_eq!(strings[1].order, 1);
225 assert_eq!(strings[1].value, "next");
226
227 frame.update(std::iter::empty::<(FrameItemId, String)>());
228 assert!(frame.is_empty());
229 assert!(frame.items::<String>().is_empty());
230 }
231
232 #[test]
233 fn clear_retains_batches_and_their_capacity() {
234 fn capacity<T: 'static>(frame: &EvaluatedFrame) -> usize {
235 let batch: &dyn Any = frame.batches[&TypeId::of::<T>()].as_ref();
236 batch
237 .downcast_ref::<Vec<FrameItem<T>>>()
238 .unwrap()
239 .capacity()
240 }
241
242 let mut frame = EvaluatedFrame::new();
243 for index in 0..32 {
244 frame.push((index, 0), index);
245 frame.push((index, 1), index.to_string());
246 }
247 let number_capacity = capacity::<usize>(&frame);
248 let string_capacity = capacity::<String>(&frame);
249
250 frame.clear();
251
252 assert!(frame.is_empty());
253 assert_eq!(frame.len(), 0);
254 assert_eq!(frame.batches.len(), 2);
255 assert!(frame.items::<usize>().is_empty());
256 assert!(frame.items::<String>().is_empty());
257 assert_eq!(capacity::<usize>(&frame), number_capacity);
258 assert_eq!(capacity::<String>(&frame), string_capacity);
259
260 frame.push((0, 0), 99_usize);
261 assert_eq!(frame.items::<usize>()[0].order, 0);
262 assert_eq!(capacity::<usize>(&frame), number_capacity);
263 }
264
265 #[test]
266 fn missing_type_has_no_items() {
267 let mut frame = EvaluatedFrame::new();
268 assert!(frame.items::<String>().is_empty());
269 frame.push((0, 0), 10_u32);
270 assert!(frame.items::<String>().is_empty());
271 }
272
273 #[test]
274 fn frame_is_send_and_sync() {
275 fn assert_send_sync<T: Send + Sync>() {}
276 assert_send_sync::<EvaluatedFrame>();
277 }
278}