Skip to content

Commit 625750e

Browse files
VictoronzSpecificProtagonist
authored andcommitted
implement FromEntitySetIterator (bevyengine#17513)
# Objective Some collections are more efficient to construct when we know that every element is unique in advance. We have `EntitySetIterator`s from bevyengine#16547, but currently no API to safely make use of them this way. ## Solution Add `FromEntitySetIterator` as a subtrait to `FromIterator`, and implement it for the `EntityHashSet`/`hashbrown::HashSet` types. To match the normal `FromIterator`, we also add a `EntitySetIterator::collect_set` method. It'd be better if these methods could shadow `from_iter` and `collect` completely, but rust-lang/rust#89151 is needed for that. While currently only `HashSet`s implement this trait, future `UniqueEntityVec`/`UniqueEntitySlice` functionality comes with more implementors. Because `HashMap`s are collected from tuples instead of singular types, implementing this same optimization for them is more complex, and has to be done separately. ## Showcase This is basically a free speedup for collecting `EntityHashSet`s! ```rust pub fn collect_milk_dippers(dippers: Query<Entity, (With<Milk>, With<Cookies>)>) { dippers.iter().collect_set::<EntityHashSet>(); // or EntityHashSet::from_entity_set_iter(dippers); } --------- Co-authored-by: SpecificProtagonist <[email protected]>
1 parent 268c4b3 commit 625750e

File tree

2 files changed

+62
-2
lines changed

2 files changed

+62
-2
lines changed

crates/bevy_ecs/src/entity/entity_set.rs

+47-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ use alloc::{
33
collections::{btree_map, btree_set},
44
rc::Rc,
55
};
6+
use bevy_platform_support::collections::HashSet;
67

78
use core::{
89
array,
910
fmt::{Debug, Formatter},
11+
hash::{BuildHasher, Hash},
1012
iter::{self, FusedIterator},
1113
option, result,
1214
};
@@ -144,7 +146,21 @@ impl<T: IntoIterator<IntoIter: EntitySetIterator>> EntitySet for T {}
144146
///
145147
/// `x != y` must hold for any 2 elements returned by the iterator.
146148
/// This is always true for iterators that cannot return more than one element.
147-
pub unsafe trait EntitySetIterator: Iterator<Item: TrustedEntityBorrow> {}
149+
pub unsafe trait EntitySetIterator: Iterator<Item: TrustedEntityBorrow> {
150+
/// Transforms an `EntitySetIterator` into a collection.
151+
///
152+
/// This is a specialized form of [`collect`], for collections which benefit from the uniqueness guarantee.
153+
/// When present, this should always be preferred over [`collect`].
154+
///
155+
/// [`collect`]: Iterator::collect
156+
// FIXME: When subtrait item shadowing stabilizes, this should be renamed and shadow `Iterator::collect`
157+
fn collect_set<B: FromEntitySetIterator<Self::Item>>(self) -> B
158+
where
159+
Self: Sized,
160+
{
161+
FromEntitySetIterator::from_entity_set_iter(self)
162+
}
163+
}
148164

149165
// SAFETY:
150166
// A correct `BTreeMap` contains only unique keys.
@@ -291,6 +307,36 @@ unsafe impl<I: EntitySetIterator, P: FnMut(&<I as Iterator>::Item) -> bool> Enti
291307
// SAFETY: Discarding elements maintains uniqueness.
292308
unsafe impl<I: EntitySetIterator> EntitySetIterator for iter::StepBy<I> {}
293309

310+
/// Conversion from an `EntitySetIterator`.
311+
///
312+
/// Some collections, while they can be constructed from plain iterators,
313+
/// benefit strongly from the additional uniqeness guarantee [`EntitySetIterator`] offers.
314+
/// Mirroring [`Iterator::collect`]/[`FromIterator::from_iter`], [`EntitySetIterator::collect_set`] and
315+
/// `FromEntitySetIterator::from_entity_set_iter` can be used for construction.
316+
///
317+
/// See also: [`EntitySet`].
318+
// FIXME: When subtrait item shadowing stabilizes, this should be renamed and shadow `FromIterator::from_iter`
319+
pub trait FromEntitySetIterator<A: TrustedEntityBorrow>: FromIterator<A> {
320+
/// Creates a value from an [`EntitySetIterator`].
321+
fn from_entity_set_iter<T: EntitySet<Item = A>>(set_iter: T) -> Self;
322+
}
323+
324+
impl<T: TrustedEntityBorrow + Hash, S: BuildHasher + Default> FromEntitySetIterator<T>
325+
for HashSet<T, S>
326+
{
327+
fn from_entity_set_iter<I: EntitySet<Item = T>>(set_iter: I) -> Self {
328+
let iter = set_iter.into_iter();
329+
let set = HashSet::<T, S>::with_capacity_and_hasher(iter.size_hint().0, S::default());
330+
iter.fold(set, |mut set, e| {
331+
// SAFETY: Every element in self is unique.
332+
unsafe {
333+
set.insert_unique_unchecked(e);
334+
}
335+
set
336+
})
337+
}
338+
}
339+
294340
/// An iterator that yields unique entities.
295341
///
296342
/// This wrapper can provide an [`EntitySetIterator`] implementation when an instance of `I` is known to uphold uniqueness.

crates/bevy_ecs/src/entity/hash_set.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use bevy_platform_support::collections::hash_set::{self, HashSet};
1616
#[cfg(feature = "bevy_reflect")]
1717
use bevy_reflect::Reflect;
1818

19-
use super::{Entity, EntityHash, EntitySetIterator};
19+
use super::{Entity, EntityHash, EntitySet, EntitySetIterator, FromEntitySetIterator};
2020

2121
/// A [`HashSet`] pre-configured to use [`EntityHash`] hashing.
2222
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
@@ -195,6 +195,20 @@ impl FromIterator<Entity> for EntityHashSet {
195195
}
196196
}
197197

198+
impl FromEntitySetIterator<Entity> for EntityHashSet {
199+
fn from_entity_set_iter<I: EntitySet<Item = Entity>>(set_iter: I) -> Self {
200+
let iter = set_iter.into_iter();
201+
let set = EntityHashSet::with_capacity(iter.size_hint().0);
202+
iter.fold(set, |mut set, e| {
203+
// SAFETY: Every element in self is unique.
204+
unsafe {
205+
set.insert_unique_unchecked(e);
206+
}
207+
set
208+
})
209+
}
210+
}
211+
198212
/// An iterator over the items of an [`EntityHashSet`].
199213
///
200214
/// This struct is created by the [`iter`] method on [`EntityHashSet`]. See its documentation for more.

0 commit comments

Comments
 (0)