Skip to content

Commit d1bd46d

Browse files
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 <[email protected]>
1 parent 0374648 commit d1bd46d

File tree

17 files changed

+90
-200
lines changed

17 files changed

+90
-200
lines changed

benches/benches/bevy_ecs/world/commands.rs

-38
Original file line numberDiff line numberDiff line change
@@ -209,41 +209,3 @@ pub fn medium_sized_commands(criterion: &mut Criterion) {
209209
pub fn large_sized_commands(criterion: &mut Criterion) {
210210
sized_commands_impl::<SizedCommand<LargeStruct>>(criterion);
211211
}
212-
213-
pub fn get_or_spawn(criterion: &mut Criterion) {
214-
let mut group = criterion.benchmark_group("get_or_spawn");
215-
group.warm_up_time(core::time::Duration::from_millis(500));
216-
group.measurement_time(core::time::Duration::from_secs(4));
217-
218-
group.bench_function("individual", |bencher| {
219-
let mut world = World::default();
220-
let mut command_queue = CommandQueue::default();
221-
222-
bencher.iter(|| {
223-
let mut commands = Commands::new(&mut command_queue, &world);
224-
for i in 0..10_000 {
225-
commands
226-
.get_or_spawn(Entity::from_raw(i))
227-
.insert((Matrix::default(), Vec3::default()));
228-
}
229-
command_queue.apply(&mut world);
230-
});
231-
});
232-
233-
group.bench_function("batched", |bencher| {
234-
let mut world = World::default();
235-
let mut command_queue = CommandQueue::default();
236-
237-
bencher.iter(|| {
238-
let mut commands = Commands::new(&mut command_queue, &world);
239-
let mut values = Vec::with_capacity(10_000);
240-
for i in 0..10_000 {
241-
values.push((Entity::from_raw(i), (Matrix::default(), Vec3::default())));
242-
}
243-
commands.insert_or_spawn_batch(values);
244-
command_queue.apply(&mut world);
245-
});
246-
});
247-
248-
group.finish();
249-
}

benches/benches/bevy_ecs/world/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ criterion_group!(
2727
zero_sized_commands,
2828
medium_sized_commands,
2929
large_sized_commands,
30-
get_or_spawn,
3130
world_entity,
3231
world_get,
3332
world_query_get,

crates/bevy_core_pipeline/src/core_3d/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,8 @@ pub fn extract_camera_prepass_phase(
590590
live_entities.insert(entity);
591591

592592
commands
593-
.get_or_spawn(entity)
593+
.get_entity(entity)
594+
.expect("Camera entity wasn't synced.")
594595
.insert_if(DepthPrepass, || depth_prepass)
595596
.insert_if(NormalPrepass, || normal_prepass)
596597
.insert_if(MotionVectorPrepass, || motion_vector_prepass)

crates/bevy_core_pipeline/src/dof/mod.rs

+18-14
Original file line numberDiff line numberDiff line change
@@ -830,20 +830,24 @@ fn extract_depth_of_field_settings(
830830
calculate_focal_length(depth_of_field.sensor_height, perspective_projection.fov);
831831

832832
// Convert `DepthOfField` to `DepthOfFieldUniform`.
833-
commands.get_or_spawn(entity).insert((
834-
*depth_of_field,
835-
DepthOfFieldUniform {
836-
focal_distance: depth_of_field.focal_distance,
837-
focal_length,
838-
coc_scale_factor: focal_length * focal_length
839-
/ (depth_of_field.sensor_height * depth_of_field.aperture_f_stops),
840-
max_circle_of_confusion_diameter: depth_of_field.max_circle_of_confusion_diameter,
841-
max_depth: depth_of_field.max_depth,
842-
pad_a: 0,
843-
pad_b: 0,
844-
pad_c: 0,
845-
},
846-
));
833+
commands
834+
.get_entity(entity)
835+
.expect("Depth of field entity wasn't synced.")
836+
.insert((
837+
*depth_of_field,
838+
DepthOfFieldUniform {
839+
focal_distance: depth_of_field.focal_distance,
840+
focal_length,
841+
coc_scale_factor: focal_length * focal_length
842+
/ (depth_of_field.sensor_height * depth_of_field.aperture_f_stops),
843+
max_circle_of_confusion_diameter: depth_of_field
844+
.max_circle_of_confusion_diameter,
845+
max_depth: depth_of_field.max_depth,
846+
pad_a: 0,
847+
pad_b: 0,
848+
pad_c: 0,
849+
},
850+
));
847851
}
848852
}
849853

crates/bevy_core_pipeline/src/taa/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,8 @@ fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut<MainWorld
375375
let has_perspective_projection = matches!(camera_projection, Projection::Perspective(_));
376376
if camera.is_active && has_perspective_projection {
377377
commands
378-
.get_or_spawn(entity.id())
378+
.get_entity(entity.id())
379+
.expect("Camera entity wasn't synced.")
379380
.insert(taa_settings.clone());
380381
taa_settings.reset = false;
381382
}

crates/bevy_ecs/src/lib.rs

-101
Original file line numberDiff line numberDiff line change
@@ -1616,107 +1616,6 @@ mod tests {
16161616
assert_eq!(0, query_min_size![(&A, &B), Or<(Changed<A>, Changed<B>)>]);
16171617
}
16181618

1619-
#[test]
1620-
fn reserve_entities_across_worlds() {
1621-
let mut world_a = World::default();
1622-
let mut world_b = World::default();
1623-
1624-
let e1 = world_a.spawn(A(1)).id();
1625-
let e2 = world_a.spawn(A(2)).id();
1626-
let e3 = world_a.entities().reserve_entity();
1627-
world_a.flush_entities();
1628-
1629-
let world_a_max_entities = world_a.entities().len();
1630-
world_b.entities.reserve_entities(world_a_max_entities);
1631-
world_b.entities.flush_as_invalid();
1632-
1633-
let e4 = world_b.spawn(A(4)).id();
1634-
assert_eq!(
1635-
e4,
1636-
Entity::from_raw(3),
1637-
"new entity is created immediately after world_a's max entity"
1638-
);
1639-
assert!(world_b.get::<A>(e1).is_none());
1640-
assert!(world_b.get_entity(e1).is_err());
1641-
1642-
assert!(world_b.get::<A>(e2).is_none());
1643-
assert!(world_b.get_entity(e2).is_err());
1644-
1645-
assert!(world_b.get::<A>(e3).is_none());
1646-
assert!(world_b.get_entity(e3).is_err());
1647-
1648-
world_b.get_or_spawn(e1).unwrap().insert(B(1));
1649-
assert_eq!(
1650-
world_b.get::<B>(e1),
1651-
Some(&B(1)),
1652-
"spawning into 'world_a' entities works"
1653-
);
1654-
1655-
world_b.get_or_spawn(e4).unwrap().insert(B(4));
1656-
assert_eq!(
1657-
world_b.get::<B>(e4),
1658-
Some(&B(4)),
1659-
"spawning into existing `world_b` entities works"
1660-
);
1661-
assert_eq!(
1662-
world_b.get::<A>(e4),
1663-
Some(&A(4)),
1664-
"spawning into existing `world_b` entities works"
1665-
);
1666-
1667-
let e4_mismatched_generation =
1668-
Entity::from_raw_and_generation(3, NonZero::<u32>::new(2).unwrap());
1669-
assert!(
1670-
world_b.get_or_spawn(e4_mismatched_generation).is_none(),
1671-
"attempting to spawn on top of an entity with a mismatched entity generation fails"
1672-
);
1673-
assert_eq!(
1674-
world_b.get::<B>(e4),
1675-
Some(&B(4)),
1676-
"failed mismatched spawn doesn't change existing entity"
1677-
);
1678-
assert_eq!(
1679-
world_b.get::<A>(e4),
1680-
Some(&A(4)),
1681-
"failed mismatched spawn doesn't change existing entity"
1682-
);
1683-
1684-
let high_non_existent_entity = Entity::from_raw(6);
1685-
world_b
1686-
.get_or_spawn(high_non_existent_entity)
1687-
.unwrap()
1688-
.insert(B(10));
1689-
assert_eq!(
1690-
world_b.get::<B>(high_non_existent_entity),
1691-
Some(&B(10)),
1692-
"inserting into newly allocated high / non-continuous entity id works"
1693-
);
1694-
1695-
let high_non_existent_but_reserved_entity = Entity::from_raw(5);
1696-
assert!(
1697-
world_b.get_entity(high_non_existent_but_reserved_entity).is_err(),
1698-
"entities between high-newly allocated entity and continuous block of existing entities don't exist"
1699-
);
1700-
1701-
let reserved_entities = vec![
1702-
world_b.entities().reserve_entity(),
1703-
world_b.entities().reserve_entity(),
1704-
world_b.entities().reserve_entity(),
1705-
world_b.entities().reserve_entity(),
1706-
];
1707-
1708-
assert_eq!(
1709-
reserved_entities,
1710-
vec![
1711-
Entity::from_raw(5),
1712-
Entity::from_raw(4),
1713-
Entity::from_raw(7),
1714-
Entity::from_raw(8),
1715-
],
1716-
"space between original entities and high entities is used for new entity ids"
1717-
);
1718-
}
1719-
17201619
#[test]
17211620
fn insert_or_spawn_batch() {
17221621
let mut world = World::default();

crates/bevy_ecs/src/system/commands/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,10 @@ impl<'w, 's> Commands<'w, 's> {
325325
/// [`Commands::spawn`]. This method should generally only be used for sharing entities across
326326
/// apps, and only when they have a scheme worked out to share an ID space (which doesn't happen
327327
/// by default).
328+
#[deprecated(since = "0.15.0", note = "use Commands::spawn instead")]
328329
pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands {
329330
self.queue(move |world: &mut World| {
331+
#[allow(deprecated)]
330332
world.get_or_spawn(entity);
331333
});
332334
EntityCommands {

crates/bevy_ecs/src/world/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,7 @@ impl World {
944944
/// This method should generally only be used for sharing entities across apps, and only when they have a
945945
/// scheme worked out to share an ID space (which doesn't happen by default).
946946
#[inline]
947+
#[deprecated(since = "0.15.0", note = "use `World::spawn` instead")]
947948
pub fn get_or_spawn(&mut self, entity: Entity) -> Option<EntityWorldMut> {
948949
self.flush();
949950
match self.entities.alloc_at_without_replacement(entity) {

crates/bevy_pbr/src/cluster/mod.rs

+12-9
Original file line numberDiff line numberDiff line change
@@ -554,14 +554,17 @@ pub fn extract_clusters(
554554
}
555555
}
556556

557-
commands.get_or_spawn(entity.id()).insert((
558-
ExtractedClusterableObjects { data },
559-
ExtractedClusterConfig {
560-
near: clusters.near,
561-
far: clusters.far,
562-
dimensions: clusters.dimensions,
563-
},
564-
));
557+
commands
558+
.get_entity(entity.id())
559+
.expect("Clusters entity wasn't synced.")
560+
.insert((
561+
ExtractedClusterableObjects { data },
562+
ExtractedClusterConfig {
563+
near: clusters.near,
564+
far: clusters.far,
565+
dimensions: clusters.dimensions,
566+
},
567+
));
565568
}
566569
}
567570

@@ -617,7 +620,7 @@ pub fn prepare_clusters(
617620

618621
view_clusters_bindings.write_buffers(render_device, &render_queue);
619622

620-
commands.get_or_spawn(entity).insert(view_clusters_bindings);
623+
commands.entity(entity).insert(view_clusters_bindings);
621624
}
622625
}
623626

crates/bevy_pbr/src/light_probe/mod.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,8 @@ fn gather_environment_map_uniform(
386386
EnvironmentMapUniform::default()
387387
};
388388
commands
389-
.get_or_spawn(view_entity.id())
389+
.get_entity(view_entity.id())
390+
.expect("Environment map light entity wasn't synced.")
390391
.insert(environment_map_uniform);
391392
}
392393
}
@@ -440,11 +441,13 @@ fn gather_light_probes<C>(
440441
// Record the per-view light probes.
441442
if render_view_light_probes.is_empty() {
442443
commands
443-
.get_or_spawn(entity)
444+
.get_entity(entity)
445+
.expect("View entity wasn't synced.")
444446
.remove::<RenderViewLightProbes<C>>();
445447
} else {
446448
commands
447-
.get_or_spawn(entity)
449+
.get_entity(entity)
450+
.expect("View entity wasn't synced.")
448451
.insert(render_view_light_probes);
449452
}
450453
}

crates/bevy_pbr/src/prepass/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,9 @@ pub fn extract_camera_previous_view_data(
585585
for (entity, camera, maybe_previous_view_data) in cameras_3d.iter() {
586586
if camera.is_active {
587587
let entity = entity.id();
588-
let mut entity = commands.get_or_spawn(entity);
588+
let mut entity = commands
589+
.get_entity(entity)
590+
.expect("Camera entity wasn't synced.");
589591

590592
if let Some(previous_view_data) = maybe_previous_view_data {
591593
entity.insert(previous_view_data.clone());

crates/bevy_pbr/src/render/light.rs

+24-21
Original file line numberDiff line numberDiff line change
@@ -403,27 +403,30 @@ pub fn extract_lights(
403403
}
404404
}
405405

406-
commands.get_or_spawn(entity.id()).insert((
407-
ExtractedDirectionalLight {
408-
color: directional_light.color.into(),
409-
illuminance: directional_light.illuminance,
410-
transform: *transform,
411-
volumetric: volumetric_light.is_some(),
412-
soft_shadow_size: directional_light.soft_shadow_size,
413-
shadows_enabled: directional_light.shadows_enabled,
414-
shadow_depth_bias: directional_light.shadow_depth_bias,
415-
// The factor of SQRT_2 is for the worst-case diagonal offset
416-
shadow_normal_bias: directional_light.shadow_normal_bias
417-
* core::f32::consts::SQRT_2,
418-
cascade_shadow_config: cascade_config.clone(),
419-
cascades: extracted_cascades,
420-
frusta: extracted_frusta,
421-
render_layers: maybe_layers.unwrap_or_default().clone(),
422-
},
423-
CascadesVisibleEntities {
424-
entities: cascade_visible_entities,
425-
},
426-
));
406+
commands
407+
.get_entity(entity.id())
408+
.expect("Light entity wasn't synced.")
409+
.insert((
410+
ExtractedDirectionalLight {
411+
color: directional_light.color.into(),
412+
illuminance: directional_light.illuminance,
413+
transform: *transform,
414+
volumetric: volumetric_light.is_some(),
415+
soft_shadow_size: directional_light.soft_shadow_size,
416+
shadows_enabled: directional_light.shadows_enabled,
417+
shadow_depth_bias: directional_light.shadow_depth_bias,
418+
// The factor of SQRT_2 is for the worst-case diagonal offset
419+
shadow_normal_bias: directional_light.shadow_normal_bias
420+
* core::f32::consts::SQRT_2,
421+
cascade_shadow_config: cascade_config.clone(),
422+
cascades: extracted_cascades,
423+
frusta: extracted_frusta,
424+
render_layers: maybe_layers.unwrap_or_default().clone(),
425+
},
426+
CascadesVisibleEntities {
427+
entities: cascade_visible_entities,
428+
},
429+
));
427430
}
428431
}
429432

crates/bevy_pbr/src/ssao/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,8 @@ fn extract_ssao_settings(
537537
}
538538
if camera.is_active {
539539
commands
540-
.get_or_spawn(entity.id())
540+
.get_entity(entity.id())
541+
.expect("SSAO entity wasn't synced.")
541542
.insert(ssao_settings.clone());
542543
}
543544
}

crates/bevy_pbr/src/volumetric_fog/render.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -280,18 +280,25 @@ pub fn extract_volumetric_fog(
280280
}
281281

282282
for (entity, volumetric_fog) in view_targets.iter() {
283-
commands.get_or_spawn(entity.id()).insert(*volumetric_fog);
283+
commands
284+
.get_entity(entity.id())
285+
.expect("Volumetric fog entity wasn't synced.")
286+
.insert(*volumetric_fog);
284287
}
285288

286289
for (entity, fog_volume, fog_transform) in fog_volumes.iter() {
287290
commands
288-
.get_or_spawn(entity.id())
291+
.get_entity(entity.id())
292+
.expect("Fog volume entity wasn't synced.")
289293
.insert((*fog_volume).clone())
290294
.insert(*fog_transform);
291295
}
292296

293297
for (entity, volumetric_light) in volumetric_lights.iter() {
294-
commands.get_or_spawn(entity.id()).insert(*volumetric_light);
298+
commands
299+
.get_entity(entity.id())
300+
.expect("Volumetric light entity wasn't synced.")
301+
.insert(*volumetric_light);
295302
}
296303
}
297304

0 commit comments

Comments
 (0)