From 5e4094b2588b2ee56d0681fdca9a9c5f2bd509b1 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Thu, 29 Apr 2021 08:50:48 -0700 Subject: [PATCH] Revamped AzFramework::Scene (#332) Updated AzFramework::Scene to allow it to serve as the one-stop location for localized singletons. Localized singletons in this case are instance that can only occur once in an environment but multiple times within an application. As an example, this allows settings up a single camera per viewport for instance. Highlights of changes: Replaced the original ebuses with interfaces and events for easy of use and performance. Removed the Entity Context specific code and moved that to new locations within the Entity Context itself. Allowed basic inheritance. If a subsystem isn't found in a scene the parent can optionally be searched. Scenes can enter a zombie state and avoid immediately being deleted. This is needed for situations where subsystems can't be destroyed until async calls have been completed. --- ...zFrameworkConfigurationSystemComponent.cpp | 34 ++- .../AzFramework/Entity/EntityContext.cpp | 26 +++ .../AzFramework/Entity/EntityContext.h | 8 +- .../AzFramework/Entity/GameEntityContextBus.h | 2 + .../Entity/GameEntityContextComponent.h | 1 + .../AzFramework/Render/Intersector.cpp | 15 +- .../AzFramework/Render/Intersector.h | 2 + .../AzFramework/AzFramework/Scene/Scene.cpp | 95 +++++++- .../AzFramework/AzFramework/Scene/Scene.h | 133 ++++++----- .../AzFramework/AzFramework/Scene/Scene.inl | 108 +++++++++ .../AzFramework/Scene/SceneSystemBus.h | 114 ---------- .../Scene/SceneSystemComponent.cpp | 167 ++++++-------- .../AzFramework/Scene/SceneSystemComponent.h | 38 ++-- .../AzFramework/Scene/SceneSystemInterface.h | 88 ++++++++ .../AzFramework/azframework_files.cmake | 3 +- .../Entity/EditorEntityContextBus.h | 2 + .../Entity/EditorEntityContextComponent.h | 1 + ...sFrameworkConfigurationSystemComponent.cpp | 41 +++- Code/Framework/Tests/NetBindingMocks.h | 1 + Code/Framework/Tests/Scene.cpp | 208 ++++++++---------- Code/Sandbox/Editor/EditorViewportWidget.cpp | 16 +- Code/Sandbox/Editor/EditorViewportWidget.h | 2 +- .../TrackView/AtomOutputFrameCapture.cpp | 2 +- .../Atom/Bootstrap/BootstrapNotificationBus.h | 16 +- .../Atom/Bootstrap/BootstrapRequestBus.h | 2 +- .../Code/Source/BootstrapSystemComponent.cpp | 29 ++- .../Code/Source/BootstrapSystemComponent.h | 10 +- .../RPI/Code/Include/Atom/RPI.Public/Scene.h | 2 +- .../Atom/RPI/Code/Source/RPI.Public/Scene.cpp | 11 +- .../Viewport/RenderViewportWidget.h | 2 +- .../Source/Viewport/RenderViewportWidget.cpp | 4 +- .../Viewport/MaterialViewportRenderer.cpp | 17 +- .../AtomDebugDisplayViewportInterface.cpp | 7 +- .../AtomLyIntegration/AtomFont/AtomFont.h | 8 +- .../AtomFont/Code/Source/AtomFont.cpp | 24 +- .../DiffuseProbeGridComponentController.cpp | 2 +- .../Source/Mesh/MeshComponentController.cpp | 2 +- .../ReflectionProbeComponentController.cpp | 2 +- .../SkinnedMesh/SkinnedMeshDebugDisplay.cpp | 2 +- .../Rendering/ThumbnailRendererData.h | 2 +- .../ThumbnailRendererSteps/InitializeStep.cpp | 23 +- .../ReleaseResourcesStep.cpp | 14 +- 42 files changed, 757 insertions(+), 529 deletions(-) create mode 100644 Code/Framework/AzFramework/AzFramework/Scene/Scene.inl delete mode 100644 Code/Framework/AzFramework/AzFramework/Scene/SceneSystemBus.h create mode 100644 Code/Framework/AzFramework/AzFramework/Scene/SceneSystemInterface.h diff --git a/Code/Framework/AzFramework/AzFramework/Components/AzFrameworkConfigurationSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Components/AzFrameworkConfigurationSystemComponent.cpp index 7ab61df70e..a221d126bd 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/AzFrameworkConfigurationSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Components/AzFrameworkConfigurationSystemComponent.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include namespace AzFramework @@ -50,36 +50,34 @@ namespace AzFramework void AzFrameworkConfigurationSystemComponent::Activate() { - // Create the defaults scene and associate the GameEntityContext with it. - AZ::Outcome createSceneOutcome = AZ::Failure("SceneSystemRequests bus not responding."); - SceneSystemRequestBus::BroadcastResult(createSceneOutcome, &AzFramework::SceneSystemRequests::CreateScene, "default"); + AZ::Outcome, AZStd::string> createSceneOutcome = + SceneSystemInterface::Get()->CreateScene(Scene::MainSceneName); if (createSceneOutcome) { - Scene* scene = createSceneOutcome.GetValue(); - bool success = false; - EntityContextId gameEntityContextId = EntityContextId::CreateNull(); - GameEntityContextRequestBus::BroadcastResult(gameEntityContextId, &GameEntityContextRequests::GetGameEntityContextId); - - if (!gameEntityContextId.IsNull()) + AZStd::shared_ptr scene = createSceneOutcome.TakeValue(); + EntityContext* gameEntityContext = nullptr; + GameEntityContextRequestBus::BroadcastResult(gameEntityContext, &GameEntityContextRequests::GetGameEntityContextInstance); + if (gameEntityContext != nullptr) { - SceneSystemRequestBus::BroadcastResult(success, &AzFramework::SceneSystemRequests::SetSceneForEntityContextId, gameEntityContextId, scene); + [[maybe_unused]] bool result = scene->SetSubsystem(gameEntityContext); + AZ_Assert(result, "Unable to register main entity context with the main scene."); + } + else + { + AZ_Assert(false, "Unable to retrieve the game entity context instance."); } - AZ_Assert(success, "The application was unable to setup a scene for the game entity context, this should always work"); } else { - AZ_Assert(false, "%s", createSceneOutcome.GetError().data()); + AZ_Assert(false, "Unable to create main scene due to: %s", createSceneOutcome.GetError().c_str()); } } void AzFrameworkConfigurationSystemComponent::Deactivate() { - bool success = false; - SceneSystemRequestBus::BroadcastResult( - success, &AzFramework::SceneSystemRequestBus::Events::RemoveScene, "default"); - - AZ_Assert(success, "\"default\" scene was not removed"); + [[maybe_unused]] bool success = SceneSystemInterface::Get()->RemoveScene(Scene::MainSceneName); + AZ_Assert(success, "Unable to remove the main scene."); } void AzFrameworkConfigurationSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) diff --git a/Code/Framework/AzFramework/AzFramework/Entity/EntityContext.cpp b/Code/Framework/AzFramework/AzFramework/Entity/EntityContext.cpp index ad3173ed56..60c70a25bb 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/EntityContext.cpp +++ b/Code/Framework/AzFramework/AzFramework/Entity/EntityContext.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include "EntityContext.h" @@ -37,6 +39,30 @@ namespace AzFramework } } + AZStd::shared_ptr EntityContext::FindContainingScene(const EntityContextId& contextId) + { + auto sceneSystem = SceneSystemInterface::Get(); + AZ_Assert(sceneSystem, "Attempting to retrieve the scene containing a entity context before the scene system is available."); + + AZStd::shared_ptr result; + sceneSystem->IterateActiveScenes([&result, &contextId](const AZStd::shared_ptr& scene) + { + EntityContext** entityContext = scene->FindSubsystemInScene(); + if (entityContext && (*entityContext)->GetContextId() == contextId) + { + result = scene; + // Result found, returning. + return false; + } + else + { + // No match, continuing to search for containing scene. + return true; + } + }); + return result; + } + //========================================================================= // EntityContext ctor //========================================================================= diff --git a/Code/Framework/AzFramework/AzFramework/Entity/EntityContext.h b/Code/Framework/AzFramework/AzFramework/Entity/EntityContext.h index f59a40f52b..c44429b6de 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/EntityContext.h +++ b/Code/Framework/AzFramework/AzFramework/Entity/EntityContext.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -27,7 +28,7 @@ namespace AZ namespace AzFramework { - class EntityContext; + class Scene; /** * Provides services for a group of entities under the umbrella of a given context. @@ -47,9 +48,11 @@ namespace AzFramework , public EntityOwnershipServiceNotificationBus::Handler { public: - AZ_TYPE_INFO(EntityContext, "{4F98A6B9-C7B5-450E-8A8A-30EEFC411EF5}"); + /// The type used to store entity in AzFramework::Scene. + using SceneStorageType = EntityContext*; + EntityContext(AZ::SerializeContext* serializeContext = nullptr); EntityContext(const EntityContextId& contextId, AZ::SerializeContext* serializeContext = nullptr); EntityContext(const EntityContextId& contextId, AZStd::unique_ptr entityOwnershipService, @@ -75,6 +78,7 @@ namespace AzFramework ////////////////////////////////////////////////////////////////////////// static void Reflect(AZ::ReflectContext* context); + static AZStd::shared_ptr FindContainingScene(const EntityContextId& contextId); protected: diff --git a/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextBus.h b/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextBus.h index ac638127ff..eb3a03ab51 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextBus.h +++ b/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextBus.h @@ -66,6 +66,8 @@ namespace AzFramework */ virtual EntityContextId GetGameEntityContextId() = 0; + virtual EntityContext* GetGameEntityContextInstance() = 0; + /** * Creates an entity in the game context. * @param name A name for the new entity. diff --git a/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.h b/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.h index 3f15027ac7..482b2133ca 100644 --- a/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Entity/GameEntityContextComponent.h @@ -51,6 +51,7 @@ namespace AzFramework ////////////////////////////////////////////////////////////////////////// // GameEntityContextRequestBus AZ::Uuid GetGameEntityContextId() override { return GetContextId(); } + EntityContext* GetGameEntityContextInstance() override { return this; } void ResetGameContext() override; AZ::Entity* CreateGameEntity(const char* name) override; BehaviorEntity CreateGameEntityForBehaviorContext(const char* name) override; diff --git a/Code/Framework/AzFramework/AzFramework/Render/Intersector.cpp b/Code/Framework/AzFramework/AzFramework/Render/Intersector.cpp index 1d55ec9711..23d74f7578 100644 --- a/Code/Framework/AzFramework/AzFramework/Render/Intersector.cpp +++ b/Code/Framework/AzFramework/AzFramework/Render/Intersector.cpp @@ -14,9 +14,9 @@ #include #include +#include #include #include -#include #include #include @@ -30,11 +30,11 @@ namespace AzFramework { IntersectorBus::Handler::BusConnect(m_contextId); IntersectionNotificationBus::Handler::BusConnect(m_contextId); - Scene* scene = nullptr; - SceneSystemRequestBus::BroadcastResult(scene, &AzFramework::SceneSystemRequestBus::Events::GetSceneFromEntityContextId, m_contextId); + + AZStd::shared_ptr scene = EntityContext::FindContainingScene(m_contextId); if (scene) { - scene->SetSubsystem(this); + scene->SetSubsystem(this); } } @@ -42,11 +42,12 @@ namespace AzFramework { IntersectorBus::Handler::BusDisconnect(); IntersectionNotificationBus::Handler::BusDisconnect(); - Scene* scene = nullptr; - SceneSystemRequestBus::BroadcastResult(scene, &AzFramework::SceneSystemRequestBus::Events::GetSceneFromEntityContextId, m_contextId); + + AZStd::shared_ptr scene = EntityContext::FindContainingScene(m_contextId); if (scene) { - scene->UnsetSubsystem(); + [[maybe_unused]] bool result = scene->UnsetSubsystem(this); + AZ_Assert(result, "Failed to unregister Intersector with scene"); } } diff --git a/Code/Framework/AzFramework/AzFramework/Render/Intersector.h b/Code/Framework/AzFramework/AzFramework/Render/Intersector.h index 5852310c21..65ba6b0ea9 100644 --- a/Code/Framework/AzFramework/AzFramework/Render/Intersector.h +++ b/Code/Framework/AzFramework/AzFramework/Render/Intersector.h @@ -33,6 +33,8 @@ namespace AzFramework , protected IntersectionNotificationBus::Handler { public: + AZ_TYPE_INFO(AzFramework::RenderGeometry::Intersector, "{4CCA7971-CD83-4856-ADEA-89CEB41FB197}"); + Intersector(AzFramework::EntityContextId contextId); ~Intersector(); diff --git a/Code/Framework/AzFramework/AzFramework/Scene/Scene.cpp b/Code/Framework/AzFramework/AzFramework/Scene/Scene.cpp index 98905b4574..5bdfb94fa1 100644 --- a/Code/Framework/AzFramework/AzFramework/Scene/Scene.cpp +++ b/Code/Framework/AzFramework/AzFramework/Scene/Scene.cpp @@ -14,13 +14,102 @@ namespace AzFramework { - Scene::Scene(AZStd::string_view name) - : m_name(name) + Scene::Scene(AZStd::string name) + : m_name(AZStd::move(name)) { } - const AZStd::string& Scene::GetName() + Scene::Scene(AZStd::string name, AZStd::shared_ptr parent) + : m_name(AZStd::move(name)) + , m_parent(AZStd::move(parent)) + { + } + + Scene::~Scene() + { + m_removalEvent.Signal(*this, RemovalEventType::Destroyed); + } + + const AZStd::string& Scene::GetName() const { return m_name; } + + const AZStd::shared_ptr& Scene::GetParent() + { + return m_parent; + } + + AZStd::shared_ptr Scene::GetParent() const + { + return m_parent; + } + + bool Scene::IsAlive() const + { + return m_isAlive; + } + + void Scene::ConnectToEvents(RemovalEvent::Handler& handler) + { + handler.Connect(m_removalEvent); + } + + void Scene::ConnectToEvents(SubsystemEvent::Handler& handler) + { + handler.Connect(m_subsystemEvent); + } + + AZStd::any* Scene::FindSubsystem(const AZ::TypeId& typeId) + { + AZStd::any* result = FindSubsystemInScene(typeId); + return (!result && m_parent) ? m_parent->FindSubsystem(typeId) : result; + } + + const AZStd::any* Scene::FindSubsystem(const AZ::TypeId& typeId) const + { + return const_cast(this)->FindSubsystem(typeId); + } + + AZStd::any* Scene::FindSubsystemInScene(const AZ::TypeId& typeId) + { + // Spot check that the internal arrays remain consistent. + AZ_Assert( + m_systemKeys.size() == m_systemObjects.size(), "Key and object list in AzFramework::Scene '%s' have gone out of sync.", + m_name.c_str()); + + const size_t m_systemKeysCount = m_systemKeys.size(); + for (size_t i = 0; i < m_systemKeysCount; ++i) + { + if (m_systemKeys[i] != typeId) + { + continue; + } + else + { + return &m_systemObjects[i]; + } + } + return nullptr; + } + + const AZStd::any* Scene::FindSubsystemInScene(const AZ::TypeId& typeId) const + { + return const_cast(this)->FindSubsystemInScene(typeId); + } + + void Scene::MarkForDestruction() + { + m_isAlive = false; + m_removalEvent.Signal(*this, RemovalEventType::Zombified); + } + + void Scene::RemoveSubsystem(size_t index, const AZ::TypeId& subsystemType) + { + m_systemKeys[index] = m_systemKeys.back(); + m_systemObjects[index] = AZStd::move(m_systemObjects.back()); + m_systemKeys.pop_back(); + m_systemObjects.pop_back(); + m_subsystemEvent.Signal(*this, SubsystemEventType::Removed, subsystemType); + } } diff --git a/Code/Framework/AzFramework/AzFramework/Scene/Scene.h b/Code/Framework/AzFramework/AzFramework/Scene/Scene.h index 6b365dae0f..78fea316eb 100644 --- a/Code/Framework/AzFramework/AzFramework/Scene/Scene.h +++ b/Code/Framework/AzFramework/AzFramework/Scene/Scene.h @@ -11,10 +11,14 @@ */ #pragma once +#include #include #include #include +#include #include +#include +#include namespace AzFramework { @@ -24,71 +28,100 @@ namespace AzFramework AZ_TYPE_INFO(Scene, "{DB449BB3-7A95-434D-BC61-47ACBB1F3436}"); AZ_CLASS_ALLOCATOR(Scene, AZ::SystemAllocator, 0); - explicit Scene(AZStd::string_view name); + friend class ISceneSystem; - const AZStd::string& GetName(); + constexpr static AZStd::string_view MainSceneName = "Main"; + constexpr static AZStd::string_view EditorMainSceneName = "Editor"; + + enum class RemovalEventType + { + Zombified, // The scene has be marked for destruction and is no longer visible in the scene system. + Destroyed, // The scene has been destroyed. + }; + using RemovalEvent = AZ::Event; + enum class SubsystemEventType + { + Added, + Removed + }; + using SubsystemEvent = AZ::Event; + + explicit Scene(AZStd::string name); + Scene(AZStd::string name, AZStd::shared_ptr parent); + ~Scene(); + + [[nodiscard]] const AZStd::string& GetName() const; + + [[nodiscard]] const AZStd::shared_ptr& GetParent(); + [[nodiscard]] AZStd::shared_ptr GetParent() const; + + [[nodiscard]] bool IsAlive() const; + + void ConnectToEvents(RemovalEvent::Handler& handler); + void ConnectToEvents(SubsystemEvent::Handler& handler); // Set the instance of a subsystem associated with this scene. template - bool SetSubsystem(T* system); + bool SetSubsystem(T&& system); // Unset the instance of a subsystem associated with this scene. template bool UnsetSubsystem(); - // Get the instance of a subsystem associated with this scene. + // Unset the instance of the exact system associated with this scene. + // Use this to make sure the expected instance is removed or to make sure type deduction is done in the same way as during setting. + template + bool UnsetSubsystem(const T& system); + + // Get the instance of a subsystem associated with this scene. This call will also look in parent scenes if not found on the target + // scene. Returns a pointer to the subsystem if found, otherwise returns a nullptr. + [[nodiscard]] AZStd::any* FindSubsystem(const AZ::TypeId& typeId); + // Get the instance of a subsystem associated with this scene. This call will also look in parent scenes if not found on the target + // scene. Returns a pointer to the subsystem if found, otherwise returns a nullptr. + [[nodiscard]] const AZStd::any* FindSubsystem(const AZ::TypeId& typeId) const; + // Get the instance of a subsystem associated with this scene. This call will also look in parent scenes if not found on the target + // scene. Returns a pointer to the subsystem if found, otherwise returns a nullptr. template - T* GetSubsystem(); + [[nodiscard]] T* FindSubsystem(); + // Get the instance of a subsystem associated with this scene. This call will also look in parent scenes if not found on the target + // scene. Returns a pointer to the subsystem if found, otherwise returns a nullptr. + template + [[nodiscard]] const T* FindSubsystem() const; + + // Get the instance of a subsystem associated with this scene. This call will only look in the selected scene. Returns a pointer to + // the subsystem if found, otherwise returns a nullptr. + [[nodiscard]] AZStd::any* FindSubsystemInScene(const AZ::TypeId& typeId); + // Get the instance of a subsystem associated with this scene. This call will only look in the selected scene. Returns a pointer to + // the subsystem if found, otherwise returns a nullptr. + [[nodiscard]] const AZStd::any* FindSubsystemInScene(const AZ::TypeId& typeId) const; + // Get the instance of a subsystem associated with this scene. This call will only look in the selected scene. Returns a pointer to + // the subsystem if found, otherwise returns a nullptr. + template + [[nodiscard]] T* FindSubsystemInScene(); + // Get the instance of a subsystem associated with this scene. This call will only look in the selected scene. Returns a pointer to + // the subsystem if found, otherwise returns a nullptr. + template + [[nodiscard]] const T* FindSubsystemInScene() const; private: + void MarkForDestruction(); + void RemoveSubsystem(size_t index, const AZ::TypeId& subsystemType); - AZStd::string m_name; + RemovalEvent m_removalEvent; + SubsystemEvent m_subsystemEvent; // Storing keys separate from data to optimize for fast key search. AZStd::vector m_systemKeys; - AZStd::vector m_systemPointers; + AZStd::vector m_systemObjects; + + // Name that identifies the scene. + AZStd::string m_name; + // Parent to this scene. Any subsystems are inherited from the parent but can be overwritten locally. + AZStd::shared_ptr m_parent; + // If false, the scene has been removed from scene system and can no longer be found. As soon as all handles to the scene are + // released it will be destroyed. + bool m_isAlive{ true }; }; - - template - bool Scene::SetSubsystem(T* system) - { - if (GetSubsystem() != nullptr) - { - return false; - } - m_systemKeys.push_back(T::RTTI_Type()); - m_systemPointers.push_back(system); - return true; - } - - template - bool Scene::UnsetSubsystem() - { - for (size_t i = 0; i < m_systemKeys.size(); ++i) - { - if (m_systemKeys.at(i) == T::RTTI_Type()) - { - m_systemKeys.at(i) = m_systemKeys.back(); - m_systemKeys.pop_back(); - m_systemPointers.at(i) = m_systemPointers.back(); - m_systemPointers.pop_back(); - return true; - } - } - return false; - } - - template - T* Scene::GetSubsystem() - { - for (size_t i = 0; i < m_systemKeys.size(); ++i) - { - if (m_systemKeys.at(i) == T::RTTI_Type()) - { - return reinterpret_cast(m_systemPointers.at(i)); - } - } - return nullptr; - } - } // AzFramework + +#include diff --git a/Code/Framework/AzFramework/AzFramework/Scene/Scene.inl b/Code/Framework/AzFramework/AzFramework/Scene/Scene.inl new file mode 100644 index 0000000000..b0c1a18b08 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Scene/Scene.inl @@ -0,0 +1,108 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +namespace AzFramework +{ + template + bool Scene::SetSubsystem(T&& system) + { + const AZ::TypeId& targetType = azrtti_typeid(); + for (const AZ::TypeId& key : m_systemKeys) + { + if (key == targetType) + { + return false; + } + } + + m_systemKeys.push_back(targetType); + m_systemObjects.emplace_back(AZStd::forward(system)); + m_subsystemEvent.Signal(*this, SubsystemEventType::Added, targetType); + return true; + } + + template + bool Scene::UnsetSubsystem() + { + const AZ::TypeId& targetType = azrtti_typeid(); + const size_t m_systemKeysCount = m_systemKeys.size(); + for (size_t i = 0; i < m_systemKeysCount; ++i) + { + if (m_systemKeys[i] != targetType) + { + continue; + } + else + { + RemoveSubsystem(i, targetType); + return true; + } + } + return false; + } + + template + bool Scene::UnsetSubsystem(const T& system) + { + const AZ::TypeId& targetType = azrtti_typeid(); + const size_t systemKeysCount = m_systemKeys.size(); + for (size_t i = 0; i < systemKeysCount; ++i) + { + if (m_systemKeys[i] != targetType) + { + continue; + } + else + { + [[maybe_unused]] T* instance = AZStd::any_cast(&m_systemObjects[i]); + AZ_Assert( + instance && *instance == system, + "Subsystem being released matched type, but wasn't pointing to the same system that was stored."); + RemoveSubsystem(i, targetType); + return true; + } + } + return false; + } + + template + T* Scene::FindSubsystem() + { + const AZ::TypeId& targetType = azrtti_typeid(); + AZStd::any* subSystem = FindSubsystem(targetType); + return subSystem ? AZStd::any_cast(subSystem) : nullptr; + } + + template + const T* Scene::FindSubsystem() const + { + const AZ::TypeId& targetType = azrtti_typeid(); + const AZStd::any* subSystem = FindSubsystem(targetType); + return subSystem ? AZStd::any_cast(subSystem) : nullptr; + } + + template + T* Scene::FindSubsystemInScene() + { + const AZ::TypeId& targetType = azrtti_typeid(); + AZStd::any* subSystem = FindSubsystemInScene(targetType); + return subSystem ? AZStd::any_cast(subSystem) : nullptr; + } + + template + const T* Scene::FindSubsystemInScene() const + { + const AZ::TypeId& targetType = azrtti_typeid(); + const AZStd::any* subSystem = FindSubsystemInScene(targetType); + return subSystem ? AZStd::any_cast(subSystem) : nullptr; + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemBus.h b/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemBus.h deleted file mode 100644 index c75a8ceb9a..0000000000 --- a/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemBus.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ -#pragma once - -#include -#include - -namespace AzFramework -{ - // Forward declarations - class Scene; - - //! Interface used to create, get, or destroy scenes. - class SceneSystemRequests - : public AZ::EBusTraits - { - public: - - virtual ~SceneSystemRequests() = default; - - //! Single handler policy since there should only be one instance of this system component. - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - - //! Creates a scene with a given name. - //! - If there is already a scene with the provided name this will return AZ::Failure(). - //! - If isDefault is set to true and there is already a default scene, the default scene will be switched to this one. - virtual AZ::Outcome CreateScene(AZStd::string_view name) = 0; - - //! Gets a scene with a given name - //! - If a scene does not exist with the given name, nullptr is returned. - virtual Scene* GetScene(AZStd::string_view name) = 0; - - //! Gets all the scenes that currently exist. - virtual AZStd::vector GetAllScenes() = 0; - - //! Remove a scene with a given name and return if the operation was successful. - //! - If the removed scene is the default scene, there will no longer be a default scene. - virtual bool RemoveScene(AZStd::string_view name) = 0; - - //! Add a mapping from the provided EntityContextId to a Scene - //! - If a scene is already associated with this EntityContextId, nothing is changed and false is returned. - virtual bool SetSceneForEntityContextId(EntityContextId entityContextId, Scene* scene) = 0; - - //! Remove a mapping from the provided EntityContextId to a Scene - //! - If no scene is found from the provided EntityContextId, false is returned. - virtual bool RemoveSceneForEntityContextId(EntityContextId entityContextId, Scene* scene) = 0; - - //! Get the scene associated with an EntityContextId - //! - If no scene is found for the provided EntityContextId, nullptr is returned. - virtual Scene* GetSceneFromEntityContextId(EntityContextId entityContextId) = 0; - }; - - using SceneSystemRequestBus = AZ::EBus; - - //! Interface used for notifications from the scene system - class SceneSystemNotifications - : public AZ::EBusTraits - { - public: - - virtual ~SceneSystemNotifications() = default; - - //! There can be multiple listeners to changes in the scene system. - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; - - //! Called when a scene has been created. - virtual void SceneCreated(Scene& /*scene*/) {}; - - //! Called just before a scene is removed. - virtual void SceneAboutToBeRemoved(Scene& /*scene*/) {}; - - }; - - using SceneSystemNotificationBus = AZ::EBus; - - //! Interface used for notifications about individual scenes - class SceneNotifications - : public AZ::EBusTraits - { - public: - - virtual ~SceneNotifications() = default; - - //! There can be multiple listeners to changes in the scene system. - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; - - //! Bus is listened to using the pointer of the scene - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; - - //! Specifies that events are addressed by the pointer to the scene - using BusIdType = Scene*; - - //! Called just before a scene is removed. - virtual void SceneAboutToBeRemoved() {}; - - //! Called when an entity context is mapped to this scene. - virtual void EntityContextMapped(EntityContextId /*entityContextId*/) {}; - - //! Called when an entity context is unmapped from this scene. - virtual void EntityContextUnmapped(EntityContextId /*entityContextId*/) {}; - }; - - using SceneNotificationBus = AZ::EBus; - -} // AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemComponent.cpp index 909880ff55..e677be4643 100644 --- a/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemComponent.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include namespace AzFramework { @@ -41,14 +41,10 @@ namespace AzFramework void SceneSystemComponent::Activate() { - // Connect busses - SceneSystemRequestBus::Handler::BusConnect(); } void SceneSystemComponent::Deactivate() { - // Disconnect Busses - SceneSystemRequestBus::Handler::BusDisconnect(); } void SceneSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) @@ -61,140 +57,105 @@ namespace AzFramework incompatible.push_back(AZ_CRC("SceneSystemComponentService", 0xd8975435)); } - AZ::Outcome SceneSystemComponent::CreateScene(AZStd::string_view name) + AZ::Outcome, AZStd::string> SceneSystemComponent::CreateScene(AZStd::string_view name) { - Scene* existingScene = GetScene(name); + return CreateSceneWithParent(name, nullptr); + } + AZ::Outcome, AZStd::string> SceneSystemComponent::CreateSceneWithParent( + AZStd::string_view name, AZStd::shared_ptr parent) + { + const AZStd::shared_ptr& existingScene = GetScene(name); if (existingScene) { return AZ::Failure("A scene already exists with this name."); } - auto newScene = AZStd::make_unique(name); - Scene* scenePointer = newScene.get(); - m_scenes.push_back(AZStd::move(newScene)); - SceneSystemNotificationBus::Broadcast(&SceneSystemNotificationBus::Events::SceneCreated, *scenePointer); - return AZ::Success(scenePointer); + auto newScene = AZStd::make_shared(name, AZStd::move(parent)); + m_activeScenes.push_back(newScene); + { + AZStd::lock_guard lock(m_eventMutex); + m_events.Signal(EventType::SceneCreated, newScene); + } + return AZ::Success(AZStd::move(newScene)); } - Scene* SceneSystemComponent::GetScene(AZStd::string_view name) + AZStd::shared_ptr SceneSystemComponent::GetScene(AZStd::string_view name) { - auto sceneIterator = AZStd::find_if(m_scenes.begin(), m_scenes.end(), + auto sceneIterator = AZStd::find_if(m_activeScenes.begin(), m_activeScenes.end(), [name](auto& scene) -> bool { return scene->GetName() == name; } ); - return sceneIterator == m_scenes.end() ? nullptr : sceneIterator->get(); + return sceneIterator == m_activeScenes.end() ? nullptr : *sceneIterator; } - AZStd::vector SceneSystemComponent::GetAllScenes() + void SceneSystemComponent::IterateActiveScenes(const ActiveIterationCallback& callback) { - AZStd::vector scenes; - scenes.resize_no_construct(m_scenes.size()); - - for (size_t i = 0; i < m_scenes.size(); ++i) + bool keepGoing = true; + auto end = m_activeScenes.end(); + for (auto it = m_activeScenes.begin(); it != end && keepGoing; ++it) { - scenes.at(i) = m_scenes.at(i).get(); + keepGoing = callback(*it); + } + } + + void SceneSystemComponent::IterateZombieScenes(const ZombieIterationCallback& callback) + { + bool keepGoing = true; + auto end = m_zombieScenes.end(); + for (auto it = m_zombieScenes.begin(); it != end && keepGoing;) + { + if (!it->expired()) + { + keepGoing = callback(*(it->lock())); + ++it; + } + else + { + *it = m_zombieScenes.back(); + m_zombieScenes.pop_back(); + end = m_zombieScenes.end(); + } } - return scenes; } bool SceneSystemComponent::RemoveScene(AZStd::string_view name) { - for (size_t i = 0; i < m_scenes.size(); ++i) + for (AZStd::shared_ptr& scene : m_activeScenes) { - auto& scenePtr = m_scenes.at(i); - if (scenePtr->GetName() == name) + if (scene->GetName() == name) { - // Remove any entityContext mappings. - Scene* scene = scenePtr.get(); - for (auto entityContextScenePairIt = m_entityContextToScenes.begin(); entityContextScenePairIt != m_entityContextToScenes.end();) + MarkSceneForDestruction(*scene); { - AZStd::pair& pair = *entityContextScenePairIt; - if (pair.second == scene) - { - // swap and pop back. - *entityContextScenePairIt = m_entityContextToScenes.back(); - m_entityContextToScenes.pop_back(); - } - else - { - ++entityContextScenePairIt; - } + AZStd::lock_guard lock(m_eventMutex); + m_events.Signal(EventType::ScenePendingRemoval, scene); } - SceneSystemNotificationBus::Broadcast(&SceneSystemNotificationBus::Events::SceneAboutToBeRemoved, *scene); - SceneNotificationBus::Event(scene, &SceneNotificationBus::Events::SceneAboutToBeRemoved); - - m_scenes.erase(&scenePtr); + // Zombies are weak pointers that are kept around for situations where there's a delay in deleting the scene. This can happen + // if there are outstanding calls like in-progress async calls or resources locked by hardware. A weak_ptr of the original + // scene is kept so the zombie scene can still be found through iteration as it may require additional calls such as Tick calls. + m_zombieScenes.push_back(scene); + scene = AZStd::move(m_activeScenes.back()); + m_activeScenes.pop_back(); + // The scene may not be held onto anymore, so check here to see if the previously added zombie can be released. + if (m_zombieScenes.back().expired()) + { + m_zombieScenes.pop_back(); + } return true; } } - AZ_Warning("SceneSystemComponent", false, "Attempting to remove scene name \"%.*s\", but that scene was not found.", static_cast(name.size()), name.data()); + AZ_Warning("SceneSystemComponent", false, R"(Attempting to remove scene name "%.*s", but that scene was not found.)", AZ_STRING_ARG(name)); return false; } - bool SceneSystemComponent::SetSceneForEntityContextId(EntityContextId entityContextId, Scene* scene) + void SceneSystemComponent::ConnectToEvents(SceneEvent::Handler& handler) { - Scene* existingSceneForEntityContext = GetSceneFromEntityContextId(entityContextId); - if (existingSceneForEntityContext) - { - // This entity context is already mapped and must be unmapped explictely before it can be changed. - char entityContextIdString[EntityContextId::MaxStringBuffer]; - entityContextId.ToString(entityContextIdString, sizeof(entityContextIdString)); - AZ_Warning("SceneSystemComponent", false, "Failed to set a scene for entity context %s, scene is already set for that entity context.", entityContextIdString); - - return false; - } - m_entityContextToScenes.emplace_back(entityContextId, scene); - SceneNotificationBus::Event(scene, &SceneNotificationBus::Events::EntityContextMapped, entityContextId); - return true; + AZStd::lock_guard lock(m_eventMutex); + handler.Connect(m_events); } - - bool SceneSystemComponent::RemoveSceneForEntityContextId(EntityContextId entityContextId, Scene* scene) - { - if (!scene || entityContextId.IsNull()) - { - return false; - } - - for (auto entityContextScenePairIt = m_entityContextToScenes.begin(); entityContextScenePairIt != m_entityContextToScenes.end();) - { - AZStd::pair& pair = *entityContextScenePairIt; - if (!(pair.first == entityContextId && pair.second == scene)) - { - ++entityContextScenePairIt; - } - else - { - // swap and pop back. - *entityContextScenePairIt = m_entityContextToScenes.back(); - m_entityContextToScenes.pop_back(); - - SceneNotificationBus::Event(scene, &SceneNotificationBus::Events::EntityContextUnmapped, entityContextId); - return true; - } - } - - char entityContextIdString[EntityContextId::MaxStringBuffer]; - entityContextId.ToString(entityContextIdString, sizeof(entityContextIdString)); - AZ_Warning("SceneSystemComponent", false, "Failed to remove scene \"%.*s\" for entity context %s, entity context is not currently mapped to that scene.", static_cast(scene->GetName().size()), scene->GetName().data(), entityContextIdString); - return false; - } - - Scene* SceneSystemComponent::GetSceneFromEntityContextId(EntityContextId entityContextId) - { - for (AZStd::pair& pair : m_entityContextToScenes) - { - if (pair.first == entityContextId) - { - return pair.second; - } - } - return nullptr; - } - -} // AzFramework +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemComponent.h b/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemComponent.h index 085efcd898..a5497bdb87 100644 --- a/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemComponent.h @@ -13,18 +13,18 @@ #include #include -#include +#include +#include #include namespace AzFramework { class SceneSystemComponent : public AZ::Component - , public SceneSystemRequestBus::Handler + , public SceneSystemInterface::Registrar { public: - - AZ_COMPONENT(SceneSystemComponent, "{7AC53AF0-BE1A-437C-BE3E-4D6A998DA945}", AZ::Component); + AZ_COMPONENT(SceneSystemComponent, "{7AC53AF0-BE1A-437C-BE3E-4D6A998DA945}", AZ::Component, ISceneSystem); SceneSystemComponent(); ~SceneSystemComponent() override; @@ -41,24 +41,26 @@ namespace AzFramework static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); ////////////////////////////////////////////////////////////////////////// - // SceneSystemRequestsBus::Handler + // SceneSystemInterface overrides ////////////////////////////////////////////////////////////////////////// - AZ::Outcome CreateScene(AZStd::string_view name) override; - Scene* GetScene(AZStd::string_view name) override; - AZStd::vector GetAllScenes() override; + AZ::Outcome, AZStd::string> CreateScene(AZStd::string_view name) override; + AZ::Outcome, AZStd::string> CreateSceneWithParent( + AZStd::string_view name, AZStd::shared_ptr parent) override; + [[nodiscard]] AZStd::shared_ptr GetScene(AZStd::string_view name) override; + void IterateActiveScenes(const ActiveIterationCallback& callback) override; + void IterateZombieScenes(const ZombieIterationCallback& callback) override; bool RemoveScene(AZStd::string_view name) override; - bool SetSceneForEntityContextId(EntityContextId entityContextId, Scene* scene) override; - bool RemoveSceneForEntityContextId(EntityContextId entityContextId, Scene* scene) override; - Scene* GetSceneFromEntityContextId(EntityContextId entityContextId) override; + void ConnectToEvents(SceneEvent::Handler& handler) override; private: + AZ_DISABLE_COPY_MOVE(SceneSystemComponent); - AZ_DISABLE_COPY(SceneSystemComponent); - - // Container of scene in order of creation - AZStd::vector> m_scenes; - - // Map of entity context Ids to scenes. Using a vector because lookups will be common, but the size will be small. - AZStd::vector> m_entityContextToScenes; + AZStd::vector> m_activeScenes; + AZStd::vector> m_zombieScenes; + // Using a mutex around the events as other threads may respond to a new/deleted scene by making + // local updates and unregistering themselves. Since Scene is single threaded, no updates (other + // then unregistering an event) should be done from other threads though. + AZStd::recursive_mutex m_eventMutex; + SceneEvent m_events; }; } diff --git a/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemInterface.h b/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemInterface.h new file mode 100644 index 0000000000..48dca7ed29 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Scene/SceneSystemInterface.h @@ -0,0 +1,88 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace AzFramework +{ + //! Interface used to create, get, or destroy scenes. + //! This interface is single thread and is intended to be called from a single thread, commonly the main thread. The exception + //! is connecting events, which is thread safe. + class ISceneSystem + { + public: + AZ_RTTI(AzFramework::ISceneSystem, "{DAE482A8-88AE-4BD3-8A5B-52D19A96E15F}"); + AZ_DISABLE_COPY_MOVE(ISceneSystem); + + enum class EventType + { + SceneCreated, + ScenePendingRemoval + }; + using SceneEvent = AZ::Event&>; + + ISceneSystem() = default; + virtual ~ISceneSystem() = default; + + using ActiveIterationCallback = AZStd::function& scene)>; + using ZombieIterationCallback = AZStd::function; + + //! Creates a scene with a given name. + //! - If there is already a scene with the provided name this will return AZ::Failure(). + virtual AZ::Outcome, AZStd::string> CreateScene(AZStd::string_view name) = 0; + + //! Creates a scene with a given name and a parent. + //! - If there is already a scene with the provided name this will return AZ::Failure(). + virtual AZ::Outcome, AZStd::string> CreateSceneWithParent( + AZStd::string_view name, AZStd::shared_ptr parent) = 0; + + //! Gets a scene with a given name + //! - If a scene does not exist with the given name, nullptr is returned. + [[nodiscard]] virtual AZStd::shared_ptr GetScene(AZStd::string_view name) = 0; + + //! Iterates over all scenes that are in active use. Iteration stops if the callback returns false or all scenes have been listed. + virtual void IterateActiveScenes(const ActiveIterationCallback& callback) = 0; + //! Iterates over all zombie scenes. Zombie scenes are scenes that have been removed but still have references held on to. This can + //! happen because scenes hold on to subsystems that can't immediately be deleted. These subsystems may still require being called + //! such as a periodic tick. Iteration stops if the callback returns false or all scenes have been listed. + virtual void IterateZombieScenes(const ZombieIterationCallback& callback) = 0; + + //! Remove a scene with a given name and return if the operation was successful. + virtual bool RemoveScene(AZStd::string_view name) = 0; + + //! Connects the provided handler to the events that are called after scenes are created or before they get removed. + virtual void ConnectToEvents(SceneEvent::Handler& handler) = 0; + + protected: + // Strictly a forwarding function to call private functions on the scene. + void MarkSceneForDestruction(Scene& scene) { scene.MarkForDestruction(); } + }; + + using SceneSystemInterface = AZ::Interface; + + // EBus wrapper for ScriptCanvas + class ISceneSystemRequests + : public AZ::EBusTraits + { + public: + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + }; + using ILoggerRequestBus = AZ::EBus; +} // AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake index 312a3766ac..b126a192ac 100644 --- a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake +++ b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake @@ -194,10 +194,11 @@ set(FILES Logging/MissingAssetLogger.h Logging/MissingAssetNotificationBus.h Scene/Scene.h + Scene/Scene.inl Scene/Scene.cpp - Scene/SceneSystemBus.h Scene/SceneSystemComponent.h Scene/SceneSystemComponent.cpp + Scene/SceneSystemInterface.h Script/ScriptComponent.h Script/ScriptComponent.cpp Script/ScriptDebugAgentBus.h diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextBus.h index cee47f55ef..7d08d87c3b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextBus.h @@ -49,6 +49,8 @@ namespace AzToolsFramework /// Retrieve the Id of the editor entity context. virtual AzFramework::EntityContextId GetEditorEntityContextId() = 0; + virtual AzFramework::EntityContext* GetEditorEntityContextInstance() = 0; + /// Creates an entity in the editor context. /// \return the EntityId for the created Entity virtual AZ::EntityId CreateNewEditorEntity(const char* name) = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.h index 9a9a2dcff0..2313f27437 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityContextComponent.h @@ -77,6 +77,7 @@ namespace AzToolsFramework ////////////////////////////////////////////////////////////////////////// // EditorEntityContextRequestBus AzFramework::EntityContextId GetEditorEntityContextId() override { return GetContextId(); } + AzFramework::EntityContext* GetEditorEntityContextInstance() override { return this; } void ResetEditorContext() override; AZ::EntityId CreateNewEditorEntity(const char* name) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/AzToolsFrameworkConfigurationSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/AzToolsFrameworkConfigurationSystemComponent.cpp index 9fda3fa291..f3c6a014cd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/AzToolsFrameworkConfigurationSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/AzToolsFrameworkConfigurationSystemComponent.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include @@ -56,22 +56,47 @@ namespace AzToolsFramework void AzToolsFrameworkConfigurationSystemComponent::Activate() { - // Associate the EditorEntityContext with the default scene. + // Create the editor specific child scene to the main scene and add the editor entity context to it. AzFramework::EntityContextId editorEntityContextId; EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); - AzFramework::Scene* defaultScene = nullptr; - AzFramework::SceneSystemRequestBus::BroadcastResult(defaultScene, &AzFramework::SceneSystemRequests::GetScene, "default"); - - if (!editorEntityContextId.IsNull() && defaultScene) + if (!editorEntityContextId.IsNull()) { - bool success = false; - AzFramework::SceneSystemRequestBus::BroadcastResult(success, &AzFramework::SceneSystemRequests::SetSceneForEntityContextId, editorEntityContextId, defaultScene); + auto sceneSystem = AzFramework::SceneSystemInterface::Get(); + AZ_Assert(sceneSystem, "Scene system not available to create the editor scene."); + AZStd::shared_ptr mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName); + if (mainScene) + { + AZ::Outcome, AZStd::string> editorScene = + sceneSystem->CreateSceneWithParent(AzFramework::Scene::EditorMainSceneName, mainScene); + if (editorScene.IsSuccess()) + { + AzFramework::EntityContext* editorEntityContext = nullptr; + EditorEntityContextRequestBus::BroadcastResult( + editorEntityContext, &EditorEntityContextRequests::GetEditorEntityContextInstance); + if (editorEntityContext != nullptr) + { + [[maybe_unused]] bool contextAdded = + editorScene.GetValue()->SetSubsystem(editorEntityContext); + AZ_Assert(contextAdded, "Unable to add editor entity context to scene."); + } + } + else + { + AZ_Assert(false, "Failed to create editor scene because: %s", editorScene.GetError().c_str()); + } + } + else + { + AZ_Assert(false, "No main scene to parent the editor scene under."); + } } } void AzToolsFrameworkConfigurationSystemComponent::Deactivate() { + [[maybe_unused]] bool success = AzFramework::SceneSystemInterface::Get()->RemoveScene(AzFramework::Scene::EditorMainSceneName); + AZ_Assert(success, "Unable to remove the main editor scene."); } void AzToolsFrameworkConfigurationSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) diff --git a/Code/Framework/Tests/NetBindingMocks.h b/Code/Framework/Tests/NetBindingMocks.h index 3f280383f5..a07cae0077 100644 --- a/Code/Framework/Tests/NetBindingMocks.h +++ b/Code/Framework/Tests/NetBindingMocks.h @@ -42,6 +42,7 @@ namespace UnitTest MOCK_METHOD3(InstantiateDynamicSlice, AzFramework::SliceInstantiationTicket(const AZ::Data::Asset&, const AZ::Transform&, const AZ::IdUtils::Remapper::IdMapper&)); MOCK_METHOD0(GetGameEntityContextId, AzFramework::EntityContextId()); + MOCK_METHOD0(GetGameEntityContextInstance, AzFramework::EntityContext*()); MOCK_METHOD1(CreateGameEntity, AZ::Entity*(const char*)); MOCK_METHOD1(AddGameEntity, void (AZ::Entity*)); MOCK_METHOD1(DestroyGameEntity, void (const AZ::EntityId&)); diff --git a/Code/Framework/Tests/Scene.cpp b/Code/Framework/Tests/Scene.cpp index a31dcc4c6e..a9affe3a01 100644 --- a/Code/Framework/Tests/Scene.cpp +++ b/Code/Framework/Tests/Scene.cpp @@ -130,6 +130,8 @@ namespace SceneUnitTest m_systemEntity->CreateComponent(); m_systemEntity->CreateComponent(); m_systemEntity->Activate(); + + m_sceneSystem = AzFramework::SceneSystemInterface::Get(); } void TearDown() override @@ -146,167 +148,119 @@ namespace SceneUnitTest AZ::IO::FileIOBase* m_prevFileIO; AZ::ComponentApplication m_app; AZ::Entity* m_systemEntity = nullptr; + AzFramework::ISceneSystem* m_sceneSystem = nullptr; }; TEST_F(SceneTest, CreateScene) - { - Scene* scene = nullptr; - AZ::Outcome createSceneOutcome = AZ::Failure(""); - + { // A scene should be able to be created with a given name. - AzFramework::SceneSystemRequestBus::BroadcastResult(createSceneOutcome, &AzFramework::SceneSystemRequestBus::Events::CreateScene, "TestScene"); + AZ::Outcome, AZStd::string> createSceneOutcome = m_sceneSystem->CreateScene("TestScene"); EXPECT_TRUE(createSceneOutcome.IsSuccess()) << "Unable to create a scene."; // The scene pointer returned should be valid - scene = createSceneOutcome.GetValue(); - EXPECT_TRUE(scene != nullptr) << "Scene creation reported success, but no scene actually was actually returned."; + AZStd::shared_ptr scene = createSceneOutcome.TakeValue(); + EXPECT_NE(scene, nullptr) << "Scene creation reported success, but no scene actually was actually returned."; // Attempting to create another scene with the same name should fail. - createSceneOutcome = AZ::Failure(""); - AzFramework::SceneSystemRequestBus::BroadcastResult(createSceneOutcome, &AzFramework::SceneSystemRequestBus::Events::CreateScene, "TestScene"); + createSceneOutcome = m_sceneSystem->CreateScene("TestScene"); EXPECT_TRUE(!createSceneOutcome.IsSuccess()) << "Should not be able to create two scenes with the same name."; } TEST_F(SceneTest, GetScene) { - Scene* createdScene = nullptr; - Scene* retrievedScene = nullptr; - Scene* nullScene = nullptr; - const static AZStd::string_view s_sceneName = "TestScene"; + constexpr AZStd::string_view sceneName = "TestScene"; - AZ::Outcome createSceneOutcome = AZ::Failure(""); - AzFramework::SceneSystemRequestBus::BroadcastResult(createSceneOutcome, &AzFramework::SceneSystemRequestBus::Events::CreateScene, s_sceneName); - createdScene = createSceneOutcome.GetValue(); + AZ::Outcome, AZStd::string> createSceneOutcome = m_sceneSystem->CreateScene(sceneName); + AZStd::shared_ptr createdScene = createSceneOutcome.TakeValue(); // Should be able to get a scene by name, and it should match the scene that was created. - AzFramework::SceneSystemRequestBus::BroadcastResult(retrievedScene, &AzFramework::SceneSystemRequestBus::Events::GetScene, s_sceneName); - EXPECT_TRUE(retrievedScene != nullptr) << "Attempting to get scene by name resulted in nullptr."; - EXPECT_TRUE(retrievedScene == createdScene) << "Retrieved scene does not match created scene."; + AZStd::shared_ptr retrievedScene = m_sceneSystem->GetScene(sceneName); + EXPECT_NE(retrievedScene, nullptr) << "Attempting to get scene by name resulted in nullptr."; + EXPECT_EQ(retrievedScene, createdScene) << "Retrieved scene does not match created scene."; // An invalid name should return a null scene. - AzFramework::SceneSystemRequestBus::BroadcastResult(nullScene, &AzFramework::SceneSystemRequestBus::Events::GetScene, "non-existant scene"); - EXPECT_TRUE(nullScene == nullptr) << "Should not be able to retrieve a scene that wasn't created."; + AZStd::shared_ptr nullScene = m_sceneSystem->GetScene("non-existant scene"); + EXPECT_EQ(nullScene, nullptr) << "Should not be able to retrieve a scene that wasn't created."; } TEST_F(SceneTest, RemoveScene) { - Scene* createdScene = nullptr; - const static AZStd::string_view s_sceneName = "TestScene"; + constexpr AZStd::string_view sceneName = "TestScene"; - AZ::Outcome createSceneOutcome = AZ::Failure(""); - AzFramework::SceneSystemRequestBus::BroadcastResult(createSceneOutcome, &AzFramework::SceneSystemRequestBus::Events::CreateScene, s_sceneName); - createdScene = createSceneOutcome.GetValue(); - - bool success = false; - AzFramework::SceneSystemRequestBus::BroadcastResult(success, &AzFramework::SceneSystemRequestBus::Events::RemoveScene, s_sceneName); + AZ::Outcome, AZStd::string> createSceneOutcome = m_sceneSystem->CreateScene(sceneName); + bool success = m_sceneSystem->RemoveScene(sceneName); EXPECT_TRUE(success) << "Failed to remove the scene that was just created."; - success = true; - AzFramework::SceneSystemRequestBus::BroadcastResult(success, &AzFramework::SceneSystemRequestBus::Events::RemoveScene, "non-existant scene"); + success = m_sceneSystem->RemoveScene("non-existant scene"); EXPECT_FALSE(success) << "Remove scene returned success for a non-existant scene."; } - TEST_F(SceneTest, GetAllScenes) + TEST_F(SceneTest, IterateActiveScenes) { constexpr size_t NumScenes = 5; - Scene* scenes[NumScenes] = { nullptr }; + AZStd::shared_ptr scenes[NumScenes] = {nullptr}; for (size_t i = 0; i < NumScenes; ++i) { - AZ::Outcome createSceneOutcome = AZ::Failure(""); - AZStd::string sceneName = AZStd::string::format("scene %zu", i); - AzFramework::SceneSystemRequestBus::BroadcastResult(createSceneOutcome, &AzFramework::SceneSystemRequestBus::Events::CreateScene, sceneName); - scenes[i] = createSceneOutcome.GetValue(); + AZ::Outcome, AZStd::string> createSceneOutcome = m_sceneSystem->CreateScene(sceneName); + scenes[i] = createSceneOutcome.TakeValue(); } - AZStd::vector retrievedScenes; - AzFramework::SceneSystemRequestBus::BroadcastResult(retrievedScenes, &AzFramework::SceneSystemRequestBus::Events::GetAllScenes); - - EXPECT_EQ(NumScenes, retrievedScenes.size()) << "GetAllScenes() returned a different number of scenes than those created."; - - for (size_t i = 0; i < NumScenes; ++i) - { - EXPECT_EQ(scenes[i], retrievedScenes.at(i)) << "GetAllScenes() returned scenes in a different order than they were created."; - } + size_t index = 0; + m_sceneSystem->IterateActiveScenes([&index, &scenes](const AZStd::shared_ptr& scene) + { + EXPECT_EQ(scenes[index++], scene); + return true; + }); } - TEST_F(SceneTest, EntityContextSceneMapping) + TEST_F(SceneTest, IterateZombieScenes) { - AZStd::unique_ptr m_entityOwnershipService = - AZStd::make_unique(AZ::Uuid::CreateNull(), m_app.GetSerializeContext()); - // Create the entity context, entity, and component - EntityContext* testEntityContext = new EntityContext(AZ::Uuid::CreateRandom(), AZStd::move(m_entityOwnershipService)); - testEntityContext->InitContext(); - EntityContextId testEntityContextId = testEntityContext->GetContextId(); - AZ::Entity* testEntity = testEntityContext->CreateEntity("TestEntity"); - TestComponent* testComponent = testEntity->CreateComponent(); + constexpr size_t NumScenes = 5; - // Try to activate an entity and get the scene before a scene has been set. This should fail. - TestComponentConfig failConfig; - failConfig.m_activateFunction = [](TestComponent* component) + AZStd::shared_ptr scenes[NumScenes] = {nullptr}; + + // Create zombies. + for (size_t i = 0; i < NumScenes; ++i) { - (void)component; - Scene* scene = nullptr; - EntityContextId entityContextId = EntityContextId::CreateNull(); + AZStd::string sceneName = AZStd::string::format("scene %zu", i); + AZ::Outcome, AZStd::string> createSceneOutcome = m_sceneSystem->CreateScene(sceneName); + scenes[i] = createSceneOutcome.TakeValue(); + m_sceneSystem->RemoveScene(sceneName); + } - AzFramework::EntityIdContextQueryBus::BroadcastResult(entityContextId, &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId); + // Check to make sure there are no more active scenes. + size_t index = 0; + m_sceneSystem->IterateActiveScenes([&index, &scenes](const AZStd::shared_ptr&) + { + index++; + return true; + }); + EXPECT_EQ(0, index); - // A null scene should be returned since a scene has not been set for this entity context. - AzFramework::SceneSystemRequestBus::BroadcastResult(scene, &AzFramework::SceneSystemRequestBus::Events::GetSceneFromEntityContextId, entityContextId); - EXPECT_TRUE(scene == nullptr) << "Found a scene when one shouldn't exist."; - }; - - testComponent->SetConfiguration(failConfig); - testComponent->Activate(); - testComponent->Deactivate(); + // Check that the scenes are still returned as zombies. + index = 0; + m_sceneSystem->IterateZombieScenes([&index, &scenes](Scene& scene) + { + EXPECT_EQ(scenes[index++].get(), &scene); + return true; + }); - // Create the scene - AZ::Outcome createSceneOutcome = AZ::Failure(""); - AzFramework::SceneSystemRequestBus::BroadcastResult(createSceneOutcome, &AzFramework::SceneSystemRequestBus::Events::CreateScene, "TestScene"); - Scene* scene = createSceneOutcome.GetValue(); - - // Map the Entity context to the scene - bool success = false; - AzFramework::SceneSystemRequestBus::BroadcastResult(success, &AzFramework::SceneSystemRequestBus::Events::SetSceneForEntityContextId, testEntityContextId, scene); - EXPECT_TRUE(success) << "Unable to associate an entity context with a scene."; - AzFramework::SceneSystemRequestBus::BroadcastResult(success, &AzFramework::SceneSystemRequestBus::Events::SetSceneForEntityContextId, testEntityContextId, scene); - EXPECT_FALSE(success) << "Attempting to map an entity context to a scene that's already mapped, this should not work."; - - // Now it should be possible to get the scene from the entity context within an Entity's Activate() - TestComponentConfig successConfig; - successConfig.m_activateFunction = [](TestComponent* component) + // Check that all scenes are removed when there are no more handles. + for (size_t i = 0; i < NumScenes; ++i) { - (void)component; - Scene* scene = nullptr; - EntityContextId entityContextId = EntityContextId::CreateNull(); - - AzFramework::EntityIdContextQueryBus::BroadcastResult(entityContextId, &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId); - - // A scene should be returned since a scene has been set for this entity context. - AzFramework::SceneSystemRequestBus::BroadcastResult(scene, &AzFramework::SceneSystemRequestBus::Events::GetSceneFromEntityContextId, entityContextId); - EXPECT_TRUE(scene != nullptr) << "Could not find a scene for the entity context."; - }; - - testComponent->SetConfiguration(successConfig); - testComponent->Activate(); - testComponent->Deactivate(); - - // Now remove the entity context / scene association and make sure things fail again. - success = false; - AzFramework::SceneSystemRequestBus::BroadcastResult(success, &AzFramework::SceneSystemRequestBus::Events::RemoveSceneForEntityContextId, testEntityContextId, nullptr); - EXPECT_FALSE(success) << "Should not be able to remove an entity context from a scene it's not associated with."; - AzFramework::SceneSystemRequestBus::BroadcastResult(success, &AzFramework::SceneSystemRequestBus::Events::RemoveSceneForEntityContextId, testEntityContextId, scene); - EXPECT_TRUE(success) << "Was not able to remove an entity context from a scene it's associated with."; - - testComponent->SetConfiguration(failConfig); - testComponent->Activate(); - testComponent->Deactivate(); - - delete testEntityContext; // This should also clean up owned entities / components. + scenes[i].reset(); + } + index = 0; + m_sceneSystem->IterateZombieScenes([&index, &scenes](Scene&) { + index++; + return true; + }); + EXPECT_EQ(0, index); } // Test classes for use in the SceneSystem test. These can't be defined in the test itself due to some functions created by AZ_RTTI not having a body which breaks VS2015. @@ -324,30 +278,48 @@ namespace SceneUnitTest TEST_F(SceneTest, SceneSystem) { // Create the scene - AZ::Outcome createSceneOutcome = AZ::Failure(""); - AzFramework::SceneSystemRequestBus::BroadcastResult(createSceneOutcome, &AzFramework::SceneSystemRequestBus::Events::CreateScene, "TestScene"); - AzFramework::Scene* scene = createSceneOutcome.GetValue(); + AZ::Outcome, AZStd::string> createSceneOutcome = m_sceneSystem->CreateScene("TestScene"); + EXPECT_TRUE(createSceneOutcome.IsSuccess()); + AZStd::shared_ptr scene = createSceneOutcome.TakeValue(); // Set a class on the Scene Foo1* foo1a = new Foo1(); EXPECT_TRUE(scene->SetSubsystem(foo1a)); // Get that class back from the Scene - EXPECT_EQ(foo1a, scene->GetSubsystem()); + EXPECT_EQ(foo1a, *scene->FindSubsystem()); // Try to set the same class type twice, this should fail. Foo1* foo1b = new Foo1(); EXPECT_FALSE(scene->SetSubsystem(foo1b)); delete foo1b; + // Add a child scene + createSceneOutcome = m_sceneSystem->CreateSceneWithParent("ChildScene", scene); + EXPECT_TRUE(createSceneOutcome.IsSuccess()); + AZStd::shared_ptr childScene = createSceneOutcome.TakeValue(); + + // Get class back from parent scene. + EXPECT_EQ(foo1a, *childScene->FindSubsystem()); + + // Find overloaded version of class on child scene. + Foo1* foo1c = new Foo1(); + EXPECT_TRUE(childScene->SetSubsystem(foo1c)); + EXPECT_EQ(foo1c, *childScene->FindSubsystem()); + + // Unset system on child scene, using alternative unset function. + EXPECT_TRUE(childScene->UnsetSubsystem(foo1c)); + delete foo1c; + // Try to un-set a class that was never set, this should fail. EXPECT_FALSE(scene->UnsetSubsystem()); // Unset the class that was previously set EXPECT_TRUE(scene->UnsetSubsystem()); + delete foo1a; - // Make sure that the previsouly set class was really removed. - EXPECT_EQ(nullptr, scene->GetSubsystem()); + // Make sure that the previously set class was really removed. + EXPECT_EQ(nullptr, scene->FindSubsystem()); } } // UnitTest diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index d319fe1d52..9165809c7f 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -2814,14 +2814,16 @@ void EditorViewportWidget::RestoreViewportAfterGameMode() void EditorViewportWidget::UpdateScene() { - AZStd::vector scenes; - AzFramework::SceneSystemRequestBus::BroadcastResult(scenes, &AzFramework::SceneSystemRequests::GetAllScenes); - if (scenes.size() > 0) + auto sceneSystem = AzFramework::SceneSystemInterface::Get(); + if (sceneSystem) { - AZ::RPI::SceneNotificationBus::Handler::BusDisconnect(); - auto scene = scenes[0]; - m_renderViewport->SetScene(scene); - AZ::RPI::SceneNotificationBus::Handler::BusConnect(m_renderViewport->GetViewportContext()->GetRenderScene()->GetId()); + AZStd::shared_ptr mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName); + if (mainScene) + { + AZ::RPI::SceneNotificationBus::Handler::BusDisconnect(); + m_renderViewport->SetScene(mainScene); + AZ::RPI::SceneNotificationBus::Handler::BusConnect(m_renderViewport->GetViewportContext()->GetRenderScene()->GetId()); + } } } diff --git a/Code/Sandbox/Editor/EditorViewportWidget.h b/Code/Sandbox/Editor/EditorViewportWidget.h index f2a40b34a5..472f7e3c62 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.h +++ b/Code/Sandbox/Editor/EditorViewportWidget.h @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/Code/Sandbox/Editor/TrackView/AtomOutputFrameCapture.cpp b/Code/Sandbox/Editor/TrackView/AtomOutputFrameCapture.cpp index 86410a0211..9508c4e265 100644 --- a/Code/Sandbox/Editor/TrackView/AtomOutputFrameCapture.cpp +++ b/Code/Sandbox/Editor/TrackView/AtomOutputFrameCapture.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include namespace TrackView { diff --git a/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapNotificationBus.h b/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapNotificationBus.h index ee7f37adf3..b20535810c 100644 --- a/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapNotificationBus.h +++ b/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapNotificationBus.h @@ -13,7 +13,7 @@ #include -#include +#include #include namespace AZ { @@ -43,15 +43,15 @@ namespace AZ EBusConnectionPolicy::Connect(busPtr, context, handler, connectLock, id); // Check if bootstrap scene already exists and fire notifications if it does - AZStd::vector scenes; - AzFramework::SceneSystemRequestBus::BroadcastResult(scenes, &AzFramework::SceneSystemRequests::GetAllScenes); - AZ_Assert(scenes.size() > 0, "AzFramework didn't set up any scenes."); + auto sceneSystem = AzFramework::SceneSystemInterface::Get(); + AZ_Assert(sceneSystem, "Notification bus called before the scene system has been initialized."); + AZStd::shared_ptr mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName); + AZ_Assert(mainScene, "AzFramework didn't set up any scenes."); - // Assume first scene is the default scene - AZ::RPI::Scene* defaultScene = scenes.at(0)->GetSubsystem(); - if (defaultScene && defaultScene->GetDefaultRenderPipeline()) + AZ::RPI::ScenePtr* defaultScene = mainScene->FindSubsystem(); + if (defaultScene && *defaultScene && (*defaultScene)->GetDefaultRenderPipeline()) { - handler->OnBootstrapSceneReady(defaultScene); + handler->OnBootstrapSceneReady(defaultScene->get()); } } }; diff --git a/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapRequestBus.h b/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapRequestBus.h index 807c00fce6..bd0c2d93d5 100644 --- a/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapRequestBus.h +++ b/Gems/Atom/Bootstrap/Code/Include/Atom/Bootstrap/BootstrapRequestBus.h @@ -13,7 +13,7 @@ #include -#include +#include #include namespace AZ::Render::Bootstrap diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp index 099e9062cf..ddf0ae9274 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp @@ -269,7 +269,7 @@ namespace AZ // Register scene to RPI system so it will be processed/rendered per tick RPI::RPISystemInterface::Get()->RegisterScene(atomScene); - scene->SetSubsystem(atomScene.get()); + scene->SetSubsystem(atomScene); atomSceneHandle = atomScene; @@ -279,11 +279,19 @@ namespace AZ void BootstrapSystemComponent::CreateDefaultScene() { // Bind atomScene to the GameEntityContext's AzFramework::Scene - AZStd::vector scenes; - AzFramework::SceneSystemRequestBus::BroadcastResult(scenes, &AzFramework::SceneSystemRequests::GetAllScenes); - AZ_Assert(scenes.size() > 0, "Error: Scenes missing during system component initialization"); // This should never happen unless scene creation has changed. - m_defaultFrameworkScene = scenes[0]; - m_defaultScene = GetOrCreateAtomSceneFromAzScene(m_defaultFrameworkScene); + m_defaultFrameworkScene = AzFramework::SceneSystemInterface::Get()->GetScene(AzFramework::Scene::MainSceneName); + // This should never happen unless scene creation has changed. + AZ_Assert(m_defaultFrameworkScene, "Error: Scenes missing during system component initialization"); + m_sceneRemovalHandler = AzFramework::Scene::RemovalEvent::Handler( + [this](AzFramework::Scene&, AzFramework::Scene::RemovalEventType eventType) + { + if (eventType == AzFramework::Scene::RemovalEventType::Zombified) + { + m_defaultFrameworkScene.reset(); + } + }); + m_defaultFrameworkScene->ConnectToEvents(m_sceneRemovalHandler); + m_defaultScene = GetOrCreateAtomSceneFromAzScene(m_defaultFrameworkScene.get()); } bool BootstrapSystemComponent::EnsureDefaultRenderPipelineInstalledForScene(AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext) @@ -405,15 +413,6 @@ namespace AZ AzFramework::WindowNotificationBus::Handler::BusDisconnect(); } - void BootstrapSystemComponent::SceneAboutToBeRemoved(AzFramework::Scene& scene) - { - if (&scene == m_defaultFrameworkScene) - { - // Set to nullptr so we don't try to unbind the RPI::Scene from it later. - m_defaultFrameworkScene = nullptr; - } - } - AzFramework::NativeWindowHandle BootstrapSystemComponent::GetDefaultWindowHandle() { return m_windowHandle; diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h index f655272350..7323a54221 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include @@ -45,7 +45,6 @@ namespace AZ , public TickBus::Handler , public AzFramework::WindowNotificationBus::Handler , public AzFramework::AssetCatalogEventBus::Handler - , private AzFramework::SceneSystemNotificationBus::Handler , public AzFramework::WindowSystemNotificationBus::Handler , public AzFramework::WindowSystemRequestBus::Handler , public Render::Bootstrap::DefaultWindowBus::Handler @@ -90,9 +89,6 @@ namespace AZ // AzFramework::AssetCatalogEventBus::Handler overrides ... void OnCatalogLoaded(const char* catalogFile) override; - // AzFramework::SceneSystemNotificationBus::Handler overrides ... - void SceneAboutToBeRemoved(AzFramework::Scene& scene) override; - // AzFramework::WindowSystemNotificationBus::Handler overrides ... void OnWindowCreated(AzFramework::NativeWindowHandle windowHandle) override; @@ -104,12 +100,14 @@ namespace AZ void CreateWindowContext(); + AzFramework::Scene::RemovalEvent::Handler m_sceneRemovalHandler; + AZStd::unique_ptr m_nativeWindow; AzFramework::NativeWindowHandle m_windowHandle = nullptr; RPI::ViewportContextPtr m_viewportContext; RPI::ScenePtr m_defaultScene = nullptr; - AzFramework::Scene* m_defaultFrameworkScene = nullptr; + AZStd::shared_ptr m_defaultFrameworkScene = nullptr; float m_simulateTime = 0; float m_deltaTime = 0.016f; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h index 3238785d81..ee54b16fea 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h @@ -35,7 +35,7 @@ #include #include -#include +#include namespace AZ { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 2c11ef9a41..a66471e038 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -28,6 +28,8 @@ #include #include +#include + namespace AZ { namespace RPI @@ -71,12 +73,15 @@ namespace AZ Scene* Scene::GetSceneForEntityContextId(AzFramework::EntityContextId entityContextId) { // Find the scene for this entity context. - AzFramework::Scene* scene = nullptr; - AzFramework::SceneSystemRequestBus::BroadcastResult(scene, &AzFramework::SceneSystemRequestBus::Events::GetSceneFromEntityContextId, entityContextId); + AZStd::shared_ptr scene = AzFramework::EntityContext::FindContainingScene(entityContextId); if (scene) { // Get the RPI::Scene subsystem from the AZFramework Scene. - return scene->GetSubsystem(); + RPI::ScenePtr* scenePtr = scene->FindSubsystem(); + if (scenePtr) + { + return scenePtr->get(); + } } return nullptr; } diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h index dd71bbc431..5dbbf04f1b 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/RenderViewportWidget.h @@ -82,7 +82,7 @@ namespace AtomToolsFramework AZ::RPI::ConstViewportContextPtr GetViewportContext() const; //! Creates an AZ::RPI::ScenePtr for the given scene and assigns it to the current ViewportContext. //! If useDefaultRenderPipeline is specified, this will initialize the scene with a rendering pipeline. - void SetScene(AzFramework::Scene* scene, bool useDefaultRenderPipeline = true); + void SetScene(const AZStd::shared_ptr& scene, bool useDefaultRenderPipeline = true); //! Gets the default camera that's been automatically registered to our ViewportContext. AZ::RPI::ViewPtr GetDefaultCamera(); AZ::RPI::ConstViewPtr GetDefaultCamera() const; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index 0f1e5718db..f282aaebee 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -118,7 +118,7 @@ namespace AtomToolsFramework return m_viewportContext; } - void RenderViewportWidget::SetScene(AzFramework::Scene* scene, bool useDefaultRenderPipeline) + void RenderViewportWidget::SetScene(const AZStd::shared_ptr& scene, bool useDefaultRenderPipeline) { if (scene == nullptr) { @@ -128,7 +128,7 @@ namespace AtomToolsFramework AZ::RPI::ScenePtr atomScene; auto initializeScene = [&](AZ::Render::Bootstrap::Request* bootstrapRequests) { - atomScene = bootstrapRequests->GetOrCreateAtomSceneFromAzScene(scene); + atomScene = bootstrapRequests->GetOrCreateAtomSceneFromAzScene(scene.get()); if (useDefaultRenderPipeline) { // atomScene may already have a default render pipeline installed. diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp index e1bfaa3872..53a2dc2ce6 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp @@ -88,10 +88,12 @@ namespace MaterialEditor m_scene->SetShaderResourceGroupCallback(callback); // Bind m_defaultScene to the GameEntityContext's AzFramework::Scene - AZStd::vector scenes; - AzFramework::SceneSystemRequestBus::BroadcastResult(scenes, &AzFramework::SceneSystemRequests::GetAllScenes); - AZ_Assert(scenes.size() > 0, "Error: Scenes missing during system component initialization"); // This should never happen unless scene creation has changed. - scenes.at(0)->SetSubsystem(m_scene.get()); + auto sceneSystem = AzFramework::SceneSystemInterface::Get(); + AZ_Assert(sceneSystem, "MaterialViewportRenderer was unable to get the scene system during construction."); + AZStd::shared_ptr mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName); + // This should never happen unless scene creation has changed. + AZ_Assert(mainScene, "Main scenes missing during system component initialization"); + mainScene->SetSubsystem(m_scene); // Create a render pipeline from the specified asset for the window context and add the pipeline to the scene AZ::Data::Asset pipelineAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath(m_defaultPipelineAssetPath.c_str(), AZ::RPI::AssetUtils::TraceLevel::Error); @@ -275,6 +277,13 @@ namespace MaterialEditor } m_lightHandles.clear(); + auto sceneSystem = AzFramework::SceneSystemInterface::Get(); + AZ_Assert(sceneSystem, "MaterialViewportRenderer was unable to get the scene system during destruction."); + AZStd::shared_ptr mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName); + // This should never happen unless scene creation has changed. + AZ_Assert(mainScene, "Main scenes missing during system component destruction"); + mainScene->UnsetSubsystem(m_scene); + m_swapChainPass = nullptr; AZ::RPI::RPISystemInterface::Get()->UnregisterScene(m_scene); m_scene = nullptr; diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp index 77f15284f1..bee6d31275 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp @@ -1263,7 +1263,12 @@ namespace AZ::AtomBridge const char* text, bool center) { - AzFramework::FontDrawInterface* fontDrawInterface = AZ::Interface::Get()->GetDefaultFontDrawInterface(); + auto fontQueryInterface = AZ::Interface::Get(); + if (!fontQueryInterface) + { + return; + } + AzFramework::FontDrawInterface* fontDrawInterface = fontQueryInterface->GetDefaultFontDrawInterface(); if (!fontDrawInterface || !text || size == 0.0f) { return; diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h index 2969e35a51..fbefc84aad 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h +++ b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont.h @@ -24,7 +24,7 @@ #include #include -#include +#include #include @@ -40,7 +40,6 @@ namespace AZ class AtomFont : public ICryFont , public AzFramework::FontQueryInterface - , public AzFramework::SceneSystemNotificationBus::Handler { friend class FFont; @@ -92,8 +91,7 @@ namespace AZ AzFramework::FontDrawInterface* GetFontDrawInterface(AzFramework::FontId fontId) const override; AzFramework::FontDrawInterface* GetDefaultFontDrawInterface() const override; - // SceneSystemNotificationBus handlers - void SceneAboutToBeRemoved(AzFramework::Scene& scene) override; + void SceneAboutToBeRemoved(AzFramework::Scene& scene); // Atom DynamicDraw interface management @@ -137,6 +135,8 @@ namespace AZ XmlNodeRef LoadFontFamilyXml(const char* fontFamilyName, string& outputDirectory, string& outputFullPath); private: + AzFramework::ISceneSystem::SceneEvent::Handler m_sceneEventHandler; + FontMap m_fonts; FontFamilyMap m_fontFamilies; //!< Map font family names to weak ptrs so we can construct shared_ptrs but not keep a ref ourselves. FontFamilyReverseLookupMap m_fontFamilyReverseLookup; //::Register(this); + + m_sceneEventHandler = AzFramework::ISceneSystem::SceneEvent::Handler( + [this](AzFramework::ISceneSystem::EventType eventType, const AZStd::shared_ptr& scene) + { + if (eventType == AzFramework::ISceneSystem::EventType::ScenePendingRemoval) + { + SceneAboutToBeRemoved(*scene); + } + }); + auto sceneSystem = AzFramework::SceneSystemInterface::Get(); + AZ_Assert(sceneSystem, "Font created before the scene system is available."); + sceneSystem->ConnectToEvents(m_sceneEventHandler); } AZ::AtomFont::~AtomFont() @@ -851,12 +863,14 @@ XmlNodeRef AZ::AtomFont::LoadFontFamilyXml(const char* fontFamilyName, string& o void AZ::AtomFont::SceneAboutToBeRemoved(AzFramework::Scene& scene) { - AZ::RPI::Scene* rpiScene = scene.GetSubsystem(); - - AZStd::lock_guard lock(m_sceneToDynamicDrawMutex); - if ( auto it = m_sceneToDynamicDrawMap.find(rpiScene); it != m_sceneToDynamicDrawMap.end()) + AZ::RPI::ScenePtr* rpiScene = scene.FindSubsystem(); + if (rpiScene) { - m_sceneToDynamicDrawMap.erase(it); + AZStd::lock_guard lock(m_sceneToDynamicDrawMutex); + if (auto it = m_sceneToDynamicDrawMap.find(rpiScene->get()); it != m_sceneToDynamicDrawMap.end()) + { + m_sceneToDynamicDrawMap.erase(it); + } } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp index eb38e02a2e..8a0bd86965 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseProbeGrid/DiffuseProbeGridComponentController.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp index 9d2196de2b..deab9d2715 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp index 005e9a6ff5..c7349adefa 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/ReflectionProbeComponentController.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkinnedMesh/SkinnedMeshDebugDisplay.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkinnedMesh/SkinnedMeshDebugDisplay.cpp index 815a2c276a..695868fb05 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkinnedMesh/SkinnedMeshDebugDisplay.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkinnedMesh/SkinnedMeshDebugDisplay.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h index 28e98082d0..a915e20591 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererData.h @@ -42,7 +42,7 @@ namespace AZ RPI::ScenePtr m_scene; AZStd::string m_sceneName = "Material Thumbnail Scene"; AZStd::string m_pipelineName = "Material Thumbnail Pipeline"; - AzFramework::Scene* m_frameworkScene = nullptr; + AZStd::shared_ptr m_frameworkScene; RPI::RenderPipelinePtr m_renderPipeline; AZStd::unique_ptr m_entityContext; AZStd::vector m_passHierarchy; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp index 2a447ce3ec..100b0f61cd 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp @@ -100,24 +100,15 @@ namespace AZ data->m_scene->SetShaderResourceGroupCallback(callback); // Bind m_defaultScene to the GameEntityContext's AzFramework::Scene - Outcome createSceneOutcome; - AzFramework::SceneSystemRequestBus::BroadcastResult( - createSceneOutcome, - &AzFramework::SceneSystemRequests::CreateScene, - data->m_sceneName); + auto* sceneSystem = AzFramework::SceneSystemInterface::Get(); + AZ_Assert(sceneSystem, "Thumbnail system failed to get scene system implementation."); + Outcome, AZStd::string> createSceneOutcome = + sceneSystem->CreateScene(data->m_sceneName); AZ_Assert(createSceneOutcome, createSceneOutcome.GetError().c_str()); // This should never happen unless scene creation has changed. - createSceneOutcome.GetValue()->SetSubsystem(data->m_scene.get()); - data->m_frameworkScene = createSceneOutcome.GetValue(); - data->m_frameworkScene->SetSubsystem(data->m_scene.get()); - - bool success = false; - AzFramework::SceneSystemRequestBus::BroadcastResult( - success, - &AzFramework::SceneSystemRequests::SetSceneForEntityContextId, - data->m_entityContext->GetContextId(), - data->m_frameworkScene); - AZ_Assert(success, "Unable to set entity context on AzFramework::Scene: %s", data->m_sceneName.c_str()); + data->m_frameworkScene = createSceneOutcome.TakeValue(); + data->m_frameworkScene->SetSubsystem(data->m_scene); + data->m_frameworkScene->SetSubsystem(data->m_entityContext.get()); // Create a render pipeline from the specified asset for the window context and add the pipeline to the scene RPI::RenderPipelineDescriptor pipelineDesc; pipelineDesc.m_mainViewTagName = "MainCamera"; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp index 03ab03336d..bad7c2fe38 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -49,11 +49,13 @@ namespace AZ m_context->GetData()->m_scene->Deactivate(); m_context->GetData()->m_scene->RemoveRenderPipeline(m_context->GetData()->m_renderPipeline->GetId()); RPI::RPISystemInterface::Get()->UnregisterScene(m_context->GetData()->m_scene); - bool sceneRemovedSuccessfully = false; - AzFramework::SceneSystemRequestBus::BroadcastResult( - sceneRemovedSuccessfully, - &AzFramework::SceneSystemRequests::RemoveScene, - m_context->GetData()->m_sceneName); + + auto sceneSystem = AzFramework::SceneSystemInterface::Get(); + AZ_Assert(sceneSystem, "Thumbnail system failed to get scene system implementation."); + [[maybe_unused]] bool sceneRemovedSuccessfully = sceneSystem->RemoveScene(m_context->GetData()->m_sceneName); + AZ_Assert( + sceneRemovedSuccessfully, "Thumbnail system was unable to remove scene '%s' from the scene system.", + m_context->GetData()->m_sceneName.c_str()); m_context->GetData()->m_scene = nullptr; m_context->GetData()->m_renderPipeline = nullptr; }