Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0200afd
typed key implementation
eugineerd May 5, 2025
6c4bf20
Implement fragmenting value components
eugineerd May 9, 2025
d5be3b5
Merge remote-tracking branch 'upstream/main' into value-components
eugineerd May 9, 2025
c84f7a1
update msrv to 1.86.0
eugineerd May 9, 2025
c8d064c
remove `filter_by_component_value` in Query implementation
eugineerd May 14, 2025
3032426
add support for `DynamicFragmentingValue` in dyn `FragmentingValue::eq`
eugineerd May 14, 2025
7fd9bf7
Remove `storage!=Table` restriction for fragmenting value components.
eugineerd May 14, 2025
7cfc733
fixed fragmenting value map being reset
eugineerd May 15, 2025
d41d9fc
add migration guide
eugineerd May 15, 2025
5f460e6
Merge branch 'main' of https://github.com/bevyengine/bevy into value-…
eugineerd May 19, 2025
a777ce6
a bit more documentation
eugineerd May 20, 2025
149634e
add some benches
eugineerd May 20, 2025
2ada359
Merge branch 'main' into value-components
eugineerd May 28, 2025
bf9c262
Merge branch 'main' into value-components
eugineerd Jun 1, 2025
5cfd383
Merge remote-tracking branch 'upstream/main' into value-components
eugineerd Jun 3, 2025
e2d48ed
Merge remote-tracking branch 'upstream/main' into value-components
eugineerd Jun 11, 2025
856f0f9
Merge branch 'main' into value-components
eugineerd Jun 21, 2025
4465d82
Merge remote-tracking branch 'upstream/main' into value-components
eugineerd Jul 23, 2025
2ce8747
restore changes after the splittening
eugineerd Jul 23, 2025
c94c5b7
fix doc
eugineerd Jul 23, 2025
8555c9d
Merge branch 'main' into value-components
eugineerd Sep 2, 2025
ad8432f
rewrite `DynamicFragmentingValue` to better support dynamic components
eugineerd Sep 17, 2025
38c09e9
Merge branch 'main' of https://github.com/bevyengine/bevy into value-…
eugineerd Sep 17, 2025
c756571
rewrite to rely on only `ComponentId`
eugineerd Sep 21, 2025
6fb3d19
fix `SpawnBatchIter`
eugineerd Sep 21, 2025
8080028
misc changes
eugineerd Sep 23, 2025
6362e3b
Merge branch 'main' of https://github.com/bevyengine/bevy into value-…
eugineerd Sep 23, 2025
684a1c9
renames
eugineerd Sep 23, 2025
59b2a98
fix insert caching wrong value components
eugineerd Sep 23, 2025
3f94ab1
move stuff around a bit
eugineerd Sep 24, 2025
47128c4
initial doc pass
eugineerd Sep 26, 2025
ddb1542
Merge branch 'main' into value-components
eugineerd Sep 26, 2025
62f3abd
another doc pass
eugineerd Sep 26, 2025
d84f68d
replace `core::sync::Arc` with `bevy_platform`'s `Arc`
eugineerd Sep 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions benches/benches/bevy_ecs/components/fragmenting_values.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use bevy_ecs::prelude::*;
use criterion::Criterion;
use glam::*;

#[derive(Component, PartialEq, Eq, Hash, Clone)]
#[component(immutable, key=Self)]
struct Fragmenting<const N: usize>(u32);

#[derive(Component)]
struct NonFragmenting<const N: usize>(Vec3);

pub fn insert_fragmenting_value(c: &mut Criterion) {
let mut group = c.benchmark_group("insert_fragmenting_value");
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(5));
group.bench_function("base", |b| {
b.iter(move || {
let mut world = World::new();
world.spawn_batch((0..10_000).map(|_| {
(
Fragmenting::<1>(1),
NonFragmenting::<1>(Vec3::ONE),
NonFragmenting::<2>(Vec3::ONE),
NonFragmenting::<3>(Vec3::ONE),
)
}));
});
});
group.bench_function("unbatched", |b| {
b.iter(move || {
let mut world = World::new();
for _ in 0..10_000 {
world.spawn((
Fragmenting::<1>(1),
NonFragmenting::<1>(Vec3::ONE),
NonFragmenting::<2>(Vec3::ONE),
NonFragmenting::<3>(Vec3::ONE),
));
}
});
});
group.bench_function("high_fragmentation_base", |b| {
b.iter(move || {
let mut world = World::new();
world.spawn_batch((0..10_000).map(|i| {
(
Fragmenting::<1>(i % 100),
NonFragmenting::<1>(Vec3::ONE),
NonFragmenting::<2>(Vec3::ONE),
NonFragmenting::<3>(Vec3::ONE),
)
}));
});
});
group.bench_function("high_fragmentation_unbatched", |b| {
b.iter(move || {
let mut world = World::new();
for i in 0..10_000 {
world.spawn((
Fragmenting::<1>(i % 100),
NonFragmenting::<1>(Vec3::ONE),
NonFragmenting::<2>(Vec3::ONE),
NonFragmenting::<3>(Vec3::ONE),
));
}
});
});
group.finish();
}

pub fn add_remove_fragmenting_value(c: &mut Criterion) {
let mut group = c.benchmark_group("add_remove_fragmenting_value");
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(5));

