Skip to content

Commit 25aba2e

Browse files
authored
Change Bundle::component_ids to return an iterator (#21821)
# Objective - As part of #21780, I need a way to iterate over the component ids of a bundle for `Entity*Except` conflict checking without allocating. Pulled this out as it changes some unrelated code too. ## Solution - Change `Bundle::component_ids` and `Bundle::get_component_ids` to return an iterator instead of taking a closure. In theory I would expect this to compile to the same asm. I would also argue that using an iterator is a more natural api for this than the closure. It probably took a closure before because expressing that the iterator doesn't capture the `&mut ComponentRegistrator` lifetime wasn't possible without the `use` syntax. - Removed some #[allow(deprecated)] in the Bundle macro that was missed. ## Testing - Checked the asm for `hook_on_add` in the observers example for to confirm it was still the same. This is a pretty simple example though, so not sure how good of a check this is. - None of the code touched are in any hot paths, but ran the spawn and insert benches. Any changes seem to be in the noise.
1 parent 2a24658 commit 25aba2e

File tree

11 files changed

+118
-90
lines changed

11 files changed

+118
-90
lines changed

crates/bevy_ecs/macros/src/lib.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
124124
}
125125
}
126126
let generics = ast.generics;
127+
let generics_ty_list = generics.type_params().map(|p| p.ident.clone());
127128
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
128129
let struct_name = &ast.ident;
129130

@@ -132,20 +133,17 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
132133
// - ComponentId is returned in field-definition-order. [get_components] uses field-definition-order
133134
// - `Bundle::get_components` is exactly once for each member. Rely's on the Component -> Bundle implementation to properly pass
134135
// the correct `StorageType` into the callback.
135-
#[allow(deprecated)]
136136
unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause {
137137
fn component_ids(
138138
components: &mut #ecs_path::component::ComponentsRegistrator,
139-
ids: &mut impl FnMut(#ecs_path::component::ComponentId)
140-
) {
141-
#(<#active_field_types as #ecs_path::bundle::Bundle>::component_ids(components, ids);)*
139+
) -> impl Iterator<Item = #ecs_path::component::ComponentId> + use<#(#generics_ty_list,)*> {
140+
core::iter::empty()#(.chain(<#active_field_types as #ecs_path::bundle::Bundle>::component_ids(components)))*
142141
}
143142

144143
fn get_component_ids(
145144
components: &#ecs_path::component::Components,
146-
ids: &mut impl FnMut(Option<#ecs_path::component::ComponentId>)
147-
) {
148-
#(<#active_field_types as #ecs_path::bundle::Bundle>::get_component_ids(components, &mut *ids);)*
145+
) -> impl Iterator<Item = Option<#ecs_path::component::ComponentId>> {
146+
core::iter::empty()#(.chain(<#active_field_types as #ecs_path::bundle::Bundle>::get_component_ids(components)))*
149147
}
150148
}
151149
};
@@ -185,7 +183,6 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
185183
let from_components_impl = attributes.impl_from_components.then(|| quote! {
186184
// SAFETY:
187185
// - ComponentId is returned in field-definition-order. [from_components] uses field-definition-order
188-
#[allow(deprecated)]
189186
unsafe impl #impl_generics #ecs_path::bundle::BundleFromComponents for #struct_name #ty_generics #where_clause {
190187
#[allow(unused_variables, non_snake_case)]
191188
unsafe fn from_components<__T, __F>(ctx: &mut __T, func: &mut __F) -> Self

crates/bevy_ecs/src/bundle/impls.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use core::any::TypeId;
1+
use core::{any::TypeId, iter};
22

33
use bevy_ptr::{MovingPtr, OwningPtr};
44
use core::mem::MaybeUninit;
@@ -14,12 +14,14 @@ use crate::{
1414
// - `Bundle::component_ids` calls `ids` for C's component id (and nothing else)
1515
// - `Bundle::get_components` is called exactly once for C and passes the component's storage type based on its associated constant.
1616
unsafe impl<C: Component> Bundle for C {
17-
fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)) {
18-
ids(components.register_component::<C>());
17+
fn component_ids(
18+
components: &mut ComponentsRegistrator,
19+
) -> impl Iterator<Item = ComponentId> + use<C> {
20+
iter::once(components.register_component::<C>())
1921
}
2022

21-
fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)) {
22-
ids(components.get_id(TypeId::of::<C>()));
23+
fn get_component_ids(components: &Components) -> impl Iterator<Item = Option<ComponentId>> {
24+
iter::once(components.get_id(TypeId::of::<C>()))
2325
}
2426
}
2527

@@ -71,12 +73,12 @@ macro_rules! tuple_impl {
7173
// - `Bundle::get_components` is called exactly once for each member. Relies on the above implementation to pass the correct
7274
// `StorageType` into the callback.
7375
unsafe impl<$($name: Bundle),*> Bundle for ($($name,)*) {
74-
fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)){
75-
$(<$name as Bundle>::component_ids(components, ids);)*
76+
fn component_ids<'a>(components: &'a mut ComponentsRegistrator) -> impl Iterator<Item = ComponentId> + use<$($name,)*> {
77+
iter::empty()$(.chain(<$name as Bundle>::component_ids(components)))*
7678
}
7779

78-
fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)){
79-
$(<$name as Bundle>::get_component_ids(components, ids);)*
80+
fn get_component_ids(components: &Components) -> impl Iterator<Item = Option<ComponentId>> {
81+
iter::empty()$(.chain(<$name as Bundle>::get_component_ids(components)))*
8082
}
8183
}
8284

