Deprecate get_or_spawn
(#15652)
# Objective After merging retained rendering world #15320, we now have a good way of creating a link between worlds (*HIYAA intensifies*). This means that `get_or_spawn` is no longer necessary for that function. Entity should be opaque as the warning above `get_or_spawn` says. This is also part of #15459. I'm deprecating `get_or_spawn_batch` in a different PR in order to keep the PR small in size. ## Solution Deprecate `get_or_spawn` and replace it with `get_entity` in most contexts. If it's possible to query `&RenderEntity`, then the entity is synced and `render_entity.id()` is initialized in the render world. ## Migration Guide If you are given an `Entity` and you want to do something with it, use `Commands.entity(...)` or `World.entity(...)`. If instead you want to spawn something use `Commands.spawn(...)` or `World.spawn(...)`. If you are not sure if an entity exists, you can always use `get_entity` and match on the `Option<...>` that is returned. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
parent
037464800e
commit
d1bd46d45e
@ -209,41 +209,3 @@ pub fn medium_sized_commands(criterion: &mut Criterion) {
|
|||||||
pub fn large_sized_commands(criterion: &mut Criterion) {
|
pub fn large_sized_commands(criterion: &mut Criterion) {
|
||||||
sized_commands_impl::<SizedCommand<LargeStruct>>(criterion);
|
sized_commands_impl::<SizedCommand<LargeStruct>>(criterion);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_or_spawn(criterion: &mut Criterion) {
|
|
||||||
let mut group = criterion.benchmark_group("get_or_spawn");
|
|
||||||
group.warm_up_time(core::time::Duration::from_millis(500));
|
|
||||||
group.measurement_time(core::time::Duration::from_secs(4));
|
|
||||||
|
|
||||||
group.bench_function("individual", |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..10_000 {
|
|
||||||
commands
|
|
||||||
.get_or_spawn(Entity::from_raw(i))
|
|
||||||
.insert((Matrix::default(), Vec3::default()));
|
|
||||||
}
|
|
||||||
command_queue.apply(&mut world);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group.bench_function("batched", |bencher| {
|
|
||||||
let mut world = World::default();
|
|
||||||
let mut command_queue = CommandQueue::default();
|
|
||||||
|
|
||||||
bencher.iter(|| {
|
|
||||||
let mut commands = Commands::new(&mut command_queue, &world);
|
|
||||||
let mut values = Vec::with_capacity(10_000);
|
|
||||||
for i in 0..10_000 {
|
|
||||||
values.push((Entity::from_raw(i), (Matrix::default(), Vec3::default())));
|
|
||||||
}
|
|
||||||
commands.insert_or_spawn_batch(values);
|
|
||||||
command_queue.apply(&mut world);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
@ -27,7 +27,6 @@ criterion_group!(
|
|||||||
zero_sized_commands,
|
zero_sized_commands,
|
||||||
medium_sized_commands,
|
medium_sized_commands,
|
||||||
large_sized_commands,
|
large_sized_commands,
|
||||||
get_or_spawn,
|
|
||||||
world_entity,
|
world_entity,
|
||||||
world_get,
|
world_get,
|
||||||
world_query_get,
|
world_query_get,
|
||||||
|
@ -590,7 +590,8 @@ pub fn extract_camera_prepass_phase(
|
|||||||
live_entities.insert(entity);
|
live_entities.insert(entity);
|
||||||
|
|
||||||
commands
|
commands
|
||||||
.get_or_spawn(entity)
|
.get_entity(entity)
|
||||||
|
.expect("Camera entity wasn't synced.")
|
||||||
.insert_if(DepthPrepass, || depth_prepass)
|
.insert_if(DepthPrepass, || depth_prepass)
|
||||||
.insert_if(NormalPrepass, || normal_prepass)
|
.insert_if(NormalPrepass, || normal_prepass)
|
||||||
.insert_if(MotionVectorPrepass, || motion_vector_prepass)
|
.insert_if(MotionVectorPrepass, || motion_vector_prepass)
|
||||||
|
@ -830,14 +830,18 @@ fn extract_depth_of_field_settings(
|
|||||||
calculate_focal_length(depth_of_field.sensor_height, perspective_projection.fov);
|
calculate_focal_length(depth_of_field.sensor_height, perspective_projection.fov);
|
||||||
|
|
||||||
// Convert `DepthOfField` to `DepthOfFieldUniform`.
|
// Convert `DepthOfField` to `DepthOfFieldUniform`.
|
||||||
commands.get_or_spawn(entity).insert((
|
commands
|
||||||
|
.get_entity(entity)
|
||||||
|
.expect("Depth of field entity wasn't synced.")
|
||||||
|
.insert((
|
||||||
*depth_of_field,
|
*depth_of_field,
|
||||||
DepthOfFieldUniform {
|
DepthOfFieldUniform {
|
||||||
focal_distance: depth_of_field.focal_distance,
|
focal_distance: depth_of_field.focal_distance,
|
||||||
focal_length,
|
focal_length,
|
||||||
coc_scale_factor: focal_length * focal_length
|
coc_scale_factor: focal_length * focal_length
|
||||||
/ (depth_of_field.sensor_height * depth_of_field.aperture_f_stops),
|
/ (depth_of_field.sensor_height * depth_of_field.aperture_f_stops),
|
||||||
max_circle_of_confusion_diameter: depth_of_field.max_circle_of_confusion_diameter,
|
max_circle_of_confusion_diameter: depth_of_field
|
||||||
|
.max_circle_of_confusion_diameter,
|
||||||
max_depth: depth_of_field.max_depth,
|
max_depth: depth_of_field.max_depth,
|
||||||
pad_a: 0,
|
pad_a: 0,
|
||||||
pad_b: 0,
|
pad_b: 0,
|
||||||
|
@ -375,7 +375,8 @@ fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut<MainWorld
|
|||||||
let has_perspective_projection = matches!(camera_projection, Projection::Perspective(_));
|
let has_perspective_projection = matches!(camera_projection, Projection::Perspective(_));
|
||||||
if camera.is_active && has_perspective_projection {
|
if camera.is_active && has_perspective_projection {
|
||||||
commands
|
commands
|
||||||
.get_or_spawn(entity.id())
|
.get_entity(entity.id())
|
||||||
|
.expect("Camera entity wasn't synced.")
|
||||||
.insert(taa_settings.clone());
|
.insert(taa_settings.clone());
|
||||||
taa_settings.reset = false;
|
taa_settings.reset = false;
|
||||||
}
|
}
|
||||||
|
@ -1616,107 +1616,6 @@ mod tests {
|
|||||||
assert_eq!(0, query_min_size![(&A, &B), Or<(Changed<A>, Changed<B>)>]);
|
assert_eq!(0, query_min_size![(&A, &B), Or<(Changed<A>, Changed<B>)>]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn reserve_entities_across_worlds() {
|
|
||||||
let mut world_a = World::default();
|
|
||||||
let mut world_b = World::default();
|
|
||||||
|
|
||||||
let e1 = world_a.spawn(A(1)).id();
|
|
||||||
let e2 = world_a.spawn(A(2)).id();
|
|
||||||
let e3 = world_a.entities().reserve_entity();
|
|
||||||
world_a.flush_entities();
|
|
||||||
|
|
||||||
let world_a_max_entities = world_a.entities().len();
|
|
||||||
world_b.entities.reserve_entities(world_a_max_entities);
|
|
||||||
world_b.entities.flush_as_invalid();
|
|
||||||
|
|
||||||
let e4 = world_b.spawn(A(4)).id();
|
|
||||||
assert_eq!(
|
|
||||||
e4,
|
|
||||||
Entity::from_raw(3),
|
|
||||||
"new entity is created immediately after world_a's max entity"
|
|
||||||
);
|
|
||||||
assert!(world_b.get::<A>(e1).is_none());
|
|
||||||
assert!(world_b.get_entity(e1).is_err());
|
|
||||||
|
|
||||||
assert!(world_b.get::<A>(e2).is_none());
|
|
||||||
assert!(world_b.get_entity(e2).is_err());
|
|
||||||
|
|
||||||
assert!(world_b.get::<A>(e3).is_none());
|
|
||||||
assert!(world_b.get_entity(e3).is_err());
|
|
||||||
|
|
||||||
world_b.get_or_spawn(e1).unwrap().insert(B(1));
|
|
||||||
assert_eq!(
|
|
||||||
world_b.get::<B>(e1),
|
|
||||||
Some(&B(1)),
|
|
||||||
"spawning into 'world_a' entities works"
|
|
||||||
);
|
|
||||||
|
|
||||||
world_b.get_or_spawn(e4).unwrap().insert(B(4));
|
|
||||||
assert_eq!(
|
|
||||||
world_b.get::<B>(e4),
|
|
||||||
Some(&B(4)),
|
|
||||||
"spawning into existing `world_b` entities works"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
world_b.get::<A>(e4),
|
|
||||||
Some(&A(4)),
|
|
||||||
"spawning into existing `world_b` entities works"
|
|
||||||
);
|
|
||||||
|
|
||||||
let e4_mismatched_generation =
|
|
||||||
Entity::from_raw_and_generation(3, NonZero::<u32>::new(2).unwrap());
|
|
||||||
assert!(
|
|
||||||
world_b.get_or_spawn(e4_mismatched_generation).is_none(),
|
|
||||||
"attempting to spawn on top of an entity with a mismatched entity generation fails"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
world_b.get::<B>(e4),
|
|
||||||
Some(&B(4)),
|
|
||||||
"failed mismatched spawn doesn't change existing entity"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
world_b.get::<A>(e4),
|
|
||||||
Some(&A(4)),
|
|
||||||
"failed mismatched spawn doesn't change existing entity"
|
|
||||||
);
|
|
||||||
|
|
||||||
let high_non_existent_entity = Entity::from_raw(6);
|
|
||||||
world_b
|
|
||||||
.get_or_spawn(high_non_existent_entity)
|
|
||||||
.unwrap()
|
|
||||||
.insert(B(10));
|
|
||||||
assert_eq!(
|
|
||||||
world_b.get::<B>(high_non_existent_entity),
|
|
||||||
Some(&B(10)),
|
|
||||||
"inserting into newly allocated high / non-continuous entity id works"
|
|
||||||
);
|
|
||||||
|
|
||||||
let high_non_existent_but_reserved_entity = Entity::from_raw(5);
|
|
||||||
assert!(
|
|
||||||
world_b.get_entity(high_non_existent_but_reserved_entity).is_err(),
|
|
||||||
"entities between high-newly allocated entity and continuous block of existing entities don't exist"
|
|
||||||
);
|
|
||||||
|
|
||||||
let reserved_entities = vec![
|
|
||||||
world_b.entities().reserve_entity(),
|
|
||||||
world_b.entities().reserve_entity(),
|
|
||||||
world_b.entities().reserve_entity(),
|
|
||||||
world_b.entities().reserve_entity(),
|
|
||||||
];
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
reserved_entities,
|
|
||||||
vec![
|
|
||||||
Entity::from_raw(5),
|
|
||||||
Entity::from_raw(4),
|
|
||||||
Entity::from_raw(7),
|
|
||||||
Entity::from_raw(8),
|
|
||||||
],
|
|
||||||
"space between original entities and high entities is used for new entity ids"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_or_spawn_batch() {
|
fn insert_or_spawn_batch() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
@ -325,8 +325,10 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
/// [`Commands::spawn`]. This method should generally only be used for sharing entities across
|
/// [`Commands::spawn`]. This method should generally only be used for sharing entities across
|
||||||
/// apps, and only when they have a scheme worked out to share an ID space (which doesn't happen
|
/// apps, and only when they have a scheme worked out to share an ID space (which doesn't happen
|
||||||
/// by default).
|
/// by default).
|
||||||
|
#[deprecated(since = "0.15.0", note = "use Commands::spawn instead")]
|
||||||
pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands {
|
pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands {
|
||||||
self.queue(move |world: &mut World| {
|
self.queue(move |world: &mut World| {
|
||||||
|
#[allow(deprecated)]
|
||||||
world.get_or_spawn(entity);
|
world.get_or_spawn(entity);
|
||||||
});
|
});
|
||||||
EntityCommands {
|
EntityCommands {
|
||||||
|
@ -944,6 +944,7 @@ impl World {
|
|||||||
/// This method should generally only be used for sharing entities across apps, and only when they have a
|
/// This method should generally only be used for sharing entities across apps, and only when they have a
|
||||||
/// scheme worked out to share an ID space (which doesn't happen by default).
|
/// scheme worked out to share an ID space (which doesn't happen by default).
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[deprecated(since = "0.15.0", note = "use `World::spawn` instead")]
|
||||||
pub fn get_or_spawn(&mut self, entity: Entity) -> Option<EntityWorldMut> {
|
pub fn get_or_spawn(&mut self, entity: Entity) -> Option<EntityWorldMut> {
|
||||||
self.flush();
|
self.flush();
|
||||||
match self.entities.alloc_at_without_replacement(entity) {
|
match self.entities.alloc_at_without_replacement(entity) {
|
||||||
|
@ -554,7 +554,10 @@ pub fn extract_clusters(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
commands.get_or_spawn(entity.id()).insert((
|
commands
|
||||||
|
.get_entity(entity.id())
|
||||||
|
.expect("Clusters entity wasn't synced.")
|
||||||
|
.insert((
|
||||||
ExtractedClusterableObjects { data },
|
ExtractedClusterableObjects { data },
|
||||||
ExtractedClusterConfig {
|
ExtractedClusterConfig {
|
||||||
near: clusters.near,
|
near: clusters.near,
|
||||||
@ -617,7 +620,7 @@ pub fn prepare_clusters(
|
|||||||
|
|
||||||
view_clusters_bindings.write_buffers(render_device, &render_queue);
|
view_clusters_bindings.write_buffers(render_device, &render_queue);
|
||||||
|
|
||||||
commands.get_or_spawn(entity).insert(view_clusters_bindings);
|
commands.entity(entity).insert(view_clusters_bindings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,7 +386,8 @@ fn gather_environment_map_uniform(
|
|||||||
EnvironmentMapUniform::default()
|
EnvironmentMapUniform::default()
|
||||||
};
|
};
|
||||||
commands
|
commands
|
||||||
.get_or_spawn(view_entity.id())
|
.get_entity(view_entity.id())
|
||||||
|
.expect("Environment map light entity wasn't synced.")
|
||||||
.insert(environment_map_uniform);
|
.insert(environment_map_uniform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -440,11 +441,13 @@ fn gather_light_probes<C>(
|
|||||||
// Record the per-view light probes.
|
// Record the per-view light probes.
|
||||||
if render_view_light_probes.is_empty() {
|
if render_view_light_probes.is_empty() {
|
||||||
commands
|
commands
|
||||||
.get_or_spawn(entity)
|
.get_entity(entity)
|
||||||
|
.expect("View entity wasn't synced.")
|
||||||
.remove::<RenderViewLightProbes<C>>();
|
.remove::<RenderViewLightProbes<C>>();
|
||||||
} else {
|
} else {
|
||||||
commands
|
commands
|
||||||
.get_or_spawn(entity)
|
.get_entity(entity)
|
||||||
|
.expect("View entity wasn't synced.")
|
||||||
.insert(render_view_light_probes);
|
.insert(render_view_light_probes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -585,7 +585,9 @@ pub fn extract_camera_previous_view_data(
|
|||||||
for (entity, camera, maybe_previous_view_data) in cameras_3d.iter() {
|
for (entity, camera, maybe_previous_view_data) in cameras_3d.iter() {
|
||||||
if camera.is_active {
|
if camera.is_active {
|
||||||
let entity = entity.id();
|
let entity = entity.id();
|
||||||
let mut entity = commands.get_or_spawn(entity);
|
let mut entity = commands
|
||||||
|
.get_entity(entity)
|
||||||
|
.expect("Camera entity wasn't synced.");
|
||||||
|
|
||||||
if let Some(previous_view_data) = maybe_previous_view_data {
|
if let Some(previous_view_data) = maybe_previous_view_data {
|
||||||
entity.insert(previous_view_data.clone());
|
entity.insert(previous_view_data.clone());
|
||||||
|
@ -403,7 +403,10 @@ pub fn extract_lights(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
commands.get_or_spawn(entity.id()).insert((
|
commands
|
||||||
|
.get_entity(entity.id())
|
||||||
|
.expect("Light entity wasn't synced.")
|
||||||
|
.insert((
|
||||||
ExtractedDirectionalLight {
|
ExtractedDirectionalLight {
|
||||||
color: directional_light.color.into(),
|
color: directional_light.color.into(),
|
||||||
illuminance: directional_light.illuminance,
|
illuminance: directional_light.illuminance,
|
||||||
|
@ -537,7 +537,8 @@ fn extract_ssao_settings(
|
|||||||
}
|
}
|
||||||
if camera.is_active {
|
if camera.is_active {
|
||||||
commands
|
commands
|
||||||
.get_or_spawn(entity.id())
|
.get_entity(entity.id())
|
||||||
|
.expect("SSAO entity wasn't synced.")
|
||||||
.insert(ssao_settings.clone());
|
.insert(ssao_settings.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,18 +280,25 @@ pub fn extract_volumetric_fog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (entity, volumetric_fog) in view_targets.iter() {
|
for (entity, volumetric_fog) in view_targets.iter() {
|
||||||
commands.get_or_spawn(entity.id()).insert(*volumetric_fog);
|
commands
|
||||||
|
.get_entity(entity.id())
|
||||||
|
.expect("Volumetric fog entity wasn't synced.")
|
||||||
|
.insert(*volumetric_fog);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (entity, fog_volume, fog_transform) in fog_volumes.iter() {
|
for (entity, fog_volume, fog_transform) in fog_volumes.iter() {
|
||||||
commands
|
commands
|
||||||
.get_or_spawn(entity.id())
|
.get_entity(entity.id())
|
||||||
|
.expect("Fog volume entity wasn't synced.")
|
||||||
.insert((*fog_volume).clone())
|
.insert((*fog_volume).clone())
|
||||||
.insert(*fog_transform);
|
.insert(*fog_transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (entity, volumetric_light) in volumetric_lights.iter() {
|
for (entity, volumetric_light) in volumetric_lights.iter() {
|
||||||
commands.get_or_spawn(entity.id()).insert(*volumetric_light);
|
commands
|
||||||
|
.get_entity(entity.id())
|
||||||
|
.expect("Volumetric light entity wasn't synced.")
|
||||||
|
.insert(*volumetric_light);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,11 +30,13 @@ use core::ops::{Deref, DerefMut};
|
|||||||
/// ```
|
/// ```
|
||||||
/// use bevy_ecs::prelude::*;
|
/// use bevy_ecs::prelude::*;
|
||||||
/// use bevy_render::Extract;
|
/// use bevy_render::Extract;
|
||||||
|
/// use bevy_render::world_sync::RenderEntity;
|
||||||
/// # #[derive(Component)]
|
/// # #[derive(Component)]
|
||||||
|
/// // Do make sure to sync the cloud entities before extracting them.
|
||||||
/// # struct Cloud;
|
/// # struct Cloud;
|
||||||
/// fn extract_clouds(mut commands: Commands, clouds: Extract<Query<Entity, With<Cloud>>>) {
|
/// fn extract_clouds(mut commands: Commands, clouds: Extract<Query<&RenderEntity, With<Cloud>>>) {
|
||||||
/// for cloud in &clouds {
|
/// for cloud in &clouds {
|
||||||
/// commands.get_or_spawn(cloud).insert(Cloud);
|
/// commands.entity(cloud.id()).insert(Cloud);
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -510,7 +510,9 @@ pub fn extract_default_ui_camera_view(
|
|||||||
TemporaryRenderEntity,
|
TemporaryRenderEntity,
|
||||||
))
|
))
|
||||||
.id();
|
.id();
|
||||||
let mut entity_commands = commands.get_or_spawn(entity);
|
let mut entity_commands = commands
|
||||||
|
.get_entity(entity)
|
||||||
|
.expect("Camera entity wasn't synced.");
|
||||||
entity_commands.insert(DefaultCameraView(default_camera_view));
|
entity_commands.insert(DefaultCameraView(default_camera_view));
|
||||||
if let Some(ui_anti_alias) = ui_anti_alias {
|
if let Some(ui_anti_alias) = ui_anti_alias {
|
||||||
entity_commands.insert(*ui_anti_alias);
|
entity_commands.insert(*ui_anti_alias);
|
||||||
|
@ -428,9 +428,7 @@ fn spawn_tree(
|
|||||||
cmd.id()
|
cmd.id()
|
||||||
};
|
};
|
||||||
|
|
||||||
commands
|
commands.entity(ents[parent_idx]).add_child(child_entity);
|
||||||
.get_or_spawn(ents[parent_idx])
|
|
||||||
.add_child(child_entity);
|
|
||||||
|
|
||||||
ents.push(child_entity);
|
ents.push(child_entity);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user