Fix spawn tracking for spawn commands (#19351)
See also https://discord.com/channels/691052431525675048/1374187654425481266/1375553989185372292. Set spawn info in `Commands::spawn_empty`. Also added a benchmark for `Commands::spawn`. See added test.
This commit is contained in:
parent
6fc2e919b8
commit
4562bb484f
@ -62,6 +62,31 @@ pub fn spawn_commands(criterion: &mut Criterion) {
|
|||||||
group.finish();
|
group.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn nonempty_spawn_commands(criterion: &mut Criterion) {
|
||||||
|
let mut group = criterion.benchmark_group("nonempty_spawn_commands");
|
||||||
|
group.warm_up_time(core::time::Duration::from_millis(500));
|
||||||
|
group.measurement_time(core::time::Duration::from_secs(4));
|
||||||
|
|
||||||
|
for entity_count in [100, 1_000, 10_000] {
|
||||||
|
group.bench_function(format!("{}_entities", entity_count), |bencher| {
|
||||||
|
let mut world = World::default();
|
||||||
|
let mut command_queue = CommandQueue::default();
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
let mut commands = Commands::new(&mut command_queue, &world);
|
||||||
|
for i in 0..entity_count {
|
||||||
|
if black_box(i % 2 == 0) {
|
||||||
|
commands.spawn(A);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command_queue.apply(&mut world);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Component)]
|
#[derive(Default, Component)]
|
||||||
struct Matrix([[f32; 4]; 4]);
|
struct Matrix([[f32; 4]; 4]);
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ criterion_group!(
|
|||||||
benches,
|
benches,
|
||||||
empty_commands,
|
empty_commands,
|
||||||
spawn_commands,
|
spawn_commands,
|
||||||
|
nonempty_spawn_commands,
|
||||||
insert_commands,
|
insert_commands,
|
||||||
fake_commands,
|
fake_commands,
|
||||||
zero_sized_commands,
|
zero_sized_commands,
|
||||||
|
@ -74,6 +74,7 @@ pub use unique_vec::{UniqueEntityEquivalentVec, UniqueEntityVec};
|
|||||||
use crate::{
|
use crate::{
|
||||||
archetype::{ArchetypeId, ArchetypeRow},
|
archetype::{ArchetypeId, ArchetypeRow},
|
||||||
change_detection::MaybeLocation,
|
change_detection::MaybeLocation,
|
||||||
|
component::Tick,
|
||||||
identifier::{
|
identifier::{
|
||||||
error::IdentifierError,
|
error::IdentifierError,
|
||||||
kinds::IdKind,
|
kinds::IdKind,
|
||||||
@ -84,7 +85,13 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use bevy_platform::sync::atomic::Ordering;
|
use bevy_platform::sync::atomic::Ordering;
|
||||||
use core::{fmt, hash::Hash, mem, num::NonZero, panic::Location};
|
use core::{
|
||||||
|
fmt,
|
||||||
|
hash::Hash,
|
||||||
|
mem::{self},
|
||||||
|
num::NonZero,
|
||||||
|
panic::Location,
|
||||||
|
};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
@ -866,6 +873,20 @@ impl Entities {
|
|||||||
meta.location = location;
|
meta.location = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// - `index` must be a valid entity index.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) unsafe fn mark_spawn_despawn(&mut self, index: u32, by: MaybeLocation, _at: Tick) {
|
||||||
|
// // SAFETY: Caller guarantees that `index` a valid entity index
|
||||||
|
// let meta = unsafe { self.meta.get_unchecked_mut(index as usize) };
|
||||||
|
// meta.spawned_or_despawned_by = MaybeUninit::new(SpawnedOrDespawned { by, at });
|
||||||
|
by.map(|caller| {
|
||||||
|
// SAFETY: Caller guarantees that `index` a valid entity index
|
||||||
|
let meta = unsafe { self.meta.get_unchecked_mut(index as usize) };
|
||||||
|
meta.spawned_or_despawned_by = MaybeLocation::new(Some(caller));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Increments the `generation` of a freed [`Entity`]. The next entity ID allocated with this
|
/// Increments the `generation` of a freed [`Entity`]. The next entity ID allocated with this
|
||||||
/// `index` will count `generation` starting from the prior `generation` + the specified
|
/// `index` will count `generation` starting from the prior `generation` + the specified
|
||||||
/// value + 1.
|
/// value + 1.
|
||||||
|
@ -318,12 +318,24 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
/// - [`spawn`](Self::spawn) to spawn an entity with components.
|
/// - [`spawn`](Self::spawn) to spawn an entity with components.
|
||||||
/// - [`spawn_batch`](Self::spawn_batch) to spawn many entities
|
/// - [`spawn_batch`](Self::spawn_batch) to spawn many entities
|
||||||
/// with the same combination of components.
|
/// with the same combination of components.
|
||||||
|
#[track_caller]
|
||||||
pub fn spawn_empty(&mut self) -> EntityCommands {
|
pub fn spawn_empty(&mut self) -> EntityCommands {
|
||||||
let entity = self.entities.reserve_entity();
|
let entity = self.entities.reserve_entity();
|
||||||
EntityCommands {
|
let mut entity_commands = EntityCommands {
|
||||||
entity,
|
entity,
|
||||||
commands: self.reborrow(),
|
commands: self.reborrow(),
|
||||||
}
|
};
|
||||||
|
let caller = MaybeLocation::caller();
|
||||||
|
entity_commands.queue(move |entity: EntityWorldMut| {
|
||||||
|
let index = entity.id().index();
|
||||||
|
let world = entity.into_world_mut();
|
||||||
|
let tick = world.change_tick();
|
||||||
|
// SAFETY: Entity has been flushed
|
||||||
|
unsafe {
|
||||||
|
world.entities_mut().mark_spawn_despawn(index, caller, tick);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
entity_commands
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns a new [`Entity`] with the given components
|
/// Spawns a new [`Entity`] with the given components
|
||||||
@ -370,9 +382,35 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
/// with the same combination of components.
|
/// with the same combination of components.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn spawn<T: Bundle>(&mut self, bundle: T) -> EntityCommands {
|
pub fn spawn<T: Bundle>(&mut self, bundle: T) -> EntityCommands {
|
||||||
let mut entity = self.spawn_empty();
|
let entity = self.entities.reserve_entity();
|
||||||
entity.insert(bundle);
|
let mut entity_commands = EntityCommands {
|
||||||
entity
|
entity,
|
||||||
|
commands: self.reborrow(),
|
||||||
|
};
|
||||||
|
let caller = MaybeLocation::caller();
|
||||||
|
|
||||||
|
entity_commands.queue(move |mut entity: EntityWorldMut| {
|
||||||
|
// Store metadata about the spawn operation.
|
||||||
|
// This is the same as in `spawn_empty`, but merged into
|
||||||
|
// the same command for better performance.
|
||||||
|
let index = entity.id().index();
|
||||||
|
entity.world_scope(|world| {
|
||||||
|
let tick = world.change_tick();
|
||||||
|
// SAFETY: Entity has been flushed
|
||||||
|
unsafe {
|
||||||
|
world.entities_mut().mark_spawn_despawn(index, caller, tick);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
entity.insert_with_caller(
|
||||||
|
bundle,
|
||||||
|
InsertMode::Replace,
|
||||||
|
caller,
|
||||||
|
crate::relationship::RelationshipHookMode::Run,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
// entity_command::insert(bundle, InsertMode::Replace)
|
||||||
|
entity_commands
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the [`EntityCommands`] for the given [`Entity`].
|
/// Returns the [`EntityCommands`] for the given [`Entity`].
|
||||||
|
Loading…
Reference in New Issue
Block a user