crates/bevy_ecs/src/bundle/info.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,8 +435,7 @@ impl Bundles {
435435
) -> BundleId {
436436
let bundle_infos = &mut self.bundle_infos;
437437
*self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
438-
let mut component_ids= Vec::new();
439-
T::component_ids(components, &mut |id| component_ids.push(id));
438+
let component_ids = T::component_ids(components).collect::<Vec<_>>();
440439
let id = BundleId(bundle_infos.len());
441440
let bundle_info =
442441
// SAFETY: T::component_id ensures:

crates/bevy_ecs/src/bundle/mod.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,14 @@ use bevy_ptr::OwningPtr;
199199
)]
200200
pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static {
201201
/// Gets this [`Bundle`]'s component ids, in the order of this bundle's [`Component`]s
202+
/// This will register the component if it doesn't exist.
202203
#[doc(hidden)]
203-
fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId));
204+
fn component_ids(
205+
components: &mut ComponentsRegistrator,
206+
) -> impl Iterator<Item = ComponentId> + use<Self>;
204207

205-
/// Gets this [`Bundle`]'s component ids. This will be [`None`] if the component has not been registered.
206-
fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>));
208+
/// Return a iterator over this [`Bundle`]'s component ids. This will be [`None`] if the component has not been registered.
209+
fn get_component_ids(components: &Components) -> impl Iterator<Item = Option<ComponentId>>;
207210
}
208211

209212
/// Creates a [`Bundle`] by taking it from internal storage.

crates/bevy_ecs/src/lib.rs

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,8 @@ mod tests {
244244
x: TableStored,
245245
y: SparseStored,
246246
}
247-
let mut ids = Vec::new();
248-
<FooBundle as Bundle>::component_ids(&mut world.components_registrator(), &mut |id| {
249-
ids.push(id);
250-
});
247+
let ids: Vec<_> =
248+
<FooBundle as Bundle>::component_ids(&mut world.components_registrator()).collect();
251249

