From 48f2487d3c45416f5ed3499abcf409090fac36dc Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Mon, 25 Oct 2021 13:43:00 -0700 Subject: [PATCH] Updates in preparation for adding entity aliases to spawnables. The following has been changed: - AssetDataStream can now return the stored streaming deadline and priority. - RootSpawnable now has an event that's called just before root spawnable spawns entities. This is an immediate event unlike the other events that are queued. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../AzCore/AzCore/Asset/AssetDataStream.h | 3 +++ .../Spawnable/RootSpawnableInterface.h | 10 ++++++++ .../Spawnable/SpawnableSystemComponent.cpp | 25 +++++++++++++++---- .../Spawnable/SpawnableSystemComponent.h | 1 + .../PrefabEditorEntityOwnershipService.cpp | 22 ++++++++-------- 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h index 62f5808207..d2b069b55b 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h @@ -70,6 +70,9 @@ namespace AZ::Data const char* GetFilename() const override { return m_filePath.c_str(); } + AZStd::chrono::milliseconds GetStreamingDeadline() const { return m_curDeadline; } + AZ::IO::IStreamerTypes::Priority GetStreamingPriority() const { return m_curPriority; } + // AssetDataStream specific APIs //! Whether or not all data has been loaded. diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h index 72a3031e3e..873123f38b 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h @@ -30,12 +30,22 @@ namespace AzFramework //! Called when the root spawnable has been assigned a new value. This may be called several times without a call to release //! in between. + //! NOTE: The callback is not queued but immediately called from a random thread. This is done because this callback is typically + //! used before entities are spawned and if it's queued then the entities spawn before this callback is called. //! @param rootSpawnable The new root spawnable that was assigned. //! @param generation The generation of the root spawnable. This will increment every time a new spawnable is assigned. virtual void OnRootSpawnableAssigned([[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) {} + //! Called when the root spawnable has completed spawning of entities. This may be called several times without a call to release + //! in between. + //! NOTE: This callback is queued and will be called with a delay and from the main thread. + //! @param rootSpawnable The new root spawnable that was used to spawn entities from. + //! @param generation The generation of the root spawnable. This will increment every time a new spawnable is assigned. + virtual void OnRootSpawnableReady( + [[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) {} //! Called when the root spawnable has Released. This will only be called if there's no root spawnable assigned to take the //! place of the original root spawnable. + //! Note: This callback is queued and will be called with a delay and from the main thread. //! @param generation The generation of the root spawnable that was released. virtual void OnRootSpawnableReleased([[maybe_unused]] uint32_t generation) {} }; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp index 957786c6df..6ca2f3a53a 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp @@ -72,7 +72,7 @@ namespace AzFramework uint64_t SpawnableSystemComponent::AssignRootSpawnable(AZ::Data::Asset rootSpawnable) { - uint64_t generation = 0; + uint32_t generation = 0; if (m_rootSpawnableId == rootSpawnable.GetId()) { @@ -87,16 +87,25 @@ namespace AzFramework // Suspend and resume processing in the container that completion calls aren't received until // everything has been setup to accept callbacks from the call. m_rootSpawnableContainer.Reset(rootSpawnable); - m_rootSpawnableContainer.SpawnAllEntities(); generation = m_rootSpawnableContainer.GetCurrentGeneration(); - AZ_TracePrintf("Spawnables", "Root spawnable set to '%s' at generation %zu.\n", rootSpawnable.GetHint().c_str(), - generation); + + // Don't send out the alert that the root spawnable has been assigned until the spawnable itself is ready. The common + // use case is for handlers to do something with the information in the spawnable before the entities get spawned. + m_rootSpawnableContainer.Alert( + [rootSpawnable](uint32_t generation) + { + RootSpawnableNotificationBus::Broadcast( + &RootSpawnableNotificationBus::Events::OnRootSpawnableAssigned, AZStd::move(rootSpawnable), generation); + }, SpawnableEntitiesContainer::CheckIfSpawnableIsLoaded::Yes); + m_rootSpawnableContainer.SpawnAllEntities(); m_rootSpawnableContainer.Alert( [newSpawnable = AZStd::move(rootSpawnable)](uint32_t generation) { RootSpawnableNotificationBus::QueueBroadcast( - &RootSpawnableNotificationBus::Events::OnRootSpawnableAssigned, newSpawnable, generation); + &RootSpawnableNotificationBus::Events::OnRootSpawnableReady, AZStd::move(newSpawnable), generation); }); + + AZ_TracePrintf("Spawnables", "Root spawnable set to '%s' at generation %zu.\n", rootSpawnable.GetHint().c_str(), generation); } else { @@ -132,6 +141,12 @@ namespace AzFramework AZ_TracePrintf("Spawnables", "New root spawnable '%s' assigned (generation: %i).\n", rootSpawnable.GetHint().c_str(), generation); } + void SpawnableSystemComponent::OnRootSpawnableReady( + [[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) + { + AZ_TracePrintf("Spawnables", "Entities from new root spawnable '%s' are ready (generation: %i).\n", rootSpawnable.GetHint().c_str(), generation); + } + void SpawnableSystemComponent::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation) { AZ_TracePrintf("Spawnables", "Generation %i of the root spawnable has been released.\n", generation); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h index 74e255d624..ecd2a9b728 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h @@ -82,6 +82,7 @@ namespace AzFramework // void OnRootSpawnableAssigned(AZ::Data::Asset rootSpawnable, uint32_t generation) override; + void OnRootSpawnableReady(AZ::Data::Asset rootSpawnable, uint32_t generation) override; void OnRootSpawnableReleased(uint32_t generation) override; protected: diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp index 67b5d99011..97953bac18 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp @@ -581,15 +581,15 @@ namespace AzToolsFramework { if (m_rootInstance && m_playInEditorData.m_isEnabled) { - AZ_Assert(m_playInEditorData.m_entities.IsSet(), + AZ_Assert( + m_playInEditorData.m_entities.IsSet(), "Invalid Game Mode Entities Container encountered after play-in-editor stopped. " "Confirm that the container was initialized correctly"); m_playInEditorData.m_entities.DespawnAllEntities(); m_playInEditorData.m_entities.Alert( [assets = AZStd::move(m_playInEditorData.m_assets), - deactivatedEntities = AZStd::move(m_playInEditorData.m_deactivatedEntities)] - ([[maybe_unused]]uint32_t generation) mutable + deactivatedEntities = AZStd::move(m_playInEditorData.m_deactivatedEntities)]([[maybe_unused]] uint32_t generation) mutable { auto end = deactivatedEntities.rend(); for (auto it = deactivatedEntities.rbegin(); it != end; ++it) @@ -614,15 +614,15 @@ namespace AzToolsFramework AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnGameEntitiesReset); }); m_playInEditorData.m_entities.Clear(); - } - // Game entity cleanup is queued onto the next tick via the DespawnEntities call. - // To avoid both game entities and Editor entities active at the same time - // we flush the tick queue to ensure the game entities are cleared first. - // The Alert callback that follows the DespawnEntities call will then reactivate the editor entities - // This should be considered temporary as a move to a less rigid event sequence that supports async entity clean up - // is the desired direction forward. - AZ::TickBus::ExecuteQueuedEvents(); + // Game entity cleanup is queued onto the next tick via the DespawnEntities call. + // To avoid both game entities and Editor entities active at the same time + // we flush the tick queue to ensure the game entities are cleared first. + // The Alert callback that follows the DespawnEntities call will then reactivate the editor entities + // This should be considered temporary as a move to a less rigid event sequence that supports async entity clean up + // is the desired direction forward. + AZ::TickBus::ExecuteQueuedEvents(); + } m_playInEditorData.m_isEnabled = false; }