Skip to content

Split EntityClonerBuilder in AllowAll and DenyAll variants #19649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 26 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
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
122 changes: 95 additions & 27 deletions benches/benches/bevy_ecs/entity_cloning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core::hint::black_box;
use benches::bench;
use bevy_ecs::bundle::Bundle;
use bevy_ecs::component::ComponentCloneBehavior;
use bevy_ecs::entity::EntityCloner;
use bevy_ecs::entity::{EntityCloner, EntityClonerBuilder};
use bevy_ecs::hierarchy::ChildOf;
use bevy_ecs::reflect::AppTypeRegistry;
use bevy_ecs::{component::Component, world::World};
Expand All @@ -19,18 +19,38 @@ criterion_group!(
hierarchy_many,
);

#[derive(Component, Reflect, Default, Clone)]
#[require(C1)]
struct E1(Mat4);

#[derive(Component, Reflect, Default, Clone)]
struct C1(Mat4);

#[derive(Component, Reflect, Default, Clone)]
#[require(C2)]
struct E2(Mat4);

#[derive(Component, Reflect, Default, Clone)]
struct C2(Mat4);

#[derive(Component, Reflect, Default, Clone)]
#[require(C3)]
struct E3(Mat4);

#[derive(Component, Reflect, Default, Clone)]
struct C3(Mat4);

#[derive(Component, Reflect, Default, Clone)]
#[require(C4)]
struct E4(Mat4);

#[derive(Component, Reflect, Default, Clone)]
struct C4(Mat4);

#[derive(Component, Reflect, Default, Clone)]
#[require(C5)]
struct E5(Mat4);

#[derive(Component, Reflect, Default, Clone)]
struct C5(Mat4);

Expand All @@ -51,12 +71,12 @@ struct C10(Mat4);

type ComplexBundle = (C1, C2, C3, C4, C5, C6, C7, C8, C9, C10);