252250
assert_eq!(
253251
ids,
@@ -294,10 +292,8 @@ mod tests {
294292
b: B,
295293
}
296294

297-
let mut ids = Vec::new();
298-
<NestedBundle as Bundle>::component_ids(&mut world.components_registrator(), &mut |id| {
299-
ids.push(id);
300-
});
295+
let ids: Vec<_> =
296+
<NestedBundle as Bundle>::component_ids(&mut world.components_registrator()).collect();
301297

302298
assert_eq!(
303299
ids,
@@ -346,13 +342,9 @@ mod tests {
346342
ignored: Ignored,
347343
}
348344

349-
let mut ids = Vec::new();
350-
<BundleWithIgnored as Bundle>::component_ids(
351-
&mut world.components_registrator(),
352-
&mut |id| {
353-
ids.push(id);
354-
},
355-
);
345+
let ids: Vec<_> =
346+
<BundleWithIgnored as Bundle>::component_ids(&mut world.components_registrator())
347+
.collect();
356348

357349
assert_eq!(ids, &[world.register_component::<C>(),]);
358350

crates/bevy_ecs/src/observer/distributed_storage.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -426,10 +426,8 @@ fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
426426
) {
427427
world.commands().queue(move |world: &mut World| {
428428
let event_key = world.register_event_key::<E>();
429-
let mut components = alloc::vec![];
430-
B::component_ids(&mut world.components_registrator(), &mut |id| {
431-
components.push(id);
432-
});
429+
let components = B::component_ids(&mut world.components_registrator());
430+
433431
if let Some(mut observer) = world.get_mut::<Observer>(entity) {
434432
observer.descriptor.event_keys.push(event_key);
435433
observer.descriptor.components.extend(components);

crates/bevy_ecs/src/query/fetch.rs

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,26 +1243,24 @@ where
12431243
fn init_state(world: &mut World) -> Self::State {
12441244
let mut access = Access::new();
12451245
access.read_all_components();
1246-
B::component_ids(&mut world.components_registrator(), &mut |id| {
1246+
for id in B::component_ids(&mut world.components_registrator()) {
12471247
access.remove_component_read(id);
1248-
});
1248+
}
12491249
access
12501250
}
12511251

12521252
fn get_state(components: &Components) -> Option<Self::State> {
12531253
let mut access = Access::new();
12541254
access.read_all_components();
1255-
B::get_component_ids(components, &mut |maybe_id| {
1256-
// If the component isn't registered, we don't have a `ComponentId`
1257-
// to use to exclude its access.
1258-
// Rather than fail, just try to take additional access.
1259-
// This is sound because access checks will run on the resulting access.
1260-
// Since the component isn't registered, there are no entities with that
1261-
// component, and the extra access will usually have no effect.
1262-
if let Some(id) = maybe_id {
1263-
access.remove_component_read(id);
1264-
}
1265-
});
1255+
// If the component isn't registered, we don't have a `ComponentId`
1256+
// to use to exclude its access.
1257+
// Rather than fail, just try to take additional access.
1258+
// This is sound because access checks will run on the resulting access.
1259+
// Since the component isn't registered, there are no entities with that
1260+
// component, and the extra access will usually have no effect.
1261+
for id in B::get_component_ids(components).flatten() {
1262+
access.remove_component_read(id);
1263+
}
12661264
Some(access)
12671265
}
12681266

@@ -1359,26 +1357,24 @@ where
13591357
fn init_state(world: &mut World) -> Self::State {
13601358
let mut access = Access::new();
13611359
access.write_all_components();
1362-
B::component_ids(&mut world.components_registrator(), &mut |id| {
1360+
for id in B::component_ids(&mut world.components_registrator()) {
13631361
access.remove_component_read(id);
1364-
});
1362+
}
13651363
access
13661364
}
13671365

13681366
fn get_state(components: &Components) -> Option<Self::State> {
13691367
let mut access = Access::new();
13701368
access.write_all_components();
1371-
B::get_component_ids(components, &mut |maybe_id| {
1372-
// If the component isn't registered, we don't have a `ComponentId`
1373-
// to use to exclude its access.
1374-
// Rather than fail, just try to take additional access.
1375-
// This is sound because access checks will run on the resulting access.
1376-
// Since the component isn't registered, there are no entities with that
1377-
// component, and the extra access will usually have no effect.
1378-
if let Some(id) = maybe_id {
1379-
access.remove_component_read(id);
1380-
}
1381-
});
1369+
// If the component isn't registered, we don't have a `ComponentId`
1370+
// to use to exclude its access.
1371+
// Rather than fail, just try to take additional access.
1372+
// This is sound because access checks will run on the resulting access.
1373+
// Since the component isn't registered, there are no entities with that
1374+
// component, and the extra access will usually have no effect.
1375+
for id in B::get_component_ids(components).flatten() {
1376+
access.remove_component_read(id);
1377+
}
13821378
Some(access)
13831379
}
13841380

crates/bevy_ecs/src/spawn.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -300,16 +300,14 @@ unsafe impl<R: Relationship, L: SpawnableList<R> + Send + Sync + 'static> Bundle
300300
{
301301
fn component_ids(
302302
components: &mut crate::component::ComponentsRegistrator,
303-
ids: &mut impl FnMut(crate::component::ComponentId),
304-
) {
305-
<R::RelationshipTarget as Bundle>::component_ids(components, ids);
303+
) -> impl Iterator<Item = crate::component::ComponentId> + use<R, L> {
304+
<R::RelationshipTarget as Bundle>::component_ids(components)
306305
}
307306

308307
fn get_component_ids(
309308
components: &crate::component::Components,
310-
ids: &mut impl FnMut(Option<crate::component::ComponentId>),
311-
) {
312-
<R::RelationshipTarget as Bundle>::get_component_ids(components, ids);
309+
) -> impl Iterator<Item = Option<crate::component::ComponentId>> {
310+
<R::RelationshipTarget as Bundle>::get_component_ids(components)
313311
}
314312
}
315313

@@ -392,16 +390,14 @@ impl<R: Relationship, B: Bundle> DynamicBundle for SpawnOneRelated<R, B> {
392390
unsafe impl<R: Relationship, B: Bundle> Bundle for SpawnOneRelated<R, B> {
393391
fn component_ids(
394392
components: &mut crate::component::ComponentsRegistrator,
395-
ids: &mut impl FnMut(crate::component::ComponentId),
396-
) {
397-
<R::RelationshipTarget as Bundle>::component_ids(components, ids);
393+
) -> impl Iterator<Item = crate::component::ComponentId> + use<R, B> {
394+
<R::RelationshipTarget as Bundle>::component_ids(components)
398395
}
399396

400397
fn get_component_ids(
401398
components: &crate::component::Components,
402-
ids: &mut impl FnMut(Option<crate::component::ComponentId>),
403-
) {
404-
<R::RelationshipTarget as Bundle>::get_component_ids(components, ids);
399+
) -> impl Iterator<Item = Option<crate::component::ComponentId>> {
400+
<R::RelationshipTarget as Bundle>::get_component_ids(components)
405401
}
406402
}
407403

crates/bevy_ecs/src/world/entity_access/except.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -490,10 +490,8 @@ where
490490
B: Bundle,
491491
{
492492
let mut found = false;
493-
B::get_component_ids(components, &mut |maybe_id| {
494-
if let Some(id) = maybe_id {
495-
found = found || id == query_id;
496-
}
497-
});
493+
for id in B::get_component_ids(components).flatten() {
494+
found = found || id == query_id;
495+
}
498496
found
499497
}

crates/bevy_ui_widgets/src/observe.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,17 @@ unsafe impl<
2727
#[inline]
2828
fn component_ids(
2929
_components: &mut bevy_ecs::component::ComponentsRegistrator,
30-
_ids: &mut impl FnMut(bevy_ecs::component::ComponentId),
31-
) {
32-
// SAFETY: Empty function body
30+
) -> impl Iterator<Item = bevy_ecs::component::ComponentId> + use<E, B, M, I> {
31+
// SAFETY: Empty iterator
32+
core::iter::empty()
3333
}
3434

3535
#[inline]
3636
fn get_component_ids(
3737
_components: &bevy_ecs::component::Components,
38-
_ids: &mut impl FnMut(Option<bevy_ecs::component::ComponentId>),
39-
) {
40-
// SAFETY: Empty function body
38+
) -> impl Iterator<Item = Option<bevy_ecs::component::ComponentId>> {
39+
// SAFETY: Empty iterator
40+
core::iter::empty()
4141
}
4242
}
4343

0 commit comments

Comments
 (0)