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) + { + [[maybe_unused]] bool result = scene->SetSubsystem(gameEntityContext); + AZ_Assert(result, "Unable to register main entity context with the main scene."); + } + else { - SceneSystemRequestBus::BroadcastResult(success, &AzFramework::SceneSystemRequests::SetSceneForEntityContextId, gameEntityContextId, scene); + 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; - }; - - 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; - } + 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 }; + }; } // 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); } - return scenes; } - bool SceneSystemComponent::RemoveScene(AZStd::string_view name) + void SceneSystemComponent::IterateZombieScenes(const ZombieIterationCallback& callback) { - for (size_t i = 0; i < m_scenes.size(); ++i) + bool keepGoing = true; + auto end = m_zombieScenes.end(); + for (auto it = m_zombieScenes.begin(); it != end && keepGoing;) { - auto& scenePtr = m_scenes.at(i); - if (scenePtr->GetName() == name) + if (!it->expired()) { - // Remove any entityContext mappings. - Scene* scene = scenePtr.get(); - for (auto entityContextScenePairIt = m_entityContextToScenes.begin(); entityContextScenePairIt != m_entityContextToScenes.end();) - { - AZStd::pair& pair = *entityContextScenePairIt; - if (pair.second == scene) - { - // swap and pop back. - *entityContextScenePairIt = m_entityContextToScenes.back(); - m_entityContextToScenes.pop_back(); - } - else - { - ++entityContextScenePairIt; - } - } - - SceneSystemNotificationBus::Broadcast(&SceneSystemNotificationBus::Events::SceneAboutToBeRemoved, *scene); - SceneNotificationBus::Event(scene, &SceneNotificationBus::Events::SceneAboutToBeRemoved); - - m_scenes.erase(&scenePtr); - return true; + keepGoing = callback(*(it->lock())); + ++it; + } + else + { + *it = m_zombieScenes.back(); + m_zombieScenes.pop_back(); + end = m_zombieScenes.end(); } } - - AZ_Warning("SceneSystemComponent", false, "Attempting to remove scene name \"%.*s\", but that scene was not found.", static_cast(name.size()), name.data()); - return false; - } - - bool SceneSystemComponent::SetSceneForEntityContextId(EntityContextId entityContextId, Scene* scene) - { - 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; } - bool SceneSystemComponent::RemoveSceneForEntityContextId(EntityContextId entityContextId, Scene* scene) + bool SceneSystemComponent::RemoveScene(AZStd::string_view name) { - if (!scene || entityContextId.IsNull()) - { - return false; - } - - for (auto entityContextScenePairIt = m_entityContextToScenes.begin(); entityContextScenePairIt != m_entityContextToScenes.end();) + for (AZStd::shared_ptr& scene : m_activeScenes) { - AZStd::pair& pair = *entityContextScenePairIt; - if (!(pair.first == entityContextId && pair.second == scene)) - { - ++entityContextScenePairIt; - } - else + if (scene->GetName() == name) { - // swap and pop back. - *entityContextScenePairIt = m_entityContextToScenes.back(); - m_entityContextToScenes.pop_back(); + MarkSceneForDestruction(*scene); + { + AZStd::lock_guard lock(m_eventMutex); + m_events.Signal(EventType::ScenePendingRemoval, scene); + } - SceneNotificationBus::Event(scene, &SceneNotificationBus::Events::EntityContextUnmapped, entityContextId); + // 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; } } - 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); + AZ_Warning("SceneSystemComponent", false, R"(Attempting to remove scene name "%.*s", but that scene was not found.)", AZ_STRING_ARG(name)); return false; } - Scene* SceneSystemComponent::GetSceneFromEntityContextId(EntityContextId entityContextId) + void SceneSystemComponent::ConnectToEvents(SceneEvent::Handler& handler) { - for (AZStd::pair& pair : m_entityContextToScenes) - { - if (pair.first == entityContextId) - { - return pair.second; - } - } - return nullptr; + AZStd::lock_guard lock(m_eventMutex); + handler.Connect(m_events); } - -} // 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(); - - // 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) - { - (void)component; - Scene* scene = nullptr; - EntityContextId entityContextId = EntityContextId::CreateNull(); - - AzFramework::EntityIdContextQueryBus::BroadcastResult(entityContextId, &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId); + constexpr size_t NumScenes = 5; - // 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(); + AZStd::shared_ptr scenes[NumScenes] = {nullptr}; - // 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) + // Create zombies. + 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(); + 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); + } - // 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."; + // 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); - 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; + }); - delete testEntityContext; // This should also clean up owned entities / components. + // Check that all scenes are removed when there are no more handles. + for (size_t i = 0; i < NumScenes; ++i) + { + 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) - { - AZ::RPI::SceneNotificationBus::Handler::BusDisconnect(); - auto scene = scenes[0]; - m_renderViewport->SetScene(scene); - AZ::RPI::SceneNotificationBus::Handler::BusConnect(m_renderViewport->GetViewportContext()->GetRenderScene()->GetId()); + auto sceneSystem = AzFramework::SceneSystemInterface::Get(); + if (sceneSystem) + { + 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; }