/// Sets the [`ComponentCloneHandler`] for all explicit and required components in a bundle `B` to
/// Sets the [`ComponentCloneBehavior`] for all explicit and required components in a bundle `B` to
/// use the [`Reflect`] trait instead of [`Clone`].
fn reflection_cloner<B: Bundle + GetTypeRegistration>(
fn reflection_cloner<B: Bundle + GetTypeRegistration, Filter>(
world: &mut World,
linked_cloning: bool,
) -> EntityCloner {
) -> impl FnOnce(&mut EntityClonerBuilder<Filter>) + 'static {
// Get mutable access to the type registry, creating it if it does not exist yet.
let registry = world.get_resource_or_init::<AppTypeRegistry>();

Expand All @@ -71,35 +91,35 @@ fn reflection_cloner<B: Bundle + GetTypeRegistration>(
// this bundle are saved.
let component_ids: Vec<_> = world.register_bundle::<B>().contributed_components().into();

let mut builder = EntityCloner::build(world);

// Overwrite the clone handler for all components in the bundle to use `Reflect`, not `Clone`.
for component in component_ids {
builder.override_clone_behavior_with_id(component, ComponentCloneBehavior::reflect());
move |builder| {
// Overwrite the clone handler for all components in the bundle to use `Reflect`, not `Clone`.
for component in component_ids {
builder.override_clone_behavior_with_id(component, ComponentCloneBehavior::reflect());
}
builder.linked_cloning(linked_cloning);
}
builder.linked_cloning(linked_cloning);

builder.finish()
}

/// A helper function that benchmarks running the [`EntityCommands::clone_and_spawn()`] command on a
/// bundle `B`.
/// A helper function that benchmarks running [`EntityCloner::spawn_clone`] with a bundle `B`.
///
/// The bundle must implement [`Default`], which is used to create the first entity that gets cloned
/// in the benchmark.
///
/// If `clone_via_reflect` is false, this will use the default [`ComponentCloneHandler`] for all
/// components (which is usually [`ComponentCloneHandler::clone_handler()`]). If `clone_via_reflect`
/// If `clone_via_reflect` is false, this will use the default [`ComponentCloneBehavior`] for all
/// components (which is usually [`ComponentCloneBehavior::clone()`]). If `clone_via_reflect`
/// is true, it will overwrite the handler for all components in the bundle to be
/// [`ComponentCloneHandler::reflect_handler()`].
fn bench_clone<B: Bundle + Default + GetTypeRegistration>(
/// [`ComponentCloneBehavior::reflect()`].
fn bench_single<B: Bundle + Default + GetTypeRegistration>(
b: &mut Bencher,
clone_via_reflect: bool,
) {
let mut world = World::default();

let mut cloner = if clone_via_reflect {
reflection_cloner::<B>(&mut world, false)
let cfg = reflection_cloner::<B, _>(&mut world, false);
let mut builder = EntityCloner::build_allow_all(&mut world);
cfg(&mut builder);
builder.finish()
} else {
EntityCloner::default()
};
Expand All @@ -114,12 +134,41 @@ fn bench_clone<B: Bundle + Default + GetTypeRegistration>(
});
}

/// A helper function that benchmarks running the [`EntityCommands::clone_and_spawn()`] command on a
/// bundle `B`.
/// A helper function that benchmarks running [`EntityCloner::clone_entity`] with[`bevy_ecs::entity::DenyAll`]
/// filter and allowing 5 components which each have one required component.
fn bench_single_actively_filtered<B: Bundle + Default + GetTypeRegistration>(
b: &mut Bencher,
clone_via_reflect: bool,
) {
let mut world = World::default();

let mut cloner = if clone_via_reflect {
let cfg = reflection_cloner::<B, _>(&mut world, false);
let mut builder = EntityCloner::build_deny_all(&mut world);
cfg(&mut builder);
builder.allow::<B>();
builder.finish()
} else {
let mut builder = EntityCloner::build_deny_all(&mut world);
builder.allow::<B>();
builder.finish()
};

let source = world.spawn(B::default()).id();
let target = world.spawn_empty().id();

b.iter(|| {
// clones the given entity's components to the target
cloner.clone_entity(&mut world, black_box(source), black_box(target));
world.flush();
});
}

/// A helper function that benchmarks running [`EntityCloner::spawn_clone`] with a bundle `B`.
///
/// As compared to [`bench_clone()`], this benchmarks recursively cloning an entity with several
/// children. It does so by setting up an entity tree with a given `height` where each entity has a
/// specified number of `children`.
/// As compared to [`bench_clone_single_unfiltered_all()`], this benchmarks recursively cloning an
/// entity with several children. It does so by setting up an entity tree with a given `height`
/// where each entity has a specified number of `children`.
///
/// For example, setting `height` to 5 and `children` to 1 creates a single chain of entities with
/// no siblings. Alternatively, setting `height` to 1 and `children` to 5 will spawn 5 direct
Expand All @@ -133,9 +182,12 @@ fn bench_clone_hierarchy<B: Bundle + Default + GetTypeRegistration>(
let mut world = World::default();

let mut cloner = if clone_via_reflect {
reflection_cloner::<B>(&mut world, true)
let cfg = reflection_cloner::<B, _>(&mut world, true);
let mut builder = EntityCloner::build_allow_all(&mut world);
cfg(&mut builder);
builder.finish()
} else {
let mut builder = EntityCloner::build(&mut world);
let mut builder = EntityCloner::build_allow_all(&mut world);
builder.linked_cloning(true);
builder.finish()
};
Expand Down Expand Up @@ -180,7 +232,23 @@ fn single(c: &mut Criterion) {

for (id, clone_via_reflect) in SCENARIOS {
group.bench_function(id, |b| {
bench_clone::<ComplexBundle>(b, clone_via_reflect);
bench_single::<ComplexBundle>(b, clone_via_reflect);
});
}

group.finish();
}

/// Benchmarks cloning a single entity with 10 components where each needs to be evaluated
fn single_actively_filtered(c: &mut Criterion) {
let mut group = c.benchmark_group(bench!("single_actively_filtered"));

// We're cloning 1 entity.
group.throughput(Throughput::Elements(1));

for (id, clone_via_reflect) in SCENARIOS {
group.bench_function(id, |b| {
bench_single_actively_filtered::<(E1, E2, E3, E4, E5)>(b, clone_via_reflect);
});
}

Expand Down
Loading