Skip to content

Commit d53266f

Browse files
committed
Implement ClearableCollection for Vec-like collections
1 parent d5d54d1 commit d53266f

1 file changed

Lines changed: 120 additions & 0 deletions

File tree

crates/bevy_ecs/src/system/scratch.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
use alloc::{
2+
collections::{BinaryHeap, VecDeque},
3+
vec::Vec,
4+
};
15
use core::{
26
num::Saturating,
37
ops::{Deref, DerefMut},
@@ -182,3 +186,119 @@ unsafe impl<'a, T: ClearableCollection + FromWorld + Send + Sync + 'static> Read
182186
for Scratch<'a, T>
183187
{
184188
}
189+
190+
impl<T> ClearableCollection for Vec<T> {
191+
fn capacity(&self) -> usize {
192+
Vec::capacity(self)
193+
}
194+
195+
fn clear(&mut self) {
196+
Vec::clear(self);
197+
}
198+
199+
fn shrink_to(&mut self, capacity: usize) {
200+
Vec::shrink_to(self, capacity);
201+
}
202+
}
203+
204+
impl<T> ClearableCollection for VecDeque<T> {
205+
fn capacity(&self) -> usize {
206+
VecDeque::capacity(self)
207+
}
208+
209+
fn clear(&mut self) {
210+
VecDeque::clear(self);
211+
}
212+
213+
fn shrink_to(&mut self, capacity: usize) {
214+
VecDeque::shrink_to(self, capacity);
215+
}
216+
}
217+
218+
impl<I> ClearableCollection for BinaryHeap<I> {
219+
fn capacity(&self) -> usize {
220+
BinaryHeap::capacity(self)
221+
}
222+
223+
fn shrink_to(&mut self, capacity: usize) {
224+
BinaryHeap::shrink_to(self, capacity);
225+
}
226+
227+
fn clear(&mut self) {
228+
BinaryHeap::clear(self);
229+
}
230+
}
231+
232+
#[cfg(test)]
233+
mod tests {
234+
use alloc::vec::Vec;
235+
use core::sync::atomic::{AtomicU32, Ordering};
236+
237+
use crate::{
238+
system::{
239+
scratch::{Scratch, DEFAULT_SHRINK_DELAY},
240+
Local,
241+
},
242+
world::World,
243+
};
244+
245+
#[test]
246+
fn scratch_shrinks() {
247+
static UPDATE_COUNTER: AtomicU32 = AtomicU32::new(0);
248+
249+
let mut world = World::new();
250+
world.increment_change_tick();
251+
252+
fn check_system(
253+
mut item_under_test: Scratch<Vec<u32>>,
254+
mut expected_capacity: Local<usize>,
255+
) {
256+
if UPDATE_COUNTER.load(Ordering::Relaxed) == 0 {
257+
item_under_test.reserve(512);
258+
// The vec may over-reserve space so we need to store the actual capacity.
259+
*expected_capacity = item_under_test.capacity();
260+
}
261+
262+
if UPDATE_COUNTER.load(Ordering::Relaxed) == (DEFAULT_SHRINK_DELAY.0 + 1) {
263+
assert!(
264+
item_under_test.capacity() < *expected_capacity,
265+
"Scratch didn't shrink allocation, capacity was {}, expected {}, update was {}",
266+
item_under_test.capacity(),
267+
*expected_capacity,
268+
UPDATE_COUNTER.load(Ordering::Relaxed),
269+
);
270+
}
271+
}
272+
273+
let test_system = world.register_system(check_system);
274+
275+
// The first tick breaks the time calculation logic. We wait it out. We also
276+
// have to wait one for the deallocation to actually happen.
277+
for _ in 0..=(DEFAULT_SHRINK_DELAY.0 + 2) {
278+
let result = world.run_system(test_system);
279+
280+
assert!(result.is_ok(), "Test system failed to update {result:?}");
281+
UPDATE_COUNTER.fetch_add(1, Ordering::Relaxed);
282+
}
283+
}
284+
285+
#[test]
286+
fn scratch_clears() {
287+
let mut world = World::new();
288+
world.increment_change_tick();
289+
290+
fn check_system(mut items: Scratch<Vec<u32>>) {
291+
assert!(items.is_empty());
292+
293+
items.push(1);
294+
}
295+
296+
let test_system = world.register_system(check_system);
297+
298+
for _ in 0..=10 {
299+
let result = world.run_system(test_system);
300+
301+
assert!(result.is_ok(), "Test system failed to update {result:?}");
302+
}
303+
}
304+
}

0 commit comments

Comments
 (0)