group.bench_function("non_fragmenting", |b| {
let mut world = World::new();
let entities: Vec<_> = world
.spawn_batch((0..10_000).map(|_| {
(
Fragmenting::<1>(1),
NonFragmenting::<1>(Vec3::ONE),
NonFragmenting::<2>(Vec3::ONE),
NonFragmenting::<3>(Vec3::ONE),
)
}))
.collect();
b.iter(move || {
for entity in &entities {
world
.entity_mut(*entity)
.insert(NonFragmenting::<4>(Vec3::ZERO));
}

for entity in &entities {
world.entity_mut(*entity).remove::<NonFragmenting<4>>();
}
});
});

group.bench_function("fragmenting", |b| {
let mut world = World::new();
let entities: Vec<_> = world
.spawn_batch((0..10_000).map(|_| {
(
Fragmenting::<1>(1),
NonFragmenting::<1>(Vec3::ONE),
NonFragmenting::<2>(Vec3::ONE),
NonFragmenting::<3>(Vec3::ONE),
)
}))
.collect();
b.iter(move || {
for entity in &entities {
world.entity_mut(*entity).insert(Fragmenting::<2>(1));
}

for entity in &entities {
world.entity_mut(*entity).remove::<Fragmenting<2>>();
}
});
});
}
4 changes: 4 additions & 0 deletions benches/benches/bevy_ecs/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ mod add_remove_sparse_set;
mod add_remove_table;
mod add_remove_very_big_table;
mod archetype_updates;
mod fragmenting_values;
mod insert_simple;
mod insert_simple_unbatched;

use archetype_updates::*;
use criterion::{criterion_group, Criterion};
use fragmenting_values::*;

criterion_group!(
benches,
Expand All @@ -19,6 +21,8 @@ criterion_group!(
insert_simple,
no_archetypes,
added_archetypes,
insert_fragmenting_value,
add_remove_fragmenting_value
);

fn add_remove(c: &mut Criterion) {
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_core_pipeline/src/oit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ impl Default for OrderIndependentTransparencySettings {
impl Component for OrderIndependentTransparencySettings {
const STORAGE_TYPE: StorageType = StorageType::SparseSet;
type Mutability = Mutable;
type Key = NoKey;

fn on_add() -> Option<ComponentHook> {
Some(|world, context| {
Expand Down
12 changes: 12 additions & 0 deletions crates/bevy_ecs/macros/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,19 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
)
};

let key = attrs
.key
.map(ToTokens::into_token_stream)
.unwrap_or_else(|| quote! {#bevy_ecs_path::component::NoKey});

// This puts `register_required` before `register_recursive_requires` to ensure that the constructors of _all_ top
// level components are initialized first, giving them precedence over recursively defined constructors for the same component type
TokenStream::from(quote! {
#required_component_docs
impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause {
const STORAGE_TYPE: #bevy_ecs_path::component::StorageType = #storage;
type Mutability = #mutable_type;
type Key = #key;
fn register_required_components(
_requiree: #bevy_ecs_path::component::ComponentId,
required_components: &mut #bevy_ecs_path::component::RequiredComponentsRegistrator,
Expand Down Expand Up @@ -342,6 +348,7 @@ pub const MAP_ENTITIES: &str = "map_entities";

pub const IMMUTABLE: &str = "immutable";
pub const CLONE_BEHAVIOR: &str = "clone_behavior";
pub const KEY: &str = "key";

/// All allowed attribute value expression kinds for component hooks.
/// This doesn't simply use general expressions because of conflicting needs:
Expand Down Expand Up @@ -460,6 +467,7 @@ struct Attrs {
immutable: bool,
clone_behavior: Option<Expr>,
map_entities: Option<MapEntitiesAttributeKind>,
key: Option<Type>,
}

#[derive(Clone, Copy)]
Expand Down Expand Up @@ -500,6 +508,7 @@ fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
immutable: false,
clone_behavior: None,
map_entities: None,
key: None,
};

let mut require_paths = HashSet::new();
Expand Down Expand Up @@ -541,6 +550,9 @@ fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
} else if nested.path.is_ident(MAP_ENTITIES) {
attrs.map_entities = Some(nested.input.parse::<MapEntitiesAttributeKind>()?);
Ok(())
} else if nested.path.is_ident(KEY) {
attrs.key = Some(nested.value()?.parse()?);
Ok(())
} else {
Err(nested.error("Unsupported attribute"))
}
Expand Down
15 changes: 15 additions & 0 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,21 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
) {
#(<#active_field_types as #ecs_path::bundle::Bundle>::get_component_ids(components, &mut *ids);)*
}

#[allow(unused_variables)]
#[inline]
fn get_fragmenting_values<'a>(
&'a self,
components: &#ecs_path::component::Components,
values: &mut impl FnMut(#ecs_path::component::FragmentingValueBorrowed<'a>)
) {
#(self.#active_field_tokens.get_fragmenting_values(components, &mut *values);)*
}

#[inline(always)]
fn count_fragmenting_values() -> usize {
0 #(+ <#active_field_types as #ecs_path::bundle::Bundle>::count_fragmenting_values())*
}
}
};

Expand Down
Loading
Loading