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.
main
AMZN-koppersr 5 years ago committed by GitHub
parent 45fd86c2bf
commit 5e4094b258
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -17,7 +17,7 @@
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Debug/Trace.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <AzFramework/Entity/GameEntityContextComponent.h>
namespace AzFramework
@ -50,36 +50,34 @@ namespace AzFramework
void AzFrameworkConfigurationSystemComponent::Activate()
{
// Create the defaults scene and associate the GameEntityContext with it.
AZ::Outcome<Scene*, AZStd::string> createSceneOutcome = AZ::Failure<AZStd::string>("SceneSystemRequests bus not responding.");
SceneSystemRequestBus::BroadcastResult(createSceneOutcome, &AzFramework::SceneSystemRequests::CreateScene, "default");
AZ::Outcome<AZStd::shared_ptr<Scene>, 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> scene = createSceneOutcome.TakeValue();
EntityContext* gameEntityContext = nullptr;
GameEntityContextRequestBus::BroadcastResult(gameEntityContext, &GameEntityContextRequests::GetGameEntityContextInstance);
if (gameEntityContext != nullptr)
{
[[maybe_unused]] bool result = scene->SetSubsystem<EntityContext::SceneStorageType&>(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)

@ -18,6 +18,8 @@
#include <AzCore/Component/TickBus.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/std/containers/stack.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include "EntityContext.h"
@ -37,6 +39,30 @@ namespace AzFramework
}
}
AZStd::shared_ptr<Scene> 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<Scene> result;
sceneSystem->IterateActiveScenes([&result, &contextId](const AZStd::shared_ptr<Scene>& scene)
{
EntityContext** entityContext = scene->FindSubsystemInScene<EntityContext::SceneStorageType>();
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
//=========================================================================

@ -17,6 +17,7 @@
#include <AzCore/Component/EntityBus.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/Serialization/IdUtils.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Entity/SliceEntityOwnershipService.h>
@ -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> entityOwnershipService,
@ -75,6 +78,7 @@ namespace AzFramework
//////////////////////////////////////////////////////////////////////////
static void Reflect(AZ::ReflectContext* context);
static AZStd::shared_ptr<Scene> FindContainingScene(const EntityContextId& contextId);
protected:

@ -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.

@ -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;

@ -14,9 +14,9 @@
#include <AzCore/std/algorithm.h>
#include <AzCore/std/sort.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzFramework/Render/Intersector.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Visibility/BoundsBus.h>
#include <algorithm>
@ -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> scene = EntityContext::FindContainingScene(m_contextId);
if (scene)
{
scene->SetSubsystem<IntersectorInterface>(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> scene = EntityContext::FindContainingScene(m_contextId);
if (scene)
{
scene->UnsetSubsystem<IntersectorInterface>();
[[maybe_unused]] bool result = scene->UnsetSubsystem(this);
AZ_Assert(result, "Failed to unregister Intersector with scene");
}
}

@ -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();

@ -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<Scene> 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>& Scene::GetParent()
{
return m_parent;
}
AZStd::shared_ptr<const Scene> 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<Scene*>(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<Scene*>(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);
}
}

@ -11,10 +11,14 @@
*/
#pragma once
#include <AzCore/EBus/Event.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/any.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/string/string_view.h>
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<Scene&, RemovalEventType>;
enum class SubsystemEventType
{
Added,
Removed
};
using SubsystemEvent = AZ::Event<Scene&, SubsystemEventType, const AZ::TypeId&>;
explicit Scene(AZStd::string name);
Scene(AZStd::string name, AZStd::shared_ptr<Scene> parent);
~Scene();
[[nodiscard]] const AZStd::string& GetName() const;
[[nodiscard]] const AZStd::shared_ptr<Scene>& GetParent();
[[nodiscard]] AZStd::shared_ptr<const Scene> 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 <typename T>
bool SetSubsystem(T* system);
bool SetSubsystem(T&& system);
// Unset the instance of a subsystem associated with this scene.
template <typename T>
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<typename T>
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 <typename T>
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<typename T>
[[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<typename T>
[[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<typename T>
[[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<AZ::TypeId> m_systemKeys;
AZStd::vector<void*> m_systemPointers;
};
template <typename T>
bool Scene::SetSubsystem(T* system)
{
if (GetSubsystem<T>() != nullptr)
{
return false;
}
m_systemKeys.push_back(T::RTTI_Type());
m_systemPointers.push_back(system);
return true;
}
template <typename T>
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 <typename T>
T* Scene::GetSubsystem()
{
for (size_t i = 0; i < m_systemKeys.size(); ++i)
{
if (m_systemKeys.at(i) == T::RTTI_Type())
{
return reinterpret_cast<T*>(m_systemPointers.at(i));
}
}
return nullptr;
}
AZStd::vector<AZStd::any> 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<Scene> 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 <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<typename T>
bool Scene::SetSubsystem(T&& system)
{
const AZ::TypeId& targetType = azrtti_typeid<T>();
for (const AZ::TypeId& key : m_systemKeys)
{
if (key == targetType)
{
return false;
}
}
m_systemKeys.push_back(targetType);
m_systemObjects.emplace_back(AZStd::forward<T>(system));
m_subsystemEvent.Signal(*this, SubsystemEventType::Added, targetType);
return true;
}
template<typename T>
bool Scene::UnsetSubsystem()
{
const AZ::TypeId& targetType = azrtti_typeid<T>();
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<typename T>
bool Scene::UnsetSubsystem(const T& system)
{
const AZ::TypeId& targetType = azrtti_typeid<T>();
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<T>(&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<typename T>
T* Scene::FindSubsystem()
{
const AZ::TypeId& targetType = azrtti_typeid<T>();
AZStd::any* subSystem = FindSubsystem(targetType);
return subSystem ? AZStd::any_cast<T>(subSystem) : nullptr;
}
template<typename T>
const T* Scene::FindSubsystem() const
{
const AZ::TypeId& targetType = azrtti_typeid<T>();
const AZStd::any* subSystem = FindSubsystem(targetType);
return subSystem ? AZStd::any_cast<const T>(subSystem) : nullptr;
}
template<typename T>
T* Scene::FindSubsystemInScene()
{
const AZ::TypeId& targetType = azrtti_typeid<T>();
AZStd::any* subSystem = FindSubsystemInScene(targetType);
return subSystem ? AZStd::any_cast<T>(subSystem) : nullptr;
}
template<typename T>
const T* Scene::FindSubsystemInScene() const
{
const AZ::TypeId& targetType = azrtti_typeid<T>();
const AZStd::any* subSystem = FindSubsystemInScene(targetType);
return subSystem ? AZStd::any_cast<const T>(subSystem) : nullptr;
}
} // namespace AzFramework

@ -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 <AzCore/EBus/EBus.h>
#include <AzFramework/Entity/EntityContext.h>
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<Scene*, AZStd::string> 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<Scene*> 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<SceneSystemRequests>;
//! 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<SceneSystemNotifications>;
//! 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<SceneNotifications>;
} // AzFramework

@ -14,7 +14,7 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/Scene/Scene.h>
#include <AzCore/std/smart_ptr/make_shared.h>
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<Scene*, AZStd::string> SceneSystemComponent::CreateScene(AZStd::string_view name)
AZ::Outcome<AZStd::shared_ptr<Scene>, AZStd::string> SceneSystemComponent::CreateScene(AZStd::string_view name)
{
Scene* existingScene = GetScene(name);
return CreateSceneWithParent(name, nullptr);
}
AZ::Outcome<AZStd::shared_ptr<Scene>, AZStd::string> SceneSystemComponent::CreateSceneWithParent(
AZStd::string_view name, AZStd::shared_ptr<Scene> parent)
{
const AZStd::shared_ptr<Scene>& existingScene = GetScene(name);
if (existingScene)
{
return AZ::Failure<AZStd::string>("A scene already exists with this name.");
}
auto newScene = AZStd::make_unique<Scene>(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<Scene>(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<Scene> 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<Scene*> SceneSystemComponent::GetAllScenes()
void SceneSystemComponent::IterateActiveScenes(const ActiveIterationCallback& callback)
{
AZStd::vector<Scene*> 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<EntityContextId, Scene*>& 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<int>(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>& scene : m_activeScenes)
{
AZStd::pair<EntityContextId, Scene*>& 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<int>(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<EntityContextId, Scene*>& 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

@ -13,18 +13,18 @@
#include <AzCore/Component/Component.h>
#include <AzCore/std/containers/map.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <AzFramework/Entity/EntityContext.h>
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<Scene*, AZStd::string> CreateScene(AZStd::string_view name) override;
Scene* GetScene(AZStd::string_view name) override;
AZStd::vector<Scene*> GetAllScenes() override;
AZ::Outcome<AZStd::shared_ptr<Scene>, AZStd::string> CreateScene(AZStd::string_view name) override;
AZ::Outcome<AZStd::shared_ptr<Scene>, AZStd::string> CreateSceneWithParent(
AZStd::string_view name, AZStd::shared_ptr<Scene> parent) override;
[[nodiscard]] AZStd::shared_ptr<Scene> 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<AZStd::unique_ptr<Scene>> 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<AZStd::pair<EntityContextId, Scene*>> m_entityContextToScenes;
AZStd::vector<AZStd::shared_ptr<Scene>> m_activeScenes;
AZStd::vector<AZStd::weak_ptr<Scene>> 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;
};
}

@ -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 <AzCore/EBus/EBus.h>
#include <AzCore/EBus/Event.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/functional.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzFramework/Scene/Scene.h>
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<EventType, const AZStd::shared_ptr<Scene>&>;
ISceneSystem() = default;
virtual ~ISceneSystem() = default;
using ActiveIterationCallback = AZStd::function<bool(const AZStd::shared_ptr<Scene>& scene)>;
using ZombieIterationCallback = AZStd::function<bool(Scene& scene)>;
//! 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::shared_ptr<Scene>, 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::shared_ptr<Scene>, AZStd::string> CreateSceneWithParent(
AZStd::string_view name, AZStd::shared_ptr<Scene> 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<Scene> 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<ISceneSystem>;
// 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<ISceneSystem, ISceneSystemRequests>;
} // AzFramework

@ -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

@ -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;

@ -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;

@ -16,7 +16,7 @@
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <AzToolsFramework/Editor/EditorSettingsAPIBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
@ -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<AzFramework::Scene> mainScene = sceneSystem->GetScene(AzFramework::Scene::MainSceneName);
if (mainScene)
{
AZ::Outcome<AZStd::shared_ptr<AzFramework::Scene>, 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<AzFramework::EntityContext::SceneStorageType&>(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)

@ -42,6 +42,7 @@ namespace UnitTest
MOCK_METHOD3(InstantiateDynamicSlice, AzFramework::SliceInstantiationTicket(const AZ::Data::Asset<AZ::Data::AssetData>&, const AZ::Transform&, const AZ::IdUtils::Remapper<AZ::EntityId>::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&));

@ -130,6 +130,8 @@ namespace SceneUnitTest
m_systemEntity->CreateComponent<AZ::JobManagerComponent>();
m_systemEntity->CreateComponent<AZ::StreamerComponent>();
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<Scene*, AZStd::string> createSceneOutcome = AZ::Failure<AZStd::string>("");
{
// A scene should be able to be created with a given name.
AzFramework::SceneSystemRequestBus::BroadcastResult(createSceneOutcome, &AzFramework::SceneSystemRequestBus::Events::CreateScene, "TestScene");
AZ::Outcome<AZStd::shared_ptr<Scene>, 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> 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<AZStd::string>("");
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<Scene*, AZStd::string> createSceneOutcome = AZ::Failure<AZStd::string>("");
AzFramework::SceneSystemRequestBus::BroadcastResult(createSceneOutcome, &AzFramework::SceneSystemRequestBus::Events::CreateScene, s_sceneName);
createdScene = createSceneOutcome.GetValue();
AZ::Outcome<AZStd::shared_ptr<Scene>, AZStd::string> createSceneOutcome = m_sceneSystem->CreateScene(sceneName);
AZStd::shared_ptr<Scene> 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<Scene> 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<Scene> 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<Scene*, AZStd::string> createSceneOutcome = AZ::Failure<AZStd::string>("");
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::shared_ptr<Scene>, 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<Scene> scenes[NumScenes] = {nullptr};
for (size_t i = 0; i < NumScenes; ++i)
{
AZ::Outcome<Scene*, AZStd::string> createSceneOutcome = AZ::Failure<AZStd::string>("");
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::shared_ptr<Scene>, AZStd::string> createSceneOutcome = m_sceneSystem->CreateScene(sceneName);
scenes[i] = createSceneOutcome.TakeValue();
}
AZStd::vector<Scene*> 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>& scene)
{
EXPECT_EQ(scenes[index++], scene);
return true;
});
}
TEST_F(SceneTest, EntityContextSceneMapping)
TEST_F(SceneTest, IterateZombieScenes)
{
AZStd::unique_ptr<SliceEntityOwnershipService> m_entityOwnershipService =
AZStd::make_unique<AzFramework::SliceEntityOwnershipService>(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<TestComponent>();
// 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<Scene> scenes[NumScenes] = {nullptr};
// Create the scene
AZ::Outcome<Scene*, AZStd::string> createSceneOutcome = AZ::Failure<AZStd::string>("");
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::shared_ptr<Scene>, 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<Scene>&)
{
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<Scene*, AZStd::string> createSceneOutcome = AZ::Failure<AZStd::string>("");
AzFramework::SceneSystemRequestBus::BroadcastResult(createSceneOutcome, &AzFramework::SceneSystemRequestBus::Events::CreateScene, "TestScene");
AzFramework::Scene* scene = createSceneOutcome.GetValue();
AZ::Outcome<AZStd::shared_ptr<Scene>, AZStd::string> createSceneOutcome = m_sceneSystem->CreateScene("TestScene");
EXPECT_TRUE(createSceneOutcome.IsSuccess());
AZStd::shared_ptr<Scene> 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<Foo1>());
EXPECT_EQ(foo1a, *scene->FindSubsystem<Foo1*>());
// 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<Scene> childScene = createSceneOutcome.TakeValue();
// Get class back from parent scene.
EXPECT_EQ(foo1a, *childScene->FindSubsystem<Foo1*>());
// Find overloaded version of class on child scene.
Foo1* foo1c = new Foo1();
EXPECT_TRUE(childScene->SetSubsystem(foo1c));
EXPECT_EQ(foo1c, *childScene->FindSubsystem<Foo1*>());
// 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<Foo2>());
// Unset the class that was previously set
EXPECT_TRUE(scene->UnsetSubsystem<Foo1>());
delete foo1a;
// Make sure that the previsouly set class was really removed.
EXPECT_EQ(nullptr, scene->GetSubsystem<Foo1>());
// Make sure that the previously set class was really removed.
EXPECT_EQ(nullptr, scene->FindSubsystem<Foo1*>());
}
} // UnitTest

@ -2814,14 +2814,16 @@ void EditorViewportWidget::RestoreViewportAfterGameMode()
void EditorViewportWidget::UpdateScene()
{
AZStd::vector<AzFramework::Scene*> 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<AzFramework::Scene> 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());
}
}
}

@ -28,7 +28,7 @@
#include <AzCore/Component/EntityId.h>
#include <AzCore/std/optional.h>
#include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/API/EditorCameraBus.h>

@ -22,7 +22,7 @@
#include <AzCore/Name/Name.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
namespace TrackView
{

@ -13,7 +13,7 @@
#include <AzCore/EBus/EBus.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <Atom/RPI.Public/Scene.h>
namespace AZ
{
@ -43,15 +43,15 @@ namespace AZ
EBusConnectionPolicy<Bus>::Connect(busPtr, context, handler, connectLock, id);
// Check if bootstrap scene already exists and fire notifications if it does
AZStd::vector<AzFramework::Scene*> 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<AzFramework::Scene> 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<AZ::RPI::Scene>();
if (defaultScene && defaultScene->GetDefaultRenderPipeline())
AZ::RPI::ScenePtr* defaultScene = mainScene->FindSubsystem<AZ::RPI::ScenePtr>();
if (defaultScene && *defaultScene && (*defaultScene)->GetDefaultRenderPipeline())
{
handler->OnBootstrapSceneReady(defaultScene);
handler->OnBootstrapSceneReady(defaultScene->get());
}
}
};

@ -13,7 +13,7 @@
#include <AzCore/EBus/EBus.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <Atom/RPI.Public/Scene.h>
namespace AZ::Render::Bootstrap

@ -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<AzFramework::Scene*> 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;

@ -17,7 +17,7 @@
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <AzFramework/Windowing/NativeWindow.h>
#include <AzFramework/Windowing/WindowBus.h>
@ -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<AzFramework::NativeWindow> m_nativeWindow;
AzFramework::NativeWindowHandle m_windowHandle = nullptr;
RPI::ViewportContextPtr m_viewportContext;
RPI::ScenePtr m_defaultScene = nullptr;
AzFramework::Scene* m_defaultFrameworkScene = nullptr;
AZStd::shared_ptr<AzFramework::Scene> m_defaultFrameworkScene = nullptr;
float m_simulateTime = 0;
float m_deltaTime = 0.016f;

@ -35,7 +35,7 @@
#include <AzCore/Script/ScriptTimePoint.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
namespace AZ
{

@ -28,6 +28,8 @@
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Jobs/JobEmpty.h>
#include <AzFramework/Entity/EntityContext.h>
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<AzFramework::Scene> scene = AzFramework::EntityContext::FindContainingScene(entityContextId);
if (scene)
{
// Get the RPI::Scene subsystem from the AZFramework Scene.
return scene->GetSubsystem<RPI::Scene>();
RPI::ScenePtr* scenePtr = scene->FindSubsystem<RPI::ScenePtr>();
if (scenePtr)
{
return scenePtr->get();
}
}
return nullptr;
}

@ -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<AzFramework::Scene>& 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;

@ -118,7 +118,7 @@ namespace AtomToolsFramework
return m_viewportContext;
}
void RenderViewportWidget::SetScene(AzFramework::Scene* scene, bool useDefaultRenderPipeline)
void RenderViewportWidget::SetScene(const AZStd::shared_ptr<AzFramework::Scene>& 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.

@ -88,10 +88,12 @@ namespace MaterialEditor
m_scene->SetShaderResourceGroupCallback(callback);
// Bind m_defaultScene to the GameEntityContext's AzFramework::Scene
AZStd::vector<AzFramework::Scene*> 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<AzFramework::Scene> 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<AZ::RPI::AnyAsset> pipelineAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(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<AzFramework::Scene> 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;

@ -1263,7 +1263,12 @@ namespace AZ::AtomBridge
const char* text,
bool center)
{
AzFramework::FontDrawInterface* fontDrawInterface = AZ::Interface<AzFramework::FontQueryInterface>::Get()->GetDefaultFontDrawInterface();
auto fontQueryInterface = AZ::Interface<AzFramework::FontQueryInterface>::Get();
if (!fontQueryInterface)
{
return;
}
AzFramework::FontDrawInterface* fontDrawInterface = fontQueryInterface->GetDefaultFontDrawInterface();
if (!fontDrawInterface || !text || size == 0.0f)
{
return;

@ -24,7 +24,7 @@
#include <map>
#include <AzFramework/Font/FontInterface.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <Atom/RPI.Public/DynamicDraw/DynamicDrawContext.h>
@ -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; //<! FontFamily pointer reverse-lookup for quick removal

@ -353,6 +353,18 @@ AZ::AtomFont::AtomFont(ISystem* system)
"Reload all fonts");
#endif
AZ::Interface<AzFramework::FontQueryInterface>::Register(this);
m_sceneEventHandler = AzFramework::ISceneSystem::SceneEvent::Handler(
[this](AzFramework::ISceneSystem::EventType eventType, const AZStd::shared_ptr<AzFramework::Scene>& 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<AZ::RPI::Scene>();
AZStd::lock_guard<AZStd::shared_mutex> lock(m_sceneToDynamicDrawMutex);
if ( auto it = m_sceneToDynamicDrawMap.find(rpiScene); it != m_sceneToDynamicDrawMap.end())
AZ::RPI::ScenePtr* rpiScene = scene.FindSubsystem<AZ::RPI::ScenePtr>();
if (rpiScene)
{
m_sceneToDynamicDrawMap.erase(it);
AZStd::lock_guard<AZStd::shared_mutex> lock(m_sceneToDynamicDrawMutex);
if (auto it = m_sceneToDynamicDrawMap.find(rpiScene->get()); it != m_sceneToDynamicDrawMap.end())
{
m_sceneToDynamicDrawMap.erase(it);
}
}
}

@ -25,7 +25,7 @@
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <AzCore/RTTI/BehaviorContext.h>

@ -27,7 +27,7 @@
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <AzCore/RTTI/BehaviorContext.h>

@ -25,7 +25,7 @@
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <AzCore/RTTI/BehaviorContext.h>

@ -14,7 +14,7 @@
#include <SkinnedMesh/SkinnedMeshDebugDisplay.h>
#include <Atom/Feature/SkinnedMesh/SkinnedMeshStatsBus.h>
#include <Atom/RPI.Public/Scene.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.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<AzFramework::Scene> m_frameworkScene;
RPI::RenderPipelinePtr m_renderPipeline;
AZStd::unique_ptr<AzFramework::EntityContext> m_entityContext;
AZStd::vector<AZStd::string> m_passHierarchy;

@ -100,24 +100,15 @@ namespace AZ
data->m_scene->SetShaderResourceGroupCallback(callback);
// Bind m_defaultScene to the GameEntityContext's AzFramework::Scene
Outcome<AzFramework::Scene*, AZStd::string> 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::shared_ptr<AzFramework::Scene>, 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";

@ -14,7 +14,7 @@
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/Scene.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemBus.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <Thumbnails/Rendering/ThumbnailRendererContext.h>
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.h>
@ -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;
}

Loading…
Cancel
Save