Skip to content

Commit 9c27c26

Browse files
authored
Fix QueuedScenes not existing when spawning queued scenes (#23821)
# Objective Fix #23731 ## Solution 1. Slim down QueuedScenes to the minimum by breaking out "waiting scenes" into its own resource 2. Store two copies of QueuedScene (one in the system and one in the resource) and mem::swap them when the system runs ## Testing Added a test, derived from the repro in #23731
1 parent 16dc750 commit 9c27c26

2 files changed

Lines changed: 62 additions & 23 deletions

File tree

crates/bevy_scene/src/lib.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ pub struct ScenePlugin;
542542
impl Plugin for ScenePlugin {
543543
fn build(&self, app: &mut App) {
544544
app.init_resource::<QueuedScenes>()
545+
.init_resource::<WaitingScenes>()
545546
.init_asset::<ScenePatch>()
546547
.init_asset::<SceneListPatch>()
547548
.add_systems(
@@ -564,7 +565,9 @@ mod tests {
564565
use bevy_asset::io::memory::{Dir, MemoryAssetReader};
565566
use bevy_asset::io::{AssetSourceBuilder, AssetSourceId};
566567
use bevy_asset::{Asset, AssetApp, AssetLoader, AssetPlugin, AssetServer, Assets, Handle};
568+
use bevy_ecs::lifecycle::HookContext;
567569
use bevy_ecs::prelude::*;
570+
use bevy_ecs::world::DeferredWorld;
568571
use bevy_reflect::TypePath;
569572
use std::path::Path;
570573
use std::sync::Mutex;
@@ -1342,6 +1345,33 @@ mod tests {
13421345
}
13431346
}
13441347

1348+
#[test]
1349+
fn queue_spawn_scene_during_spawn() {
1350+
#[derive(Component, Default, Clone)]
1351+
#[component(on_insert)]
1352+
struct SpawnOnInsert;
1353+
1354+
impl SpawnOnInsert {
1355+
fn on_insert(mut world: DeferredWorld, _context: HookContext) {
1356+
world.commands().queue_spawn_scene(scene2());
1357+
}
1358+
}
1359+
1360+
fn scene1() -> impl Scene {
1361+
bsn!(SpawnOnInsert)
1362+
}
1363+
1364+
fn scene2() -> impl Scene {
1365+
bsn!(#Name)
1366+
}
1367+
1368+
let mut app = test_app();
1369+
let world = app.world_mut();
1370+
world.queue_spawn_scene(scene1());
1371+
1372+
app.update();
1373+
}
1374+
13451375
#[test]
13461376
fn drop_is_called_for_uninserted_components() {
13471377
#[derive(Component, FromTemplate)]

crates/bevy_scene/src/spawn.rs

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ pub fn resolve_scene_patches(
588588
assets: Res<AssetServer>,
589589
mut patches: ResMut<Assets<ScenePatch>>,
590590
mut list_patches: ResMut<Assets<SceneListPatch>>,
591-
mut queued: ResMut<QueuedScenes>,
591+
mut waiting: ResMut<WaitingScenes>,
592592
) {
593593
for event in events.read() {
594594
match *event {
@@ -604,11 +604,11 @@ pub fn resolve_scene_patches(
604604
}
605605
}
606606
AssetEvent::Removed { id } => {
607-
if let Some(waiting) = queued.waiting_scene_entities.remove(&id)
608-
&& !waiting.is_empty()
607+
if let Some(waiting_entities) = waiting.scene_entities.remove(&id)
608+
&& !waiting_entities.is_empty()
609609
{
610610
error!(
611-
"Failed to spawn entities waiting for scene {id:?} because it was removed: {waiting:?}"
611+
"Failed to spawn entities waiting for scene {id:?} because it was removed: {waiting_entities:?}"
612612
);
613613
}
614614
}
@@ -628,18 +628,19 @@ pub fn resolve_scene_patches(
628628
}
629629
}
630630
AssetEvent::Removed { id } => {
631-
if let Some(waiting) = queued.waiting_scene_list_spawns.remove(&id)
632-
&& waiting > 0
631+
if let Some(waiting_scene_lists) = waiting.scene_list_spawns.remove(&id)
632+
&& waiting_scene_lists > 0
633633
{
634634
error!(
635-
"Failed to spawn scene list {id:?} {waiting} times because it was removed."
635+
"Failed to spawn scene list {id:?} {waiting_scene_lists} times because it was removed."
636636
);
637637
}
638638

639-
if let Some(waiting) = queued.waiting_related_list_entities.remove(&id)
640-
&& !waiting.is_empty()
639+
if let Some(waiting_related) = waiting.related_list_entities.remove(&id)
640+
&& !waiting_related.is_empty()
641641
{
642-
let waiting_entities = waiting.iter().map(|r| r.entity).collect::<Vec<_>>();
642+
let waiting_entities =
643+
waiting_related.iter().map(|r| r.entity).collect::<Vec<_>>();
643644
error!(
644645
"Failed to spawn related entities for scene list {id:?} because it was removed: {waiting_entities:?}"
645646
);
@@ -656,9 +657,14 @@ pub struct QueuedScenes {
656657
new_scene_entities: Vec<Entity>,
657658
related_scene_list_spawns: Vec<(RelatedSceneListSpawn, Handle<SceneListPatch>)>,
658659
scene_list_spawns: Vec<Handle<SceneListPatch>>,
659-
waiting_scene_entities: HashMap<Handle<ScenePatch>, Vec<Entity>>,
660-
waiting_related_list_entities: HashMap<Handle<SceneListPatch>, Vec<RelatedSceneListSpawn>>,
661-
waiting_scene_list_spawns: HashMap<Handle<SceneListPatch>, usize>,
660+
}
661+
662+
/// A [`Resource`] that tracks entities / scenes that are waiting for an asset to load
663+
#[derive(Resource, Default)]
664+
pub struct WaitingScenes {
665+
scene_entities: HashMap<Handle<ScenePatch>, Vec<Entity>>,
666+
related_list_entities: HashMap<Handle<SceneListPatch>, Vec<RelatedSceneListSpawn>>,
667+
scene_list_spawns: HashMap<Handle<SceneListPatch>, usize>,
662668
}
663669

664670
pub(crate) struct RelatedSceneListSpawn {
@@ -678,18 +684,21 @@ pub fn on_add_scene_patch_instance(
678684
pub fn spawn_queued(
679685
world: &mut World,
680686
scene_patch_instances: &mut QueryState<&ScenePatchInstance>,
687+
mut queued: Local<QueuedScenes>,
681688
mut bundle_scratch: Local<BundleScratch>,
682689
mut reader: Local<MessageCursor<AssetEvent<ScenePatch>>>,
683690
mut list_reader: Local<MessageCursor<AssetEvent<SceneListPatch>>>,
684691
) {
692+
core::mem::swap(&mut *world.resource_mut::<QueuedScenes>(), &mut queued);
685693
world.resource_scope(|world, mut list_patches: Mut<Assets<SceneListPatch>>| {
686-
world.resource_scope(|world, mut queued: Mut<QueuedScenes>| {
694+
world.resource_scope(|world, mut waiting: Mut<WaitingScenes>| {
687695
loop {
688696
if queued.is_empty() {
689697
break;
690698
}
691699
queued.spawn_queued(
692700
world,
701+
&mut waiting,
693702
scene_patch_instances,
694703
&mut bundle_scratch,
695704
&list_patches,
@@ -701,7 +710,7 @@ pub fn spawn_queued(
701710
let patches = world.resource::<Assets<ScenePatch>>();
702711
if let AssetEvent::LoadedWithDependencies { id } = event
703712
&& let Some(resolved) = patches.get(*id).and_then(|p| p.resolved.clone())
704-
&& let Some(entities) = queued.waiting_scene_entities.remove(id)
713+
&& let Some(entities) = waiting.scene_entities.remove(id)
705714
{
706715
for entity in entities {
707716
if let Ok(mut entity_mut) = world.get_entity_mut(entity)
@@ -724,7 +733,7 @@ pub fn spawn_queued(
724733
&& let Some(list_patch) = list_patches.get_mut(*id)
725734
{
726735
if let Some(scene_list_spawns) =
727-
queued.waiting_related_list_entities.remove(id)
736+
waiting.related_list_entities.remove(id)
728737
{
729738
for scene_list_spawn in scene_list_spawns {
730739
let result = list_patch.spawn_with(world, |entity| {
@@ -737,8 +746,7 @@ pub fn spawn_queued(
737746
}
738747
}
739748

740-
if let Some(waiting_list_spawns) =
741-
queued.waiting_scene_list_spawns.remove(id)
749+
if let Some(waiting_list_spawns) = waiting.scene_list_spawns.remove(id)
742750
{
743751
for _ in 0..waiting_list_spawns {
744752
let result = list_patch.spawn(world);
@@ -765,6 +773,7 @@ impl QueuedScenes {
765773
fn spawn_queued(
766774
&mut self,
767775
world: &mut World,
776+
waiting_scenes: &mut WaitingScenes,
768777
scene_patch_instances: &mut QueryState<&ScenePatchInstance>,
769778
bundle_scratch: &mut BundleScratch,
770779
list_patches: &Assets<SceneListPatch>,
@@ -787,8 +796,8 @@ impl QueuedScenes {
787796
);
788797
}
789798
} else {
790-
let entities = self
791-
.waiting_scene_entities
799+
let entities = waiting_scenes
800+
.scene_entities
792801
.entry(handle.clone())
793802
.or_default();
794803
entities.push(entity);
@@ -810,8 +819,8 @@ impl QueuedScenes {
810819
);
811820
}
812821
} else {
813-
let entities = self
814-
.waiting_related_list_entities
822+
let entities = waiting_scenes
823+
.related_list_entities
815824
.entry(handle)
816825
.or_default();
817826
entities.push(scene_list_spawn);
@@ -830,7 +839,7 @@ impl QueuedScenes {
830839
);
831840
}
832841
} else {
833-
let count = self.waiting_scene_list_spawns.entry(handle).or_default();
842+
let count = waiting_scenes.scene_list_spawns.entry(handle).or_default();
834843
*count += 1;
835844
}
836845
}

0 commit comments

Comments
 (0)