From 30bd997cb91f79e83f32ffaae0e7ce6a2f429463 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Fri, 24 Sep 2021 14:14:31 -0700 Subject: [PATCH 001/177] Fixed potential render scene time precision issues. The timestamp was simply converted from GetTimeAtCurrentTick to a float. Since this value is backed by QueryPerformanceCounter which is 0 at boot, you could see broken animations on the GPU when your system has been on for a long time. So I simplified the RPI's time API (removed unused code), and subtracted the application start time each frame before converting the time value to a float. Also moved FindShaderInputConstantIndex("m_time") to be called only once, instead of every frame. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../Code/Include/Atom/RPI.Public/RPISystem.h | 8 +++---- .../Include/Atom/RPI.Public/RenderPipeline.h | 3 +-- .../RPI/Code/Include/Atom/RPI.Public/Scene.h | 15 +++++-------- .../RPI/Code/Source/RPI.Public/RPISystem.cpp | 22 ++++++++++--------- .../Code/Source/RPI.Public/RenderPipeline.cpp | 4 ++-- .../Atom/RPI/Code/Source/RPI.Public/Scene.cpp | 17 +++++++------- 6 files changed, 33 insertions(+), 36 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h index 4914e4b6fe..c0e8f8105a 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h @@ -96,8 +96,7 @@ namespace AZ // SystemTickBus::OnTick void OnSystemTick() override; - // Fill system time and game time information for simulation or rendering - void FillTickTimeInfo(); + float GetCurrentTime(); // The set of core asset handlers registered by the system. AZStd::vector> m_assetHandlers; @@ -123,8 +122,9 @@ namespace AZ // The job policy used for feature processor's rendering prepare RHI::JobPolicy m_prepareRenderJobPolicy = RHI::JobPolicy::Parallel; - TickTimeInfo m_tickTime; - + ScriptTimePoint m_startTime; + float m_currentSimulationTime = 0.0f; + RPISystemDescriptor m_descriptor; // Reference to the shader asset that is used diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h index 90389687de..22ee18815b 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h @@ -32,7 +32,6 @@ namespace AZ namespace RPI { class Scene; - struct TickTimeInfo; class ShaderResourceGroup; class AnyAsset; class WindowContext; @@ -203,7 +202,7 @@ namespace AZ void OnRemovedFromScene(Scene* scene); // Called when this pipeline is about to be rendered - void OnStartFrame(const TickTimeInfo& tick); + void OnStartFrame(float time); // Called when the rendering of current frame is finished. void OnFrameEnd(); 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 b1c6aac92d..a711e78057 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h @@ -47,14 +47,6 @@ namespace AZ // Callback function to modify values of a ShaderResourceGroup using ShaderResourceGroupCallback = AZStd::function; - //! A structure for ticks which contains system time and game time. - struct TickTimeInfo - { - float m_currentGameTime; - float m_gameDeltaTime = 0; - }; - - class Scene final : public SceneRequestBus::Handler { @@ -173,12 +165,14 @@ namespace AZ // Cpu simulation which runs all active FeatureProcessor Simulate() functions. // @param jobPolicy if it's JobPolicy::Parallel, the function will spawn a job thread for each FeatureProcessor's simulation. - void Simulate(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy); + // @param simulationTime the number of seconds since the application started + void Simulate(RHI::JobPolicy jobPolicy, float simulationTime); // Collect DrawPackets from FeatureProcessors // @param jobPolicy if it's JobPolicy::Parallel, the function will spawn a job thread for each FeatureProcessor's // PrepareRender. - void PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy); + // @param simulationTime the number of seconds since the application started; this is the same time value that was passed to Simulate() + void PrepareRender(RHI::JobPolicy jobPolicy, float simulationTime); // Function called when the current frame is finished rendering. void OnFrameEnd(); @@ -240,6 +234,7 @@ namespace AZ // Registry which allocates draw filter tag for RenderPipeline RHI::Ptr m_drawFilterTagRegistry; + RHI::ShaderInputConstantIndex m_timeInputIndex; float m_simulationTime; }; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp index 5df1c655d6..662e932369 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp @@ -249,23 +249,25 @@ namespace AZ AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit); - // Update tick time info - FillTickTimeInfo(); + m_currentSimulationTime = GetCurrentTime(); for (auto& scene : m_scenes) { - scene->Simulate(m_tickTime, m_simulationJobPolicy); + scene->Simulate(m_simulationJobPolicy, m_currentSimulationTime); } } - void RPISystem::FillTickTimeInfo() + float RPISystem::GetCurrentTime() { - AZ::TickRequestBus::BroadcastResult(m_tickTime.m_gameDeltaTime, &AZ::TickRequestBus::Events::GetTickDeltaTime); - ScriptTimePoint currentTime; - AZ::TickRequestBus::BroadcastResult(currentTime, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick); - m_tickTime.m_currentGameTime = static_cast(currentTime.GetMilliseconds()); - } + ScriptTimePoint timeAtCurrentTick; + AZ::TickRequestBus::BroadcastResult(timeAtCurrentTick, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick); + // We subtract the start time to maximize precision of the time value, since we will be converting it to a float. + double currentTime = timeAtCurrentTick.GetSeconds() - m_startTime.GetSeconds(); + + return aznumeric_cast(currentTime); + } + void RPISystem::RenderTick() { if (!m_systemAssetsInitialized) @@ -282,7 +284,7 @@ namespace AZ // [GFX TODO] We may parallel scenes' prepare render. for (auto& scenePtr : m_scenes) { - scenePtr->PrepareRender(m_tickTime, m_prepareRenderJobPolicy); + scenePtr->PrepareRender(m_prepareRenderJobPolicy, m_currentSimulationTime); } m_rhiSystem.FrameUpdate( diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp index 1b99abae4e..f23c43af27 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp @@ -375,11 +375,11 @@ namespace AZ m_scene->RemoveRenderPipeline(m_nameId); } - void RenderPipeline::OnStartFrame(const TickTimeInfo& tick) + void RenderPipeline::OnStartFrame(float time) { AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame"); - m_lastRenderStartTime = tick.m_currentGameTime; + m_lastRenderStartTime = time; OnPassModified(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 646cba1999..fe75fe9c81 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -42,6 +42,9 @@ namespace AZ { auto shaderAsset = RPISystemInterface::Get()->GetCommonShaderAssetForSrgs(); scene->m_srg = ShaderResourceGroup::Create(shaderAsset, sceneSrgLayout->GetName()); + + // Set value for constants defined in SceneTimeSrg.azsli + scene->m_timeInputIndex = scene->m_srg->FindShaderInputConstantIndex(Name{ "m_time" }); } return ScenePtr(scene); @@ -346,11 +349,11 @@ namespace AZ return nullptr; } - void Scene::Simulate([[maybe_unused]] const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) + void Scene::Simulate(RHI::JobPolicy jobPolicy, float simulationTime) { AZ_PROFILE_SCOPE(RPI, "Scene: Simulate"); - m_simulationTime = tickInfo.m_currentGameTime; + m_simulationTime = simulationTime; // If previous simulation job wasn't done, wait for it to finish. WaitAndCleanCompletionJob(m_simulationCompletion); @@ -404,11 +407,9 @@ namespace AZ { if (m_srg) { - // Set value for constants defined in SceneTimeSrg.azsli - RHI::ShaderInputConstantIndex timeIndex = m_srg->FindShaderInputConstantIndex(Name{ "m_time" }); - if (timeIndex.IsValid()) + if (m_timeInputIndex.IsValid()) { - m_srg->SetConstant(timeIndex, m_simulationTime); + m_srg->SetConstant(m_timeInputIndex, m_simulationTime); } // signal any handlers to update values for their partial scene srg @@ -418,7 +419,7 @@ namespace AZ } } - void Scene::PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) + void Scene::PrepareRender(RHI::JobPolicy jobPolicy, float simulationTime) { AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender"); @@ -438,7 +439,7 @@ namespace AZ if (pipeline->NeedsRender()) { activePipelines.push_back(pipeline); - pipeline->OnStartFrame(tickInfo); + pipeline->OnStartFrame(simulationTime); } } } From 86a4b760761c3895b89d15d080b6e31b5779d7dd Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Thu, 30 Sep 2021 16:33:01 -0700 Subject: [PATCH 002/177] Fixed tabs to spaces Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h index c0e8f8105a..13d95dae41 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h @@ -123,7 +123,7 @@ namespace AZ RHI::JobPolicy m_prepareRenderJobPolicy = RHI::JobPolicy::Parallel; ScriptTimePoint m_startTime; - float m_currentSimulationTime = 0.0f; + float m_currentSimulationTime = 0.0f; RPISystemDescriptor m_descriptor; From 37243c74ec791132b4018b1301d760a1d4182147 Mon Sep 17 00:00:00 2001 From: nggieber Date: Thu, 28 Oct 2021 09:21:56 -0700 Subject: [PATCH 003/177] Make gem tags clickable and filter by their text in the Gem Catalog when clicked Signed-off-by: nggieber --- .../Source/GemCatalog/GemCatalogHeaderWidget.cpp | 5 +++++ .../Source/GemCatalog/GemCatalogHeaderWidget.h | 3 +++ .../Source/GemCatalog/GemCatalogScreen.cpp | 2 ++ .../ProjectManager/Source/GemCatalog/GemInspector.cpp | 1 + .../ProjectManager/Source/GemCatalog/GemInspector.h | 3 +++ Code/Tools/ProjectManager/Source/GemsSubWidget.cpp | 1 + Code/Tools/ProjectManager/Source/GemsSubWidget.h | 5 +++++ Code/Tools/ProjectManager/Source/TagWidget.cpp | 9 ++++++++- Code/Tools/ProjectManager/Source/TagWidget.h | 9 +++++++++ 9 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 5d65c740af..77b0e2b7d2 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -439,4 +439,9 @@ namespace O3DE::ProjectManager { m_filterLineEdit->setText({}); } + + void GemCatalogHeaderWidget::SetSearchFilter(const QString& filter) + { + m_filterLineEdit->setText(filter); + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index 4d17259840..66bd617fc4 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -86,6 +86,9 @@ namespace O3DE::ProjectManager void ReinitForProject(); + public slots: + void SetSearchFilter(const QString& filter); + signals: void AddGem(); void OpenGemsRepo(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index a22f41d054..deea46b582 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -54,6 +54,8 @@ namespace O3DE::ProjectManager m_gemInspector = new GemInspector(m_gemModel, this); m_gemInspector->setFixedWidth(240); + connect(m_gemInspector, &GemInspector::TagClicked, m_headerWidget, &GemCatalogHeaderWidget::SetSearchFilter); + QWidget* filterWidget = new QWidget(this); filterWidget->setFixedWidth(240); m_filterWidgetLayout = new QVBoxLayout(); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp index 7630e92e88..3d9a8f6c86 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp @@ -175,6 +175,7 @@ namespace O3DE::ProjectManager // Depending gems m_dependingGems = new GemsSubWidget(); + connect(m_dependingGems, &GemsSubWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); }); m_mainLayout->addWidget(m_dependingGems); m_mainLayout->addSpacing(20); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h index ca36cef240..38285577fd 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h @@ -40,6 +40,9 @@ namespace O3DE::ProjectManager inline constexpr static const char* s_headerColor = "#FFFFFF"; inline constexpr static const char* s_textColor = "#DDDDDD"; + signals: + void TagClicked(const QString& tag); + private slots: void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); diff --git a/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp b/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp index eb24008eb1..8b7b183008 100644 --- a/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp @@ -33,6 +33,7 @@ namespace O3DE::ProjectManager m_layout->addWidget(m_textLabel); m_tagWidget = new TagContainerWidget(); + connect(m_tagWidget, &TagContainerWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); }); m_layout->addWidget(m_tagWidget); } diff --git a/Code/Tools/ProjectManager/Source/GemsSubWidget.h b/Code/Tools/ProjectManager/Source/GemsSubWidget.h index 1b10ec8861..a9fabf5e92 100644 --- a/Code/Tools/ProjectManager/Source/GemsSubWidget.h +++ b/Code/Tools/ProjectManager/Source/GemsSubWidget.h @@ -22,10 +22,15 @@ namespace O3DE::ProjectManager class GemsSubWidget : public QWidget { + Q_OBJECT // AUTOMOC + public: GemsSubWidget(QWidget* parent = nullptr); void Update(const QString& title, const QString& text, const QStringList& gemNames); + signals: + void TagClicked(const QString& tag); + private: QLabel* m_titleLabel = nullptr; QLabel* m_textLabel = nullptr; diff --git a/Code/Tools/ProjectManager/Source/TagWidget.cpp b/Code/Tools/ProjectManager/Source/TagWidget.cpp index ace9d72d8f..39231ace4b 100644 --- a/Code/Tools/ProjectManager/Source/TagWidget.cpp +++ b/Code/Tools/ProjectManager/Source/TagWidget.cpp @@ -18,6 +18,11 @@ namespace O3DE::ProjectManager setObjectName("TagWidget"); } + void TagWidget::mousePressEvent([[maybe_unused]] QMouseEvent* event) + { + emit(TagClicked(text())); + } + TagContainerWidget::TagContainerWidget(QWidget* parent) : QWidget(parent) { @@ -45,7 +50,9 @@ namespace O3DE::ProjectManager foreach (const QString& tag, tags) { - flowLayout->addWidget(new TagWidget(tag)); + TagWidget* tagWidget = new TagWidget(tag); + connect(tagWidget, &TagWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); }); + flowLayout->addWidget(tagWidget); } } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/TagWidget.h b/Code/Tools/ProjectManager/Source/TagWidget.h index 0dad7468eb..4cda01b347 100644 --- a/Code/Tools/ProjectManager/Source/TagWidget.h +++ b/Code/Tools/ProjectManager/Source/TagWidget.h @@ -25,6 +25,12 @@ namespace O3DE::ProjectManager public: explicit TagWidget(const QString& text, QWidget* parent = nullptr); ~TagWidget() = default; + + signals: + void TagClicked(const QString& tag); + + protected: + void mousePressEvent(QMouseEvent* event) override; }; // Widget containing multiple tags, automatically wrapping based on the size @@ -38,5 +44,8 @@ namespace O3DE::ProjectManager ~TagContainerWidget() = default; void Update(const QStringList& tags); + + signals: + void TagClicked(const QString& tag); }; } // namespace O3DE::ProjectManager From 48f2487d3c45416f5ed3499abcf409090fac36dc Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Mon, 25 Oct 2021 13:43:00 -0700 Subject: [PATCH 004/177] Updates in preparation for adding entity aliases to spawnables. The following has been changed: - AssetDataStream can now return the stored streaming deadline and priority. - RootSpawnable now has an event that's called just before root spawnable spawns entities. This is an immediate event unlike the other events that are queued. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../AzCore/AzCore/Asset/AssetDataStream.h | 3 +++ .../Spawnable/RootSpawnableInterface.h | 10 ++++++++ .../Spawnable/SpawnableSystemComponent.cpp | 25 +++++++++++++++---- .../Spawnable/SpawnableSystemComponent.h | 1 + .../PrefabEditorEntityOwnershipService.cpp | 22 ++++++++-------- 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h index 62f5808207..d2b069b55b 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetDataStream.h @@ -70,6 +70,9 @@ namespace AZ::Data const char* GetFilename() const override { return m_filePath.c_str(); } + AZStd::chrono::milliseconds GetStreamingDeadline() const { return m_curDeadline; } + AZ::IO::IStreamerTypes::Priority GetStreamingPriority() const { return m_curPriority; } + // AssetDataStream specific APIs //! Whether or not all data has been loaded. diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h index 72a3031e3e..873123f38b 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h @@ -30,12 +30,22 @@ namespace AzFramework //! Called when the root spawnable has been assigned a new value. This may be called several times without a call to release //! in between. + //! NOTE: The callback is not queued but immediately called from a random thread. This is done because this callback is typically + //! used before entities are spawned and if it's queued then the entities spawn before this callback is called. //! @param rootSpawnable The new root spawnable that was assigned. //! @param generation The generation of the root spawnable. This will increment every time a new spawnable is assigned. virtual void OnRootSpawnableAssigned([[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) {} + //! Called when the root spawnable has completed spawning of entities. This may be called several times without a call to release + //! in between. + //! NOTE: This callback is queued and will be called with a delay and from the main thread. + //! @param rootSpawnable The new root spawnable that was used to spawn entities from. + //! @param generation The generation of the root spawnable. This will increment every time a new spawnable is assigned. + virtual void OnRootSpawnableReady( + [[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) {} //! Called when the root spawnable has Released. This will only be called if there's no root spawnable assigned to take the //! place of the original root spawnable. + //! Note: This callback is queued and will be called with a delay and from the main thread. //! @param generation The generation of the root spawnable that was released. virtual void OnRootSpawnableReleased([[maybe_unused]] uint32_t generation) {} }; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp index 957786c6df..6ca2f3a53a 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.cpp @@ -72,7 +72,7 @@ namespace AzFramework uint64_t SpawnableSystemComponent::AssignRootSpawnable(AZ::Data::Asset rootSpawnable) { - uint64_t generation = 0; + uint32_t generation = 0; if (m_rootSpawnableId == rootSpawnable.GetId()) { @@ -87,16 +87,25 @@ namespace AzFramework // Suspend and resume processing in the container that completion calls aren't received until // everything has been setup to accept callbacks from the call. m_rootSpawnableContainer.Reset(rootSpawnable); - m_rootSpawnableContainer.SpawnAllEntities(); generation = m_rootSpawnableContainer.GetCurrentGeneration(); - AZ_TracePrintf("Spawnables", "Root spawnable set to '%s' at generation %zu.\n", rootSpawnable.GetHint().c_str(), - generation); + + // Don't send out the alert that the root spawnable has been assigned until the spawnable itself is ready. The common + // use case is for handlers to do something with the information in the spawnable before the entities get spawned. + m_rootSpawnableContainer.Alert( + [rootSpawnable](uint32_t generation) + { + RootSpawnableNotificationBus::Broadcast( + &RootSpawnableNotificationBus::Events::OnRootSpawnableAssigned, AZStd::move(rootSpawnable), generation); + }, SpawnableEntitiesContainer::CheckIfSpawnableIsLoaded::Yes); + m_rootSpawnableContainer.SpawnAllEntities(); m_rootSpawnableContainer.Alert( [newSpawnable = AZStd::move(rootSpawnable)](uint32_t generation) { RootSpawnableNotificationBus::QueueBroadcast( - &RootSpawnableNotificationBus::Events::OnRootSpawnableAssigned, newSpawnable, generation); + &RootSpawnableNotificationBus::Events::OnRootSpawnableReady, AZStd::move(newSpawnable), generation); }); + + AZ_TracePrintf("Spawnables", "Root spawnable set to '%s' at generation %zu.\n", rootSpawnable.GetHint().c_str(), generation); } else { @@ -132,6 +141,12 @@ namespace AzFramework AZ_TracePrintf("Spawnables", "New root spawnable '%s' assigned (generation: %i).\n", rootSpawnable.GetHint().c_str(), generation); } + void SpawnableSystemComponent::OnRootSpawnableReady( + [[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) + { + AZ_TracePrintf("Spawnables", "Entities from new root spawnable '%s' are ready (generation: %i).\n", rootSpawnable.GetHint().c_str(), generation); + } + void SpawnableSystemComponent::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation) { AZ_TracePrintf("Spawnables", "Generation %i of the root spawnable has been released.\n", generation); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h index 74e255d624..ecd2a9b728 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableSystemComponent.h @@ -82,6 +82,7 @@ namespace AzFramework // void OnRootSpawnableAssigned(AZ::Data::Asset rootSpawnable, uint32_t generation) override; + void OnRootSpawnableReady(AZ::Data::Asset rootSpawnable, uint32_t generation) override; void OnRootSpawnableReleased(uint32_t generation) override; protected: diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp index 67b5d99011..97953bac18 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp @@ -581,15 +581,15 @@ namespace AzToolsFramework { if (m_rootInstance && m_playInEditorData.m_isEnabled) { - AZ_Assert(m_playInEditorData.m_entities.IsSet(), + AZ_Assert( + m_playInEditorData.m_entities.IsSet(), "Invalid Game Mode Entities Container encountered after play-in-editor stopped. " "Confirm that the container was initialized correctly"); m_playInEditorData.m_entities.DespawnAllEntities(); m_playInEditorData.m_entities.Alert( [assets = AZStd::move(m_playInEditorData.m_assets), - deactivatedEntities = AZStd::move(m_playInEditorData.m_deactivatedEntities)] - ([[maybe_unused]]uint32_t generation) mutable + deactivatedEntities = AZStd::move(m_playInEditorData.m_deactivatedEntities)]([[maybe_unused]] uint32_t generation) mutable { auto end = deactivatedEntities.rend(); for (auto it = deactivatedEntities.rbegin(); it != end; ++it) @@ -614,15 +614,15 @@ namespace AzToolsFramework AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnGameEntitiesReset); }); m_playInEditorData.m_entities.Clear(); - } - // Game entity cleanup is queued onto the next tick via the DespawnEntities call. - // To avoid both game entities and Editor entities active at the same time - // we flush the tick queue to ensure the game entities are cleared first. - // The Alert callback that follows the DespawnEntities call will then reactivate the editor entities - // This should be considered temporary as a move to a less rigid event sequence that supports async entity clean up - // is the desired direction forward. - AZ::TickBus::ExecuteQueuedEvents(); + // Game entity cleanup is queued onto the next tick via the DespawnEntities call. + // To avoid both game entities and Editor entities active at the same time + // we flush the tick queue to ensure the game entities are cleared first. + // The Alert callback that follows the DespawnEntities call will then reactivate the editor entities + // This should be considered temporary as a move to a less rigid event sequence that supports async entity clean up + // is the desired direction forward. + AZ::TickBus::ExecuteQueuedEvents(); + } m_playInEditorData.m_isEnabled = false; } From a0d7048fd4dced2fa0b216aff2a9cae8f1bf9cc5 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Mon, 25 Oct 2021 14:07:14 -0700 Subject: [PATCH 005/177] Added support for entity aliases to Spawnable. Entity aliases can be used to have a request to spawn an entity: - spawn the original entity as normal - be disabled - redirected to another entity in another spawnable - also spawn an entity from another spawnable - add the components from an entity in another spawnable An entity alias can indicate whether or not to load the spawnable dependency. If the spawnable dependency is loaded it will be loaded asynchronously because starting blocking loads in an asset handler can lead to deadlocks once there are no more jobs available to deserialize assets. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../AzFramework/Spawnable/Spawnable.cpp | 494 +++++++++++++++++- .../AzFramework/Spawnable/Spawnable.h | 152 +++++- .../AzFramework/Spawnable/SpawnableAssetBus.h | 38 ++ .../Spawnable/SpawnableAssetHandler.cpp | 39 ++ .../Spawnable/SpawnableAssetHandler.h | 8 + .../AzFramework/azframework_files.cmake | 1 + 6 files changed, 727 insertions(+), 5 deletions(-) create mode 100644 Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetBus.h diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp index 4855dc15b3..2be76c28a6 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp @@ -8,10 +8,447 @@ #include #include +#include #include namespace AzFramework { + // + // EntityAlias + // + + + bool Spawnable::EntityAlias::HasLowerIndex(const EntityAlias& other) const + { + return m_sourceIndex == other.m_sourceIndex ? + m_aliasType < other.m_aliasType : + m_sourceIndex < other.m_sourceIndex; + } + + + // + // EntityAliasVisitorBase + // + + bool Spawnable::EntityAliasVisitorBase::HasLock(const EntityAliasList* aliases) const + { + return aliases != nullptr; + } + + bool Spawnable::EntityAliasVisitorBase::HasAliases(const EntityAliasList* aliases) const + { + AZ_Assert(aliases, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + return !aliases->empty(); + } + + bool Spawnable::EntityAliasVisitorBase::AreAllSpawnablesReady(const EntityAliasList* aliases) const + { + AZ_Assert(aliases, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + for (const EntityAlias& alias : *aliases) + { + if ((alias.m_aliasType != Spawnable::EntityAliasType::Original && alias.m_aliasType != Spawnable::EntityAliasType::Disabled) && + !alias.m_spawnable.IsReady()) + { + return false; + } + } + return true; + } + + auto Spawnable::EntityAliasVisitorBase::begin(const EntityAliasList* aliases) const -> EntityAliasList::const_iterator + { + AZ_Assert(aliases, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + return aliases->cbegin(); + } + + auto Spawnable::EntityAliasVisitorBase::end(const EntityAliasList* aliases) const -> EntityAliasList::const_iterator + { + AZ_Assert(aliases, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + return aliases->cend(); + } + + auto Spawnable::EntityAliasVisitorBase::cbegin(const EntityAliasList* aliases) const -> EntityAliasList::const_iterator + { + AZ_Assert(aliases, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + return aliases->cbegin(); + } + + auto Spawnable::EntityAliasVisitorBase::cend(const EntityAliasList* aliases) const -> EntityAliasList::const_iterator + { + AZ_Assert(aliases, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + return aliases->cend(); + } + + void Spawnable::EntityAliasVisitorBase::ListTargetSpawnables( + const EntityAliasList* aliases, const ListTargetSpawanblesCallback& callback) const + { + AZ_Assert(aliases, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + AZStd::unordered_set spawnableIds; + for (const Spawnable::EntityAlias& alias : *aliases) + { + auto it = spawnableIds.find(alias.m_spawnable.GetId()); + if (it == spawnableIds.end()) + { + callback(alias.m_spawnable); + spawnableIds.emplace(alias.m_spawnable.GetId()); + } + } + } + + void Spawnable::EntityAliasVisitorBase::ListTargetSpawnables( + const EntityAliasList* aliases, AZ::Crc32 tag, const ListTargetSpawanblesCallback& callback) const + { + AZ_Assert(aliases, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + AZStd::unordered_set spawnableIds; + for (const Spawnable::EntityAlias& alias : *aliases) + { + if (alias.m_tag == tag) + { + auto it = spawnableIds.find(alias.m_spawnable.GetId()); + if (it == spawnableIds.end()) + { + callback(alias.m_spawnable); + spawnableIds.emplace(alias.m_spawnable.GetId()); + } + } + } + } + + + // + // EntityAliasVisitor + // + + + Spawnable::EntityAliasVisitor::EntityAliasVisitor(Spawnable& owner, EntityAliasList* entityAliasList) + : m_owner(owner) + , m_entityAliasList(entityAliasList) + { + } + + Spawnable::EntityAliasVisitor::~EntityAliasVisitor() + { + if (HasLock()) + { + Optimize(); + + AZ_Assert( + m_owner.m_lockState == LockState::Locked, "Attempting to unlock a spawnable that's not in the locked state (%i).", + m_owner.m_lockState.load()); + m_owner.m_lockState = LockState::Unlocked; + } + } + + Spawnable::EntityAliasVisitor::EntityAliasVisitor(EntityAliasVisitor&& rhs) + : m_owner(rhs.m_owner) + , m_entityAliasList(rhs.m_entityAliasList) + { + m_dirty = rhs.m_dirty; + + rhs.m_entityAliasList = nullptr; + rhs.m_dirty = false; + } + + auto Spawnable::EntityAliasVisitor::operator=(EntityAliasVisitor&& rhs) -> EntityAliasVisitor& + { + if (this != &rhs) + { + this->~EntityAliasVisitor(); + *this = EntityAliasVisitor(rhs.m_owner, rhs.m_entityAliasList); + m_dirty = rhs.m_dirty; + + rhs.m_entityAliasList = nullptr; + rhs.m_dirty = false; + } + return *this; + } + + bool Spawnable::EntityAliasVisitor::HasLock() const + { + return EntityAliasVisitorBase::HasLock(m_entityAliasList); + } + + bool Spawnable::EntityAliasVisitor::HasAliases() const + { + return EntityAliasVisitorBase::HasAliases(m_entityAliasList); + } + + bool Spawnable::EntityAliasVisitor::AreAllSpawnablesReady() const + { + return EntityAliasVisitorBase::AreAllSpawnablesReady(m_entityAliasList); + } + + auto Spawnable::EntityAliasVisitor::begin() const -> EntityAliasList::const_iterator + { + return EntityAliasVisitorBase::begin(m_entityAliasList); + } + + auto Spawnable::EntityAliasVisitor::end() const -> EntityAliasList::const_iterator + { + return EntityAliasVisitorBase::end(m_entityAliasList); + } + + auto Spawnable::EntityAliasVisitor::cbegin() const -> EntityAliasList::const_iterator + { + return EntityAliasVisitorBase::cbegin(m_entityAliasList); + } + + auto Spawnable::EntityAliasVisitor::cend() const -> EntityAliasList::const_iterator + { + return EntityAliasVisitorBase::cend(m_entityAliasList); + } + + void Spawnable::EntityAliasVisitor::ListTargetSpawnables(const ListTargetSpawanblesCallback& callback) const + { + EntityAliasVisitorBase::ListTargetSpawnables(m_entityAliasList, callback); + } + + void Spawnable::EntityAliasVisitor::ListTargetSpawnables(AZ::Crc32 tag, const ListTargetSpawanblesCallback& callback) const + { + EntityAliasVisitorBase::ListTargetSpawnables(m_entityAliasList, tag, callback); + } + + void Spawnable::EntityAliasVisitor::AddAlias( + AZ::Data::Asset targetSpawnable, + AZ::Crc32 tag, + uint32_t sourceIndex, + uint32_t targetIndex, + Spawnable::EntityAliasType aliasType, + bool queueLoad) + { + AZ_Assert(m_entityAliasList, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + AZ_Assert(sourceIndex < m_owner.GetEntities().size(), "Invalid source index (%i) for entity alias", sourceIndex); + if (targetSpawnable.IsReady()) + { + AZ_Assert( + targetIndex < targetSpawnable->GetEntities().size(), "Invalid target index (%i) for entity alias '%s'", targetIndex, + targetSpawnable.GetHint().c_str()); + } + + m_entityAliasList->push_back(Spawnable::EntityAlias{ targetSpawnable, tag, sourceIndex, targetIndex, aliasType, queueLoad }); + m_dirty = true; + } + + void Spawnable::EntityAliasVisitor::ListSpawnablesPendingLoad(const ListSpawnablesPendingLoadCallback& callback) + { + AZ_Assert(m_entityAliasList, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + for (Spawnable::EntityAlias& alias : *m_entityAliasList) + { + if (alias.m_queueLoad && + alias.m_aliasType != Spawnable::EntityAliasType::Original && + alias.m_aliasType != Spawnable::EntityAliasType::Disabled && + !alias.m_spawnable.IsLoading() && + !alias.m_spawnable.IsReady() && + !alias.m_spawnable.IsError()) + { + callback(alias.m_spawnable); + } + } + } + + void Spawnable::EntityAliasVisitor::UpdateAliases(const UpdateCallback& callback) + { + AZ_Assert(m_entityAliasList, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + for (Spawnable::EntityAlias& alias : *m_entityAliasList) + { + AZ::Data::Asset targetSpawnable(alias.m_spawnable); + callback(alias.m_aliasType, alias.m_queueLoad, targetSpawnable, alias.m_tag, alias.m_sourceIndex, alias.m_targetIndex); + } + m_dirty = true; + } + + void Spawnable::EntityAliasVisitor::UpdateAliases(AZ::Crc32 tag, const UpdateCallback& callback) + { + AZ_Assert(m_entityAliasList, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + for (Spawnable::EntityAlias& alias : *m_entityAliasList) + { + if (alias.m_tag == tag) + { + AZ::Data::Asset targetSpawnable(alias.m_spawnable); + callback(alias.m_aliasType, alias.m_queueLoad, targetSpawnable, alias.m_tag, alias.m_sourceIndex, alias.m_targetIndex); + m_dirty = true; + } + } + } + + void Spawnable::EntityAliasVisitor::UpdateAliasType(uint32_t index, Spawnable::EntityAliasType newType) + { + AZ_Assert(m_entityAliasList, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + AZ_Assert( + index < m_entityAliasList->size(), "Unable to update entity alias at index %i as there are only %zu aliases in spawnable.", + index, m_entityAliasList->size()); + (*m_entityAliasList)[index].m_aliasType = newType; + m_dirty = true; + } + + void Spawnable::EntityAliasVisitor::Optimize() + { + AZ_Assert(m_entityAliasList, "Attempting to visit entity aliases on a spawnable that wasn't locked."); + if (m_dirty) + { + AZStd::stable_sort( + m_entityAliasList->begin(), m_entityAliasList->end(), + [](const Spawnable::EntityAlias& lhs, const Spawnable::EntityAlias& rhs) + { + // Sort by source index from smallest to largest so during spawning the entities can be iterated linearly over. + // If the source index is the same then sort by alias type so the next steps can optimize away superfluous steps. + return lhs.HasLowerIndex(rhs); + }); + + // Remove aliases that are not going to have any practical effect and insert aliases where needed to simplify the spawning. + // This is done at runtime rather than at build time because the above ebus allows other systems to make adjustments to the + // aliases, for instance Networking can decide to disable certain aliases when running on a client. This in turn also requires + // the aliases to be in their recorded order during building as the ebus handlers may depend on that order to determine what + // entities need to be updated. + Spawnable::EntityAlias* compare = m_entityAliasList->begin(); + Spawnable::EntityAlias* it = m_entityAliasList->begin() + 1; + Spawnable::EntityAlias* end = m_entityAliasList->end(); + while (it < end) + { + switch (it->m_aliasType) + { + case Spawnable::EntityAliasType::Original: + // If this is the only alias for the entity then the original can be removed. + { + Spawnable::EntityAlias* next = it + 1; + if (next == end || next->m_sourceIndex != it->m_sourceIndex) + { + // Erase instead of a swap-and-pop in order to preserver the order. + m_entityAliasList->erase(compare); + --end; + break; + } + } + [[fallthrough]]; + case Spawnable::EntityAliasType::Disabled: + [[fallthrough]]; + case Spawnable::EntityAliasType::Replace: + // If the previous entry was a disabled, original or replace alias then remove it as it will be overwritten by the + // current entry. + if (compare->m_sourceIndex == it->m_sourceIndex && + (compare->m_aliasType == Spawnable::EntityAliasType::Original || + compare->m_aliasType == Spawnable::EntityAliasType::Disabled || + compare->m_aliasType == Spawnable::EntityAliasType::Replace)) + { + // Erase instead of a swap-and-pop in order to preserver the order. + m_entityAliasList->erase(compare); + --end; + } + else + { + ++compare; + ++it; + } + break; + case Spawnable::EntityAliasType::Additional: + [[fallthrough]]; + case Spawnable::EntityAliasType::Merge: + // If this is the first entry for this type insert an original in front of it so the spawnable entity manager + // does have to check for the case there's a merge and/or addition without a prefix. + if (compare->m_sourceIndex != it->m_sourceIndex) + { + Spawnable::EntityAlias insert; + // No load, as the asset is already loaded. + insert.m_spawnable = AZ::Data::Asset(&m_owner, AZ::Data::AssetLoadBehavior::NoLoad); + insert.m_sourceIndex = it->m_sourceIndex; + insert.m_targetIndex = it->m_sourceIndex; // Source index as the original entry for this slot is added. + insert.m_aliasType = Spawnable::EntityAliasType::Original; + m_entityAliasList->insert(compare, AZStd::move(insert)); + compare += 2; + it += 2; + ++end; + } + else + { + ++compare; + ++it; + } + break; + default: + AZ_Assert(false, "Invalid Spawnable entity alias type found during asset loading: %i", compare->m_aliasType); + break; + } + } + // Reclaim memory because after this point the aliases will not change anymore. + m_entityAliasList->shrink_to_fit(); + m_dirty = false; + } + } + + + + // + // EntityAliasConstVisitor + // + + Spawnable::EntityAliasConstVisitor::EntityAliasConstVisitor(const Spawnable& owner, const EntityAliasList* entityAliasList) + : m_owner(owner) + , m_entityAliasList(entityAliasList) + { + } + + Spawnable::EntityAliasConstVisitor::~EntityAliasConstVisitor() + { + if (HasLock()) + { + AZ_Assert( + m_owner.m_lockState < 0, "Attempting to unlock a read shared spawnable that was not in a read shared mode (%i).", + m_owner.m_lockState.load()); + m_owner.m_lockState++; + } + } + + bool Spawnable::EntityAliasConstVisitor::HasLock() const + { + return EntityAliasVisitorBase::HasLock(m_entityAliasList); + } + + bool Spawnable::EntityAliasConstVisitor::HasAliases() const + { + return EntityAliasVisitorBase::HasAliases(m_entityAliasList); + } + + bool Spawnable::EntityAliasConstVisitor::AreAllSpawnablesReady() const + { + return EntityAliasVisitorBase::AreAllSpawnablesReady(m_entityAliasList); + } + + auto Spawnable::EntityAliasConstVisitor::begin() const -> EntityAliasList::const_iterator + { + return EntityAliasVisitorBase::begin(m_entityAliasList); + } + + auto Spawnable::EntityAliasConstVisitor::end() const -> EntityAliasList::const_iterator + { + return EntityAliasVisitorBase::end(m_entityAliasList); + } + + auto Spawnable::EntityAliasConstVisitor::cbegin() const -> EntityAliasList::const_iterator + { + return EntityAliasVisitorBase::cbegin(m_entityAliasList); + } + + auto Spawnable::EntityAliasConstVisitor::cend() const -> EntityAliasList::const_iterator + { + return EntityAliasVisitorBase::cend(m_entityAliasList); + } + + void Spawnable::EntityAliasConstVisitor::ListTargetSpawnables(const ListTargetSpawanblesCallback& callback) const + { + EntityAliasVisitorBase::ListTargetSpawnables(m_entityAliasList, callback); + } + + void Spawnable::EntityAliasConstVisitor::ListTargetSpawnables(AZ::Crc32 tag, const ListTargetSpawanblesCallback& callback) const + { + EntityAliasVisitorBase::ListTargetSpawnables(m_entityAliasList, tag, callback); + } + + + + // + // Spawnable + // + Spawnable::Spawnable(const AZ::Data::AssetId& id, AssetStatus status) : AZ::Data::AssetData(id, status) { @@ -27,11 +464,56 @@ namespace AzFramework return m_entities; } + auto Spawnable::TryGetAliasesConst() const -> EntityAliasConstVisitor + { + int32_t expected = LockState::Unlocked; + do + { + // Try to set the lock to a negative number to indicate a shared read. + if (m_lockState.compare_exchange_strong(expected, expected - 1)) + { + return EntityAliasConstVisitor(*this, &m_entityAliases); + } + // as long as the value is negative keep trying to get a shared read lock. + } while (expected <= 0); + return EntityAliasConstVisitor(*this, nullptr); + } + + auto Spawnable::TryGetAliases() const -> EntityAliasConstVisitor + { + return TryGetAliasesConst(); + } + + auto Spawnable::TryGetAliases() -> EntityAliasVisitor + { + int32_t expected = LockState::Unlocked; + return m_lockState.compare_exchange_strong(expected, LockState::Locked) ? EntityAliasVisitor(*this, &m_entityAliases) + : EntityAliasVisitor(*this, nullptr); + } + bool Spawnable::IsEmpty() const { return m_entities.empty(); } + bool Spawnable::IsPermanentlyLocked() const + { + return m_lockState == LockState::PermanentLock; + } + + bool Spawnable::LockPermanently() + { + if (!IsPermanentlyLocked()) + { + int32_t expected = LockState::Unlocked; + return m_lockState.compare_exchange_strong(expected, LockState::PermanentLock); + } + else + { + return true; + } + } + SpawnableMetaData& Spawnable::GetMetaData() { return m_metaData; @@ -46,8 +528,18 @@ namespace AzFramework { if (auto* serializeContext = azrtti_cast(context); serializeContext != nullptr) { - serializeContext->Class()->Version(1) + serializeContext->Class() + ->Version(1) + ->Field("Spawnable", &Spawnable::EntityAlias::m_spawnable) + ->Field("Tag", &Spawnable::EntityAlias::m_tag) + ->Field("Source Index", &Spawnable::EntityAlias::m_sourceIndex) + ->Field("Target Index", &Spawnable::EntityAlias::m_targetIndex) + ->Field("Alias Type", &Spawnable::EntityAlias::m_aliasType) + ->Field("Queue Load", &Spawnable::EntityAlias::m_queueLoad); + + serializeContext->Class()->Version(2) ->Field("Meta data", &Spawnable::m_metaData) + ->Field("Entity aliases", &Spawnable::m_entityAliases) ->Field("Entities", &Spawnable::m_entities); } } diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h index 37c22d503d..05ae10e580 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,139 @@ namespace AzFramework AZ_CLASS_ALLOCATOR(Spawnable, AZ::SystemAllocator, 0); AZ_RTTI(AzFramework::Spawnable, "{855E3021-D305-4845-B284-20C3F7FDF16B}", AZ::Data::AssetData); + // The order is important for sorting in the SpawnableAssetHandler. + enum class EntityAliasType : uint8_t + { + Original, //!< The original entity is spawned. + Disabled, //!< No entity will be spawned. + Replace, //!< The entity alias is spawned instead of the original. + Additional, //!< The original entity is spawned as well as the alias. The alias will get a new entity id. + Merge //!< The original entity is spawned and the components of the alias are added. The caller is responsible for + //!< maintaining a valid component list. + }; + + enum LockState : int32_t + { + Unlocked, + Locked, + PermanentLock + }; + + //! An entity alias redirects the spawning of an entity to another entity, possibly in another spawnable. + struct EntityAlias + { + AZ_CLASS_ALLOCATOR(EntityAlias, AZ::SystemAllocator, 0); + AZ_TYPE_INFO(AzFramework::Spawnable::EntityAlias, "{C8D0C5BC-1F0B-4572-98C1-73B2CA8C9356}"); + + bool HasLowerIndex(const EntityAlias& other) const; + + AZ::Data::Asset m_spawnable; //!< The spawnable containing the target entity to spawn. + uint32_t m_tag{ 0 }; //!< A unique tag to identify this alias with. + uint32_t m_sourceIndex{ 0 }; //!< The index of the entity in the original spawnable that will be replaced. + uint32_t m_targetIndex{ 0 }; //!< The index of the entity in the target spawnable that will be used to replace the original. + EntityAliasType m_aliasType{ EntityAliasType::Original }; //!< The kind of replacement. + bool m_queueLoad{ false }; //!< Whether or not to automatically queue the spawnable for loading. + }; + using EntityList = AZStd::vector>; + using EntityAliasList = AZStd::vector; + + private: + class EntityAliasVisitorBase + { + protected: + bool HasLock(const EntityAliasList* aliases) const; + bool HasAliases(const EntityAliasList* aliases) const; + bool AreAllSpawnablesReady(const EntityAliasList* aliases) const; + + EntityAliasList::const_iterator begin(const EntityAliasList* aliases) const; + EntityAliasList::const_iterator end(const EntityAliasList* aliases) const; + EntityAliasList::const_iterator cbegin(const EntityAliasList* aliases) const; + EntityAliasList::const_iterator cend(const EntityAliasList* aliases) const; + + using ListTargetSpawanblesCallback = AZStd::function& targetSpawnable)>; + void ListTargetSpawnables(const EntityAliasList* aliases, const ListTargetSpawanblesCallback& callback) const; + void ListTargetSpawnables(const EntityAliasList* aliases, AZ::Crc32 tag, const ListTargetSpawanblesCallback& callback) const; + }; + + public: + class EntityAliasVisitor final : public EntityAliasVisitorBase + { + public: + EntityAliasVisitor(Spawnable& owner, EntityAliasList* m_entityAliasList); + ~EntityAliasVisitor(); + + EntityAliasVisitor(EntityAliasVisitor&& rhs); + EntityAliasVisitor& operator=(EntityAliasVisitor&& rhs); + + EntityAliasVisitor(const EntityAliasVisitor& rhs) = delete; + EntityAliasVisitor& operator=(const EntityAliasVisitor& rhs) = delete; + + bool HasLock() const; + bool HasAliases() const; + bool AreAllSpawnablesReady() const; + + EntityAliasList::const_iterator begin() const; + EntityAliasList::const_iterator end() const; + EntityAliasList::const_iterator cbegin() const; + EntityAliasList::const_iterator cend() const; + + void ListTargetSpawnables(const ListTargetSpawanblesCallback& callback) const; + void ListTargetSpawnables(AZ::Crc32 tag, const ListTargetSpawanblesCallback& callback) const; + + void AddAlias( + AZ::Data::Asset targetSpawnable, + AZ::Crc32 tag, + uint32_t sourceIndex, + uint32_t targetIndex, + Spawnable::EntityAliasType aliasType, + bool queueLoad); + + using ListSpawnablesPendingLoadCallback = AZStd::function& spawnablePendingLoad)>; + void ListSpawnablesPendingLoad(const ListSpawnablesPendingLoadCallback& callback); + + using UpdateCallback = AZStd::function& aliasedSpawnable, + const AZ::Crc32 tag, + const uint32_t sourceIndex, + const uint32_t targetIndex)>; + void UpdateAliases(const UpdateCallback& callback); + void UpdateAliases(AZ::Crc32 tag, const UpdateCallback& callback); + void UpdateAliasType(uint32_t index, Spawnable::EntityAliasType newType); + + void Optimize(); + + private: + Spawnable& m_owner; + EntityAliasList* m_entityAliasList{ nullptr }; + bool m_dirty{ false }; + }; + + class EntityAliasConstVisitor final : public EntityAliasVisitorBase + { + public: + EntityAliasConstVisitor(const Spawnable& owner, const EntityAliasList* m_entityAliasList); + ~EntityAliasConstVisitor(); + + bool HasLock() const; + bool HasAliases() const; + bool AreAllSpawnablesReady() const; + + EntityAliasList::const_iterator begin() const; + EntityAliasList::const_iterator end() const; + EntityAliasList::const_iterator cbegin() const; + EntityAliasList::const_iterator cend() const; + + void ListTargetSpawnables(const ListTargetSpawanblesCallback& callback) const; + void ListTargetSpawnables(AZ::Crc32 tag, const ListTargetSpawanblesCallback& callback) const; + + private: + const Spawnable& m_owner; + const EntityAliasList* m_entityAliasList; + + }; inline static constexpr const char* FileExtension = "spawnable"; inline static constexpr const char* DotFileExtension = ".spawnable"; @@ -39,14 +172,24 @@ namespace AzFramework Spawnable(const Spawnable& rhs) = delete; Spawnable(Spawnable&& other) = delete; ~Spawnable() override = default; - + Spawnable& operator=(const Spawnable& rhs) = delete; Spawnable& operator=(Spawnable&& other) = delete; const EntityList& GetEntities() const; EntityList& GetEntities(); + EntityAliasConstVisitor TryGetAliasesConst() const; + EntityAliasConstVisitor TryGetAliases() const; + EntityAliasVisitor TryGetAliases(); bool IsEmpty() const; + //! Whether or not the spawnable is permanently locked. If so then parts of the spawnable can no longer be modified. + bool IsPermanentlyLocked() const; + //! Permanently locks access to parts of the spawnable from being modified. + //! @return True if the spawnable could be locked. If false is returned another operation is still making modifications. In this case + //! call this again at a later point in time. + bool LockPermanently(); + SpawnableMetaData& GetMetaData(); const SpawnableMetaData& GetMetaData() const; @@ -55,11 +198,12 @@ namespace AzFramework private: SpawnableMetaData m_metaData; + // Aliases that optionally replace the ones stored in this spawnable. + EntityAliasList m_entityAliases; // Container for keeping all entities of the prefab the Spawnable was created from. // Includes both direct and nested entities of the prefab. EntityList m_entities; + + mutable AZStd::atomic m_lockState{ LockState::Unlocked }; }; - - using SpawnableList = AZStd::vector; - } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetBus.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetBus.h new file mode 100644 index 0000000000..d3d7bfabb7 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetBus.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +namespace AzFramework +{ + class SpawnableAssetEvents : public AZ::EBusTraits + { + public: + using MutexType = AZStd::recursive_mutex; + + //! Callback to allow the entity aliases in a spawnable to adjusted based on runtime requirements. + //! This will be called by the Asset Manager as part of the creation of the spawnable asset from loaded file data. Any work done + //! in this callback will be counted towards the maximum amount of time allocated to asset handlers to construct their assets, + //! it's recommended to keep work done in this callback to a minimum and prefer delaying any complex processing. + //! + //! ALERT: Do not start blocking asset requests in this callback. + //! Since this is part of the Asset Manager's asset streaming, doing a blocking load in this callback will cause the job + //! processing the spawnable asset to locked out of doing any asset streaming work. If there are more spawnables doing + //! this than there are job threads available the engine will enter a deadlock situation as no more assets can complete + //! loading and no job threads become free as they're all waiting for assets to complete. It is however safe to queue + //! an asset for loading. + virtual void OnResolveAliases( + Spawnable::EntityAliasVisitor& aliases, const SpawnableMetaData& metadata, const Spawnable::EntityList& entities) = 0; + }; + + using SpawnableAssetEventsBus = AZ::EBus; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp index c24b538de7..bf4d350a69 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp @@ -9,8 +9,10 @@ #include #include #include +#include #include #include +#include namespace AzFramework { @@ -52,6 +54,7 @@ namespace AzFramework AZ::ObjectStream::FilterDescriptor filter(assetLoadFilterCB); if (AZ::Utils::LoadObjectFromStreamInPlace(*stream, *spawnable, nullptr /*SerializeContext*/, filter)) { + ResolveEntityAliases(spawnable, asset, stream->GetStreamingDeadline(), stream->GetStreamingPriority(), assetLoadFilterCB); return AZ::Data::AssetHandler::LoadResult::LoadComplete; } else @@ -91,4 +94,40 @@ namespace AzFramework AZ::Uuid subIdHash = AZ::Uuid::CreateData(id.data(), id.size()); return azlossy_caster(subIdHash.GetHash()); } + + void SpawnableAssetHandler::ResolveEntityAliases( + Spawnable* spawnable, + const AZ::Data::Asset& asset, + AZStd::chrono::milliseconds streamingDeadline, + AZ::IO::IStreamerTypes::Priority streamingPriority, + const AZ::Data::AssetFilterCB& assetLoadFilterCB) + { + Spawnable::EntityAliasVisitor aliases = spawnable->TryGetAliases(); + AZ_Assert(aliases.HasLock(), "Newly created Spawnable '%s' was already locked.", asset.GetHint().c_str()); + if (aliases.HasAliases()) + { + AZ_Assert( + AZStd::is_sorted( + aliases.begin(), aliases.end(), + [](const Spawnable::EntityAlias& lhs, const Spawnable::EntityAlias& rhs) + { + return lhs.HasLowerIndex(rhs); + }), + "Spawnable '%s' has an unsorted entity alias list.", asset.GetHint().c_str()); + + SpawnableAssetEventsBus::Broadcast( + &SpawnableAssetEvents::OnResolveAliases, aliases, spawnable->GetMetaData(), spawnable->GetEntities()); + + aliases.Optimize(); + aliases.ListSpawnablesPendingLoad( + [&assetLoadFilterCB, streamingDeadline, streamingPriority](AZ::Data::Asset& assetPendingLoad) + { + AZ::Data::AssetLoadParameters loadInfo; + loadInfo.m_assetLoadFilterCB = assetLoadFilterCB; + loadInfo.m_deadline = streamingDeadline; + loadInfo.m_priority = streamingPriority; + assetPendingLoad.QueueLoad(loadInfo); + }); + } + } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.h index 94ec9b13fd..e043019e29 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.h @@ -50,5 +50,13 @@ namespace AzFramework const AZ::Data::Asset& asset, AZStd::shared_ptr stream, const AZ::Data::AssetFilterCB& assetLoadFilterCB) override; + + private: + void ResolveEntityAliases( + class Spawnable* spawnable, + const AZ::Data::Asset& asset, + AZStd::chrono::milliseconds streamingDeadline, + AZ::IO::IStreamerTypes::Priority streamingPriority, + const AZ::Data::AssetFilterCB& assetLoadFilterCB); }; } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake index e03d166cfc..22fbddb39a 100644 --- a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake +++ b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake @@ -286,6 +286,7 @@ set(FILES Spawnable/RootSpawnableInterface.h Spawnable/Spawnable.cpp Spawnable/Spawnable.h + Spawnable/SpawnableAssetBus.h Spawnable/SpawnableAssetHandler.h Spawnable/SpawnableAssetHandler.cpp Spawnable/SpawnableEntitiesContainer.h From a05d5f5d6dbc18ed85654b853fe9b815ebd70b8e Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Mon, 25 Oct 2021 14:35:49 -0700 Subject: [PATCH 006/177] Extended the Spawnable Entities Interface to allow entity aliases to be updated. Entity aliases can now be updated as a reaction to the spawnable being loaded or at any other time afterwards through the Spawnable Entities Interface. Currently these changes are applied to the spawnable that owns the entity aliases, but once the Spawnable Entities Interface makes use of AzFramework::Scene a copy of the entity aliases should be stored in the scene and be updated instead of the spawnable. This change also adds support for a load barrier, which acts the same as a regular barrier but also accounts for the spawnable being loaded and won't trigger the callback until has completed. The return values in from the processing functions in the Spawnable Entities Manager now have a clearer return value to indicate whether a request has completed or is being re-queued. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../AzFramework/Spawnable/Spawnable.cpp | 9 +- .../Spawnable/SpawnableEntitiesContainer.cpp | 24 +- .../Spawnable/SpawnableEntitiesContainer.h | 16 +- .../Spawnable/SpawnableEntitiesInterface.cpp | 8 +- .../Spawnable/SpawnableEntitiesInterface.h | 64 +- .../Spawnable/SpawnableEntitiesManager.cpp | 602 +++++++++++++----- .../Spawnable/SpawnableEntitiesManager.h | 90 ++- 7 files changed, 595 insertions(+), 218 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp index 2be76c28a6..92728a4575 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp @@ -46,8 +46,13 @@ namespace AzFramework AZ_Assert(aliases, "Attempting to visit entity aliases on a spawnable that wasn't locked."); for (const EntityAlias& alias : *aliases) { - if ((alias.m_aliasType != Spawnable::EntityAliasType::Original && alias.m_aliasType != Spawnable::EntityAliasType::Disabled) && - !alias.m_spawnable.IsReady()) + if (!alias.m_queueLoad || + alias.m_aliasType == Spawnable::EntityAliasType::Original || + alias.m_aliasType == Spawnable::EntityAliasType::Disabled) + { + continue; + } + if (!alias.m_spawnable.IsReady() && !alias.m_spawnable.IsError()) { return false; } diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp index b98ea275e4..912bf05058 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.cpp @@ -26,7 +26,7 @@ namespace AzFramework return m_threadData != nullptr; } - uint64_t SpawnableEntitiesContainer::GetCurrentGeneration() const + uint32_t SpawnableEntitiesContainer::GetCurrentGeneration() const { return m_currentGeneration; } @@ -37,7 +37,7 @@ namespace AzFramework SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_threadData->m_spawnedEntitiesTicket); } - void SpawnableEntitiesContainer::SpawnEntities(AZStd::vector entityIndices) + void SpawnableEntitiesContainer::SpawnEntities(AZStd::vector entityIndices) { AZ_Assert(m_threadData, "Calling SpawnEntities on a Spawnable container that's not set."); SpawnableEntitiesInterface::Get()->SpawnEntities( @@ -78,15 +78,21 @@ namespace AzFramework } } - void SpawnableEntitiesContainer::Alert(AlertCallback callback) + void SpawnableEntitiesContainer::Alert(AlertCallback callback, CheckIfSpawnableIsLoaded spawnableCheck) { AZ_Assert(m_threadData, "Calling DespawnEntities on a Spawnable container that's not set."); - SpawnableEntitiesInterface::Get()->Barrier( - m_threadData->m_spawnedEntitiesTicket, - [generation = m_threadData->m_generation, callback = AZStd::move(callback)](EntitySpawnTicket::Id) - { - callback(generation); - }); + auto callbackWrapper = [generation = m_threadData->m_generation, callback = AZStd::move(callback)](EntitySpawnTicket::Id) + { + callback(generation); + }; + if (spawnableCheck == CheckIfSpawnableIsLoaded::No) + { + SpawnableEntitiesInterface::Get()->Barrier(m_threadData->m_spawnedEntitiesTicket, AZStd::move(callbackWrapper)); + } + else + { + SpawnableEntitiesInterface::Get()->LoadBarrier(m_threadData->m_spawnedEntitiesTicket, AZStd::move(callbackWrapper)); + } } void SpawnableEntitiesContainer::Connect(AZ::Data::Asset spawnable) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.h index 6fa295e18c..1ec6e6a665 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.h @@ -36,6 +36,12 @@ namespace AzFramework public: using AlertCallback = AZStd::function; + enum class CheckIfSpawnableIsLoaded : bool + { + Yes, + No + }; + //! Constructs a new spawnables entity container that has not been connected. SpawnableEntitiesContainer() = default; //! Constructs a new spawnables entity container that connects to the provided spawnable. @@ -48,13 +54,13 @@ namespace AzFramework //! Returns a number that identifies the current generation of the container with. The completion callback can still receive //! calls from older generations as processing completes on those. The returned value can be used to help calls tell //! older versions apart from newer ones. - [[nodiscard]] uint64_t GetCurrentGeneration() const; + [[nodiscard]] uint32_t GetCurrentGeneration() const; //! Puts in a request to spawn entities using all entities in the provided spawnable as a template. void SpawnAllEntities(); //! Puts in a request to spawn entities using the entities found in the spawnable at the provided indices as a template. //! @param entityIndices A list of indices to the entities in the spawnable. - void SpawnEntities(AZStd::vector entityIndices); + void SpawnEntities(AZStd::vector entityIndices); //! Puts in a request to despawn all previous spawned entities. void DespawnAllEntities(); @@ -73,7 +79,11 @@ namespace AzFramework //! other than the calling thread including the main thread. Note that because the alert is queued it can still be called //! after the container has been deleted or can be called for a previously assigned spawnable. In the latter case check //! if the current generation matches the generation provided with the callback. - void Alert(AlertCallback callback); + //! @callback The function called when the alert triggers. This can be called from a different thread than the one that + //! the one that made the call to Alert. + //! @checkSpawnableIsLoaded If true the alert will also block until the spawnable has been loaded. If false then it will + //! be called after all previous calls have completed, but the spawnable may not be loaded at that point. + void Alert(AlertCallback callback, CheckIfSpawnableIsLoaded spawnableCheck = CheckIfSpawnableIsLoaded::No); private: void Connect(AZ::Data::Asset spawnable); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp index 171d626b27..37091d8f0f 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.cpp @@ -152,7 +152,7 @@ namespace AzFramework // SpawnableIndexEntityPair // - SpawnableIndexEntityPair::SpawnableIndexEntityPair(AZ::Entity** entityIterator, size_t* indexIterator) + SpawnableIndexEntityPair::SpawnableIndexEntityPair(AZ::Entity** entityIterator, uint32_t* indexIterator) : m_entity(entityIterator) , m_index(indexIterator) { @@ -168,7 +168,7 @@ namespace AzFramework return *m_entity; } - size_t SpawnableIndexEntityPair::GetIndex() const + uint32_t SpawnableIndexEntityPair::GetIndex() const { return *m_index; } @@ -177,7 +177,7 @@ namespace AzFramework // SpawnableIndexEntityIterator // - SpawnableIndexEntityIterator::SpawnableIndexEntityIterator(AZ::Entity** entityIterator, size_t* indexIterator) + SpawnableIndexEntityIterator::SpawnableIndexEntityIterator(AZ::Entity** entityIterator, uint32_t* indexIterator) : m_value(entityIterator, indexIterator) { } @@ -248,7 +248,7 @@ namespace AzFramework // SpawnableConstIndexEntityContainerView::SpawnableConstIndexEntityContainerView( - AZ::Entity** beginEntity, size_t* beginIndices, size_t length) + AZ::Entity** beginEntity, uint32_t* beginIndices, size_t length) : m_begin(beginEntity, beginIndices) , m_end(beginEntity + length, beginIndices + length) { diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h index 74a17020df..dc9c7b4538 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h @@ -85,19 +85,19 @@ namespace AzFramework AZ::Entity* GetEntity(); const AZ::Entity* GetEntity() const; - size_t GetIndex() const; + uint32_t GetIndex() const; private: SpawnableIndexEntityPair() = default; SpawnableIndexEntityPair(const SpawnableIndexEntityPair&) = default; SpawnableIndexEntityPair(SpawnableIndexEntityPair&&) = default; - SpawnableIndexEntityPair(AZ::Entity** entityIterator, size_t* indexIterator); + SpawnableIndexEntityPair(AZ::Entity** entityIterator, uint32_t* indexIterator); SpawnableIndexEntityPair& operator=(const SpawnableIndexEntityPair&) = default; SpawnableIndexEntityPair& operator=(SpawnableIndexEntityPair&&) = default; AZ::Entity** m_entity { nullptr }; - size_t* m_index { nullptr }; + uint32_t* m_index { nullptr }; }; class SpawnableIndexEntityIterator @@ -110,7 +110,7 @@ namespace AzFramework using pointer = SpawnableIndexEntityPair*; using reference = SpawnableIndexEntityPair&; - SpawnableIndexEntityIterator(AZ::Entity** entityIterator, size_t* indexIterator); + SpawnableIndexEntityIterator(AZ::Entity** entityIterator, uint32_t* indexIterator); SpawnableIndexEntityIterator& operator++(); SpawnableIndexEntityIterator operator++(int); @@ -132,7 +132,7 @@ namespace AzFramework class SpawnableConstIndexEntityContainerView { public: - SpawnableConstIndexEntityContainerView(AZ::Entity** beginEntity, size_t* beginIndices, size_t length); + SpawnableConstIndexEntityContainerView(AZ::Entity** beginEntity, uint32_t* beginIndices, size_t length); const SpawnableIndexEntityIterator& begin(); const SpawnableIndexEntityIterator& end(); @@ -144,6 +144,16 @@ namespace AzFramework SpawnableIndexEntityIterator m_end; }; + //! Information used when updating the type of an entity alias. + struct EntityAliasTypeChange + { + //! The index of the alias in the spawnable. Note that due to optimizations done on the entity aliases the index of an alias + //! can change over time. + uint32_t m_aliasIndex; + //! The type to replace type stored in the spawnable at the index provided by m_aliasIndex. + Spawnable::EntityAliasType m_newAliasType; + }; + //! Requests to the SpawnableEntitiesInterface require a ticket with a valid spawnable that is used as a template. A ticket can //! be reused for multiple calls on the same spawnable and is safe to be used by multiple threads at the same time. Entities created //! from the spawnable may be tracked by the ticket and so using the same ticket is needed to despawn the exact entities created @@ -178,6 +188,7 @@ namespace AzFramework using EntityDespawnCallback = AZStd::function; using RetrieveEntitySpawnTicketCallback = AZStd::function; using ReloadSpawnableCallback = AZStd::function; + using UpdateEntityAliasTypesCallback = AZStd::function; using ListEntitiesCallback = AZStd::function; using ListIndicesEntitiesCallback = AZStd::function; using ClaimEntitiesCallback = AZStd::function; @@ -247,6 +258,15 @@ namespace AzFramework SpawnablePriority m_priority { SpawnablePriority_Default }; }; + struct UpdateEntityAliasTypesOptionalArgs final + { + //! Callback that's called when entity aliases are updated. This can be triggered from a different thread than the one that + //! made the function call to update. + UpdateEntityAliasTypesCallback m_completionCallback; + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; + }; + struct ListEntitiesOptionalArgs final { //! The priority at which this call will be executed. @@ -265,6 +285,14 @@ namespace AzFramework SpawnablePriority m_priority{ SpawnablePriority_Default }; }; + struct LoadBarrierOptionalArgs final + { + //! The priority at which this call will be executed. + SpawnablePriority m_priority{ SpawnablePriority_Default }; + //! Also checks if the spawnables referenced in the entity aliases that are marked to be loaded are loaded. + bool m_checkAliasSpawnables{ true }; + }; + //! Interface definition to (de)spawn entities from a spawnable into the game world. //! //! While the callbacks of the individual calls are being processed they will block processing any other request. Callbacks can be @@ -298,7 +326,7 @@ namespace AzFramework //! @param entityIndices The indices into the template entities stored in the spawnable that will be used to spawn entities from. //! @param optionalArgs Optional additional arguments, see SpawnEntitiesOptionalArgs. virtual void SpawnEntities( - EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; + EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0; //! Removes all entities in the provided list from the environment. //! @param ticket The ticket previously used to spawn entities with. //! @param optionalArgs Optional additional arguments, see DespawnAllEntitiesOptionalArgs. @@ -320,6 +348,16 @@ namespace AzFramework virtual void ReloadSpawnable( EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) = 0; + //! Allows updating the entity alias on a spawnable. This allows the spawning behavior for all entities spawned from the used + //! spawnable to be changed and is not restricted to this ticket alone. + //! @param ticket Holds the information for the spawnable. + //! @param updateAliases An array of index and alias type values used to update the entity alias list. + //! @param optionalArgs Optional additional arguments, see UpdateEntityAliasTypesOptionalArgs. + virtual void UpdateEntityAliasTypes( + EntitySpawnTicket& ticket, + AZStd::vector updatedAliases, + UpdateEntityAliasTypesOptionalArgs optionalArgs = {}) = 0; + //! List all entities that are spawned using this ticket. //! @param ticket Only the entities associated with this ticket will be listed. //! @param listCallback Required callback that will be called to list the entities on. @@ -351,31 +389,37 @@ namespace AzFramework //! @param completionCallback Required callback that will be called as soon as the barrier has been reached. //! @param optionalArgs Optional additional arguments, see BarrierOptionalArgs. virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) = 0; + //! Blocks until the spawnable is loaded and all operations made on the provided ticket before the barrier call have completed. + //! @param ticket The ticket to monitor. + //! @param completionCallback Required callback that will be called as soon as the barrier has been reached. + //! @param optionalArgs Optional additional arguments, see BarrierOptionalArgs. + virtual void LoadBarrier( + EntitySpawnTicket& ticket, BarrierCallback completionCallback, LoadBarrierOptionalArgs optionalArgs = {}) = 0; protected: [[nodiscard]] virtual AZStd::pair CreateTicket(AZ::Data::Asset&& spawnable) = 0; virtual void DestroyTicket(void* ticket) = 0; template - static T& GetTicketPayload(EntitySpawnTicket& ticket) + [[nodiscard]] static T& GetTicketPayload(EntitySpawnTicket& ticket) { return *reinterpret_cast(ticket.m_payload); } template - static const T& GetTicketPayload(const EntitySpawnTicket& ticket) + [[nodiscard]] static const T& GetTicketPayload(const EntitySpawnTicket& ticket) { return *reinterpret_cast(ticket.m_payload); } template - static T* GetTicketPayload(EntitySpawnTicket* ticket) + [[nodiscard]] static T* GetTicketPayload(EntitySpawnTicket* ticket) { return reinterpret_cast(ticket->m_payload); } template - static const T* GetTicketPayload(const EntitySpawnTicket* ticket) + [[nodiscard]] static const T* GetTicketPayload(const EntitySpawnTicket* ticket) { return reinterpret_cast(ticket->m_payload); } diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index ef7351aabb..0ac80c3ed6 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -60,7 +60,7 @@ namespace AzFramework } void SpawnableEntitiesManager::SpawnEntities( - EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs) + EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs) { AZ_Assert(ticket.IsValid(), "Ticket provided to SpawnEntities hasn't been initialized."); @@ -128,6 +128,20 @@ namespace AzFramework QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } + void SpawnableEntitiesManager::UpdateEntityAliasTypes( + EntitySpawnTicket& ticket, + AZStd::vector updatedAliases, + UpdateEntityAliasTypesOptionalArgs optionalArgs) + { + AZ_Assert(ticket.IsValid(), "Ticket provided to ReloadSpawnable hasn't been initialized."); + + UpdateEntityAliasTypesCommand queueEntry; + queueEntry.m_entityAliases = AZStd::move(updatedAliases); + queueEntry.m_ticketId = ticket.GetId(); + queueEntry.m_completionCallback = AZStd::move(optionalArgs.m_completionCallback); + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); + } + void SpawnableEntitiesManager::ListEntities( EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs) { @@ -175,6 +189,19 @@ namespace AzFramework QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); } + void SpawnableEntitiesManager::LoadBarrier( + EntitySpawnTicket& ticket, BarrierCallback completionCallback, LoadBarrierOptionalArgs optionalArgs) + { + AZ_Assert(completionCallback, "Load barrier on spawnable entities called without a valid callback to use."); + AZ_Assert(ticket.IsValid(), "Ticket provided to LoadBarrier hasn't been initialized."); + + LoadBarrierCommand queueEntry; + queueEntry.m_ticketId = ticket.GetId(); + queueEntry.m_completionCallback = AZStd::move(completionCallback); + queueEntry.m_checkAliasSpawnables = optionalArgs.m_checkAliasSpawnables; + QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry)); + } + auto SpawnableEntitiesManager::ProcessQueue(CommandQueuePriority priority) -> CommandQueueStatus { CommandQueueStatus result = CommandQueueStatus::NoCommandsLeft; @@ -203,13 +230,13 @@ namespace AzFramework for (size_t i = 0; i < delayedSize; ++i) { Requests& request = queue.m_delayed.front(); - bool result = AZStd::visit( - [this](auto&& args) -> bool + CommandResult result = AZStd::visit( + [this](auto&& args) -> CommandResult { return ProcessRequest(args); }, request); - if (!result) + if (result == CommandResult::Requeue) { queue.m_delayed.emplace_back(AZStd::move(request)); } @@ -230,13 +257,13 @@ namespace AzFramework while (!pendingRequestQueue.empty()) { Requests& request = pendingRequestQueue.front(); - bool result = AZStd::visit( - [this](auto&& args) -> bool + CommandResult result = AZStd::visit( + [this](auto&& args) -> CommandResult { return ProcessRequest(args); }, request); - if (!result) + if (result == CommandResult::Requeue) { queue.m_delayed.emplace_back(AZStd::move(request)); } @@ -276,11 +303,81 @@ namespace AzFramework AZ::Entity* SpawnableEntitiesManager::CloneSingleEntity(const AZ::Entity& entityTemplate, EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext) { - // If the same ID gets remapped more than once, preserve the original remapping instead of overwriting it. + if (!entityTemplate.GetComponents().empty()) + { + // If the same ID gets remapped more than once, preserve the original remapping instead of overwriting it. + constexpr bool allowDuplicateIds = false; + + return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( + &entityTemplate, templateToCloneMap, &serializeContext); + } + else + { + return nullptr; + } + } + + AZ::Entity* SpawnableEntitiesManager::CloneSingleAliasedEntity( + const AZ::Entity& entityTemplate, + const Spawnable::EntityAlias& alias, + EntityIdMap& templateToCloneMap, + AZ::Entity* previouslySpawnedEntity, + AZ::SerializeContext& serializeContext) + { + using ResultType = AZStd::pair; + + AZ::Entity* clone = nullptr; + switch (alias.m_aliasType) + { + case Spawnable::EntityAliasType::Original: + // Behave as the original version. + clone = CloneSingleEntity(entityTemplate, templateToCloneMap, serializeContext); + AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); + return clone; + case Spawnable::EntityAliasType::Disabled: + // Do nothing. + return nullptr; + case Spawnable::EntityAliasType::Replace: + clone = CloneSingleEntity(*(alias.m_spawnable->GetEntities()[alias.m_targetIndex]), templateToCloneMap, serializeContext); + AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); + return clone; + case Spawnable::EntityAliasType::Additional: + // The asset handler will have sorted and inserted a Spawnable::EntityAliasType::Original, so the just + // spawn the additional entity. + clone = CloneSingleEntity(*(alias.m_spawnable->GetEntities()[alias.m_targetIndex]), templateToCloneMap, serializeContext); + AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); + return clone; + case Spawnable::EntityAliasType::Merge: + AZ_Assert(previouslySpawnedEntity != nullptr, "Merging components but there's no entity to add to yet."); + AZ_Assert( + previouslySpawnedEntity->GetId() == alias.m_spawnable->GetEntities()[alias.m_targetIndex]->GetId(), + "Entity ids for merging spawnables don't match."); + AppendComponents( + *previouslySpawnedEntity, alias.m_spawnable->GetEntities()[alias.m_targetIndex]->GetComponents(), templateToCloneMap, serializeContext); + return nullptr; + default: + AZ_Assert(false, "Unsupported spawnable entity alias type: %i", alias.m_aliasType); + return nullptr; + } + } + + void SpawnableEntitiesManager::AppendComponents( + AZ::Entity& target, + const AZ::Entity::ComponentArrayType& componentTemplates, + EntityIdMap& templateToCloneMap, + AZ::SerializeContext& serializeContext) + { + // Only components are added and entities are looked up so no duplicate entity ids should be encountered. constexpr bool allowDuplicateIds = false; - return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( - &entityTemplate, templateToCloneMap, &serializeContext); + for (const AZ::Component* component : componentTemplates) + { + AZ::Component* clone = AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( + component, templateToCloneMap, &serializeContext); + AZ_Assert(clone, "Unable to clone component for entity '%s' (%zu).", target.GetName().c_str(), target.GetId()); + [[maybe_unused]] bool result = target.AddComponent(clone); + AZ_Assert(result, "Unable to add cloned component to entity '%s' (%zu).", target.GetName().c_str(), target.GetId()); + } } void SpawnableEntitiesManager::InitializeEntityIdMappings( @@ -316,161 +413,276 @@ namespace AzFramework } } - - bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request) + auto SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request) -> CommandResult { Ticket& ticket = *request.m_ticket; if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) { - AZStd::vector& spawnedEntities = ticket.m_spawnedEntities; - AZStd::vector& spawnedEntityIndices = ticket.m_spawnedEntityIndices; - - // Keep track how many entities there were in the array initially - size_t spawnedEntitiesInitialCount = spawnedEntities.size(); - - // These are 'template' entities we'll be cloning from - const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities(); - size_t entitiesToSpawnSize = entitiesToSpawn.size(); - - // Reserve buffers - spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize); - spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize); - - // Pre-generate the full set of entity id to new entity id mappings, so that during the clone operation below, - // any entity references that point to a not-yet-cloned entity will still get their ids remapped correctly. - // We clear out and regenerate the set of IDs on every SpawnAllEntities call, because presumably every entity reference - // in every entity we're about to instantiate is intended to point to an entity in our newly-instantiated batch, regardless - // of spawn order. If we didn't clear out the map, it would be possible for some entities here to have references to - // previously-spawned entities from a previous SpawnEntities or SpawnAllEntities call. - InitializeEntityIdMappings(entitiesToSpawn, ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned); - - for (size_t i = 0; i < entitiesToSpawnSize; ++i) + if (Spawnable::EntityAliasConstVisitor aliases = ticket.m_spawnable->TryGetAliasesConst(); + aliases.HasLock() && aliases.AreAllSpawnablesReady()) { - // If this entity has previously been spawned, give it a new id in the reference map - RefreshEntityIdMapping(entitiesToSpawn[i].get()->GetId(), ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned); + AZStd::vector& spawnedEntities = ticket.m_spawnedEntities; + AZStd::vector& spawnedEntityIndices = ticket.m_spawnedEntityIndices; - AZ::Entity* clone = CloneSingleEntity(*entitiesToSpawn[i], ticket.m_entityIdReferenceMap, *request.m_serializeContext); - AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); + // Keep track how many entities there were in the array initially + size_t spawnedEntitiesInitialCount = spawnedEntities.size(); - spawnedEntities.emplace_back(clone); - spawnedEntityIndices.push_back(i); - } + // These are 'template' entities we'll be cloning from + const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities(); + uint32_t entitiesToSpawnSize = aznumeric_caster(entitiesToSpawn.size()); - // loadAll is true if every entity has been spawned only once - ticket.m_loadAll = (spawnedEntities.size() == entitiesToSpawnSize); - - // Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context. - if (request.m_preInsertionCallback) - { - request.m_preInsertionCallback(request.m_ticketId, SpawnableEntityContainerView( - ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); - } + // Reserve buffers + spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize); + spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize); - // Add to the game context, now the entities are active - for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it) - { - (*it)->SetSpawnTicketId(request.m_ticketId); - GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); - } - - // Let other systems know about newly spawned entities for any post-processing after adding to the scene/game context. - if (request.m_completionCallback) - { - request.m_completionCallback(request.m_ticketId, SpawnableConstEntityContainerView( - ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); - } - - ticket.m_currentRequestId++; - return true; - } - else - { - return false; - } - } - - bool SpawnableEntitiesManager::ProcessRequest(SpawnEntitiesCommand& request) - { - Ticket& ticket = *request.m_ticket; - if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) - { - AZStd::vector& spawnedEntities = ticket.m_spawnedEntities; - AZStd::vector& spawnedEntityIndices = ticket.m_spawnedEntityIndices; - AZ_Assert( - spawnedEntities.size() == spawnedEntityIndices.size(), - "The indices for the spawned entities has gone out of sync with the entities."); - - // Keep track of how many entities there were in the array initially - size_t spawnedEntitiesInitialCount = spawnedEntities.size(); - - // These are 'template' entities we'll be cloning from - const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities(); - size_t entitiesToSpawnSize = request.m_entityIndices.size(); - - if (ticket.m_entityIdReferenceMap.empty() || !request.m_referencePreviouslySpawnedEntities) - { - // This map keeps track of ids from template (spawnable) to clone (instance) allowing patch ups of fields referring - // to entityIds outside of a given entity. - // We pre-generate the full set of entity id to new entity id mappings, so that during the clone operation below, + // Pre-generate the full set of entity-id-to-new-entity-id mappings, so that during the clone operation below, // any entity references that point to a not-yet-cloned entity will still get their ids remapped correctly. - // By default, we only initialize this map once because it needs to persist across multiple SpawnEntities calls, so - // that reference fixups work even when the entity being referenced is spawned in a different SpawnEntities - // (or SpawnAllEntities) call. - // However, the caller can also choose to reset the map by passing in "m_referencePreviouslySpawnedEntities = false". + // We clear out and regenerate the set of IDs on every SpawnAllEntities call, because presumably every entity reference + // in every entity we're about to instantiate is intended to point to an entity in our newly-instantiated batch, regardless + // of spawn order. If we didn't clear out the map, it would be possible for some entities here to have references to + // previously-spawned entities from a previous SpawnEntities or SpawnAllEntities call. InitializeEntityIdMappings(entitiesToSpawn, ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned); - } - spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize); - spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize); - - for (size_t index : request.m_entityIndices) - { - if (index < entitiesToSpawn.size()) + auto aliasIt = aliases.begin(); + auto aliasEnd = aliases.end(); + if (aliasIt == aliasEnd) { - // If this entity has previously been spawned, give it a new id in the reference map - RefreshEntityIdMapping( - entitiesToSpawn[index].get()->GetId(), ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned); + for (uint32_t i = 0; i < entitiesToSpawnSize; ++i) + { + // If this entity has previously been spawned, give it a new id in the reference map + RefreshEntityIdMapping( + entitiesToSpawn[i].get()->GetId(), ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned); - AZ::Entity* clone = - CloneSingleEntity(*entitiesToSpawn[index], ticket.m_entityIdReferenceMap, *request.m_serializeContext); - AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - - spawnedEntities.push_back(clone); - spawnedEntityIndices.push_back(index); + spawnedEntities.emplace_back( + CloneSingleEntity(*entitiesToSpawn[i], ticket.m_entityIdReferenceMap, *request.m_serializeContext)); + spawnedEntityIndices.push_back(i); + } } - } - ticket.m_loadAll = false; + else + { + for (uint32_t i = 0; i < entitiesToSpawnSize; ++i) + { + // If this entity has previously been spawned, give it a new id in the reference map + RefreshEntityIdMapping( + entitiesToSpawn[i].get()->GetId(), ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned); - // Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context. - if (request.m_preInsertionCallback) - { - request.m_preInsertionCallback(request.m_ticketId, SpawnableEntityContainerView( - ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); - } + if (aliasIt == aliasEnd || aliasIt->m_sourceIndex != i) + { + AZ::Entity* clone = + CloneSingleEntity(*entitiesToSpawn[i], ticket.m_entityIdReferenceMap, *request.m_serializeContext); + spawnedEntities.emplace_back(clone); + spawnedEntityIndices.push_back(i); + } + else + { + // The list of entities has already been sorted and optimized (See SpawnableEntitiesAliasList:Optimize) so can + // be safely executed in order without risking an invalid state. + AZ::Entity* previousEntity = nullptr; + do + { + AZ::Entity* clone = CloneSingleAliasedEntity( + *entitiesToSpawn[i], *aliasIt, ticket.m_entityIdReferenceMap, previousEntity, + *request.m_serializeContext); + // Not all alias operations create a new instance. It's also possible for an empty entity to be left behind, + // in which case it's also filtered out as the entity component framework doesn't handle these gracefully. + if (clone) + { + if (!clone->GetComponents().empty()) + { + previousEntity = clone; + spawnedEntities.emplace_back(clone); + spawnedEntityIndices.push_back(i); + } + else + { + delete clone; + } + } + ++aliasIt; + } while (aliasIt != aliasEnd && aliasIt->m_sourceIndex == i); + } + } + } - // Add to the game context, now the entities are active - for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it) - { + // There were no initial entities then the ticket now holds exactly all entities. If there were already entities then + // a new set are not added so it no longer holds exactly the number of entities. + ticket.m_loadAll = spawnedEntitiesInitialCount == 0; + + auto newEntitiesBegin = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; + auto newEntitiesEnd = ticket.m_spawnedEntities.end(); + // Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context. + if (request.m_preInsertionCallback) + { + request.m_preInsertionCallback(request.m_ticketId, SpawnableEntityContainerView(newEntitiesBegin, newEntitiesEnd)); + } + + // Add to the game context, now the entities are active + for (auto it = newEntitiesBegin; it != newEntitiesEnd; ++it) + { (*it)->SetSpawnTicketId(request.m_ticketId); - GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); - } + GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); + } - if (request.m_completionCallback) - { - request.m_completionCallback(request.m_ticketId, SpawnableConstEntityContainerView( - ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); - } + // Let other systems know about newly spawned entities for any post-processing after adding to the scene/game context. + if (request.m_completionCallback) + { + request.m_completionCallback(request.m_ticketId, SpawnableConstEntityContainerView(newEntitiesBegin, newEntitiesEnd)); + } - ticket.m_currentRequestId++; - return true; - } - else - { - return false; + ticket.m_currentRequestId++; + return CommandResult::Executed; + } } + return CommandResult::Requeue; } - bool SpawnableEntitiesManager::ProcessRequest(DespawnAllEntitiesCommand& request) + auto SpawnableEntitiesManager::ProcessRequest(SpawnEntitiesCommand& request) -> CommandResult + { + Ticket& ticket = *request.m_ticket; + if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) + { + if (Spawnable::EntityAliasConstVisitor aliases = ticket.m_spawnable->TryGetAliasesConst(); + aliases.HasLock() && aliases.AreAllSpawnablesReady()) + { + AZStd::vector& spawnedEntities = ticket.m_spawnedEntities; + AZStd::vector& spawnedEntityIndices = ticket.m_spawnedEntityIndices; + AZ_Assert( + spawnedEntities.size() == spawnedEntityIndices.size(), + "The indices for the spawned entities has gone out of sync with the entities."); + + // Keep track of how many entities there were in the array initially + size_t spawnedEntitiesInitialCount = spawnedEntities.size(); + + // These are 'template' entities we'll be cloning from + const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities(); + size_t entitiesToSpawnSize = request.m_entityIndices.size(); + + if (ticket.m_entityIdReferenceMap.empty() || !request.m_referencePreviouslySpawnedEntities) + { + // This map keeps track of ids from template (spawnable) to clone (instance) allowing patch ups of fields referring + // to entityIds outside of a given entity. + // We pre-generate the full set of entity id to new entity id mappings, so that during the clone operation below, + // any entity references that point to a not-yet-cloned entity will still get their ids remapped correctly. + // By default, we only initialize this map once because it needs to persist across multiple SpawnEntities calls, so + // that reference fixups work even when the entity being referenced is spawned in a different SpawnEntities + // (or SpawnAllEntities) call. + // However, the caller can also choose to reset the map by passing in "m_referencePreviouslySpawnedEntities = false". + InitializeEntityIdMappings(entitiesToSpawn, ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned); + } + + spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize); + spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize); + + auto aliasBegin = aliases.begin(); + auto aliasEnd = aliases.end(); + if (aliasBegin == aliasEnd) + { + for (uint32_t index : request.m_entityIndices) + { + if (index < entitiesToSpawn.size()) + { + // If this entity has previously been spawned, give it a new id in the reference map + RefreshEntityIdMapping( + entitiesToSpawn[index].get()->GetId(), ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned); + + AZ::Entity* clone = + CloneSingleEntity(*entitiesToSpawn[index], ticket.m_entityIdReferenceMap, *request.m_serializeContext); + AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); + spawnedEntities.push_back(clone); + spawnedEntityIndices.push_back(index); + } + } + } + else + { + for (uint32_t index : request.m_entityIndices) + { + if (index < entitiesToSpawn.size()) + { + // If this entity has previously been spawned, give it a new id in the reference map + RefreshEntityIdMapping( + entitiesToSpawn[index].get()->GetId(), ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned); + + auto aliasIt = AZStd::lower_bound( + aliasBegin, aliasEnd, index, + [](const Spawnable::EntityAlias& lhs, uint32_t rhs) + { + return lhs.m_sourceIndex < rhs; + }); + + if (aliasIt == aliasEnd) + { + AZ::Entity* clone = + CloneSingleEntity(*entitiesToSpawn[index], ticket.m_entityIdReferenceMap, *request.m_serializeContext); + spawnedEntities.emplace_back(clone); + spawnedEntityIndices.push_back(index); + } + else + { + // The list of entities has already been sorted and optimized (See SpawnableEntitiesAliasList:Optimize) so + // can be safely executed in order without risking an invalid state. + AZ::Entity* previousEntity = nullptr; + do + { + AZ::Entity* clone = CloneSingleAliasedEntity( + *entitiesToSpawn[index], *aliasIt, ticket.m_entityIdReferenceMap, previousEntity, + *request.m_serializeContext); + // Not all alias operations create a new instance. It's also possible for an empty entity to be left + // behind, in which case it's also filtered out as the entity component framework doesn't handle these + // gracefully. + if (clone) + { + if (!clone->GetComponents().empty()) + { + previousEntity = clone; + spawnedEntities.emplace_back(clone); + spawnedEntityIndices.push_back(index); + } + else + { + delete clone; + } + } + ++aliasIt; + } while (aliasIt != aliasEnd && aliasIt->m_sourceIndex == index); + } + } + } + } + ticket.m_loadAll = false; + + // Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context. + if (request.m_preInsertionCallback) + { + request.m_preInsertionCallback( + request.m_ticketId, + SpawnableEntityContainerView( + ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); + } + + // Add to the game context, now the entities are active + for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it) + { + (*it)->SetSpawnTicketId(request.m_ticketId); + GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); + } + + if (request.m_completionCallback) + { + request.m_completionCallback( + request.m_ticketId, + SpawnableConstEntityContainerView( + ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end())); + } + + ticket.m_currentRequestId++; + return CommandResult::Executed; + } + } + return CommandResult::Requeue; + } + + auto SpawnableEntitiesManager::ProcessRequest(DespawnAllEntitiesCommand& request) -> CommandResult { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -495,15 +707,15 @@ namespace AzFramework } ticket.m_currentRequestId++; - return true; + return CommandResult::Executed; } else { - return false; + return CommandResult::Requeue; } } - bool SpawnableEntitiesManager::ProcessRequest(DespawnEntityCommand& request) + auto SpawnableEntitiesManager::ProcessRequest(DespawnEntityCommand& request) -> CommandResult { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -529,15 +741,15 @@ namespace AzFramework } ticket.m_currentRequestId++; - return true; + return CommandResult::Executed; } else { - return false; + return CommandResult::Requeue; } } - bool SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request) + auto SpawnableEntitiesManager::ProcessRequest(ReloadSpawnableCommand& request) -> CommandResult { Ticket& ticket = *request.m_ticket; AZ_Assert(ticket.m_spawnable.GetId() == request.m_spawnable.GetId(), @@ -574,7 +786,7 @@ namespace AzFramework ticket.m_spawnedEntityIndices.clear(); size_t entitiesToSpawnSize = entities.size(); - for (size_t i = 0; i < entitiesToSpawnSize; ++i) + for (uint32_t i = 0; i < entitiesToSpawnSize; ++i) { // If this entity has previously been spawned, give it a new id in the reference map RefreshEntityIdMapping(entities[i].get()->GetId(), ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned); @@ -590,7 +802,7 @@ namespace AzFramework { size_t entitiesSize = entities.size(); - for (size_t index : ticket.m_spawnedEntityIndices) + for (uint32_t index : ticket.m_spawnedEntityIndices) { // It's possible for the new spawnable to have a different number of entities, so guard against this. // It's also possible that the entities have moved within the spawnable to a new index. This can't be @@ -616,15 +828,47 @@ namespace AzFramework ticket.m_currentRequestId++; - return true; + return CommandResult::Executed; } else { - return false; + return CommandResult::Requeue; } } - bool SpawnableEntitiesManager::ProcessRequest(ListEntitiesCommand& request) + auto SpawnableEntitiesManager::ProcessRequest(UpdateEntityAliasTypesCommand& request) -> CommandResult + { + Ticket& ticket = *request.m_ticket; + if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) + { + if (Spawnable::EntityAliasVisitor aliases = ticket.m_spawnable->TryGetAliases(); aliases.HasLock()) + { + for (EntityAliasTypeChange& replacement : request.m_entityAliases) + { + aliases.UpdateAliasType(replacement.m_aliasIndex, replacement.m_newAliasType); + } + aliases.Optimize(); + + if (request.m_completionCallback) + { + request.m_completionCallback(request.m_ticketId); + } + + ticket.m_currentRequestId++; + return CommandResult::Executed; + } + else + { + AZ_Assert( + ticket.m_spawnable->IsPermanentlyLocked(), + "An request to UpdateEntityAliasTypes on the Spawnables Entities Manager was processed on a spawnable that's permanently " + "locked."); + } + } + return CommandResult::Requeue; + } + + auto SpawnableEntitiesManager::ProcessRequest(ListEntitiesCommand& request) -> CommandResult { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -632,15 +876,15 @@ namespace AzFramework request.m_listCallback(request.m_ticketId, SpawnableConstEntityContainerView( ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntities.end())); ticket.m_currentRequestId++; - return true; + return CommandResult::Executed; } else { - return false; + return CommandResult::Requeue; } } - bool SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request) + auto SpawnableEntitiesManager::ProcessRequest(ListIndicesEntitiesCommand& request) -> CommandResult { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -651,15 +895,15 @@ namespace AzFramework request.m_listCallback(request.m_ticketId, SpawnableConstIndexEntityContainerView( ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntityIndices.begin(), ticket.m_spawnedEntities.size())); ticket.m_currentRequestId++; - return true; + return CommandResult::Executed; } else { - return false; + return CommandResult::Requeue; } } - bool SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request) + auto SpawnableEntitiesManager::ProcessRequest(ClaimEntitiesCommand& request) -> CommandResult { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -671,15 +915,15 @@ namespace AzFramework ticket.m_spawnedEntityIndices.clear(); ticket.m_currentRequestId++; - return true; + return CommandResult::Executed; } else { - return false; + return CommandResult::Requeue; } } - bool SpawnableEntitiesManager::ProcessRequest(BarrierCommand& request) + auto SpawnableEntitiesManager::ProcessRequest(BarrierCommand& request) -> CommandResult { Ticket& ticket = *request.m_ticket; if (request.m_requestId == ticket.m_currentRequestId) @@ -690,15 +934,39 @@ namespace AzFramework } ticket.m_currentRequestId++; - return true; + return CommandResult::Executed; } else { - return false; + return CommandResult::Requeue; } } - bool SpawnableEntitiesManager::ProcessRequest(DestroyTicketCommand& request) + auto SpawnableEntitiesManager::ProcessRequest(LoadBarrierCommand& request) -> CommandResult + { + Ticket& ticket = *request.m_ticket; + if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) + { + if (request.m_checkAliasSpawnables) + { + if (Spawnable::EntityAliasConstVisitor visitor = ticket.m_spawnable->TryGetAliasesConst(); + !visitor.HasLock() || !visitor.AreAllSpawnablesReady()) + { + return CommandResult::Requeue; + } + } + + request.m_completionCallback(request.m_ticketId); + ticket.m_currentRequestId++; + return CommandResult::Executed; + } + else + { + return CommandResult::Requeue; + } + } + + auto SpawnableEntitiesManager::ProcessRequest(DestroyTicketCommand& request) -> CommandResult { if (request.m_requestId == request.m_ticket->m_currentRequestId) { @@ -714,11 +982,11 @@ namespace AzFramework } delete request.m_ticket; - return true; + return CommandResult::Executed; } else { - return false; + return CommandResult::Requeue; } } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index c3de5be003..09e9b1acfc 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -55,13 +55,18 @@ namespace AzFramework void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs = {}) override; void SpawnEntities( - EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) override; + EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) override; void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) override; void DespawnEntity(AZ::EntityId entityId, EntitySpawnTicket& ticket, DespawnEntityOptionalArgs optionalArgs = {}) override; void RetrieveEntitySpawnTicket(EntitySpawnTicket::Id entitySpawnTicketId, RetrieveEntitySpawnTicketCallback callback) override; void ReloadSpawnable( EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) override; + void UpdateEntityAliasTypes( + EntitySpawnTicket& ticket, + AZStd::vector updatedAliases, + UpdateEntityAliasTypesOptionalArgs optionalArgs = {}) override; + void ListEntities( EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) override; void ListIndicesAndEntities( @@ -70,6 +75,8 @@ namespace AzFramework EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs = {}) override; void Barrier(EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) override; + void LoadBarrier( + EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback, LoadBarrierOptionalArgs optionalArgs = {}) override; // // The following function is thread safe but intended to be run from the main thread. @@ -78,7 +85,13 @@ namespace AzFramework CommandQueueStatus ProcessQueue(CommandQueuePriority priority); protected: - struct Ticket + enum class CommandResult : bool + { + Executed, + Requeue + }; + + struct Ticket final { AZ_CLASS_ALLOCATOR(Ticket, AZ::ThreadPoolAllocator, 0); static constexpr uint32_t Processing = AZStd::numeric_limits::max(); @@ -100,14 +113,14 @@ namespace AzFramework AZStd::unordered_set m_previouslySpawned; AZStd::vector m_spawnedEntities; - AZStd::vector m_spawnedEntityIndices; + AZStd::vector m_spawnedEntityIndices; AZ::Data::Asset m_spawnable; uint32_t m_nextRequestId{ 0 }; //!< Next id for this ticket. uint32_t m_currentRequestId { 0 }; //!< The id for the command that should be executed. bool m_loadAll{ true }; }; - struct SpawnAllEntitiesCommand + struct SpawnAllEntitiesCommand final { EntitySpawnCallback m_completionCallback; EntityPreInsertionCallback m_preInsertionCallback; @@ -116,9 +129,9 @@ namespace AzFramework EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; }; - struct SpawnEntitiesCommand + struct SpawnEntitiesCommand final { - AZStd::vector m_entityIndices; + AZStd::vector m_entityIndices; EntitySpawnCallback m_completionCallback; EntityPreInsertionCallback m_preInsertionCallback; AZ::SerializeContext* m_serializeContext; @@ -127,7 +140,7 @@ namespace AzFramework uint32_t m_requestId; bool m_referencePreviouslySpawnedEntities; }; - struct DespawnAllEntitiesCommand + struct DespawnAllEntitiesCommand final { EntityDespawnCallback m_completionCallback; Ticket* m_ticket; @@ -142,7 +155,7 @@ namespace AzFramework EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; }; - struct ReloadSpawnableCommand + struct ReloadSpawnableCommand final { AZ::Data::Asset m_spawnable; ReloadSpawnableCallback m_completionCallback; @@ -151,35 +164,51 @@ namespace AzFramework EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; }; - struct ListEntitiesCommand + struct UpdateEntityAliasTypesCommand final + { + AZStd::vector m_entityAliases; + UpdateEntityAliasTypesCallback m_completionCallback; + Ticket* m_ticket; + EntitySpawnTicket::Id m_ticketId; + uint32_t m_requestId; + }; + struct ListEntitiesCommand final { ListEntitiesCallback m_listCallback; Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; }; - struct ListIndicesEntitiesCommand + struct ListIndicesEntitiesCommand final { ListIndicesEntitiesCallback m_listCallback; Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; }; - struct ClaimEntitiesCommand + struct ClaimEntitiesCommand final { ClaimEntitiesCallback m_listCallback; Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; }; - struct BarrierCommand + struct BarrierCommand final { BarrierCallback m_completionCallback; Ticket* m_ticket; EntitySpawnTicket::Id m_ticketId; uint32_t m_requestId; }; - struct DestroyTicketCommand + struct LoadBarrierCommand final + { + BarrierCallback m_completionCallback; + Ticket* m_ticket; + EntitySpawnTicket::Id m_ticketId; + uint32_t m_requestId; + bool m_checkAliasSpawnables; + }; + struct DestroyTicketCommand final { Ticket* m_ticket; uint32_t m_requestId; @@ -191,10 +220,12 @@ namespace AzFramework DespawnAllEntitiesCommand, DespawnEntityCommand, ReloadSpawnableCommand, + UpdateEntityAliasTypesCommand, ListEntitiesCommand, ListIndicesEntitiesCommand, ClaimEntitiesCommand, BarrierCommand, + LoadBarrierCommand, DestroyTicketCommand>; struct Queue @@ -213,17 +244,30 @@ namespace AzFramework AZ::Entity* CloneSingleEntity( const AZ::Entity& entityTemplate, EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext); + AZ::Entity* CloneSingleAliasedEntity( + const AZ::Entity& entityTemplate, + const Spawnable::EntityAlias& alias, + EntityIdMap& templateToCloneMap, + AZ::Entity* previouslySpawnedEntity, + AZ::SerializeContext& serializeContext); + void AppendComponents( + AZ::Entity& target, + const AZ::Entity::ComponentArrayType& componentTemplates, + EntityIdMap& templateToCloneMap, + AZ::SerializeContext& serializeContext); - bool ProcessRequest(SpawnAllEntitiesCommand& request); - bool ProcessRequest(SpawnEntitiesCommand& request); - bool ProcessRequest(DespawnAllEntitiesCommand& request); - bool ProcessRequest(DespawnEntityCommand& request); - bool ProcessRequest(ReloadSpawnableCommand& request); - bool ProcessRequest(ListEntitiesCommand& request); - bool ProcessRequest(ListIndicesEntitiesCommand& request); - bool ProcessRequest(ClaimEntitiesCommand& request); - bool ProcessRequest(BarrierCommand& request); - bool ProcessRequest(DestroyTicketCommand& request); + CommandResult ProcessRequest(SpawnAllEntitiesCommand& request); + CommandResult ProcessRequest(SpawnEntitiesCommand& request); + CommandResult ProcessRequest(DespawnAllEntitiesCommand& request); + CommandResult ProcessRequest(DespawnEntityCommand& request); + CommandResult ProcessRequest(ReloadSpawnableCommand& request); + CommandResult ProcessRequest(UpdateEntityAliasTypesCommand& request); + CommandResult ProcessRequest(ListEntitiesCommand& request); + CommandResult ProcessRequest(ListIndicesEntitiesCommand& request); + CommandResult ProcessRequest(ClaimEntitiesCommand& request); + CommandResult ProcessRequest(BarrierCommand& request); + CommandResult ProcessRequest(LoadBarrierCommand& request); + CommandResult ProcessRequest(DestroyTicketCommand& request); //! Generate a base set of original-to-new entity ID mappings to use during spawning. //! Since Entity references get fixed up on an entity-by-entity basis while spawning, it's important to have the complete From b3cd33990444f428e2e5f7399de6e6372976199c Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Mon, 25 Oct 2021 14:55:04 -0700 Subject: [PATCH 007/177] Added support for setting up entity aliases during the prefab to spawnable conversion. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../Prefab/Instance/Instance.cpp | 173 +++++++++++-- .../Prefab/Instance/Instance.h | 39 ++- .../Spawnable/PrefabCatchmentProcessor.cpp | 56 ++-- .../Spawnable/PrefabConversionPipeline.cpp | 1 + .../Spawnable/PrefabProcessorContext.cpp | 136 +++++++++- .../Prefab/Spawnable/PrefabProcessorContext.h | 85 +++++- .../Prefab/Spawnable/ProcesedObjectStore.cpp | 18 +- .../Prefab/Spawnable/ProcesedObjectStore.h | 16 +- .../Prefab/Spawnable/SpawnableUtils.cpp | 242 +++++++++++++++++- .../Prefab/Spawnable/SpawnableUtils.h | 45 ++++ .../PrefabBuilder/PrefabBuilderComponent.cpp | 21 +- 11 files changed, 743 insertions(+), 89 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp index b5db46d0db..490a151925 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.cpp @@ -129,7 +129,7 @@ namespace AzToolsFramework void Instance::SetLinkId(LinkId linkId) { - m_linkId = AZStd::move(linkId); + m_linkId = linkId; } LinkId Instance::GetLinkId() const @@ -154,23 +154,26 @@ namespace AzToolsFramework bool Instance::AddEntity(AZ::Entity& entity) { - EntityAlias newEntityAlias = GenerateEntityAlias(); - return AddEntity(entity, newEntityAlias); + return AddEntity(entity, GenerateEntityAlias()); + } + + bool Instance::AddEntity(AZStd::unique_ptr&& entity) + { + return AddEntity(AZStd::move(entity), GenerateEntityAlias()); } bool Instance::AddEntity(AZ::Entity& entity, EntityAlias entityAlias) { - if (!RegisterEntity(entity.GetId(), entityAlias)) - { - return false; - } + return + RegisterEntity(entity.GetId(), entityAlias) && + m_entities.emplace(AZStd::move(entityAlias), &entity).second; + } - if (!m_entities.emplace(AZStd::make_pair(entityAlias, &entity)).second) - { - return false; - } - - return true; + bool Instance::AddEntity(AZStd::unique_ptr&& entity, EntityAlias entityAlias) + { + return + RegisterEntity(entity->GetId(), entityAlias) && + m_entities.emplace(AZStd::move(entityAlias), AZStd::move(entity)).second; } AZStd::unique_ptr Instance::DetachEntity(const AZ::EntityId& entityId) @@ -228,6 +231,23 @@ namespace AzToolsFramework m_entities.clear(); } + AZStd::unique_ptr Instance::ReplaceEntity(AZStd::unique_ptr&& entity, EntityAliasView alias) + { + AZStd::unique_ptr result; + auto it = m_entities.find(alias); + if (it != m_entities.end()) + { + // Swap entity ids as these need to remain stable + AZ::EntityId originalId = it->second->GetId(); + it->second->SetId(entity->GetId()); + entity->SetId(originalId); + + result = AZStd::move(it->second); + it->second = AZStd::move(entity); + } + return result; + } + void Instance::RemoveNestedEntities( const AZStd::function&)>& filter) { @@ -377,7 +397,12 @@ namespace AzToolsFramework return entityAliases; } - void Instance::GetNestedEntityIds(const AZStd::function& callback) + size_t Instance::GetEntityAliasCount() const + { + return m_entities.size(); + } + + void Instance::GetNestedEntityIds(const AZStd::function& callback) const { GetEntityIds(callback); @@ -387,7 +412,7 @@ namespace AzToolsFramework } } - void Instance::GetEntityIds(const AZStd::function& callback) + void Instance::GetEntityIds(const AZStd::function& callback) const { for (auto&&[entityAlias, entityId] : m_templateToInstanceEntityIdMap) { @@ -398,6 +423,17 @@ namespace AzToolsFramework } } + void Instance::GetEntityIdToAlias(const AZStd::function& callback) const + { + for (auto&& [entityAlias, entityId] : m_templateToInstanceEntityIdMap) + { + if (!callback(entityId, entityAlias)) + { + break; + } + } + } + bool Instance::GetEntities_Impl(const AZStd::function&)>& callback) { for (auto& [entityAlias, entity] : m_entities) @@ -514,24 +550,81 @@ namespace AzToolsFramework } } - EntityAliasOptionalReference Instance::GetEntityAlias(const AZ::EntityId& id) + EntityAliasOptionalReference Instance::GetEntityAlias(AZ::EntityId id) { - if (m_instanceToTemplateEntityIdMap.count(id)) - { - return m_instanceToTemplateEntityIdMap[id]; - } - - return AZStd::nullopt; + auto it = m_instanceToTemplateEntityIdMap.find(id); + return it != m_instanceToTemplateEntityIdMap.end() ? EntityAliasOptionalReference(it->second) + : EntityAliasOptionalReference(AZStd::nullopt); } - AZ::EntityId Instance::GetEntityId(const EntityAlias& alias) + EntityAliasView Instance::GetEntityAlias(AZ::EntityId id) const { - if (m_templateToInstanceEntityIdMap.count(alias)) + auto it = m_instanceToTemplateEntityIdMap.find(id); + return it != m_instanceToTemplateEntityIdMap.end() ? EntityAliasView(it->second) : EntityAliasView(); + } + + AZStd::pair Instance::FindInstanceAndAlias(AZ::EntityId entity) + { + auto it = m_instanceToTemplateEntityIdMap.find(entity); + if (it != m_instanceToTemplateEntityIdMap.end()) { - return m_templateToInstanceEntityIdMap[alias]; + return AZStd::pair(this, it->second); } - - return AZ::EntityId(); + else + { + for (auto&& [_, instance] : m_nestedInstances) + { + AZStd::pair next = instance->FindInstanceAndAlias(entity); + if (next.first != nullptr) + { + return next; + } + } + } + return AZStd::pair(nullptr, ""); + } + + AZStd::pair Instance::FindInstanceAndAlias(AZ::EntityId entity) const + { + return const_cast(this)->FindInstanceAndAlias(entity); + } + + EntityOptionalReference Instance::GetEntity(const EntityAlias& alias) + { + auto it = m_entities.find(alias); + return it != m_entities.end() ? EntityOptionalReference(*it->second) : EntityOptionalReference(AZStd::nullopt); + } + + EntityOptionalConstReference Instance::GetEntity(const EntityAlias& alias) const + { + auto it = m_entities.find(alias); + return it != m_entities.end() ? EntityOptionalConstReference(*it->second) : EntityOptionalConstReference(AZStd::nullopt); + } + + AZ::EntityId Instance::GetEntityId(const EntityAlias& alias) const + { + auto it = m_templateToInstanceEntityIdMap.find(alias); + return it != m_templateToInstanceEntityIdMap.end() ? it->second : AZ::EntityId(); + } + + AZ::EntityId Instance::GetEntityIdFromAliasPath(AliasPathView relativeAliasPath) const + { + const Instance* instance = this; + AliasPathView path = relativeAliasPath.ParentPath(); + for (auto it : path) + { + InstanceOptionalConstReference child = instance->FindNestedInstance(it.Native()); + if (child.has_value()) + { + instance = &(child->get()); + } + else + { + return AZ::EntityId(); + } + } + + return instance->GetEntityId(relativeAliasPath.Filename().Native()); } AZStd::vector Instance::GetNestedInstanceAliases(TemplateId templateId) const @@ -572,6 +665,32 @@ namespace AzToolsFramework return aliasPathResult; } + AliasPath Instance::GetAliasPathRelativeToInstance(const AZ::EntityId& entity) const + { + AliasPath result = AliasPath(s_aliasPathSeparator); + auto&& [instance, alias] = FindInstanceAndAlias(entity); + if (instance) + { + AZStd::vector instanceChain; + + while (instance && instance != this) + { + instanceChain.push_back(instance); + instance = instance->m_parent; + } + + for (auto it = instanceChain.rbegin(); it != instanceChain.rend(); ++it) + { + result.Append((*it)->m_alias); + } + return result.Append(alias); + } + else + { + return result; + } + } + EntityAlias Instance::GenerateEntityAlias() { return AZStd::string::format("Entity_%s", AZ::Entity::MakeId().ToString().c_str()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h index 50a39268fe..25971093cd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/Instance.h @@ -38,6 +38,7 @@ namespace AzToolsFramework using AliasPath = AZ::IO::Path; using AliasPathView = AZ::IO::PathView; using EntityAlias = AZStd::string; + using EntityAliasView = AZStd::string_view; using InstanceAlias = AZStd::string; class Instance; @@ -83,9 +84,17 @@ namespace AzToolsFramework void SetContainerEntityName(AZStd::string_view containerName); bool AddEntity(AZ::Entity& entity); + bool AddEntity(AZStd::unique_ptr&& entity); bool AddEntity(AZ::Entity& entity, EntityAlias entityAlias); + bool AddEntity(AZStd::unique_ptr&& entity, EntityAlias entityAlias); AZStd::unique_ptr DetachEntity(const AZ::EntityId& entityId); void DetachEntities(const AZStd::function)>& callback); + /** + * Replaces the entity stored under the provided alias with a new one. + * + * @return The original entity or a nullptr if not found. + */ + AZStd::unique_ptr ReplaceEntity(AZStd::unique_ptr&& entity, EntityAliasView alias); /** * Detaches all entities in the instance hierarchy. @@ -109,13 +118,15 @@ namespace AzToolsFramework * @return The list of EntityAliases */ AZStd::vector GetEntityAliases(); + size_t GetEntityAliasCount() const; /** * Gets the ids for the entities in the Instance DOM. Can recursively trace all nested instances. */ - void GetNestedEntityIds(const AZStd::function& callback); + void GetNestedEntityIds(const AZStd::function& callback) const; - void GetEntityIds(const AZStd::function& callback); + void GetEntityIds(const AZStd::function& callback) const; + void GetEntityIdToAlias(const AZStd::function& callback) const; /** * Gets the entities in the Instance DOM. Can recursively trace all nested instances. @@ -131,14 +142,33 @@ namespace AzToolsFramework * * @return entityAlias via optional */ - AZStd::optional> GetEntityAlias(const AZ::EntityId& id); + EntityAliasOptionalReference GetEntityAlias(AZ::EntityId id); + EntityAliasView GetEntityAlias(AZ::EntityId id) const; + /** + * Searches for the entity in this instance and its nested instances. + * + * @return The instance that owns the entity and the alias under which the entity is known. + * If the entity isn't found then the instance will be null and the alias empty. + */ + AZStd::pair FindInstanceAndAlias(AZ::EntityId entity); + AZStd::pair FindInstanceAndAlias(AZ::EntityId entity) const; + + EntityOptionalReference GetEntity(const EntityAlias& alias); + EntityOptionalConstReference GetEntity(const EntityAlias& alias) const; /** * Gets the id for a given EnitityAlias in the Instance DOM. * * @return entityId, invalid ID if not found */ - AZ::EntityId GetEntityId(const EntityAlias& alias); + AZ::EntityId GetEntityId(const EntityAlias& alias) const; + + /** + * Retrieves the entity id from an alias path that's relative to this instance. + * + * @return entityId, invalid ID if not found + */ + AZ::EntityId GetEntityIdFromAliasPath(AliasPathView relativeAliasPath) const; /** @@ -180,6 +210,7 @@ namespace AzToolsFramework static EntityAlias GenerateEntityAlias(); AliasPath GetAbsoluteInstanceAliasPath() const; + AliasPath GetAliasPathRelativeToInstance(const AZ::EntityId& entity) const; static InstanceAlias GenerateInstanceAlias(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabCatchmentProcessor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabCatchmentProcessor.cpp index 7b7107ae3e..7a54950c47 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabCatchmentProcessor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabCatchmentProcessor.cpp @@ -13,9 +13,12 @@ #include #include #include +#include +#include #include #include + namespace AzToolsFramework::Prefab::PrefabConversionUtils { void PrefabCatchmentProcessor::Process(PrefabProcessorContext& context) @@ -37,7 +40,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils ->Value("Text", SerializationFormats::Text); serializeContext->Class() - ->Version(2) + ->Version(3) ->Field("SerializationFormat", &PrefabCatchmentProcessor::m_serializationFormat); } } @@ -45,6 +48,8 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils void PrefabCatchmentProcessor::ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab, AZ::DataStream::StreamType serializationFormat) { + using namespace AzToolsFramework::Prefab::SpawnableUtils; + AZStd::string uniqueName = prefabName; uniqueName += AzFramework::Spawnable::DotFileExtension; @@ -59,33 +64,38 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils AZStd::move(uniqueName), context.GetSourceUuid(), AZStd::move(serializer)); AZ_Assert(spawnable, "Failed to create a new spawnable."); - bool result = SpawnableUtils::CreateSpawnable(*spawnable, prefab, object.GetReferencedAssets()); - if (result) + Instance instance; + if (Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom( + instance, prefab, object.GetReferencedAssets(), + Prefab::PrefabDomUtils::LoadFlags::AssignRandomEntityId)) // Always assign random entity ids because the spawnable is + // going to be used to create clones of the entities. { + // Resolve entity aliases that store PrefabDOM information to use the spawnable instead. This is done before the entities are + // moved from the instance as they'd otherwise can't be found. + context.ResolveSpawnableEntityAliases(prefabName, *spawnable, instance); + AzFramework::Spawnable::EntityList& entities = spawnable->GetEntities(); - for (auto it = entities.begin(); it != entities.end(); ) - { - if (*it) + instance.DetachAllEntitiesInHierarchy( + [&entities, &context](AZStd::unique_ptr entity) { - (*it)->InvalidateDependencies(); - AZ::Entity::DependencySortOutcome evaluation = (*it)->EvaluateDependenciesGetDetails(); - if (evaluation.IsSuccess()) + if (entity) { - ++it; + entity->InvalidateDependencies(); + AZ::Entity::DependencySortOutcome evaluation = entity->EvaluateDependenciesGetDetails(); + if (evaluation.IsSuccess()) + { + entities.emplace_back(AZStd::move(entity)); + } + else + { + AZ_Error( + "Prefabs", false, "Entity '%s' %s cannot be activated for the following reason: %s", + entity->GetName().c_str(), entity->GetId().ToString().c_str(), evaluation.GetError().m_message.c_str()); + context.ErrorEncountered(); + } } - else - { - AZ_Error( - "Prefabs", false, "Entity '%s' %s cannot be activated for the following reason: %s", (*it)->GetName().c_str(), - (*it)->GetId().ToString().c_str(), evaluation.GetError().m_message.c_str()); - it = entities.erase(it); - } - } - else - { - it = entities.erase(it); - } - } + }); + SpawnableUtils::SortEntitiesByTransformHierarchy(*spawnable); context.GetProcessedObjects().push_back(AZStd::move(object)); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.cpp index ac3ada7557..c40f0d2b2e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.cpp @@ -54,6 +54,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils { processor->Process(context); } + context.ResolveLinks(); } size_t PrefabConversionPipeline::CalculateProcessorFingerprint(AZ::SerializeContext* context) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp index 0a7fb3a224..e12ddb616f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp @@ -6,12 +6,26 @@ * */ +#include #include - +#include #include +#include namespace AzToolsFramework::Prefab::PrefabConversionUtils { + EntityAliasSpawnableLink::EntityAliasSpawnableLink(AzFramework::Spawnable& spawnable, AZ::EntityId index) + : m_spawnable(spawnable) + , m_index(index) + { + } + + EntityAliasPrefabLink::EntityAliasPrefabLink(AZStd::string prefabName, AzToolsFramework::Prefab::AliasPath alias) + : m_prefabName(AZStd::move(prefabName)) + , m_alias(AZStd::move(alias)) + { + } + PrefabProcessorContext::PrefabProcessorContext(const AZ::Uuid& sourceUuid) : m_sourceUuid(sourceUuid) {} @@ -45,7 +59,8 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils return !m_prefabs.empty(); } - bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency(AZStd::string prefabName, AZStd::string dependentPrefabName) + bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency( + AZStd::string prefabName, AZStd::string dependentPrefabName, EntityAliasSpawnableLoadBehavior loadBehavior) { using ConversionUtils = PrefabConversionUtils::ProcessedObjectStore; @@ -55,10 +70,11 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils dependentPrefabName += AzFramework::Spawnable::DotFileExtension; uint32_t spawnablePrefabSubId = ConversionUtils::BuildSubId(AZStd::move(dependentPrefabName)); - return RegisterSpawnableProductAssetDependency(spawnableSubId, spawnablePrefabSubId); + return RegisterSpawnableProductAssetDependency(spawnableSubId, spawnablePrefabSubId, loadBehavior); } - bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency(AZStd::string prefabName, const AZ::Data::AssetId& dependentAssetId) + bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency( + AZStd::string prefabName, const AZ::Data::AssetId& dependentAssetId, EntityAliasSpawnableLoadBehavior loadBehavior) { using ConversionUtils = PrefabConversionUtils::ProcessedObjectStore; @@ -67,20 +83,78 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils AZ::Data::AssetId spawnableAssetId(GetSourceUuid(), spawnableSubId); - return RegisterProductAssetDependency(spawnableAssetId, dependentAssetId); + return RegisterProductAssetDependency(spawnableAssetId, dependentAssetId, ToAssetLoadBehavior(loadBehavior)); } - bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency(uint32_t spawnableAssetSubId, uint32_t dependentSpawnableAssetSubId) + bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency( + uint32_t spawnableAssetSubId, uint32_t dependentSpawnableAssetSubId, EntityAliasSpawnableLoadBehavior loadBehavior) { AZ::Data::AssetId spawnableAssetId(GetSourceUuid(), spawnableAssetSubId); AZ::Data::AssetId dependentSpawnableAssetId(GetSourceUuid(), dependentSpawnableAssetSubId); - return RegisterProductAssetDependency(spawnableAssetId, dependentSpawnableAssetId); + return RegisterProductAssetDependency(spawnableAssetId, dependentSpawnableAssetId, ToAssetLoadBehavior(loadBehavior)); } bool PrefabProcessorContext::RegisterProductAssetDependency(const AZ::Data::AssetId& assetId, const AZ::Data::AssetId& dependentAssetId) { - return m_registeredProductAssetDependencies[assetId].emplace(dependentAssetId).second; + return RegisterProductAssetDependency(assetId, dependentAssetId, AZ::Data::AssetLoadBehavior::NoLoad); + } + + bool PrefabProcessorContext::RegisterProductAssetDependency( + const AZ::Data::AssetId& assetId, const AZ::Data::AssetId& dependentAssetId, AZ::Data::AssetLoadBehavior loadBehavior) + { + auto dependencies = m_registeredProductAssetDependencies.equal_range(assetId); + if (dependencies.first != dependencies.second) + { + for (auto it = dependencies.first; it != dependencies.second; ++it) + { + if (it->second.m_assetId == dependentAssetId) + { + if (it->second.m_loadBehavior < loadBehavior) + { + it->second.m_loadBehavior = loadBehavior; + } + return true; + } + } + } + + return m_registeredProductAssetDependencies.emplace(assetId, AssetDependencyInfo{ dependentAssetId, loadBehavior }).second; + } + + void PrefabProcessorContext::RegisterSpawnableEntityAlias(EntityAliasStore link) + { + m_entityAliases.push_back(AZStd::move(link)); + } + + void PrefabProcessorContext::ResolveSpawnableEntityAliases( + AZStd::string_view prefabName, AzFramework::Spawnable& spawnable, const AzToolsFramework::Prefab::Instance& instance) + { + using namespace AzToolsFramework::Prefab; + + for (EntityAliasStore& entityAlias : m_entityAliases) + { + auto sourcePrefab = AZStd::get_if(&entityAlias.m_source); + if (sourcePrefab != nullptr && sourcePrefab->m_prefabName == prefabName) + { + AZ::EntityId id = instance.GetEntityIdFromAliasPath(sourcePrefab->m_alias); + AZ_Assert( + id.IsValid(), + "Entity '%s' was not found in Prefab Instance created from '%s' even though it was previously found.", + sourcePrefab->m_alias.c_str(), sourcePrefab->m_prefabName.c_str()); + entityAlias.m_source.emplace(spawnable, id); + } + + auto targetPrefab = AZStd::get_if(&entityAlias.m_target); + if (targetPrefab != nullptr && targetPrefab->m_prefabName == prefabName) + { + AZ::EntityId id = instance.GetEntityIdFromAliasPath(targetPrefab->m_alias); + AZ_Assert( + id.IsValid(), "Entity '%s' was not found in Prefab Instance created from '%s' even though it was previously found.", + targetPrefab->m_alias.c_str(), targetPrefab->m_prefabName.c_str()); + entityAlias.m_target.emplace(spawnable, id); + } + } } PrefabProcessorContext::ProcessedObjectStoreContainer& PrefabProcessorContext::GetProcessedObjects() @@ -118,6 +192,46 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils return m_sourceUuid; } + void PrefabProcessorContext::ResolveLinks() + { + // Store the aliases visitor here when first encountered to avoid the visitor sorting the aliases for every addition. + // Once this map goes out of scope the visitors will be destroyed and in turn sort their aliases. + AZStd::unordered_map aliasVisitors; + + for (EntityAliasStore& alias : m_entityAliases) + { + auto source = AZStd::get_if(&alias.m_source); + AZ_Assert(source, "Entity alias found that has a source that's not yet resolved to a spawnable"); + auto target = AZStd::get_if(&alias.m_target); + AZ_Assert(target, "Entity alias found that has a target that's not yet resolved to a spawnable"); + + uint32_t sourceIndex = SpawnableUtils::FindEntityIndex(source->m_index, source->m_spawnable); + AZ_Assert( + sourceIndex != SpawnableUtils::InvalidEntityIndex, "Entity %zu not found in source spawnable while resolving to index.", + aznumeric_cast(source->m_index)); + uint32_t targetIndex = SpawnableUtils::FindEntityIndex(target->m_index, target->m_spawnable); + AZ_Assert( + targetIndex != SpawnableUtils::InvalidEntityIndex, "Entity %zu not found in target spawnable while resolving to index.", + aznumeric_cast(target->m_index)); + + AZ::Data::AssetLoadBehavior loadBehavior = ToAssetLoadBehavior(alias.m_loadBehavior); + + auto it = aliasVisitors.find(source->m_spawnable.GetId()); + if (it == aliasVisitors.end()) + { + AzFramework::Spawnable::EntityAliasVisitor visitor = source->m_spawnable.TryGetAliases(); + AZ_Assert(visitor.HasLock(), "Unable to obtain lock for a newly create spawnable."); + it = aliasVisitors.emplace(source->m_spawnable.GetId(), AZStd::move(visitor)).first; + } + it->second.AddAlias( + AZ::Data::Asset(&target->m_spawnable, loadBehavior), alias.m_tag, sourceIndex, targetIndex, + alias.m_aliasType, alias.m_loadBehavior == EntityAliasSpawnableLoadBehavior::QueueLoad); + + // Register the dependency between the two spawnables. + RegisterProductAssetDependency(source->m_spawnable.GetId(), target->m_spawnable.GetId(), loadBehavior); + } + } + bool PrefabProcessorContext::HasCompletedSuccessfully() const { return m_completedSuccessfully; @@ -127,4 +241,10 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils { m_completedSuccessfully = false; } + + AZ::Data::AssetLoadBehavior PrefabProcessorContext::ToAssetLoadBehavior(EntityAliasSpawnableLoadBehavior loadBehavior) const + { + return loadBehavior == EntityAliasSpawnableLoadBehavior::DependentLoad ? AZ::Data::AssetLoadBehavior::PreLoad + : AZ::Data::AssetLoadBehavior::NoLoad; + } } // namespace AzToolsFramework::Prefab::PrefabConversionUtils diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h index 7c21d446de..7fc01367b2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h @@ -9,24 +9,84 @@ #pragma once #include +#include #include #include #include +#include #include #include #include #include +#include +#include #include #include namespace AzToolsFramework::Prefab::PrefabConversionUtils { + enum class EntityAliasType : uint8_t + { + Disabled, //!< No alias is added. + OptionalReplace, //!< At runtime the entity might be replaced. If the alias is disabled the original entity will be spawned. + //!< The original entity will be left in the spawnable and a copy is returned. + Replace, //!< At runtime the entity will be replaced. If the alias is disabled nothing will be spawned not. The original + //!< entity is returned and a blank entity is left. + Additional, //!< At runtime the alias entity will be added as an additional but unrelated entity with a new entity id. + //!< An empty entity will be returned. + Merge //!< At runtime the components in both entities will be merged. An empty entity will be returned. The added + //!< components may no conflict with the entities already in the root entity. + }; + + enum class EntityAliasSpawnableLoadBehavior : uint8_t + { + NoLoad, //!< Don't load the spawnable referenced in the entity alias. Loading will be up to the caller. + QueueLoad, //!< Queue the spawnable referenced in the entity alias for loading. This will be an async load because asset + //!< handlers aren't allowed to start a blocking load as this can lead to deadlocks. + DependentLoad //!< The spawnable referenced in the entity alias is made a dependency of the spawnable that holds the entity + //!< alias. This will cause the spawnable to be automatically loaded along with the owning spawnable. + }; + + struct EntityAliasSpawnableLink + { + EntityAliasSpawnableLink() = default; + EntityAliasSpawnableLink(AzFramework::Spawnable& spawnable, AZ::EntityId index); + + AzFramework::Spawnable& m_spawnable; + AZ::EntityId m_index; + }; + + struct EntityAliasPrefabLink + { + EntityAliasPrefabLink() = default; + EntityAliasPrefabLink(AZStd::string prefabName, AzToolsFramework::Prefab::AliasPath alias); + + AZStd::string m_prefabName; + AzToolsFramework::Prefab::AliasPath m_alias; + }; + + struct EntityAliasStore + { + using LinkStore = AZStd::variant; + + LinkStore m_source; + LinkStore m_target; + uint32_t m_tag; + AzFramework::Spawnable::EntityAliasType m_aliasType; + EntityAliasSpawnableLoadBehavior m_loadBehavior; + }; + + struct AssetDependencyInfo + { + AZ::Data::AssetId m_assetId; + AZ::Data::AssetLoadBehavior m_loadBehavior; + }; + class PrefabProcessorContext { public: using ProcessedObjectStoreContainer = AZStd::vector; - using ProductAssetDependencyContainer = - AZStd::unordered_map>; + using ProductAssetDependencyContainer = AZStd::unordered_multimap; AZ_CLASS_ALLOCATOR(PrefabProcessorContext, AZ::SystemAllocator, 0); AZ_RTTI(PrefabProcessorContext, "{C7D77E3A-C544-486B-B774-7C82C38FE22F}"); @@ -39,11 +99,20 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils virtual void ListPrefabs(const AZStd::function& callback) const; virtual bool HasPrefabs() const; - virtual bool RegisterSpawnableProductAssetDependency(AZStd::string prefabName, AZStd::string dependentPrefabName); - virtual bool RegisterSpawnableProductAssetDependency(AZStd::string prefabName, const AZ::Data::AssetId& dependentAssetId); - virtual bool RegisterSpawnableProductAssetDependency(uint32_t spawnableAssetSubId, uint32_t dependentSpawnableAssetSubId); + virtual bool RegisterSpawnableProductAssetDependency( + AZStd::string prefabName, AZStd::string dependentPrefabName, EntityAliasSpawnableLoadBehavior loadBehavior); + virtual bool RegisterSpawnableProductAssetDependency( + AZStd::string prefabName, const AZ::Data::AssetId& dependentAssetId, EntityAliasSpawnableLoadBehavior loadBehavior); + virtual bool RegisterSpawnableProductAssetDependency( + uint32_t spawnableAssetSubId, uint32_t dependentSpawnableAssetSubId, EntityAliasSpawnableLoadBehavior loadBehavior); virtual bool RegisterProductAssetDependency(const AZ::Data::AssetId& assetId, const AZ::Data::AssetId& dependentAssetId); + virtual bool RegisterProductAssetDependency( + const AZ::Data::AssetId& assetId, const AZ::Data::AssetId& dependentAssetId, AZ::Data::AssetLoadBehavior loadBehavior); + virtual void RegisterSpawnableEntityAlias(EntityAliasStore link); + virtual void ResolveSpawnableEntityAliases( + AZStd::string_view prefabName, AzFramework::Spawnable& spawnable, const AzToolsFramework::Prefab::Instance& instance); + virtual ProcessedObjectStoreContainer& GetProcessedObjects(); virtual const ProcessedObjectStoreContainer& GetProcessedObjects() const; @@ -54,13 +123,19 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils virtual const AZ::PlatformTagSet& GetPlatformTags() const; virtual const AZ::Uuid& GetSourceUuid() const; + virtual void ResolveLinks(); + virtual bool HasCompletedSuccessfully() const; virtual void ErrorEncountered(); protected: using NamedPrefabContainer = AZStd::unordered_map; + using SpawnableEntityAliasStore = AZStd::vector; + + AZ::Data::AssetLoadBehavior ToAssetLoadBehavior(EntityAliasSpawnableLoadBehavior loadBehavior) const; NamedPrefabContainer m_prefabs; + SpawnableEntityAliasStore m_entityAliases; ProcessedObjectStoreContainer m_products; ProductAssetDependencyContainer m_registeredProductAssetDependencies; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp index 9c59b6b559..a6770ee2c7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.cpp @@ -11,7 +11,16 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils { - ProcessedObjectStore::ProcessedObjectStore(AZStd::string uniqueId, AZStd::unique_ptr asset, SerializerFunction assetSerializer) + void ProcessedObjectStore::AssetSmartPtrDeleter::operator()(AZ::Data::AssetData* asset) + { + if (asset->GetUseCount() == 0) + { + // Only delete the asset if it wasn't turned into a full asset + delete asset; + } + } + + ProcessedObjectStore::ProcessedObjectStore(AZStd::string uniqueId, AssetSmartPtr asset, SerializerFunction assetSerializer) : m_uniqueId(AZStd::move(uniqueId)) , m_assetSerializer(AZStd::move(assetSerializer)) , m_asset(AZStd::move(asset)) @@ -62,7 +71,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils return m_referencedAssets; } - AZStd::unique_ptr ProcessedObjectStore::ReleaseAsset() + auto ProcessedObjectStore::ReleaseAsset() -> AssetSmartPtr { return AZStd::move(m_asset); } @@ -72,6 +81,11 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils return AzFramework::SpawnableAssetHandler::BuildSubId(id); } + uint32_t ProcessedObjectStore::GetSubId() const + { + return m_asset->GetId().m_subId; + } + const AZStd::string& ProcessedObjectStore::GetId() const { return m_uniqueId; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h index 61d529842c..c75e72840e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h @@ -26,6 +26,12 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils public: using SerializerFunction = AZStd::function&, const ProcessedObjectStore&)>; + struct AssetSmartPtrDeleter + { + void operator()(AZ::Data::AssetData* asset); + }; + using AssetSmartPtr = AZStd::unique_ptr; + //! Constructs a new instance. //! @param uniqueId A name for the object that's unique within the scope of the Prefab. This name will be used to generate a sub id for the product //! which requires that the name to be stable between runs. @@ -37,24 +43,24 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils bool Serialize(AZStd::vector& output) const; static uint32_t BuildSubId(AZStd::string_view id); + uint32_t GetSubId() const; bool HasAsset() const; const AZ::Data::AssetType& GetAssetType() const; const AZ::Data::AssetData& GetAsset() const; AZ::Data::AssetData& GetAsset(); - AZStd::unique_ptr ReleaseAsset(); + AssetSmartPtr ReleaseAsset(); AZStd::vector>& GetReferencedAssets(); const AZStd::vector>& GetReferencedAssets() const; - const AZStd::string& GetId() const; private: - ProcessedObjectStore(AZStd::string uniqueId, AZStd::unique_ptr asset, SerializerFunction assetSerializer); + ProcessedObjectStore(AZStd::string uniqueId, AssetSmartPtr asset, SerializerFunction assetSerializer); SerializerFunction m_assetSerializer; - AZStd::unique_ptr m_asset; + AssetSmartPtr m_asset; AZStd::vector> m_referencedAssets; AZStd::string m_uniqueId; }; @@ -66,7 +72,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils static_assert(AZStd::is_base_of_v, "ProcessedObjectStore can only be created from a class that derives from AZ::Data::AssetData."); AZ::Data::AssetId assetId(sourceId, BuildSubId(uniqueId)); - auto instance = AZStd::make_unique(assetId, AZ::Data::AssetData::AssetStatus::Ready); + auto instance = AssetSmartPtr(aznew T(assetId, AZ::Data::AssetData::AssetStatus::Ready)); ProcessedObjectStore resultLeft(AZStd::move(uniqueId), AZStd::move(instance), AZStd::move(assetSerializer)); T* resultRight = static_cast(&resultLeft.GetAsset()); return AZStd::make_pair(AZStd::move(resultLeft), resultRight); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp index 902f439280..154337e957 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp @@ -8,9 +8,11 @@ #include +#include #include #include #include +#include #include #include #include @@ -20,18 +22,134 @@ namespace AzToolsFramework::Prefab::SpawnableUtils { + namespace Internal + { + AZ::SerializeContext* GetSerializeContext() + { + AZ::SerializeContext* result = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(result, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + AZ_Assert(result, "SpawnbleUtils was unable to locate the Serialize Context."); + return result; + } + + AZ::Entity* FindEntity(AZ::EntityId entity, AzToolsFramework::Prefab::Instance& source) + { + AZ::Entity* result = nullptr; + source.GetEntities( + [&result, entity](AZStd::unique_ptr& instance) + { + if (instance->GetId() != entity) + { + return true; + } + else + { + result = instance.get(); + return false; + } + }); + return result; + } + + AZ::Entity* FindEntity(AZ::EntityId entity, AzFramework::Spawnable& source) + { + uint32_t index = AzToolsFramework::Prefab::SpawnableUtils::FindEntityIndex(entity, source); + return index != InvalidEntityIndex ? source.GetEntities()[index].get() : nullptr; + } + + template + AZStd::unique_ptr CloneEntity(AZ::EntityId entity, T& source) + { + AZ::Entity* target = Internal::FindEntity(entity, source); + AZ_Assert( + target, "SpawnbleUtils were unable to locate entity with id %zu in Instance or Spawnable for cloning.", + aznumeric_cast(entity)); + auto clone = AZStd::make_unique(); + + static AZ::SerializeContext* sc = GetSerializeContext(); + sc->CloneObjectInplace(*clone, target); + clone->SetId(AZ::Entity::MakeId()); + + return clone; + } + + AZStd::unique_ptr ReplaceEntityWithPlaceholder(AZ::EntityId entity, AzToolsFramework::Prefab::Instance& source) + { + auto&& [instance, alias] = source.FindInstanceAndAlias(entity); + AZ_Assert( + instance, "SpawnbleUtils were unable to locate entity alias with id %zu in Instance '%s' for replacing.", + aznumeric_cast(entity), source.GetTemplateSourcePath().c_str()); + + EntityOptionalReference entityData = instance->GetEntity(alias); + AZ_Assert( + entityData.has_value(), "SpawnbleUtils were unable to locate entity '%.*s' in Instance '%s' for replacing.", + AZ_STRING_ARG(alias), source.GetTemplateSourcePath().c_str()); + auto placeholder = AZStd::make_unique(entityData->get().GetId(), entityData->get().GetName()); + return instance->ReplaceEntity(AZStd::move(placeholder), alias); + } + + AZStd::unique_ptr ReplaceEntityWithPlaceholder(AZ::EntityId entity, AzFramework::Spawnable& source) + { + uint32_t index = AzToolsFramework::Prefab::SpawnableUtils::FindEntityIndex(entity, source); + AZ_Assert( + index != InvalidEntityIndex, "SpawnbleUtils were unable to locate entity alias with id %zu in Spawnable for replacing.", + aznumeric_cast(entity)); + + AZStd::unique_ptr original = AZStd::move(source.GetEntities()[index]); + AZ_Assert( + original, "SpawnbleUtils were unable to locate entity with id %zu in Spawnable for replacing.", + aznumeric_cast(entity)); + + source.GetEntities()[index] = AZStd::make_unique(original->GetId(), original->GetName()); + + return original; + } + + template + AZStd::pair, AzFramework::Spawnable::EntityAliasType> ApplyAlias( + Source& source, AZ::EntityId entity, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType) + { + namespace PCU = AzToolsFramework::Prefab::PrefabConversionUtils; + using ResultPair = AZStd::pair, AzFramework::Spawnable::EntityAliasType>; + + switch (aliasType) + { + case PCU::EntityAliasType::Disabled: + // No need to do anything as the alias is disabled. + return ResultPair(nullptr, AzFramework::Spawnable::EntityAliasType::Disabled); + case PCU::EntityAliasType::OptionalReplace: + return ResultPair(CloneEntity(entity, source), AzFramework::Spawnable::EntityAliasType::Replace); + case PCU::EntityAliasType::Replace: + return ResultPair(ReplaceEntityWithPlaceholder(entity, source), AzFramework::Spawnable::EntityAliasType::Replace); + case PCU::EntityAliasType::Additional: + ResultPair(AZStd::make_unique(AZ::Entity::MakeId()), AzFramework::Spawnable::EntityAliasType::Additional); + case PCU::EntityAliasType::Merge: + // Use the same entity id as the original entity so at runtime the entity ids can be verified to match. + ResultPair(AZStd::make_unique(entity), AzFramework::Spawnable::EntityAliasType::Merge); + default: + AZ_Assert( + false, "Invalid PrefabProcessorContext::EntityAliasType type (%i) provided.", aznumeric_cast(aliasType)); + return ResultPair(nullptr, AzFramework::Spawnable::EntityAliasType::Disabled); + } + } + } + bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom) { AZStd::vector> referencedAssets; return CreateSpawnable(spawnable, prefabDom, referencedAssets); } - bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom, AZStd::vector>& referencedAssets) + bool CreateSpawnable( + AzFramework::Spawnable& spawnable, + const PrefabDom& prefabDom, + AZStd::vector>& referencedAssets) { Instance instance; - if (Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(instance, prefabDom, referencedAssets, - Prefab::PrefabDomUtils::LoadFlags::AssignRandomEntityId)) // Always assign random entity ids because the spawnable is - // going to be used to create clones of the entities. + if (Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom( + instance, prefabDom, referencedAssets, + Prefab::PrefabDomUtils::LoadFlags::AssignRandomEntityId)) // Always assign random entity ids because the spawnable is + // going to be used to create clones of the entities. { AzFramework::Spawnable::EntityList& entities = spawnable.GetEntities(); instance.DetachAllEntitiesInHierarchy( @@ -47,6 +165,122 @@ namespace AzToolsFramework::Prefab::SpawnableUtils } } + AZ::Entity* CreateEntityAlias( + AZStd::string sourcePrefabName, + AzToolsFramework::Prefab::Instance& source, + AZStd::string targetPrefabName, + AzToolsFramework::Prefab::Instance& target, + AZ::EntityId entity, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, + uint32_t tag, + AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context) + { + using namespace AzToolsFramework::Prefab::PrefabConversionUtils; + + AliasPath alias = source.GetAliasPathRelativeToInstance(entity); + if (!alias.empty()) + { + auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entity, aliasType); + + AZ::Entity* result = replacement.get(); + target.AddEntity(AZStd::move(replacement), alias.Filename().Native()); + + EntityAliasStore store; + store.m_aliasType = storedAliasType; + store.m_source.emplace(AZStd::move(sourcePrefabName), AZStd::move(alias)); + store.m_target.emplace( + AZStd::move(targetPrefabName), target.GetAliasPathRelativeToInstance(result->GetId())); + store.m_loadBehavior = loadBehavior; + store.m_tag = tag; + context.RegisterSpawnableEntityAlias(AZStd::move(store)); + + return result; + } + else + { + AZ_Assert(false, "Entity with id %zu was not found in the source prefab.", static_cast(entity)); + return nullptr; + } + } + + AZ::Entity* CreateEntityAlias( + AZStd::string sourcePrefabName, + AzToolsFramework::Prefab::Instance& source, + AzFramework::Spawnable& target, + AZ::EntityId entity, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, + uint32_t tag, + AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context) + { + using namespace AzToolsFramework::Prefab::PrefabConversionUtils; + + AliasPath alias = source.GetAliasPathRelativeToInstance(entity); + if (!alias.empty()) + { + auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entity, aliasType); + + AZ::Entity* result = replacement.get(); + target.GetEntities().push_back(AZStd::move(replacement)); + + EntityAliasStore store; + store.m_aliasType = storedAliasType; + store.m_source.emplace(AZStd::move(sourcePrefabName), AZStd::move(alias)); + store.m_target.emplace(target, result->GetId()); + store.m_tag = tag; + store.m_loadBehavior = loadBehavior; + context.RegisterSpawnableEntityAlias(AZStd::move(store)); + + return result; + } + else + { + AZ_Assert(false, "Entity with id %zu was not found in the source prefab.", static_cast(entity)); + return nullptr; + } + } + + AZ::Entity* CreateEntityAlias( + AzFramework::Spawnable& source, + AzFramework::Spawnable& target, + AZ::EntityId entity, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, + uint32_t tag, + AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context) + { + using namespace AzToolsFramework::Prefab::PrefabConversionUtils; + + auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entity, aliasType); + AZ::Entity* result = replacement.get(); + target.GetEntities().push_back(AZStd::move(replacement)); + + EntityAliasStore store; + store.m_aliasType = storedAliasType; + store.m_source.emplace(source, entity); + store.m_target.emplace(target, result->GetId()); + store.m_tag = tag; + store.m_loadBehavior = loadBehavior; + context.RegisterSpawnableEntityAlias(AZStd::move(store)); + + return result; + } + + uint32_t FindEntityIndex(AZ::EntityId entity, const AzFramework::Spawnable& spawnable) + { + auto begin = spawnable.GetEntities().begin(); + auto end = spawnable.GetEntities().end(); + for(auto it = begin; it != end; ++it) + { + if ((*it)->GetId() == entity) + { + return AZStd::distance(begin, it); + } + } + return InvalidEntityIndex; + } + template void OrganizeEntitiesForSorting( AZStd::vector& entities, diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h index ffcf13e081..892b83455d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h @@ -8,14 +8,59 @@ #pragma once +#include +#include #include #include +#include + +namespace AZ +{ + class Entity; +} + +namespace AzToolsFramework::Prefab +{ + class Instance; +} namespace AzToolsFramework::Prefab::SpawnableUtils { + static constexpr uint32_t InvalidEntityIndex = AZStd::numeric_limits::max(); + bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom); bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom, AZStd::vector>& referencedAssets); + AZ::Entity* CreateEntityAlias( + AZStd::string sourcePrefabName, + AzToolsFramework::Prefab::Instance& source, + AZStd::string targetPrefabName, + AzToolsFramework::Prefab::Instance& target, + AZ::EntityId entity, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, + uint32_t tag, + AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context); + AZ::Entity* CreateEntityAlias( + AZStd::string sourcePrefabName, + AzToolsFramework::Prefab::Instance& source, + AzFramework::Spawnable& target, + AZ::EntityId entity, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, + uint32_t tag, + AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context); + AZ::Entity* CreateEntityAlias( + AzFramework::Spawnable& source, + AzFramework::Spawnable& target, + AZ::EntityId entity, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, + uint32_t tag, + AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context); + + uint32_t FindEntityIndex(AZ::EntityId entity, const AzFramework::Spawnable& spawnable); + void SortEntitiesByTransformHierarchy(AzFramework::Spawnable& spawnable); template diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderComponent.cpp b/Gems/Prefab/PrefabBuilder/PrefabBuilderComponent.cpp index a86d7b57e2..6107226783 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderComponent.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderComponent.cpp @@ -175,6 +175,8 @@ namespace AZ::Prefab const AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext::ProductAssetDependencyContainer& registeredDependencies, AZStd::vector& outputProducts) const { + using namespace AzToolsFramework::Prefab::PrefabConversionUtils; + outputProducts.reserve(store.size()); AZStd::vector data; @@ -211,17 +213,14 @@ namespace AZ::Prefab if (AssetBuilderSDK::OutputObject(&object.GetAsset(), object.GetAssetType(), productPath.String(), object.GetAssetType(), object.GetAsset().GetId().m_subId, product)) { - auto findRegisteredDependencies = registeredDependencies.find(object.GetAsset().GetId()); - if (findRegisteredDependencies != registeredDependencies.end()) - { - AZStd::transform(findRegisteredDependencies->second.begin(), findRegisteredDependencies->second.end(), - AZStd::back_inserter(product.m_dependencies), - [](const AZ::Data::AssetId& productId) -> AssetBuilderSDK::ProductDependency - { - return AssetBuilderSDK::ProductDependency(productId, - AZ::Data::ProductDependencyInfo::CreateFlags(AZ::Data::AssetLoadBehavior::NoLoad)); - }); - } + auto range = registeredDependencies.equal_range(object.GetAsset().GetId()); + AZStd::transform(range.first, range.second, + AZStd::back_inserter(product.m_dependencies), + [](const auto& dependency) -> AssetBuilderSDK::ProductDependency + { + return AssetBuilderSDK::ProductDependency( + dependency.second.m_assetId, AZ::Data::ProductDependencyInfo::CreateFlags(dependency.second.m_loadBehavior)); + }); outputProducts.push_back(AZStd::move(product)); } From 15ea380d3988bd79471c38a2e7524f7a3934bdb1 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Tue, 26 Oct 2021 10:43:27 -0700 Subject: [PATCH 008/177] Post integration fixes and additional changes for entity aliases in spawnables. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../AzFramework/Spawnable/Spawnable.cpp | 43 ++++++------------- .../AzFramework/Spawnable/Spawnable.h | 17 +++----- .../Spawnable/SpawnableEntitiesManager.cpp | 7 --- 3 files changed, 18 insertions(+), 49 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp index 92728a4575..48662fae99 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include @@ -138,9 +139,9 @@ namespace AzFramework Optimize(); AZ_Assert( - m_owner.m_lockState == LockState::Locked, "Attempting to unlock a spawnable that's not in the locked state (%i).", - m_owner.m_lockState.load()); - m_owner.m_lockState = LockState::Unlocked; + m_owner.m_shareState == ShareState::ReadWrite, "Attempting to unlock a spawnable that's not in the locked state (%i).", + m_owner.m_shareState.load()); + m_owner.m_shareState = ShareState::NotShared; } } @@ -397,9 +398,9 @@ namespace AzFramework if (HasLock()) { AZ_Assert( - m_owner.m_lockState < 0, "Attempting to unlock a read shared spawnable that was not in a read shared mode (%i).", - m_owner.m_lockState.load()); - m_owner.m_lockState++; + m_owner.m_shareState <= ShareState::Read, "Attempting to unlock a read shared spawnable that was not in a read shared mode (%i).", + m_owner.m_shareState.load()); + m_owner.m_shareState++; } } @@ -471,15 +472,15 @@ namespace AzFramework auto Spawnable::TryGetAliasesConst() const -> EntityAliasConstVisitor { - int32_t expected = LockState::Unlocked; + int32_t expected = ShareState::NotShared; do { // Try to set the lock to a negative number to indicate a shared read. - if (m_lockState.compare_exchange_strong(expected, expected - 1)) + if (m_shareState.compare_exchange_strong(expected, expected - 1)) { return EntityAliasConstVisitor(*this, &m_entityAliases); } - // as long as the value is negative keep trying to get a shared read lock. + // as long as the value is negative or not shared then keep trying to get a shared read lock. } while (expected <= 0); return EntityAliasConstVisitor(*this, nullptr); } @@ -491,9 +492,9 @@ namespace AzFramework auto Spawnable::TryGetAliases() -> EntityAliasVisitor { - int32_t expected = LockState::Unlocked; - return m_lockState.compare_exchange_strong(expected, LockState::Locked) ? EntityAliasVisitor(*this, &m_entityAliases) - : EntityAliasVisitor(*this, nullptr); + int32_t expected = ShareState::NotShared; + return m_shareState.compare_exchange_strong(expected, ShareState::ReadWrite) ? EntityAliasVisitor(*this, &m_entityAliases) + : EntityAliasVisitor(*this, nullptr); } bool Spawnable::IsEmpty() const @@ -501,24 +502,6 @@ namespace AzFramework return m_entities.empty(); } - bool Spawnable::IsPermanentlyLocked() const - { - return m_lockState == LockState::PermanentLock; - } - - bool Spawnable::LockPermanently() - { - if (!IsPermanentlyLocked()) - { - int32_t expected = LockState::Unlocked; - return m_lockState.compare_exchange_strong(expected, LockState::PermanentLock); - } - else - { - return true; - } - } - SpawnableMetaData& Spawnable::GetMetaData() { return m_metaData; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h index 05ae10e580..4fb1f84b6f 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h @@ -41,11 +41,11 @@ namespace AzFramework //!< maintaining a valid component list. }; - enum LockState : int32_t + enum ShareState : int32_t { - Unlocked, - Locked, - PermanentLock + Read = -1, + NotShared = 0, + ReadWrite = 1 }; //! An entity alias redirects the spawning of an entity to another entity, possibly in another spawnable. @@ -183,13 +183,6 @@ namespace AzFramework EntityAliasVisitor TryGetAliases(); bool IsEmpty() const; - //! Whether or not the spawnable is permanently locked. If so then parts of the spawnable can no longer be modified. - bool IsPermanentlyLocked() const; - //! Permanently locks access to parts of the spawnable from being modified. - //! @return True if the spawnable could be locked. If false is returned another operation is still making modifications. In this case - //! call this again at a later point in time. - bool LockPermanently(); - SpawnableMetaData& GetMetaData(); const SpawnableMetaData& GetMetaData() const; @@ -204,6 +197,6 @@ namespace AzFramework // Includes both direct and nested entities of the prefab. EntityList m_entities; - mutable AZStd::atomic m_lockState{ LockState::Unlocked }; + mutable AZStd::atomic m_shareState{ ShareState::NotShared }; }; } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 0ac80c3ed6..22918b7173 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -857,13 +857,6 @@ namespace AzFramework ticket.m_currentRequestId++; return CommandResult::Executed; } - else - { - AZ_Assert( - ticket.m_spawnable->IsPermanentlyLocked(), - "An request to UpdateEntityAliasTypes on the Spawnables Entities Manager was processed on a spawnable that's permanently " - "locked."); - } } return CommandResult::Requeue; } From 66df146554feb6cac2e841803ddabbbc06d608c2 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Tue, 26 Oct 2021 13:33:02 -0700 Subject: [PATCH 009/177] Fixed existing spawnable unit tests to work with entity alias changes. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../Spawnable/SpawnableEntitiesManager.cpp | 96 ++++++++----------- .../SpawnableEntitiesManagerTests.cpp | 10 +- 2 files changed, 43 insertions(+), 63 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 22918b7173..a018a55210 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -303,18 +303,11 @@ namespace AzFramework AZ::Entity* SpawnableEntitiesManager::CloneSingleEntity(const AZ::Entity& entityTemplate, EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext) { - if (!entityTemplate.GetComponents().empty()) - { - // If the same ID gets remapped more than once, preserve the original remapping instead of overwriting it. - constexpr bool allowDuplicateIds = false; + // If the same ID gets remapped more than once, preserve the original remapping instead of overwriting it. + constexpr bool allowDuplicateIds = false; - return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( - &entityTemplate, templateToCloneMap, &serializeContext); - } - else - { - return nullptr; - } + return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( + &entityTemplate, templateToCloneMap, &serializeContext); } AZ::Entity* SpawnableEntitiesManager::CloneSingleAliasedEntity( @@ -468,9 +461,8 @@ namespace AzFramework if (aliasIt == aliasEnd || aliasIt->m_sourceIndex != i) { - AZ::Entity* clone = - CloneSingleEntity(*entitiesToSpawn[i], ticket.m_entityIdReferenceMap, *request.m_serializeContext); - spawnedEntities.emplace_back(clone); + spawnedEntities.emplace_back( + CloneSingleEntity(*entitiesToSpawn[i], ticket.m_entityIdReferenceMap, *request.m_serializeContext)); spawnedEntityIndices.push_back(i); } else @@ -483,21 +475,9 @@ namespace AzFramework AZ::Entity* clone = CloneSingleAliasedEntity( *entitiesToSpawn[i], *aliasIt, ticket.m_entityIdReferenceMap, previousEntity, *request.m_serializeContext); - // Not all alias operations create a new instance. It's also possible for an empty entity to be left behind, - // in which case it's also filtered out as the entity component framework doesn't handle these gracefully. - if (clone) - { - if (!clone->GetComponents().empty()) - { - previousEntity = clone; - spawnedEntities.emplace_back(clone); - spawnedEntityIndices.push_back(i); - } - else - { - delete clone; - } - } + previousEntity = clone; + spawnedEntities.emplace_back(clone); + spawnedEntityIndices.push_back(i); ++aliasIt; } while (aliasIt != aliasEnd && aliasIt->m_sourceIndex == i); } @@ -519,8 +499,13 @@ namespace AzFramework // Add to the game context, now the entities are active for (auto it = newEntitiesBegin; it != newEntitiesEnd; ++it) { - (*it)->SetSpawnTicketId(request.m_ticketId); - GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); + AZ::Entity* clone = (*it); + // The entity component framework doesn't handle entities without TransformComponent safely. + if (!clone->GetComponents().empty()) + { + clone->SetSpawnTicketId(request.m_ticketId); + GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); + } } // Let other systems know about newly spawned entities for any post-processing after adding to the scene/game context. @@ -585,10 +570,8 @@ namespace AzFramework RefreshEntityIdMapping( entitiesToSpawn[index].get()->GetId(), ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned); - AZ::Entity* clone = - CloneSingleEntity(*entitiesToSpawn[index], ticket.m_entityIdReferenceMap, *request.m_serializeContext); - AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - spawnedEntities.push_back(clone); + spawnedEntities.push_back( + CloneSingleEntity(*entitiesToSpawn[index], ticket.m_entityIdReferenceMap, *request.m_serializeContext)); spawnedEntityIndices.push_back(index); } } @@ -612,9 +595,8 @@ namespace AzFramework if (aliasIt == aliasEnd) { - AZ::Entity* clone = - CloneSingleEntity(*entitiesToSpawn[index], ticket.m_entityIdReferenceMap, *request.m_serializeContext); - spawnedEntities.emplace_back(clone); + spawnedEntities.emplace_back( + CloneSingleEntity(*entitiesToSpawn[index], ticket.m_entityIdReferenceMap, *request.m_serializeContext)); spawnedEntityIndices.push_back(index); } else @@ -627,22 +609,10 @@ namespace AzFramework AZ::Entity* clone = CloneSingleAliasedEntity( *entitiesToSpawn[index], *aliasIt, ticket.m_entityIdReferenceMap, previousEntity, *request.m_serializeContext); - // Not all alias operations create a new instance. It's also possible for an empty entity to be left - // behind, in which case it's also filtered out as the entity component framework doesn't handle these - // gracefully. - if (clone) - { - if (!clone->GetComponents().empty()) - { - previousEntity = clone; - spawnedEntities.emplace_back(clone); - spawnedEntityIndices.push_back(index); - } - else - { - delete clone; - } - } + previousEntity = clone; + spawnedEntities.emplace_back(clone); + spawnedEntityIndices.push_back(index); + ++aliasIt; } while (aliasIt != aliasEnd && aliasIt->m_sourceIndex == index); } @@ -663,8 +633,13 @@ namespace AzFramework // Add to the game context, now the entities are active for (auto it = ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount; it != ticket.m_spawnedEntities.end(); ++it) { - (*it)->SetSpawnTicketId(request.m_ticketId); - GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); + AZ::Entity* clone = (*it); + // The entity component framework doesn't handle entities without TransformComponent safely. + if (!clone->GetComponents().empty()) + { + clone->SetSpawnTicketId(request.m_ticketId); + GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, *it); + } } if (request.m_completionCallback) @@ -965,13 +940,18 @@ namespace AzFramework { for (AZ::Entity* entity : request.m_ticket->m_spawnedEntities) { - if (entity != nullptr) + if (entity != nullptr && !entity->GetComponents().empty()) { - // Setting it to 0 is needed to avoid the infite loop between GameEntityContext and SpawnableEntitiesManager. + // Setting it to 0 is needed to avoid the infinite loop between GameEntityContext and SpawnableEntitiesManager. entity->SetSpawnTicketId(0); GameEntityContextRequestBus::Broadcast( &GameEntityContextRequestBus::Events::DestroyGameEntity, entity->GetId()); } + else + { + // Entities without components wouldn't have been send to the GameEntityContext. + delete entity; + } } delete request.m_ticket; diff --git a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 2dc32d14d5..ff48f73769 100644 --- a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -403,7 +403,7 @@ namespace UnitTest static constexpr size_t NumEntities = 4; FillSpawnable(NumEntities); - AZStd::vector indices = { 0, 2, 3, 1 }; + AZStd::vector indices = { 0, 2, 3, 1 }; size_t spawnedEntitiesCount = 0; auto callback = [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) @@ -423,7 +423,7 @@ namespace UnitTest static constexpr size_t NumEntities = 1; FillSpawnable(NumEntities); - AZStd::vector indices = { 0, 0 }; + AZStd::vector indices = { 0, 0 }; size_t spawnedEntitiesCount = 0; auto callback = @@ -444,7 +444,7 @@ namespace UnitTest static constexpr size_t NumEntities = 4; FillSpawnable(NumEntities); - AZStd::vector indices = { 0, 2, 3, 1 }; + AZStd::vector indices = { 0, 2, 3, 1 }; size_t spawnedEntitiesCount = 0; auto callback = @@ -467,7 +467,7 @@ namespace UnitTest FillSpawnable(NumEntities); CreateSingleParent(); - AZStd::vector indices = { 0, 1, 2, 3 }; + AZStd::vector indices = { 0, 1, 2, 3 }; AZStd::vector parents; auto callback = [&parents](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) @@ -499,7 +499,7 @@ namespace UnitTest FillSpawnable(NumEntities); CreateSingleParent(); - AZStd::vector indices = { 0, 1, 2, 3 }; + AZStd::vector indices = { 0, 1, 2, 3 }; AZStd::vector parents; auto callback = From 6587e149b758a35b3fa62655db5e640b3b81e0ac Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Thu, 28 Oct 2021 09:57:33 -0700 Subject: [PATCH 010/177] Added unit tests for spawnable entity aliases. This also fixes several issues discovered through the unit tests and renames a few functions to be clearer. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../AzFramework/Spawnable/Spawnable.cpp | 108 ++-- .../AzFramework/Spawnable/Spawnable.h | 16 +- .../Spawnable/SpawnableAssetHandler.cpp | 4 +- .../Spawnable/SpawnableEntitiesManager.cpp | 29 +- .../SpawnableEntitiesManagerTests.cpp | 479 ++++++++++++++++- .../Tests/Spawnable/SpawnableTests.cpp | 499 ++++++++++++++++++ .../Tests/frameworktests_files.cmake | 1 + .../Spawnable/PrefabProcessorContext.cpp | 2 +- .../Prefab/Spawnable/PrefabProcessorContext.h | 3 +- 9 files changed, 1057 insertions(+), 84 deletions(-) create mode 100644 Code/Framework/AzFramework/Tests/Spawnable/SpawnableTests.cpp diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp index 48662fae99..7548a8f181 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp @@ -9,7 +9,9 @@ #include #include #include +#include #include +#include #include namespace AzFramework @@ -31,7 +33,7 @@ namespace AzFramework // EntityAliasVisitorBase // - bool Spawnable::EntityAliasVisitorBase::HasLock(const EntityAliasList* aliases) const + bool Spawnable::EntityAliasVisitorBase::IsSet(const EntityAliasList* aliases) const { return aliases != nullptr; } @@ -92,11 +94,15 @@ namespace AzFramework AZStd::unordered_set spawnableIds; for (const Spawnable::EntityAlias& alias : *aliases) { - auto it = spawnableIds.find(alias.m_spawnable.GetId()); - if (it == spawnableIds.end()) + // If the spawnable id is not valid it means that the alias is referencing the spawnable it's stored on. + if (alias.m_spawnable.GetId().IsValid()) { - callback(alias.m_spawnable); - spawnableIds.emplace(alias.m_spawnable.GetId()); + auto it = spawnableIds.find(alias.m_spawnable.GetId()); + if (it == spawnableIds.end()) + { + callback(alias.m_spawnable); + spawnableIds.emplace(alias.m_spawnable.GetId()); + } } } } @@ -108,7 +114,8 @@ namespace AzFramework AZStd::unordered_set spawnableIds; for (const Spawnable::EntityAlias& alias : *aliases) { - if (alias.m_tag == tag) + // If the spawnable id is not valid it means that the alias is referencing the spawnable it's stored on. + if (alias.m_tag == tag && alias.m_spawnable.GetId().IsValid()) { auto it = spawnableIds.find(alias.m_spawnable.GetId()); if (it == spawnableIds.end()) @@ -134,7 +141,7 @@ namespace AzFramework Spawnable::EntityAliasVisitor::~EntityAliasVisitor() { - if (HasLock()) + if (IsSet()) { Optimize(); @@ -169,9 +176,9 @@ namespace AzFramework return *this; } - bool Spawnable::EntityAliasVisitor::HasLock() const + bool Spawnable::EntityAliasVisitor::IsSet() const { - return EntityAliasVisitorBase::HasLock(m_entityAliasList); + return EntityAliasVisitorBase::IsSet(m_entityAliasList); } bool Spawnable::EntityAliasVisitor::HasAliases() const @@ -235,7 +242,7 @@ namespace AzFramework m_dirty = true; } - void Spawnable::EntityAliasVisitor::ListSpawnablesPendingLoad(const ListSpawnablesPendingLoadCallback& callback) + void Spawnable::EntityAliasVisitor::ListSpawnablesRequiringLoad(const ListSpawnablesRequiringLoadCallback& callback) { AZ_Assert(m_entityAliasList, "Attempting to visit entity aliases on a spawnable that wasn't locked."); for (Spawnable::EntityAlias& alias : *m_entityAliasList) @@ -306,75 +313,92 @@ namespace AzFramework // aliases, for instance Networking can decide to disable certain aliases when running on a client. This in turn also requires // the aliases to be in their recorded order during building as the ebus handlers may depend on that order to determine what // entities need to be updated. - Spawnable::EntityAlias* compare = m_entityAliasList->begin(); - Spawnable::EntityAlias* it = m_entityAliasList->begin() + 1; + uint32_t previousIndex = AZStd::numeric_limits::max(); + Spawnable::EntityAliasType previousType = + static_cast(AZStd::numeric_limits>::max()); + Spawnable::EntityAlias* it = m_entityAliasList->begin(); Spawnable::EntityAlias* end = m_entityAliasList->end(); while (it < end) { + // If there's a switch to a new source index and the previous index only had an original it can + // be removed. + if (previousType == Spawnable::EntityAliasType::Original && previousIndex != it->m_sourceIndex) + { + it = m_entityAliasList->erase(it - 1); + end = m_entityAliasList->end(); + if (it == end) + { + break; + } + } + switch (it->m_aliasType) { case Spawnable::EntityAliasType::Original: - // If this is the only alias for the entity then the original can be removed. - { - Spawnable::EntityAlias* next = it + 1; - if (next == end || next->m_sourceIndex != it->m_sourceIndex) - { - // Erase instead of a swap-and-pop in order to preserver the order. - m_entityAliasList->erase(compare); - --end; - break; - } - } [[fallthrough]]; case Spawnable::EntityAliasType::Disabled: [[fallthrough]]; case Spawnable::EntityAliasType::Replace: // If the previous entry was a disabled, original or replace alias then remove it as it will be overwritten by the // current entry. - if (compare->m_sourceIndex == it->m_sourceIndex && - (compare->m_aliasType == Spawnable::EntityAliasType::Original || - compare->m_aliasType == Spawnable::EntityAliasType::Disabled || - compare->m_aliasType == Spawnable::EntityAliasType::Replace)) + if (previousIndex == it->m_sourceIndex && + (previousType == Spawnable::EntityAliasType::Original || + previousType == Spawnable::EntityAliasType::Disabled || + previousType == Spawnable::EntityAliasType::Replace)) { + previousIndex = it->m_sourceIndex; + previousType = it->m_aliasType; // Erase instead of a swap-and-pop in order to preserver the order. - m_entityAliasList->erase(compare); - --end; + it = m_entityAliasList->erase(it - 1) + 1; + end = m_entityAliasList->end(); } else { - ++compare; + previousIndex = it->m_sourceIndex; + previousType = it->m_aliasType; ++it; } break; case Spawnable::EntityAliasType::Additional: [[fallthrough]]; case Spawnable::EntityAliasType::Merge: - // If this is the first entry for this type insert an original in front of it so the spawnable entity manager - // does have to check for the case there's a merge and/or addition without a prefix. - if (compare->m_sourceIndex != it->m_sourceIndex) + // If this is the first entry for this index then insert an original in front of it so the spawnable entity manager + // doesn't have to check for the case there's a merge and/or addition without an entity to extend. + if (previousIndex != it->m_sourceIndex) { Spawnable::EntityAlias insert; // No load, as the asset is already loaded. - insert.m_spawnable = AZ::Data::Asset(&m_owner, AZ::Data::AssetLoadBehavior::NoLoad); + insert.m_spawnable = AZ::Data::Asset({}, azrtti_typeid()); insert.m_sourceIndex = it->m_sourceIndex; insert.m_targetIndex = it->m_sourceIndex; // Source index as the original entry for this slot is added. insert.m_aliasType = Spawnable::EntityAliasType::Original; - m_entityAliasList->insert(compare, AZStd::move(insert)); - compare += 2; + + previousIndex = it->m_sourceIndex; + previousType = it->m_aliasType; + + // Insert to maintain the order. + it = m_entityAliasList->insert(it, AZStd::move(insert)); it += 2; - ++end; + end = m_entityAliasList->end(); } else { - ++compare; + previousType = it->m_aliasType; ++it; } break; default: - AZ_Assert(false, "Invalid Spawnable entity alias type found during asset loading: %i", compare->m_aliasType); + AZ_Assert(false, "Invalid Spawnable entity alias type found during asset loading: %i", it->m_aliasType); break; } } + + // Check if the last entry is an "Original" in which case it can be removed. + if (!m_entityAliasList->empty() && m_entityAliasList->back().m_aliasType == Spawnable::EntityAliasType::Original) + { + m_entityAliasList->pop_back(); + } + // Reclaim memory because after this point the aliases will not change anymore. m_entityAliasList->shrink_to_fit(); m_dirty = false; @@ -395,7 +419,7 @@ namespace AzFramework Spawnable::EntityAliasConstVisitor::~EntityAliasConstVisitor() { - if (HasLock()) + if (IsSet()) { AZ_Assert( m_owner.m_shareState <= ShareState::Read, "Attempting to unlock a read shared spawnable that was not in a read shared mode (%i).", @@ -404,9 +428,9 @@ namespace AzFramework } } - bool Spawnable::EntityAliasConstVisitor::HasLock() const + bool Spawnable::EntityAliasConstVisitor::IsSet() const { - return EntityAliasVisitorBase::HasLock(m_entityAliasList); + return EntityAliasVisitorBase::IsSet(m_entityAliasList); } bool Spawnable::EntityAliasConstVisitor::HasAliases() const diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h index 4fb1f84b6f..0a35b81fb1 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h @@ -71,7 +71,8 @@ namespace AzFramework class EntityAliasVisitorBase { protected: - bool HasLock(const EntityAliasList* aliases) const; + bool IsSet(const EntityAliasList* aliases) const; + bool HasAliases(const EntityAliasList* aliases) const; bool AreAllSpawnablesReady(const EntityAliasList* aliases) const; @@ -98,7 +99,9 @@ namespace AzFramework EntityAliasVisitor(const EntityAliasVisitor& rhs) = delete; EntityAliasVisitor& operator=(const EntityAliasVisitor& rhs) = delete; - bool HasLock() const; + //! Checks if the visitor was able to retrieve data. This needs to be checked before calling any other functions. + bool IsSet() const; + bool HasAliases() const; bool AreAllSpawnablesReady() const; @@ -118,8 +121,8 @@ namespace AzFramework Spawnable::EntityAliasType aliasType, bool queueLoad); - using ListSpawnablesPendingLoadCallback = AZStd::function& spawnablePendingLoad)>; - void ListSpawnablesPendingLoad(const ListSpawnablesPendingLoadCallback& callback); + using ListSpawnablesRequiringLoadCallback = AZStd::function& spawnablePendingLoad)>; + void ListSpawnablesRequiringLoad(const ListSpawnablesRequiringLoadCallback& callback); using UpdateCallback = AZStd::functionTryGetAliases(); - AZ_Assert(aliases.HasLock(), "Newly created Spawnable '%s' was already locked.", asset.GetHint().c_str()); + AZ_Assert(aliases.IsSet(), "Newly created Spawnable '%s' was already locked.", asset.GetHint().c_str()); if (aliases.HasAliases()) { AZ_Assert( @@ -119,7 +119,7 @@ namespace AzFramework &SpawnableAssetEvents::OnResolveAliases, aliases, spawnable->GetMetaData(), spawnable->GetEntities()); aliases.Optimize(); - aliases.ListSpawnablesPendingLoad( + aliases.ListSpawnablesRequiringLoad( [&assetLoadFilterCB, streamingDeadline, streamingPriority](AZ::Data::Asset& assetPendingLoad) { AZ::Data::AssetLoadParameters loadInfo; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index a018a55210..d7d3f59154 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -342,9 +342,6 @@ namespace AzFramework return clone; case Spawnable::EntityAliasType::Merge: AZ_Assert(previouslySpawnedEntity != nullptr, "Merging components but there's no entity to add to yet."); - AZ_Assert( - previouslySpawnedEntity->GetId() == alias.m_spawnable->GetEntities()[alias.m_targetIndex]->GetId(), - "Entity ids for merging spawnables don't match."); AppendComponents( *previouslySpawnedEntity, alias.m_spawnable->GetEntities()[alias.m_targetIndex]->GetComponents(), templateToCloneMap, serializeContext); return nullptr; @@ -412,7 +409,7 @@ namespace AzFramework if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) { if (Spawnable::EntityAliasConstVisitor aliases = ticket.m_spawnable->TryGetAliasesConst(); - aliases.HasLock() && aliases.AreAllSpawnablesReady()) + aliases.IsSet() && aliases.AreAllSpawnablesReady()) { AZStd::vector& spawnedEntities = ticket.m_spawnedEntities; AZStd::vector& spawnedEntityIndices = ticket.m_spawnedEntityIndices; @@ -475,9 +472,12 @@ namespace AzFramework AZ::Entity* clone = CloneSingleAliasedEntity( *entitiesToSpawn[i], *aliasIt, ticket.m_entityIdReferenceMap, previousEntity, *request.m_serializeContext); - previousEntity = clone; - spawnedEntities.emplace_back(clone); - spawnedEntityIndices.push_back(i); + previousEntity = clone; + if (clone) + { + spawnedEntities.emplace_back(clone); + spawnedEntityIndices.push_back(i); + } ++aliasIt; } while (aliasIt != aliasEnd && aliasIt->m_sourceIndex == i); } @@ -527,7 +527,7 @@ namespace AzFramework if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) { if (Spawnable::EntityAliasConstVisitor aliases = ticket.m_spawnable->TryGetAliasesConst(); - aliases.HasLock() && aliases.AreAllSpawnablesReady()) + aliases.IsSet() && aliases.AreAllSpawnablesReady()) { AZStd::vector& spawnedEntities = ticket.m_spawnedEntities; AZStd::vector& spawnedEntityIndices = ticket.m_spawnedEntityIndices; @@ -593,7 +593,7 @@ namespace AzFramework return lhs.m_sourceIndex < rhs; }); - if (aliasIt == aliasEnd) + if (aliasIt == aliasEnd || aliasIt->m_sourceIndex != index) { spawnedEntities.emplace_back( CloneSingleEntity(*entitiesToSpawn[index], ticket.m_entityIdReferenceMap, *request.m_serializeContext)); @@ -610,8 +610,11 @@ namespace AzFramework *entitiesToSpawn[index], *aliasIt, ticket.m_entityIdReferenceMap, previousEntity, *request.m_serializeContext); previousEntity = clone; - spawnedEntities.emplace_back(clone); - spawnedEntityIndices.push_back(index); + if (clone) + { + spawnedEntities.emplace_back(clone); + spawnedEntityIndices.push_back(index); + } ++aliasIt; } while (aliasIt != aliasEnd && aliasIt->m_sourceIndex == index); @@ -816,7 +819,7 @@ namespace AzFramework Ticket& ticket = *request.m_ticket; if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) { - if (Spawnable::EntityAliasVisitor aliases = ticket.m_spawnable->TryGetAliases(); aliases.HasLock()) + if (Spawnable::EntityAliasVisitor aliases = ticket.m_spawnable->TryGetAliases(); aliases.IsSet()) { for (EntityAliasTypeChange& replacement : request.m_entityAliases) { @@ -918,7 +921,7 @@ namespace AzFramework if (request.m_checkAliasSpawnables) { if (Spawnable::EntityAliasConstVisitor visitor = ticket.m_spawnable->TryGetAliasesConst(); - !visitor.HasLock() || !visitor.AreAllSpawnablesReady()) + !visitor.IsSet() || !visitor.AreAllSpawnablesReady()) { return CommandResult::Requeue; } diff --git a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index ff48f73769..38847edc07 100644 --- a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -55,6 +55,40 @@ namespace UnitTest AZ::EntityId m_entityReference; }; + class SourceSpawnableComponent : public AZ::Component + { + public: + AZ_COMPONENT(SourceSpawnableComponent, "{47FF79CE-A95B-420E-8BEB-F1CC58087B87}"); + + void Activate() override {} + void Deactivate() override {} + + static void Reflect(AZ::ReflectContext* reflection) + { + if (auto* serializeContext = azrtti_cast(reflection)) + { + serializeContext->Class(); + } + } + }; + + class TargetSpawnableComponent : public AZ::Component + { + public: + AZ_COMPONENT(TargetSpawnableComponent, "{B4041561-63A7-4E1E-80F1-78C08D497960}"); + + void Activate() override {} + void Deactivate() override {} + + static void Reflect(AZ::ReflectContext* reflection) + { + if (auto* serializeContext = azrtti_cast(reflection)) + { + serializeContext->Class(); + } + } + }; + class SpawnableEntitiesManagerTest : public AllocatorsFixture { public: @@ -66,6 +100,8 @@ namespace UnitTest AZ::ComponentApplication::Descriptor descriptor; m_application->Start(descriptor); m_application->RegisterComponentDescriptor(ComponentWithEntityReference::CreateDescriptor()); + m_application->RegisterComponentDescriptor(SourceSpawnableComponent::CreateDescriptor()); + m_application->RegisterComponentDescriptor(TargetSpawnableComponent::CreateDescriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash @@ -109,7 +145,50 @@ namespace UnitTest entities.reserve(numElements); for (size_t i=0; i()); + auto entry = AZStd::make_unique(); + entry->AddComponent(aznew SourceSpawnableComponent()); + entities.push_back(AZStd::move(entry)); + } + } + + AZ::Data::Asset CreateTargetSpawnable(size_t numElements) + { + auto target = aznew AzFramework::Spawnable( + AZ::Data::AssetId(AZ::Uuid("{716CD8C3-0BA8-4F32-B579-0EC7C967796F}")), AZ::Data::AssetData::AssetStatus::Ready); + + AzFramework::Spawnable::EntityList& entities = target->GetEntities(); + entities.reserve(numElements); + for (size_t i = 0; i < numElements; ++i) + { + auto entry = AZStd::make_unique(); + entry->AddComponent(aznew TargetSpawnableComponent()); + entities.push_back(AZStd::move(entry)); + } + + return AZ::Data::Asset(target, AZ::Data::AssetLoadBehavior::NoLoad); + } + + template + void InsertEntityAliases( + const AZStd::array& sourceIds, + const AZStd::array& targetIds, + const AZStd::array& aliasTypes, + AZ::Data::Asset* target = nullptr) + { + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + + for (uint32_t i = 0; i < AliasCount; ++i) + { + if (target) + { + visitor.AddAlias(*target, AZ::Crc32(i), sourceIds[i], targetIds[i], aliasTypes[i], false); + } + else + { + AZ::Data::Asset spawnable( + AZ::Data::AssetId(AZ::Uuid("{4CBEC17A-52D6-42D5-9037-F4C05B9CE1D9}"), i), azrtti_typeid()); + visitor.AddAlias(AZStd::move(spawnable), AZ::Crc32(i), sourceIds[i], targetIds[i], aliasTypes[i], false); + } } } @@ -245,6 +324,30 @@ namespace UnitTest TestApplication* m_application { nullptr }; }; + + // + // Constructors + // + + TEST_F(SpawnableEntitiesManagerTest, EntitySpawnTicket_Move_Works) + { + AzFramework::EntitySpawnTicket ticket1(*m_spawnableAsset); + AzFramework::EntitySpawnTicket ticket2(*m_spawnableAsset); + + const AzFramework::EntitySpawnTicket::Id ticket1Id = ticket1.GetId(); + const AzFramework::EntitySpawnTicket::Id ticket2Id = ticket2.GetId(); + + AzFramework::EntitySpawnTicket ticketMoveConstructor(AZStd::move(ticket1)); + EXPECT_TRUE(ticketMoveConstructor.IsValid()); + EXPECT_EQ(ticketMoveConstructor.GetId(), ticket1Id); + + AzFramework::EntitySpawnTicket ticketMoveOperator; + ticketMoveOperator = AZStd::move(ticket2); + EXPECT_TRUE(ticketMoveOperator.IsValid()); + EXPECT_EQ(ticketMoveOperator.GetId(), ticket2Id); + } + + // // SpawnAllEntitities // @@ -366,24 +469,6 @@ namespace UnitTest } } - TEST_F(SpawnableEntitiesManagerTest, EntitySpawnTicket_Move_Works) - { - AzFramework::EntitySpawnTicket ticket1(*m_spawnableAsset); - AzFramework::EntitySpawnTicket ticket2(*m_spawnableAsset); - - const AzFramework::EntitySpawnTicket::Id ticket1Id = ticket1.GetId(); - const AzFramework::EntitySpawnTicket::Id ticket2Id = ticket2.GetId(); - - AzFramework::EntitySpawnTicket ticketMoveConstructor(AZStd::move(ticket1)); - EXPECT_TRUE(ticketMoveConstructor.IsValid()); - EXPECT_EQ(ticketMoveConstructor.GetId(), ticket1Id); - - AzFramework::EntitySpawnTicket ticketMoveOperator; - ticketMoveOperator = AZStd::move(ticket2); - EXPECT_TRUE(ticketMoveOperator.IsValid()); - EXPECT_EQ(ticketMoveOperator.GetId(), ticket2Id); - } - TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_DeleteTicketBeforeCall_NoCrash) { { @@ -393,6 +478,178 @@ namespace UnitTest m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_AllAliasesWithDisabled_NoEntitiesSpawned) + { + using namespace AzFramework; + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + InsertEntityAliases( + { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, + { Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled, + Spawnable::EntityAliasType::Disabled }); + + size_t spawnedEntitiesCount = 0; + auto callback = [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(0, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_SomeAliasesWithDisabled_RegularEntitiesAreSpawned) + { + using namespace AzFramework; + static constexpr size_t NumEntities = 8; + FillSpawnable(NumEntities); + InsertEntityAliases<2>({ 1, 3 }, { 1, 3 }, { Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled }); + + size_t spawnedEntitiesCount = 0; + auto callback = [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(6, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_AllAliasesWithReplace_EntitiesSpawnedFromTarget) + { + using namespace AzFramework; + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + AZ::Data::Asset target = CreateTargetSpawnable(4); + InsertEntityAliases<4>( + { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, + { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, + Spawnable::EntityAliasType::Replace }, + &target); + + size_t spawnedEntitiesCount = 0; + bool allReplaced = true; + auto callback = [&spawnedEntitiesCount, &allReplaced]( + AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + for (const AZ::Entity* entity : entities) + { + if (entity) + { + allReplaced = allReplaced && entity->FindComponent() == nullptr; + allReplaced = allReplaced && entity->FindComponent() != nullptr; + } + else + { + allReplaced = false; + } + } + }; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(4, spawnedEntitiesCount); + EXPECT_TRUE(allReplaced); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_AllAliasesWithAdditional_SourceAndTargetComponentsMerged) + { + using namespace AzFramework; + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + AZ::Data::Asset target = CreateTargetSpawnable(4); + InsertEntityAliases<4>( + { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, + { Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, + Spawnable::EntityAliasType::Additional }, + &target); + + size_t spawnedEntitiesCount = 0; + bool allReplaced = true; + auto callback = [&spawnedEntitiesCount, &allReplaced]( + AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + bool onSource = true; + for (const AZ::Entity* entity : entities) + { + if (entity) + { + if (onSource) + { + allReplaced = allReplaced && entity->FindComponent() != nullptr; + allReplaced = allReplaced && entity->FindComponent() == nullptr; + } + else + { + allReplaced = allReplaced && entity->FindComponent() == nullptr; + allReplaced = allReplaced && entity->FindComponent() != nullptr; + } + onSource = !onSource; + } + else + { + allReplaced = false; + } + } + }; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(8, spawnedEntitiesCount); + EXPECT_TRUE(allReplaced); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_AllAliasesWithMerge_SourceAndTargetComponentsMerged) + { + using namespace AzFramework; + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + AZ::Data::Asset target = CreateTargetSpawnable(4); + InsertEntityAliases<4>( + { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, + { Spawnable::EntityAliasType::Merge, Spawnable::EntityAliasType::Merge, Spawnable::EntityAliasType::Merge, + Spawnable::EntityAliasType::Merge }, + &target); + + size_t spawnedEntitiesCount = 0; + bool allReplaced = true; + auto callback = [&spawnedEntitiesCount, &allReplaced]( + AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + for (const AZ::Entity* entity : entities) + { + if (entity) + { + allReplaced = allReplaced && entity->FindComponent() != nullptr; + allReplaced = allReplaced && entity->FindComponent() != nullptr; + } + else + { + allReplaced = false; + } + } + }; + AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(4, spawnedEntitiesCount); + EXPECT_TRUE(allReplaced); + } // // SpawnEntities @@ -754,6 +1011,190 @@ namespace UnitTest m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); } + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_AllAliasesWithDisabled_NoEntitiesSpawned) + { + using namespace AzFramework; + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + + InsertEntityAliases( + { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, + { Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled, + Spawnable::EntityAliasType::Disabled }); + + AZStd::vector indices = { 0, 2, 3, 1 }; + + size_t spawnedEntitiesCount = 0; + auto callback = [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(0, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_SomeAliasesWithDisabled_RegularEntitiesAreSpawned) + { + using namespace AzFramework; + FillSpawnable(8); + InsertEntityAliases<3>( + { 1, 3, 6 }, { 1, 3, 6 }, + { Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled }); + + AZStd::vector indices = { 0, 2, 3, 1, 2, 3, 0, 1, 6, 4, 5, 7, 4, 1, 0, 6 }; + + size_t spawnedEntitiesCount = 0; + auto callback = [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(9, spawnedEntitiesCount); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_AllAliasesWithReplace_EntitiesSpawnedFromTarget) + { + using namespace AzFramework; + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + AZ::Data::Asset target = CreateTargetSpawnable(4); + InsertEntityAliases<4>( + { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, + { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, + Spawnable::EntityAliasType::Replace }, + &target); + + AZStd::vector indices = { 0, 2, 3, 1 }; + + size_t spawnedEntitiesCount = 0; + bool allReplaced = true; + auto callback = [&spawnedEntitiesCount, &allReplaced]( + AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + for (const AZ::Entity* entity : entities) + { + if (entity) + { + allReplaced = allReplaced && entity->FindComponent() == nullptr; + allReplaced = allReplaced && entity->FindComponent() != nullptr; + } + else + { + allReplaced = false; + } + } + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(4, spawnedEntitiesCount); + EXPECT_TRUE(allReplaced); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_AllAliasesWithAdditional_SourceAndTargetComponentsMerged) + { + using namespace AzFramework; + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + AZ::Data::Asset target = CreateTargetSpawnable(4); + InsertEntityAliases<4>( + { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, + { Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, + Spawnable::EntityAliasType::Additional }, + &target); + + AZStd::vector indices = { 0, 2, 3, 1 }; + + size_t spawnedEntitiesCount = 0; + bool allReplaced = true; + auto callback = [&spawnedEntitiesCount, &allReplaced]( + AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + bool onSource = true; + for (const AZ::Entity* entity : entities) + { + if (entity) + { + if (onSource) + { + allReplaced = allReplaced && entity->FindComponent() != nullptr; + allReplaced = allReplaced && entity->FindComponent() == nullptr; + } + else + { + allReplaced = allReplaced && entity->FindComponent() == nullptr; + allReplaced = allReplaced && entity->FindComponent() != nullptr; + } + onSource = !onSource; + } + else + { + allReplaced = false; + } + } + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(8, spawnedEntitiesCount); + EXPECT_TRUE(allReplaced); + } + + TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_AllAliasesWithMerge_SourceAndTargetComponentsMerged) + { + using namespace AzFramework; + static constexpr size_t NumEntities = 4; + FillSpawnable(NumEntities); + AZ::Data::Asset target = CreateTargetSpawnable(4); + InsertEntityAliases<4>( + { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, + { Spawnable::EntityAliasType::Merge, Spawnable::EntityAliasType::Merge, Spawnable::EntityAliasType::Merge, + Spawnable::EntityAliasType::Merge }, + &target); + + AZStd::vector indices = { 0, 2, 3, 1 }; + + size_t spawnedEntitiesCount = 0; + bool allReplaced = true; + auto callback = [&spawnedEntitiesCount, &allReplaced]( + AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) + { + spawnedEntitiesCount += entities.size(); + for (const AZ::Entity* entity : entities) + { + if (entity) + { + allReplaced = allReplaced && entity->FindComponent() != nullptr; + allReplaced = allReplaced && entity->FindComponent() != nullptr; + } + else + { + allReplaced = false; + } + } + }; + AzFramework::SpawnEntitiesOptionalArgs optionalArgs; + optionalArgs.m_completionCallback = AZStd::move(callback); + m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs)); + m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); + + EXPECT_EQ(4, spawnedEntitiesCount); + EXPECT_TRUE(allReplaced); + } // // DespawnAllEntities diff --git a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableTests.cpp b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableTests.cpp new file mode 100644 index 0000000000..f7d6d5190f --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableTests.cpp @@ -0,0 +1,499 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +namespace UnitTest +{ + class SpawnableTest : public AllocatorsFixture + { + public: + void SetUp() override + { + AllocatorsFixture::SetUp(); + + m_spawnable = aznew AzFramework::Spawnable(); + } + + void TearDown() override + { + delete m_spawnable; + m_spawnable = nullptr; + + AllocatorsFixture::TearDown(); + } + + void InsertEightEntities() + { + AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities(); + entities.reserve(entities.size() + 8); + for (size_t i = 0; i < 8; ++i) + { + entities.emplace_back(AZStd::make_unique()); + } + } + + void InsertEightEntityAliases( + const AZStd::array& sourceIds, + const AZStd::array& targetIds, + const AZStd::array& aliasTypes, + bool queueLoad = false) + { + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + + for (uint32_t i = 0; i < 8; ++i) + { + AZ::Data::Asset spawnable( + AZ::Data::AssetId(AZ::Uuid("{4CBEC17A-52D6-42D5-9037-F4C05B9CE1D9}"), i), azrtti_typeid()); + visitor.AddAlias(spawnable, AZ::Crc32(i), sourceIds[i], targetIds[i], aliasTypes[i], queueLoad); + } + } + + void InsertEightEntityAliases(bool queueLoad) + { + using namespace AzFramework; + InsertEightEntityAliases( + { 0, 1, 2, 3, 4, 5, 6, 7 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, + { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, + Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, + Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace }, + queueLoad); + } + + void InsertEightEntityAliases() + { + InsertEightEntityAliases(false); + } + + protected: + AzFramework::Spawnable* m_spawnable; + }; + + + // + // TryGetAliasesConst + // + + TEST_F(SpawnableTest, TryGetAliasesConst_GetVisitor_VisitorDataIsAvailable) + { + AzFramework::Spawnable::EntityAliasConstVisitor visitor = m_spawnable->TryGetAliasesConst(); + EXPECT_TRUE(visitor.IsSet()); + } + + TEST_F(SpawnableTest, TryGetAliasesConst_VisitorThatIsNotReadShared_VisitorDataIsNotAvailable) + { + AzFramework::Spawnable::EntityAliasVisitor readWriteVisitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(readWriteVisitor.IsSet()); + + AzFramework::Spawnable::EntityAliasConstVisitor visitor = m_spawnable->TryGetAliasesConst(); + EXPECT_FALSE(visitor.IsSet()); + } + + TEST_F(SpawnableTest, TryGetAliasesConst_VisitorThatIsAlreadyReadShared_VisitorDataIsAvailable) + { + AzFramework::Spawnable::EntityAliasConstVisitor readVisitor = m_spawnable->TryGetAliasesConst(); + ASSERT_TRUE(readVisitor.IsSet()); + + AzFramework::Spawnable::EntityAliasConstVisitor visitor = m_spawnable->TryGetAliasesConst(); + EXPECT_TRUE(visitor.IsSet()); + } + + + // + // TryGetAliases + // + + TEST_F(SpawnableTest, TryGetAliases_GetVisitor_VisitorDataIsAvailable) + { + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + EXPECT_TRUE(visitor.IsSet()); + } + + TEST_F(SpawnableTest, TryGetAliasesConst_VisitorThatIsAlreadyShared_VisitorDataNotIsAvailable) + { + AzFramework::Spawnable::EntityAliasConstVisitor readVisitor = m_spawnable->TryGetAliasesConst(); + ASSERT_TRUE(readVisitor.IsSet()); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + EXPECT_FALSE(visitor.IsSet()); + } + + + // + // EntityAliasVisitor + // + + + // + // HasAliases + // + + TEST_F(SpawnableTest, EntityAliasVisitor_HasAliases_EmptyAliasList_ReturnsFalse) + { + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + EXPECT_FALSE(visitor.HasAliases()); + } + + TEST_F(SpawnableTest, EntityAliasVisitor_HasAliases_FilledInAliasList_ReturnsTue) + { + InsertEightEntities(); + InsertEightEntityAliases(); + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + EXPECT_TRUE(visitor.HasAliases()); + } + + + // + // Optimize + // + + TEST_F(SpawnableTest, EntityAliasVisitor_Optimize_SortEntityAliases_AliasesAreSortedBySourceAndTargetId) + { + InsertEightEntities(); + InsertEightEntityAliases(); + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + // Optimize doesn't need to be explicitly called because the setup of the aliases will cause the alias list to be sorted and optimized. + + uint32_t sourceIndex = 0; + uint32_t targetIndex = 0; + for (const AzFramework::Spawnable::EntityAlias& alias : visitor) + { + if (alias.m_sourceIndex != sourceIndex) + { + ASSERT_LE(sourceIndex, alias.m_sourceIndex); + } + else + { + ASSERT_LE(targetIndex, alias.m_targetIndex); + } + sourceIndex = alias.m_sourceIndex; + targetIndex = alias.m_targetIndex; + } + } + + TEST_F( + SpawnableTest, EntityAliasVisitor_Optimize_RemoveUnused_OnlySecondToLastAliasRemains) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases( + { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, + { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Disabled, + Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Disabled, + Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original }); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + EXPECT_EQ(1, AZStd::distance(visitor.begin(), visitor.end())); + EXPECT_EQ(Spawnable::EntityAliasType::Replace, visitor.begin()->m_aliasType); + EXPECT_EQ(6, visitor.begin()->m_targetIndex); + } + + TEST_F(SpawnableTest, EntityAliasVisitor_Optimize_AddAdditional_ThreeAdditionalAliasesAreAdded) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases( + { 0, 0, 0, 0, 1, 2, 2, 2 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, + { Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, + Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, + Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional }); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + EXPECT_EQ(11, AZStd::distance(visitor.begin(), visitor.end())); + EXPECT_EQ(Spawnable::EntityAliasType::Original, visitor.begin()->m_aliasType); + EXPECT_EQ(Spawnable::EntityAliasType::Original, visitor.begin()[5].m_aliasType); + EXPECT_EQ(Spawnable::EntityAliasType::Original, visitor.begin()[7].m_aliasType); + } + + TEST_F(SpawnableTest, EntityAliasVisitor_Optimize_OriginalsOnly_AliasListIsEmpty) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases( + { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, + { Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, + Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, + Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original }); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + EXPECT_EQ(0, AZStd::distance(visitor.begin(), visitor.end())); + } + + TEST_F(SpawnableTest, EntityAliasVisitor_Optimize_MixedOriginals_AllOriginalsRemoved) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases( + { 0, 0, 0, 1, 1, 2, 2, 2 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, + { Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, + Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Original, + Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original }); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + EXPECT_EQ(2, AZStd::distance(visitor.begin(), visitor.end())); + EXPECT_EQ(Spawnable::EntityAliasType::Disabled, visitor.begin()->m_aliasType); + EXPECT_EQ(Spawnable::EntityAliasType::Replace, visitor.begin()[1].m_aliasType); + } + + TEST_F(SpawnableTest, EntityAliasVisitor_Optimize_MergeAfterOriginal_NoAdditionalOriginalIsInserted) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases( + { 0, 0, 1, 1, 2, 2, 2, 2 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, + { Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Merge, Spawnable::EntityAliasType::Replace, + Spawnable::EntityAliasType::Merge, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, + Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original }); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + EXPECT_EQ(4, AZStd::distance(visitor.begin(), visitor.end())); + EXPECT_EQ(Spawnable::EntityAliasType::Original, visitor.begin()->m_aliasType); + EXPECT_EQ(Spawnable::EntityAliasType::Merge, visitor.begin()[1].m_aliasType); + EXPECT_EQ(Spawnable::EntityAliasType::Replace, visitor.begin()[2].m_aliasType); + EXPECT_EQ(Spawnable::EntityAliasType::Merge, visitor.begin()[3].m_aliasType); + } + + + // + // UpdateAliasType + // + + TEST_F(SpawnableTest, EntityAliasVisitor_UpdateAliasType_AllToOriginal_NoAliasesAfterOptimization) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases( + { 0, 1, 2, 3, 4, 5, 6, 7 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, + { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, + Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, + Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace }); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + for (uint32_t i = 0; i < 8; ++i) + { + visitor.UpdateAliasType(i, Spawnable::EntityAliasType::Original); + } + + for (const Spawnable::EntityAlias& alias : visitor) + { + EXPECT_EQ(Spawnable::EntityAliasType::Original, alias.m_aliasType); + } + + visitor.Optimize(); + + EXPECT_EQ(0, AZStd::distance(visitor.begin(), visitor.end())); + } + + + // + // UpdateAliases + // + + TEST_F(SpawnableTest, EntityAliasVisitor_UpdateAliases_AllToOriginal_NoAliasesAfterOptimization) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases( + { 0, 1, 2, 3, 4, 5, 6, 7 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, + { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, + Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, + Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace }); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + auto callback = + [](Spawnable::EntityAliasType& aliasType, bool& /*queueLoad*/, const AZ::Data::Asset& /*aliasedSpawnable*/, + const AZ::Crc32 /*tag*/, const uint32_t /*sourceIndex*/, const uint32_t /*targetIndex*/) + { + aliasType = Spawnable::EntityAliasType::Original; + }; + visitor.UpdateAliases(AZStd::move(callback)); + + for (const Spawnable::EntityAlias& alias : visitor) + { + EXPECT_EQ(Spawnable::EntityAliasType::Original, alias.m_aliasType); + } + + visitor.Optimize(); + + EXPECT_EQ(0, AZStd::distance(visitor.begin(), visitor.end())); + } + + TEST_F(SpawnableTest, EntityAliasVisitor_UpdateAliases_FilterByTag_OnlyOneAliasUpdated) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases( + { 0, 1, 2, 3, 4, 5, 6, 7 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, + { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, + Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, + Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace }); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + bool correctTag = false; + size_t numberOfUpdates = 0; + auto callback = [&correctTag, &numberOfUpdates](Spawnable::EntityAliasType& aliasType, bool& /*queueLoad*/, + const AZ::Data::Asset& /*aliasedSpawnable*/, const AZ::Crc32 tag, const uint32_t /*sourceIndex*/, + const uint32_t /*targetIndex*/) + { + correctTag = (tag == AZ::Crc32(3)); + numberOfUpdates++; + aliasType = Spawnable::EntityAliasType::Original; + }; + visitor.UpdateAliases(AZ::Crc32(3), AZStd::move(callback)); + + EXPECT_EQ(Spawnable::EntityAliasType::Original, visitor.begin()[3].m_aliasType); + } + + + // + // AreAllSpawnablesReady + // + + TEST_F(SpawnableTest, EntityAliasVisitor_AreAllSpawnablesReady_CheckFakeLoadedAssets_ReturnsTrue) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases(); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + EXPECT_TRUE(visitor.AreAllSpawnablesReady()); + } + + TEST_F(SpawnableTest, EntityAliasVisitor_AreAllSpawnablesReady_CheckFakeNotLoadedAssets_ReturnsFalse) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases(true); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + EXPECT_FALSE(visitor.AreAllSpawnablesReady()); + } + + + // + // ListTargetSpawnables + // + + TEST_F(SpawnableTest, EntityAliasVisitor_ListTargetSpawnables_ListAllTargetAssets_AllTargetsListed) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases(); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + size_t count = 0; + bool correctAssets = true; + auto callback = [&count, &correctAssets](const AZ::Data::Asset& targetSpawnable) + { + correctAssets = correctAssets && (targetSpawnable.GetId().m_subId == count); + count++; + }; + visitor.ListTargetSpawnables(callback); + + EXPECT_EQ(8, count); + EXPECT_TRUE(correctAssets); + } + + TEST_F(SpawnableTest, EntityAliasVisitor_ListTargetSpawnables_ListTaggedTargetAssets_OneAssetListed) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases(); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + size_t count = 0; + bool correctAsset = false; + auto callback = [&count, &correctAsset](const AZ::Data::Asset& targetSpawnable) + { + correctAsset = (targetSpawnable.GetId().m_subId == 3); + count++; + }; + visitor.ListTargetSpawnables(AZ::Crc32(3), callback); + + EXPECT_EQ(1, count); + EXPECT_TRUE(correctAsset); + } + + + // + // ListSpawnablesRequiringLoad + // + + TEST_F(SpawnableTest, EntityAliasVisitor_ListSpawnablesRequiringLoad_AllSetToLoaded_AllTargetsListed) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases(true); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + size_t count = 0; + bool correctAssets = true; + auto callback = [&count, &correctAssets](const AZ::Data::Asset& targetSpawnable) + { + correctAssets = correctAssets && (targetSpawnable.GetId().m_subId == count); + count++; + }; + visitor.ListSpawnablesRequiringLoad(callback); + + EXPECT_EQ(8, count); + EXPECT_TRUE(correctAssets); + } + + TEST_F(SpawnableTest, EntityAliasVisitor_ListSpawnablesRequiringLoad_AllSetToNotLoaded_NoTargetsListed) + { + using namespace AzFramework; + InsertEightEntities(); + InsertEightEntityAliases(false); + + AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); + ASSERT_TRUE(visitor.IsSet()); + + size_t count = 0; + auto callback = [&count](const AZ::Data::Asset& /*targetSpawnable*/) + { + count++; + }; + visitor.ListSpawnablesRequiringLoad(callback); + + EXPECT_EQ(0, count); + } +} // namespace UnitTest diff --git a/Code/Framework/AzFramework/Tests/frameworktests_files.cmake b/Code/Framework/AzFramework/Tests/frameworktests_files.cmake index 6c4f611352..e4877e34a9 100644 --- a/Code/Framework/AzFramework/Tests/frameworktests_files.cmake +++ b/Code/Framework/AzFramework/Tests/frameworktests_files.cmake @@ -10,6 +10,7 @@ set(FILES Main.cpp Spawnable/SpawnableEntitiesInterfaceTests.cpp Spawnable/SpawnableEntitiesManagerTests.cpp + Spawnable/SpawnableTests.cpp ArchiveCompressionTests.cpp ArchiveTests.cpp BehaviorEntityTests.cpp diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp index e12ddb616f..21fa3db52b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp @@ -220,7 +220,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils if (it == aliasVisitors.end()) { AzFramework::Spawnable::EntityAliasVisitor visitor = source->m_spawnable.TryGetAliases(); - AZ_Assert(visitor.HasLock(), "Unable to obtain lock for a newly create spawnable."); + AZ_Assert(visitor.IsSet(), "Unable to obtain lock for a newly create spawnable."); it = aliasVisitors.emplace(source->m_spawnable.GetId(), AZStd::move(visitor)).first; } it->second.AddAlias( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h index 7fc01367b2..c937b974e9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h @@ -42,7 +42,8 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils { NoLoad, //!< Don't load the spawnable referenced in the entity alias. Loading will be up to the caller. QueueLoad, //!< Queue the spawnable referenced in the entity alias for loading. This will be an async load because asset - //!< handlers aren't allowed to start a blocking load as this can lead to deadlocks. + //!< handlers aren't allowed to start a blocking load as this can lead to deadlocks. This option will allow + //!< to disable loading the referenced spawnable through the event fired from the spawnables asset handler. DependentLoad //!< The spawnable referenced in the entity alias is made a dependency of the spawnable that holds the entity //!< alias. This will cause the spawnable to be automatically loaded along with the owning spawnable. }; From 459f636fff596135b400ced020b074c94877e693 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Thu, 28 Oct 2021 18:29:15 -0700 Subject: [PATCH 011/177] Fixed the chicken mohawk material to address depth sorting issues. Before the material used a workaround to expose the otherwise hidden double-sided flag, made the object get rendered in the transparent pass. I updated the material to be opaque, not that the double-sided flag is available outside the opacity property group. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../cloth/Chicken/Actor/chicken_mohawkmat.material | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_mohawkmat.material b/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_mohawkmat.material index 7e12d7fdee..22c673469c 100644 --- a/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_mohawkmat.material +++ b/Gems/NvCloth/Assets/Objects/cloth/Chicken/Actor/chicken_mohawkmat.material @@ -1,8 +1,8 @@ { "description": "", - "materialType": "Materials/Types/StandardPBR.materialtype", "parentMaterial": "", - "propertyLayoutVersion": 3, + "materialType": "Materials/Types/StandardPBR.materialtype", + "materialTypeVersion": 4, "properties": { "baseColor": { "color": [ @@ -22,11 +22,8 @@ "intensity": 6.742737293243408, "textureMap": "Objects/cloth/Chicken/Actor/chicken_diff.png" }, - "opacity": { - "alphaSource": "None", - "doubleSided": true, - "factor": 1.0, - "mode": "Blended" + "general": { + "doubleSided": true } } -} +} \ No newline at end of file From 781eaabd943ffb7852e5d9481a373a7d28cdd862 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Fri, 29 Oct 2021 12:05:42 -0700 Subject: [PATCH 012/177] Access gem repos from catalog non-destructively Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> --- .../Source/CreateProjectCtrl.cpp | 23 ++++++++++++-- .../ProjectManager/Source/CreateProjectCtrl.h | 2 ++ .../Source/GemCatalog/GemCatalogScreen.cpp | 16 ---------- .../Source/UpdateProjectCtrl.cpp | 30 ++++++++++++++++--- .../ProjectManager/Source/UpdateProjectCtrl.h | 6 +++- 5 files changed, 54 insertions(+), 23 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index 65e01803aa..c5325bfb42 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -47,8 +48,12 @@ namespace O3DE::ProjectManager m_gemCatalogScreen = new GemCatalogScreen(this); m_stack->addWidget(m_gemCatalogScreen); + + m_gemRepoScreen = new GemRepoScreen(this); + m_stack->addWidget(m_gemRepoScreen); vLayout->addWidget(m_stack); + connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest); // When there are multiple project templates present, we re-gather the gems when changing the selected the project template. @@ -89,6 +94,9 @@ namespace O3DE::ProjectManager buttons->setObjectName("footer"); vLayout->addWidget(buttons); + m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole); + connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton); + #ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED connect(m_newProjectSettingsScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest); @@ -100,8 +108,6 @@ namespace O3DE::ProjectManager Update(); #endif // TEMPLATE_GEM_CONFIGURATION_ENABLED - m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole); - connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton); setLayout(vLayout); } @@ -160,12 +166,21 @@ namespace O3DE::ProjectManager { m_header->setSubTitle(tr("Configure project with Gems")); m_secondaryButton->setVisible(false); + m_primaryButton->setVisible(true); + } + else if (m_stack->currentWidget() == m_gemRepoScreen) + { + m_header->setSubTitle(tr("Gem Repositories")); + m_secondaryButton->setVisible(true); + m_secondaryButton->setText(tr("Back")); + m_primaryButton->setVisible(false); } else { m_header->setSubTitle(tr("Enter Project Details")); m_secondaryButton->setVisible(true); m_secondaryButton->setText(tr("Configure Gems")); + m_primaryButton->setVisible(true); } } @@ -175,6 +190,10 @@ namespace O3DE::ProjectManager { HandleSecondaryButton(); } + else if (screen == ProjectManagerScreen::GemRepos) + { + NextScreen(); + } else { emit ChangeScreenRequest(screen); diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h index 40ddb14b83..5352d58082 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.h @@ -23,6 +23,7 @@ namespace O3DE::ProjectManager QT_FORWARD_DECLARE_CLASS(ScreenHeader) QT_FORWARD_DECLARE_CLASS(NewProjectSettingsScreen) QT_FORWARD_DECLARE_CLASS(GemCatalogScreen) + QT_FORWARD_DECLARE_CLASS(GemRepoScreen) class CreateProjectCtrl : public ScreenWidget @@ -67,6 +68,7 @@ namespace O3DE::ProjectManager NewProjectSettingsScreen* m_newProjectSettingsScreen = nullptr; GemCatalogScreen* m_gemCatalogScreen = nullptr; + GemRepoScreen* m_gemRepoScreen = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 98121d7cd2..1d238cf275 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -348,22 +348,6 @@ namespace O3DE::ProjectManager void GemCatalogScreen::HandleOpenGemRepo() { - QVector gemsToBeAdded = m_gemModel->GatherGemsToBeAdded(true); - QVector gemsToBeRemoved = m_gemModel->GatherGemsToBeRemoved(true); - - if (!gemsToBeAdded.empty() || !gemsToBeRemoved.empty()) - { - QMessageBox::StandardButton warningResult = QMessageBox::warning( - nullptr, "Pending Changes", - "There are some unsaved changes to the gem selection,
they will be lost if you change screens.
Are you sure?", - QMessageBox::No | QMessageBox::Yes); - - if (warningResult != QMessageBox::Yes) - { - return; - } - } - emit ChangeScreenRequest(ProjectManagerScreen::GemRepos); } diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index 1c8f9a6931..5de3511f84 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -39,10 +40,9 @@ namespace O3DE::ProjectManager m_updateSettingsScreen = new UpdateProjectSettingsScreen(); m_gemCatalogScreen = new GemCatalogScreen(); + m_gemRepoScreen = new GemRepoScreen(this); - connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, [this](ProjectManagerScreen screen){ - emit ChangeScreenRequest(screen); - }); + connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &UpdateProjectCtrl::OnChangeScreenRequest); m_stack = new QStackedWidget(this); m_stack->setObjectName("body"); @@ -69,6 +69,7 @@ namespace O3DE::ProjectManager m_stack->addWidget(topBarFrameWidget); m_stack->addWidget(m_gemCatalogScreen); + m_stack->addWidget(m_gemRepoScreen); QDialogButtonBox* backNextButtons = new QDialogButtonBox(); backNextButtons->setObjectName("footer"); @@ -102,6 +103,19 @@ namespace O3DE::ProjectManager m_gemCatalogScreen->ReinitForProject(m_projectInfo.m_path); } + void UpdateProjectCtrl::OnChangeScreenRequest(ProjectManagerScreen screen) + { + if (screen == ProjectManagerScreen::GemRepos) + { + m_stack->setCurrentWidget(m_gemRepoScreen); + Update(); + } + else + { + emit ChangeScreenRequest(screen); + } + } + void UpdateProjectCtrl::HandleGemsButton() { if (UpdateProjectSettings(true)) @@ -181,18 +195,26 @@ namespace O3DE::ProjectManager void UpdateProjectCtrl::Update() { - if (m_stack->currentIndex() == ScreenOrder::Gems) + if (m_stack->currentIndex() == ScreenOrder::GemRepos) + { + m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName())); + m_header->setSubTitle(QString(tr("Gem Repositories"))); + m_nextButton->setVisible(false); + } + else if (m_stack->currentIndex() == ScreenOrder::Gems) { m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName())); m_header->setSubTitle(QString(tr("Configure Gems"))); m_nextButton->setText(tr("Save")); + m_nextButton->setVisible(true); } else { m_header->setTitle(""); m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName())); m_nextButton->setText(tr("Save")); + m_nextButton->setVisible(true); } } diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h index 3321fad638..5fef296ee7 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h @@ -22,6 +22,7 @@ namespace O3DE::ProjectManager QT_FORWARD_DECLARE_CLASS(ScreenHeader) QT_FORWARD_DECLARE_CLASS(UpdateProjectSettingsScreen) QT_FORWARD_DECLARE_CLASS(GemCatalogScreen) + QT_FORWARD_DECLARE_CLASS(GemRepoScreen) class UpdateProjectCtrl : public ScreenWidget { @@ -37,6 +38,7 @@ namespace O3DE::ProjectManager void HandleBackButton(); void HandleNextButton(); void HandleGemsButton(); + void OnChangeScreenRequest(ProjectManagerScreen screen); void UpdateCurrentProject(const QString& projectPath); private: @@ -47,13 +49,15 @@ namespace O3DE::ProjectManager enum ScreenOrder { Settings, - Gems + Gems, + GemRepos }; ScreenHeader* m_header = nullptr; QStackedWidget* m_stack = nullptr; UpdateProjectSettingsScreen* m_updateSettingsScreen = nullptr; GemCatalogScreen* m_gemCatalogScreen = nullptr; + GemRepoScreen* m_gemRepoScreen = nullptr; QPushButton* m_backButton = nullptr; QPushButton* m_nextButton = nullptr; From 08255d2eda03bb65c2fea0358e1bbb76c707b74c Mon Sep 17 00:00:00 2001 From: nggieber Date: Fri, 29 Oct 2021 13:20:11 -0700 Subject: [PATCH 013/177] Clicking tag now select gem and scrolls to it, it also resets filters if gem is filtered out, also gem filter creation was refactored Signed-off-by: nggieber --- .../Source/GemCatalog/GemCatalogScreen.cpp | 38 ++- .../Source/GemCatalog/GemCatalogScreen.h | 3 +- .../Source/GemCatalog/GemFilterWidget.cpp | 295 ++++++++---------- .../Source/GemCatalog/GemFilterWidget.h | 32 +- .../Source/GemCatalog/GemModel.cpp | 1 + .../GemCatalog/GemSortFilterProxyModel.cpp | 2 + 6 files changed, 187 insertions(+), 184 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index deea46b582..6e377a5041 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -30,7 +30,7 @@ namespace O3DE::ProjectManager : ScreenWidget(parent) { m_gemModel = new GemModel(this); - m_proxModel = new GemSortFilterProxyModel(m_gemModel, this); + m_proxyModel = new GemSortFilterProxyModel(m_gemModel, this); QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setMargin(0); @@ -39,7 +39,7 @@ namespace O3DE::ProjectManager m_downloadController = new DownloadController(); - m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel, m_downloadController); + m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxyModel, m_downloadController); vLayout->addWidget(m_headerWidget); connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged); @@ -50,11 +50,11 @@ namespace O3DE::ProjectManager hLayout->setMargin(0); vLayout->addLayout(hLayout); - m_gemListView = new GemListView(m_proxModel, m_proxModel->GetSelectionModel(), this); + m_gemListView = new GemListView(m_proxyModel, m_proxyModel->GetSelectionModel(), this); m_gemInspector = new GemInspector(m_gemModel, this); m_gemInspector->setFixedWidth(240); - connect(m_gemInspector, &GemInspector::TagClicked, m_headerWidget, &GemCatalogHeaderWidget::SetSearchFilter); + connect(m_gemInspector, &GemInspector::TagClicked, this, &GemCatalogScreen::SelectGem); QWidget* filterWidget = new QWidget(this); filterWidget->setFixedWidth(240); @@ -63,7 +63,7 @@ namespace O3DE::ProjectManager m_filterWidgetLayout->setSpacing(0); filterWidget->setLayout(m_filterWidgetLayout); - GemListHeaderWidget* listHeaderWidget = new GemListHeaderWidget(m_proxModel); + GemListHeaderWidget* listHeaderWidget = new GemListHeaderWidget(m_proxyModel); QVBoxLayout* middleVLayout = new QVBoxLayout(); middleVLayout->setMargin(0); @@ -86,15 +86,17 @@ namespace O3DE::ProjectManager m_gemsToRegisterWithProject.clear(); FillModel(projectPath); + m_proxyModel->ResetFilters(); + if (m_filterWidget) { - m_filterWidget->hide(); - m_filterWidget->deleteLater(); + m_filterWidget->ResetAllFilters(); + } + else + { + m_filterWidget = new GemFilterWidget(m_proxyModel); + m_filterWidgetLayout->addWidget(m_filterWidget); } - - m_proxModel->ResetFilters(); - m_filterWidget = new GemFilterWidget(m_proxModel); - m_filterWidgetLayout->addWidget(m_filterWidget); m_headerWidget->ReinitForProject(); @@ -193,6 +195,20 @@ namespace O3DE::ProjectManager } } + void GemCatalogScreen::SelectGem(const QString& gemName) + { + QModelIndex modelIndex = m_gemModel->FindIndexByNameString(gemName); + if (!m_proxyModel->filterAcceptsRow(modelIndex.row(), QModelIndex())) + { + m_proxyModel->ResetFilters(); + m_filterWidget->ResetAllFilters(); + } + + QModelIndex proxyIndex = m_proxyModel->mapFromSource(modelIndex); + m_proxyModel->GetSelectionModel()->select(proxyIndex, QItemSelectionModel::ClearAndSelect); + m_gemListView->scrollTo(proxyIndex); + } + void GemCatalogScreen::hideEvent(QHideEvent* event) { ScreenWidget::hideEvent(event); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 1ade87af0c..cfcd77e67c 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -48,6 +48,7 @@ namespace O3DE::ProjectManager public slots: void OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies); void OnAddGemClicked(); + void SelectGem(const QString& gemName); protected: void hideEvent(QHideEvent* event) override; @@ -68,7 +69,7 @@ namespace O3DE::ProjectManager GemInspector* m_gemInspector = nullptr; GemModel* m_gemModel = nullptr; GemCatalogHeaderWidget* m_headerWidget = nullptr; - GemSortFilterProxyModel* m_proxModel = nullptr; + GemSortFilterProxyModel* m_proxyModel = nullptr; QVBoxLayout* m_filterWidgetLayout = nullptr; GemFilterWidget* m_filterWidget = nullptr; DownloadController* m_downloadController = nullptr; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp index 4f737d8629..b608445d0f 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.cpp @@ -213,11 +213,99 @@ namespace O3DE::ProjectManager m_filterLayout->setContentsMargins(0, 0, 0, 0); filterSection->setLayout(m_filterLayout); + ResetAllFilters(); + } + + void GemFilterWidget::ResetAllFilters() + { ResetGemStatusFilter(); - AddGemOriginFilter(); - AddTypeFilter(); - AddPlatformFilter(); - AddFeatureFilter(); + ResetGemOriginFilter(); + ResetTypeFilter(); + ResetPlatformFilter(); + ResetFeatureFilter(); + } + + void GemFilterWidget::ResetFilterWidget( + FilterCategoryWidget*& filterPtr, + const QString& filterName, + const QVector& elementNames, + const QVector& elementCounts, + int defaultShowCount) + { + bool wasCollapsed = false; + if (filterPtr) + { + wasCollapsed = filterPtr->IsCollapsed(); + } + + FilterCategoryWidget* filterWidget = new FilterCategoryWidget( + filterName, elementNames, elementCounts, /*showAllLessButton=*/defaultShowCount != 4, /*collapsed*/ wasCollapsed, + /*defaultShowCount=*/defaultShowCount); + if (filterPtr) + { + m_filterLayout->replaceWidget(filterPtr, filterWidget); + } + else + { + m_filterLayout->addWidget(filterWidget); + } + + filterPtr->deleteLater(); + filterPtr = filterWidget; + } + + template + void GemFilterWidget::ResetSimpleOrFilter( + FilterCategoryWidget*& filterPtr, + const QString& filterName, + int numFilterElements, + bool (*filterMatcher)(GemModel*, filterType, int), + QString (*typeStringGetter)(filterType), + filterFlagsType (GemSortFilterProxyModel::*filterFlagsGetter)() const, + void (GemSortFilterProxyModel::*filterFlagsSetter)(const filterFlagsType&)) + { + QVector elementNames; + QVector elementCounts; + const int numGems = m_gemModel->rowCount(); + for (int filterIndex = 0; filterIndex < numFilterElements; ++filterIndex) + { + const filterType gemFilterToBeCounted = static_cast(1 << filterIndex); + + int gemFilterCount = 0; + for (int gemIndex = 0; gemIndex < numGems; ++gemIndex) + { + // If filter matches increment filter count + gemFilterCount += filterMatcher(m_gemModel, gemFilterToBeCounted, gemIndex); + } + elementNames.push_back(typeStringGetter(gemFilterToBeCounted)); + elementCounts.push_back(gemFilterCount); + } + + // Replace existing filter and delete old one + ResetFilterWidget(filterPtr, filterName, elementNames, elementCounts); + + const QList buttons = filterPtr->GetButtonGroup()->buttons(); + for (int i = 0; i < buttons.size(); ++i) + { + const filterType gemFilter = static_cast(1 << i); + QAbstractButton* button = buttons[i]; + + connect( + button, &QAbstractButton::toggled, this, + [=](bool checked) + { + filterFlagsType gemFilters = (m_filterProxyModel->*filterFlagsGetter)(); + if (checked) + { + gemFilters |= gemFilter; + } + else + { + gemFilters &= ~gemFilter; + } + (m_filterProxyModel->*filterFlagsSetter)(gemFilters); + }); + } } void GemFilterWidget::ResetGemStatusFilter() @@ -241,25 +329,7 @@ namespace O3DE::ProjectManager elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Inactive)); elementCounts.push_back(totalGems - enabledGemTotal); - bool wasCollapsed = false; - if (m_statusFilter) - { - wasCollapsed = m_statusFilter->IsCollapsed(); - } - - FilterCategoryWidget* filterWidget = - new FilterCategoryWidget("Status", elementNames, elementCounts, /*showAllLessButton=*/false, /*collapsed*/wasCollapsed); - if (m_statusFilter) - { - m_filterLayout->replaceWidget(m_statusFilter, filterWidget); - } - else - { - m_filterLayout->addWidget(filterWidget); - } - - m_statusFilter->deleteLater(); - m_statusFilter = filterWidget; + ResetFilterWidget(m_statusFilter, "Status", elementNames, elementCounts); const QList buttons = m_statusFilter->GetButtonGroup()->buttons(); @@ -317,157 +387,42 @@ namespace O3DE::ProjectManager connect(activeButton, &QAbstractButton::toggled, this, updateGemActive); } - void GemFilterWidget::AddGemOriginFilter() + void GemFilterWidget::ResetGemOriginFilter() { - QVector elementNames; - QVector elementCounts; - const int numGems = m_gemModel->rowCount(); - for (int originIndex = 0; originIndex < GemInfo::NumGemOrigins; ++originIndex) - { - const GemInfo::GemOrigin gemOriginToBeCounted = static_cast(1 << originIndex); - - int gemOriginCount = 0; - for (int gemIndex = 0; gemIndex < numGems; ++gemIndex) + ResetSimpleOrFilter + ( + m_originFilter, "Provider", GemInfo::NumGemOrigins, + [](GemModel* gemModel, GemInfo::GemOrigin origin, int gemIndex) { - const GemInfo::GemOrigin gemOrigin = m_gemModel->GetGemOrigin(m_gemModel->index(gemIndex, 0)); - - // Is the gem of the given origin? - if (gemOriginToBeCounted == gemOrigin) - { - gemOriginCount++; - } - } - - elementNames.push_back(GemInfo::GetGemOriginString(gemOriginToBeCounted)); - elementCounts.push_back(gemOriginCount); - } - - FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Provider", elementNames, elementCounts, /*showAllLessButton=*/false); - m_filterLayout->addWidget(filterWidget); - - const QList buttons = filterWidget->GetButtonGroup()->buttons(); - for (int i = 0; i < buttons.size(); ++i) - { - const GemInfo::GemOrigin gemOrigin = static_cast(1 << i); - QAbstractButton* button = buttons[i]; - - connect(button, &QAbstractButton::toggled, this, [=](bool checked) - { - GemInfo::GemOrigins gemOrigins = m_filterProxyModel->GetGemOrigins(); - if (checked) - { - gemOrigins |= gemOrigin; - } - else - { - gemOrigins &= ~gemOrigin; - } - m_filterProxyModel->SetGemOrigins(gemOrigins); - }); - } + return origin == gemModel->GetGemOrigin(gemModel->index(gemIndex, 0)); + }, + &GemInfo::GetGemOriginString, &GemSortFilterProxyModel::GetGemOrigins, &GemSortFilterProxyModel::SetGemOrigins + ); } - void GemFilterWidget::AddTypeFilter() + void GemFilterWidget::ResetTypeFilter() { - QVector elementNames; - QVector elementCounts; - const int numGems = m_gemModel->rowCount(); - for (int typeIndex = 0; typeIndex < GemInfo::NumTypes; ++typeIndex) - { - const GemInfo::Type type = static_cast(1 << typeIndex); - - int typeGemCount = 0; - for (int gemIndex = 0; gemIndex < numGems; ++gemIndex) + ResetSimpleOrFilter( + m_typeFilter, "Type", GemInfo::NumTypes, + [](GemModel* gemModel, GemInfo::Type type, int gemIndex) { - const GemInfo::Types types = m_gemModel->GetTypes(m_gemModel->index(gemIndex, 0)); - - // Is type (Asset, Code, Tool) part of the gem? - if (types & type) - { - typeGemCount++; - } - } - - elementNames.push_back(GemInfo::GetTypeString(type)); - elementCounts.push_back(typeGemCount); - } - - FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Type", elementNames, elementCounts, /*showAllLessButton=*/false); - m_filterLayout->addWidget(filterWidget); - - const QList buttons = filterWidget->GetButtonGroup()->buttons(); - for (int i = 0; i < buttons.size(); ++i) - { - const GemInfo::Type type = static_cast(1 << i); - QAbstractButton* button = buttons[i]; - - connect(button, &QAbstractButton::toggled, this, [=](bool checked) - { - GemInfo::Types types = m_filterProxyModel->GetTypes(); - if (checked) - { - types |= type; - } - else - { - types &= ~type; - } - m_filterProxyModel->SetTypes(types); - }); - } + return static_cast(type & gemModel->GetTypes(gemModel->index(gemIndex, 0))); + }, + &GemInfo::GetTypeString, &GemSortFilterProxyModel::GetTypes, &GemSortFilterProxyModel::SetTypes); } - void GemFilterWidget::AddPlatformFilter() + void GemFilterWidget::ResetPlatformFilter() { - QVector elementNames; - QVector elementCounts; - const int numGems = m_gemModel->rowCount(); - for (int platformIndex = 0; platformIndex < GemInfo::NumPlatforms; ++platformIndex) - { - const GemInfo::Platform platform = static_cast(1 << platformIndex); - - int platformGemCount = 0; - for (int gemIndex = 0; gemIndex < numGems; ++gemIndex) + ResetSimpleOrFilter( + m_platformFilter, "Supported Platforms", GemInfo::NumPlatforms, + [](GemModel* gemModel, GemInfo::Platform platform, int gemIndex) { - const GemInfo::Platforms platforms = m_gemModel->GetPlatforms(m_gemModel->index(gemIndex, 0)); - - // Is platform supported? - if (platforms & platform) - { - platformGemCount++; - } - } - - elementNames.push_back(GemInfo::GetPlatformString(platform)); - elementCounts.push_back(platformGemCount); - } - - FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Supported Platforms", elementNames, elementCounts, /*showAllLessButton=*/false); - m_filterLayout->addWidget(filterWidget); - - const QList buttons = filterWidget->GetButtonGroup()->buttons(); - for (int i = 0; i < buttons.size(); ++i) - { - const GemInfo::Platform platform = static_cast(1 << i); - QAbstractButton* button = buttons[i]; - - connect(button, &QAbstractButton::toggled, this, [=](bool checked) - { - GemInfo::Platforms platforms = m_filterProxyModel->GetPlatforms(); - if (checked) - { - platforms |= platform; - } - else - { - platforms &= ~platform; - } - m_filterProxyModel->SetPlatforms(platforms); - }); - } + return static_cast(platform & gemModel->GetPlatforms(gemModel->index(gemIndex, 0))); + }, + &GemInfo::GetPlatformString, &GemSortFilterProxyModel::GetPlatforms, &GemSortFilterProxyModel::SetPlatforms); } - void GemFilterWidget::AddFeatureFilter() + void GemFilterWidget::ResetFeatureFilter() { // Alphabetically sorted, unique features and their number of occurrences in the gem database. QMap uniqueFeatureCounts; @@ -497,11 +452,15 @@ namespace O3DE::ProjectManager elementCounts.push_back(iterator.value()); } - FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Features", elementNames, elementCounts, - /*showAllLessButton=*/true, false, /*defaultShowCount=*/5); - m_filterLayout->addWidget(filterWidget); + ResetFilterWidget(m_featureFilter, "Features", elementNames, elementCounts, /*defaultShowCount=*/5); - const QList buttons = filterWidget->GetButtonGroup()->buttons(); + for (QMetaObject::Connection& connection : m_featureTagConnections) + { + disconnect(connection); + } + m_featureTagConnections.clear(); + + const QList buttons = m_featureFilter->GetButtonGroup()->buttons(); for (int i = 0; i < buttons.size(); ++i) { const QString& feature = elementNames[i]; @@ -523,13 +482,13 @@ namespace O3DE::ProjectManager }); // Sync the UI state with the proxy model filtering. - connect(m_filterProxyModel, &GemSortFilterProxyModel::OnInvalidated, this, [=] + m_featureTagConnections.push_back(connect(m_filterProxyModel, &GemSortFilterProxyModel::OnInvalidated, this, [=] { const QSet& filteredFeatureTags = m_filterProxyModel->GetFeatures(); const bool isChecked = filteredFeatureTags.contains(button->text()); QSignalBlocker signalsBlocker(button); button->setChecked(isChecked); - }); + })); } } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h index 6340f8309b..e422178d08 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h @@ -66,17 +66,41 @@ namespace O3DE::ProjectManager ~GemFilterWidget() = default; public slots: + void ResetAllFilters(); void ResetGemStatusFilter(); private: - void AddGemOriginFilter(); - void AddTypeFilter(); - void AddPlatformFilter(); - void AddFeatureFilter(); + void ResetGemOriginFilter(); + void ResetTypeFilter(); + void ResetPlatformFilter(); + void ResetFeatureFilter(); + + void ResetFilterWidget( + FilterCategoryWidget*& filterPtr, + const QString& filterName, + const QVector& elementNames, + const QVector& elementCounts, + int defaultShowCount = 4); + + template + void ResetSimpleOrFilter( + FilterCategoryWidget*& filterPtr, + const QString& filterName, + int numFilterElements, + bool (*filterMatcher)(GemModel*, filterType, int), + QString (*typeStringGetter)(filterType), + filterFlagsType (GemSortFilterProxyModel::*filterFlagsGetter)() const, + void (GemSortFilterProxyModel::*filterFlagsSetter)(const filterFlagsType&)); QVBoxLayout* m_filterLayout = nullptr; GemModel* m_gemModel = nullptr; GemSortFilterProxyModel* m_filterProxyModel = nullptr; FilterCategoryWidget* m_statusFilter = nullptr; + FilterCategoryWidget* m_originFilter = nullptr; + FilterCategoryWidget* m_typeFilter = nullptr; + FilterCategoryWidget* m_platformFilter = nullptr; + FilterCategoryWidget* m_featureFilter = nullptr; + + QVector m_featureTagConnections; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index acdef483ae..47f45b5559 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -54,6 +54,7 @@ namespace O3DE::ProjectManager appendRow(item); const QModelIndex modelIndex = index(rowCount()-1, 0); + m_nameToIndexMap[gemInfo.m_displayName] = modelIndex; m_nameToIndexMap[gemInfo.m_name] = modelIndex; } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp index 7ec45ac721..32d0e2fee9 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.cpp @@ -207,6 +207,8 @@ namespace O3DE::ProjectManager void GemSortFilterProxyModel::ResetFilters() { m_searchString.clear(); + m_gemSelectedFilter = GemSelected::NoFilter; + m_gemActiveFilter = GemActive::NoFilter; m_gemOriginFilter = {}; m_platformFilter = {}; m_typeFilter = {}; From 858e287b1fc0313f6b5dade0f56a34feb3828d71 Mon Sep 17 00:00:00 2001 From: nggieber Date: Fri, 29 Oct 2021 13:37:58 -0700 Subject: [PATCH 014/177] Removed unused set filter function Signed-off-by: nggieber --- .../Source/GemCatalog/GemCatalogHeaderWidget.cpp | 5 ----- .../Source/GemCatalog/GemCatalogHeaderWidget.h | 3 --- 2 files changed, 8 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 77b0e2b7d2..5d65c740af 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -439,9 +439,4 @@ namespace O3DE::ProjectManager { m_filterLineEdit->setText({}); } - - void GemCatalogHeaderWidget::SetSearchFilter(const QString& filter) - { - m_filterLineEdit->setText(filter); - } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index 66bd617fc4..4d17259840 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -86,9 +86,6 @@ namespace O3DE::ProjectManager void ReinitForProject(); - public slots: - void SetSearchFilter(const QString& filter); - signals: void AddGem(); void OpenGemsRepo(); From 259ee654aec3c1793d5e78cc4fdbe443747fa3f6 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Mon, 1 Nov 2021 11:18:38 -0700 Subject: [PATCH 015/177] WIP refresh gem catalog in place Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> --- .../Source/CreateProjectCtrl.cpp | 7 +++ .../Source/GemCatalog/GemCatalogScreen.cpp | 55 +++++++++++++++++++ .../Source/GemCatalog/GemCatalogScreen.h | 1 + .../Source/GemCatalog/GemModel.cpp | 11 ++++ .../Source/GemCatalog/GemModel.h | 3 + .../Source/GemRepo/GemRepoScreen.cpp | 4 ++ .../Source/GemRepo/GemRepoScreen.h | 4 ++ 7 files changed, 85 insertions(+) diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index c5325bfb42..e33e9449fe 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -55,6 +55,13 @@ namespace O3DE::ProjectManager connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest); + connect( + m_gemRepoScreen, &GemRepoScreen::OnRefresh, + [this]() + { + const QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath(); + m_gemCatalogScreen->Refresh(projectTemplatePath + "/Template"); + }); // When there are multiple project templates present, we re-gather the gems when changing the selected the project template. connect(m_newProjectSettingsScreen, &NewProjectSettingsScreen::OnTemplateSelectionChanged, this, [=](int oldIndex, [[maybe_unused]] int newIndex) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 1d238cf275..fb78736a3c 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include namespace O3DE::ProjectManager { @@ -145,6 +147,59 @@ namespace O3DE::ProjectManager } } + void GemCatalogScreen::Refresh(const QString& projectPath) + { + QHash gemInfoHash; + + AZ::Outcome, AZStd::string> allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath); + if (allGemInfosResult.IsSuccess()) + { + QVector gemInfos = allGemInfosResult.GetValue(); + for (const GemInfo& gemInfo : gemInfos) + { + gemInfoHash.insert(gemInfo.m_name, gemInfo); + } + } + + AZ::Outcome, AZStd::string> allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos(); + if (allRepoGemInfosResult.IsSuccess()) + { + const QVector allRepoGemInfos = allRepoGemInfosResult.GetValue(); + for (const GemInfo& gemInfo : allRepoGemInfos) + { + if (!gemInfoHash.contains(gemInfo.m_name)) + { + gemInfoHash.insert(gemInfo.m_name, gemInfo); + } + } + } + + // remove rows for gems that were removed and not project dependencies + int i = 0; + while (i < m_gemModel->rowCount()) + { + QModelIndex index = m_gemModel->index(i,0); + QString gemName = m_gemModel->GetName(index); + if (!gemInfoHash.contains(gemName) && !m_gemModel->IsAdded(index) && !m_gemModel->IsAddedDependency(index)) + { + m_gemModel->removeRow(i); + } + else + { + gemInfoHash.remove(gemName); + i++; + } + } + + // add new rows + for(auto iter = gemInfoHash.begin(); iter != gemInfoHash.end(); ++iter) + { + m_gemModel->AddGem(iter.value()); + } + + m_gemModel->UpdateGemDependencies(); + } + void GemCatalogScreen::OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies) { if (m_notificationsEnabled) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 1b34019d1a..b866b2d3af 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -33,6 +33,7 @@ namespace O3DE::ProjectManager ProjectManagerScreen GetScreenEnum() override; void ReinitForProject(const QString& projectPath); + void Refresh(const QString& projectPath); enum class EnableDisableGemsResult { diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index fb228c0b4a..a059df4cbf 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -18,6 +18,7 @@ namespace O3DE::ProjectManager : QStandardItemModel(parent) { m_selectionModel = new QItemSelectionModel(this, parent); + connect(this, &QAbstractItemModel::rowsAboutToBeRemoved, this, &GemModel::OnRowsAboutToBeRemoved); } QItemSelectionModel* GemModel::GetSelectionModel() const @@ -359,6 +360,16 @@ namespace O3DE::ProjectManager gemModel->emit gemStatusChanged(gemName, numChangedDependencies); } + void GemModel::OnRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last) + { + for (int i = first; i <= last; ++i) + { + QModelIndex modelIndex = index(i, 0, parent); + const QString& gemName = GetName(modelIndex); + m_nameToIndexMap.remove(gemName); + } + } + void GemModel::SetIsAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded) { model.setData(modelIndex, isAdded, RoleIsAddedDependency); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index 35231cc105..f4bc1ec502 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -80,6 +80,9 @@ namespace O3DE::ProjectManager signals: void gemStatusChanged(const QString& gemName, uint32_t numChangedDependencies); + protected slots: + void OnRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last); + private: void FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames); void GetAllDependingGems(const QModelIndex& modelIndex, QSet& inOutGems); diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp index 0ddfe41434..30a57417d2 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp @@ -91,6 +91,7 @@ namespace O3DE::ProjectManager if (addGemRepoResult) { Reinit(); + emit OnRefresh(); } else { @@ -116,6 +117,7 @@ namespace O3DE::ProjectManager if (removeGemRepoResult) { Reinit(); + emit OnRefresh(); } else { @@ -130,6 +132,7 @@ namespace O3DE::ProjectManager { bool refreshResult = PythonBindingsInterface::Get()->RefreshAllGemRepos(); Reinit(); + emit OnRefresh(); if (!refreshResult) { @@ -146,6 +149,7 @@ namespace O3DE::ProjectManager if (refreshResult.IsSuccess()) { Reinit(); + emit OnRefresh(); } else { diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h index 46a733362a..0516005eef 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h @@ -28,6 +28,7 @@ namespace O3DE::ProjectManager class GemRepoScreen : public ScreenWidget { + Q_OBJECT public: explicit GemRepoScreen(QWidget* parent = nullptr); ~GemRepoScreen() = default; @@ -37,6 +38,9 @@ namespace O3DE::ProjectManager GemRepoModel* GetGemRepoModel() const { return m_gemRepoModel; } + signals: + void OnRefresh(); + public slots: void HandleAddRepoButton(); void HandleRemoveRepoButton(const QModelIndex& modelIndex); From 1025eb3929178d1578870ded1714517a4cdad1cc Mon Sep 17 00:00:00 2001 From: srikappa-amzn Date: Mon, 1 Nov 2021 10:12:22 -0700 Subject: [PATCH 016/177] Revert "Delay propagation for all template updates in detach prefab workflow (#4707)" This reverts commit 87533d80c11812c14b1262b151493f3be655739e. Signed-off-by: srikappa-amzn --- .../Prefab/PrefabPublicHandler.cpp | 6 +++--- .../AzToolsFramework/Prefab/PrefabUndo.cpp | 14 ++++++++++---- .../AzToolsFramework/Prefab/PrefabUndo.h | 8 ++++---- .../AzToolsFramework/Prefab/PrefabUndoHelpers.cpp | 4 ++-- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index d976c91c3e..4a7d41a3af 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -1051,10 +1051,10 @@ namespace AzToolsFramework DuplicateNestedEntitiesInInstance(commonOwningInstance->get(), entities, instanceDomAfter, duplicatedEntityAndInstanceIds, duplicateEntityAliasMap); - PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication", false); + PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication"); command->SetParent(undoBatch.GetUndoBatch()); command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId()); - command->Redo(); + command->RedoBatched(); DuplicateNestedInstancesInInstance(commonOwningInstance->get(), instances, instanceDomAfter, duplicatedEntityAndInstanceIds, newInstanceAliasToOldInstanceMap); @@ -1322,7 +1322,7 @@ namespace AzToolsFramework Prefab::PrefabDom instanceDomAfter; m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, parentInstance); - PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment", false); + PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment"); command->Capture(instanceDomBefore, instanceDomAfter, parentTemplateId); command->SetParent(undoBatch.GetUndoBatch()); { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp index 1c2230fa83..b298304e3b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp @@ -17,16 +17,17 @@ namespace AzToolsFramework { PrefabUndoBase::PrefabUndoBase(const AZStd::string& undoOperationName) : UndoSystem::URSequencePoint(undoOperationName) + , m_changed(true) + , m_templateId(InvalidTemplateId) { m_instanceToTemplateInterface = AZ::Interface::Get(); AZ_Assert(m_instanceToTemplateInterface, "Failed to grab instance to template interface"); } //PrefabInstanceUndo - PrefabUndoInstance::PrefabUndoInstance(const AZStd::string& undoOperationName, bool useImmediatePropagation) + PrefabUndoInstance::PrefabUndoInstance(const AZStd::string& undoOperationName) : PrefabUndoBase(undoOperationName) { - m_useImmediatePropagation = useImmediatePropagation; } void PrefabUndoInstance::Capture( @@ -42,12 +43,17 @@ namespace AzToolsFramework void PrefabUndoInstance::Undo() { - m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, m_useImmediatePropagation); + m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, true); } void PrefabUndoInstance::Redo() { - m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, m_useImmediatePropagation); + m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, true); + } + + void PrefabUndoInstance::RedoBatched() + { + m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h index bc0b86a8c6..8669024df7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h @@ -29,15 +29,14 @@ namespace AzToolsFramework bool Changed() const override { return m_changed; } protected: - TemplateId m_templateId = InvalidTemplateId; + TemplateId m_templateId; PrefabDom m_redoPatch; PrefabDom m_undoPatch; InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr; - bool m_changed = true; - bool m_useImmediatePropagation = true; + bool m_changed; }; //! handles the addition and removal of entities from instances @@ -45,7 +44,7 @@ namespace AzToolsFramework : public PrefabUndoBase { public: - explicit PrefabUndoInstance(const AZStd::string& undoOperationName, bool useImmediatePropagation = true); + explicit PrefabUndoInstance(const AZStd::string& undoOperationName); void Capture( const PrefabDom& initialState, @@ -54,6 +53,7 @@ namespace AzToolsFramework void Undo() override; void Redo() override; + void RedoBatched(); }; //! handles entity updates, such as when the values on an entity change diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp index 31a0c60bcb..9803b55324 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp @@ -23,10 +23,10 @@ namespace AzToolsFramework PrefabDom instanceDomAfterUpdate; PrefabDomUtils::StoreInstanceInPrefabDom(instance, instanceDomAfterUpdate); - PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage, false); + PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage); state->Capture(instanceDomBeforeUpdate, instanceDomAfterUpdate, instance.GetTemplateId()); state->SetParent(undoBatch); - state->Redo(); + state->RedoBatched(); } LinkId CreateLink( From 729a79dc82a2070b4d37a6ea8cec9f3a30b640cb Mon Sep 17 00:00:00 2001 From: srikappa-amzn Date: Mon, 1 Nov 2021 10:26:57 -0700 Subject: [PATCH 017/177] Revert "Fixing undo/redo not updating transform pivot point (#4375)" This reverts commit 7018f16088c36377b88f86f79c6de3d03cb81beb. Signed-off-by: srikappa-amzn --- .../Prefab/Instance/InstanceToTemplateInterface.h | 3 +-- .../Instance/InstanceToTemplatePropagator.cpp | 4 ++-- .../Instance/InstanceToTemplatePropagator.h | 2 +- .../Prefab/Instance/InstanceUpdateExecutor.cpp | 7 +------ .../Prefab/Instance/InstanceUpdateExecutor.h | 2 +- .../Instance/InstanceUpdateExecutorInterface.h | 2 +- .../Prefab/PrefabPublicHandler.cpp | 2 +- .../Prefab/PrefabSystemComponent.cpp | 10 +++++----- .../Prefab/PrefabSystemComponent.h | 7 ++----- .../Prefab/PrefabSystemComponentInterface.h | 2 +- .../AzToolsFramework/Prefab/PrefabUndo.cpp | 15 +++++---------- .../AzToolsFramework/Prefab/PrefabUndo.h | 1 - .../AzToolsFramework/Prefab/PrefabUndoHelpers.cpp | 2 +- 13 files changed, 22 insertions(+), 37 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h index a8c717d6b0..b944ef159a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h @@ -46,11 +46,10 @@ namespace AzToolsFramework //! Updates the template links (updating instances) for the given template and triggers propagation on its instances. //! @param providedPatch The patch to apply to the template. //! @param templateId The id of the template to update. - //! @param immediate An optional flag whether to apply the patch immediately (needed for Undo/Redos) or wait until next system tick. //! @param instanceToExclude An optional reference to an instance of the template being updated that should not be refreshes as part of propagation. //! Defaults to nullopt, which means that all instances will be refreshed. //! @return True if the template was patched correctly, false if the operation failed. - virtual bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0; + virtual bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0; virtual void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp index 73acb9b8a4..6b281bcbae 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp @@ -156,7 +156,7 @@ namespace AzToolsFramework } } - bool InstanceToTemplatePropagator::PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, bool immediate, InstanceOptionalReference instanceToExclude) + bool InstanceToTemplatePropagator::PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, InstanceOptionalReference instanceToExclude) { PrefabDom& templateDomReference = m_prefabSystemComponentInterface->FindTemplateDom(templateId); @@ -178,7 +178,7 @@ namespace AzToolsFramework (result.GetOutcome() != AZ::JsonSerializationResult::Outcomes::PartialSkip), "Some of the patches were not successfully applied."); m_prefabSystemComponentInterface->SetTemplateDirtyFlag(templateId, true); - m_prefabSystemComponentInterface->PropagateTemplateChanges(templateId, immediate, instanceToExclude); + m_prefabSystemComponentInterface->PropagateTemplateChanges(templateId, instanceToExclude); return true; } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.h index 80fe7de8d5..75acb410c9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.h @@ -33,7 +33,7 @@ namespace AzToolsFramework InstanceOptionalReference GetTopMostInstanceInHierarchy(AZ::EntityId entityId) override; - bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override; + bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override; void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp index feea3ce25b..9ef74167a6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp @@ -52,7 +52,7 @@ namespace AzToolsFramework AZ::Interface::Unregister(this); } - void InstanceUpdateExecutor::AddTemplateInstancesToQueue(TemplateId instanceTemplateId, bool immediate, InstanceOptionalReference instanceToExclude) + void InstanceUpdateExecutor::AddTemplateInstancesToQueue(TemplateId instanceTemplateId, InstanceOptionalReference instanceToExclude) { auto findInstancesResult = m_templateInstanceMapperInterface->FindInstancesOwnedByTemplate(instanceTemplateId); @@ -79,11 +79,6 @@ namespace AzToolsFramework m_instancesUpdateQueue.emplace_back(instance); } } - - if (immediate) - { - UpdateTemplateInstancesInQueue(); - } } void InstanceUpdateExecutor::RemoveTemplateInstanceFromQueue(const Instance* instance) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.h index de2b483c4d..ee461eae88 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.h @@ -31,7 +31,7 @@ namespace AzToolsFramework explicit InstanceUpdateExecutor(int instanceCountToUpdateInBatch = 0); - void AddTemplateInstancesToQueue(TemplateId instanceTemplateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override; + void AddTemplateInstancesToQueue(TemplateId instanceTemplateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override; bool UpdateTemplateInstancesInQueue() override; virtual void RemoveTemplateInstanceFromQueue(const Instance* instance) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h index 3b894efd21..8ad032e1d0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h @@ -23,7 +23,7 @@ namespace AzToolsFramework virtual ~InstanceUpdateExecutorInterface() = default; // Add all Instances of Template with given Id into a queue for updating them later. - virtual void AddTemplateInstancesToQueue(TemplateId instanceTemplateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0; + virtual void AddTemplateInstancesToQueue(TemplateId instanceTemplateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0; // Update Instances in the waiting queue. virtual bool UpdateTemplateInstancesInQueue() = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 4a7d41a3af..84fe476fb3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -1054,7 +1054,7 @@ namespace AzToolsFramework PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication"); command->SetParent(undoBatch.GetUndoBatch()); command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId()); - command->RedoBatched(); + command->Redo(); DuplicateNestedInstancesInInstance(commonOwningInstance->get(), instances, instanceDomAfter, duplicatedEntityAndInstanceIds, newInstanceAliasToOldInstanceMap); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index c930c66786..1f00b952b1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -159,10 +159,10 @@ namespace AzToolsFramework newInstance->SetTemplateId(newTemplateId); } } - - void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId, bool immediate, InstanceOptionalReference instanceToExclude) + + void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId, InstanceOptionalReference instanceToExclude) { - UpdatePrefabInstances(templateId, immediate, instanceToExclude); + UpdatePrefabInstances(templateId, instanceToExclude); auto templateIdToLinkIdsIterator = m_templateToLinkIdsMap.find(templateId); if (templateIdToLinkIdsIterator != m_templateToLinkIdsMap.end()) @@ -191,9 +191,9 @@ namespace AzToolsFramework } } - void PrefabSystemComponent::UpdatePrefabInstances(TemplateId templateId, bool immediate, InstanceOptionalReference instanceToExclude) + void PrefabSystemComponent::UpdatePrefabInstances(TemplateId templateId, InstanceOptionalReference instanceToExclude) { - m_instanceUpdateExecutor.AddTemplateInstancesToQueue(templateId, immediate, instanceToExclude); + m_instanceUpdateExecutor.AddTemplateInstancesToQueue(templateId, instanceToExclude); } void PrefabSystemComponent::UpdateLinkedInstances(AZStd::queue& linkIdsQueue) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h index 480bb83121..c3190c6201 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h @@ -231,17 +231,14 @@ namespace AzToolsFramework */ void UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom) override; - void PropagateTemplateChanges(TemplateId templateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override; + void PropagateTemplateChanges(TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override; /** * Updates all Instances owned by a Template. * * @param templateId The id of the Template owning Instances to update. - * @param immediate An optional flag whether to apply the patch immediately (needed for Undo/Redos) or wait until next system tick. - * @param instanceToExclude An optional reference to an instance of the template being updated that should not be refreshes as part of propagation. - * Defaults to nullopt, which means that all instances will be refreshed. */ - void UpdatePrefabInstances(TemplateId templateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt); + void UpdatePrefabInstances(TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt); private: AZ_DISABLE_COPY_MOVE(PrefabSystemComponent); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h index 66d85ccee9..761d66fd52 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h @@ -67,7 +67,7 @@ namespace AzToolsFramework virtual PrefabDom& FindTemplateDom(TemplateId templateId) = 0; virtual void UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom) = 0; - virtual void PropagateTemplateChanges(TemplateId templateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0; + virtual void PropagateTemplateChanges(TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0; virtual AZStd::unique_ptr InstantiatePrefab( AZ::IO::PathView filePath, InstanceOptionalReference parent = AZStd::nullopt) = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp index b298304e3b..385e9b149b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp @@ -43,15 +43,10 @@ namespace AzToolsFramework void PrefabUndoInstance::Undo() { - m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, true); + m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId); } void PrefabUndoInstance::Redo() - { - m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, true); - } - - void PrefabUndoInstance::RedoBatched() { m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId); } @@ -96,7 +91,7 @@ namespace AzToolsFramework void PrefabUndoEntityUpdate::Undo() { [[maybe_unused]] bool isPatchApplicationSuccessful = - m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, true); + m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId); AZ_Error( "Prefab", isPatchApplicationSuccessful, @@ -107,7 +102,7 @@ namespace AzToolsFramework void PrefabUndoEntityUpdate::Redo() { [[maybe_unused]] bool isPatchApplicationSuccessful = - m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, true); + m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId); AZ_Error( "Prefab", isPatchApplicationSuccessful, @@ -118,7 +113,7 @@ namespace AzToolsFramework void PrefabUndoEntityUpdate::Redo(InstanceOptionalReference instanceToExclude) { [[maybe_unused]] bool isPatchApplicationSuccessful = - m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, false, instanceToExclude); + m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, instanceToExclude); AZ_Error( "Prefab", isPatchApplicationSuccessful, @@ -334,7 +329,7 @@ namespace AzToolsFramework //propagate the link changes link->get().UpdateTarget(); - m_prefabSystemComponentInterface->PropagateTemplateChanges(link->get().GetTargetTemplateId(), false, instanceToExclude); + m_prefabSystemComponentInterface->PropagateTemplateChanges(link->get().GetTargetTemplateId(), instanceToExclude); //mark as dirty m_prefabSystemComponentInterface->SetTemplateDirtyFlag(link->get().GetTargetTemplateId(), true); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h index 8669024df7..0af94f86cc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h @@ -53,7 +53,6 @@ namespace AzToolsFramework void Undo() override; void Redo() override; - void RedoBatched(); }; //! handles entity updates, such as when the values on an entity change diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp index 9803b55324..9c44fc7ffd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp @@ -26,7 +26,7 @@ namespace AzToolsFramework PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage); state->Capture(instanceDomBeforeUpdate, instanceDomAfterUpdate, instance.GetTemplateId()); state->SetParent(undoBatch); - state->RedoBatched(); + state->Redo(); } LinkId CreateLink( From f7a48fda117f9966da1a533b719680e9961b9bcc Mon Sep 17 00:00:00 2001 From: srikappa-amzn Date: Mon, 1 Nov 2021 11:41:05 -0700 Subject: [PATCH 018/177] Added a missing function comment for UpdatePrefabInstances function Signed-off-by: srikappa-amzn --- .../AzToolsFramework/Prefab/PrefabSystemComponent.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h index c3190c6201..7b18d64b08 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h @@ -237,6 +237,8 @@ namespace AzToolsFramework * Updates all Instances owned by a Template. * * @param templateId The id of the Template owning Instances to update. + * @param instanceToExclude An optional reference to an instance of the template being updated that should not be refreshed + * as part of propagation.Defaults to nullopt, which means that all instances will be refreshed. */ void UpdatePrefabInstances(TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt); From 045a826c681c1ce903fa7924c7f1d781d784d238 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Mon, 1 Nov 2021 14:18:04 -0700 Subject: [PATCH 019/177] Updates to the Spawnable entity aliases based on provided feedback on PR. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../Spawnable/RootSpawnableInterface.h | 4 ++-- .../AzFramework/Spawnable/Spawnable.cpp | 16 +++++----------- .../AzFramework/Spawnable/Spawnable.h | 7 +++++-- .../Spawnable/SpawnableAssetHandler.cpp | 1 + .../Spawnable/SpawnableEntitiesContainer.h | 4 ++-- .../Spawnable/SpawnableEntitiesManager.cpp | 2 +- .../Spawnable/SpawnableEntitiesManagerTests.cpp | 12 ++++++------ .../Tests/Spawnable/SpawnableTests.cpp | 8 ++++---- .../Prefab/Spawnable/PrefabProcessorContext.h | 2 +- .../Prefab/Spawnable/SpawnableUtils.cpp | 6 +++--- 10 files changed, 30 insertions(+), 32 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h b/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h index 873123f38b..d3fe62eae3 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/RootSpawnableInterface.h @@ -30,7 +30,7 @@ namespace AzFramework //! Called when the root spawnable has been assigned a new value. This may be called several times without a call to release //! in between. - //! NOTE: The callback is not queued but immediately called from a random thread. This is done because this callback is typically + //! @note: The callback is not queued but immediately called from a random thread. This is done because this callback is typically //! used before entities are spawned and if it's queued then the entities spawn before this callback is called. //! @param rootSpawnable The new root spawnable that was assigned. //! @param generation The generation of the root spawnable. This will increment every time a new spawnable is assigned. @@ -38,7 +38,7 @@ namespace AzFramework [[maybe_unused]] uint32_t generation) {} //! Called when the root spawnable has completed spawning of entities. This may be called several times without a call to release //! in between. - //! NOTE: This callback is queued and will be called with a delay and from the main thread. + //! @note: This callback is queued and will be called with a delay and from the main thread. //! @param rootSpawnable The new root spawnable that was used to spawn entities from. //! @param generation The generation of the root spawnable. This will increment every time a new spawnable is assigned. virtual void OnRootSpawnableReady( diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp index 7548a8f181..a3cbf861cb 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp @@ -20,7 +20,6 @@ namespace AzFramework // EntityAlias // - bool Spawnable::EntityAlias::HasLowerIndex(const EntityAlias& other) const { return m_sourceIndex == other.m_sourceIndex ? @@ -51,7 +50,7 @@ namespace AzFramework { if (!alias.m_queueLoad || alias.m_aliasType == Spawnable::EntityAliasType::Original || - alias.m_aliasType == Spawnable::EntityAliasType::Disabled) + alias.m_aliasType == Spawnable::EntityAliasType::Disable) { continue; } @@ -132,7 +131,6 @@ namespace AzFramework // EntityAliasVisitor // - Spawnable::EntityAliasVisitor::EntityAliasVisitor(Spawnable& owner, EntityAliasList* entityAliasList) : m_owner(owner) , m_entityAliasList(entityAliasList) @@ -167,11 +165,7 @@ namespace AzFramework if (this != &rhs) { this->~EntityAliasVisitor(); - *this = EntityAliasVisitor(rhs.m_owner, rhs.m_entityAliasList); - m_dirty = rhs.m_dirty; - - rhs.m_entityAliasList = nullptr; - rhs.m_dirty = false; + new(this) EntityAliasVisitor(AZStd::move(rhs)); } return *this; } @@ -249,7 +243,7 @@ namespace AzFramework { if (alias.m_queueLoad && alias.m_aliasType != Spawnable::EntityAliasType::Original && - alias.m_aliasType != Spawnable::EntityAliasType::Disabled && + alias.m_aliasType != Spawnable::EntityAliasType::Disable && !alias.m_spawnable.IsLoading() && !alias.m_spawnable.IsReady() && !alias.m_spawnable.IsError()) @@ -336,14 +330,14 @@ namespace AzFramework { case Spawnable::EntityAliasType::Original: [[fallthrough]]; - case Spawnable::EntityAliasType::Disabled: + case Spawnable::EntityAliasType::Disable: [[fallthrough]]; case Spawnable::EntityAliasType::Replace: // If the previous entry was a disabled, original or replace alias then remove it as it will be overwritten by the // current entry. if (previousIndex == it->m_sourceIndex && (previousType == Spawnable::EntityAliasType::Original || - previousType == Spawnable::EntityAliasType::Disabled || + previousType == Spawnable::EntityAliasType::Disable || previousType == Spawnable::EntityAliasType::Replace)) { previousIndex = it->m_sourceIndex; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h index 0a35b81fb1..9058ac7ba4 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h @@ -34,7 +34,7 @@ namespace AzFramework enum class EntityAliasType : uint8_t { Original, //!< The original entity is spawned. - Disabled, //!< No entity will be spawned. + Disable, //!< No entity will be spawned. Replace, //!< The entity alias is spawned instead of the original. Additional, //!< The original entity is spawned as well as the alias. The alias will get a new entity id. Merge //!< The original entity is spawned and the components of the alias are added. The caller is responsible for @@ -105,6 +105,9 @@ namespace AzFramework bool HasAliases() const; bool AreAllSpawnablesReady() const; + // Modification of aliases is limited to specific changes that can only be done through the available modification functions. + // For this reason access through iterators is limited to unmodifiable constant iterators. + EntityAliasList::const_iterator begin() const; EntityAliasList::const_iterator end() const; EntityAliasList::const_iterator cbegin() const; @@ -146,7 +149,7 @@ namespace AzFramework class EntityAliasConstVisitor final : public EntityAliasVisitorBase { public: - EntityAliasConstVisitor(const Spawnable& owner, const EntityAliasList* m_entityAliasList); + EntityAliasConstVisitor(const Spawnable& owner, const EntityAliasList* entityAliasList); ~EntityAliasConstVisitor(); //! Checks if the visitor was able to retrieve data. This needs to be checked before calling any other functions. diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp index 0cd9891948..6ef423fa91 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp @@ -118,6 +118,7 @@ namespace AzFramework SpawnableAssetEventsBus::Broadcast( &SpawnableAssetEvents::OnResolveAliases, aliases, spawnable->GetMetaData(), spawnable->GetEntities()); + // The aliases will only be optimized if OnResolveAliases has made any changes. aliases.Optimize(); aliases.ListSpawnablesRequiringLoad( [&assetLoadFilterCB, streamingDeadline, streamingPriority](AZ::Data::Asset& assetPendingLoad) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.h index 1ec6e6a665..cabef38ff5 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesContainer.h @@ -79,9 +79,9 @@ namespace AzFramework //! other than the calling thread including the main thread. Note that because the alert is queued it can still be called //! after the container has been deleted or can be called for a previously assigned spawnable. In the latter case check //! if the current generation matches the generation provided with the callback. - //! @callback The function called when the alert triggers. This can be called from a different thread than the one that + //! @param callback The function called when the alert triggers. This can be called from a different thread than the one that //! the one that made the call to Alert. - //! @checkSpawnableIsLoaded If true the alert will also block until the spawnable has been loaded. If false then it will + //! @param checkSpawnableIsLoaded If true the alert will also block until the spawnable has been loaded. If false then it will //! be called after all previous calls have completed, but the spawnable may not be loaded at that point. void Alert(AlertCallback callback, CheckIfSpawnableIsLoaded spawnableCheck = CheckIfSpawnableIsLoaded::No); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index d7d3f59154..33fe1601af 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -327,7 +327,7 @@ namespace AzFramework clone = CloneSingleEntity(entityTemplate, templateToCloneMap, serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); return clone; - case Spawnable::EntityAliasType::Disabled: + case Spawnable::EntityAliasType::Disable: // Do nothing. return nullptr; case Spawnable::EntityAliasType::Replace: diff --git a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 38847edc07..83c895bc7a 100644 --- a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -485,8 +485,8 @@ namespace UnitTest FillSpawnable(NumEntities); InsertEntityAliases( { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, - { Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled, - Spawnable::EntityAliasType::Disabled }); + { Spawnable::EntityAliasType::Disable, Spawnable::EntityAliasType::Disable, Spawnable::EntityAliasType::Disable, + Spawnable::EntityAliasType::Disable }); size_t spawnedEntitiesCount = 0; auto callback = [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) @@ -506,7 +506,7 @@ namespace UnitTest using namespace AzFramework; static constexpr size_t NumEntities = 8; FillSpawnable(NumEntities); - InsertEntityAliases<2>({ 1, 3 }, { 1, 3 }, { Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled }); + InsertEntityAliases<2>({ 1, 3 }, { 1, 3 }, { Spawnable::EntityAliasType::Disable, Spawnable::EntityAliasType::Disable }); size_t spawnedEntitiesCount = 0; auto callback = [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) @@ -1019,8 +1019,8 @@ namespace UnitTest InsertEntityAliases( { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, - { Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled, - Spawnable::EntityAliasType::Disabled }); + { Spawnable::EntityAliasType::Disable, Spawnable::EntityAliasType::Disable, Spawnable::EntityAliasType::Disable, + Spawnable::EntityAliasType::Disable }); AZStd::vector indices = { 0, 2, 3, 1 }; @@ -1043,7 +1043,7 @@ namespace UnitTest FillSpawnable(8); InsertEntityAliases<3>( { 1, 3, 6 }, { 1, 3, 6 }, - { Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Disabled }); + { Spawnable::EntityAliasType::Disable, Spawnable::EntityAliasType::Disable, Spawnable::EntityAliasType::Disable }); AZStd::vector indices = { 0, 2, 3, 1, 2, 3, 0, 1, 6, 4, 5, 7, 4, 1, 0, 6 }; diff --git a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableTests.cpp b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableTests.cpp index f7d6d5190f..c689295f17 100644 --- a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableTests.cpp +++ b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableTests.cpp @@ -191,8 +191,8 @@ namespace UnitTest InsertEightEntities(); InsertEightEntityAliases( { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, - { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Disabled, - Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Disabled, + { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Disable, + Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Disable, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original }); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); @@ -245,14 +245,14 @@ namespace UnitTest InsertEightEntityAliases( { 0, 0, 0, 1, 1, 2, 2, 2 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, { Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, - Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Disabled, Spawnable::EntityAliasType::Original, + Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Disable, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original }); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); ASSERT_TRUE(visitor.IsSet()); EXPECT_EQ(2, AZStd::distance(visitor.begin(), visitor.end())); - EXPECT_EQ(Spawnable::EntityAliasType::Disabled, visitor.begin()->m_aliasType); + EXPECT_EQ(Spawnable::EntityAliasType::Disable, visitor.begin()->m_aliasType); EXPECT_EQ(Spawnable::EntityAliasType::Replace, visitor.begin()[1].m_aliasType); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h index c937b974e9..8e29deadca 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h @@ -27,7 +27,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils { enum class EntityAliasType : uint8_t { - Disabled, //!< No alias is added. + Disable, //!< No alias is added. OptionalReplace, //!< At runtime the entity might be replaced. If the alias is disabled the original entity will be spawned. //!< The original entity will be left in the spawnable and a copy is returned. Replace, //!< At runtime the entity will be replaced. If the alias is disabled nothing will be spawned not. The original diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp index 154337e957..7b14ad1228 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp @@ -114,9 +114,9 @@ namespace AzToolsFramework::Prefab::SpawnableUtils switch (aliasType) { - case PCU::EntityAliasType::Disabled: + case PCU::EntityAliasType::Disable: // No need to do anything as the alias is disabled. - return ResultPair(nullptr, AzFramework::Spawnable::EntityAliasType::Disabled); + return ResultPair(nullptr, AzFramework::Spawnable::EntityAliasType::Disable); case PCU::EntityAliasType::OptionalReplace: return ResultPair(CloneEntity(entity, source), AzFramework::Spawnable::EntityAliasType::Replace); case PCU::EntityAliasType::Replace: @@ -129,7 +129,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils default: AZ_Assert( false, "Invalid PrefabProcessorContext::EntityAliasType type (%i) provided.", aznumeric_cast(aliasType)); - return ResultPair(nullptr, AzFramework::Spawnable::EntityAliasType::Disabled); + return ResultPair(nullptr, AzFramework::Spawnable::EntityAliasType::Disable); } } } From 2ae927c754fcf3b44b501e69fcd24e604bdce47a Mon Sep 17 00:00:00 2001 From: nggieber Date: Mon, 1 Nov 2021 14:47:27 -0700 Subject: [PATCH 020/177] Fix minor indent issue Signed-off-by: nggieber --- Code/Tools/ProjectManager/Source/TagWidget.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Tools/ProjectManager/Source/TagWidget.h b/Code/Tools/ProjectManager/Source/TagWidget.h index 4cda01b347..7b4a5b1aaa 100644 --- a/Code/Tools/ProjectManager/Source/TagWidget.h +++ b/Code/Tools/ProjectManager/Source/TagWidget.h @@ -26,7 +26,7 @@ namespace O3DE::ProjectManager explicit TagWidget(const QString& text, QWidget* parent = nullptr); ~TagWidget() = default; - signals: + signals: void TagClicked(const QString& tag); protected: From 08c51aaf276145eb53f86345fa930ab24a5db984 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Tue, 26 Oct 2021 11:56:11 -0700 Subject: [PATCH 021/177] [Linux] Terminate AssetProcessor when spawned by the parent project process This adds support for the `ap_tether_lifetime` cvar in Linux. It extends the solution implemented in #2799 to add the same support on Linux. Signed-off-by: Chris Burel --- .../AssetSystemComponentHelper_Linux.cpp | 113 ++++++++++++------ 1 file changed, 74 insertions(+), 39 deletions(-) diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp index dcdc4a5925..6cb474f4ce 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp @@ -9,19 +9,71 @@ #include #include #include +#include #include #include -#include +#include #include #include +#include #include +AZ_CVAR(bool, ap_tether_lifetime, true, nullptr, AZ::ConsoleFunctorFlags::Null, + "If enabled, a parent process that launches the AP will terminate the AP on exit"); + namespace AzFramework::AssetSystem::Platform { void AllowAssetProcessorToForeground() {} + [[noreturn]] static void LaunchAssetProcessorDirectly(const AZ::IO::FixedMaxPath& assetProcessorPath, AZStd::string_view engineRoot, AZStd::string_view projectPath) + { + AZStd::fixed_vector args { + assetProcessorPath.c_str(), + "--start-hidden", + }; + + // Add the engine path to the launch command if not empty + AZ::IO::FixedMaxPathString engineRootArg; + if (!engineRoot.empty()) + { + // No need to quote these paths, this code calls exec directly and + // does not go through shell string interpolation + engineRootArg = AZ::IO::FixedMaxPathString{"--engine-path="} + AZ::IO::FixedMaxPathString{engineRoot}; + args.push_back(engineRootArg.data()); + } + + // Add the active project path to the launch command if not empty + AZ::IO::FixedMaxPathString projectPathArg; + if (!projectPath.empty()) + { + projectPathArg = AZ::IO::FixedMaxPathString{"--regset=/Amazon/AzCore/Bootstrap/project_path="} + AZ::IO::FixedMaxPathString{projectPath}; + args.push_back(projectPathArg.data()); + } + + // Make sure this is at the end + args.push_back(nullptr); // argv itself needs to be null-terminated + + execv(args[0], const_cast(args.data())); + + // exec* family of functions only return on error + fprintf(stderr, "Asset Processor failed with error: %s\n", strerror(errno)); + _exit(1); + } + + static pid_t LaunchAssetProcessorDaemonized(const AZ::IO::FixedMaxPath& assetProcessorPath, AZStd::string_view engineRoot, AZStd::string_view projectPath) + { + // detach the child from parent + setsid(); + const pid_t secondChildPid = fork(); + if (secondChildPid == 0) + { + LaunchAssetProcessorDirectly(assetProcessorPath, engineRoot, projectPath); + } + return secondChildPid; + } + bool LaunchAssetProcessor(AZStd::string_view executableDirectory, AZStd::string_view engineRoot, AZStd::string_view projectPath) { @@ -40,7 +92,8 @@ namespace AzFramework::AssetSystem::Platform } } - pid_t firstChildPid = fork(); + const pid_t parentPid = getpid(); + const pid_t firstChildPid = fork(); if (firstChildPid == 0) { // redirect output to dev/null so it doesn't hijack an existing console window @@ -53,51 +106,33 @@ namespace AzFramework::AssetSystem::Platform AZ::IO::FileDescriptorRedirector stderrRedirect(STDERR_FILENO); stderrRedirect.RedirectTo(devNull, mode); - // detach the child from parent - setsid(); - pid_t secondChildPid = fork(); - if (secondChildPid == 0) + if (ap_tether_lifetime) { - AZStd::array args { - assetProcessorPath.c_str(), assetProcessorPath.c_str(), "--start-hidden", - static_cast(nullptr), static_cast(nullptr), static_cast(nullptr) - }; - int optionalArgPos = 3; - - // Add the engine path to the launch command if not empty - AZ::IO::FixedMaxPathString engineRootArg; - if (!engineRoot.empty()) + prctl(PR_SET_PDEATHSIG, SIGTERM); + if (getppid() != parentPid) { - engineRootArg = AZ::IO::FixedMaxPathString::format(R"(--engine-path="%.*s")", - aznumeric_cast(engineRoot.size()), engineRoot.data()); - args[optionalArgPos++] = engineRootArg.data(); + _exit(1); } + LaunchAssetProcessorDirectly(assetProcessorPath, engineRoot, projectPath); + } + else + { + const pid_t secondChildPid = LaunchAssetProcessorDaemonized(assetProcessorPath, engineRoot, projectPath); + stdoutRedirect.Reset(); + stderrRedirect.Reset(); - // Add the active project path to the launch command if not empty - AZ::IO::FixedMaxPathString projectPathArg; - if (!projectPath.empty()) - { - projectPathArg = AZ::IO::FixedMaxPathString::format(R"(--regset="/Amazon/AzCore/Bootstrap/project_path=%.*s")", - aznumeric_cast(projectPath.size()), projectPath.data()); - args[optionalArgPos++] = projectPathArg.data(); - } - - AZStd::apply(execl, args); - - // exec* family of functions only exit on error - AZ_Error("AssetSystemComponent", false, "Asset Processor failed with error: %s", strerror(errno)); - _exit(1); + // exit the transient child with proper return code + int ret = (secondChildPid < 0) ? 1 : 0; + _exit(ret); } - stdoutRedirect.Reset(); - stderrRedirect.Reset(); - - // exit the transient child with proper return code - int ret = (secondChildPid < 0) ? 1 : 0; - _exit(ret); } else if (firstChildPid > 0) { + if (ap_tether_lifetime) + { + return true; + } // wait for first child to exit to ensure the second child was started int status = 0; pid_t ret = waitpid(firstChildPid, &status, 0); @@ -106,4 +141,4 @@ namespace AzFramework::AssetSystem::Platform return false; } -} +} // namespace AzFramework::AssetSystem::Platform From 05c374768e01964802ee8d625c25762f08857d68 Mon Sep 17 00:00:00 2001 From: dmcdiar Date: Mon, 1 Nov 2021 20:13:00 -0700 Subject: [PATCH 022/177] Added missing passes to the ReflectionProbe baking pipeline Signed-off-by: dmcdiar --- .../Passes/EnvironmentCubeMapForwardMSAA.pass | 16 +- ...vironmentCubeMapForwardSubsurfaceMSAA.pass | 158 ++++++++++++++++ .../Passes/EnvironmentCubeMapPipeline.pass | 170 +++++++++++++++++- .../Assets/Passes/PassTemplates.azasset | 4 + 4 files changed, 339 insertions(+), 9 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardSubsurfaceMSAA.pass diff --git a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass index 877ae489c0..683346c291 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardMSAA.pass @@ -91,10 +91,10 @@ "LoadStoreAction": { "ClearValue": { "Value": [ - 0.4000000059604645, - 0.4000000059604645, - 0.4000000059604645, - {} + 0.0, + 0.0, + 0.0, + 0.0 ] }, "LoadAction": "Clear" @@ -107,10 +107,10 @@ "LoadStoreAction": { "ClearValue": { "Value": [ - 0.4000000059604645, - 0.4000000059604645, - 0.4000000059604645, - {} + 0.0, + 0.0, + 0.0, + 0.0 ] }, "LoadAction": "Clear" diff --git a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardSubsurfaceMSAA.pass b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardSubsurfaceMSAA.pass new file mode 100644 index 0000000000..f6f7dd1e2d --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapForwardSubsurfaceMSAA.pass @@ -0,0 +1,158 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "EnvironmentCubeMapForwardSubsurfaceMSAAPassTemplate", + "PassClass": "RasterPass", + "Slots": [ + // Inputs... + { + "Name": "BRDFTextureInput", + "ShaderInputName": "m_brdfMap", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "DirectionalLightShadowmap", + "ShaderInputName": "m_directionalLightShadowmap", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ImageViewDesc": { + "IsArray": 1 + } + }, + { + "Name": "ExponentialShadowmapDirectional", + "ShaderInputName": "m_directionalLightExponentialShadowmap", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ImageViewDesc": { + "IsArray": 1 + } + }, + { + "Name": "ProjectedShadowmap", + "ShaderInputName": "m_projectedShadowmaps", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ImageViewDesc": { + "IsArray": 1 + } + }, + { + "Name": "ExponentialShadowmapProjected", + "ShaderInputName": "m_projectedExponentialShadowmap", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ImageViewDesc": { + "IsArray": 1 + } + }, + { + "Name": "TileLightData", + "SlotType": "Input", + "ShaderInputName": "m_tileLightData", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "LightListRemapped", + "SlotType": "Input", + "ShaderInputName": "m_lightListRemapped", + "ScopeAttachmentUsage": "Shader" + }, + // Input/Outputs... + { + "Name": "DepthStencilInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "DepthStencil" + }, + { + "Name": "DiffuseOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" + }, + { + "Name": "SpecularOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" + }, + { + "Name": "AlbedoOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" + }, + { + "Name": "SpecularF0Output", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" + }, + { + "Name": "NormalOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" + }, + // Outputs... + { + "Name": "ScatterDistanceOutput", + "SlotType": "Output", + "ScopeAttachmentUsage": "RenderTarget", + "LoadStoreAction": { + "ClearValue": { + "Value": [ + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "LoadAction": "Clear" + } + } + ], + "ImageAttachments": [ + { + "Name": "BRDFTexture", + "Lifetime": "Imported", + "AssetRef": { + "FilePath": "Textures/BRDFTexture.attimage" + } + }, + { + "Name": "ScatterDistanceImage", + "SizeSource": { + "Source": { + "Pass": "Parent", + "Attachment": "Output" + } + }, + "MultisampleSource": { + "Pass": "This", + "Attachment": "DepthStencilInputOutput" + }, + "ImageDescriptor": { + "Format": "R11G11B10_FLOAT", + "SharedQueueMask": "Graphics" + } + } + ], + "Connections": [ + { + "LocalSlot": "BRDFTextureInput", + "AttachmentRef": { + "Pass": "This", + "Attachment": "BRDFTexture" + } + }, + { + "LocalSlot": "ScatterDistanceOutput", + "AttachmentRef": { + "Pass": "This", + "Attachment": "ScatterDistanceImage" + } + } + ] + } + } +} \ No newline at end of file diff --git a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapPipeline.pass index 70f1999d8c..3bd0401011 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapPipeline.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/EnvironmentCubeMapPipeline.pass @@ -211,6 +211,105 @@ } } }, + { + "Name": "ForwardSubsurfaceMSAAPass", + "TemplateName": "EnvironmentCubeMapForwardSubsurfaceMSAAPassTemplate", + "Connections": [ + { + "LocalSlot": "DirectionalLightShadowmap", + "AttachmentRef": { + "Pass": "CascadedShadowmapsPass", + "Attachment": "Shadowmap" + } + }, + { + "LocalSlot": "ExponentialShadowmapDirectional", + "AttachmentRef": { + "Pass": "EsmShadowmapsPassDirectional", + "Attachment": "EsmShadowmaps" + } + }, + { + "LocalSlot": "ProjectedShadowmap", + "AttachmentRef": { + "Pass": "ProjectedShadowmapsPass", + "Attachment": "Shadowmap" + } + }, + { + "LocalSlot": "ExponentialShadowmapProjected", + "AttachmentRef": { + "Pass": "EsmShadowmapsPassProjected", + "Attachment": "EsmShadowmaps" + } + }, + { + "LocalSlot": "TileLightData", + "AttachmentRef": { + "Pass": "LightCullingPass", + "Attachment": "TileLightData" + } + }, + { + "LocalSlot": "LightListRemapped", + "AttachmentRef": { + "Pass": "LightCullingPass", + "Attachment": "LightListRemapped" + } + }, + // Input/Outputs... + { + "LocalSlot": "DepthStencilInputOutput", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "DepthMSAA" + } + }, + { + "LocalSlot": "DiffuseOutput", + "AttachmentRef": { + "Pass": "ForwardMSAAPass", + "Attachment": "DiffuseOutput" + } + }, + { + "LocalSlot": "SpecularOutput", + "AttachmentRef": { + "Pass": "ForwardMSAAPass", + "Attachment": "SpecularOutput" + } + }, + { + "LocalSlot": "AlbedoOutput", + "AttachmentRef": { + "Pass": "ForwardMSAAPass", + "Attachment": "AlbedoOutput" + } + }, + { + "LocalSlot": "SpecularF0Output", + "AttachmentRef": { + "Pass": "ForwardMSAAPass", + "Attachment": "SpecularF0Output" + } + }, + { + "LocalSlot": "NormalOutput", + "AttachmentRef": { + "Pass": "ForwardMSAAPass", + "Attachment": "NormalOutput" + } + } + ], + "PassData": { + "$type": "RasterPassData", + "DrawListTag": "forwardWithSubsurfaceOutput", + "PipelineViewTag": "MainCamera", + "PassSrgShaderAsset": { + "FilePath": "Shaders/ForwardPassSrg.shader" + } + } + }, { "Name": "SkyBoxPass", "TemplateName": "EnvironmentCubeMapSkyBoxPassTemplate", @@ -325,6 +424,75 @@ } ] }, + { + "Name": "MSAAResolveScatterDistancePass", + "TemplateName": "MSAAResolveColorTemplate", + "Connections": [ + { + "LocalSlot": "Input", + "AttachmentRef": { + "Pass": "ForwardSubsurfaceMSAAPass", + "Attachment": "ScatterDistanceOutput" + } + } + ] + }, + { + "Name": "SubsurfaceScatteringPass", + "TemplateName": "SubsurfaceScatteringPassTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "InputDiffuse", + "AttachmentRef": { + "Pass": "MSAAResolveDiffusePass", + "Attachment": "Output" + } + }, + { + "LocalSlot": "InputLinearDepth", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "DepthLinear" + } + }, + { + "LocalSlot": "InputScatterDistance", + "AttachmentRef": { + "Pass": "MSAAResolveScatterDistancePass", + "Attachment": "Output" + } + } + ], + "PassData": { + "$type": "ComputePassData", + "ShaderAsset": { + "FilePath": "Shaders/PostProcessing/ScreenSpaceSubsurfaceScatteringCS.shader" + }, + "Make Fullscreen Pass": true, + "PipelineViewTag": "MainCamera" + } + }, + { + "Name": "Ssao", + "TemplateName": "SsaoParentTemplate", + "Connections": [ + { + "LocalSlot": "LinearDepth", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "DepthLinear" + } + }, + { + "LocalSlot": "Modulate", + "AttachmentRef": { + "Pass": "SubsurfaceScatteringPass", + "Attachment": "Output" + } + } + ] + }, { "Name": "DiffuseSpecularMergePass", "TemplateName": "DiffuseSpecularMergeTemplate", @@ -332,7 +500,7 @@ { "LocalSlot": "InputDiffuse", "AttachmentRef": { - "Pass": "MSAAResolveDiffusePass", + "Pass": "Ssao", "Attachment": "Output" } }, diff --git a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset index f2df085228..eba745fb3c 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset +++ b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset @@ -252,6 +252,10 @@ "Name": "EnvironmentCubeMapForwardMSAAPassTemplate", "Path": "Passes/EnvironmentCubeMapForwardMSAA.pass" }, + { + "Name": "EnvironmentCubeMapForwardSubsurfaceMSAAPassTemplate", + "Path": "Passes/EnvironmentCubeMapForwardSubsurfaceMSAA.pass" + }, { "Name": "EnvironmentCubeMapDepthMSAAPassTemplate", "Path": "Passes/EnvironmentCubeMapDepthMSAA.pass" From 3505abe435b43fe663301e64295f8f9fe4cc816c Mon Sep 17 00:00:00 2001 From: Andre Mitchell Date: Tue, 2 Nov 2021 11:17:55 -0400 Subject: [PATCH 023/177] Prevent GetNodesFromGraphNodeIDs() from adding null pointers to the list that it returns. Signed-off-by: Andre Mitchell --- Gems/GraphModel/Code/Source/Integration/GraphController.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Gems/GraphModel/Code/Source/Integration/GraphController.cpp b/Gems/GraphModel/Code/Source/Integration/GraphController.cpp index 86cfb42990..c20d093e4b 100644 --- a/Gems/GraphModel/Code/Source/Integration/GraphController.cpp +++ b/Gems/GraphModel/Code/Source/Integration/GraphController.cpp @@ -698,7 +698,10 @@ namespace GraphModelIntegration GraphModel::NodePtrList nodeList; for (auto nodeId : nodeIds) { - nodeList.push_back(m_elementMap.Find(nodeId)); + if (GraphModel::NodePtr nodePtr = m_elementMap.Find(nodeId)) + { + nodeList.push_back(nodePtr); + } } return nodeList; From 403e2ff1e3b73d0c900f5a04957ac42e2265cd63 Mon Sep 17 00:00:00 2001 From: bosnichd Date: Tue, 2 Nov 2021 10:31:50 -0600 Subject: [PATCH 024/177] Fix bug in LocalFileIO::ConvertToAliasBuffer when a resolved alias ends in a path separator. (#5136) * Fix bug in LocalFileIO::ConvertToAliasBuffer when a resolved alias ends in a path separator, in which case we do not want to consume it when replacing it with the alias. eg. If the @products@ alias resolves to "C:\" and we call ConvertToAliasBuffer with "C:\some_folder\some_file.txt", the current behaviour results in "@products@some_folder\some_file.txt", but it needs to be "@products@\some_folder\some_file.txt" Signed-off-by: bosnichd * Update based on review feedback. Signed-off-by: bosnichd --- Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp index c1b9c941bc..19bffaccbd 100644 --- a/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp +++ b/Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp @@ -635,6 +635,7 @@ namespace AZ size_t longestMatch = 0; size_t bufStringLength = inBuffer.size(); AZStd::string_view longestAlias; + AZStd::string_view longestResolvedAlias; for (const auto& [alias, resolvedAlias] : m_aliases) { @@ -653,6 +654,7 @@ namespace AZ { longestMatch = resolvedAlias.size(); longestAlias = alias; + longestResolvedAlias = resolvedAlias; } } } @@ -661,7 +663,10 @@ namespace AZ // rearrange the buffer to have // [alias][old path] size_t aliasSize = longestAlias.size(); - size_t charsToAbsorb = longestMatch; + // If the resolved alias ends in a path separator, do not consume it. + const bool resolvedAliasEndsInPathSeparator = (longestResolvedAlias.ends_with(AZ::IO::PosixPathSeparator) || + longestResolvedAlias.ends_with(AZ::IO::WindowsPathSeparator)); + const size_t charsToAbsorb = resolvedAliasEndsInPathSeparator ? longestMatch - 1 : longestMatch; size_t remainingData = bufStringLength - charsToAbsorb; size_t finalStringSize = aliasSize + remainingData; if (finalStringSize >= outBufferLength) From a9f7ab4aafe6c472de49a1d13b587c512f7599e4 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Tue, 2 Nov 2021 11:48:09 -0500 Subject: [PATCH 025/177] Fixed the return value of the ConvertToAbsolutePath function (#5195) Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/Utils/Utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp index e6bfd78806..12c6473905 100644 --- a/Code/Framework/AzCore/AzCore/Utils/Utils.cpp +++ b/Code/Framework/AzCore/AzCore/Utils/Utils.cpp @@ -59,7 +59,7 @@ namespace AZ::Utils { // Fix the size value of the fixed string by calculating the c-string length using char traits absolutePath.resize_no_construct(AZStd::char_traits::length(absolutePath.data())); - return srcPath; + return absolutePath; } return AZStd::nullopt; From 22b21acc975d08281e9853b05bcbc0acc3e9ddcc Mon Sep 17 00:00:00 2001 From: Mikhail Naumov <82239319+AMZN-mnaumov@users.noreply.github.com> Date: Tue, 2 Nov 2021 14:24:14 -0500 Subject: [PATCH 026/177] No longer can create camera in an empty level (#5189) Signed-off-by: Mikhail Naumov --- Code/Editor/EditorViewportWidget.cpp | 4 +++- .../Code/Source/CameraEditorSystemComponent.cpp | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp index c7814cc842..b16758a07e 100644 --- a/Code/Editor/EditorViewportWidget.cpp +++ b/Code/Editor/EditorViewportWidget.cpp @@ -1124,7 +1124,9 @@ void EditorViewportWidget::OnTitleMenu(QMenu* menu) action = menu->addAction(tr("Create camera entity from current view")); connect(action, &QAction::triggered, this, &EditorViewportWidget::OnMenuCreateCameraEntityFromCurrentView); - if (!gameEngine || !gameEngine->IsLevelLoaded()) + const auto prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); + if (!gameEngine || !gameEngine->IsLevelLoaded() || + (prefabEditorEntityOwnershipInterface && !prefabEditorEntityOwnershipInterface->IsRootPrefabAssigned())) { action->setEnabled(false); action->setToolTip(tr(AZ::ViewportHelpers::TextCantCreateCameraNoLevel)); diff --git a/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp b/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp index 00a147c17a..fe49d1737a 100644 --- a/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp +++ b/Gems/Camera/Code/Source/CameraEditorSystemComponent.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "ViewportCameraSelectorWindow.h" @@ -70,7 +71,20 @@ namespace Camera if (!(flags & AzToolsFramework::EditorEvents::eECMF_HIDE_ENTITY_CREATION)) { QAction* action = menu->addAction(QObject::tr("Create camera entity from view")); - QObject::connect(action, &QAction::triggered, [this]() { CreateCameraEntityFromViewport(); }); + const auto prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); + if (prefabEditorEntityOwnershipInterface && !prefabEditorEntityOwnershipInterface->IsRootPrefabAssigned()) + { + action->setEnabled(false); + } + else + { + QObject::connect( + action, &QAction::triggered, + [this]() + { + CreateCameraEntityFromViewport(); + }); + } } } From fab0326188e2e67c8c7481d430cc6e0ceefc0cbc Mon Sep 17 00:00:00 2001 From: Tommy Walton Date: Tue, 2 Nov 2021 13:42:11 -0700 Subject: [PATCH 027/177] Creating default seedList.seed files for Atom gems (#5147) Signed-off-by: Tommy Walton --- Gems/Atom/Bootstrap/Assets/seedList.seed | 13 + Gems/Atom/Feature/Common/Assets/seedList.seed | 317 ++++++++++++++++++ Gems/Atom/RPI/Assets/seedList.seed | 29 ++ .../AtomFont/Assets/seedList.seed | 13 + .../Assets/seedList.seed | 13 + .../CommonFeatures/Assets/seedList.seed | 45 +++ Gems/AtomTressFX/Assets/seedList.seed | 101 ++++++ Gems/LyShine/Assets/seedList.seed | 24 ++ 8 files changed, 555 insertions(+) create mode 100644 Gems/Atom/Bootstrap/Assets/seedList.seed create mode 100644 Gems/Atom/Feature/Common/Assets/seedList.seed create mode 100644 Gems/Atom/RPI/Assets/seedList.seed create mode 100644 Gems/AtomLyIntegration/AtomFont/Assets/seedList.seed create mode 100644 Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/seedList.seed create mode 100644 Gems/AtomLyIntegration/CommonFeatures/Assets/seedList.seed create mode 100644 Gems/AtomTressFX/Assets/seedList.seed diff --git a/Gems/Atom/Bootstrap/Assets/seedList.seed b/Gems/Atom/Bootstrap/Assets/seedList.seed new file mode 100644 index 0000000000..0f42b7790a --- /dev/null +++ b/Gems/Atom/Bootstrap/Assets/seedList.seed @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Gems/Atom/Feature/Common/Assets/seedList.seed b/Gems/Atom/Feature/Common/Assets/seedList.seed new file mode 100644 index 0000000000..9881686940 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/seedList.seed @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gems/Atom/RPI/Assets/seedList.seed b/Gems/Atom/RPI/Assets/seedList.seed new file mode 100644 index 0000000000..300092e6c3 --- /dev/null +++ b/Gems/Atom/RPI/Assets/seedList.seed @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gems/AtomLyIntegration/AtomFont/Assets/seedList.seed b/Gems/AtomLyIntegration/AtomFont/Assets/seedList.seed new file mode 100644 index 0000000000..f879f523d0 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomFont/Assets/seedList.seed @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/seedList.seed b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/seedList.seed new file mode 100644 index 0000000000..2e22bca486 --- /dev/null +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/Assets/seedList.seed @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Gems/AtomLyIntegration/CommonFeatures/Assets/seedList.seed b/Gems/AtomLyIntegration/CommonFeatures/Assets/seedList.seed new file mode 100644 index 0000000000..157172ad34 --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Assets/seedList.seed @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gems/AtomTressFX/Assets/seedList.seed b/Gems/AtomTressFX/Assets/seedList.seed new file mode 100644 index 0000000000..95389a753a --- /dev/null +++ b/Gems/AtomTressFX/Assets/seedList.seed @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gems/LyShine/Assets/seedList.seed b/Gems/LyShine/Assets/seedList.seed index 499469bd63..b19aa77191 100644 --- a/Gems/LyShine/Assets/seedList.seed +++ b/Gems/LyShine/Assets/seedList.seed @@ -16,6 +16,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + From f8aa265253e2550d953fc65726d5b0980ffc9fbc Mon Sep 17 00:00:00 2001 From: Tommy Walton Date: Tue, 2 Nov 2021 13:42:20 -0700 Subject: [PATCH 028/177] Modifying a copy to not overrun if the target is smaller than the size of the default value array (#5186) Signed-off-by: Tommy Walton --- .../Source/Integration/Components/SimpleLODComponent.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp index aa17058adf..fdc6426e4c 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp @@ -85,10 +85,13 @@ namespace EMotionFX if (numLODs != m_lodSampleRates.size()) { - // Generate the default LOD Sample Rate to 140, 60, 45, 25, 15, 10 + // Generate the default LOD Sample Rate to 140, 60, 45, 25, 15, 10, 10, 10, ... constexpr AZStd::array defaultSampleRate {140.0f, 60.0f, 45.0f, 25.0f, 15.0f, 10.0f}; - m_lodSampleRates.resize(numLODs); - AZStd::copy(begin(defaultSampleRate), end(defaultSampleRate), begin(m_lodSampleRates)); + m_lodSampleRates.resize(numLODs, 10.0f); + + // Do not copy more than what fits in defaultSampleRates or numLODs. + size_t copyCount = std::min(defaultSampleRate.size(), numLODs); + AZStd::copy(begin(defaultSampleRate), begin(defaultSampleRate) + copyCount, begin(m_lodSampleRates)); } } From 6763e2a3ac80a9162895d5dec1d2d051da97fed0 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Tue, 2 Nov 2021 16:21:26 -0500 Subject: [PATCH 029/177] Shaders changes require two or more change cycles before updating (#5142) * Shaders changes require two or more change cycles before updating This fixes the problem described in the title. Consolidated the responsibility to update the root shader variant asset into the Shader() class. It was unnecessarily spread across Shader(), ShaderVariant() and ShaderAsset(). In particular OnAssetReloaded now makes a temporary copy of the root ShaderVariantAsset and updates the ShaderAsset with such reference only when OnAssetReloaded() is called on behalf of the ShaderAsset. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../Code/Source/Decals/DecalTextureArray.cpp | 2 +- .../Include/Atom/RPI.Public/Shader/Shader.h | 18 ++--- .../Atom/RPI.Public/Shader/ShaderVariant.h | 4 - .../Atom/RPI.Reflect/Shader/ShaderAsset.h | 17 ++-- .../Source/RPI.Public/Material/Material.cpp | 4 +- .../Source/RPI.Public/Pass/PassLibrary.cpp | 2 +- .../Specific/ImageAttachmentPreviewPass.cpp | 2 +- .../Code/Source/RPI.Public/Shader/Shader.cpp | 78 ++++++++++++------- .../RPI.Public/Shader/ShaderVariant.cpp | 34 ++------ .../RPI.Reflect/Material/MaterialAsset.cpp | 2 +- .../Source/RPI.Reflect/Shader/ShaderAsset.cpp | 38 +-------- .../EditorDiffuseProbeGridComponent.cpp | 2 +- .../EditorReflectionProbeComponent.cpp | 2 +- 13 files changed, 80 insertions(+), 125 deletions(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.cpp index 87ac0a9679..36a59bd07f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.cpp @@ -78,7 +78,7 @@ namespace AZ AZ_Warning("DecalTextureArray", false, "Material property: %s does not have a valid asset Id", propertyName.GetCStr()); return {}; } - return { imageAsset.GetAs< AZ::RPI::StreamingImageAsset>(), AZ::Data::AssetLoadBehavior::PreLoad }; + return Data::static_pointer_cast(imageAsset); } static AZ::Data::Asset GetStreamingImageAsset(const AZ::Data::Asset materialAssetData, const AZ::Name& propertyName) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/Shader.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/Shader.h index e2568f3b61..edb1ac47d9 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/Shader.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/Shader.h @@ -52,9 +52,8 @@ namespace AZ */ class Shader final : public Data::InstanceData - , public Data::AssetBus::Handler + , public Data::AssetBus::MultiHandler , public ShaderVariantFinderNotificationBus::Handler - , public ShaderReloadNotificationBus::Handler { friend class ShaderSystem; public: @@ -165,15 +164,6 @@ namespace AZ void OnShaderVariantTreeAssetReady(Data::Asset /*shaderVariantTreeAsset*/, bool /*isError*/) override {}; void OnShaderVariantAssetReady(Data::Asset shaderVariantAsset, bool IsError) override; /////////////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////////////// - // ShaderReloadNotificationBus overrides... - void OnShaderAssetReinitialized(const Data::Asset& shaderAsset) override; - // Note we don't need OnShaderVariantReinitialized because the Shader class doesn't do anything with the data inside - // the ShaderVariant object. The only thing we might want to do is propagate the message upward, but that's unnecessary - // because the ShaderReloadNotificationBus uses the Shader's AssetId as the ID for all messages including those from the variants. - // And of course we don't need to handle OnShaderReinitialized because this *is* this Shader. - /////////////////////////////////////////////////////////////////// //! A strong reference to the shader asset. Data::Asset m_asset; @@ -206,6 +196,12 @@ namespace AZ //! PipelineLibrary file name char m_pipelineLibraryPath[AZ_MAX_PATH_LEN] = { 0 }; + + //! During OnAssetReloaded, the internal references to ShaderVariantAsset inside + //! ShaderAsset are not updated correctly. We store here a reference to the root ShaderVariantAsset + //! when it got reloaded, later when We get OnAssetReloaded for the ShaderAsset We update its internal + //! reference to the root variant asset. + Data::Asset m_reloadedRootShaderVariantAsset; }; } } diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariant.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariant.h index 7cfa2f91f5..0fb76e45c2 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariant.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Shader/ShaderVariant.h @@ -19,7 +19,6 @@ namespace AZ //! the RHI::PipelineStateType of the parent Shader instance. For shaders on the raster //! pipeline, the RHI::DrawFilterTag is also provided. class ShaderVariant final - : public Data::AssetBus::MultiHandler { friend class Shader; public: @@ -58,9 +57,6 @@ namespace AZ const Data::Asset& shaderVariantAsset, SupervariantIndex supervariantIndex); - // AssetBus overrides... - void OnAssetReloaded(Data::Asset asset) override; - //! A reference to the shader asset that this is a variant of. Data::Asset m_shaderAsset; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderAsset.h index 2c24d6052a..c5df9a4b51 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Shader/ShaderAsset.h @@ -53,12 +53,12 @@ namespace AZ class ShaderAsset final : public Data::AssetData , public ShaderVariantFinderNotificationBus::Handler - , public Data::AssetBus::Handler , public AssetInitBus::Handler { friend class ShaderAssetCreator; friend class ShaderAssetHandler; friend class ShaderAssetTester; + friend class Shader; public: AZ_RTTI(ShaderAsset, "{823395A3-D570-49F4-99A9-D820CD1DEF98}", Data::AssetData); static void Reflect(ReflectContext* context); @@ -212,22 +212,19 @@ namespace AZ return GetAttribute(shaderStage, attributeName, DefaultSupervariantIndex); } - private: - /////////////////////////////////////////////////////////////////// - /// AssetBus overrides - void OnAssetReloaded(Data::Asset asset) override; - void OnAssetReady(Data::Asset asset) override; - /////////////////////////////////////////////////////////////////// - - void ReinitializeRootShaderVariant(Data::Asset asset); - /////////////////////////////////////////////////////////////////// /// ShaderVariantFinderNotificationBus overrides void OnShaderVariantTreeAssetReady(Data::Asset shaderVariantTreeAsset, bool isError) override; void OnShaderVariantAssetReady(Data::Asset /*shaderVariantAsset*/, bool /*isError*/) override {}; /////////////////////////////////////////////////////////////////// + // Only Shader::OnAssetReloaded() should call this function, because it is pointless for an Asset to + // to refresh its own "serialized references" to other assets during OnAssetReloaded(). + // The problem is that OnAssetReloaded() doesn't do a good job at updating "serialized references" to other assets, + // So some other class must update the reference and that's why Shader() is the best class to do it. + void UpdateRootShaderVariantAsset(SupervariantIndex SupervariantIndex, Data::Asset newRootVariant); + //! A Supervariant represents a set of static shader compilation parameters. //! Those parameters can be predefined c-preprocessor macros or specific arguments //! for AZSLc. diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp index 050ae47749..63e55d379d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp @@ -234,7 +234,7 @@ namespace AZ { ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Material::OnAssetReloaded %s", this, asset.GetHint().c_str()); - Data::Asset newMaterialAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; + Data::Asset newMaterialAsset = Data::static_pointer_cast(asset); if (newMaterialAsset) { @@ -610,7 +610,7 @@ namespace AZ } } - if (Data::Asset streamingImageAsset = { imageAsset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }) + if (Data::Asset streamingImageAsset = Data::static_pointer_cast(imageAsset)) { Data::Instance image = StreamingImage::FindOrCreate(streamingImageAsset); if (!image) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp index 6a6f5f3ff9..87c6eee814 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp @@ -281,7 +281,7 @@ namespace AZ void PassLibrary::OnAssetReloaded(Data::Asset asset) { // Handle pass asset reload - Data::Asset passAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; + Data::Asset passAsset = Data::static_pointer_cast(asset); if (passAsset && passAsset->GetPassTemplate()) { LoadPassAsset(passAsset->GetPassTemplate()->m_name, passAsset, true); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp index 8f83e4efe1..9a4428a03a 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.cpp @@ -231,7 +231,7 @@ namespace AZ void ImageAttachmentPreviewPass::OnAssetReloaded(Data::Asset asset) { - Data::Asset shaderAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; + Data::Asset shaderAsset = Data::static_pointer_cast(asset); if (shaderAsset) { m_needsShaderLoad = true; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp index 51dd9c36d3..a2d91fefd6 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Shader.cpp @@ -15,6 +15,8 @@ #include #include +#include + namespace AZ { @@ -96,8 +98,7 @@ namespace AZ RHI::ResultCode Shader::Init(ShaderAsset& shaderAsset) { - Data::AssetBus::Handler::BusDisconnect(); - ShaderReloadNotificationBus::Handler::BusDisconnect(); + Data::AssetBus::MultiHandler::BusDisconnect(); ShaderVariantFinderNotificationBus::Handler::BusDisconnect(); RHI::RHISystemInterface* rhiSystem = RHI::RHISystemInterface::Get(); @@ -112,7 +113,8 @@ namespace AZ AZStd::unique_lock lock(m_variantCacheMutex); m_shaderVariants.clear(); } - m_rootVariant.Init(Data::Asset{&shaderAsset, AZ::Data::AssetLoadBehavior::PreLoad}, shaderAsset.GetRootVariant(m_supervariantIndex), m_supervariantIndex); + auto rootShaderVariantAsset = shaderAsset.GetRootVariant(m_supervariantIndex); + m_rootVariant.Init(m_asset, rootShaderVariantAsset, m_supervariantIndex); if (m_pipelineLibraryHandle.IsNull()) { @@ -146,8 +148,8 @@ namespace AZ } ShaderVariantFinderNotificationBus::Handler::BusConnect(m_asset.GetId()); - Data::AssetBus::Handler::BusConnect(m_asset.GetId()); - ShaderReloadNotificationBus::Handler::BusConnect(m_asset.GetId()); + Data::AssetBus::MultiHandler::BusConnect(rootShaderVariantAsset.GetId()); + Data::AssetBus::MultiHandler::BusConnect(m_asset.GetId()); return RHI::ResultCode::Success; } @@ -155,8 +157,7 @@ namespace AZ void Shader::Shutdown() { ShaderVariantFinderNotificationBus::Handler::BusDisconnect(); - Data::AssetBus::Handler::BusDisconnect(); - ShaderReloadNotificationBus::Handler::BusDisconnect(); + Data::AssetBus::MultiHandler::BusDisconnect(); if (m_pipelineLibraryHandle.IsValid()) { @@ -181,14 +182,52 @@ namespace AZ { ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Shader::OnAssetReloaded %s", this, asset.GetHint().c_str()); - if (asset->GetId() == m_asset->GetId()) + if (asset.GetAs()) { - Data::Asset newAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; - AZ_Assert(newAsset, "Reloaded ShaderAsset is null"); + m_reloadedRootShaderVariantAsset = Data::static_pointer_cast(asset); + if (m_asset->m_shaderAssetBuildTimestamp == m_reloadedRootShaderVariantAsset->GetBuildTimestamp()) + { + Init(*m_asset.Get()); + ShaderReloadNotificationBus::Event(asset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderReinitialized, *this); + } + return; + } - Init(*newAsset.Get()); + if (asset.GetAs()) + { + m_asset = Data::static_pointer_cast(asset); + if (!m_reloadedRootShaderVariantAsset.IsReady()) + { + // Do nothing, as We should not re-initilize until the root shader variant asset has been reloaded. + return; + } + AZ_Assert(m_asset->m_shaderAssetBuildTimestamp == m_reloadedRootShaderVariantAsset->GetBuildTimestamp(), + "shaderAsset timeStamp=%lld, but Root ShaderVariantAsset timeStamp=%lld", + m_asset->m_shaderAssetBuildTimestamp, m_reloadedRootShaderVariantAsset->GetBuildTimestamp()); + m_asset->UpdateRootShaderVariantAsset(m_supervariantIndex, m_reloadedRootShaderVariantAsset); + m_reloadedRootShaderVariantAsset = {}; // Clear the temporary reference. + + if (ShaderReloadDebugTracker::IsEnabled()) + { + auto makeTimeString = [](AZStd::sys_time_t timestamp, AZStd::sys_time_t now) + { + AZStd::sys_time_t elapsedMicroseconds = now - timestamp; + double elapsedSeconds = aznumeric_cast(elapsedMicroseconds / 1'000'000); + AZStd::string timeString = AZStd::string::format("%lld (%f seconds ago)", timestamp, elapsedSeconds); + return timeString; + }; + + AZStd::sys_time_t now = AZStd::GetTimeNowMicroSecond(); + + const auto shaderVariantAsset = m_asset->GetRootVariant(); + ShaderReloadDebugTracker::Printf("{%p}->Shader::OnAssetReloaded for shader '%s' [build time %s] found variant '%s' [build time %s]", this, + m_asset.GetHint().c_str(), makeTimeString(m_asset->m_shaderAssetBuildTimestamp, now).c_str(), + shaderVariantAsset.GetHint().c_str(), makeTimeString(shaderVariantAsset->GetBuildTimestamp(), now).c_str()); + } + Init(*m_asset.Get()); ShaderReloadNotificationBus::Event(asset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderReinitialized, *this); } + } /////////////////////////////////////////////////////////////////////// @@ -253,23 +292,6 @@ namespace AZ ShaderReloadNotificationBus::Event(m_asset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderVariantReinitialized, updatedVariant); } /////////////////////////////////////////////////////////////////// - - - /////////////////////////////////////////////////////////////////// - // ShaderReloadNotificationBus overrides... - void Shader::OnShaderAssetReinitialized(const Data::Asset& shaderAsset) - { - // When reloads occur, it's possible for old Asset objects to hang around and report reinitialization, - // so we can reduce unnecessary reinitialization in that case. - if (shaderAsset.Get() == m_asset.Get()) - { - ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->Shader::OnShaderAssetReinitialized %s", this, shaderAsset.GetHint().c_str()); - - Init(*m_asset.Get()); - ShaderReloadNotificationBus::Event(shaderAsset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderReinitialized, *this); - } - } - /////////////////////////////////////////////////////////////////// ConstPtr Shader::LoadPipelineLibrary() const { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderVariant.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderVariant.cpp index 7aa70de6f8..ce33a31fbb 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderVariant.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderVariant.cpp @@ -22,24 +22,20 @@ namespace AZ const Data::Asset& shaderAsset, const Data::Asset& shaderVariantAsset, SupervariantIndex supervariantIndex) - { + { + m_shaderAsset = shaderAsset; + m_shaderVariantAsset = shaderVariantAsset; + m_supervariantIndex = supervariantIndex; m_pipelineStateType = shaderAsset->GetPipelineStateType(); m_pipelineLayoutDescriptor = shaderAsset->GetPipelineLayoutDescriptor(supervariantIndex); - m_shaderVariantAsset = shaderVariantAsset; m_renderStates = &shaderAsset->GetRenderStates(supervariantIndex); - m_supervariantIndex = supervariantIndex; - Data::AssetBus::MultiHandler::BusDisconnect(); - Data::AssetBus::MultiHandler::BusConnect(shaderAsset.GetId()); - Data::AssetBus::MultiHandler::BusConnect(shaderVariantAsset.GetId()); - - m_shaderAsset = shaderAsset; return true; } ShaderVariant::~ShaderVariant() { - Data::AssetBus::MultiHandler::BusDisconnect(); + } void ShaderVariant::ConfigurePipelineState(RHI::PipelineStateDescriptor& descriptor) const @@ -82,25 +78,5 @@ namespace AZ } } - - void ShaderVariant::OnAssetReloaded(Data::Asset asset) - { - ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderVariant::OnAssetReloaded %s", this, asset.GetHint().c_str()); - - if (asset.GetAs()) - { - Data::Asset shaderVariantAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; - Init(m_shaderAsset, shaderVariantAsset, m_supervariantIndex); - ShaderReloadNotificationBus::Event(m_shaderAsset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderVariantReinitialized, *this); - } - - if (asset.GetAs()) - { - Data::Asset shaderAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; - Init(shaderAsset, m_shaderVariantAsset, m_supervariantIndex); - ShaderReloadNotificationBus::Event(m_shaderAsset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderVariantReinitialized, *this); - } - } - } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp index 36f4947e3d..ac90333842 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp @@ -237,7 +237,7 @@ namespace AZ void MaterialAsset::ReinitializeMaterialTypeAsset(Data::Asset asset) { - Data::Asset newMaterialTypeAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; + Data::Asset newMaterialTypeAsset = Data::static_pointer_cast(asset); if (newMaterialTypeAsset) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp index 84757d58ab..10b748aa06 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp @@ -108,7 +108,6 @@ namespace AZ ShaderAsset::~ShaderAsset() { - Data::AssetBus::Handler::BusDisconnect(); ShaderVariantFinderNotificationBus::Handler::BusDisconnect(); AssetInitBus::Handler::BusDisconnect(); } @@ -570,46 +569,16 @@ namespace AZ bool ShaderAsset::PostLoadInit() { - // Once the ShaderAsset is loaded, it is necessary to listen for changes in the Root Variant Asset. - Data::AssetBus::Handler::BusConnect(GetRootVariant().GetId()); ShaderVariantFinderNotificationBus::Handler::BusConnect(GetId()); - AssetInitBus::Handler::BusDisconnect(); - return true; } - - void ShaderAsset::ReinitializeRootShaderVariant(Data::Asset asset) - { - Data::Asset shaderVariantAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; - AZ_Assert(shaderVariantAsset->GetStableId() == RootShaderVariantStableId, "Was expecting to update the root variant"); - SupervariantIndex supervariantIndex = GetSupervariantIndexFromAssetId(asset.GetId()); - GetCurrentShaderApiData().m_supervariants[supervariantIndex.GetIndex()].m_rootShaderVariantAsset = asset; - ShaderReloadNotificationBus::Event(GetId(), &ShaderReloadNotificationBus::Events::OnShaderAssetReinitialized, Data::Asset{ this, AZ::Data::AssetLoadBehavior::PreLoad } ); - } - /////////////////////////////////////////////////////////////////////// - // AssetBus overrides... - void ShaderAsset::OnAssetReloaded(Data::Asset asset) + + void ShaderAsset::UpdateRootShaderVariantAsset(SupervariantIndex supervariantIndex, Data::Asset newRootVariant) { - ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderAsset::OnAssetReloaded %s", this, asset.GetHint().c_str()); - ReinitializeRootShaderVariant(asset); + GetCurrentShaderApiData().m_supervariants[supervariantIndex.GetIndex()].m_rootShaderVariantAsset = newRootVariant; } - void ShaderAsset::OnAssetReady(Data::Asset asset) - { - // We have to listen to OnAssetReady, OnAssetReloaded isn't enough, because of the following scenario: - // The user changes a .shader file, which causes the AP to rebuild the ShaderAsset and root ShaderVariantAsset. - // 1) Thread A creates the new ShaderAsset, loads it, and gets the old ShaderVariantAsset. - // 2) Thread B creates the new ShaderVariantAsset, loads it, and calls OnAssetReloaded. - // 3) Main thread calls ShaderAsset::PostLoadInit which connects to the AssetBus but it's too late to receive OnAssetReloaded, - // so it continues using the old ShaderVariantAsset instead of the new one. - // The OnAssetReady bus function is called automatically whenever a connection to AssetBus is made, so listening to this gives - // us the opportunity to assign the appropriate ShaderVariantAsset. - - ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderAsset::OnAssetReady %s", this, asset.GetHint().c_str()); - ReinitializeRootShaderVariant(asset); - } - /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// /// ShaderVariantFinderNotificationBus overrides @@ -628,7 +597,6 @@ namespace AZ m_shaderVariantTree = shaderVariantTreeAsset; } lock.unlock(); - ShaderReloadNotificationBus::Event(GetId(), &ShaderReloadNotificationBus::Events::OnShaderAssetReinitialized, Data::Asset{ this, AZ::Data::AssetLoadBehavior::PreLoad }); } /////////////////////////////////////////////////////////////////// diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp index c14510f195..f54d1f05e5 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/EditorDiffuseProbeGridComponent.cpp @@ -196,7 +196,7 @@ namespace AZ { // bake is complete, update configuration with the new baked texture asset AzToolsFramework::ScopedUndoBatch undoBatch("DiffuseProbeGrid Texture Bake"); - configurationAsset = { textureAsset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; + configurationAsset = textureAsset; SetDirty(); if (m_controller.m_configuration.m_bakedIrradianceTextureAsset.IsReady() && diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp index 99e99abf4a..ae5c930096 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ReflectionProbe/EditorReflectionProbeComponent.cpp @@ -178,7 +178,7 @@ namespace AZ if (notificationType == CubeMapAssetNotificationType::Ready) { // bake is complete, update configuration with the new baked cubemap asset - m_controller.m_configuration.m_bakedCubeMapAsset = { cubeMapAsset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; + m_controller.m_configuration.m_bakedCubeMapAsset = cubeMapAsset; // refresh the currently rendered cubemap m_controller.UpdateCubeMap(); From 0900f5075fa0a7bfecdc953b2a63f99de8f81d7f Mon Sep 17 00:00:00 2001 From: antonmic <56370189+antonmic@users.noreply.github.com> Date: Tue, 2 Nov 2021 14:40:18 -0700 Subject: [PATCH 030/177] Removed unused SsaoHalfRes pass Signed-off-by: antonmic <56370189+antonmic@users.noreply.github.com> --- .../Common/Assets/Passes/SsaoHalfRes.pass | 88 ------------------- .../atom_feature_common_asset_files.cmake | 1 - 2 files changed, 89 deletions(-) delete mode 100644 Gems/Atom/Feature/Common/Assets/Passes/SsaoHalfRes.pass diff --git a/Gems/Atom/Feature/Common/Assets/Passes/SsaoHalfRes.pass b/Gems/Atom/Feature/Common/Assets/Passes/SsaoHalfRes.pass deleted file mode 100644 index d34ae4161d..0000000000 --- a/Gems/Atom/Feature/Common/Assets/Passes/SsaoHalfRes.pass +++ /dev/null @@ -1,88 +0,0 @@ -{ - "Type": "JsonSerialization", - "Version": 1, - "ClassName": "PassAsset", - "ClassData": { - "PassTemplate": { - "Name": "SsaoHalfResTemplate", - "PassClass": "ParentPass", - "Slots": [ - { - "Name": "LinearDepth", - "SlotType": "Input", - "ScopeAttachmentUsage": "Shader" - }, - { - "Name": "Output", - "SlotType": "Output", - "ScopeAttachmentUsage": "Shader" - } - ], - "Connections": [ - { - "LocalSlot": "Output", - "AttachmentRef": { - "Pass": "Upsample", - "Attachment": "Output" - } - } - ], - "PassRequests": [ - { - "Name": "DepthDownsample", - "TemplateName": "DepthDownsampleTemplate", - "Connections": [ - { - "LocalSlot": "FullResDepth", - "AttachmentRef": { - "Pass": "Parent", - "Attachment": "LinearDepth" - } - } - ] - }, - { - "Name": "DownsampledSsao", - "TemplateName": "SsaoParentTemplate", - "Connections": [ - { - "LocalSlot": "LinearDepth", - "AttachmentRef": { - "Pass": "DepthDownsample", - "Attachment": "HalfResDepth" - } - } - ] - }, - { - "Name": "Upsample", - "TemplateName": "DepthUpsampleTemplate", - "Enabled": true, - "Connections": [ - { - "LocalSlot": "FullResDepth", - "AttachmentRef": { - "Pass": "Parent", - "Attachment": "LinearDepth" - } - }, - { - "LocalSlot": "HalfResDepth", - "AttachmentRef": { - "Pass": "DepthDownsample", - "Attachment": "HalfResDepth" - } - }, - { - "LocalSlot": "HalfResSource", - "AttachmentRef": { - "Pass": "DownsampledSsao", - "Attachment": "Output" - } - } - ] - } - ] - } - } -} diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index 94b711e86c..c4d198fef9 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -205,7 +205,6 @@ set(FILES Passes/SMAAEdgeDetection.pass Passes/SMAANeighborhoodBlending.pass Passes/SsaoCompute.pass - Passes/SsaoHalfRes.pass Passes/SsaoParent.pass Passes/SubsurfaceScattering.pass Passes/Taa.pass From c9f4600bf38c1d422453fa079a88c973fcbe2bc7 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Tue, 2 Nov 2021 15:13:18 -0700 Subject: [PATCH 031/177] Fixes SDK mix between monolithic and non-monolithic Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/LYWrappers.cmake | 5 +- cmake/Platform/Common/Install_common.cmake | 175 +++++++++++++-------- cmake/install/Findo3de.cmake.in | 10 +- 3 files changed, 119 insertions(+), 71 deletions(-) diff --git a/cmake/LYWrappers.cmake b/cmake/LYWrappers.cmake index fb3d420c26..0416d41ece 100644 --- a/cmake/LYWrappers.cmake +++ b/cmake/LYWrappers.cmake @@ -403,7 +403,7 @@ function(ly_target_link_libraries TARGET) message(FATAL_ERROR "You must provide a target") endif() - set_property(GLOBAL APPEND PROPERTY LY_DELAYED_LINK_${TARGET} ${ARGN}) + set_property(TARGET ${TARGET} APPEND PROPERTY LY_DELAYED_LINK ${ARGN}) set_property(GLOBAL APPEND PROPERTY LY_DELAYED_LINK_TARGETS ${TARGET}) # to walk them at the end endfunction() @@ -430,7 +430,7 @@ function(ly_delayed_target_link_libraries) get_property(delayed_targets GLOBAL PROPERTY LY_DELAYED_LINK_TARGETS) foreach(target ${delayed_targets}) - get_property(delayed_link GLOBAL PROPERTY LY_DELAYED_LINK_${target}) + get_property(delayed_link TARGET ${target} PROPERTY LY_DELAYED_LINK) if(delayed_link) cmake_parse_arguments(ly_delayed_target_link_libraries "" "" "${visibilities}" ${delayed_link}) @@ -458,7 +458,6 @@ function(ly_delayed_target_link_libraries) endforeach() endforeach() - set_property(GLOBAL PROPERTY LY_DELAYED_LINK_${target}) endif() diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 44cdeb1994..d5b1a45df6 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -157,16 +157,15 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar endif() # Includes need additional processing to add the install root - if(include_directories) - foreach(include ${include_directories}) - string(GENEX_STRIP ${include} include_genex_expr) - if(include_genex_expr STREQUAL include) # only for cases where there are no generation expressions - # Make the include path relative to the source dir where the target will be declared - cmake_path(RELATIVE_PATH include BASE_DIRECTORY ${absolute_target_source_dir} OUTPUT_VARIABLE target_include) - string(APPEND INCLUDE_DIRECTORIES_PLACEHOLDER "${PLACEHOLDER_INDENT}${target_include}\n") - endif() - endforeach() - endif() + foreach(include IN LISTS include_directories) + string(GENEX_STRIP ${include} include_genex_expr) + if(include_genex_expr STREQUAL include) # only for cases where there are no generation expressions + # Make the include path relative to the source dir where the target will be declared + cmake_path(RELATIVE_PATH include BASE_DIRECTORY ${absolute_target_source_dir} OUTPUT_VARIABLE target_include) + list(APPEND INCLUDE_DIRECTORIES_PLACEHOLDER "${PLACEHOLDER_INDENT}${target_include}") + endif() + endforeach() + list(JOIN INCLUDE_DIRECTORIES_PLACEHOLDER "\n" INCLUDE_DIRECTORIES_PLACEHOLDER) string(REPEAT " " 8 PLACEHOLDER_INDENT) get_target_property(RUNTIME_DEPENDENCIES_PLACEHOLDER ${TARGET_NAME} MANUALLY_ADDED_DEPENDENCIES) @@ -178,27 +177,27 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar endif() string(REPEAT " " 12 PLACEHOLDER_INDENT) - get_target_property(inteface_build_dependencies_props ${TARGET_NAME} INTERFACE_LINK_LIBRARIES) + get_property(interface_build_dependencies_props TARGET ${TARGET_NAME} PROPERTY LY_DELAYED_LINK) unset(INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER) - if(inteface_build_dependencies_props) - foreach(build_dependency ${inteface_build_dependencies_props}) + if(interface_build_dependencies_props) + cmake_parse_arguments(build_deps "" "" "PRIVATE;PUBLIC;INTERFACE" ${interface_build_dependencies_props}) + # Interface and public dependencies should always be exposed + set(build_deps_target ${build_deps_INTERFACE}) + if(build_deps_PUBLIC) + set(build_deps_target "${build_deps_target};${build_deps_PUBLIC}") + endif() + # Private dependencies should only be exposed if it is a static library, since in those cases, link + # dependencies are transfered to the downstream dependencies + if("${target_type}" STREQUAL "STATIC_LIBRARY") + set(build_deps_target "${build_deps_target};${build_deps_PRIVATE}") + endif() + foreach(build_dependency IN LISTS build_deps_target) # Skip wrapping produced when targets are not created in the same directory - if(NOT ${build_dependency} MATCHES "^::@") + if(build_dependency) list(APPEND INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${PLACEHOLDER_INDENT}${build_dependency}") endif() endforeach() endif() - # We also need to pass the private link libraries since we will use that to generate the runtime dependencies - get_target_property(private_build_dependencies_props ${TARGET_NAME} LINK_LIBRARIES) - if(private_build_dependencies_props) - foreach(build_dependency ${private_build_dependencies_props}) - # Skip wrapping produced when targets are not created in the same directory - if(NOT ${build_dependency} MATCHES "^::@") - list(APPEND INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "${PLACEHOLDER_INDENT}${build_dependency}") - endif() - endforeach() - endif() - list(REMOVE_DUPLICATES INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER) list(JOIN INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER "\n" INTERFACE_BUILD_DEPENDENCIES_PLACEHOLDER) string(REPEAT " " 8 PLACEHOLDER_INDENT) @@ -322,7 +321,7 @@ include(Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cm file(CONFIGURE OUTPUT "${target_install_source_dir}/Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake" CONTENT [[ @cmake_copyright_comment@ if(LY_MONOLITHIC_GAME) - include(Platform/${PAL_PLATFORM_NAME}/Monolithic/permutation.cmake) + include(Platform/${PAL_PLATFORM_NAME}/Monolithic/permutation.cmake OPTIONAL) else() include(Platform/${PAL_PLATFORM_NAME}/Default/permutation.cmake) endif() @@ -354,29 +353,6 @@ endif() endfunction() -#! ly_setup_o3de_install: orchestrates the installation of the different parts. This is the entry point from the root CMakeLists.txt -function(ly_setup_o3de_install) - - ly_setup_subdirectories() - ly_setup_cmake_install() - ly_setup_runtime_dependencies() - ly_setup_assets() - - # Misc - install(FILES - ${LY_ROOT_FOLDER}/pytest.ini - ${LY_ROOT_FOLDER}/LICENSE.txt - ${LY_ROOT_FOLDER}/README.md - DESTINATION . - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - ) - - if(COMMAND ly_post_install_steps) - ly_post_install_steps() - endif() - -endfunction() - #! ly_setup_cmake_install: install the "cmake" folder function(ly_setup_cmake_install) @@ -385,8 +361,10 @@ function(ly_setup_cmake_install) COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} PATTERN "__pycache__" EXCLUDE PATTERN "Findo3de.cmake" EXCLUDE + PATTERN "cmake/ConfigurationTypes.cmake" EXCLUDE REGEX "3rdParty/Platform\/.*\/BuiltInPackages_.*\.cmake" EXCLUDE ) + # Connect configuration types install(FILES "${LY_ROOT_FOLDER}/cmake/install/ConfigurationTypes.cmake" DESTINATION cmake @@ -446,6 +424,7 @@ function(ly_setup_cmake_install) list(APPEND additional_platform_files "${plat_files}") endforeach() endforeach() + install(FILES ${additional_find_files} DESTINATION cmake/3rdParty COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} @@ -455,40 +434,68 @@ function(ly_setup_cmake_install) COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - # Findo3de.cmake file: we generate a different Findo3de.camke file than the one we have in cmake. This one is going to expose all - # targets that are pre-built - unset(FIND_PACKAGES_PLACEHOLDER) - - # Add to the FIND_PACKAGES_PLACEHOLDER all directories in which ly_add_target were called in - get_property(all_subdirectories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES) - foreach(target_subdirectory IN LISTS all_subdirectories) - cmake_path(RELATIVE_PATH target_subdirectory BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE relative_target_subdirectory) - string(APPEND FIND_PACKAGES_PLACEHOLDER " add_subdirectory(${relative_target_subdirectory})\n") - endforeach() - + # Findo3de.cmake file: we generate a different Findo3de.cmake file than the one we have in the source dir. configure_file(${LY_ROOT_FOLDER}/cmake/install/Findo3de.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake" DESTINATION cmake COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) - # BuiltInPackage_.cmake: since associations could happen in any cmake file across the engine. We collect - # all the associations in ly_associate_package and then generate them into BuiltInPackages_.cmake. This - # will consolidate all associations in one file + unset(find_subdirectories) + # Add to find_subdirectories all directories in which ly_add_target were called in + get_property(all_subdirectories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES) + foreach(target_subdirectory IN LISTS all_subdirectories) + cmake_path(RELATIVE_PATH target_subdirectory BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE relative_target_subdirectory) + string(APPEND find_subdirectories "add_subdirectory(${relative_target_subdirectory})\n") + endforeach() + set(permutation_find_subdirectories ${CMAKE_CURRENT_BINARY_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/o3de_subdirectories_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + file(GENERATE OUTPUT ${permutation_find_subdirectories} + CONTENT +"# Generated by O3DE install\n +${find_subdirectories} +" + ) + install(FILES "${permutation_find_subdirectories}" + DESTINATION cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} + ) + + set(pal_builtin_file ${CMAKE_CURRENT_BINARY_DIR}/cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}/BuiltInPackages_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + file(GENERATE OUTPUT ${pal_builtin_file} + CONTENT +"# Generated by O3DE install\n +if(LY_MONOLITHIC_GAME) + include(cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}/Monolithic/BuiltInPackages_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) +else() + include(cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}/Default/BuiltInPackages_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) +endif() +" + ) + install(FILES "${pal_builtin_file}" + DESTINATION cmake/3rdParty/Platform/${PAL_PLATFORM_NAME} + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} + ) + + # ${LY_BUILD_PERMUTATION}/BuiltInPackage_.cmake: since associations could happen in any cmake file across the engine. We collect + # all the associations in ly_associate_package and then generate them into BuiltInPackages_.cmake. This will consolidate all + # associations in one file + # Associations are sensitive to platform and build permutation, so we make different files for each. get_property(all_package_names GLOBAL PROPERTY LY_PACKAGE_NAMES) + list(REMOVE_DUPLICATES all_package_names) set(builtinpackages "# Generated by O3DE install\n\n") foreach(package_name IN LISTS all_package_names) get_property(package_hash GLOBAL PROPERTY LY_PACKAGE_HASH_${package_name}) get_property(targets GLOBAL PROPERTY LY_PACKAGE_TARGETS_${package_name}) + list(REMOVE_DUPLICATES targets) string(APPEND builtinpackages "ly_associate_package(PACKAGE_NAME ${package_name} TARGETS ${targets} PACKAGE_HASH ${package_hash})\n") endforeach() - set(pal_builtin_file ${CMAKE_CURRENT_BINARY_DIR}/cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}/BuiltInPackages_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) - file(GENERATE OUTPUT ${pal_builtin_file} + set(permutation_builtin_file ${CMAKE_CURRENT_BINARY_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/BuiltInPackages_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + file(GENERATE OUTPUT ${permutation_builtin_file} CONTENT ${builtinpackages} ) - install(FILES "${pal_builtin_file}" - DESTINATION cmake/3rdParty/Platform/${PAL_PLATFORM_NAME} + install(FILES "${permutation_builtin_file}" + DESTINATION cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) @@ -632,6 +639,7 @@ function(ly_setup_assets) if (NOT gem_install_dest_dir) cmake_path(SET gem_install_dest_dir .) endif() + if(IS_DIRECTORY ${gem_absolute_path}) install(DIRECTORY "${gem_absolute_path}" DESTINATION ${gem_install_dest_dir} @@ -702,4 +710,37 @@ function(ly_setup_subdirectory_enable_gems absolute_target_source_dir output_scr string(APPEND enable_gems_calls ${enable_gems_command}) endforeach() set(${output_script} ${enable_gems_calls} PARENT_SCOPE) +endfunction() + +#! ly_setup_o3de_install: orchestrates the installation of the different parts. This is the entry point from the root CMakeLists.txt +function(ly_setup_o3de_install) + + ly_setup_subdirectories() + ly_setup_cmake_install() + ly_setup_runtime_dependencies() + ly_setup_assets() + + # Misc + install(FILES + ${LY_ROOT_FOLDER}/pytest.ini + ${LY_ROOT_FOLDER}/LICENSE.txt + ${LY_ROOT_FOLDER}/README.md + DESTINATION . + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} + ) + + # Inject other build directories + foreach(external_dir ${LY_INSTALL_EXTERNAL_BUILD_DIRS}) + install(CODE +"set(LY_CORE_COMPONENT_ALREADY_INCLUDED TRUE) +include(${external_dir}/cmake_install.cmake) +set(LY_CORE_COMPONENT_ALREADY_INCLUDED FALSE)" + ALL_COMPONENTS + ) + endforeach() + + if(COMMAND ly_post_install_steps) + ly_post_install_steps() + endif() + endfunction() \ No newline at end of file diff --git a/cmake/install/Findo3de.cmake.in b/cmake/install/Findo3de.cmake.in index e267b6b5c6..4239aa3b1e 100644 --- a/cmake/install/Findo3de.cmake.in +++ b/cmake/install/Findo3de.cmake.in @@ -12,7 +12,15 @@ include(FindPackageHandleStandardArgs) # This will be called from within the installed engine's CMakeLists.txt macro(ly_find_o3de_packages) -@FIND_PACKAGES_PLACEHOLDER@ + if(LY_MONOLITHIC_GAME) + set(monolithic_file "${LY_ROOT_FOLDER}/Platform/${PAL_PLATFORM_NAME}/Monolithic/o3de_subdirectories_${PAL_PLATFORM_NAME_LOWERCASE}.cmake") + if(NOT EXISTS ${monolithic_file}) + message(FATAL_ERROR "O3DE SDK was not generated to support monolithic builds") + endif() + include("${monolithic_file}") + else() + include("cmake/Platform/${PAL_PLATFORM_NAME}/Default/o3de_subdirectories_${PAL_PLATFORM_NAME_LOWERCASE}.cmake") + endif() find_package(LauncherGenerator) endmacro() From 4e2c28105c38df500d8f7693929b6c3fbcb78857 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 2 Nov 2021 15:32:51 -0700 Subject: [PATCH 032/177] LYN-7547 | Focus Mode - It is possible to create a child entity of a closed container (#5193) (#5220) * Disable drag&drop of entities on closed containers. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Do not show the Create Entity context menu when right clicking a closed prefab container. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Disable entity creation on closed containers, both via the Create Entity flow and drag/drop of assets. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Minor changes to modernize old code. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../SandboxIntegration.cpp | 26 +++++++++------ .../Prefab/PrefabPublicHandler.cpp | 12 ++++++- .../UI/Outliner/EntityOutlinerListModel.cpp | 33 ++++++++++++++++--- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp index 5b849dcbe7..82d9f9ede2 100644 --- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp +++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -642,6 +643,9 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con AzToolsFramework::EntityIdList selected; GetSelectedOrHighlightedEntities(selected); + bool prefabSystemEnabled = false; + AzFramework::ApplicationRequests::Bus::BroadcastResult(prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled); + QAction* action = nullptr; // when nothing is selected, entity is created at root level @@ -658,18 +662,20 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con // when a single entity is selected, entity is created as its child else if (selected.size() == 1) { - action = menu->addAction(QObject::tr("Create entity")); - QObject::connect( - action, &QAction::triggered, action, - [selected] - { - EBUS_EVENT(AzToolsFramework::EditorRequests::Bus, CreateNewEntityAsChild, selected.front()); - }); + auto containerEntityInterface = AZ::Interface::Get(); + if (!prefabSystemEnabled || (containerEntityInterface && containerEntityInterface->IsContainerOpen(selected.front()))) + { + action = menu->addAction(QObject::tr("Create entity")); + QObject::connect( + action, &QAction::triggered, action, + [selected] + { + AzToolsFramework::EditorRequestBus::Broadcast(&AzToolsFramework::EditorRequestBus::Handler::CreateNewEntityAsChild, selected.front()); + } + ); + } } - bool prefabSystemEnabled = false; - AzFramework::ApplicationRequests::Bus::BroadcastResult(prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled); - if (!prefabSystemEnabled) { menu->addSeparator(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 84fe476fb3..5ac9f772dc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -12,11 +12,12 @@ #include #include +#include #include #include #include -#include #include +#include #include #include #include @@ -565,6 +566,7 @@ namespace AzToolsFramework parentId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId); } + // If the parent entity isn't owned by a prefab instance, bail. InstanceOptionalReference owningInstanceOfParentEntity = GetOwnerInstanceByEntityId(parentId); if (!owningInstanceOfParentEntity) { @@ -572,6 +574,14 @@ namespace AzToolsFramework "Cannot add entity because the owning instance of parent entity with id '%llu' could not be found.", static_cast(parentId))); } + + // If the parent entity is a closed container, bail. + if (auto containerEntityInterface = AZ::Interface::Get(); !containerEntityInterface->IsContainerOpen(parentId)) + { + return AZ::Failure(AZStd::string::format( + "Cannot add entity because the parent entity (id '%llu') is a closed container entity.", + static_cast(parentId))); + } EntityAlias entityAlias = Instance::GenerateEntityAlias(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp index 434a1d8303..13ec27c1b8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -764,10 +765,21 @@ namespace AzToolsFramework return canHandleData; } - bool EntityOutlinerListModel::CanDropMimeDataAssets(const QMimeData* data, Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex& /*parent*/) const + bool EntityOutlinerListModel::CanDropMimeDataAssets( + const QMimeData* data, + [[maybe_unused]] Qt::DropAction action, + [[maybe_unused]] int row, + [[maybe_unused]] int column, + const QModelIndex& parent) const { - using namespace AzToolsFramework; - + // Disable dropping assets on closed container entities. + AZ::EntityId parentId = GetEntityFromIndex(parent); + if (auto containerEntityInterface = AZ::Interface::Get(); + !containerEntityInterface->IsContainerOpen(parentId)) + { + return false; + } + if (data->hasFormat(AssetBrowser::AssetBrowserEntry::GetMimeType())) { return DecodeAssetMimeData(data); @@ -788,8 +800,15 @@ namespace AzToolsFramework return false; } + // If the parent entity is a closed container, bail. + if (auto containerEntityInterface = AZ::Interface::Get(); + !containerEntityInterface->IsContainerOpen(assignParentId)) + { + return false; + } + // Source Files - if (sourceFiles.size() > 0) + if (!sourceFiles.empty()) { // Get position (center of viewport). If no viewport is available, (0,0,0) will be used. AZ::Vector3 viewportCenterPosition = AZ::Vector3::CreateZero(); @@ -973,6 +992,12 @@ namespace AzToolsFramework return false; } + // If the new parent is a closed container, bail. + if (auto containerEntityInterface = AZ::Interface::Get(); !containerEntityInterface->IsContainerOpen(newParentId)) + { + return false; + } + // Ignore entities not owned by the editor context. It is assumed that all entities belong // to the same context since multiple selection doesn't span across views. for (const AZ::EntityId& entityId : selectedEntityIds) From 3334f5eb91c8e973b02556ff833fc952536e8cdd Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Tue, 2 Nov 2021 16:36:02 -0700 Subject: [PATCH 033/177] Fix paths to BuildInPackages and o3de_subdirectories Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/Platform/Common/Install_common.cmake | 4 ++-- cmake/install/Findo3de.cmake.in | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index d5b1a45df6..f22f4c43c9 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -490,12 +490,12 @@ endif() string(APPEND builtinpackages "ly_associate_package(PACKAGE_NAME ${package_name} TARGETS ${targets} PACKAGE_HASH ${package_hash})\n") endforeach() - set(permutation_builtin_file ${CMAKE_CURRENT_BINARY_DIR}/cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/BuiltInPackages_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + set(permutation_builtin_file ${CMAKE_CURRENT_BINARY_DIR}/cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION}/BuiltInPackages_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) file(GENERATE OUTPUT ${permutation_builtin_file} CONTENT ${builtinpackages} ) install(FILES "${permutation_builtin_file}" - DESTINATION cmake/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} + DESTINATION cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}/${LY_BUILD_PERMUTATION} COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) diff --git a/cmake/install/Findo3de.cmake.in b/cmake/install/Findo3de.cmake.in index 4239aa3b1e..12daf9df95 100644 --- a/cmake/install/Findo3de.cmake.in +++ b/cmake/install/Findo3de.cmake.in @@ -13,7 +13,7 @@ include(FindPackageHandleStandardArgs) # This will be called from within the installed engine's CMakeLists.txt macro(ly_find_o3de_packages) if(LY_MONOLITHIC_GAME) - set(monolithic_file "${LY_ROOT_FOLDER}/Platform/${PAL_PLATFORM_NAME}/Monolithic/o3de_subdirectories_${PAL_PLATFORM_NAME_LOWERCASE}.cmake") + set(monolithic_file "${LY_ROOT_FOLDER}/cmake/Platform/${PAL_PLATFORM_NAME}/Monolithic/o3de_subdirectories_${PAL_PLATFORM_NAME_LOWERCASE}.cmake") if(NOT EXISTS ${monolithic_file}) message(FATAL_ERROR "O3DE SDK was not generated to support monolithic builds") endif() From a433b9e8dd6a8d74c3e09cec8a0298f7d9f9735b Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Tue, 2 Nov 2021 17:05:14 -0700 Subject: [PATCH 034/177] Making paths consistent (PR comment) Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- cmake/install/Findo3de.cmake.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/install/Findo3de.cmake.in b/cmake/install/Findo3de.cmake.in index 12daf9df95..c3db7f1ec6 100644 --- a/cmake/install/Findo3de.cmake.in +++ b/cmake/install/Findo3de.cmake.in @@ -19,7 +19,7 @@ macro(ly_find_o3de_packages) endif() include("${monolithic_file}") else() - include("cmake/Platform/${PAL_PLATFORM_NAME}/Default/o3de_subdirectories_${PAL_PLATFORM_NAME_LOWERCASE}.cmake") + include("${LY_ROOT_FOLDER}/cmake/Platform/${PAL_PLATFORM_NAME}/Default/o3de_subdirectories_${PAL_PLATFORM_NAME_LOWERCASE}.cmake") endif() find_package(LauncherGenerator) endmacro() From b56783dce0a24bc1b25da02e6b05d89b6bde9035 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Wed, 3 Nov 2021 11:28:42 -0500 Subject: [PATCH 035/177] Add quick iteration workflow to the custom python tool template. Signed-off-by: Chris Galvan --- .../Template/Editor/Scripts/${NameLower}_dialog.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Templates/PythonToolGem/Template/Editor/Scripts/${NameLower}_dialog.py b/Templates/PythonToolGem/Template/Editor/Scripts/${NameLower}_dialog.py index 19194ec97f..3a0e6c9a7b 100644 --- a/Templates/PythonToolGem/Template/Editor/Scripts/${NameLower}_dialog.py +++ b/Templates/PythonToolGem/Template/Editor/Scripts/${NameLower}_dialog.py @@ -33,3 +33,12 @@ class ${SanitizedCppName}Dialog(QDialog): self.mainLayout.addWidget(self.helpLabel, 0, Qt.AlignCenter) self.setLayout(self.mainLayout) + + +if __name__ == "__main__": + # Create a new instance of the tool if launched from the Python Scripts window, + # which allows for quick iteration without having to close/re-launch the Editor + test_dialog = ${SanitizedCppName}Dialog() + test_dialog.setWindowTitle("${SanitizedCppName}") + test_dialog.show() + test_dialog.adjustSize() From 618777f8d4168fab12d8c6d4f2158f04c215f2f5 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 3 Nov 2021 11:15:11 -0700 Subject: [PATCH 036/177] LYN-7536 | Focus Mode - Introduce shortcuts to open/close prefab editing (#5230) * Enable closing prefab by double-clicking it when it's in focus. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Enable double clicking on level prefab to close focus mode and return to editing the level. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Introduce API function to go up one level in the Prefab Focus Mode hierarchy. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Enable keyboard shortcuts to more easily navigate the prefab hierarchy. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Tie the "Open/Edit Prefab" action to the = key on top of +. This allows users with compact US keyboards to use either key, preventing them from having to press Shift and =. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Replace the behavior of the "Up one level" button in the breadcrumbs with the new function that serves the same purpose. Also show the - shortcut in the tooltip. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Fix FocusOnParentOfFocusedPrefab to require the entity context id (to conform with the other functions in the API that don't pass entityIds). Expand its usage to other functions that did the same operation manually. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Include fix for non-unity builds Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Prefab/PrefabFocusHandler.cpp | 39 ++++++ .../Prefab/PrefabFocusHandler.h | 1 + .../Prefab/PrefabFocusPublicInterface.h | 3 + .../UI/Prefab/LevelRootUiHandler.cpp | 13 ++ .../UI/Prefab/LevelRootUiHandler.h | 1 + .../UI/Prefab/PrefabIntegrationManager.cpp | 121 +++++++++++++++--- .../UI/Prefab/PrefabIntegrationManager.h | 8 ++ .../UI/Prefab/PrefabUiHandler.cpp | 25 ++-- .../UI/Prefab/PrefabUiHandler.h | 4 + .../Prefab/PrefabViewportFocusPathHandler.cpp | 7 +- 10 files changed, 195 insertions(+), 27 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 5ba7382831..8098727177 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -86,6 +86,45 @@ namespace AzToolsFramework::Prefab return AZ::Success(); } + PrefabFocusOperationResult PrefabFocusHandler::FocusOnParentOfFocusedPrefab( + [[maybe_unused]] AzFramework::EntityContextId entityContextId) + { + // If only one instance is in the hierarchy, this operation is invalid + size_t hierarchySize = m_instanceFocusHierarchy.size(); + if (hierarchySize <= 1) + { + return AZ::Failure( + AZStd::string("Prefab Focus Handler: Could not complete FocusOnParentOfFocusedPrefab operation while focusing on the root.")); + } + + // Retrieve parent of currently focused prefab. + InstanceOptionalReference parentInstance = m_instanceFocusHierarchy[hierarchySize - 2]; + + // Use container entity of parent Instance for focus operations. + AZ::EntityId entityId = parentInstance->get().GetContainerEntityId(); + + // Initialize Undo Batch object + ScopedUndoBatch undoBatch("Edit Prefab"); + + // Clear selection + { + const EntityIdList selectedEntities = EntityIdList{}; + auto selectionUndo = aznew SelectionCommand(selectedEntities, "Clear Selection"); + selectionUndo->SetParent(undoBatch.GetUndoBatch()); + ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities); + } + + // Edit Prefab + { + auto editUndo = aznew PrefabFocusUndo("Edit Prefab"); + editUndo->Capture(entityId); + editUndo->SetParent(undoBatch.GetUndoBatch()); + FocusOnPrefabInstanceOwningEntityId(entityId); + } + + return AZ::Success(); + } + PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index) { if (index < 0 || index >= m_instanceFocusHierarchy.size()) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index 9decaed1ec..75b9666389 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -50,6 +50,7 @@ namespace AzToolsFramework::Prefab // PrefabFocusPublicInterface overrides ... PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override; + PrefabFocusOperationResult FocusOnParentOfFocusedPrefab(AzFramework::EntityContextId entityContextId) override; PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override; AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const override; bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h index 5bd4c6b0f6..2fc9ef6b9a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h @@ -30,6 +30,9 @@ namespace AzToolsFramework::Prefab //! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on. virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0; + //! Set the focused prefab instance to the parent of the currently focused prefab instance. Supports undo/redo. + virtual PrefabFocusOperationResult FocusOnParentOfFocusedPrefab(AzFramework::EntityContextId entityContextId) = 0; + //! Set the focused prefab instance to the instance at position index of the current path. Supports undo/redo. //! @param index The index of the instance in the current path that we want the prefab system to focus on. virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp index bb6bdf0ebd..b34bbff298 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp @@ -8,6 +8,7 @@ #include +#include #include #include @@ -92,4 +93,16 @@ namespace AzToolsFramework painter->drawLine(rect.bottomLeft(), rect.bottomRight()); painter->restore(); } + + bool LevelRootUiHandler::OnEntityDoubleClick(AZ::EntityId entityId) const + { + if (auto prefabFocusPublicInterface = AZ::Interface::Get(); + !prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) + { + prefabFocusPublicInterface->FocusOnOwningPrefab(entityId); + } + + // Don't propagate event. + return true; + } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h index 1e485572b8..3f7f56670e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h @@ -33,6 +33,7 @@ namespace AzToolsFramework bool CanToggleLockVisibility(AZ::EntityId entityId) const override; bool CanRename(AZ::EntityId entityId) const override; void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + bool OnEntityDoubleClick(AZ::EntityId entityId) const override; private: Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index ae0d18b077..951b876347 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,8 @@ namespace AzToolsFramework { namespace Prefab { + AzFramework::EntityContextId PrefabIntegrationManager::s_editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + ContainerEntityInterface* PrefabIntegrationManager::s_containerEntityInterface = nullptr; EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr; PrefabFocusPublicInterface* PrefabIntegrationManager::s_prefabFocusPublicInterface = nullptr; @@ -136,6 +139,9 @@ namespace AzToolsFramework return; } + // Get EditorEntityContextId + EditorEntityContextRequestBus::BroadcastResult(s_editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); + // Initialize Editor functionality for the Prefab Focus Handler auto prefabFocusInterface = AZ::Interface::Get(); prefabFocusInterface->InitializeEditorInterfaces(); @@ -145,10 +151,14 @@ namespace AzToolsFramework PrefabInstanceContainerNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension); + + InitializeShortcuts(); } PrefabIntegrationManager::~PrefabIntegrationManager() { + UninitializeShortcuts(); + AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect(); AZ::Interface::Unregister(this); PrefabInstanceContainerNotificationBus::Handler::BusDisconnect(); @@ -161,6 +171,74 @@ namespace AzToolsFramework PrefabUserSettings::Reflect(context); } + void PrefabIntegrationManager::InitializeShortcuts() + { + // Open/Edit Prefab (+) + // We also support = to enable easier editing on compact US keyboards. + { + m_actions.emplace_back(AZStd::make_unique(nullptr)); + + m_actions.back()->setShortcuts({ QKeySequence(Qt::Key_Plus), QKeySequence(Qt::Key_Equal) }); + m_actions.back()->setText("Open/Edit Prefab"); + m_actions.back()->setStatusTip("Edit the prefab in focus mode."); + + QObject::connect( + m_actions.back().get(), &QAction::triggered, m_actions.back().get(), + [] + { + AzToolsFramework::EntityIdList selectedEntities; + AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult( + selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities); + + if (selectedEntities.size() != 1) + { + return; + } + + AZ::EntityId selectedEntity = selectedEntities[0]; + + if (!s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity)) + { + return; + } + + if (!s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity)) + { + ContextMenu_EditPrefab(selectedEntity); + } + }); + + EditorActionRequestBus::Broadcast( + &EditorActionRequests::AddActionViaBusCrc, AZ_CRC_CE("com.o3de.action.editortransform.prefabopen"), + m_actions.back().get()); + } + + // Close Prefab (-) + { + m_actions.emplace_back(AZStd::make_unique(nullptr)); + + m_actions.back()->setShortcuts({ QKeySequence(Qt::Key_Minus) }); + m_actions.back()->setText("Close Prefab"); + m_actions.back()->setStatusTip("Close focus mode for this prefab and move one level up."); + + QObject::connect( + m_actions.back().get(), &QAction::triggered, m_actions.back().get(), + [] + { + ContextMenu_ClosePrefab(); + }); + + EditorActionRequestBus::Broadcast( + &EditorActionRequests::AddActionViaBusCrc, AZ_CRC_CE("com.o3de.action.editortransform.prefabclose"), + m_actions.back().get()); + } + } + + void PrefabIntegrationManager::UninitializeShortcuts() + { + m_actions.clear(); + } + int PrefabIntegrationManager::GetMenuPosition() const { return aznumeric_cast(EditorContextMenuOrdering::MIDDLE); @@ -181,16 +259,13 @@ namespace AzToolsFramework AzFramework::ApplicationRequests::Bus::BroadcastResult( prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); - auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); - EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); - // Create Prefab { if (!selectedEntities.empty()) { // Hide if the only selected entity is the Focused Instance Container if (selectedEntities.size() > 1 || - selectedEntities[0] != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)) + selectedEntities[0] != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(s_editorEntityContextId)) { bool layerInSelection = false; @@ -254,17 +329,30 @@ namespace AzToolsFramework if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity)) { - // Edit Prefab if (!s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity)) { - QAction* editAction = menu->addAction(QObject::tr("Edit Prefab")); + // Edit Prefab + QAction* editAction = menu->addAction(QObject::tr("Open/Edit Prefab")); + editAction->setShortcut(QKeySequence(Qt::Key_Plus)); editAction->setToolTip(QObject::tr("Edit the prefab in focus mode.")); QObject::connect(editAction, &QAction::triggered, editAction, [selectedEntity] { ContextMenu_EditPrefab(selectedEntity); }); + } + else + { + // Close Prefab + QAction* closeAction = menu->addAction(QObject::tr("Close Prefab")); + closeAction->setShortcut(QKeySequence(Qt::Key_Minus)); + closeAction->setToolTip(QObject::tr("Close focus mode for this prefab and move one level up.")); - itemWasShown = true; + QObject::connect( + closeAction, &QAction::triggered, closeAction, + [] + { + ContextMenu_ClosePrefab(); + }); } // Save Prefab @@ -279,9 +367,9 @@ namespace AzToolsFramework QObject::connect(saveAction, &QAction::triggered, saveAction, [selectedEntity] { ContextMenu_SavePrefab(selectedEntity); }); - - itemWasShown = true; } + + itemWasShown = true; } } } @@ -295,7 +383,8 @@ namespace AzToolsFramework QObject::connect(deleteAction, &QAction::triggered, deleteAction, [] { ContextMenu_DeleteSelected(); }); if (selectedEntities.empty() || - (selectedEntities.size() == 1 && selectedEntities[0] == s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId))) + (selectedEntities.size() == 1 && + selectedEntities[0] == s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(s_editorEntityContextId))) { deleteAction->setDisabled(true); } @@ -306,7 +395,7 @@ namespace AzToolsFramework AZ::EntityId selectedEntityId = selectedEntities[0]; if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntityId) && - selectedEntityId != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)) + selectedEntityId != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(s_editorEntityContextId)) { QAction* detachPrefabAction = menu->addAction(QObject::tr("Detach Prefab...")); QObject::connect( @@ -343,12 +432,9 @@ namespace AzToolsFramework const AZStd::string prefabFilesPath = "@projectroot@/Prefabs"; // Remove focused instance container entity if it's part of the list - auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); - EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); - auto focusedContainerIter = AZStd::find( selectedEntities.begin(), selectedEntities.end(), - s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)); + s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(s_editorEntityContextId)); if (focusedContainerIter != selectedEntities.end()) { selectedEntities.erase(focusedContainerIter); @@ -500,6 +586,11 @@ namespace AzToolsFramework } } + void PrefabIntegrationManager::ContextMenu_ClosePrefab() + { + s_prefabFocusPublicInterface->FocusOnParentOfFocusedPrefab(s_editorEntityContextId); + } + void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity) { s_prefabFocusPublicInterface->FocusOnOwningPrefab(containerEntity); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h index e8c10c150a..a8d325c4cf 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h @@ -96,11 +96,16 @@ namespace AzToolsFramework static void ContextMenu_CreatePrefab(AzToolsFramework::EntityIdList selectedEntities); static void ContextMenu_InstantiatePrefab(); static void ContextMenu_InstantiateProceduralPrefab(); + static void ContextMenu_ClosePrefab(); static void ContextMenu_EditPrefab(AZ::EntityId containerEntity); static void ContextMenu_SavePrefab(AZ::EntityId containerEntity); static void ContextMenu_DeleteSelected(); static void ContextMenu_DetachPrefab(AZ::EntityId containerEntity); + // Shortcut setup handlers + void InitializeShortcuts(); + void UninitializeShortcuts(); + // Prompt and resolve dialogs static bool QueryUserForPrefabSaveLocation( const AZStd::string& suggestedName, const char* initialTargetDirectory, AZ::u32 prefabUserSettingsId, QWidget* activeWindow, @@ -140,7 +145,10 @@ namespace AzToolsFramework AZStd::unique_ptr ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference); void SavePrefabsInDialog(QDialog* unsavedPrefabsDialog); + AZStd::vector> m_actions; + static const AZStd::string s_prefabFileExtension; + static AzFramework::EntityContextId s_editorEntityContextId; static ContainerEntityInterface* s_containerEntityInterface; static EditorEntityUiInterface* s_editorEntityUiInterface; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp index 447f94fc15..8bd1b7db04 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp @@ -21,6 +21,8 @@ namespace AzToolsFramework { + AzFramework::EntityContextId PrefabUiHandler::s_editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + const QColor PrefabUiHandler::m_backgroundColor = QColor("#444444"); const QColor PrefabUiHandler::m_backgroundHoverColor = QColor("#5A5A5A"); const QColor PrefabUiHandler::m_backgroundSelectedColor = QColor("#656565"); @@ -47,6 +49,9 @@ namespace AzToolsFramework AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusPublicInterface on PrefabUiHandler construction."); return; } + + // Get EditorEntityContextId + EditorEntityContextRequestBus::BroadcastResult(s_editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); } QString PrefabUiHandler::GenerateItemInfoString(AZ::EntityId entityId) const @@ -425,19 +430,23 @@ namespace AzToolsFramework if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) { - auto editorEntityContextId = AzFramework::EntityContextId::CreateNull(); - EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId); - - // Go one level up. - int length = m_prefabFocusPublicInterface->GetPrefabFocusPathLength(editorEntityContextId); - m_prefabFocusPublicInterface->FocusOnPathIndex(editorEntityContextId, length - 2); + // Close this prefab and focus on the parent + m_prefabFocusPublicInterface->FocusOnParentOfFocusedPrefab(s_editorEntityContextId); } } bool PrefabUiHandler::OnEntityDoubleClick(AZ::EntityId entityId) const { - // Focus on this prefab - m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId); + if (!m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId)) + { + // Focus on this prefab + m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId); + } + else + { + // Close this prefab and focus on the parent + m_prefabFocusPublicInterface->FocusOnParentOfFocusedPrefab(s_editorEntityContextId); + } // Don't propagate event. return true; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h index 6c78afc5b7..3627449ab4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h @@ -10,6 +10,8 @@ #include +#include + namespace AzToolsFramework { @@ -49,6 +51,8 @@ namespace AzToolsFramework static QModelIndex GetLastVisibleChild(const QModelIndex& parent); static QModelIndex Internal_GetLastVisibleChild(const QAbstractItemModel* model, const QModelIndex& index); + static AzFramework::EntityContextId s_editorEntityContextId; + static constexpr int m_prefabCapsuleRadius = 6; static constexpr int m_prefabBorderThickness = 2; static const QColor m_backgroundColor; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp index 52cf3279a4..920e99665d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp @@ -59,12 +59,11 @@ namespace AzToolsFramework::Prefab connect(m_backButton, &QToolButton::clicked, this, [&]() { - if (int length = m_prefabFocusPublicInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1) - { - m_prefabFocusPublicInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2); - } + m_prefabFocusPublicInterface->FocusOnParentOfFocusedPrefab(m_editorEntityContextId); } ); + + m_backButton->setToolTip("Up one level (-)"); } void PrefabViewportFocusPathHandler::OnPrefabFocusChanged() From 27c7e715168973e5b7fc175f427844500afbb4b1 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Wed, 3 Nov 2021 12:22:26 -0700 Subject: [PATCH 037/177] WIP default gem sorting Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> --- .../Source/GemCatalog/GemCatalogScreen.cpp | 6 +++ .../Source/GemCatalog/GemModel.h | 50 +++++++++---------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index fb78736a3c..c76735d4f2 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -34,6 +34,9 @@ namespace O3DE::ProjectManager m_gemModel = new GemModel(this); m_proxModel = new GemSortFilterProxyModel(m_gemModel, this); + // default to sort by gem name + m_proxModel->setSortRole(GemModel::RoleName); + QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setMargin(0); vLayout->setSpacing(0); @@ -93,6 +96,8 @@ namespace O3DE::ProjectManager } m_proxModel->ResetFilters(); + m_proxModel->sort(/*column=*/0); + m_filterWidget = new GemFilterWidget(m_proxModel); m_filterWidgetLayout->addWidget(m_filterWidget); @@ -198,6 +203,7 @@ namespace O3DE::ProjectManager } m_gemModel->UpdateGemDependencies(); + m_proxModel->sort(/*column=*/0); } void GemCatalogScreen::OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index f4bc1ec502..9fac47d18e 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -26,6 +26,31 @@ namespace O3DE::ProjectManager explicit GemModel(QObject* parent = nullptr); QItemSelectionModel* GetSelectionModel() const; + enum UserRole + { + RoleName = Qt::UserRole, + RoleDisplayName, + RoleCreator, + RoleGemOrigin, + RolePlatforms, + RoleSummary, + RoleWasPreviouslyAdded, + RoleWasPreviouslyAddedDependency, + RoleIsAdded, + RoleIsAddedDependency, + RoleDirectoryLink, + RoleDocLink, + RoleDependingGems, + RoleVersion, + RoleLastUpdated, + RoleBinarySize, + RoleFeatures, + RoleTypes, + RolePath, + RoleRequirement, + RoleDownloadStatus + }; + void AddGem(const GemInfo& gemInfo); void Clear(); void UpdateGemDependencies(); @@ -88,31 +113,6 @@ namespace O3DE::ProjectManager void GetAllDependingGems(const QModelIndex& modelIndex, QSet& inOutGems); QStringList GetDependingGems(const QModelIndex& modelIndex); - enum UserRole - { - RoleName = Qt::UserRole, - RoleDisplayName, - RoleCreator, - RoleGemOrigin, - RolePlatforms, - RoleSummary, - RoleWasPreviouslyAdded, - RoleWasPreviouslyAddedDependency, - RoleIsAdded, - RoleIsAddedDependency, - RoleDirectoryLink, - RoleDocLink, - RoleDependingGems, - RoleVersion, - RoleLastUpdated, - RoleBinarySize, - RoleFeatures, - RoleTypes, - RolePath, - RoleRequirement, - RoleDownloadStatus - }; - QHash m_nameToIndexMap; QItemSelectionModel* m_selectionModel = nullptr; QHash> m_gemDependencyMap; From d8c2088d1dd2e9f3c9e2a70e3d2b60a95427f15f Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Tue, 2 Nov 2021 08:59:19 -0700 Subject: [PATCH 038/177] bugifx: resolve crash with project manager (#5151) - system allocator not configured in environment for AZQtComponents - WA_DeleteOnClose will destroy the toast dialog causing a crashing when ToastNotificationsView tries to access the pointer issue: https://github.com/o3de/o3de/issues/5129 Signed-off-by: Michael Pollind --- .../AzQtComponents/Components/ToastNotification.cpp | 1 - .../AzQtComponents/Components/ToastNotification.h | 3 +-- .../AzQtComponents/Components/ToastNotificationConfiguration.h | 1 - .../UI/Notifications/ToastNotificationsView.cpp | 2 +- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp index 8831bef89c..11fd1e60d8 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.cpp @@ -27,7 +27,6 @@ namespace AzQtComponents setProperty("HasNoWindowDecorations", true); setAttribute(Qt::WA_ShowWithoutActivating); - setAttribute(Qt::WA_DeleteOnClose); m_borderRadius = toastConfiguration.m_borderRadius; if (m_borderRadius > 0) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h index 4343f37df4..81b1cb4055 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotification.h @@ -31,7 +31,6 @@ namespace AzQtComponents { Q_OBJECT public: - AZ_CLASS_ALLOCATOR(ToastNotification, AZ::SystemAllocator, 0); ToastNotification(QWidget* parent, const ToastConfiguration& toastConfiguration); virtual ~ToastNotification(); @@ -73,7 +72,7 @@ namespace AzQtComponents AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING AZStd::chrono::milliseconds m_fadeDuration; - AZStd::unique_ptr m_ui; + QScopedPointer m_ui; AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING }; } // namespace AzQtComponents diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h index 5ace9d1be2..2db374640e 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/ToastNotificationConfiguration.h @@ -27,7 +27,6 @@ namespace AzQtComponents class AZ_QT_COMPONENTS_API ToastConfiguration { public: - AZ_CLASS_ALLOCATOR(ToastConfiguration, AZ::SystemAllocator, 0); ToastConfiguration(ToastType toastType, const QString& title, const QString& description); bool m_closeOnClick = true; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp index a88711a9ed..277d3fa3c5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Notifications/ToastNotificationsView.cpp @@ -129,7 +129,7 @@ namespace AzToolsFramework ToastId ToastNotificationsView::CreateToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration) { - AzQtComponents::ToastNotification* notification = aznew AzQtComponents::ToastNotification(parentWidget(), toastConfiguration); + AzQtComponents::ToastNotification* notification = new AzQtComponents::ToastNotification(this, toastConfiguration); ToastId toastId = AZ::Entity::MakeId(); m_notifications[toastId] = notification; From 861b29ffc7f6c829061e0c64f3cea975e57eef60 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Wed, 3 Nov 2021 13:45:40 -0700 Subject: [PATCH 039/177] Fix spelling change for member variable Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> --- .../ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 08669d39a0..8234b80c8e 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -92,7 +92,7 @@ namespace O3DE::ProjectManager FillModel(projectPath); m_proxyModel->ResetFilters(); - m_proxModel->sort(/*column=*/0); + m_proxyModel->sort(/*column=*/0); if (m_filterWidget) { @@ -150,6 +150,7 @@ namespace O3DE::ProjectManager { m_gemModel->AddGem(gemInfoResult.GetValue()); m_gemModel->UpdateGemDependencies(); + m_proxyModel->sort(/*column=*/0); } } } @@ -206,7 +207,7 @@ namespace O3DE::ProjectManager } m_gemModel->UpdateGemDependencies(); - m_proxModel->sort(/*column=*/0); + m_proxyModel->sort(/*column=*/0); } void GemCatalogScreen::OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies) From 94110834f38ebdb25783d95e22031c28e53cd519 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 3 Nov 2021 15:47:11 -0500 Subject: [PATCH 040/177] Fix material editor crash on shutdown if graph canvas gem is loaded Signed-off-by: Guthrie Adams --- .../Code/Source/Translation/TranslationBuilder.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gems/GraphCanvas/Code/Source/Translation/TranslationBuilder.cpp b/Gems/GraphCanvas/Code/Source/Translation/TranslationBuilder.cpp index bac58c3ba1..1b01f7885d 100644 --- a/Gems/GraphCanvas/Code/Source/Translation/TranslationBuilder.cpp +++ b/Gems/GraphCanvas/Code/Source/Translation/TranslationBuilder.cpp @@ -40,10 +40,14 @@ namespace GraphCanvas { AZ::Data::AssetManager::Instance().RegisterHandler(m_assetHandler.get(), assetType); } + + AssetBuilderSDK::AssetBuilderCommandBus::Handler::BusConnect(GetUUID()); } void TranslationAssetWorker::Deactivate() { + AssetBuilderSDK::AssetBuilderCommandBus::Handler::BusDisconnect(); + if (AZ::Data::AssetManager::Instance().GetHandler(AZ::Data::AssetType{ azrtti_typeid() })) { AZ::Data::AssetManager::Instance().UnregisterHandler(m_assetHandler.get()); From 5b734b9d4159c666a0a78d6d595c531e9f334367 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Wed, 3 Nov 2021 14:17:20 -0700 Subject: [PATCH 041/177] A number of fixes to timeout and disconnect handling Signed-off-by: kberg-amzn --- .../AzCore/EBus/ScheduledEventHandle.cpp | 2 +- .../AutoGen/CorePackets.AutoPackets.xml | 4 +- .../TcpTransport/TcpConnection.cpp | 11 +- .../AzNetworking/TcpTransport/TcpConnection.h | 13 +- .../TcpTransport/TcpConnection.inl | 10 -- .../TcpTransport/TcpNetworkInterface.cpp | 47 +------ .../TcpTransport/TcpNetworkInterface.h | 11 -- .../UdpTransport/UdpConnection.cpp | 7 +- .../AzNetworking/UdpTransport/UdpConnection.h | 14 +- .../UdpTransport/UdpNetworkInterface.cpp | 68 ++++----- .../UdpTransport/UdpNetworkInterface.h | 32 ++--- .../Source/MultiplayerSystemComponent.cpp | 1 - .../Code/Source/MultiplayerSystemComponent.h | 1 - .../NetworkEntityAuthorityTracker.cpp | 132 ++++++------------ .../NetworkEntityAuthorityTracker.h | 26 +--- .../NetworkEntity/NetworkEntityManager.cpp | 4 + 16 files changed, 104 insertions(+), 279 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/EBus/ScheduledEventHandle.cpp b/Code/Framework/AzCore/AzCore/EBus/ScheduledEventHandle.cpp index 37b4895b4a..08a5a7d4ad 100644 --- a/Code/Framework/AzCore/AzCore/EBus/ScheduledEventHandle.cpp +++ b/Code/Framework/AzCore/AzCore/EBus/ScheduledEventHandle.cpp @@ -50,7 +50,7 @@ namespace AZ } else { - AZLOG_WARN("ScheduledEventHandle event pointer doesn't match to the pointer of handle to the event."); + //AZLOG_WARN("ScheduledEventHandle event pointer doesn't match to the pointer of handle to the event."); } } return false; // Event has been deleted, so the handle class must be deleted after this function. diff --git a/Code/Framework/AzNetworking/AzNetworking/AutoGen/CorePackets.AutoPackets.xml b/Code/Framework/AzNetworking/AzNetworking/AutoGen/CorePackets.AutoPackets.xml index 8ce3e5ad86..ae025b67e3 100644 --- a/Code/Framework/AzNetworking/AzNetworking/AutoGen/CorePackets.AutoPackets.xml +++ b/Code/Framework/AzNetworking/AzNetworking/AutoGen/CorePackets.AutoPackets.xml @@ -13,7 +13,9 @@ - + + + diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp index 6d7358a425..7537232a27 100644 --- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp @@ -27,13 +27,11 @@ namespace AzNetworking ConnectionId connectionId, const IpAddress& remoteAddress, TcpNetworkInterface& networkInterface, - TcpSocket& socket, - TimeoutId timeoutId + TcpSocket& socket ) : IConnection(connectionId, remoteAddress) , m_networkInterface(networkInterface) , m_socket(socket.CloneAndTakeOwnership()) - , m_timeoutId(timeoutId) , m_state(m_socket->IsOpen() ? ConnectionState::Connecting : ConnectionState::Disconnected) , m_connectionRole(ConnectionRole::Acceptor) , m_registeredSocketFd(InvalidSocketFd) @@ -163,13 +161,6 @@ namespace AzNetworking break; } - TimeoutQueue::TimeoutItem* timeoutItem = m_networkInterface.m_connectionTimeoutQueue.RetrieveItem(GetTimeoutId()); - if (timeoutItem == nullptr) - { - return true; - } - timeoutItem->UpdateTimeoutTime(startTimeMs); - NetworkOutputSerializer serializer(buffer.GetBuffer(), static_cast(buffer.GetSize())); if (m_state == ConnectionState::Connecting) { diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h index b769aea086..3d74f3f336 100644 --- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h +++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h @@ -38,14 +38,12 @@ namespace AzNetworking //! @param remoteAddress IP address of the remote endpoint //! @param networkInterface TcpNetworkInterface that owns this connection instance //! @param socket TCP socket to take ownership of and use for sending and receiving data - //! @param timeoutId timeout identifier of this connection instance TcpConnection ( ConnectionId connectionId, const IpAddress& remoteAddress, TcpNetworkInterface& networkInterface, - TcpSocket& socket, - TimeoutId timeoutId + TcpSocket& socket ); //! Construct a new socket with optional encryption, used when initiating a new connection @@ -69,14 +67,6 @@ namespace AzNetworking //! @return the TcpSocket bound to this TcpConnection TcpSocket* GetTcpSocket() const; - //! Sets the timeout identifier for this TcpConnection. - //! @param timeoutId the timeout identifier to use for this TcpConnection - void SetTimeoutId(TimeoutId timeoutId); - - //! Returns the timeout identifier for this TcpConnection. - //! @return the timeout identifier for this TcpConnection - TimeoutId GetTimeoutId() const; - //! Returns true if this connection instance is in an open state, and is capable of actively sending and receiving packets. //! @return boolean true if this connection instance is in an open state bool IsOpen() const; @@ -142,7 +132,6 @@ namespace AzNetworking AZStd::unique_ptr m_socket; AZStd::unique_ptr m_compressor; - TimeoutId m_timeoutId; PacketId m_lastSentPacketId = InvalidPacketId; ConnectionState m_state = ConnectionState::Disconnected; ConnectionRole m_connectionRole = ConnectionRole::Connector; diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.inl b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.inl index ecd1e5e908..5b5f38774e 100644 --- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.inl +++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.inl @@ -15,16 +15,6 @@ namespace AzNetworking return m_socket.get(); } - inline void TcpConnection::SetTimeoutId(TimeoutId timeoutId) - { - m_timeoutId = timeoutId; - } - - inline TimeoutId TcpConnection::GetTimeoutId() const - { - return m_timeoutId; - } - inline bool TcpConnection::IsOpen() const { return m_socket->IsOpen(); diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.cpp b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.cpp index 1ccff7be50..0278856ce9 100644 --- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.cpp @@ -21,16 +21,11 @@ namespace AzNetworking static const bool net_TcpUseEncryption = false; #endif - AZ_CVAR(bool, net_TcpTimeoutConnections, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Boolean value on whether we should timeout Tcp connections"); - AZ_CVAR(AZ::TimeMs, net_TcpHeartbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Tcp connection heartbeat frequency"); - AZ_CVAR(AZ::TimeMs, net_TcpDefaultTimeoutMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Tcp connection"); - TcpNetworkInterface::TcpNetworkInterface(AZ::Name name, IConnectionListener& connectionListener, TrustZone trustZone, TcpListenThread& listenThread) : m_name(name) , m_trustZone(trustZone) , m_connectionListener(connectionListener) , m_listenThread(listenThread) - , m_timeoutMs(net_TcpDefaultTimeoutMs) { ; } @@ -98,8 +93,6 @@ namespace AzNetworking } AZLOG_INFO("Adding new socket %d", static_cast(tcpSocket->GetSocketFd())); - const TimeoutId newTimeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast(tcpSocket->GetSocketFd()), net_TcpHeartbeatTimeMs); - connection->SetTimeoutId(newTimeoutId); connection->SendReliablePacket(CorePackets::InitiateConnectionPacket()); m_connectionListener.OnConnect(connection.get()); m_connectionSet.AddConnection(AZStd::move(connection)); @@ -110,12 +103,6 @@ namespace AzNetworking { const AZ::TimeMs startTimeMs = AZ::GetElapsedTimeMs(); - // Time out any stale connections - { - ConnectionTimeoutFunctor functor(*this); - m_connectionTimeoutQueue.UpdateTimeouts(functor); - } - AcceptNewConnections(); auto readCallback = [this, startTimeMs](SocketFd socketFd) { HandleConnectionRecv(socketFd, startTimeMs); }; @@ -258,8 +245,7 @@ namespace AzNetworking return; } AZLOG(NET_TcpTraffic, "Adding new socket %d", static_cast(tcpSocket.GetSocketFd())); - const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast(tcpSocket.GetSocketFd()), m_timeoutMs); - AZStd::unique_ptr connection = AZStd::make_unique(connectionId, remoteAddress, *this, tcpSocket, timeoutId); + AZStd::unique_ptr connection = AZStd::make_unique(connectionId, remoteAddress, *this, tcpSocket); AZ_Assert(connection->GetConnectionRole() == ConnectionRole::Acceptor, "Invalid role for connection"); GetConnectionListener().OnConnect(connection.get()); m_connectionSet.AddConnection(AZStd::move(connection)); @@ -286,7 +272,6 @@ namespace AzNetworking m_pendingRemoves.resize_no_construct(0); } - TcpNetworkInterface::PendingConnection::PendingConnection(SocketFd socketFd, uint32_t remoteIpAddress, uint16_t remotePort, uint16_t listenPort) : m_socketFd(socketFd) , m_remoteIpAddress(remoteIpAddress) @@ -295,34 +280,4 @@ namespace AzNetworking { ; } - - TcpNetworkInterface::ConnectionTimeoutFunctor::ConnectionTimeoutFunctor(TcpNetworkInterface& networkInterface) - : m_networkInterface(networkInterface) - { - ; - } - - TimeoutResult TcpNetworkInterface::ConnectionTimeoutFunctor::HandleTimeout(TimeoutQueue::TimeoutItem& item) - { - const SocketFd socketFd = static_cast(item.m_userData); - TcpConnection* tcpConnection = m_networkInterface.m_connectionSet.GetConnection(socketFd); - - if (tcpConnection == nullptr) - { - // We've already deleted this connection - return TimeoutResult::Delete; - } - - if (tcpConnection->GetConnectionRole() == ConnectionRole::Connector) - { - tcpConnection->SendReliablePacket(CorePackets::HeartbeatPacket()); - } - else if (net_TcpTimeoutConnections && (m_networkInterface.GetTimeoutMs() > AZ::TimeMs{ 0 })) - { - tcpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local); - return TimeoutResult::Delete; - } - - return TimeoutResult::Refresh; - } } diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.h b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.h index d8f5d1b62b..8d45e847a8 100644 --- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.h +++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.h @@ -137,16 +137,6 @@ namespace AzNetworking AZ_DISABLE_COPY_MOVE(TcpNetworkInterface); - struct ConnectionTimeoutFunctor final - : public ITimeoutHandler - { - ConnectionTimeoutFunctor(TcpNetworkInterface& networkInterface); - TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) override; - private: - AZ_DISABLE_COPY_MOVE(ConnectionTimeoutFunctor); - TcpNetworkInterface& m_networkInterface; - }; - struct PendingRemove { SocketFd m_socketFd; @@ -162,7 +152,6 @@ namespace AzNetworking TcpSocketManager m_tcpSocketManager; AZ::ThreadSafeDeque m_pendingConnections; AZStd::vector m_pendingRemoves; - TimeoutQueue m_connectionTimeoutQueue; TcpListenThread& m_listenThread; friend class TcpConnection; // For access to private RequestDisconnect() method diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp index 7b451865c4..03d4e7bec9 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp @@ -79,7 +79,7 @@ namespace AzNetworking AZLOG(NET_Acks, "Unacked packet count exceeded, sending client heartbeat (curr %u : max %u)", m_unackedPacketCount, static_cast(net_UdpMaxUnackedPacketCount)); // This simply times out unreliable chunks that haven't completed within our timeout delay m_fragmentQueue.Update(); - SendUnreliablePacket(CorePackets::HeartbeatPacket()); + SendUnreliablePacket(CorePackets::HeartbeatPacket(false)); } } @@ -289,7 +289,10 @@ namespace AzNetworking { return PacketDispatchResult::Failure; } - // Do nothing, we've already processed our ack packets + if (packet.GetRequestResponse()) + { + SendUnreliablePacket(CorePackets::HeartbeatPacket(false)); + } return PacketDispatchResult::Success; } break; diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h index 199a5a8347..c728016239 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h @@ -136,19 +136,19 @@ namespace AzNetworking AZ_DISABLE_COPY_MOVE(UdpConnection); UdpNetworkInterface& m_networkInterface; - UdpPacketTracker m_packetTracker; - UdpReliableQueue m_reliableQueue; - UdpFragmentQueue m_fragmentQueue; - ConnectionState m_state = ConnectionState::Disconnected; - ConnectionRole m_connectionRole = ConnectionRole::Connector; - DtlsEndpoint m_dtlsEndpoint; + UdpPacketTracker m_packetTracker; + UdpReliableQueue m_reliableQueue; + UdpFragmentQueue m_fragmentQueue; + ConnectionState m_state = ConnectionState::Disconnected; + ConnectionRole m_connectionRole = ConnectionRole::Connector; + DtlsEndpoint m_dtlsEndpoint; AZ::TimeMs m_lastSentPacketMs; uint32_t m_unackedPacketCount = 0; uint32_t m_connectionMtu = MaxUdpTransmissionUnit; TimeoutId m_timeoutId; - uint32_t m_timeoutCounter = 0; + int32_t m_timeoutCounter = 0; }; } diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp index b0e64f93e3..67bb80a44c 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp @@ -31,7 +31,7 @@ namespace AzNetworking AZ_CVAR(bool, net_UdpTimeoutConnections, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Boolean value on whether we should timeout Udp connections"); AZ_CVAR(AZ::TimeMs, net_UdpPacketTimeSliceMs, AZ::TimeMs{ 8 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The number of milliseconds to allow for packet processing"); - AZ_CVAR(AZ::TimeMs, net_UdpHeartbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Udp connection heartbeat frequency"); + AZ_CVAR(int32_t, net_UdpUnackedHeartbeats, 3, nullptr, AZ::ConsoleFunctorFlags::Null, "The number of heartbeats to attempt to send to keep a connection alive before giving up"); AZ_CVAR(AZ::TimeMs, net_UdpDefaultTimeoutMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Udp connection"); AZ_CVAR(AZ::TimeMs, net_MinPacketTimeoutMs, AZ::TimeMs{ 200 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Minimum time to wait before timing out an unacked packet"); AZ_CVAR(int32_t, net_MaxTimeoutsPerFrame, 1000, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of packet timeouts to allow to process in a single frame"); @@ -139,7 +139,8 @@ namespace AzNetworking } const ConnectionId connectionId = m_connectionSet.GetNextConnectionId(); - const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast(connectionId), m_timeoutMs); + const AZ::TimeMs timeoutTimeMs = m_timeoutMs / static_cast(static_cast(net_UdpUnackedHeartbeats)); + const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast(connectionId), timeoutTimeMs); AZStd::unique_ptr connection = AZStd::make_unique(connectionId, remoteAddress, *this, ConnectionRole::Connector); UdpPacketEncodingBuffer dtlsData; @@ -277,6 +278,7 @@ namespace AzNetworking } timeoutItem->UpdateTimeoutTime(startTimeMs); + connection->m_timeoutCounter = 0; PacketDispatchResult handledPacket = PacketDispatchResult::Failure; if (header.GetPacketType() < aznumeric_cast(CorePackets::PacketType::MAX)) @@ -319,16 +321,10 @@ namespace AzNetworking const AZ::TimeMs receiveTimeMs = AZ::GetElapsedTimeMs() - startTimeMs; // Time out any stale client connections - { - ConnectionTimeoutFunctor functor(*this); - m_connectionTimeoutQueue.UpdateTimeouts(functor); - } + m_connectionTimeoutQueue.UpdateTimeouts([this](TimeoutQueue::TimeoutItem& item) { return HandleConnectionTimeout(item); }); // Time out any packets that haven't been acked within our timeout window - { - PacketTimeoutFunctor functor(*this); - m_packetTimeoutQueue.UpdateTimeouts(functor, static_cast(net_MaxTimeoutsPerFrame)); - } + m_packetTimeoutQueue.UpdateTimeouts([this](TimeoutQueue::TimeoutItem& item) { return HandlePacketTimeout(item); }, static_cast(net_MaxTimeoutsPerFrame)); // Delete any connections we've disconnected for (RemovedConnection& removedConnection : m_removedConnections) @@ -709,21 +705,14 @@ namespace AzNetworking { // Packets involved in handshake are InitiateConnection, ConnectionHandshake and FragmentedPackets of ConnectionHandshake return packetType == aznumeric_cast(CorePackets::PacketType::InitiateConnectionPacket) || - packetType == aznumeric_cast(CorePackets::PacketType::ConnectionHandshakePacket) || - (packetType == aznumeric_cast(CorePackets::PacketType::FragmentedPacket) && endpoint.IsConnecting()); + packetType == aznumeric_cast(CorePackets::PacketType::ConnectionHandshakePacket) || + (packetType == aznumeric_cast(CorePackets::PacketType::FragmentedPacket) && endpoint.IsConnecting()); } - - UdpNetworkInterface::ConnectionTimeoutFunctor::ConnectionTimeoutFunctor(UdpNetworkInterface& networkInterface) - : m_networkInterface(networkInterface) - { - ; - } - - TimeoutResult UdpNetworkInterface::ConnectionTimeoutFunctor::HandleTimeout(TimeoutQueue::TimeoutItem& item) + TimeoutResult UdpNetworkInterface::HandleConnectionTimeout(TimeoutQueue::TimeoutItem& item) { const ConnectionId connectionId = ConnectionId(aznumeric_cast(item.m_userData)); - UdpConnection* udpConnection = static_cast(m_networkInterface.m_connectionSet.GetConnection(connectionId)); + UdpConnection* udpConnection = static_cast(m_connectionSet.GetConnection(connectionId)); if (udpConnection == nullptr) { @@ -731,22 +720,23 @@ namespace AzNetworking return TimeoutResult::Delete; } - if (udpConnection->GetConnectionState() == ConnectionState::Connecting) + if ((udpConnection->GetConnectionState() == ConnectionState::Connecting) + && udpConnection->GetDtlsEndpoint().IsConnecting()) { - if (udpConnection->GetDtlsEndpoint().IsConnecting()) - { - // DTLS prefers we resend data lost over the wire with fresh SSL IDs so account for that here - UdpPacketEncodingBuffer dtlsData; - udpConnection->ProcessHandshakeData(dtlsData); - return TimeoutResult::Refresh; - } + // DTLS prefers we resend data lost over the wire with fresh SSL IDs so account for that here + UdpPacketEncodingBuffer dtlsData; + udpConnection->ProcessHandshakeData(dtlsData); + return TimeoutResult::Refresh; } - if (udpConnection->GetConnectionRole() == ConnectionRole::Connector) + if ((udpConnection->GetConnectionRole() == ConnectionRole::Connector) + && (udpConnection->m_timeoutCounter < net_UdpUnackedHeartbeats)) { - udpConnection->SendUnreliablePacket(CorePackets::HeartbeatPacket()); + // Set the request response flag to true since we want a response to keep the connection alive + udpConnection->SendUnreliablePacket(CorePackets::HeartbeatPacket(true)); + ++udpConnection->m_timeoutCounter; } - else if (net_UdpTimeoutConnections && (m_networkInterface.GetTimeoutMs() > AZ::TimeMs{ 0 })) + else if (net_UdpTimeoutConnections && (GetTimeoutMs() > AZ::TimeMs{ 0 })) { udpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local); return TimeoutResult::Delete; @@ -755,19 +745,13 @@ namespace AzNetworking return TimeoutResult::Refresh; } - UdpNetworkInterface::PacketTimeoutFunctor::PacketTimeoutFunctor(UdpNetworkInterface& networkInterface) - : m_networkInterface(networkInterface) - { - ; - } - - TimeoutResult UdpNetworkInterface::PacketTimeoutFunctor::HandleTimeout(TimeoutQueue::TimeoutItem& item) + TimeoutResult UdpNetworkInterface::HandlePacketTimeout(TimeoutQueue::TimeoutItem& item) { ConnectionId connectionId; PacketId packetId; ReliabilityType reliability; DecodeTimeoutId(item.m_userData, connectionId, packetId, reliability); - UdpConnection* connection = static_cast(m_networkInterface.m_connectionSet.GetConnection(connectionId)); + UdpConnection* connection = static_cast(m_connectionSet.GetConnection(connectionId)); if (connection == nullptr) { @@ -782,16 +766,14 @@ namespace AzNetworking case PacketTimeoutResult::Acked: // Packet was already acked, just discard this timeout entry return TimeoutResult::Delete; - case PacketTimeoutResult::Pending: // Packet timed out before we received any info about it's sequence from the remote endpoint // The connection latency may have increased, and our Rtt metrics may still be adjusting.. // Just throw it back into the timeout queue return TimeoutResult::Refresh; - case PacketTimeoutResult::Lost: // Packet timed out and was not acked, so we consider it lost - m_networkInterface.m_connectionListener.OnPacketLost(connection, packetId); + m_connectionListener.OnPacketLost(connection, packetId); break; } diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.h b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.h index 8f827c74c4..e6abeded0d 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.h +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.h @@ -149,34 +149,24 @@ namespace AzNetworking //! @param endpoint whether the disconnection was initiated locally or remotely void RequestDisconnect(UdpConnection* connection, DisconnectReason reason, TerminationEndpoint endpoint); - //! Internal helper to check if a packet's type is for connection handshake + //! Internal helper to check if a packet's type is for connection handshake. //! @param endpoint DTLS endpoint participating in the handshake //! @param packetType type of the packet //! @return if the packet is for handshake bool IsHandshakePacket(const DtlsEndpoint& endpoint, AzNetworking::PacketType packetType) const; + //! Internal helper to manage connection timeout behaviour. + //! @param item the timeout item corresponding to the timed out connection + //! @return whether to delete or persist the timeout item + TimeoutResult HandleConnectionTimeout(TimeoutQueue::TimeoutItem& item); + + //! Internal helper to manage packet timeout behaviour. + //! @param item the timeout item corresponding to the timed out packet + //! @return whether to delete or persist the timeout item + TimeoutResult HandlePacketTimeout(TimeoutQueue::TimeoutItem& item); + AZ_DISABLE_COPY_MOVE(UdpNetworkInterface); - struct ConnectionTimeoutFunctor final - : public ITimeoutHandler - { - ConnectionTimeoutFunctor(UdpNetworkInterface& networkInterface); - TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) override; - private: - AZ_DISABLE_COPY_MOVE(ConnectionTimeoutFunctor); - UdpNetworkInterface& m_networkInterface; - }; - - struct PacketTimeoutFunctor final - : public ITimeoutHandler - { - PacketTimeoutFunctor(UdpNetworkInterface& networkInterface); - TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) override; - private: - AZ_DISABLE_COPY_MOVE(PacketTimeoutFunctor); - UdpNetworkInterface& m_networkInterface; - }; - AZ::Name m_name; TrustZone m_trustZone; uint16_t m_port = 0; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index fdd7602f71..0bf5cea64b 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -1107,7 +1107,6 @@ namespace Multiplayer void MultiplayerSystemComponent::OnAutonomousEntityReplicatorCreated() { m_autonomousEntityReplicatorCreatedHandler.Disconnect(); - //m_networkEntityManager.GetNetworkEntityAuthorityTracker()->ResetTimeoutTime(AZ::TimeMs{ 2000 }); m_clientMigrationEndEvent.Signal(); } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 87d084d5bc..53707523bb 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -155,7 +155,6 @@ namespace Multiplayer AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session"); AzNetworking::INetworkInterface* m_networkInterface = nullptr; - AzNetworking::INetworkInterface* m_networkEditorInterface = nullptr; AZ::ConsoleCommandInvokedEvent::Handler m_consoleCommandHandler; AZ::ThreadSafeDeque m_cvarCommands; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp index b3f87ea9ab..7b6e8476e7 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -33,37 +34,21 @@ namespace Multiplayer AZLOG ( NET_AuthTracker, - "AuthTracker: Removing timeout for networkEntityId %llu from %s, new owner is %s", + "AuthTracker: Removing timeout for networkEntityId %llu, new owner is %s", aznumeric_cast(entityHandle.GetNetEntityId()), - timeoutData->second.m_previousOwner.GetString().c_str(), newOwner.GetString().c_str() ); m_timeoutDataMap.erase(timeoutData); ret = true; } - auto iter = m_entityAuthorityMap.find(entityHandle.GetNetEntityId()); - if (iter != m_entityAuthorityMap.end()) - { - AZLOG - ( - NET_AuthTracker, - "AuthTracker: Assigning networkEntityId %llu from %s to %s", - aznumeric_cast(entityHandle.GetNetEntityId()), - iter->second.back().GetString().c_str(), - newOwner.GetString().c_str() - ); - } - else - { - AZLOG - ( - NET_AuthTracker, - "AuthTracker: Assigning networkEntityId %llu to %s", - aznumeric_cast(entityHandle.GetNetEntityId()), - newOwner.GetString().c_str() - ); - } + AZLOG + ( + NET_AuthTracker, + "AuthTracker: Assigning networkEntityId %llu to %s", + aznumeric_cast(entityHandle.GetNetEntityId()), + newOwner.GetString().c_str() + ); m_entityAuthorityMap[entityHandle.GetNetEntityId()].push_back(newOwner); return ret; @@ -103,14 +88,41 @@ namespace Multiplayer { AZ_Assert ( - (m_timeoutDataMap.find(entityHandle.GetNetEntityId()) == m_timeoutDataMap.end()) || - (m_timeoutDataMap[entityHandle.GetNetEntityId()].m_previousOwner == previousOwner), + m_timeoutDataMap.find(entityHandle.GetNetEntityId()) == m_timeoutDataMap.end(), "Trying to add something twice to the timeout map, this is unexpected" ); - m_timeoutQueue.RegisterItem(aznumeric_cast(entityHandle.GetNetEntityId()), net_EntityMigrationTimeoutMs); - TimeoutData& timeoutData = m_timeoutDataMap[entityHandle.GetNetEntityId()]; - timeoutData.m_entityHandle = entityHandle; - timeoutData.m_previousOwner = previousOwner; + m_timeoutDataMap.insert(entityHandle.GetNetEntityId()); + AZ::Interface::Get()->AddCallback([this, netEntityId = entityHandle.GetNetEntityId(), previousOwner] + { + auto timeoutData = m_timeoutDataMap.find(netEntityId); + if (timeoutData != m_timeoutDataMap.end()) + { + m_timeoutDataMap.erase(timeoutData); + ConstNetworkEntityHandle entityHandle = m_networkEntityManager.GetEntity(netEntityId); + if (auto entity = entityHandle.GetEntity()) + { + NetEntityRole networkRole = NetEntityRole::InvalidRole; + NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent(); + if (netBindComponent != nullptr) + { + networkRole = netBindComponent->GetNetEntityRole(); + } + if (networkRole != NetEntityRole::Authority) + { + AZLOG_ERROR + ( + "Timed out entity id %llu during migration previous owner %s, removing it", + aznumeric_cast(entityHandle.GetNetEntityId()), + previousOwner.GetString().c_str() + ); + m_networkEntityManager.MarkForRemoval(entityHandle); + } + } + } + }, + AZ::Name("Entity authority removal functor"), + net_EntityMigrationTimeoutMs + ); } else { @@ -127,18 +139,6 @@ namespace Multiplayer } HostId NetworkEntityAuthorityTracker::GetEntityAuthorityManager(ConstNetworkEntityHandle entityHandle) const - { - HostId hostId = GetEntityAuthorityManagerInternal(entityHandle); - AZ_Assert(hostId != InvalidHostId, "Unable to determine manager for entity"); - return hostId; - } - - bool NetworkEntityAuthorityTracker::DoesEntityHaveOwner(ConstNetworkEntityHandle entityHandle) const - { - return InvalidHostId != GetEntityAuthorityManagerInternal(entityHandle); - } - - HostId NetworkEntityAuthorityTracker::GetEntityAuthorityManagerInternal(ConstNetworkEntityHandle entityHandle) const { if (auto localEnt = entityHandle.GetEntity()) { @@ -167,52 +167,8 @@ namespace Multiplayer return InvalidHostId; } - NetworkEntityAuthorityTracker::TimeoutData::TimeoutData(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner) - : m_entityHandle(entityHandle) - , m_previousOwner(previousOwner) + bool NetworkEntityAuthorityTracker::DoesEntityHaveOwner(ConstNetworkEntityHandle entityHandle) const { - ; - } - - NetworkEntityAuthorityTracker::NetworkEntityTimeoutFunctor::NetworkEntityTimeoutFunctor - ( - NetworkEntityAuthorityTracker& networkEntityAuthorityTracker, - INetworkEntityManager& networkEntityManager - ) - : m_networkEntityAuthorityTracker(networkEntityAuthorityTracker) - , m_networkEntityManager(networkEntityManager) - { - ; - } - - AzNetworking::TimeoutResult NetworkEntityAuthorityTracker::NetworkEntityTimeoutFunctor::HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item) - { - const NetEntityId netEntityId = aznumeric_cast(item.m_userData); - auto timeoutData = m_networkEntityAuthorityTracker.m_timeoutDataMap.find(netEntityId); - if (timeoutData != m_networkEntityAuthorityTracker.m_timeoutDataMap.end()) - { - m_networkEntityAuthorityTracker.m_timeoutDataMap.erase(timeoutData); - ConstNetworkEntityHandle entityHandle = m_networkEntityManager.GetEntity(netEntityId); - if (auto entity = entityHandle.GetEntity()) - { - NetEntityRole networkRole = NetEntityRole::InvalidRole; - NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent(); - if (netBindComponent != nullptr) - { - networkRole = netBindComponent->GetNetEntityRole(); - } - if (networkRole != NetEntityRole::Authority) - { - AZLOG_ERROR - ( - "Timed out entity id %llu during migration previous owner %s, removing it", - aznumeric_cast(entityHandle.GetNetEntityId()), - timeoutData->second.m_previousOwner.GetString().c_str() - ); - m_networkEntityManager.MarkForRemoval(entityHandle); - } - } - } - return AzNetworking::TimeoutResult::Delete; + return InvalidHostId != GetEntityAuthorityManager(entityHandle); } } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h index 0f4ff5665a..c2e330ab4d 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h @@ -29,37 +29,13 @@ namespace Multiplayer HostId GetEntityAuthorityManager(ConstNetworkEntityHandle entityHandle) const; private: - - HostId GetEntityAuthorityManagerInternal(ConstNetworkEntityHandle entityHandle) const; - NetworkEntityAuthorityTracker& operator= (const NetworkEntityAuthorityTracker&) = delete; - struct TimeoutData final - { - TimeoutData() = default; - TimeoutData(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner); - ConstNetworkEntityHandle m_entityHandle; - HostId m_previousOwner = InvalidHostId; - }; - - struct NetworkEntityTimeoutFunctor final - : public AzNetworking::ITimeoutHandler - { - NetworkEntityTimeoutFunctor(NetworkEntityAuthorityTracker& networkEntityAuthorityTracker, INetworkEntityManager& m_networkEntityManager); - AzNetworking::TimeoutResult HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item) override; - private: - AZ_DISABLE_COPY_MOVE(NetworkEntityTimeoutFunctor); - NetworkEntityAuthorityTracker& m_networkEntityAuthorityTracker; - INetworkEntityManager& m_networkEntityManager; - }; - - using TimeoutDataMap = AZStd::unordered_map; + using TimeoutDataMap = AZStd::unordered_set; using EntityAuthorityMap = AZStd::unordered_map>; TimeoutDataMap m_timeoutDataMap; EntityAuthorityMap m_entityAuthorityMap; INetworkEntityManager& m_networkEntityManager; - AzNetworking::TimeoutQueue m_timeoutQueue; }; } - diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index c7582af83f..d973f0c80a 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -241,6 +241,10 @@ namespace Multiplayer { AZ::Entity* entity = it->second; NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity); + if (netBindComponent == nullptr) + { + continue; + } AZ::Aabb entityBounds = AZ::Interface::Get()->GetEntityWorldBoundsUnion(entity->GetId()); entityBounds.Expand(AZ::Vector3(0.01f)); if (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority) From fda7a6353e758eeef016ee2c75c130aff7bf1344 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Wed, 3 Nov 2021 14:20:56 -0700 Subject: [PATCH 042/177] Backing out some temporary debugging code Signed-off-by: kberg-amzn --- Code/Framework/AzCore/AzCore/EBus/ScheduledEventHandle.cpp | 2 +- .../Code/Source/NetworkEntity/NetworkEntityManager.cpp | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/EBus/ScheduledEventHandle.cpp b/Code/Framework/AzCore/AzCore/EBus/ScheduledEventHandle.cpp index 08a5a7d4ad..37b4895b4a 100644 --- a/Code/Framework/AzCore/AzCore/EBus/ScheduledEventHandle.cpp +++ b/Code/Framework/AzCore/AzCore/EBus/ScheduledEventHandle.cpp @@ -50,7 +50,7 @@ namespace AZ } else { - //AZLOG_WARN("ScheduledEventHandle event pointer doesn't match to the pointer of handle to the event."); + AZLOG_WARN("ScheduledEventHandle event pointer doesn't match to the pointer of handle to the event."); } } return false; // Event has been deleted, so the handle class must be deleted after this function. diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index d973f0c80a..c7582af83f 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -241,10 +241,6 @@ namespace Multiplayer { AZ::Entity* entity = it->second; NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity); - if (netBindComponent == nullptr) - { - continue; - } AZ::Aabb entityBounds = AZ::Interface::Get()->GetEntityWorldBoundsUnion(entity->GetId()); entityBounds.Expand(AZ::Vector3(0.01f)); if (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority) From 70a1eb65d81079b28d84e15d5ead7f53758c1801 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Wed, 3 Nov 2021 14:26:41 -0700 Subject: [PATCH 043/177] Improving comments around heartbeat sends + bumping number of heartbeats for increased keep-alive robustness under high packet loss Signed-off-by: kberg-amzn --- .../AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp | 2 ++ .../AzNetworking/UdpTransport/UdpNetworkInterface.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp index 03d4e7bec9..452992f971 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp @@ -79,6 +79,7 @@ namespace AzNetworking AZLOG(NET_Acks, "Unacked packet count exceeded, sending client heartbeat (curr %u : max %u)", m_unackedPacketCount, static_cast(net_UdpMaxUnackedPacketCount)); // This simply times out unreliable chunks that haven't completed within our timeout delay m_fragmentQueue.Update(); + // This heartbeat is sent to minimize the time the remote endpoint spends waiting for ack vector replication, we don't require a response SendUnreliablePacket(CorePackets::HeartbeatPacket(false)); } } @@ -291,6 +292,7 @@ namespace AzNetworking } if (packet.GetRequestResponse()) { + // We're replying to a heartbeat request, we don't want a response SendUnreliablePacket(CorePackets::HeartbeatPacket(false)); } return PacketDispatchResult::Success; diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp index 67bb80a44c..280b749d9e 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp @@ -31,7 +31,7 @@ namespace AzNetworking AZ_CVAR(bool, net_UdpTimeoutConnections, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Boolean value on whether we should timeout Udp connections"); AZ_CVAR(AZ::TimeMs, net_UdpPacketTimeSliceMs, AZ::TimeMs{ 8 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The number of milliseconds to allow for packet processing"); - AZ_CVAR(int32_t, net_UdpUnackedHeartbeats, 3, nullptr, AZ::ConsoleFunctorFlags::Null, "The number of heartbeats to attempt to send to keep a connection alive before giving up"); + AZ_CVAR(int32_t, net_UdpUnackedHeartbeats, 5, nullptr, AZ::ConsoleFunctorFlags::Null, "The number of heartbeats to attempt to send to keep a connection alive before giving up"); AZ_CVAR(AZ::TimeMs, net_UdpDefaultTimeoutMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Udp connection"); AZ_CVAR(AZ::TimeMs, net_MinPacketTimeoutMs, AZ::TimeMs{ 200 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Minimum time to wait before timing out an unacked packet"); AZ_CVAR(int32_t, net_MaxTimeoutsPerFrame, 1000, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of packet timeouts to allow to process in a single frame"); From d598a7c709b9114b35bbcfded63a5923fd2a7186 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Wed, 3 Nov 2021 16:54:37 -0700 Subject: [PATCH 044/177] [Linux] Update Jenkins Linux build to use Ubuntu 20, clang 12 (#5035) Signed-off-by: Chris Burel --- .../build/Platform/Linux/build_config.json | 24 +++++++++---------- scripts/build/Platform/Linux/pipeline.json | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index b76a950beb..84c5976215 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -37,7 +37,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -53,7 +53,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -66,7 +66,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -80,7 +80,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest --no-tests=error", @@ -93,7 +93,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest --no-tests=error", @@ -110,7 +110,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "AssetProcessorBatch", "ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch", @@ -124,7 +124,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "AssetProcessorBatch", "ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch", @@ -142,7 +142,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", "CTEST_OPTIONS": "-L (SUITE_periodic) --no-tests=error", @@ -162,7 +162,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", "CTEST_OPTIONS": "-L (SUITE_sandbox) --no-tests=error" @@ -178,7 +178,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_benchmark", "CTEST_OPTIONS": "-L (SUITE_benchmark) --no-tests=error", @@ -195,7 +195,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -210,7 +210,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/mono_linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } diff --git a/scripts/build/Platform/Linux/pipeline.json b/scripts/build/Platform/Linux/pipeline.json index d964a693ce..7f16ec6ab5 100644 --- a/scripts/build/Platform/Linux/pipeline.json +++ b/scripts/build/Platform/Linux/pipeline.json @@ -1,6 +1,6 @@ { "ENV": { - "NODE_LABEL": "linux", + "NODE_LABEL": "linux-707531fc7", "LY_3RDPARTY_PATH": "/home/lybuilder/ly/workspace/3rdParty", "TIMEOUT": 30, "WORKSPACE": "/data/workspace", @@ -17,4 +17,4 @@ "CLEAN_WORKSPACE": true } } -} \ No newline at end of file +} From ae1b6d6729f5d469bcfca77d8eea35b9ea2fafbc Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Tue, 26 Oct 2021 16:33:43 -0700 Subject: [PATCH 045/177] [Linux] Unify Editor and Game raw mouse event handling Signed-off-by: Chris Burel --- .../Editor/Core/QtEditorApplication_linux.cpp | 30 ++++- .../Editor/Core/QtEditorApplication_linux.h | 18 ++- .../Common/Xcb/AzFramework/XcbApplication.cpp | 27 ++++ .../Xcb/AzFramework/XcbConnectionManager.h | 3 + .../Common/Xcb/AzFramework/XcbEventHandler.h | 3 - .../Xcb/AzFramework/XcbInputDeviceMouse.cpp | 117 +++--------------- .../Xcb/AzFramework/XcbInputDeviceMouse.h | 10 -- 7 files changed, 86 insertions(+), 122 deletions(-) diff --git a/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp index ad5e57479b..8ba152a9f9 100644 --- a/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp +++ b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp @@ -10,6 +10,8 @@ #ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB #include +#include +#include #endif namespace Editor @@ -23,16 +25,34 @@ namespace Editor return nullptr; } + xcb_connection_t* EditorQtApplicationXcb::GetXcbConnectionFromQt() + { + QPlatformNativeInterface* native = platformNativeInterface(); + AZ_Warning("EditorQtApplicationXcb", native, "Unable to retrieve the native platform interface"); + if (!native) + { + return nullptr; + } + return reinterpret_cast(native->nativeResourceForIntegration(QByteArray("connection"))); + } + + void EditorQtApplicationXcb::OnStartPlayInEditor() + { + auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); + interface->SetEnableXInput(GetXcbConnectionFromQt(), true); + } + + void EditorQtApplicationXcb::OnStopPlayInEditor() + { + auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); + interface->SetEnableXInput(GetXcbConnectionFromQt(), false); + } + bool EditorQtApplicationXcb::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long*) { if (GetIEditor()->IsInGameMode()) { #ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - // We need to handle RAW Input events in a separate loop. This is a workaround to enable XInput2 RAW Inputs using Editor mode. - // TODO To have this call here might be not be perfect. - AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::PollSpecialEvents); - - // Now handle the rest of the events. AzFramework::XcbEventHandlerBus::Broadcast( &AzFramework::XcbEventHandler::HandleXcbEvent, static_cast(message)); #endif diff --git a/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h index 8c145c3aa7..109ae1742b 100644 --- a/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h +++ b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h @@ -6,19 +6,35 @@ * */ +#if !defined(Q_MOC_RUN) #include +#include +#endif + +using xcb_connection_t = struct xcb_connection_t; namespace Editor { - class EditorQtApplicationXcb : public EditorQtApplication + class EditorQtApplicationXcb + : public EditorQtApplication + , public AzToolsFramework::EditorEntityContextNotificationBus::Handler { Q_OBJECT public: EditorQtApplicationXcb(int& argc, char** argv) : EditorQtApplication(argc, argv) { + // Connect bus to listen for OnStart/StopPlayInEditor events + AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect(); } + xcb_connection_t* GetXcbConnectionFromQt(); + + /////////////////////////////////////////////////////////////////////// + // AzToolsFramework::EditorEntityContextNotificationBus overrides + void OnStartPlayInEditor() override; + void OnStopPlayInEditor() override; + // QAbstractNativeEventFilter: bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; }; diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp index f57f4a89ac..780e1e72fe 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp @@ -10,6 +10,8 @@ #include #include +#include + namespace AzFramework { //////////////////////////////////////////////////////////////////////////////////////////////// @@ -34,6 +36,31 @@ namespace AzFramework return m_xcbConnection.get(); } + void SetEnableXInput(xcb_connection_t* connection, bool enable) override + { + struct Mask + { + xcb_input_event_mask_t head; + xcb_input_xi_event_mask_t mask; + }; + const Mask mask { + /*.head=*/{ + /*.device_id=*/XCB_INPUT_DEVICE_ALL_MASTER, + /*.mask_len=*/1 + }, + /*.mask=*/ enable ? + (xcb_input_xi_event_mask_t)(XCB_INPUT_XI_EVENT_MASK_RAW_MOTION | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE) : + (xcb_input_xi_event_mask_t)XCB_NONE + }; + + const xcb_setup_t* xcbSetup = xcb_get_setup(connection); + const xcb_screen_t* xcbScreen = xcb_setup_roots_iterator(xcbSetup).data; + + xcb_input_xi_select_events(connection, xcbScreen->root, 1, &mask.head); + + xcb_flush(connection); + } + private: XcbUniquePtr m_xcbConnection = nullptr; }; diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h index daa5bf35af..ca7ce06e6c 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h @@ -24,6 +24,9 @@ namespace AzFramework virtual ~XcbConnectionManager() = default; virtual xcb_connection_t* GetXcbConnection() const = 0; + + //! Enables/Disables XInput Raw Input events. + virtual void SetEnableXInput(xcb_connection_t* connection, bool enable) = 0; }; class XcbConnectionManagerBusTraits diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h index 251342093a..f32e45ed99 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h @@ -23,9 +23,6 @@ namespace AzFramework virtual ~XcbEventHandler() = default; virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0; - - // ATTN This is used as a workaround for RAW Input events when using the Editor. - virtual void PollSpecialEvents(){}; }; class XcbEventHandlerBusTraits : public AZ::EBusTraits diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp index 56f21e6533..b938bf4825 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp @@ -64,7 +64,7 @@ namespace AzFramework return nullptr; } - s_xcbConnection = AzFramework::XcbConnectionManagerInterface::Get()->GetXcbConnection(); + s_xcbConnection = interface->GetXcbConnection(); if (!s_xcbConnection) { AZ_Warning("XcbInput", false, "XCB connection not available"); @@ -268,33 +268,6 @@ namespace AzFramework return m_xInputInitialized; } - void XcbInputDeviceMouse::SetEnableXInput(bool enable) - { - struct - { - xcb_input_event_mask_t head; - int mask; - } mask; - - mask.head.deviceid = XCB_INPUT_DEVICE_ALL; - mask.head.mask_len = 1; - - if (enable) - { - mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_MOTION | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS | - XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE | XCB_INPUT_XI_EVENT_MASK_MOTION | XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS | - XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE; - } - else - { - mask.mask = XCB_NONE; - } - - xcb_input_xi_select_events(s_xcbConnection, s_xcbScreen->root, 1, &mask.head); - - xcb_flush(s_xcbConnection); - } - void XcbInputDeviceMouse::SetSystemCursorState(SystemCursorState systemCursorState) { if (systemCursorState != m_systemCursorState) @@ -354,8 +327,6 @@ namespace AzFramework m_prevConstraintWindow = window; } - SetEnableXInput(!cursorShown); - CreateBarriers(window, confined); ShowCursor(window, cursorShown); } @@ -500,14 +471,6 @@ namespace AzFramework } } - void XcbInputDeviceMouse::HandlePointerMotionEvents(const xcb_generic_event_t* event) - { - const xcb_input_motion_event_t* mouseMotionEvent = reinterpret_cast(event); - - m_systemCursorPosition[0] = mouseMotionEvent->event_x; - m_systemCursorPosition[1] = mouseMotionEvent->event_y; - } - void XcbInputDeviceMouse::HandleRawInputEvents(const xcb_ge_generic_event_t* event) { const xcb_ge_generic_event_t* genericEvent = reinterpret_cast(event); @@ -552,78 +515,20 @@ namespace AzFramework } } - void XcbInputDeviceMouse::PollSpecialEvents() - { - while (xcb_generic_event_t* genericEvent = xcb_poll_for_queued_event(s_xcbConnection)) - { - // TODO Is the following correct? If we are showing the cursor, don't poll RAW Input events. - switch (genericEvent->response_type & ~0x80) - { - case XCB_GE_GENERIC: - { - const xcb_ge_generic_event_t* geGenericEvent = reinterpret_cast(genericEvent); - - // Only handle raw inputs if we have focus. - // Handle Raw Input events first. - if ((geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) || - (geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) || - (geGenericEvent->event_type == XCB_INPUT_RAW_MOTION)) - { - HandleRawInputEvents(geGenericEvent); - - free(genericEvent); - } - } - break; - } - } - } - void XcbInputDeviceMouse::HandleXcbEvent(xcb_generic_event_t* event) { switch (event->response_type & ~0x80) { - // QT5 is using by default XInput which means we do need to check for XCB_GE_GENERIC event to parse all mouse related events. + // XInput raw events are sent from the server as a XCB_GE_GENERIC + // event. A XCB_GE_GENERIC event is typecast to a + // xcb_ge_generic_event_t, which is distinct from a + // xcb_generic_event_t, and exists so that X11 extensions can extend + // the event emission beyond the size that a normal X11 event could + // contain. case XCB_GE_GENERIC: { const xcb_ge_generic_event_t* genericEvent = reinterpret_cast(event); - - // Handling RAW Inputs here works in GameMode but not in Editor mode because QT is - // not handling RAW input events and passing to. - if (!m_cursorShown) - { - // Handle Raw Input events first. - if ((genericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) || - (genericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) || (genericEvent->event_type == XCB_INPUT_RAW_MOTION)) - { - HandleRawInputEvents(genericEvent); - } - } - else - { - switch (genericEvent->event_type) - { - case XCB_INPUT_BUTTON_PRESS: - { - const xcb_input_button_press_event_t* mouseButtonEvent = - reinterpret_cast(genericEvent); - HandleButtonPressEvents(mouseButtonEvent->detail, true); - } - break; - case XCB_INPUT_BUTTON_RELEASE: - { - const xcb_input_button_release_event_t* mouseButtonEvent = - reinterpret_cast(genericEvent); - HandleButtonPressEvents(mouseButtonEvent->detail, false); - } - break; - case XCB_INPUT_MOTION: - { - HandlePointerMotionEvents(event); - } - break; - } - } + HandleRawInputEvents(genericEvent); } break; case XCB_FOCUS_IN: @@ -634,6 +539,9 @@ namespace AzFramework m_focusWindow = focusInEvent->event; HandleCursorState(m_focusWindow, m_systemCursorState); } + + auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); + interface->SetEnableXInput(interface->GetXcbConnection(), true); } break; case XCB_FOCUS_OUT: @@ -645,6 +553,9 @@ namespace AzFramework ResetInputChannelStates(); m_focusWindow = XCB_NONE; + + auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); + interface->SetEnableXInput(interface->GetXcbConnection(), false); } break; } diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h index 106d204ca9..8d8f0a2845 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h @@ -65,9 +65,6 @@ namespace AzFramework //! \ref AzFramework::InputDeviceMouse::Implementation::TickInputDevice void TickInputDevice() override; - //! This method is called by the Editor to accommodate some events with the Editor. Never called in Game mode. - void PollSpecialEvents() override; - //! Handle X11 events. void HandleXcbEvent(xcb_generic_event_t* event) override; @@ -77,9 +74,6 @@ namespace AzFramework //! Initialize XInput extension. Used for raw input during confinement and showing/hiding the cursor. static bool InitializeXInput(); - //! Enables/Disables XInput Raw Input events. - void SetEnableXInput(bool enable); - //! Create barriers. void CreateBarriers(xcb_window_t window, bool create); @@ -98,9 +92,6 @@ namespace AzFramework //! Handle button press/release events. void HandleButtonPressEvents(uint32_t detail, bool pressed); - //! Handle motion notify events. - void HandlePointerMotionEvents(const xcb_generic_event_t* event); - //! Will set cursor states and confinement modes. void HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState); @@ -160,7 +151,6 @@ namespace AzFramework AZ::Vector2 m_cursorHiddenPosition; AZ::Vector2 m_systemCursorPositionNormalized; - uint32_t m_systemCursorPosition[MAX_XI_RAW_AXIS]; static xcb_connection_t* s_xcbConnection; static xcb_screen_t* s_xcbScreen; From 7e67064ef89173fd403789549c791dace7e4eb7d Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Tue, 26 Oct 2021 16:34:16 -0700 Subject: [PATCH 046/177] [Linux] Style fixes: prefer `nullptr` to `NULL`, etc Signed-off-by: Chris Burel --- .../Xcb/AzFramework/XcbInputDeviceMouse.cpp | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp index b938bf4825..4c6ee28039 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp @@ -24,9 +24,6 @@ namespace AzFramework return XCB_NONE; } - // TODO Clang compile error because cast .... loses information. On GNU/Linux HWND is void* and on 64-bit - // machines its obviously 64 bit but we receive the window id from m_renderOverlay.winId() which is xcb_window_t 32-bit. - return static_cast(reinterpret_cast(systemCursorFocusWindow)); } @@ -57,7 +54,7 @@ namespace AzFramework InputDeviceMouse::Implementation* XcbInputDeviceMouse::Create(InputDeviceMouse& inputDevice) { - auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); + const auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); if (!interface) { AZ_Warning("XcbInput", false, "XCB interface not available"); @@ -126,7 +123,7 @@ namespace AzFramework // Get window information. const XcbStdFreePtr xcbGeometryReply{ xcb_get_geometry_reply( - s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) }; + s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), nullptr) }; if (!xcbGeometryReply) { @@ -137,7 +134,7 @@ namespace AzFramework xcb_translate_coordinates(s_xcbConnection, window, s_xcbScreen->root, 0, 0); const XcbStdFreePtr xkbTranslateCoordReply{ xcb_translate_coordinates_reply( - s_xcbConnection, translate_coord, NULL) }; + s_xcbConnection, translate_coord, nullptr) }; if (!xkbTranslateCoordReply) { @@ -173,11 +170,11 @@ namespace AzFramework for (const auto& barrier : m_activeBarriers) { xcb_void_cookie_t cookie = xcb_xfixes_create_pointer_barrier_checked( - s_xcbConnection, barrier.id, window, barrier.x0, barrier.y0, barrier.x1, barrier.y1, barrier.direction, 0, NULL); - const XcbStdFreePtr xkbError{ xcb_request_check(s_xcbConnection, cookie) }; + s_xcbConnection, barrier.id, window, barrier.x0, barrier.y0, barrier.x1, barrier.y1, barrier.direction, 0, nullptr); + const XcbStdFreePtr xcbError{ xcb_request_check(s_xcbConnection, cookie) }; AZ_Warning( - "XcbInput", !xkbError, "XFixes, failed to create barrier %d at (%d %d %d %d)", barrier.id, barrier.x0, barrier.y0, + "XcbInput", !xcbError, "XFixes, failed to create barrier %d at (%d %d %d %d)", barrier.id, barrier.x0, barrier.y0, barrier.x1, barrier.y1); } } @@ -207,7 +204,7 @@ namespace AzFramework const xcb_xfixes_query_version_cookie_t query_cookie = xcb_xfixes_query_version(s_xcbConnection, 5, 0); - xcb_generic_error_t* error = NULL; + xcb_generic_error_t* error = nullptr; const XcbStdFreePtr xkbQueryRequestReply{ xcb_xfixes_query_version_reply( s_xcbConnection, query_cookie, &error) }; @@ -244,7 +241,7 @@ namespace AzFramework const xcb_input_xi_query_version_cookie_t query_version_cookie = xcb_input_xi_query_version(s_xcbConnection, 2, 2); - xcb_generic_error_t* error = NULL; + xcb_generic_error_t* error = nullptr; const XcbStdFreePtr xkbQueryRequestReply{ xcb_input_xi_query_version_reply( s_xcbConnection, query_version_cookie, &error) }; @@ -340,7 +337,7 @@ namespace AzFramework { // TODO Basically not done at all. Added only the basic functions needed. const XcbStdFreePtr xkbGeometryReply{ xcb_get_geometry_reply( - s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) }; + s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), nullptr) }; if (!xkbGeometryReply) { @@ -372,7 +369,7 @@ namespace AzFramework const xcb_query_pointer_cookie_t pointer = xcb_query_pointer(s_xcbConnection, window); - const XcbStdFreePtr xkbQueryPointerReply{ xcb_query_pointer_reply(s_xcbConnection, pointer, NULL) }; + const XcbStdFreePtr xkbQueryPointerReply{ xcb_query_pointer_reply(s_xcbConnection, pointer, nullptr) }; if (!xkbQueryPointerReply) { @@ -380,7 +377,7 @@ namespace AzFramework } const XcbStdFreePtr xkbGeometryReply{ xcb_get_geometry_reply( - s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) }; + s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), nullptr) }; if (!xkbGeometryReply) { @@ -426,11 +423,11 @@ namespace AzFramework cookie = xcb_xfixes_hide_cursor_checked(s_xcbConnection, window); } - const XcbStdFreePtr xkbError{ xcb_request_check(s_xcbConnection, cookie) }; + const XcbStdFreePtr xcbError{ xcb_request_check(s_xcbConnection, cookie) }; - if (xkbError) + if (xcbError) { - AZ_Warning("XcbInput", false, "ShowCursor failed: %d", xkbError->error_code); + AZ_Warning("XcbInput", false, "ShowCursor failed: %d", xcbError->error_code); return; } From 43c83f13c6f86e12da5ec07c6f64c50c84598f11 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Tue, 26 Oct 2021 16:35:29 -0700 Subject: [PATCH 047/177] [Linux] Add unit tests for xcb mouse input Signed-off-by: Chris Burel --- .../Tests/Platform/Common/Xcb/Actions.h | 35 ++ .../Platform/Common/Xcb/MockXcbInterface.cpp | 91 ++++ .../Platform/Common/Xcb/MockXcbInterface.h | 33 ++ .../Platform/Common/Xcb/XcbBaseTestFixture.h | 6 + .../Xcb/XcbInputDeviceKeyboardTests.cpp | 6 - .../Common/Xcb/XcbInputDeviceMouseTests.cpp | 401 ++++++++++++++++++ .../Xcb/azframework_xcb_tests_files.cmake | 1 + 7 files changed, 567 insertions(+), 6 deletions(-) create mode 100644 Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceMouseTests.cpp diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h index 1650ff4f8d..d227729fd3 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h @@ -11,6 +11,13 @@ #include #include +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_0_VALUE_PARAMS()) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{}; + return value; +} ACTION_TEMPLATE(ReturnMalloc, HAS_1_TEMPLATE_PARAMS(typename, T), AND_1_VALUE_PARAMS(p0)) { @@ -25,3 +32,31 @@ ACTION_TEMPLATE(ReturnMalloc, *value = T{ p0, p1 }; return value; } +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_3_VALUE_PARAMS(p0, p1, p2)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0, p1, p2 }; + return value; +} +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_4_VALUE_PARAMS(p0, p1, p2, p3)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0, p1, p2, p3 }; + return value; +} +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0, p1, p2, p3, p4 }; + return value; +} +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0, p1, p2, p3, p4, p5 }; + return value; +} diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp index b15809a4c6..39d425b799 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp @@ -32,6 +32,51 @@ xcb_generic_error_t* xcb_request_check(xcb_connection_t* c, xcb_void_cookie_t co { return MockXcbInterface::Instance()->xcb_request_check(c, cookie); } +const xcb_setup_t* xcb_get_setup(xcb_connection_t *c) +{ + return MockXcbInterface::Instance()->xcb_get_setup(c); +} +xcb_screen_iterator_t xcb_setup_roots_iterator(const xcb_setup_t* R) +{ + return MockXcbInterface::Instance()->xcb_setup_roots_iterator(R); +} +const xcb_query_extension_reply_t* xcb_get_extension_data(xcb_connection_t* c, xcb_extension_t* ext) +{ + return MockXcbInterface::Instance()->xcb_get_extension_data(c, ext); +} +int xcb_flush(xcb_connection_t *c) +{ + return MockXcbInterface::Instance()->xcb_flush(c); +} +xcb_query_pointer_cookie_t xcb_query_pointer(xcb_connection_t* c, xcb_window_t window) +{ + return MockXcbInterface::Instance()->xcb_query_pointer(c, window); +} +xcb_query_pointer_reply_t* xcb_query_pointer_reply(xcb_connection_t* c, xcb_query_pointer_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_query_pointer_reply(c, cookie, e); +} +xcb_get_geometry_cookie_t xcb_get_geometry(xcb_connection_t* c, xcb_drawable_t drawable) +{ + return MockXcbInterface::Instance()->xcb_get_geometry(c, drawable); +} +xcb_get_geometry_reply_t* xcb_get_geometry_reply(xcb_connection_t* c, xcb_get_geometry_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_get_geometry_reply(c, cookie, e); +} +xcb_void_cookie_t xcb_warp_pointer( + xcb_connection_t* c, + xcb_window_t src_window, + xcb_window_t dst_window, + int16_t src_x, + int16_t src_y, + uint16_t src_width, + uint16_t src_height, + int16_t dst_x, + int16_t dst_y) +{ + return MockXcbInterface::Instance()->xcb_warp_pointer(c, src_window, dst_window, src_x, src_y, src_width, src_height, dst_x, dst_y); +} // ---------------------------------------------------------------------------- // xcb-xkb @@ -116,4 +161,50 @@ xkb_state_component xkb_state_update_mask( state, depressed_mods, latched_mods, locked_mods, depressed_layout, latched_layout, locked_layout); } +// ---------------------------------------------------------------------------- +// xcb-xfixes +xcb_xfixes_query_version_cookie_t xcb_xfixes_query_version( + xcb_connection_t* c, uint32_t client_major_version, uint32_t client_minor_version) +{ + return MockXcbInterface::Instance()->xcb_xfixes_query_version(c, client_major_version, client_minor_version); +} +xcb_xfixes_query_version_reply_t* xcb_xfixes_query_version_reply( + xcb_connection_t* c, xcb_xfixes_query_version_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_xfixes_query_version_reply(c, cookie, e); +} +xcb_void_cookie_t xcb_xfixes_show_cursor_checked(xcb_connection_t* c, xcb_window_t window) +{ + return MockXcbInterface::Instance()->xcb_xfixes_show_cursor_checked(c, window); +} +xcb_void_cookie_t xcb_xfixes_hide_cursor_checked(xcb_connection_t* c, xcb_window_t window) +{ + return MockXcbInterface::Instance()->xcb_xfixes_hide_cursor_checked(c, window); +} + +// ---------------------------------------------------------------------------- +// xcb-xinput +xcb_input_xi_query_version_cookie_t xcb_input_xi_query_version(xcb_connection_t* c, uint16_t major_version, uint16_t minor_version) +{ + return MockXcbInterface::Instance()->xcb_input_xi_query_version(c, major_version, minor_version); +} +xcb_input_xi_query_version_reply_t* xcb_input_xi_query_version_reply( + xcb_connection_t* c, xcb_input_xi_query_version_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_input_xi_query_version_reply(c, cookie, e); +} +xcb_void_cookie_t xcb_input_xi_select_events( + xcb_connection_t* c, xcb_window_t window, uint16_t num_mask, const xcb_input_event_mask_t* masks) +{ + return MockXcbInterface::Instance()->xcb_input_xi_select_events(c, window, num_mask, masks); +} +int xcb_input_raw_button_press_axisvalues_length (const xcb_input_raw_button_press_event_t *R) +{ + return MockXcbInterface::Instance()->xcb_input_raw_button_press_axisvalues_length(R); +} +xcb_input_fp3232_t* xcb_input_raw_button_press_axisvalues_raw(const xcb_input_raw_button_press_event_t* R) +{ + return MockXcbInterface::Instance()->xcb_input_raw_button_press_axisvalues_raw(R); +} + } diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h index b57751344e..f38e327f50 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h @@ -18,6 +18,8 @@ #undef explicit #include #include +#include +#include #include "Printers.h" @@ -62,6 +64,24 @@ public: MOCK_CONST_METHOD1(xcb_disconnect, void(xcb_connection_t* c)); MOCK_CONST_METHOD1(xcb_poll_for_event, xcb_generic_event_t*(xcb_connection_t* c)); MOCK_CONST_METHOD2(xcb_request_check, xcb_generic_error_t*(xcb_connection_t* c, xcb_void_cookie_t cookie)); + MOCK_CONST_METHOD1(xcb_get_setup, const xcb_setup_t*(xcb_connection_t *c)); + MOCK_CONST_METHOD1(xcb_setup_roots_iterator, xcb_screen_iterator_t(const xcb_setup_t* R)); + MOCK_CONST_METHOD2(xcb_get_extension_data, const xcb_query_extension_reply_t*(xcb_connection_t* c, xcb_extension_t* ext)); + MOCK_CONST_METHOD1(xcb_flush, int(xcb_connection_t *c)); + MOCK_CONST_METHOD2(xcb_query_pointer, xcb_query_pointer_cookie_t(xcb_connection_t* c, xcb_window_t window)); + MOCK_CONST_METHOD3(xcb_query_pointer_reply, xcb_query_pointer_reply_t*(xcb_connection_t* c, xcb_query_pointer_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD2(xcb_get_geometry, xcb_get_geometry_cookie_t(xcb_connection_t* c, xcb_drawable_t drawable)); + MOCK_CONST_METHOD3(xcb_get_geometry_reply, xcb_get_geometry_reply_t*(xcb_connection_t* c, xcb_get_geometry_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD9(xcb_warp_pointer, xcb_void_cookie_t( + xcb_connection_t* c, + xcb_window_t src_window, + xcb_window_t dst_window, + int16_t src_x, + int16_t src_y, + uint16_t src_width, + uint16_t src_height, + int16_t dst_x, + int16_t dst_y)); // xcb-xkb MOCK_CONST_METHOD3(xcb_xkb_use_extension, xcb_xkb_use_extension_cookie_t(xcb_connection_t* c, uint16_t wantedMajor, uint16_t wantedMinor)); @@ -83,6 +103,19 @@ public: MOCK_CONST_METHOD4(xkb_state_key_get_utf8, int(xkb_state* state, xkb_keycode_t key, char* buffer, size_t size)); MOCK_CONST_METHOD7(xkb_state_update_mask, xkb_state_component(xkb_state* state, xkb_mod_mask_t depressed_mods, xkb_mod_mask_t latched_mods, xkb_mod_mask_t locked_mods, xkb_layout_index_t depressed_layout, xkb_layout_index_t latched_layout, xkb_layout_index_t locked_layout)); + // xcb-xfixes + MOCK_CONST_METHOD3(xcb_xfixes_query_version, xcb_xfixes_query_version_cookie_t(xcb_connection_t* c, uint32_t client_major_version, uint32_t client_minor_version)); + MOCK_CONST_METHOD3(xcb_xfixes_query_version_reply, xcb_xfixes_query_version_reply_t*(xcb_connection_t* c, xcb_xfixes_query_version_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD2(xcb_xfixes_show_cursor_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_window_t window)); + MOCK_CONST_METHOD2(xcb_xfixes_hide_cursor_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_window_t window)); + + // xcb-xinput + MOCK_CONST_METHOD3(xcb_input_xi_query_version, xcb_input_xi_query_version_cookie_t(xcb_connection_t* c, uint16_t major_version, uint16_t minor_version)); + MOCK_CONST_METHOD3(xcb_input_xi_query_version_reply, xcb_input_xi_query_version_reply_t*(xcb_connection_t* c, xcb_input_xi_query_version_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD4(xcb_input_xi_select_events, xcb_void_cookie_t(xcb_connection_t* c, xcb_window_t window, uint16_t num_mask, const xcb_input_event_mask_t* masks)); + MOCK_CONST_METHOD1(xcb_input_raw_button_press_axisvalues_length, int(const xcb_input_raw_button_press_event_t* R)); + MOCK_CONST_METHOD1(xcb_input_raw_button_press_axisvalues_raw, xcb_input_fp3232_t*(const xcb_input_raw_button_press_event_t* R)); + private: static inline MockXcbInterface* self = nullptr; }; diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.h index 8e9b008fc1..2a63a5f158 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.h +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.h @@ -22,6 +22,12 @@ namespace AzFramework public: void SetUp() override; + template + static xcb_generic_event_t MakeEvent(T event) + { + return *reinterpret_cast(&event); + } + protected: testing::NiceMock m_interface; xcb_connection_t m_connection{}; diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp index 76d00eda50..7209875235 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp @@ -21,12 +21,6 @@ #include "XcbBaseTestFixture.h" #include "XcbTestApplication.h" -template -xcb_generic_event_t MakeEvent(T event) -{ - return *reinterpret_cast(&event); -} - namespace AzFramework { // Sets up default behavior for mock keyboard responses to xcb methods diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceMouseTests.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceMouseTests.cpp new file mode 100644 index 0000000000..40dfdec26f --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceMouseTests.cpp @@ -0,0 +1,401 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include + +#include +#include + +#include "XcbBaseTestFixture.h" +#include "XcbTestApplication.h" +#include "Matchers.h" +#include "Actions.h" + +namespace AzFramework +{ + // Sets up default behavior for mock keyboard responses to xcb methods + class XcbInputDeviceMouseTests + : public XcbBaseTestFixture + { + public: + void SetUp() override + { + using testing::Return; + using testing::_; + + XcbBaseTestFixture::SetUp(); + + ON_CALL(m_interface, xcb_get_setup(&m_connection)) + .WillByDefault(Return(&s_xcbSetup)); + ON_CALL(m_interface, xcb_setup_roots_iterator(&s_xcbSetup)) + .WillByDefault(Return(xcb_screen_iterator_t{&s_xcbScreen})); + + ON_CALL(m_interface, xcb_get_extension_data(&m_connection, &xcb_xfixes_id)) + .WillByDefault(Return(&s_xfixesExtensionReply)); + ON_CALL(m_interface, xcb_xfixes_query_version_reply(&m_connection, _, _)) + .WillByDefault(ReturnMalloc( + /*response_type=*/(uint8_t)XCB_XFIXES_QUERY_VERSION, + /*pad0=*/(uint8_t)0, + /*sequence=*/(uint16_t)1, + /*length=*/0u, + /*major_version=*/5u, + /*minor_version=*/0u + )); + + ON_CALL(m_interface, xcb_get_extension_data(&m_connection, &xcb_input_id)) + .WillByDefault(Return(&s_xfixesExtensionReply)); + ON_CALL(m_interface, xcb_input_xi_query_version_reply(&m_connection, _, _)) + .WillByDefault(ReturnMalloc( + /*response_type=*/(uint8_t)XCB_INPUT_XI_QUERY_VERSION, + /*pad0=*/(uint8_t)0, + /*sequence=*/(uint16_t)1, + /*length=*/0u, + /*major_version=*/(uint16_t)2, + /*minor_version=*/(uint16_t)2 + )); + } + + void PumpApplication() + { + m_application.PumpSystemEventLoopUntilEmpty(); + m_application.TickSystem(); + m_application.Tick(); + } + + protected: + static constexpr inline uint8_t s_xinputMajorOpcode = 131; + static constexpr inline xcb_window_t s_rootWindow = 1; + static constexpr inline xcb_input_device_id_t s_virtualCorePointerId = 2; + static constexpr inline xcb_input_device_id_t s_physicalPointerDeviceId = 3; + static constexpr inline uint16_t s_screenWidthInPixels = 3840; + static constexpr inline uint16_t s_screenHeightInPixels = 2160; + static constexpr inline xcb_setup_t s_xcbSetup{ + /*.status=*/1, + /*.pad0=*/0, + /*.protocol_major_version=*/11, + /*.protocol_minor_version=*/0, + }; + static inline xcb_screen_t s_xcbScreen{ + /*.root=*/s_rootWindow, + /*.default_colormap=*/32, + /*.white_pixel=*/16777215, + /*.black_pixel=*/0, + /*.current_input_masks=*/0, + /*.width_in_pixels=*/s_screenWidthInPixels, + /*.height_in_pixels=*/s_screenHeightInPixels, + /*.width_in_millimeters=*/602, + /*.height_in_millimeters=*/341, + }; + static constexpr inline xcb_query_extension_reply_t s_xfixesExtensionReply{ + /*.response_type=*/XCB_QUERY_EXTENSION, + /*.pad0=*/0, + /*.sequence=*/1, + /*.length=*/0, + /*.present=*/1, + }; + static constexpr inline xcb_query_extension_reply_t s_xinputExtensionReply{ + /*.response_type=*/XCB_QUERY_EXTENSION, + /*.pad0=*/0, + /*.sequence=*/1, + /*.length=*/0, + /*.present=*/1, + /*.major_opcode=*/s_xinputMajorOpcode, + }; + XcbTestApplication m_application{ + /*enabledGamepadsCount=*/0, + /*keyboardEnabled=*/false, + /*motionEnabled=*/false, + /*mouseEnabled=*/true, + /*touchEnabled=*/false, + /*virtualKeyboardEnabled=*/false + }; + }; + + struct MouseButtonTestData + { + xcb_button_index_t m_button; + }; + + class XcbInputDeviceMouseButtonTests + : public XcbInputDeviceMouseTests + , public testing::WithParamInterface + { + public: + static InputChannelId GetInputChannelIdForButton(const xcb_button_index_t button) + { + switch (button) + { + case XCB_BUTTON_INDEX_1: + return InputDeviceMouse::Button::Left; + case XCB_BUTTON_INDEX_2: + return InputDeviceMouse::Button::Right; + case XCB_BUTTON_INDEX_3: + return InputDeviceMouse::Button::Middle; + } + return InputChannelId{}; + } + + AZStd::array GetIdleChannelIdsForButton(const xcb_button_index_t button) + { + switch (button) + { + case XCB_BUTTON_INDEX_1: + return { InputDeviceMouse::Button::Right, InputDeviceMouse::Button::Middle, InputDeviceMouse::Button::Other1, InputDeviceMouse::Button::Other2 }; + case XCB_BUTTON_INDEX_2: + return { InputDeviceMouse::Button::Left, InputDeviceMouse::Button::Middle, InputDeviceMouse::Button::Other1, InputDeviceMouse::Button::Other2 }; + case XCB_BUTTON_INDEX_3: + return { InputDeviceMouse::Button::Left, InputDeviceMouse::Button::Right, InputDeviceMouse::Button::Other1, InputDeviceMouse::Button::Other2 }; + case XCB_BUTTON_INDEX_4: + return { InputDeviceMouse::Button::Left, InputDeviceMouse::Button::Right, InputDeviceMouse::Button::Middle, InputDeviceMouse::Button::Other2 }; + case XCB_BUTTON_INDEX_5: + return { InputDeviceMouse::Button::Left, InputDeviceMouse::Button::Right, InputDeviceMouse::Button::Middle, InputDeviceMouse::Button::Other1 }; + } + return AZStd::array(); + } + }; + + TEST_P(XcbInputDeviceMouseButtonTests, ButtonInputChannelsUpdateStateFromXcbEvents) + { + using testing::Each; + using testing::Eq; + using testing::NotNull; + using testing::Property; + using testing::Return; + + // Set the expectations for the events that will be generated + // nullptr entries represent when the event queue is empty, and will cause + // PumpSystemEventLoopUntilEmpty to return + // + // Event pointers are freed by the calling code, so these actions + // malloc new copies + // + // The xcb mouse does not react to the `XCB_BUTTON_PRESS` / + // `XCB_BUTTON_RELEASE` events, but it will still receive those events + // from the X server. + EXPECT_CALL(m_interface, xcb_poll_for_event(&m_connection)) + .WillOnce(ReturnMalloc(MakeEvent(xcb_input_raw_button_press_event_t{ + /*response_type=*/XCB_GE_GENERIC, + /*extension=*/s_xinputMajorOpcode, + /*sequence=*/4, + /*length=*/2, + /*event_type=*/XCB_INPUT_RAW_BUTTON_PRESS, + /*deviceid=*/s_virtualCorePointerId, + /*time=*/3984920, + /*detail=*/GetParam().m_button, + /*sourceid=*/s_physicalPointerDeviceId, + /*valuators_len=*/2, + /*flags=*/0, + /*pad0[4]=*/{}, + /*full_sequence=*/4 + }))) + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(MakeEvent(xcb_button_press_event_t{ + /*response_type=*/XCB_BUTTON_PRESS, + /*detail=*/static_cast(GetParam().m_button), + /*sequence=*/4, + /*time=*/3984920, + /*root=*/s_rootWindow, + /*event=*/119537664, + /*child=*/0, + /*root_x=*/55, + /*root_y=*/1099, + /*event_x=*/55, + /*event_y=*/55, + /*state=*/0, + /*same_screen=*/1 + }))) + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(MakeEvent(xcb_input_raw_button_release_event_t{ + /*response_type=*/XCB_GE_GENERIC, + /*extension=*/s_xinputMajorOpcode, + /*sequence=*/4, + /*length=*/2, + /*event_type=*/XCB_INPUT_RAW_BUTTON_RELEASE, + /*deviceid=*/s_virtualCorePointerId, + /*time=*/3984964, + /*detail=*/GetParam().m_button, + /*sourceid=*/s_physicalPointerDeviceId, + /*valuators_len=*/2, + /*flags=*/0, + /*pad0[4]=*/{}, + /*full_sequence=*/4 + }))) + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(MakeEvent(xcb_button_release_event_t{ + /*response_type=*/XCB_BUTTON_RELEASE, + /*detail=*/static_cast(GetParam().m_button), + /*sequence=*/4, + /*time=*/3984964, + /*root=*/s_rootWindow, + /*event=*/119537664, + /*child=*/0, + /*root_x=*/55, + /*root_y=*/1099, + /*event_x=*/55, + /*event_y=*/55, + /*state=*/XCB_KEY_BUT_MASK_BUTTON_1, + /*same_screen=*/1 + }))) + .WillOnce(Return(nullptr)) + ; + + m_application.Start(); + InputSystemCursorRequestBus::Event( + InputDeviceMouse::Id, + &InputSystemCursorRequests::SetSystemCursorState, + SystemCursorState::ConstrainedAndHidden); + + const InputChannel* activeButtonChannel = InputChannelRequests::FindInputChannel(GetInputChannelIdForButton(GetParam().m_button)); + const auto inactiveButtonChannels = [this]() + { + const auto inactiveButtonChannelIds = GetIdleChannelIdsForButton(GetParam().m_button); + AZStd::array channels{}; + AZStd::transform(begin(inactiveButtonChannelIds), end(inactiveButtonChannelIds), begin(channels), [](const InputChannelId& id) + { + return InputChannelRequests::FindInputChannel(id); + }); + return channels; + }(); + + ASSERT_TRUE(activeButtonChannel); + ASSERT_THAT(inactiveButtonChannels, Each(NotNull())); + + EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Idle)); + EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle)))); + + PumpApplication(); + + EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Began)); + EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle)))); + + PumpApplication(); + + EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Updated)); + EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle)))); + + PumpApplication(); + + EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Ended)); + EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle)))); + + PumpApplication(); + + EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Idle)); + EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle)))); + } + + INSTANTIATE_TEST_CASE_P( + AllButtons, + XcbInputDeviceMouseButtonTests, + testing::Values( + MouseButtonTestData{ XCB_BUTTON_INDEX_1 }, + MouseButtonTestData{ XCB_BUTTON_INDEX_2 }, + MouseButtonTestData{ XCB_BUTTON_INDEX_3 } + // XCB_BUTTON_INDEX_4 and XCB_BUTTON_INDEX_5 map to positive and + // negative scroll wheel events, which are handled as motion events + ) + ); + + TEST_F(XcbInputDeviceMouseTests, MovementInputChannelsUpdateStateFromXcbEvents) + { + using testing::Each; + using testing::Eq; + using testing::FloatEq; + using testing::NotNull; + using testing::Property; + using testing::Return; + + // Set the expectations for the events that will be generated + // nullptr entries represent when the event queue is empty, and will cause + // PumpSystemEventLoopUntilEmpty to return + // + // Event pointers are freed by the calling code, so these actions + // malloc new copies + // + // The xcb mouse does not react to the `XCB_MOTION_NOTIFY` event, but + // it will still receive it from the X server. + EXPECT_CALL(m_interface, xcb_poll_for_event(&m_connection)) + .WillOnce(ReturnMalloc(MakeEvent(xcb_input_raw_motion_event_t{ + /*response_type=*/XCB_GE_GENERIC, + /*extension=*/s_xinputMajorOpcode, + /*sequence=*/5, + /*length=*/10, + /*event_type=*/XCB_INPUT_RAW_MOTION, + /*deviceid=*/s_virtualCorePointerId, + /*time=*/0, // use the time value to identify each event + /*detail=*/XCB_MOTION_NORMAL, + /*sourceid=*/s_physicalPointerDeviceId, + /*valuators_len=*/2, // number of axes that have values for this event + /*flags=*/0, + /*pad0[4]=*/{}, + /*full_sequence=*/5, + }))) + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(MakeEvent(xcb_motion_notify_event_t{ + /*response_type=*/XCB_MOTION_NOTIFY, + /*detail=*/XCB_MOTION_NORMAL, + /*sequence=*/5, + /*time=*/1, // use the time value to identify each event + /*root=*/s_rootWindow, + /*event=*/127926272, + /*child=*/0, + /*root_x=*/95, + /*root_y=*/1079, + /*event_x=*/95, + /*event_y=*/20, + /*state=*/0, + /*same_screen=*/1, + }))) + .WillOnce(Return(nullptr)) + ; + + AZStd::array axisValues + { + xcb_input_fp3232_t{ /*.integral=*/ 1, /*.fraction=*/0 }, // x motion + xcb_input_fp3232_t{ /*.integral=*/ 2, /*.fraction=*/0 } // y motion + }; + + EXPECT_CALL(m_interface, xcb_input_raw_button_press_axisvalues_length(testing::Field(&xcb_input_raw_button_press_event_t::time, 0))) + .WillRepeatedly(testing::Return(2)); // x and y axis + EXPECT_CALL(m_interface, xcb_input_raw_button_press_axisvalues_raw(testing::Field(&xcb_input_raw_button_press_event_t::time, 0))) + .WillRepeatedly(testing::Return(axisValues.data())); // x and y axis + + m_application.Start(); + InputSystemCursorRequestBus::Event( + InputDeviceMouse::Id, + &InputSystemCursorRequests::SetSystemCursorState, + SystemCursorState::ConstrainedAndHidden); + + const InputChannel* xMotionChannel = InputChannelRequests::FindInputChannel(InputDeviceMouse::Movement::X); + const InputChannel* yMotionChannel = InputChannelRequests::FindInputChannel(InputDeviceMouse::Movement::Y); + ASSERT_TRUE(xMotionChannel); + ASSERT_TRUE(yMotionChannel); + + EXPECT_THAT(xMotionChannel->GetState(), Eq(InputChannel::State::Idle)); + EXPECT_THAT(yMotionChannel->GetState(), Eq(InputChannel::State::Idle)); + EXPECT_THAT(xMotionChannel->GetValue(), FloatEq(0.0f)); + EXPECT_THAT(yMotionChannel->GetValue(), FloatEq(0.0f)); + + PumpApplication(); + + EXPECT_THAT(xMotionChannel->GetState(), Eq(InputChannel::State::Began)); + EXPECT_THAT(yMotionChannel->GetState(), Eq(InputChannel::State::Began)); + EXPECT_THAT(xMotionChannel->GetValue(), FloatEq(1.0f)); + EXPECT_THAT(yMotionChannel->GetValue(), FloatEq(2.0f)); + + PumpApplication(); + + EXPECT_THAT(xMotionChannel->GetState(), Eq(InputChannel::State::Ended)); + EXPECT_THAT(yMotionChannel->GetState(), Eq(InputChannel::State::Ended)); + EXPECT_THAT(xMotionChannel->GetValue(), FloatEq(0.0f)); + EXPECT_THAT(yMotionChannel->GetValue(), FloatEq(0.0f)); + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake index 147fd2bfe1..7da00fa18c 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake @@ -17,5 +17,6 @@ set(FILES XcbBaseTestFixture.cpp XcbBaseTestFixture.h XcbInputDeviceKeyboardTests.cpp + XcbInputDeviceMouseTests.cpp XcbTestApplication.h ) From 0502ddbe2b3cae5ecd76f3187d5735f04b7834c5 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Wed, 3 Nov 2021 10:11:55 -0700 Subject: [PATCH 048/177] [Linux] Return the active window when there's no cursor constraint window The cursor constraint window is only set by the Editor. In the game launcher, this function was returning a null window, which makes `GetSystemCursorPositionNormalized()` unable to determine the cursor position. This in turn causes mouse input in ImGui to not work. Fixes #4722, LYN-7491 Signed-off-by: Chris Burel --- .../Xcb/AzFramework/XcbInputDeviceMouse.cpp | 131 ++++++++-------- .../Xcb/AzFramework/XcbInputDeviceMouse.h | 3 - .../Tests/Platform/Common/Xcb/Actions.h | 7 + .../Platform/Common/Xcb/MockXcbInterface.cpp | 57 +++++++ .../Platform/Common/Xcb/MockXcbInterface.h | 27 ++++ .../Common/Xcb/XcbInputDeviceMouseTests.cpp | 144 ++++++++++++++++++ 6 files changed, 305 insertions(+), 64 deletions(-) diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp index 4c6ee28039..c3b7a97ccf 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp @@ -13,18 +13,68 @@ namespace AzFramework { - xcb_window_t GetSystemCursorFocusWindow() + xcb_window_t GetSystemCursorFocusWindow(xcb_connection_t* connection) { void* systemCursorFocusWindow = nullptr; AzFramework::InputSystemCursorConstraintRequestBus::BroadcastResult( systemCursorFocusWindow, &AzFramework::InputSystemCursorConstraintRequests::GetSystemCursorConstraintWindow); - if (!systemCursorFocusWindow) + if (systemCursorFocusWindow) { - return XCB_NONE; + return static_cast(reinterpret_cast(systemCursorFocusWindow)); } - return static_cast(reinterpret_cast(systemCursorFocusWindow)); + // EWMH-compliant window managers set the "_NET_ACTIVE_WINDOW" property + // of the X server's root window to the currently active window. This + // retrieves value of that property. + + // Get the atom for the _NET_ACTIVE_WINDOW property + constexpr int propertyNameLength = 18; + xcb_generic_error_t* error = nullptr; + XcbStdFreePtr activeWindowAtom {xcb_intern_atom_reply( + connection, + xcb_intern_atom(connection, /*only_if_exists=*/ 1, propertyNameLength, "_NET_ACTIVE_WINDOW"), + &error + )}; + if (!activeWindowAtom || error) + { + if (error) + { + AZ_Warning("XcbInput", false, "Retrieving _NET_ACTIVE_WINDOW atom failed : Error code %d", error->error_code); + free(error); + } + return XCB_WINDOW_NONE; + } + + // Get the root window + const xcb_window_t rootWId = xcb_setup_roots_iterator(xcb_get_setup(connection)).data->root; + + // Fetch the value of the root window's _NET_ACTIVE_WINDOW property + XcbStdFreePtr property {xcb_get_property_reply( + connection, + xcb_get_property( + /*c=*/connection, + /*_delete=*/ 0, + /*window=*/rootWId, + /*property=*/activeWindowAtom->atom, + /*type=*/XCB_ATOM_WINDOW, + /*long_offset=*/0, + /*long_length=*/1 + ), + &error + )}; + + if (!property || error) + { + if (error) + { + AZ_Warning("XcbInput", false, "Retrieving _NET_ACTIVE_WINDOW atom failed : Error code %d", error->error_code); + free(error); + } + return XCB_WINDOW_NONE; + } + + return *static_cast(xcb_get_property_value(property.get())); } xcb_connection_t* XcbInputDeviceMouse::s_xcbConnection = nullptr; @@ -36,8 +86,7 @@ namespace AzFramework : InputDeviceMouse::Implementation(inputDevice) , m_systemCursorState(SystemCursorState::Unknown) , m_systemCursorPositionNormalized(0.5f, 0.5f) - , m_prevConstraintWindow(XCB_NONE) - , m_focusWindow(XCB_NONE) + , m_focusWindow(XCB_WINDOW_NONE) , m_cursorShown(true) { XcbEventHandlerBus::Handler::BusConnect(); @@ -271,7 +320,7 @@ namespace AzFramework { m_systemCursorState = systemCursorState; - m_focusWindow = GetSystemCursorFocusWindow(); + m_focusWindow = GetSystemCursorFocusWindow(s_xcbConnection); HandleCursorState(m_focusWindow, systemCursorState); } @@ -279,50 +328,10 @@ namespace AzFramework void XcbInputDeviceMouse::HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState) { - bool confined = false, cursorShown = true; - switch (systemCursorState) - { - case SystemCursorState::ConstrainedAndHidden: - { - //!< Constrained to the application's main window and hidden - confined = true; - cursorShown = false; - } - break; - case SystemCursorState::ConstrainedAndVisible: - { - //!< Constrained to the application's main window and visible - confined = true; - } - break; - case SystemCursorState::UnconstrainedAndHidden: - { - //!< Free to move outside the main window but hidden while inside - cursorShown = false; - } - break; - case SystemCursorState::UnconstrainedAndVisible: - { - //!< Free to move outside the application's main window and visible - } - case SystemCursorState::Unknown: - default: - break; - } - - // ATTN GetSystemCursorFocusWindow when getting out of the play in editor will return XCB_NONE - // We need however the window id to reset the cursor. - if (XCB_NONE == window && (confined || cursorShown)) - { - // Reuse the previous window to reset states. - window = m_prevConstraintWindow; - m_prevConstraintWindow = XCB_NONE; - } - else - { - // Remember the window we used to modify cursor and barrier states. - m_prevConstraintWindow = window; - } + const bool confined = (systemCursorState == SystemCursorState::ConstrainedAndHidden) || + (systemCursorState == SystemCursorState::ConstrainedAndVisible); + const bool cursorShown = (systemCursorState == SystemCursorState::ConstrainedAndVisible) || + (systemCursorState == SystemCursorState::UnconstrainedAndVisible); CreateBarriers(window, confined); ShowCursor(window, cursorShown); @@ -336,26 +345,26 @@ namespace AzFramework void XcbInputDeviceMouse::SetSystemCursorPositionNormalizedInternal(xcb_window_t window, AZ::Vector2 positionNormalized) { // TODO Basically not done at all. Added only the basic functions needed. - const XcbStdFreePtr xkbGeometryReply{ xcb_get_geometry_reply( + const XcbStdFreePtr xcbGeometryReply{ xcb_get_geometry_reply( s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), nullptr) }; - if (!xkbGeometryReply) + if (!xcbGeometryReply) { return; } - const int16_t x = static_cast(positionNormalized.GetX() * xkbGeometryReply->width); - const int16_t y = static_cast(positionNormalized.GetY() * xkbGeometryReply->height); + const int16_t x = static_cast(positionNormalized.GetX() * xcbGeometryReply->width); + const int16_t y = static_cast(positionNormalized.GetY() * xcbGeometryReply->height); - xcb_warp_pointer(s_xcbConnection, XCB_NONE, window, 0, 0, 0, 0, x, y); + xcb_warp_pointer(s_xcbConnection, XCB_WINDOW_NONE, window, 0, 0, 0, 0, x, y); xcb_flush(s_xcbConnection); } void XcbInputDeviceMouse::SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized) { - const xcb_window_t window = GetSystemCursorFocusWindow(); - if (XCB_NONE == window) + const xcb_window_t window = GetSystemCursorFocusWindow(s_xcbConnection); + if (XCB_WINDOW_NONE == window) { return; } @@ -397,8 +406,8 @@ namespace AzFramework AZ::Vector2 XcbInputDeviceMouse::GetSystemCursorPositionNormalized() const { - const xcb_window_t window = GetSystemCursorFocusWindow(); - if (XCB_NONE == window) + const xcb_window_t window = GetSystemCursorFocusWindow(s_xcbConnection); + if (XCB_WINDOW_NONE == window) { return AZ::Vector2::CreateZero(); } @@ -549,7 +558,7 @@ namespace AzFramework ProcessRawEventQueues(); ResetInputChannelStates(); - m_focusWindow = XCB_NONE; + m_focusWindow = XCB_WINDOW_NONE; auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); interface->SetEnableXInput(interface->GetXcbConnection(), false); diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h index 8d8f0a2845..a69a8a9ec5 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h @@ -161,9 +161,6 @@ namespace AzFramework //! Will be true if the xinput2 extension could be initialized. static bool m_xInputInitialized; - //! The window that had focus - xcb_window_t m_prevConstraintWindow; - //! The current window that has focus xcb_window_t m_focusWindow; diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h index d227729fd3..e86904efa3 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h @@ -60,3 +60,10 @@ ACTION_TEMPLATE(ReturnMalloc, *value = T{ p0, p1, p2, p3, p4, p5 }; return value; } +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0, p1, p2, p3, p4, p5, p6 }; + return value; +} diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp index 39d425b799..a19642e388 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp @@ -77,6 +77,37 @@ xcb_void_cookie_t xcb_warp_pointer( { return MockXcbInterface::Instance()->xcb_warp_pointer(c, src_window, dst_window, src_x, src_y, src_width, src_height, dst_x, dst_y); } +xcb_intern_atom_cookie_t xcb_intern_atom(xcb_connection_t* c, uint8_t only_if_exists, uint16_t name_len, const char* name) +{ + return MockXcbInterface::Instance()->xcb_intern_atom(c, only_if_exists, name_len, name); +} +xcb_intern_atom_reply_t* xcb_intern_atom_reply(xcb_connection_t* c, xcb_intern_atom_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_intern_atom_reply(c, cookie, e); +} +xcb_get_property_cookie_t xcb_get_property( + xcb_connection_t* c, + uint8_t _delete, + xcb_window_t window, + xcb_atom_t property, + xcb_atom_t type, + uint32_t long_offset, + uint32_t long_length) +{ + return MockXcbInterface::Instance()->xcb_get_property(c, _delete, window, property, type, long_offset, long_length); +} +xcb_get_property_reply_t* xcb_get_property_reply(xcb_connection_t* c, xcb_get_property_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_get_property_reply(c, cookie, e); +} +void* xcb_get_property_value(const xcb_get_property_reply_t* R) +{ + return MockXcbInterface::Instance()->xcb_get_property_value(R); +} +uint32_t xcb_generate_id(xcb_connection_t *c) +{ + return MockXcbInterface::Instance()->xcb_generate_id(c); +} // ---------------------------------------------------------------------------- // xcb-xkb @@ -181,6 +212,32 @@ xcb_void_cookie_t xcb_xfixes_hide_cursor_checked(xcb_connection_t* c, xcb_window { return MockXcbInterface::Instance()->xcb_xfixes_hide_cursor_checked(c, window); } +xcb_void_cookie_t xcb_xfixes_delete_pointer_barrier_checked(xcb_connection_t* c, xcb_xfixes_barrier_t barrier) +{ + return MockXcbInterface::Instance()->xcb_xfixes_delete_pointer_barrier_checked(c, barrier); +} +xcb_translate_coordinates_cookie_t xcb_translate_coordinates(xcb_connection_t* c, xcb_window_t src_window, xcb_window_t dst_window, int16_t src_x, int16_t src_y) +{ + return MockXcbInterface::Instance()->xcb_translate_coordinates(c, src_window, dst_window, src_x, src_y); +} +xcb_translate_coordinates_reply_t* xcb_translate_coordinates_reply(xcb_connection_t* c, xcb_translate_coordinates_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_translate_coordinates_reply(c, cookie, e); +} +xcb_void_cookie_t xcb_xfixes_create_pointer_barrier_checked( + xcb_connection_t* c, + xcb_xfixes_barrier_t barrier, + xcb_window_t window, + uint16_t x1, + uint16_t y1, + uint16_t x2, + uint16_t y2, + uint32_t directions, + uint16_t num_devices, + const uint16_t* devices) +{ + return MockXcbInterface::Instance()->xcb_xfixes_create_pointer_barrier_checked(c, barrier, window, x1, y1, x2, y2, directions, num_devices, devices); +} // ---------------------------------------------------------------------------- // xcb-xinput diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h index f38e327f50..c554993110 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h @@ -82,6 +82,19 @@ public: uint16_t src_height, int16_t dst_x, int16_t dst_y)); + MOCK_CONST_METHOD4(xcb_intern_atom, xcb_intern_atom_cookie_t(xcb_connection_t* c, uint8_t only_if_exists, uint16_t name_len, const char* name)); + MOCK_CONST_METHOD3(xcb_intern_atom_reply, xcb_intern_atom_reply_t*(xcb_connection_t* c, xcb_intern_atom_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD7(xcb_get_property, xcb_get_property_cookie_t( + xcb_connection_t* c, + uint8_t _delete, + xcb_window_t window, + xcb_atom_t property, + xcb_atom_t type, + uint32_t long_offset, + uint32_t long_length)); + MOCK_CONST_METHOD3(xcb_get_property_reply, xcb_get_property_reply_t*(xcb_connection_t* c, xcb_get_property_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD1(xcb_get_property_value, void*(const xcb_get_property_reply_t* R)); + MOCK_CONST_METHOD1(xcb_generate_id, uint32_t(xcb_connection_t *c)); // xcb-xkb MOCK_CONST_METHOD3(xcb_xkb_use_extension, xcb_xkb_use_extension_cookie_t(xcb_connection_t* c, uint16_t wantedMajor, uint16_t wantedMinor)); @@ -108,6 +121,20 @@ public: MOCK_CONST_METHOD3(xcb_xfixes_query_version_reply, xcb_xfixes_query_version_reply_t*(xcb_connection_t* c, xcb_xfixes_query_version_cookie_t cookie, xcb_generic_error_t** e)); MOCK_CONST_METHOD2(xcb_xfixes_show_cursor_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_window_t window)); MOCK_CONST_METHOD2(xcb_xfixes_hide_cursor_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_window_t window)); + MOCK_CONST_METHOD2(xcb_xfixes_delete_pointer_barrier_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_xfixes_barrier_t barrier)); + MOCK_CONST_METHOD5(xcb_translate_coordinates, xcb_translate_coordinates_cookie_t(xcb_connection_t* c, xcb_window_t src_window, xcb_window_t dst_window, int16_t src_x, int16_t src_y)); + MOCK_CONST_METHOD3(xcb_translate_coordinates_reply, xcb_translate_coordinates_reply_t*(xcb_connection_t* c, xcb_translate_coordinates_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD10(xcb_xfixes_create_pointer_barrier_checked, xcb_void_cookie_t( + xcb_connection_t* c, + xcb_xfixes_barrier_t barrier, + xcb_window_t window, + uint16_t x1, + uint16_t y1, + uint16_t x2, + uint16_t y2, + uint32_t directions, + uint16_t num_devices, + const uint16_t* devices)); // xcb-xinput MOCK_CONST_METHOD3(xcb_input_xi_query_version, xcb_input_xi_query_version_cookie_t(xcb_connection_t* c, uint16_t major_version, uint16_t minor_version)); diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceMouseTests.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceMouseTests.cpp index 40dfdec26f..05784462d7 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceMouseTests.cpp +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceMouseTests.cpp @@ -28,7 +28,10 @@ namespace AzFramework public: void SetUp() override { + using testing::Eq; + using testing::Field; using testing::Return; + using testing::StrEq; using testing::_; XcbBaseTestFixture::SetUp(); @@ -61,6 +64,37 @@ namespace AzFramework /*major_version=*/(uint16_t)2, /*minor_version=*/(uint16_t)2 )); + + // Set the default focus window + EXPECT_CALL(m_interface, xcb_intern_atom(&m_connection, 1, 18, StrEq("_NET_ACTIVE_WINDOW"))) + .WillRepeatedly(Return(xcb_intern_atom_cookie_t{/*.sequence=*/ 1})); + ON_CALL(m_interface, xcb_intern_atom_reply(&m_connection, Field(&xcb_intern_atom_cookie_t::sequence, Eq(1)), _)) + .WillByDefault(ReturnMalloc( + /*response_type=*/(uint8_t)XCB_INTERN_ATOM, + /*pad0=*/(uint8_t)0, + /*sequence=*/(uint16_t)1, + /*length=*/0u, + /*xcb_atom_t=*/s_netActiveWindowAtom + )); + ON_CALL(m_interface, xcb_get_property(&m_connection, 0, s_rootWindow, s_netActiveWindowAtom, XCB_ATOM_WINDOW, 0, 1)) + .WillByDefault(Return(xcb_get_property_cookie_t{/*.sequence=*/ s_getActiveWindowPropertySequence})); + ON_CALL(m_interface, xcb_get_property_reply(&m_connection, Field(&xcb_get_property_cookie_t::sequence, Eq(s_getActiveWindowPropertySequence)), _)) + .WillByDefault(ReturnMalloc( + /*response_type=*/(uint8_t)XCB_GET_PROPERTY, + /*format=*/(uint8_t)0, + /*sequence=*/(uint16_t)s_getActiveWindowPropertySequence, + /*length=*/0u, + /*type=*/XCB_ATOM_WINDOW, + /*bytes_after=*/0u, + /*value_len=*/1u + )); + ON_CALL(m_interface, xcb_get_property_value(Field(&xcb_get_property_reply_t::sequence, Eq(s_getActiveWindowPropertySequence)))) + .WillByDefault(Return(const_cast(&s_nullWindow))); + + ON_CALL(m_interface, xcb_get_geometry(&m_connection, _)) + .WillByDefault(Return(xcb_get_geometry_cookie_t{/*.sequence=*/1})); + ON_CALL(m_interface, xcb_get_geometry_reply(&m_connection, Field(&xcb_get_geometry_cookie_t::sequence, Eq(1)), _)) + .WillByDefault(ReturnMalloc(s_defaultWindowGeometry)); } void PumpApplication() @@ -73,10 +107,13 @@ namespace AzFramework protected: static constexpr inline uint8_t s_xinputMajorOpcode = 131; static constexpr inline xcb_window_t s_rootWindow = 1; + static constexpr inline xcb_window_t s_nullWindow = XCB_WINDOW_NONE; static constexpr inline xcb_input_device_id_t s_virtualCorePointerId = 2; static constexpr inline xcb_input_device_id_t s_physicalPointerDeviceId = 3; static constexpr inline uint16_t s_screenWidthInPixels = 3840; static constexpr inline uint16_t s_screenHeightInPixels = 2160; + static constexpr inline uint16_t s_getActiveWindowPropertySequence = 2160; + static constexpr inline xcb_atom_t s_netActiveWindowAtom = 1; static constexpr inline xcb_setup_t s_xcbSetup{ /*.status=*/1, /*.pad0=*/0, @@ -109,6 +146,19 @@ namespace AzFramework /*.present=*/1, /*.major_opcode=*/s_xinputMajorOpcode, }; + static constexpr inline xcb_get_geometry_reply_t s_defaultWindowGeometry{ + /*.response_type=*/XCB_GET_GEOMETRY, + /*.depth=*/0, + /*.sequence=*/1, + /*.length=*/0, + /*.root=*/s_rootWindow, + /*.x=*/100, + /*.y=*/100, + /*.width=*/100, + /*.height=*/100, + /*.border_width=*/3, + /*.pad0[2]=*/{}, + }; XcbTestApplication m_application{ /*enabledGamepadsCount=*/0, /*keyboardEnabled=*/false, @@ -398,4 +448,98 @@ namespace AzFramework EXPECT_THAT(xMotionChannel->GetValue(), FloatEq(0.0f)); EXPECT_THAT(yMotionChannel->GetValue(), FloatEq(0.0f)); } + + struct GetCursorPositionParam + { + int16_t m_x; + int16_t m_y; + }; + + class XcbGetSystemCursorPositionTests + : public XcbInputDeviceMouseTests + , public testing::WithParamInterface + { + }; + + TEST_P(XcbGetSystemCursorPositionTests, GetSystemCursorPositionNormalizedReturnsCorrectValue) + { + using testing::Eq; + using testing::Field; + using testing::Return; + using testing::_; + + xcb_window_t focusWindow = 42; + const xcb_query_pointer_reply_t queryPointerReply{ + /*.response_type=*/XCB_QUERY_POINTER, + /*.same_screen=*/1, + /*.sequence=*/0, + /*.length=*/1, + /*.root=*/s_rootWindow, + /*.child=*/focusWindow, + /*.root_x=*/static_cast(GetParam().m_x + s_defaultWindowGeometry.x), + /*.root_y=*/static_cast(GetParam().m_y + s_defaultWindowGeometry.y), + /*.win_x=*/GetParam().m_x, + /*.win_y=*/GetParam().m_y, + /*.mask=*/{}, + /*.pad0[2]=*/{}, + }; + + // Querying the root window's pointer gives its absolute value + const xcb_query_pointer_reply_t rootWindowQueryPointerReply{ + /*.response_type=*/XCB_QUERY_POINTER, + /*.same_screen=*/1, + /*.sequence=*/0, + /*.length=*/1, + /*.root=*/s_rootWindow, + /*.child=*/s_rootWindow, + /*.root_x=*/static_cast(GetParam().m_x + s_defaultWindowGeometry.x), + /*.root_y=*/static_cast(GetParam().m_y + s_defaultWindowGeometry.y), + /*.win_x=*/static_cast(GetParam().m_x + s_defaultWindowGeometry.x), + /*.win_y=*/static_cast(GetParam().m_y + s_defaultWindowGeometry.y), + /*.mask=*/{}, + /*.pad0[2]=*/{}, + }; + + EXPECT_CALL(m_interface, xcb_get_property_value(Field(&xcb_get_property_reply_t::sequence, Eq(s_getActiveWindowPropertySequence)))) + .WillRepeatedly(Return(&focusWindow)); + + EXPECT_CALL(m_interface, xcb_query_pointer(&m_connection, focusWindow)) + .WillRepeatedly(Return(xcb_query_pointer_cookie_t{/*.sequence=*/1})); + EXPECT_CALL(m_interface, xcb_query_pointer_reply(&m_connection, Field(&xcb_query_pointer_cookie_t::sequence, 1), _)) + .WillRepeatedly(ReturnMalloc(queryPointerReply)); + + EXPECT_CALL(m_interface, xcb_query_pointer(&m_connection, s_rootWindow)) + .WillRepeatedly(Return(xcb_query_pointer_cookie_t{/*.sequence=*/2})); + EXPECT_CALL(m_interface, xcb_query_pointer_reply(&m_connection, Field(&xcb_query_pointer_cookie_t::sequence, 2), _)) + .WillRepeatedly(ReturnMalloc(rootWindowQueryPointerReply)); + + m_application.Start(); + InputSystemCursorRequestBus::Event( + InputDeviceMouse::Id, + &InputSystemCursorRequests::SetSystemCursorState, + SystemCursorState::ConstrainedAndHidden); + + AZ::Vector2 systemCursorPositionNormalized = AZ::Vector2::CreateZero(); + InputSystemCursorRequestBus::EventResult( + systemCursorPositionNormalized, + InputDeviceMouse::Id, + &InputSystemCursorRequests::GetSystemCursorPositionNormalized); + + EXPECT_THAT(systemCursorPositionNormalized, ::testing::AllOf( + testing::Property(&AZ::Vector2::GetX, testing::FloatEq(static_cast(GetParam().m_x) / s_defaultWindowGeometry.width)), + testing::Property(&AZ::Vector2::GetY, testing::FloatEq(static_cast(GetParam().m_y) / s_defaultWindowGeometry.height)) + )); + } + + INSTANTIATE_TEST_CASE_P( + AllPointerPositions, + XcbGetSystemCursorPositionTests, + testing::Values( + // Default mocked window geometry sets width and height to 100, all + // parameter values should be within [0, 100) + GetCursorPositionParam{ 50, 50 }, + GetCursorPositionParam{ 25, 25 }, + GetCursorPositionParam{ 0, 100 } + ) + ); } // namespace AzFramework From ce713fad5e2928f0a84647c9a2aae3ff94592beb Mon Sep 17 00:00:00 2001 From: Adi Bar-Lev <82479970+Adi-Amazon@users.noreply.github.com> Date: Wed, 3 Nov 2021 20:59:05 -0400 Subject: [PATCH 049/177] Hair - crucial optimization and bug fix: - Back light correction. This fix will block TT lobe (back lobe) from allowing light transfer - By doing this we remove the requirement to add self shadowing in most cases, hence removing heavy render pass. Exception: - Thin hair will still pass light and therefor there is still a need to read depth buffer and compare as a second step to avoid adding heavy shadowing pass / comparison. Signed-off-by: Adi Bar-Lev <82479970+Adi-Amazon@users.noreply.github.com> --- .../Assets/Passes/HairParentShortCutPass.pass | 9 ++++++++- .../Assets/Passes/HairShortCutGeometryShading.pass | 6 ++++++ .../Assets/Shaders/HairRenderingFillPPLL.azsl | 3 --- .../Assets/Shaders/HairShortCutGeometryShading.azsl | 11 ++++++++--- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass b/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass index 83f9f0d432..d8389a3da8 100644 --- a/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass +++ b/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass @@ -264,7 +264,7 @@ "Attachment": "HairColorRenderTarget" } }, - { + { // The final render target - this is MSAA mode RT - would it be cheaper to // use non-MSAA and then copy? "LocalSlot": "RenderTargetInputOutput", @@ -280,6 +280,13 @@ "Attachment": "DepthLinearInput" } }, + { + "LocalSlot": "AccumulatedInverseAlpha", + "AttachmentRef": { + "Pass": "HairShortCutGeometryDepthAlphaPass", + "Attachment": "InverseAlphaRTOutput" + } + }, { "LocalSlot": "Depth", "AttachmentRef": { diff --git a/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryShading.pass b/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryShading.pass index 5940f8c549..53fa2b358b 100644 --- a/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryShading.pass +++ b/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryShading.pass @@ -32,6 +32,12 @@ "SlotType": "Input", "ScopeAttachmentUsage": "Shader" }, + { // Used as the thickness accumulation to block TT (back) lobe lighting + "Name": "AccumulatedInverseAlpha", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ShaderInputName": "m_accumInvAlpha" + }, { // For comparing the depth to early disqualify but not to write "Name": "Depth", "SlotType": "Input", diff --git a/Gems/AtomTressFX/Assets/Shaders/HairRenderingFillPPLL.azsl b/Gems/AtomTressFX/Assets/Shaders/HairRenderingFillPPLL.azsl index 02777435f5..8dcd1ed372 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairRenderingFillPPLL.azsl +++ b/Gems/AtomTressFX/Assets/Shaders/HairRenderingFillPPLL.azsl @@ -51,9 +51,6 @@ ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback RWTexture2D m_fragmentListHead; RWStructuredBuffer m_linkedListNodes; RWBuffer m_linkedListCounter; - - // Linear depth is used for getting the screen to world transform - Texture2D m_linearDepth; } //------------------------------------------------------------------------------ diff --git a/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.azsl b/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.azsl index c2a2958dfe..2a3e00b1e7 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.azsl +++ b/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.azsl @@ -52,6 +52,9 @@ ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback //! Originally in TressFXRendering.hlsl this is space 0 HairObjectShadeParams m_hairParams[AMD_TRESSFX_MAX_HAIR_GROUP_RENDER]; + // Will be used as thickness indication to block TT (back) lobe + Texture2D m_accumInvAlpha; + // Linear depth is used for getting the screen to world transform Texture2D m_linearDepth; @@ -164,9 +167,11 @@ float4 HairShortCutGeometryColorPS(PS_INPUT_HAIR input) : SV_Target float2 pixelCoord = input.Position.xy; float depth = input.Position.z; - // [To Do] - the thickness will need to be corrected somehow since this technique doesn't - // keeps track of the accumulated alpha / thickness - float thickness = alpha; + + // The following is a quick correction to remove the TT lobe (back lobe) contribution in case + // the hair is thick. We do that by accumulating alpha from the hair for the blend operation + // and this can be used here as an indication of thickness. + float thickness = saturate(1.0 - PassSrg::m_accumInvAlpha[int2(pixelCoord)]); float3 shadedFragment = TressFXShading(pixelCoord, depth, input.Tangent.xyz, strandColor.rgb, thickness, RenderParamsIndex); // Color channel: Pre-multiply with alpha to create non-normalized weighted sum. From 8a3d055f8b7c4654dcb4285026f5b9d089d407d8 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Wed, 3 Nov 2021 19:34:10 -0700 Subject: [PATCH 050/177] Some cleanup around handling of migrations to simplify interfaces and add additional hooks for functionality Signed-off-by: kberg-amzn --- .../Multiplayer/EntityDomains/IEntityDomain.h | 12 +- .../NetworkEntity/INetworkEntityManager.h | 23 ++- .../Source/Components/NetBindComponent.cpp | 4 +- .../FullOwnershipEntityDomain.cpp | 9 +- .../EntityDomains/FullOwnershipEntityDomain.h | 6 +- .../Source/EntityDomains/NullEntityDomain.cpp | 41 +++++ .../Source/EntityDomains/NullEntityDomain.h | 31 ++++ .../Source/MultiplayerSystemComponent.cpp | 10 +- .../NetworkEntityAuthorityTracker.cpp | 21 +-- .../NetworkEntityAuthorityTracker.h | 3 + .../NetworkEntity/NetworkEntityManager.cpp | 140 +++++++++--------- .../NetworkEntity/NetworkEntityManager.h | 8 +- Gems/Multiplayer/Code/multiplayer_files.cmake | 2 + 13 files changed, 194 insertions(+), 116 deletions(-) create mode 100644 Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.cpp create mode 100644 Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.h diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/EntityDomains/IEntityDomain.h b/Gems/Multiplayer/Code/Include/Multiplayer/EntityDomains/IEntityDomain.h index fc41a9b4d0..4b1bbbdba8 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/EntityDomains/IEntityDomain.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/EntityDomains/IEntityDomain.h @@ -17,8 +17,6 @@ namespace Multiplayer class IEntityDomain { public: - using EntitiesNotInDomain = AZStd::unordered_set; - virtual ~IEntityDomain() = default; //! For domains that operate on a region of space, this sets the area the domain is responsible for. @@ -34,12 +32,10 @@ namespace Multiplayer //! @return false if this entity should not belong to the entity manger, true if it could be owned by the entity manager virtual bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const = 0; - //! Enable Entity Domain Exit Tracking for entities on the host. - //! @param ownedEntitySet the set of entities to activate tracking for - virtual void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) = 0; - - //! Return the set of netbound entities not included in this domain. - virtual const EntitiesNotInDomain& RetrieveEntitiesNotInDomain() const = 0; + //! This method will be invoked whenever we unexpectedly lose the authoritative entity replicator for an entity. + //! This gives our entity domain a chance to determine whether or not it should assume authority in this instance. + //! @param entityHandle the network entity handle of the entity that has lost it's authoritative replicator + virtual void HandleLossOfAuthoritativeReplicator(const ConstNetworkEntityHandle& entityHandle) = 0; //! Debug draw to visualize host entity domains. virtual void DebugDraw() const = 0; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h index 43915127ed..8c16176736 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h @@ -26,6 +26,7 @@ namespace Multiplayer using EntityExitDomainEvent = AZ::Event; using ControllersActivatedEvent = AZ::Event; using ControllersDeactivatedEvent = AZ::Event; + using NetEntityIdSet = AZStd::unordered_set; //! @class INetworkEntityManager //! @brief The interface for managing all networked entities. @@ -34,18 +35,17 @@ namespace Multiplayer public: AZ_RTTI(INetworkEntityManager, "{109759DE-9492-439C-A0B1-AE46E6FD029C}"); - using OwnedEntitySet = AZStd::unordered_set; using EntityList = AZStd::vector; virtual ~INetworkEntityManager() = default; - //! Configures the NetworkEntityManager to operate as an authoritative host. - //! @param hostId the hostId of this NetworkEntityManager + //! Configures the NetworkEntityManager. + //! @param hostId the hostId of this NetworkEntityManager (invalid for clients) //! @param entityDomain the entity domain used to determine which entities this manager has authority over virtual void Initialize(const HostId& hostId, AZStd::unique_ptr entityDomain) = 0; - //! Returns whether or not the network entity manager has been initialized to host. - //! @return boolean true if this network entity manager has been intialized to host + //! Returns whether or not the network entity manager has been initialized. + //! @return boolean true if this network entity manager has been intialized virtual bool IsInitialized() const = 0; //! Returns the entity domain associated with this network entity manager, this will be nullptr on clients. @@ -181,6 +181,19 @@ namespace Multiplayer //! @param entityRpcMessage the local rpc message to handle virtual void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) = 0; + //! Handles a set of entities transitioning between entity domains. + //! @param entitiesNotInDomain the set of entities that are no longer contained within our entity domain + virtual void HandleEntitiesExitDomain(const NetEntityIdSet& entitiesNotInDomain) = 0; + + //! Forcibly assumes authoritative control over the given entity. + //! This should only be used in the event of the unexpected loss of the previous authority, any other usage could corrupt the simulation. + //! @param entityHandle the entity to forcibly assume authoritative control over + virtual void ForceAssumeAuthority(const ConstNetworkEntityHandle& entityHandle) = 0; + + //! Overrides the default timeout time used during entity migrations. + //! @param timeoutTimeMs the timeout time to use in milliseconds + virtual void SetMigrateTimeoutTimeMs(AZ::TimeMs timeoutTimeMs) = 0; + //! Visualization of network entity manager state. virtual void DebugDraw() const = 0; }; diff --git a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp index cc71000d33..ceb9412408 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp @@ -317,7 +317,7 @@ namespace Multiplayer return false; } - bool NetBindComponent::HandlePropertyChangeMessage([[maybe_unused]] AzNetworking::ISerializer& serializer, [[maybe_unused]] bool notifyChanges) + bool NetBindComponent::HandlePropertyChangeMessage(AzNetworking::ISerializer& serializer, bool notifyChanges) { const NetEntityRole netEntityRole = m_netEntityRole; ReplicationRecord replicationRecord(netEntityRole); @@ -492,7 +492,7 @@ namespace Multiplayer void NetBindComponent::FillTotalReplicationRecord(ReplicationRecord& replicationRecord) const { replicationRecord.Append(m_totalRecord); - // if we have any outstanding changes yet to be logged, grab those as well + // If we have any outstanding changes yet to be logged, grab those as well if (m_currentRecord.HasChanges()) { replicationRecord.Append(m_currentRecord); diff --git a/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.cpp b/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.cpp index 9d53990fb4..59e2afc638 100644 --- a/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.cpp +++ b/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.cpp @@ -26,14 +26,9 @@ namespace Multiplayer return true; } - void FullOwnershipEntityDomain::ActivateTracking([[maybe_unused]] const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) + void FullOwnershipEntityDomain::HandleLossOfAuthoritativeReplicator([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) { - ; - } - - const IEntityDomain::EntitiesNotInDomain& FullOwnershipEntityDomain::RetrieveEntitiesNotInDomain() const - { - return m_entitiesNotInDomain; + AZ_Assert(false, "FullOwnershipEntityDomain has authoritative control over all entities, something unexpected has happened"); } void FullOwnershipEntityDomain::DebugDraw() const diff --git a/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.h b/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.h index ae80c16ab8..203d9579ea 100644 --- a/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.h +++ b/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.h @@ -24,12 +24,8 @@ namespace Multiplayer void SetAabb(const AZ::Aabb& aabb) override; const AZ::Aabb& GetAabb() const override; bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const override; - void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) override; - const EntitiesNotInDomain& RetrieveEntitiesNotInDomain() const override; + void HandleLossOfAuthoritativeReplicator(const ConstNetworkEntityHandle& entityHandle) override; void DebugDraw() const override; //! @} - - private: - EntitiesNotInDomain m_entitiesNotInDomain; }; } diff --git a/Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.cpp b/Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.cpp new file mode 100644 index 0000000000..21d6abcb5a --- /dev/null +++ b/Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +namespace Multiplayer +{ + void NullEntityDomain::SetAabb([[maybe_unused]] const AZ::Aabb& aabb) + { + ; // Do nothing, by definition we own everything + } + + const AZ::Aabb& NullEntityDomain::GetAabb() const + { + static AZ::Aabb nullAabb = AZ::Aabb::CreateNull(); + return nullAabb; + } + + bool NullEntityDomain::IsInDomain([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) const + { + return false; + } + + void NullEntityDomain::HandleLossOfAuthoritativeReplicator([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) + { + AZLOG_ERROR("Timed out entity id %llu during migration, marking for removal", aznumeric_cast(entityHandle.GetNetEntityId())); + GetNetworkEntityManager()->MarkForRemoval(entityHandle); + } + + void NullEntityDomain::DebugDraw() const + { + ; + } +} diff --git a/Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.h b/Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.h new file mode 100644 index 0000000000..247d82b366 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +namespace Multiplayer +{ + class NullEntityDomain + : public IEntityDomain + { + public: + NullEntityDomain() = default; + NullEntityDomain(const NullEntityDomain& rhs) = default; + + //! IEntityDomain overrides. + //! @{ + void SetAabb(const AZ::Aabb& aabb) override; + const AZ::Aabb& GetAabb() const override; + bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const override; + void HandleLossOfAuthoritativeReplicator(const ConstNetworkEntityHandle& entityHandle) override; + void DebugDraw() const override; + //! @} + }; +} diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 0bf5cea64b..1bc3404292 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -832,18 +833,21 @@ namespace Multiplayer if (multiplayerType == MultiplayerAgentType::ClientServer || multiplayerType == MultiplayerAgentType::DedicatedServer) { m_spawnNetboundEntities = true; - m_initEvent.Signal(m_networkInterface); - + m_initEvent.Signal(m_networkInterface); //< Note! This might initialize our network entity manager for us if (!m_networkEntityManager.IsInitialized()) { - // Set up a full ownership domain if we didn't construct a domain during the initialize event const AZ::CVarFixedString serverAddr = cl_serveraddr; const uint16_t serverPort = cl_serverport; const AzNetworking::ProtocolType serverProtocol = sv_protocol; const AzNetworking::IpAddress hostId = AzNetworking::IpAddress(serverAddr.c_str(), serverPort, serverProtocol); + // Set up a full ownership domain if we didn't construct a domain during the initialize event m_networkEntityManager.Initialize(hostId, AZStd::make_unique()); } } + else if (multiplayerType == MultiplayerAgentType::Client) + { + m_networkEntityManager.Initialize(AzNetworking::IpAddress(), AZStd::make_unique()); + } } m_agentType = multiplayerType; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp index 7b6e8476e7..9f66bbee9e 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,14 +18,20 @@ namespace Multiplayer { - AZ_CVAR(AZ::TimeMs, net_EntityMigrationTimeoutMs, AZ::TimeMs{ 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time to wait for a new authority to attach to an entity before we delete the entity"); + AZ_CVAR(AZ::TimeMs, net_DefaultEntityMigrationTimeoutMs, AZ::TimeMs{ 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time to wait for a new authority to attach to an entity before we delete the entity"); NetworkEntityAuthorityTracker::NetworkEntityAuthorityTracker(INetworkEntityManager& networkEntityManager) : m_networkEntityManager(networkEntityManager) + , m_timeoutTimeMs(net_DefaultEntityMigrationTimeoutMs) { ; } + void NetworkEntityAuthorityTracker::SetTimeoutTimeMs(AZ::TimeMs timeoutTimeMs) + { + m_timeoutTimeMs = timeoutTimeMs; + } + bool NetworkEntityAuthorityTracker::AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& newOwner) { bool ret = false; @@ -92,7 +99,7 @@ namespace Multiplayer "Trying to add something twice to the timeout map, this is unexpected" ); m_timeoutDataMap.insert(entityHandle.GetNetEntityId()); - AZ::Interface::Get()->AddCallback([this, netEntityId = entityHandle.GetNetEntityId(), previousOwner] + AZ::Interface::Get()->AddCallback([this, netEntityId = entityHandle.GetNetEntityId()] { auto timeoutData = m_timeoutDataMap.find(netEntityId); if (timeoutData != m_timeoutDataMap.end()) @@ -109,19 +116,13 @@ namespace Multiplayer } if (networkRole != NetEntityRole::Authority) { - AZLOG_ERROR - ( - "Timed out entity id %llu during migration previous owner %s, removing it", - aznumeric_cast(entityHandle.GetNetEntityId()), - previousOwner.GetString().c_str() - ); - m_networkEntityManager.MarkForRemoval(entityHandle); + m_networkEntityManager.GetEntityDomain()->HandleLossOfAuthoritativeReplicator(entityHandle); } } } }, AZ::Name("Entity authority removal functor"), - net_EntityMigrationTimeoutMs + m_timeoutTimeMs ); } else diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h index c2e330ab4d..edb1b26ca8 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h @@ -23,6 +23,7 @@ namespace Multiplayer public: NetworkEntityAuthorityTracker(INetworkEntityManager& networkEntityManager); + void SetTimeoutTimeMs(AZ::TimeMs timeoutTimeMs); bool DoesEntityHaveOwner(ConstNetworkEntityHandle entityHandle) const; bool AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& newOwner); void RemoveEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner); @@ -37,5 +38,7 @@ namespace Multiplayer TimeoutDataMap m_timeoutDataMap; EntityAuthorityMap m_entityAuthorityMap; INetworkEntityManager& m_networkEntityManager; + + AZ::TimeMs m_timeoutTimeMs = AZ::TimeMs{ 0 }; }; } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index c7582af83f..b178ebeb02 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -28,12 +28,10 @@ namespace Multiplayer { AZ_CVAR(bool, net_DebugCheckNetworkEntityManager, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables extra debug checks inside the NetworkEntityManager"); - AZ_CVAR(AZ::TimeMs, net_EntityDomainUpdateMs, AZ::TimeMs{ 500 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Frequency for updating the entity domain in ms"); NetworkEntityManager::NetworkEntityManager() : m_networkEntityAuthorityTracker(*this) , m_removeEntitiesEvent([this] { RemoveEntities(); }, AZ::Name("NetworkEntityManager remove entities event")) - , m_updateEntityDomainEvent([this] { UpdateEntityDomain(); }, AZ::Name("NetworkEntityManager update entity domain event")) { AZ::Interface::Register(this); AzFramework::RootSpawnableNotificationBus::Handler::BusConnect(); @@ -63,8 +61,6 @@ namespace Multiplayer } m_entityDomain = AZStd::move(entityDomain); - m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true); - m_entityDomain->ActivateTracking(m_ownedEntities); } bool NetworkEntityManager::IsInitialized() const @@ -231,6 +227,74 @@ namespace Multiplayer m_localDeferredRpcMessages.emplace_back(AZStd::move(message)); } + void NetworkEntityManager::HandleEntitiesExitDomain(const NetEntityIdSet& entitiesNotInDomain) + { + for (NetEntityId exitingId : entitiesNotInDomain) + { + bool safeToExit = true; + NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(exitingId); + + // We need special handling for the NetworkHierarchy as well, since related entities need to be migrated together + NetworkHierarchyRootComponentController* hierarchyRootController = entityHandle.FindController(); + NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController(); + + // Find the root entity + AZ::Entity* hierarchyRootEntity = nullptr; + if (hierarchyRootController) + { + hierarchyRootEntity = hierarchyRootController->GetParent().GetHierarchicalRoot(); + } + else if (hierarchyChildController) + { + hierarchyRootEntity = hierarchyChildController->GetParent().GetHierarchicalRoot(); + } + + if (hierarchyRootEntity) + { + NetEntityId rootNetId = GetNetEntityIdById(hierarchyRootEntity->GetId()); + ConstNetworkEntityHandle rootEntityHandle = GetEntity(rootNetId); + + // Check if the root entity is still tracked by this authority + if (rootEntityHandle.Exists() && rootEntityHandle.GetNetBindComponent()->HasController()) + { + safeToExit = false; + } + } + + // Validate that we aren't already planning to remove this entity + if (safeToExit) + { + for (auto remoteEntityId : m_removeList) + { + if (remoteEntityId == remoteEntityId) + { + safeToExit = false; + } + } + } + + if (safeToExit) + { + // Tell all the attached replicators for this entity that it's exited the domain + m_entityExitDomainEvent.Signal(entityHandle); + } + } + } + + void NetworkEntityManager::ForceAssumeAuthority(const ConstNetworkEntityHandle& entityHandle) + { + NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent(); + if (netBindComponent != nullptr) + { + netBindComponent->ConstructControllers(); + } + } + + void NetworkEntityManager::SetMigrateTimeoutTimeMs(AZ::TimeMs timeoutTimeMs) + { + m_networkEntityAuthorityTracker.SetTimeoutTimeMs(timeoutTimeMs); + } + void NetworkEntityManager::DebugDraw() const { AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; @@ -243,7 +307,7 @@ namespace Multiplayer NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity); AZ::Aabb entityBounds = AZ::Interface::Get()->GetEntityWorldBoundsUnion(entity->GetId()); entityBounds.Expand(AZ::Vector3(0.01f)); - if (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority) + if ((netBindComponent != nullptr) && netBindComponent->GetNetEntityRole() == NetEntityRole::Authority) { debugDisplay->SetColor(AZ::Colors::Black); debugDisplay->SetAlpha(0.5f); @@ -277,77 +341,11 @@ namespace Multiplayer m_localDeferredRpcMessages.clear(); } - void NetworkEntityManager::UpdateEntityDomain() - { - if (m_entityDomain == nullptr) - { - return; - } - - const IEntityDomain::EntitiesNotInDomain& entitiesNotInDomain = m_entityDomain->RetrieveEntitiesNotInDomain(); - for (NetEntityId exitingId : entitiesNotInDomain) - { - OnEntityExitDomain(exitingId); - } - } - - void NetworkEntityManager::OnEntityExitDomain(NetEntityId entityId) - { - bool safeToExit = true; - NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(entityId); - - // We also need special handling for the NetworkHierarchy as well, since related entities need to be migrated together - NetworkHierarchyRootComponentController* hierarchyRootController = entityHandle.FindController(); - NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController(); - - // Find the root entity - AZ::Entity* hierarchyRootEntity = nullptr; - if (hierarchyRootController) - { - hierarchyRootEntity = hierarchyRootController->GetParent().GetHierarchicalRoot(); - } - else if (hierarchyChildController) - { - hierarchyRootEntity = hierarchyChildController->GetParent().GetHierarchicalRoot(); - } - - if (hierarchyRootEntity) - { - NetEntityId rootNetId = GetNetEntityIdById(hierarchyRootEntity->GetId()); - ConstNetworkEntityHandle rootEntityHandle = GetEntity(rootNetId); - - // Check if the root entity is still tracked by this authority - if (rootEntityHandle.Exists() && rootEntityHandle.GetNetBindComponent()->HasController()) - { - safeToExit = false; - } - } - - // Validate that we aren't already planning to remove this entity - if (safeToExit) - { - for (auto remoteEntityId : m_removeList) - { - if (remoteEntityId == remoteEntityId) - { - safeToExit = false; - } - } - } - - if (safeToExit) - { - m_entityExitDomainEvent.Signal(entityHandle); - } - } - void NetworkEntityManager::Reset() { m_multiplayerComponentRegistry.Reset(); m_removeList.clear(); m_entityDomain = nullptr; - m_updateEntityDomainEvent.RemoveFromQueue(); - m_ownedEntities.clear(); m_entityExitDomainEvent.DisconnectAllHandlers(); m_onEntityMarkedDirty.DisconnectAllHandlers(); m_onEntityNotifyChanges.DisconnectAllHandlers(); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h index 133c35dce0..7c6fdd94f9 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h @@ -79,12 +79,13 @@ namespace Multiplayer void NotifyControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override; void NotifyControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override; void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) override; + void HandleEntitiesExitDomain(const NetEntityIdSet& entitiesNotInDomain) override; + void ForceAssumeAuthority(const ConstNetworkEntityHandle& entityHandle) override; + void SetMigrateTimeoutTimeMs(AZ::TimeMs timeoutTimeMs) override; void DebugDraw() const override; //! @} void DispatchLocalDeferredRpcMessages(); - void UpdateEntityDomain(); - void OnEntityExitDomain(NetEntityId entityId); //! RootSpawnableNotificationBus //! @{ @@ -106,9 +107,6 @@ namespace Multiplayer AZ::ScheduledEvent m_removeEntitiesEvent; AZStd::vector m_removeList; AZStd::unique_ptr m_entityDomain; - AZ::ScheduledEvent m_updateEntityDomainEvent; - - OwnedEntitySet m_ownedEntities; EntityExitDomainEvent m_entityExitDomainEvent; AZ::Event<> m_onEntityMarkedDirty; diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index a799278203..d417304877 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -94,6 +94,8 @@ set(FILES Source/Editor/MultiplayerEditorConnection.h Source/EntityDomains/FullOwnershipEntityDomain.cpp Source/EntityDomains/FullOwnershipEntityDomain.h + Source/EntityDomains/NullEntityDomain.cpp + Source/EntityDomains/NullEntityDomain.h Source/MultiplayerStats.cpp Source/MultiplayerSystemComponent.cpp Source/MultiplayerSystemComponent.h From 7e65104155a539fb1999e09862928b95641f9071 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Wed, 3 Nov 2021 19:40:20 -0700 Subject: [PATCH 051/177] Addressing PR feedback Signed-off-by: kberg-amzn --- .../AzNetworking/UdpTransport/UdpConnection.h | 2 +- .../UdpTransport/UdpNetworkInterface.cpp | 2 +- .../NetworkEntityAuthorityTracker.cpp | 16 ++++++++-------- .../NetworkEntityAuthorityTracker.h | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h index c728016239..ddd75c9946 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h @@ -148,7 +148,7 @@ namespace AzNetworking uint32_t m_connectionMtu = MaxUdpTransmissionUnit; TimeoutId m_timeoutId; - int32_t m_timeoutCounter = 0; + uint32_t m_timeoutCounter = 0; }; } diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp index 280b749d9e..b810c7a347 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp @@ -31,7 +31,7 @@ namespace AzNetworking AZ_CVAR(bool, net_UdpTimeoutConnections, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Boolean value on whether we should timeout Udp connections"); AZ_CVAR(AZ::TimeMs, net_UdpPacketTimeSliceMs, AZ::TimeMs{ 8 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The number of milliseconds to allow for packet processing"); - AZ_CVAR(int32_t, net_UdpUnackedHeartbeats, 5, nullptr, AZ::ConsoleFunctorFlags::Null, "The number of heartbeats to attempt to send to keep a connection alive before giving up"); + AZ_CVAR(uint32_t, net_UdpUnackedHeartbeats, 5, nullptr, AZ::ConsoleFunctorFlags::Null, "The number of heartbeats to attempt to send to keep a connection alive before giving up"); AZ_CVAR(AZ::TimeMs, net_UdpDefaultTimeoutMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Udp connection"); AZ_CVAR(AZ::TimeMs, net_MinPacketTimeoutMs, AZ::TimeMs{ 200 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Minimum time to wait before timing out an unacked packet"); AZ_CVAR(int32_t, net_MaxTimeoutsPerFrame, 1000, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of packet timeouts to allow to process in a single frame"); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp index 9f66bbee9e..b401b85a27 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp @@ -35,8 +35,8 @@ namespace Multiplayer bool NetworkEntityAuthorityTracker::AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& newOwner) { bool ret = false; - auto timeoutData = m_timeoutDataMap.find(entityHandle.GetNetEntityId()); - if (timeoutData != m_timeoutDataMap.end()) + auto timeoutData = m_timedOutNetEntityIds.find(entityHandle.GetNetEntityId()); + if (timeoutData != m_timedOutNetEntityIds.end()) { AZLOG ( @@ -45,7 +45,7 @@ namespace Multiplayer aznumeric_cast(entityHandle.GetNetEntityId()), newOwner.GetString().c_str() ); - m_timeoutDataMap.erase(timeoutData); + m_timedOutNetEntityIds.erase(timeoutData); ret = true; } @@ -95,16 +95,16 @@ namespace Multiplayer { AZ_Assert ( - m_timeoutDataMap.find(entityHandle.GetNetEntityId()) == m_timeoutDataMap.end(), + m_timedOutNetEntityIds.find(entityHandle.GetNetEntityId()) == m_timedOutNetEntityIds.end(), "Trying to add something twice to the timeout map, this is unexpected" ); - m_timeoutDataMap.insert(entityHandle.GetNetEntityId()); + m_timedOutNetEntityIds.insert(entityHandle.GetNetEntityId()); AZ::Interface::Get()->AddCallback([this, netEntityId = entityHandle.GetNetEntityId()] { - auto timeoutData = m_timeoutDataMap.find(netEntityId); - if (timeoutData != m_timeoutDataMap.end()) + auto timeoutData = m_timedOutNetEntityIds.find(netEntityId); + if (timeoutData != m_timedOutNetEntityIds.end()) { - m_timeoutDataMap.erase(timeoutData); + m_timedOutNetEntityIds.erase(timeoutData); ConstNetworkEntityHandle entityHandle = m_networkEntityManager.GetEntity(netEntityId); if (auto entity = entityHandle.GetEntity()) { diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h index edb1b26ca8..ae9aac04ea 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace Multiplayer { @@ -32,10 +33,9 @@ namespace Multiplayer private: NetworkEntityAuthorityTracker& operator= (const NetworkEntityAuthorityTracker&) = delete; - using TimeoutDataMap = AZStd::unordered_set; using EntityAuthorityMap = AZStd::unordered_map>; - TimeoutDataMap m_timeoutDataMap; + NetEntityIdSet m_timedOutNetEntityIds; EntityAuthorityMap m_entityAuthorityMap; INetworkEntityManager& m_networkEntityManager; From 4c41a4dfc912beec7f9746cdc74b1621977ef2df Mon Sep 17 00:00:00 2001 From: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> Date: Thu, 4 Nov 2021 09:16:27 +0000 Subject: [PATCH 052/177] Ensure ImGui menu is displayed when the Viewport UI viewport border is showing (#5240) * add optimize off code temporarily Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * ensure the imgui menu displays when the viewport border is active Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> --- Code/Editor/EditorViewportWidget.cpp | 26 ++++++++++- Code/Editor/EditorViewportWidget.h | 5 +++ .../AzFramework/Viewport/ViewportBus.h | 40 ++++++++++++++--- .../ViewportUi/ViewportUiDisplay.cpp | 37 +++++++++------- .../ViewportUi/ViewportUiDisplay.h | 2 +- .../ViewportUi/ViewportUiDisplayLayout.h | 17 ++++--- .../Source/LYCommonMenu/ImGuiLYCommonMenu.cpp | 44 ++++++++++++++++--- 7 files changed, 135 insertions(+), 36 deletions(-) diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp index b16758a07e..8e6983a9d7 100644 --- a/Code/Editor/EditorViewportWidget.cpp +++ b/Code/Editor/EditorViewportWidget.cpp @@ -43,10 +43,11 @@ // AzToolsFramework #include +#include +#include #include #include #include -#include // AtomToolsFramework #include @@ -1032,6 +1033,7 @@ void EditorViewportWidget::ConnectViewportInteractionRequestBus() AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusConnect(GetViewportId()); AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusConnect(GetViewportId()); m_viewportUi.ConnectViewportUiBus(GetViewportId()); + AzFramework::ViewportBorderRequestBus::Handler::BusConnect(GetViewportId()); AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusConnect(); } @@ -1040,6 +1042,7 @@ void EditorViewportWidget::DisconnectViewportInteractionRequestBus() { AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusDisconnect(); + AzFramework::ViewportBorderRequestBus::Handler::BusDisconnect(); m_viewportUi.DisconnectViewportUiBus(); AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusDisconnect(); AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusDisconnect(); @@ -2638,4 +2641,25 @@ void EditorViewportWidget::StopFullscreenPreview() // Show the main window MainWindow::instance()->show(); } + +AZStd::optional EditorViewportWidget::GetViewportBorderPadding() const +{ + if (auto viewportEditorModeTracker = AZ::Interface::Get()) + { + auto viewportEditorModes = viewportEditorModeTracker->GetViewportEditorModes({ AzToolsFramework::GetEntityContextId() }); + if (viewportEditorModes->IsModeActive(AzToolsFramework::ViewportEditorMode::Focus) || + viewportEditorModes->IsModeActive(AzToolsFramework::ViewportEditorMode::Component)) + { + AzFramework::ViewportBorderPadding viewportBorderPadding = {}; + viewportBorderPadding.m_top = AzToolsFramework::ViewportUi::ViewportUiTopBorderSize; + viewportBorderPadding.m_left = AzToolsFramework::ViewportUi::ViewportUiLeftRightBottomBorderSize; + viewportBorderPadding.m_right = AzToolsFramework::ViewportUi::ViewportUiLeftRightBottomBorderSize; + viewportBorderPadding.m_bottom = AzToolsFramework::ViewportUi::ViewportUiLeftRightBottomBorderSize; + return viewportBorderPadding; + } + } + + return AZStd::nullopt; +} + #include diff --git a/Code/Editor/EditorViewportWidget.h b/Code/Editor/EditorViewportWidget.h index 68ea48c7f5..01f6068d56 100644 --- a/Code/Editor/EditorViewportWidget.h +++ b/Code/Editor/EditorViewportWidget.h @@ -38,6 +38,7 @@ #include #include +#include // forward declarations. class CBaseObject; @@ -86,6 +87,7 @@ AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING class SANDBOX_API EditorViewportWidget final : public QtViewport + , public AzFramework::ViewportBorderRequestBus::Handler , private IEditorNotifyListener , private IUndoManagerListener , private Camera::EditorCameraRequestBus::Handler @@ -120,6 +122,9 @@ public: void SetFOV(float fov) override; float GetFOV() const override; + // AzFramework::ViewportBorderRequestBus overrides ... + AZStd::optional GetViewportBorderPadding() const override; + private: //////////////////////////////////////////////////////////////////////// // Private types ... diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h index 444173f773..131ecc0c57 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h @@ -8,8 +8,9 @@ #pragma once -#include #include +#include +#include namespace AZ { @@ -20,18 +21,15 @@ namespace AZ namespace AzFramework { - class ViewportRequests - : public AZ::EBusTraits + class ViewportRequests : public AZ::EBusTraits { public: static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; using BusIdType = ViewportId; static void Reflect(AZ::ReflectContext* context); - virtual ~ViewportRequests() {} - //! Gets the current camera's world to view matrix. virtual const AZ::Matrix4x4& GetCameraViewMatrix() const = 0; //! Sets the current camera's world to view matrix. @@ -44,8 +42,36 @@ namespace AzFramework virtual AZ::Transform GetCameraTransform() const = 0; //! Convenience method, sets the camera's world to view matrix from this AZ::Transform. virtual void SetCameraTransform(const AZ::Transform& transform) = 0; + + protected: + ~ViewportRequests() = default; }; using ViewportRequestBus = AZ::EBus; -} //namespace AzFramework + //! The additional padding around the viewport when a viewport border is active. + struct ViewportBorderPadding + { + float m_top; + float m_bottom; + float m_left; + float m_right; + }; + + //! For performing queries about the state of the viewport border. + class ViewportBorderRequests : public AZ::EBusTraits + { + public: + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + using BusIdType = ViewportId; + + //! Returns if a viewport border is in effect and what the current dimensions (padding) of the border are. + virtual AZStd::optional GetViewportBorderPadding() const = 0; + + protected: + ~ViewportBorderRequests() = default; + }; + + using ViewportBorderRequestBus = AZ::EBus; +} // namespace AzFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp index 43e2a6793f..c53aa66d81 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +20,6 @@ namespace AzToolsFramework::ViewportUi::Internal { const static int HighlightBorderSize = 5; - const static int TopHighlightBorderSize = 25; const static char* HighlightBorderColor = "#4A90E2"; static void UnparentWidgets(ViewportUiElementIdInfoLookup& viewportUiElementIdInfoLookup) @@ -61,7 +61,7 @@ namespace AzToolsFramework::ViewportUi::Internal , m_uiOverlay(parent) , m_fullScreenLayout(&m_uiOverlay) , m_uiOverlayLayout() - , m_componentModeBorderText(&m_uiOverlay) + , m_viewportBorderText(&m_uiOverlay) { } @@ -221,11 +221,11 @@ namespace AzToolsFramework::ViewportUi::Internal AZStd::shared_ptr ViewportUiDisplay::GetViewportUiElement(ViewportUiElementId elementId) { - auto element = m_viewportUiElements.find(elementId); - if (element != m_viewportUiElements.end()) + if (auto element = m_viewportUiElements.find(elementId); element != m_viewportUiElements.end()) { return element->second.m_widget; } + return nullptr; } @@ -287,27 +287,30 @@ namespace AzToolsFramework::ViewportUi::Internal { return element.IsValid() && element.m_widget->isVisible(); } + return false; } void ViewportUiDisplay::CreateViewportBorder(const AZStd::string& borderTitle) { const AZStd::string styleSheet = AZStd::string::format( - "border: %dpx solid %s; border-top: %dpx solid %s;", HighlightBorderSize, HighlightBorderColor, TopHighlightBorderSize, + "border: %dpx solid %s; border-top: %dpx solid %s;", HighlightBorderSize, HighlightBorderColor, ViewportUiTopBorderSize, HighlightBorderColor); m_uiOverlay.setStyleSheet(styleSheet.c_str()); m_uiOverlayLayout.setContentsMargins( - HighlightBorderSize + ViewportUiOverlayMargin, TopHighlightBorderSize + ViewportUiOverlayMargin, + HighlightBorderSize + ViewportUiOverlayMargin, ViewportUiTopBorderSize + ViewportUiOverlayMargin, HighlightBorderSize + ViewportUiOverlayMargin, HighlightBorderSize + ViewportUiOverlayMargin); - m_componentModeBorderText.setVisible(true); - m_componentModeBorderText.setText(borderTitle.c_str()); + m_viewportBorderText.setVisible(true); + m_viewportBorderText.setText(borderTitle.c_str()); } void ViewportUiDisplay::RemoveViewportBorder() { - m_componentModeBorderText.setVisible(false); + m_viewportBorderText.setVisible(false); m_uiOverlay.setStyleSheet("border: none;"); - m_uiOverlayLayout.setMargin(ViewportUiOverlayMargin); + m_uiOverlayLayout.setContentsMargins( + ViewportUiOverlayMargin, ViewportUiOverlayMargin + ViewportUiOverlayTopMarginPadding, ViewportUiOverlayMargin, + ViewportUiOverlayMargin); } void ViewportUiDisplay::PositionViewportUiElementFromWorldSpace(ViewportUiElementId elementId, const AZ::Vector3& pos) @@ -359,10 +362,10 @@ namespace AzToolsFramework::ViewportUi::Internal // format the label which will appear on top of the highlight border AZStd::string styleSheet = AZStd::string::format("background-color: %s; border: none;", HighlightBorderColor); - m_componentModeBorderText.setStyleSheet(styleSheet.c_str()); - m_componentModeBorderText.setFixedHeight(TopHighlightBorderSize); - m_componentModeBorderText.setVisible(false); - m_fullScreenLayout.addWidget(&m_componentModeBorderText, 0, 0, Qt::AlignTop | Qt::AlignHCenter); + m_viewportBorderText.setStyleSheet(styleSheet.c_str()); + m_viewportBorderText.setFixedHeight(ViewportUiTopBorderSize); + m_viewportBorderText.setVisible(false); + m_fullScreenLayout.addWidget(&m_viewportBorderText, 0, 0, Qt::AlignTop | Qt::AlignHCenter); } void ViewportUiDisplay::PrepareWidgetForViewportUi(QPointer widget) @@ -395,14 +398,14 @@ namespace AzToolsFramework::ViewportUi::Internal void ViewportUiDisplay::UpdateUiOverlayGeometry() { - // add the component mode border region if visible + // add the viewport border region if visible QRegion region; - if (m_componentModeBorderText.isVisible()) + if (m_viewportBorderText.isVisible()) { // get the border region by taking the entire region and subtracting the non-border area region += m_uiOverlay.rect(); region -= QRect( - QPoint(m_uiOverlay.rect().left() + HighlightBorderSize, m_uiOverlay.rect().top() + TopHighlightBorderSize), + QPoint(m_uiOverlay.rect().left() + HighlightBorderSize, m_uiOverlay.rect().top() + ViewportUiTopBorderSize), QPoint(m_uiOverlay.rect().right() - HighlightBorderSize, m_uiOverlay.rect().bottom() - HighlightBorderSize)); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h index 5020241815..32b746a1ac 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h @@ -113,7 +113,7 @@ namespace AzToolsFramework::ViewportUi::Internal QWidget m_uiOverlay; //!< The UI Overlay which displays Viewport UI Elements. QGridLayout m_fullScreenLayout; //!< The layout which extends across the full screen. ViewportUiDisplayLayout m_uiOverlayLayout; //!< The layout used for optionally anchoring Viewport UI Elements. - QLabel m_componentModeBorderText; //!< The text used for the Component Mode border. + QLabel m_viewportBorderText; //!< The text used for the viewport border. QWidget* m_renderOverlay; QPointer m_fullScreenWidget; //!< Reference to the widget attached to m_fullScreenLayout if any. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h index 4a44f07491..335f664094 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h @@ -14,13 +14,20 @@ #include #include +namespace AzToolsFramework::ViewportUi +{ + //! Margin for the Viewport UI Overlay (in pixels) + constexpr int ViewportUiOverlayMargin = 5; + //! Padding to make space for ImGui (in pixels) + constexpr int ViewportUiOverlayTopMarginPadding = 20; + //! Size of the top viewport border (in pixels) + constexpr int ViewportUiTopBorderSize = 25; + //! Size of the left, right and bottom viewport border (in pixels) + constexpr int ViewportUiLeftRightBottomBorderSize = 5; +} // namespace AzToolsFramework::ViewportUi + namespace AzToolsFramework::ViewportUi::Internal { - // margin for the Viewport UI Overlay in pixels - constexpr int ViewportUiOverlayMargin = 5; - // padding to make space for ImGui - constexpr int ViewportUiOverlayTopMarginPadding = 20; - //! QGridLayout implementation that uses a grid of QVBox/QHBoxLayouts internally to stack widgets. class ViewportUiDisplayLayout : public QGridLayout { diff --git a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp index 81ec4b17c8..d2f86a65cd 100644 --- a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp +++ b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "ImGuiColorDefines.h" #include "LYImGuiUtils/ImGuiDrawHelpers.h" @@ -92,8 +93,36 @@ namespace ImGui void ImGuiLYCommonMenu::OnImGuiUpdate() { + float dpiScalingFactor = 1.0f; + ImGuiManagerBus::BroadcastResult(dpiScalingFactor, &ImGuiManagerBus::Events::GetDpiScalingFactor); + + // Utility function to calculate the size in device pixels based on the current DPI + const auto dpiAwareSizeFn = [dpiScalingFactor](float size) + { + return dpiScalingFactor * size; + }; + + AZStd::optional viewportBorderPaddingOpt; + AzFramework::ViewportBorderRequestBus::BroadcastResult( + viewportBorderPaddingOpt, &AzFramework::ViewportBorderRequestBus::Events::GetViewportBorderPadding); + + AzFramework::ViewportBorderPadding viewportBorderPadding = viewportBorderPaddingOpt.value_or(AzFramework::ViewportBorderPadding{}); + // Utility function to return the current offset (scaled by DPI) if a viewport border + // is active (otherwise 0.0) + auto dpiAwareBorderOffsetFn = [&viewportBorderPaddingOpt, &dpiAwareSizeFn](float size) + { + return viewportBorderPaddingOpt.has_value() ? dpiAwareSizeFn(size) : 0.0f; + }; + + // Shift the menu down if a viewport border is active + ImVec2 cachedSafeArea = ImGui::GetStyle().DisplaySafeAreaPadding; + ImGui::GetStyle().DisplaySafeAreaPadding = ImVec2(cachedSafeArea.x, cachedSafeArea.y + dpiAwareSizeFn(viewportBorderPadding.m_top)); + if (ImGui::BeginMainMenuBar()) { + // Constant to shift right aligned menu items by (distance to the left) when a viewport border is active + const float rightAlignedBorderOffset = dpiAwareBorderOffsetFn(36.0f); + // Get Discrete Input state now, we will use it both inside the ImGui SubMenu, and along the main task bar ( when it is on ) bool discreteInputEnabled = false; ImGuiManagerBus::BroadcastResult(discreteInputEnabled, &IImGuiManager::GetEnableDiscreteInputMode); @@ -101,7 +130,8 @@ namespace ImGui // Input Mode Display { const float prevCursorPos = ImGui::GetCursorPosX(); - ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 300.0f); + ImGui::SetCursorPosX( + ImGui::GetWindowWidth() - dpiAwareSizeFn(300.0f + viewportBorderPadding.m_right) - rightAlignedBorderOffset); AZStd::string inputTitle = "Input: "; if (!discreteInputEnabled) @@ -152,7 +182,7 @@ namespace ImGui } // Add some space before the first menu so it won't overlap with view control buttons - ImGui::SetCursorPosX(40.f); + ImGui::SetCursorPosX(dpiAwareSizeFn(40.0f + viewportBorderPadding.m_left)); // Main Open 3D Engine menu if (ImGui::BeginMenu("O3DE")) @@ -557,11 +587,12 @@ namespace ImGui // End LY Common Tools menu ImGui::EndMenu(); } - const int labelSize{ 100 }; - const int buttonSize{ 40 }; + + const float labelSize = dpiAwareSizeFn(100.0f + viewportBorderPadding.m_right) + rightAlignedBorderOffset; + const float buttonSize = dpiAwareSizeFn(40.0f + viewportBorderPadding.m_right) + rightAlignedBorderOffset; ImGuiUpdateListenerBus::Broadcast(&IImGuiUpdateListener::OnImGuiMainMenuUpdate); ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - labelSize); - float backgroundHeight = ImGui::GetTextLineHeight() + 3; + float backgroundHeight = ImGui::GetTextLineHeight() + dpiAwareSizeFn(3.0f); ImVec2 cursorPos = ImGui::GetCursorScreenPos(); ImGui::GetWindowDrawList()->AddRectFilled( cursorPos, ImVec2(cursorPos.x + labelSize, cursorPos.y + backgroundHeight), IM_COL32(0, 115, 187, 255)); @@ -580,6 +611,9 @@ namespace ImGui ImGui::EndMainMenuBar(); } + // Restore original safe area. + ImGui::GetStyle().DisplaySafeAreaPadding = cachedSafeArea; + // Update Contextual Controller Window if (m_controllerLegendWindowVisible) { From e553eb0116548859617b21543e12e2ac485267e6 Mon Sep 17 00:00:00 2001 From: moraaar Date: Thu, 4 Nov 2021 09:27:23 +0000 Subject: [PATCH 053/177] Fixed editor crash dropping an fbx to entity inspector (#5242) The issue was that EditorActorComponent (added by the drag and drop of the FBX into the entity) continued with the loading of an actor asset even though the component is not activated due to incompatible services, which ultimately lead to logic which should never have been reached and crashing. Also fixed EditorActorComponent missing activation and deactivation of the base editor component class, which is necessary. All tests from EMotionFX.Editor.Tests passed. Signed-off-by: moraaar --- .../Editor/Components/EditorActorComponent.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index 5da9716d15..11cf3feb3b 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -195,6 +195,8 @@ namespace EMotionFX ////////////////////////////////////////////////////////////////////////// void EditorActorComponent::Activate() { + AzToolsFramework::Components::EditorComponentBase::Activate(); + LoadActorAsset(); const AZ::EntityId entityId = GetEntityId(); @@ -225,6 +227,8 @@ namespace EMotionFX DestroyActorInstance(); m_actorAsset.Release(); + + AzToolsFramework::Components::EditorComponentBase::Deactivate(); } ////////////////////////////////////////////////////////////////////////// @@ -587,7 +591,15 @@ namespace EMotionFX if (asset) { m_actorAsset = asset; - OnAssetSelected(); + + // SetPrimaryAsset function can be called while this component is not activated + // due to incompatible services. For example by dragging and dropping a FBX to an + // entity that already has an actor or mesh component in it. Only proceed to load actor + // asset if the component is activated (by checking if it's connected to EditorActorComponentRequestBus). + if (EditorActorComponentRequestBus::Handler::BusIsConnected()) + { + OnAssetSelected(); + } } } From 6b5f5bc666e5a92dc112b222cae684306008af8e Mon Sep 17 00:00:00 2001 From: AMZN-stankowi <4838196+AMZN-stankowi@users.noreply.github.com> Date: Thu, 4 Nov 2021 06:46:11 -0700 Subject: [PATCH 054/177] Bundled release build bug fixes cherry picked from development (#5270) * Fixed some files missed when groundplane_521 was renamed to 512 (#4958) * Fixed references to 521x521 to reference the correct 512x512 FBX file Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> * Fixed asset hints Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> * Moved the Asset Catalog loading from LmbrCentral to the AzFramework::Application (#4568) * Moved the loading of the AssetCatalog from LmbrCentralSystemComponent to AzFramework Application Modified the AssetCatalog::InitializeCatalog function to no longer rely on the TickBus to send out the `AssetCatalogEventBus::OnCatalogLoaded` event. It now queues a function on the AssetCatalogRequestBus to send the OnCatalogLoaded event as soon as the dispatching for the AssetCatalogRequestBus has completed on the current thread. This is done by updating the AssetCatalogRequestBus to use EBus ThreadDispatchPolicy to add a callback to invoke any queued function has soon a thread has finished dispatching and has released its DispatchMutex Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the AssetCatalogRequestBus to add a custom DispatchLockGuard The AssetCatalogRequestBus uses the custom lock guard to dispatch queued events after it has unlocked it's context mutex for the current thread. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed GetContext call from the AssetCatalogRequests::PostThreadDispatchInvoker Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the definition of FileTagQueryManager::GetDefaultFileTagFilePath function to return a path Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the AZ_CONSOLEFREEFUNC macro to actually use the _NAME The _NAME parameter was not being used before, resulting in the Console stringified name of the function being used. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed CrySystem dependencies from the BundlingSystemComponent Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Moved the loading of the AssetCatalog from LmbrCentralSystemComponent to AzFramework Application Modified the AssetCatalog::InitializeCatalog function to no longer rely on the TickBus to send out the `AssetCatalogEventBus::OnCatalogLoaded` event. It now queues a function on the AssetCatalogRequestBus to send the OnCatalogLoaded event as soon as the dispatching for the AssetCatalogRequestBus has completed on the current thread. This is done by updating the AssetCatalogRequestBus to use EBus ThreadDispatchPolicy to add a callback to invoke any queued function has soon a thread has finished dispatching and has released its DispatchMutex Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the AssetCatalogRequestBus to add a custom DispatchLockGuard The AssetCatalogRequestBus uses the custom lock guard to dispatch queued events after it has unlocked it's context mutex for the current thread. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed GetContext call from the AssetCatalogRequests::PostThreadDispatchInvoker Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the definition of FileTagQueryManager::GetDefaultFileTagFilePath function to return a path Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the AZ_CONSOLEFREEFUNC macro to actually use the _NAME The _NAME parameter was not being used before, resulting in the Console stringified name of the function being used. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed CrySystem dependencies from the BundlingSystemComponent Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Addded missing template parameter to AssetCatalogRequests The fixes the compile error. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Adding AssetBus::MultiHandler::BusDisconnect call The BlastSystemComponent was connecting to the Bus, but not disconnecting from it, causing an assert to fire to it being a multi-thread bus Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Added support for DataDrive lifecycle events to the ComponentApplication The events are using the SettingsRegistry NotifyEvent to track when certain keys are modified to signal handlers. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Corrected invalid JSON creation in ModuleManager::DeactivateEntities Resolved clang warning about used type alias Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Fix for dangling reference in lambda registered to the SettingsRegistry Notifier event This was causing the EditorPythonBinding tests to crash due to the following circumstances. First Python has created an instance of a SettingsRegistryProxy Second the SettingsRegistry sends an event during the time when the SettingsRegistryProxy exists. This issue was exposed due to the ComponentApplication Lifecycle events using the SettingsRegistry to dispatch during various times of the application workflow. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Added the generated cmake_dependencies.*.setreg files to engine.pak (#5073) * Copied the generated cmake_dependencies.*.setreg file to the Cache directory Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed the platform name from the bootstrap.game.*.setreg Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Fixes for release builds with DCO fix (#5164) * This set of changes is work toward allowing release builds to work with asset bundler generated bundles and legacy, non-prefab levels. This requires some other in-flight changes before this work is complete. Updated engine seed list + fixed automated test ComponentApplicationLifecycle has the ability to automatically register events if asked to register a handler and the event doesn't exist. This is only intended for cases where you need to register a handler early in startup before the settings registry file is loaded. Added two new lifecycle events: One after the system entity has been activated, and one after the system interface has been created. If you load an archive before the system entity has been activated, archive.cpp caches information about those archives until that time, so it can finish registration. This is because the serialization system and BundlingSystemComponent both need to be available to do this registration, but the bundles have to be loaded before those are initialized so that the settings registry file can be loaded. Fixed an error were mounted pak files were searching for levels.pak and not level.pak, and not finding them. I'm pretty sure this logic doesn't do anything functional either way, but I've been testing legacy levels with this change and they work now. Moved wildcard pak loading to where engine.pak is loaded. This is because the settings registry file that defines the IO stack to spin up must be available early in application startup, and this file must be within a mounted pak file. If you're using asset bundler generated bundles, they need to be loaded at this time so that file can be loaded. Atom's BootstrapSystemComponent.cpp no longer initializes on AssetCatalogLoaded, and instead initializes on the ApplicationLifecycle event SystemInterfaceCreated. This is because the base assetcatalog.xml file is really just a development time concept, this file should not be used in packaged release builds, because those builds will make use of delta catalogs in each bundle loaded. The asset catalog contains the list of all assets that were in the cache at development time, and this contains content that developers don't want to ship, and they may want to specifically hide from their customers, so data miners don't find secrets about upcoming game content. Recovering from a branch that had incorrect DCO Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> * Fixed an incorrect ebus disconnect and removed an include that's no longer needed Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> * Fixed a copy and paste typo from trying to recover the previous pull request Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> * Updated product IDs for the settings registry builder to no longer collide with the JSON builder. Now they are based on a hash of the configuration. Updated the engine default seed list to include the new asset ID info for the renamed bootstrap file Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> * Updated the path to the application lifecycle events, because runtime settings aren't included in the merged bootstrap file. Addressed some feedback on printing out a string view on an error Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> * Removed a test that uses old assets that aren't relevant. We may not need this test anymore, but if we do we've backlogged a task to create a new test to cover this behavior without using old assets. Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> * Renamed SystemInterfaceCreated event to LegacySystemInterfaceCreated Removed SystemEntityActivated event. Now that I have the rest of the fixes in this pull request, this new event wasn't needed, the already existing SystemComponentsActivated event does what I need. Changed list to vector Signed-off-by: stankowi <4838196+AMZN-stankowi@users.noreply.github.com> Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- Assets/Editor/Prefabs/Default_Level.prefab | 4 +- Assets/Engine/Engine_Dependencies.xml | 23 +- Assets/Engine/SeedAssetList.seed | 877 ++++++------------ .../asset_processor_batch_dependency_tests.py | 76 -- Code/Editor/CryEdit.cpp | 6 + .../AzCore/AzCore/Asset/AssetManagerBus.h | 42 +- .../AzCore/Component/ComponentApplication.cpp | 27 +- .../AzCore/Component/ComponentApplication.h | 4 +- .../ComponentApplicationLifecycle.cpp | 93 ++ .../Component/ComponentApplicationLifecycle.h | 56 ++ .../AzCore/AzCore/Console/IConsole.h | 2 +- .../AzCore/AzCore/Module/ModuleManager.cpp | 38 +- .../Settings/SettingsRegistryMergeUtils.cpp | 14 +- .../Settings/SettingsRegistryScriptUtils.cpp | 18 +- .../AzCore/AzCore/azcore_files.cmake | 2 + .../AzFramework/Application/Application.cpp | 38 +- .../AzFramework/Archive/Archive.cpp | 76 +- .../AzFramework/AzFramework/Archive/Archive.h | 37 + .../AzFramework/Asset/AssetCatalog.cpp | 15 +- .../AzFramework/Asset/AssetRegistry.cpp | 1 + .../AzFramework/FileTag/FileTag.cpp | 21 +- .../AzFramework/AzFramework/FileTag/FileTag.h | 3 +- .../AzFramework/FileTag/FileTagComponent.cpp | 4 +- .../AzFramework/Tests/AssetCatalog.cpp | 4 +- .../Application/GameApplication.cpp | 9 +- Code/LauncherUnified/Launcher.cpp | 3 + Code/Legacy/CrySystem/IDebugCallStack.cpp | 4 +- Code/Legacy/CrySystem/System.h | 2 +- Code/Legacy/CrySystem/SystemInit.cpp | 25 +- .../SettingsRegistryBuilder.cpp | 22 +- .../SerializeContextTools/SliceConverter.cpp | 2 - .../Code/Source/BootstrapSystemComponent.cpp | 25 +- .../Code/Source/BootstrapSystemComponent.h | 15 +- .../Application/AtomToolsApplication.cpp | 2 - .../PreviewRendererSystemComponent.cpp | 23 +- .../PreviewRendererSystemComponent.h | 5 - .../test_sponza_material_conversion.prefab | 10 +- .../Assets/LevelAssets/default.slice | 2 +- .../Components/BlastSystemComponent.cpp | 1 + .../Bundling/BundlingSystemComponent.cpp | 97 +- .../Source/Bundling/BundlingSystemComponent.h | 17 +- Gems/LmbrCentral/Code/Source/LmbrCentral.cpp | 43 +- Gems/LmbrCentral/Code/Source/LmbrCentral.h | 10 - Registry/application_lifecycle_events.setreg | 30 + cmake/Projects.cmake | 25 +- 45 files changed, 886 insertions(+), 967 deletions(-) create mode 100644 Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp create mode 100644 Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h create mode 100644 Registry/application_lifecycle_events.setreg diff --git a/Assets/Editor/Prefabs/Default_Level.prefab b/Assets/Editor/Prefabs/Default_Level.prefab index d02d669f53..0267131fa0 100644 --- a/Assets/Editor/Prefabs/Default_Level.prefab +++ b/Assets/Editor/Prefabs/Default_Level.prefab @@ -185,7 +185,7 @@ { "id": { "materialAssetId": { - "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", "subId": 803645540 } } @@ -197,7 +197,7 @@ "id": { "lodIndex": 0, "materialAssetId": { - "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", "subId": 803645540 } } diff --git a/Assets/Engine/Engine_Dependencies.xml b/Assets/Engine/Engine_Dependencies.xml index 0d6541e18e..b969b3bc97 100644 --- a/Assets/Engine/Engine_Dependencies.xml +++ b/Assets/Engine/Engine_Dependencies.xml @@ -1,16 +1,9 @@ - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + diff --git a/Assets/Engine/SeedAssetList.seed b/Assets/Engine/SeedAssetList.seed index aafbffbe8f..18ccd19dc3 100644 --- a/Assets/Engine/SeedAssetList.seed +++ b/Assets/Engine/SeedAssetList.seed @@ -1,621 +1,260 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py index ee8e177bbb..303a012cda 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py @@ -47,82 +47,6 @@ class TestsAssetProcessorBatch_DependenycyTests(object): """ AssetProcessorBatch Dependency tests """ - - @pytest.mark.test_case_id("C16877166") - @pytest.mark.BAT - @pytest.mark.assetpipeline - # fmt:off - def test_WindowsMacPlatforms_RunAPBatch_NotMissingDependency(self, ap_setup_fixture, asset_processor, - workspace): - # fmt:on - """ - Engine Schema - This test case has a conditional scenario depending on the existence of surfacetypes.xml in a project. - Some projects have this file and others do not. Run the conditional scenario depending on the existence - of the file in the project - libs/materialeffects/surfacetypes.xml is listed as an entry engine_dependencies.xml - libs/materialeffects/surfacetypes.xml is not listed as a missing dependency - in the 'assetprocessorbatch' console output - - Test Steps: - 1. Assets are pre-processed - 2. Verify that engine_dependencies.xml exists - 3. Verify engine_dependencies.xml has surfacetypes.xml present - 4. Run Missing Dependency scanner against the engine_dependenciese.xml - 5. Verify that Surfacetypes.xml is NOT in the missing depdencies output - 6. Add the schema file which allows our xml parser to understand dependencies for our engine_dependencies file - 7. Process assets - 8. Run Missing Dependency scanner against the engine_dependenciese.xml - 9. Verify that surfacetypes.xml is in the missing dependencies out - """ - - env = ap_setup_fixture - BATCH_LOG_PATH = env["ap_batch_log_file"] - asset_processor.create_temp_asset_root() - asset_processor.add_relative_source_asset(os.path.join("Assets", "Engine", "engine_dependencies.xml")) - asset_processor.add_scan_folder(os.path.join("Assets", "Engine")) - asset_processor.add_relative_source_asset(os.path.join("Assets", "Engine", "Libs", "MaterialEffects", "surfacetypes.xml")) - - # Precondition: Assets are all processed - asset_processor.batch_process() - - DEPENDENCIES_PATH = os.path.join(asset_processor.temp_project_cache(), "engine_dependencies.xml") - assert os.path.exists(DEPENDENCIES_PATH), "The engine_dependencies.xml does not exist." - surfacetypes_in_dependencies = False - surfacetypes_missing_logline = False - - # Read engine_dependencies.xml to see if surfacetypes.xml is present - with open(DEPENDENCIES_PATH, "r") as dependencies_file: - for line in dependencies_file.readlines(): - if "surfacetypes.xml" in line: - surfacetypes_in_dependencies = True - logger.info("Surfacetypes.xml was listed in the engine_dependencies.xml file.") - break - - if not surfacetypes_in_dependencies: - logger.info("Surfacetypes.xml was not listed in the engine_dependencies.xml file.") - - _, output = asset_processor.batch_process(capture_output=True, - extra_params="--dsp=%engine_dependencies.xml") - log = APOutputParser(output) - for _ in log.get_lines(run=-1, contains=["surfacetypes.xml", "Missing"]): - surfacetypes_missing_logline = True - - assert surfacetypes_missing_logline, "Surfacetypes.xml not seen in the batch log as missing." - - # Add the schema file which allows our xml parser to understand dependencies for our engine_dependencies file - asset_processor.add_relative_source_asset(os.path.join("Assets", "Engine", "Schema", "enginedependency.xmlschema")) - asset_processor.batch_process() - - _, output = asset_processor.batch_process(capture_output=True, - extra_params="--dsp=%engine_dependencies.xml") - log = APOutputParser(output) - surfacetypes_missing_logline = False - for _ in log.get_lines(run=-1, contains=["surfacetypes.xml", "Missing"]): - surfacetypes_missing_logline = True - - assert not surfacetypes_missing_logline, "Surfacetypes.xml not seen in the batch log as missing." - schemas = [ ("C16877167", ".ent"), ("C16877168", "Environment.xml"), diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index e4138da932..5cffedd774 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -45,6 +45,7 @@ AZ_POP_DISABLE_WARNING // AzCore #include +#include #include #include #include @@ -1686,6 +1687,11 @@ bool CCryEditApp::InitInstance() return false; } + if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get()) + { + AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "LegacySystemInterfaceCreated", R"({})"); + } + // Process some queued events come from system init // Such as asset catalog loaded notification. // There are some systems need to load configurations from assets for post initialization but before loading level diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h b/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h index f8e263d6b0..f76ea19589 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h @@ -65,8 +65,35 @@ namespace AZ ////////////////////////////////////////////////////////////////////////// // EBusTraits overrides - Application is a singleton - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - typedef AZStd::recursive_mutex MutexType; + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + using MutexType = AZStd::recursive_mutex; + + static constexpr bool EnableEventQueue = true; + using EventQueueMutexType = AZStd::mutex; + struct PostThreadDispatchInvoker + { + ~PostThreadDispatchInvoker(); + }; + + template + struct ThreadDispatchLockGuard + { + ThreadDispatchLockGuard(DispatchMutex& contextMutex) + : m_lock{ contextMutex } + {} + ThreadDispatchLockGuard(DispatchMutex& contextMutex, AZStd::adopt_lock_t adopt_lock) + : m_lock{ contextMutex, adopt_lock } + {} + ThreadDispatchLockGuard(const ThreadDispatchLockGuard&) = delete; + ThreadDispatchLockGuard& operator=(const ThreadDispatchLockGuard&) = delete; + private: + PostThreadDispatchInvoker m_threadPolicyInvoker; + using LockType = AZStd::conditional_t, AZStd::scoped_lock>; + LockType m_lock; + }; + + template + using DispatchLockGuard = ThreadDispatchLockGuard; ////////////////////////////////////////////////////////////////////////// virtual ~AssetCatalogRequests() = default; @@ -200,6 +227,17 @@ namespace AZ using AssetCatalogRequestBus = AZ::EBus; + inline AssetCatalogRequests::PostThreadDispatchInvoker::~PostThreadDispatchInvoker() + { + if (!AssetCatalogRequestBus::IsInDispatchThisThread()) + { + if (AssetCatalogRequestBus::QueuedEventCount()) + { + AssetCatalogRequestBus::ExecuteQueuedEvents(); + } + } + } + /* * Events that AssetManager listens for */ diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp index ba66d56379..df8db79db0 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -44,8 +45,6 @@ #include #include -#include -#include #include #include @@ -216,11 +215,6 @@ namespace AZ m_oldProjectPath = newProjectPath; // Merge the project.json file into settings registry under ProjectSettingsRootKey path. - AZ::IO::FixedMaxPath projectMetadataFile{ AZ::SettingsRegistryMergeUtils::FindEngineRoot(m_registry) / newProjectPath }; - projectMetadataFile /= "project.json"; - m_registry.MergeSettingsFile(projectMetadataFile.Native(), - AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey); - // Update all the runtime file paths based on the new "project_path" value. AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry); } @@ -506,6 +500,16 @@ namespace AZ SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands); SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry); + // The /O3DE/Application/LifecycleEvents array contains a valid set of lifecycle events + // Those lifecycle events are normally read from the /Registry + // which isn't merged until ComponentApplication::Create invokes MergeSettingsToRegistry + // So pre-populate the valid lifecycle even entries + ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "SystemAllocatorCreated"); + ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "SettingsRegistryAvailable"); + ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "ConsoleAvailable"); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SystemAllocatorCreated", R"({})"); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SettingsRegistryAvailable", R"({})"); + // Create the Module Manager m_moduleManager = AZStd::make_unique(); @@ -520,6 +524,7 @@ namespace AZ m_ownsConsole = true; m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead()); m_settingsRegistryConsoleFunctors = AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_settingsRegistry, *m_console); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleAvailable", R"({})"); } } @@ -551,6 +556,7 @@ namespace AZ { AZ::Interface::Unregister(m_console); delete m_console; + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleUnavailable", R"({})"); } m_moduleManager.reset(); @@ -558,6 +564,8 @@ namespace AZ if (AZ::SettingsRegistry::Get() == m_settingsRegistry.get()) { SettingsRegistry::Unregister(m_settingsRegistry.get()); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SettingsRegistryUnavailable", R"({})"); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SystemAllocatorPendingDestruction", R"({})"); } m_settingsRegistry.reset(); @@ -672,6 +680,8 @@ namespace AZ ReflectionEnvironment::GetReflectionManager()->Reflect(azrtti_typeid(this), [this](ReflectContext* context) {Reflect(context); }); RegisterCoreComponents(); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ReflectionManagerAvailable", R"({})"); + TickBus::AllowFunctionQueuing(true); SystemTickBus::AllowFunctionQueuing(true); @@ -691,6 +701,7 @@ namespace AZ // Load the actual modules LoadModules(); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsLoaded", R"({})"); // Execute user.cfg after modules have been loaded but before processing any command-line overrides AZ::IO::FixedMaxPath platformCachePath; @@ -756,12 +767,14 @@ namespace AZ m_entities.rehash(0); // force free all memory DestroyReflectionManager(); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ReflectionManagerUnavailable", R"({})"); static_cast(m_settingsRegistry.get())->ClearNotifiers(); static_cast(m_settingsRegistry.get())->ClearMergeEvents(); // Uninit and unload any dynamic modules. m_moduleManager->UnloadModules(); + ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsUnloaded", R"({})"); NameDictionary::Destroy(); diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h index f2b1bb8905..6df93aff4e 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h @@ -175,6 +175,8 @@ namespace AZ bool m_loadDynamicModules = true; //! Used by test fixtures to ensure reflection occurs to edit context. bool m_createEditContext = false; + //! Indicates whether the AssetCatalog.xml should be loaded by default in Application::StartCommon + bool m_loadAssetCatalog = true; }; ComponentApplication(); @@ -356,7 +358,7 @@ namespace AZ /// Calculates the root directory of the engine. void CalculateEngineRoot(); - /// Calculates the directory where the bootstrap.cfg file resides. + /// Deprecated: The term "AppRoot" has no meaning void CalculateAppRoot(); template diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp new file mode 100644 index 0000000000..8e15871bc0 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +namespace AZ::ComponentApplicationLifecycle +{ + bool ValidateEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName) + { + using FixedValueString = SettingsRegistryInterface::FixedValueString; + using Type = SettingsRegistryInterface::Type; + FixedValueString eventRegistrationKey{ ApplicationLifecycleEventRegistrationKey }; + eventRegistrationKey += '/'; + eventRegistrationKey += eventName; + return settingsRegistry.GetType(eventRegistrationKey) == Type::Object; + } + + bool SignalEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName, AZStd::string_view eventValue) + { + using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; + using Format = AZ::SettingsRegistryInterface::Format; + + if (!ValidateEvent(settingsRegistry, eventName)) + { + AZ_Warning("ComponentApplicationLifecycle", false, R"(Cannot signal event %.*s. Name does is not a field of object "%.*s".)" + R"( Please make sure the entry exists in the '/Registry/application_lifecycle_events.setreg")" + " or in *.setreg within the project", AZ_STRING_ARG(eventName), AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey)); + return false; + } + auto eventRegistrationKey = FixedValueString::format("%.*s/%.*s", AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey), + AZ_STRING_ARG(eventName)); + + return settingsRegistry.MergeSettings(eventValue, Format::JsonMergePatch, eventRegistrationKey); + } + + bool RegisterEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName) + { + using FixedValueString = SettingsRegistryInterface::FixedValueString; + using Format = AZ::SettingsRegistryInterface::Format; + + if (!ValidateEvent(settingsRegistry, eventName)) + { + FixedValueString eventRegistrationKey{ ApplicationLifecycleEventRegistrationKey }; + eventRegistrationKey += '/'; + eventRegistrationKey += eventName; + return settingsRegistry.MergeSettings(R"({})", Format::JsonMergePatch, eventRegistrationKey); + } + + return true; + } + + bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler, + AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName, bool autoRegisterEvent) + { + using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; + using Type = AZ::SettingsRegistryInterface::Type; + using NotifyEventHandler = AZ::SettingsRegistryInterface::NotifyEventHandler; + + // Some systems may attempt to register a handler before the settings registry has been loaded + // If so, this flag lets them automatically register an event if it hasn't yet been registered. + // RegisterEvent calls validate event. + if ((!autoRegisterEvent && !ValidateEvent(settingsRegistry, eventName)) || + (autoRegisterEvent && !RegisterEvent(settingsRegistry, eventName))) + { + AZ_Warning( + "ComponentApplicationLifecycle", false, + R"(Cannot register event %.*s. Name is not a field of object "%.*s".)" + R"( Please make sure the entry exists in the '/Registry/application_lifecycle_events.setreg")" + " or in *.setreg within the project", AZ_STRING_ARG(eventName), AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey)); + return false; + } + auto eventNameRegistrationKey = FixedValueString::format("%.*s/%.*s", AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey), + AZ_STRING_ARG(eventName)); + auto lifecycleCallback = [callback = AZStd::move(callback), eventNameRegistrationKey](AZStd::string_view path, Type type) + { + if (path == eventNameRegistrationKey) + { + callback(path, type); + } + }; + + handler = NotifyEventHandler(AZStd::move(lifecycleCallback)); + settingsRegistry.RegisterNotifier(handler); + + return true; + } +} diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h new file mode 100644 index 0000000000..6f07c0da3f --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace AZ::ComponentApplicationLifecycle +{ + //! Root Key where lifecycle events should be registered under + inline constexpr AZStd::string_view ApplicationLifecycleEventRegistrationKey = "/O3DE/Application/LifecycleEvents"; + + + //! Validates that the event @eventName is stored in the array at ApplicationLifecycleEventRegistrationKey + //! @param settingsRegistry registry where @eventName will be searched + //! @param eventName name of key that validated that exists as an element in the ApplicationLifecycleEventRegistrationKey array + //! @return true if the @eventName was found in the ApplicationLifecycleEventRegistrationKey array + bool ValidateEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName); + + //! Wrapper around setting a value underneath the ApplicationLifecycleEventRegistrationKey + //! It validates if the @eventName is is part of the ApplicationLifecycleEventRegistrationKey array + //! It then appends the @eventName to the ApplicationLifecycleEventRegistrationKey merges the @eventValue into + //! the SettingsRegistry at that key + //! NOTE: This function should only be invoked from ComponentApplication and its derived classes + //! @param settingsRegistry registry where eventName should be set + //! @param eventName name of key underneath the ApplicationLifecycleEventRegistrationKey to signal + //! @param eventValue JSON Object that will be merged into the SettingsRegistry at / + //! @return true if the eventValue was successfully merged at the / + bool SignalEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName, AZStd::string_view eventValue); + + //! Register that the event @eventName is stored in the array at ApplicationLifecycleEventRegistrationKey + //! @param settingsRegistry registry where @eventName will be searched + //! @param eventName name of key that will be stored in the ApplicationLifecycleEventRegistrationKey array + //! @return true if the event passed validation or the eventName was stored in the ApplicationLifecycleEventRegistrationKey array + bool RegisterEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName); + + //! Wrapper around registering the NotifyEventHandler with the SettingsRegistry for the specified event + //! It validates if the @eventName is is part of the ApplicationLifecycleEventRegistrationKey array and if + //! so moves the @callback into @handler and then registers the handler with the SettingsRegistry NotifyEvent + //! @param settingsRegistry registry where handler will be registered + //! @param handler handler where callback will be moved into and then registered with the SettingsRegistry + //! if the specified @eventName passes validation + //! @param callback will be moved into the handler if the specified @eventName is valid + //! @param eventName name of key underneath the ApplicationLifecycleEventRegistrationKey to register + //! @param autoRegisterEvent automatically register this event if it hasn't been registered yet. This is useful + //! when registering a handler before the settings registry has been loaded. + //! @return true if the handler was registered with the SettingsRegistry NotifyEvent + bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler, + AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName, bool autoRegisterEvent = false); +} diff --git a/Code/Framework/AzCore/AzCore/Console/IConsole.h b/Code/Framework/AzCore/AzCore/Console/IConsole.h index 73d17ac65a..aef163d22b 100644 --- a/Code/Framework/AzCore/AzCore/Console/IConsole.h +++ b/Code/Framework/AzCore/AzCore/Console/IConsole.h @@ -262,6 +262,6 @@ static constexpr AZ::ThreadSafety ConsoleThreadSafety<_TYPE, std::enable_if_t Functor##_FUNCTION(#_FUNCTION, _DESC, _FLAGS | AZ::ConsoleFunctorFlags::DontDuplicate, AZ::TypeId::CreateNull(), &_FUNCTION) + inline AZ::ConsoleFunctor Functor##_FUNCTION(_NAME, _DESC, _FLAGS | AZ::ConsoleFunctorFlags::DontDuplicate, AZ::TypeId::CreateNull(), &_FUNCTION) #define AZ_CONSOLEFREEFUNC(...) AZ_MACRO_SPECIALIZE(AZ_CONSOLEFREEFUNC_, AZ_VA_NUM_ARGS(__VA_ARGS__), (__VA_ARGS__)) diff --git a/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp b/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp index 1fdeafa309..cbf3ce5243 100644 --- a/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp +++ b/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp @@ -10,16 +10,13 @@ #include #include -#include -#include #include #include #include -#include #include +#include +#include #include -#include -#include #include #include @@ -221,11 +218,16 @@ namespace AZ } } + AZStd::string componentNamesArray = R"({ "SystemComponents":[)"; + const char* comma = ""; // For all system components, deactivate for (auto componentIt = m_systemComponents.rbegin(); componentIt != m_systemComponents.rend(); ++componentIt) { ModuleEntity::DeactivateComponent(**componentIt); + componentNamesArray += AZStd::string::format(R"(%s"%s")", comma, (*componentIt)->RTTI_GetTypeName()); + comma = ", "; } + componentNamesArray += R"(]})"; // For all modules that we created an entity for, set them to "Init" (meaning not Activated) for (auto& moduleData : m_ownedModules) @@ -239,6 +241,13 @@ namespace AZ // Since the system components have been deactivated clear out the vector. m_systemComponents.clear(); + + // Signal that the System Components have deactivated + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "SystemComponentsDeactivated", componentNamesArray); + } + } //========================================================================= @@ -284,7 +293,11 @@ namespace AZ { // Split the tag list AZStd::vector tagList; - AZStd::tokenize(tags, ",", tagList); + auto TokenizeTags = [&tagList](AZStd::string_view token) + { + tagList.push_back(token); + }; + AZ::StringFunc::TokenizeVisitor(tags, TokenizeTags, ','); m_systemComponentTags.resize(tagList.size()); AZStd::transform(tagList.begin(), tagList.end(), m_systemComponentTags.begin(), [](const AZStd::string_view& tag) @@ -737,11 +750,17 @@ namespace AZ } } + AZStd::string componentNamesArray = R"({ "SystemComponents":[)"; + const char* comma = ""; // Activate the entities in the appropriate order for (Component* component : componentsToActivate) { ModuleEntity::ActivateComponent(*component); + + componentNamesArray += AZStd::string::format(R"(%s"%s")", comma, component->RTTI_GetTypeName()); + comma = ", "; } + componentNamesArray += R"(]})"; // Done activating; set state to active for (auto& moduleData : modulesToInit) @@ -755,5 +774,12 @@ namespace AZ // Save the activated components for deactivation later m_systemComponents.insert(m_systemComponents.end(), componentsToActivate.begin(), componentsToActivate.end()); + + // Signal that the System Components are activated + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "SystemComponentsActivated", + componentNamesArray); + } } } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp index 36f66312d8..5458a3fadf 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp @@ -634,12 +634,18 @@ namespace AZ::SettingsRegistryMergeUtils } // Project name - if it was set via merging project.json use that value, otherwise use the project path's folder name. - auto projectNameKey = - AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey) + constexpr auto projectNameKey = + FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey) + "/project_name"; - AZ::SettingsRegistryInterface::FixedValueString projectName; - if (!registry.Get(projectName, projectNameKey)) + // Read the project name from the project.json file if it exists + if (AZ::IO::FixedMaxPath projectJsonPath = normalizedProjectPath / "project.json"; + AZ::IO::SystemFile::Exists(projectJsonPath.c_str())) + { + registry.MergeSettingsFile(projectJsonPath.Native(), + AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey); + } + if (FixedValueString projectName; !registry.Get(projectName, projectNameKey)) { projectName = path.Filename().Native(); registry.Set(projectNameKey, projectName); diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp index c32591874b..5cd36785dc 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp @@ -15,20 +15,20 @@ namespace AZ::SettingsRegistryScriptUtils::Internal { - static void RegisterScriptProxyForNotify(SettingsRegistryScriptProxy& settingsRegistryProxy) + static void RegisterScriptProxyForNotify(SettingsRegistryInterface* settingsRegistry, + SettingsRegistryScriptProxy::NotifyEventProxy* notifyEventProxy) { - if (settingsRegistryProxy.IsValid()) + if (settingsRegistry != nullptr) { - auto ForwardSettingsUpdateToProxyEvent = [&settingsRegistryProxy](AZStd::string_view path, AZ::SettingsRegistryInterface::Type) + auto ForwardSettingsUpdateToProxyEvent = [notifyEventProxy](AZStd::string_view path, AZ::SettingsRegistryInterface::Type) { - if (settingsRegistryProxy.m_notifyEventProxy) + if (notifyEventProxy) { - settingsRegistryProxy.m_notifyEventProxy->m_scriptNotifyEvent.Signal(path); + notifyEventProxy->m_scriptNotifyEvent.Signal(path); } }; // Register the forwarding function with the BehaviorContext - settingsRegistryProxy.m_notifyEventProxy->m_settingsUpdatedHandler = - settingsRegistryProxy.m_settingsRegistry->RegisterNotifier(ForwardSettingsUpdateToProxyEvent); + notifyEventProxy->m_settingsUpdatedHandler = settingsRegistry->RegisterNotifier(ForwardSettingsUpdateToProxyEvent); } } @@ -37,7 +37,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal : m_settingsRegistry(AZStd::move(settingsRegistry)) , m_notifyEventProxy(AZStd::make_shared()) { - RegisterScriptProxyForNotify(*this); + RegisterScriptProxyForNotify(m_settingsRegistry.get(), m_notifyEventProxy.get()); } // Raw AZ::SettingsRegistryInterface pointer is not owned by the proxy, so it's deleter is a no-op @@ -45,7 +45,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal : m_settingsRegistry(settingsRegistry, [](AZ::SettingsRegistryInterface*) {}) , m_notifyEventProxy(AZStd::make_shared()) { - RegisterScriptProxyForNotify(*this); + RegisterScriptProxyForNotify(m_settingsRegistry.get(), m_notifyEventProxy.get()); } // SettingsRegistryScriptProxy function that determines if the SettingsRegistry object is valid diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 41229429f2..0c5a360844 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -41,6 +41,8 @@ set(FILES Component/ComponentApplication.cpp Component/ComponentApplication.h Component/ComponentApplicationBus.h + Component/ComponentApplicationLifecycle.cpp + Component/ComponentApplicationLifecycle.h Component/ComponentBus.cpp Component/ComponentBus.h Component/ComponentExport.h diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp index f8d0ea8bb1..323e834413 100644 --- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp +++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -120,6 +121,11 @@ namespace AzFramework m_archiveFileIO = AZStd::make_unique(m_archive.get()); AZ::IO::FileIOBase::SetInstance(m_archiveFileIO.get()); SetFileIOAliases(); + // The FileIOAvailable event needs to be registered here as this event is sent out + // before the settings registry has merged the .setreg files from the + // (That happens in MergeSettingsToRegistry + AZ::ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "FileIOAvailable"); + AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "FileIOAvailable", R"({})"); } if (auto nativeUI = AZ::Interface::Get(); nativeUI == nullptr) @@ -172,6 +178,8 @@ namespace AzFramework // Archive classes relies on the FileIOBase DirectInstance to close // files properly m_directFileIO.reset(); + + AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "FileIOUnavailable", R"({})"); } void Application::Start(const Descriptor& descriptor, const StartupParameters& startupParameters) @@ -196,7 +204,24 @@ namespace AzFramework systemEntity->Activate(); AZ_Assert(systemEntity->GetState() == AZ::Entity::State::Active, "System Entity failed to activate."); - m_isStarted = (systemEntity->GetState() == AZ::Entity::State::Active); + if (m_isStarted = (systemEntity->GetState() == AZ::Entity::State::Active); m_isStarted) + { + if (m_startupParameters.m_loadAssetCatalog) + { + // Start Monitoring Asset changes over the network and load the AssetCatalog + auto StartMonitoringAssetsAndLoadCatalog = [this](AZ::Data::AssetCatalogRequests* assetCatalogRequests) + { + if (AZ::IO::FixedMaxPath assetCatalogPath; + m_settingsRegistry->Get(assetCatalogPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder)) + { + assetCatalogPath /= "assetcatalog.xml"; + assetCatalogRequests->LoadCatalog(assetCatalogPath.c_str()); + } + }; + using AssetCatalogBus = AZ::Data::AssetCatalogRequestBus; + AssetCatalogBus::Broadcast(AZStd::move(StartMonitoringAssetsAndLoadCatalog)); + } + } } void Application::PreModuleLoad() @@ -210,6 +235,17 @@ namespace AzFramework { if (m_isStarted) { + if (m_startupParameters.m_loadAssetCatalog) + { + // Stop Monitoring Assets changes + auto StopMonitoringAssets = [](AZ::Data::AssetCatalogRequests* assetCatalogRequests) + { + assetCatalogRequests->StopMonitoringAssets(); + }; + using AssetCatalogBus = AZ::Data::AssetCatalogRequestBus; + AssetCatalogBus::Broadcast(AZStd::move(StopMonitoringAssets)); + } + ApplicationLifecycleEvents::Bus::Broadcast(&ApplicationLifecycleEvents::OnApplicationAboutToStop); m_pimpl.reset(); diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp index cd30b753b5..c1c5775958 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -363,6 +364,23 @@ namespace AZ::IO , m_mainThreadId{ AZStd::this_thread::get_id() } { CompressionBus::Handler::BusConnect(); + + // If the settings registry is not available at this point, + // then something catastrophic has happened in the application startup. + // That should have been caught and messaged out earlier in startup. + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + // Automatically register the event if it's not registered, because + // this system is initialized before the settings registry has loaded the event list. + AZ::ComponentApplicationLifecycle::RegisterHandler( + *settingsRegistry, m_componentApplicationLifecycleHandler, + [this](AZStd::string_view /*path*/, AZ::SettingsRegistryInterface::Type /*type*/) + { + OnSystemEntityActivated(); + }, + "SystemComponentsActivated", + /*autoRegisterEvent*/ true); + } } ////////////////////////////////////////////////////////////////////////// @@ -1175,13 +1193,20 @@ namespace AZ::IO } } - auto bundleManifest = GetBundleManifest(desc.pZip); AZStd::shared_ptr bundleCatalog; + auto bundleManifest = GetBundleManifest(desc.pZip); if (bundleManifest) { bundleCatalog = GetBundleCatalog(desc.pZip, bundleManifest->GetCatalogName()); } + // If this archive is loaded before the serialize context is available, then the manifest and catalog will need to be loaded later. + if (!bundleManifest || !bundleCatalog) + { + m_archivesWithCatalogsToLoad.push_back( + ArchivesWithCatalogsToLoad(szFullPath, szBindRoot, flags, nextBundle, desc.m_strFileName)); + } + bool usePrefabSystemForLevels = false; AzFramework::ApplicationRequests::Bus::BroadcastResult( usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled); @@ -1219,12 +1244,17 @@ namespace AZ::IO m_levelOpenEvent.Signal(levelDirs); } - AZ::IO::ArchiveNotificationBus::Broadcast([](AZ::IO::ArchiveNotifications* archiveNotifications, const char* bundleName, - AZStd::shared_ptr bundleManifest, const AZ::IO::FixedMaxPath& nextBundle, AZStd::shared_ptr bundleCatalog) + if (bundleManifest && bundleCatalog) { - archiveNotifications->BundleOpened(bundleName, bundleManifest, nextBundle.c_str(), bundleCatalog); - }, desc.m_strFileName.c_str(), bundleManifest, nextBundle, bundleCatalog); - + AZ::IO::ArchiveNotificationBus::Broadcast( + [](AZ::IO::ArchiveNotifications* archiveNotifications, const char* bundleName, + AZStd::shared_ptr bundleManifest, const AZ::IO::FixedMaxPath& nextBundle, + AZStd::shared_ptr bundleCatalog) + { + archiveNotifications->BundleOpened(bundleName, bundleManifest, nextBundle.c_str(), bundleCatalog); + }, + desc.m_strFileName.c_str(), bundleManifest, nextBundle, bundleCatalog); + } return true; } @@ -2138,7 +2168,7 @@ namespace AZ::IO } currentDirPattern = currentDir + AZ_FILESYSTEM_SEPARATOR_WILDCARD; - currentFilePattern = currentDir + AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING + "levels.pak"; + currentFilePattern = currentDir + AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING + "level.pak"; ZipDir::FileEntry* fileEntry = findFile.FindExact(currentFilePattern.c_str()); if (fileEntry) @@ -2175,4 +2205,36 @@ namespace AZ::IO return catalogInfo; } + + void Archive::OnSystemEntityActivated() + { + for (const auto& archiveInfo : m_archivesWithCatalogsToLoad) + { + AZStd::intrusive_ptr archive = + OpenArchive(archiveInfo.m_fullPath, archiveInfo.m_bindRoot, archiveInfo.m_flags, nullptr); + if (!archive) + { + continue; + } + + ZipDir::CachePtr pZip = static_cast(archive.get())->GetCache(); + + AZStd::shared_ptr bundleCatalog; + auto bundleManifest = GetBundleManifest(pZip); + if (bundleManifest) + { + bundleCatalog = GetBundleCatalog(pZip, bundleManifest->GetCatalogName()); + } + + AZ::IO::ArchiveNotificationBus::Broadcast( + [](AZ::IO::ArchiveNotifications* archiveNotifications, const char* bundleName, + AZStd::shared_ptr bundleManifest, const AZ::IO::FixedMaxPath& nextBundle, + AZStd::shared_ptr bundleCatalog) + { + archiveNotifications->BundleOpened(bundleName, bundleManifest, nextBundle.c_str(), bundleCatalog); + }, + archiveInfo.m_strFileName.c_str(), bundleManifest, archiveInfo.m_nextBundle, bundleCatalog); + } + m_archivesWithCatalogsToLoad.clear(); + } } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h index f08d90a66e..279702b433 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -271,6 +272,11 @@ namespace AZ::IO ZipDir::CachePtr* pZip = {}) const; private: + // Archives can't be fully mounted until the system entity has been activated, + // because mounting them requires the BundlingSystemComponent and the serialization system + // to both be available. + void OnSystemEntityActivated(); + bool OpenPackCommon(AZStd::string_view szBindRoot, AZStd::string_view pName, AZStd::intrusive_ptr pData = nullptr, bool addLevels = true); bool OpenPacksCommon(AZStd::string_view szDir, AZStd::string_view pWildcardIn, AZStd::vector* pFullPaths = nullptr, bool addLevels = true); @@ -313,6 +319,8 @@ namespace AZ::IO mutable AZStd::shared_mutex m_csZips; ZipArray m_arrZips; + AZ::SettingsRegistryInterface::NotifyEventHandler m_componentApplicationLifecycleHandler; + ////////////////////////////////////////////////////////////////////////// // Opened files collector. ////////////////////////////////////////////////////////////////////////// @@ -339,5 +347,34 @@ namespace AZ::IO // [LYN-2376] Remove once legacy slice support is removed LevelPackOpenEvent m_levelOpenEvent; LevelPackCloseEvent m_levelCloseEvent; + + // If pak files are loaded before the serialization and bundling system + // are ready to go, their asset catalogs can't be loaded. + // In this case, cache information about those archives, + // and attempt to load the catalogs later, when the required systems are enabled. + struct ArchivesWithCatalogsToLoad + { + ArchivesWithCatalogsToLoad( + AZStd::string_view fullPath, + AZStd::string_view bindRoot, + int flags, + AZ::IO::PathView nextBundle, + AZ::IO::Path strFileName) + : m_fullPath(fullPath) + , m_bindRoot(bindRoot) + , m_flags(flags) + , m_nextBundle(nextBundle) + , m_strFileName(strFileName) + { + } + + AZ::IO::Path m_strFileName; + AZStd::string m_fullPath; + AZStd::string m_bindRoot; + AZ::IO::PathView m_nextBundle; + int m_flags; + }; + + AZStd::vector m_archivesWithCatalogsToLoad; }; } diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp index e6b8211c28..3d76bcefc4 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp @@ -565,7 +565,7 @@ namespace AzFramework if (!bytes.empty()) { - AZStd::shared_ptr < AzFramework::AssetRegistry> prevRegistry; + AZStd::shared_ptr prevRegistry; if (!m_initialized) { // First time initialization may have updates already processed which we want to apply @@ -589,7 +589,6 @@ namespace AzFramework AZ_TracePrintf("AssetCatalog", "Loaded registry containing %u assets.\n", m_registry->m_assetIdToInfo.size()); // It's currently possible in tools for us to have received updates from AP which were applied before the catalog was ready to load - // due to CryPak and CrySystem coming online later than our components if (!m_initialized) { ApplyDeltaCatalog(prevRegistry); @@ -611,12 +610,13 @@ namespace AzFramework // the mutex. If the listener tries to perform a blocking asset load via GetAsset() / BlockUntilLoadComplete(), the spawned asset // thread will make a call to the AssetCatalogRequestBus and block on the held mutex. This would cause a deadlock, since the listener // won't free the mutex until the load is complete. - // So instead, queue the notification until the next tick, so that it doesn't occur within the AssetCatalogRequestBus mutex, and also + // So instead, queue the notification until after the AssetCatalogRequestBus mutex is unlocked for the current thread, and also // so that the entire AssetCatalog initialization is complete. - AZ::TickBus::QueueFunction([catalogRegistryString = AZStd::string(catalogRegistryFile)]() - { - AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryString.c_str()); - }); + auto OnCatalogLoaded = [catalogRegistryString = AZStd::string(catalogRegistryFile)]() + { + AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryString.c_str()); + }; + AZ::Data::AssetCatalogRequestBus::QueueFunction(AZStd::move(OnCatalogLoaded)); } } @@ -978,6 +978,7 @@ namespace AzFramework AZStd::lock_guard lock(m_registryMutex); m_registry->Clear(); + m_initialized = false; } diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp index f9eefa7639..26a22f4625 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp @@ -61,6 +61,7 @@ namespace AzFramework //========================================================================= void AssetRegistry::Clear() { + m_assetDependencies = {}; m_assetIdToInfo = AssetIdToInfoMap(); m_assetPathToId = AssetPathToIdMap(); } diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp index f820ee56ed..abbb9ec2f1 100644 --- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp +++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp @@ -10,11 +10,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -89,19 +89,19 @@ namespace AzFramework bool FileTagManager::Save(FileTagType fileTagType, const AZStd::string& destinationFilePath = AZStd::string()) { AzFramework::FileTag::FileTagAsset* fileTagAsset = GetFileTagAsset(fileTagType); - AZStd::string filePathToSave = destinationFilePath; + AZ::IO::Path filePathToSave = destinationFilePath; if (filePathToSave.empty()) { filePathToSave = FileTagQueryManager::GetDefaultFileTagFilePath(fileTagType); } - if (!AzFramework::StringFunc::EndsWith(filePathToSave, AzFramework::FileTag::FileTagAsset::Extension())) + if (!filePathToSave.Extension().Native().ends_with(AzFramework::FileTag::FileTagAsset::Extension())) { AZ_Error("FileTag", false, "Unable to save tag file (%s). Invalid file extension, file tag can only have (%s) extension.\n", filePathToSave.c_str(), AzFramework::FileTag::FileTagAsset::Extension()); return false; } - return AZ::Utils::SaveObjectToFile(filePathToSave, AZ::DataStream::StreamType::ST_XML, fileTagAsset); + return AZ::Utils::SaveObjectToFile(filePathToSave.Native(), AZ::DataStream::StreamType::ST_XML, fileTagAsset); } AZ::Outcome FileTagManager::AddTagsInternal(AZStd::string filePath, FileTagType fileTagType, AZStd::vector fileTags, AzFramework::FileTag::FilePatternType filePatternType) @@ -239,17 +239,22 @@ namespace AzFramework QueryFileTagsEventBus::Handler::BusDisconnect(); } - AZStd::string FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType fileTagType) + AZ::IO::Path FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType fileTagType) { - auto destinationFilePath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / EngineAssetSourceRelPath; + AZ::IO::Path destinationFilePath; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + settingsRegistry->Get(destinationFilePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + } + destinationFilePath /= EngineAssetSourceRelPath; destinationFilePath /= fileTagType == FileTagType::Exclude ? ExcludeFileName : IncludeFileName; destinationFilePath.ReplaceExtension(AzFramework::FileTag::FileTagAsset::Extension()); - return destinationFilePath.String(); + return destinationFilePath; } bool FileTagQueryManager::Load(const AZStd::string& filePath) { - AZStd::string fileToLoad = filePath; + AZ::IO::Path fileToLoad = filePath; if (fileToLoad.empty()) { fileToLoad = GetDefaultFileTagFilePath(m_fileTagType); diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h index be3d6e6d08..d2dee3f629 100644 --- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h +++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace AzFramework @@ -88,7 +89,7 @@ namespace AzFramework ///////////////////////////////////////////////////////////////////////// - static AZStd::string GetDefaultFileTagFilePath(FileTagType fileTagType); + static AZ::IO::Path GetDefaultFileTagFilePath(FileTagType fileTagType); protected: diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp b/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp index 1e03a6df0d..1d09500415 100644 --- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp @@ -16,6 +16,7 @@ #include #include +#include #include namespace AzFramework @@ -66,7 +67,8 @@ namespace AzFramework m_excludeFileQueryManager.reset(aznew FileTagQueryManager(FileTagType::Exclude)); if (!m_excludeFileQueryManager.get()->Load()) { - AZ_Error("FileTagQueryComponent", false, "Not able to load default exclude file (%s). Please make sure that it exists on disk.\n", FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType::Exclude).c_str()); + AZ_Error("FileTagQueryComponent", false, "Not able to load default exclude file (%s). Please make sure that it exists on disk.\n", + FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType::Exclude).c_str()); } AzFramework::AssetCatalogEventBus::Handler::BusConnect(); diff --git a/Code/Framework/AzFramework/Tests/AssetCatalog.cpp b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp index 8cdc54f34f..9e5c72f74c 100644 --- a/Code/Framework/AzFramework/Tests/AssetCatalog.cpp +++ b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp @@ -308,7 +308,9 @@ namespace UnitTest registry->Set(projectPathKey, "AutomatedTesting"); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); - m_app->Start(desc); + AZ::ComponentApplication::StartupParameters startupParameters; + startupParameters.m_loadAssetCatalog = false; + m_app->Start(desc, startupParameters); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash diff --git a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp index 0cce93d751..f0417d206e 100644 --- a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp +++ b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp @@ -45,6 +45,13 @@ namespace AzGameFramework enginePakPath = AZ::IO::FixedMaxPath(AZ::Utils::GetExecutableDirectory()) / "engine.pak"; m_archive->OpenPack("@products@", enginePakPath.Native()); } + + // By default, load all archives in the products folder. + // If you want to adjust this for your project, make sure that the archive containing + // the bootstrap for the settings registry is still loaded here, and any archives containing + // assets used early in startup, like default shaders, are loaded here. + constexpr AZStd::string_view paksFolder = "@products@/*.pak"; // (@products@ assumed) + m_archive->OpenPacks(paksFolder); } GameApplication::~GameApplication() @@ -82,7 +89,7 @@ namespace AzGameFramework // Used the lowercase the platform name since the bootstrap.game...setreg is being loaded // from the asset cache root where all the files are in lowercased from regardless of the filesystem case-sensitivity - static constexpr char filename[] = "bootstrap.game." AZ_BUILD_CONFIGURATION_TYPE "." AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER ".setreg"; + static constexpr char filename[] = "bootstrap.game." AZ_BUILD_CONFIGURATION_TYPE ".setreg"; AZ::IO::FixedMaxPath cacheRootPath; if (registry.Get(cacheRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder)) diff --git a/Code/LauncherUnified/Launcher.cpp b/Code/LauncherUnified/Launcher.cpp index 0ef9cdfc3b..0f832ff1a5 100644 --- a/Code/LauncherUnified/Launcher.cpp +++ b/Code/LauncherUnified/Launcher.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -664,6 +665,8 @@ namespace O3DELauncher systemInitParams.pSystem = CreateSystemInterface(systemInitParams); #endif // !defined(AZ_MONOLITHIC_BUILD) + AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "LegacySystemInterfaceCreated", R"({})"); + ReturnCode status = ReturnCode::Success; if (systemInitParams.pSystem) diff --git a/Code/Legacy/CrySystem/IDebugCallStack.cpp b/Code/Legacy/CrySystem/IDebugCallStack.cpp index deb9c11581..a02658d34a 100644 --- a/Code/Legacy/CrySystem/IDebugCallStack.cpp +++ b/Code/Legacy/CrySystem/IDebugCallStack.cpp @@ -242,7 +242,7 @@ void IDebugCallStack::WriteLineToLog(const char* format, ...) va_end(ArgList); AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; - AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle); + AZ::IO::FileIOBase::GetDirectInstance()->Open("@log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle); if (fileHandle != AZ::IO::InvalidHandle) { AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, szBuffer, strlen(szBuffer)); @@ -254,7 +254,7 @@ void IDebugCallStack::WriteLineToLog(const char* format, ...) ////////////////////////////////////////////////////////////////////////// void IDebugCallStack::StartMemLog() { - AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle); + AZ::IO::FileIOBase::GetDirectInstance()->Open("@log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle); assert(m_memAllocFileHandle != AZ::IO::InvalidHandle); } diff --git a/Code/Legacy/CrySystem/System.h b/Code/Legacy/CrySystem/System.h index 015b09a69b..a3a0f12278 100644 --- a/Code/Legacy/CrySystem/System.h +++ b/Code/Legacy/CrySystem/System.h @@ -590,7 +590,7 @@ public: bool InitVTuneProfiler(); - void OpenBasicPaks(); + void OpenPlatformPaks(); void OpenLanguagePak(const char* sLanguage); void OpenLanguageAudioPak(const char* sLanguage); void GetLocalizedPath(const char* sLanguage, AZStd::string& sLocalizedPath); diff --git a/Code/Legacy/CrySystem/SystemInit.cpp b/Code/Legacy/CrySystem/SystemInit.cpp index ee1c498a9a..ac0683cf1b 100644 --- a/Code/Legacy/CrySystem/SystemInit.cpp +++ b/Code/Legacy/CrySystem/SystemInit.cpp @@ -649,7 +649,7 @@ bool CSystem::InitFileSystem_LoadEngineFolders(const SSystemInitParams&) auto projectName = AZ::Utils::GetProjectName(); AZ_Printf(AZ_TRACE_SYSTEM_WINDOW, "Project Name: %s\n", projectName.empty() ? "None specified" : projectName.c_str()); - OpenBasicPaks(); + OpenPlatformPaks(); // Load game-specific folder. LoadConfiguration("game.cfg"); @@ -786,29 +786,19 @@ void CSystem::InitLocalization() OpenLanguageAudioPak(language.c_str()); } -void CSystem::OpenBasicPaks() +void CSystem::OpenPlatformPaks() { - static bool bBasicPaksLoaded = false; - if (bBasicPaksLoaded) + static bool bPlatformPaksLoaded = false; + if (bPlatformPaksLoaded) { return; } - bBasicPaksLoaded = true; - - // open pak files - constexpr AZStd::string_view paksFolder = "@products@/*.pak"; // (@products@ assumed) - m_env.pCryPak->OpenPacks(paksFolder); - - InlineInitializationProcessing("CSystem::OpenBasicPaks OpenPacks( paksFolder.c_str() )"); + bPlatformPaksLoaded = true; ////////////////////////////////////////////////////////////////////////// // Open engine packs ////////////////////////////////////////////////////////////////////////// - const char* const assetsDir = "@products@"; - - // After game paks to have same search order as with files on disk - m_env.pCryPak->OpenPack(assetsDir, "engine.pak"); #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION SYSTEMINIT_CPP_SECTION_15 @@ -816,6 +806,7 @@ void CSystem::OpenBasicPaks() #endif #ifdef AZ_PLATFORM_ANDROID + const char* const assetsDir = "@products@"; // Load Android Obb files if available const char* obbStorage = AZ::Android::Utils::GetObbStoragePath(); AZStd::string mainObbPath = AZStd::move(AZStd::string::format("%s/%s", obbStorage, AZ::Android::Utils::GetObbFileName(true))); @@ -824,7 +815,7 @@ void CSystem::OpenBasicPaks() m_env.pCryPak->OpenPack(assetsDir, patchObbPath.c_str()); #endif //AZ_PLATFORM_ANDROID - InlineInitializationProcessing("CSystem::OpenBasicPaks OpenPacks( Engine... )"); + InlineInitializationProcessing("CSystem::OpenPlatformPaks OpenPacks( Engine... )"); } ////////////////////////////////////////////////////////////////////////// @@ -1328,7 +1319,7 @@ AZ_POP_DISABLE_WARNING ////////////////////////////////////////////////////////////////////////// // Open basic pak files after intro movie playback started ////////////////////////////////////////////////////////////////////////// - OpenBasicPaks(); + OpenPlatformPaks(); ////////////////////////////////////////////////////////////////////////// // AUDIO diff --git a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp index c65ab24aed..7823a0582f 100644 --- a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp +++ b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp @@ -159,6 +159,7 @@ namespace AssetProcessor builderDesc.m_busId = m_builderId; builderDesc.m_createJobFunction = AZStd::bind(&SettingsRegistryBuilder::CreateJobs, this, AZStd::placeholders::_1, AZStd::placeholders::_2); builderDesc.m_processJobFunction = AZStd::bind(&SettingsRegistryBuilder::ProcessJob, this, AZStd::placeholders::_1, AZStd::placeholders::_2); + builderDesc.m_version = 1; AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDesc); @@ -259,6 +260,11 @@ namespace AssetProcessor scratchBuffer.reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer; AZStd::fixed_vector platformCodes; AzFramework::PlatformHelper::AppendPlatformCodeNames(platformCodes, request.m_platformInfo.m_identifier); + AZ_Assert(platformCodes.size() <= 1, "A one-to-one mapping of asset type platform identifier" + " to platform codename is required in the SettingsRegistryBuilder." + " The bootstrap.game is now only produced per build configuration and doesn't take into account" + " different platforms names"); + const AZStd::string& assetPlatformIdentifier = request.m_jobDescription.GetPlatformIdentifier(); // Determines the suffix that will be used for the launcher based on processing server vs non-server assets const char* launcherType = assetPlatformIdentifier != AzFramework::PlatformHelper::GetPlatformName(AzFramework::PlatformId::SERVER) @@ -293,9 +299,9 @@ namespace AssetProcessor outputBuffer.Reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer; SettingsExporter exporter(outputBuffer, excludes); - for (AZStd::string_view platform : platformCodes) + if (!platformCodes.empty()) { - AZ::u32 productSubID = static_cast(AZStd::hash{}(platform)); // Deliberately ignoring half the bits. + AZStd::string_view platform = platformCodes.front(); for (size_t i = 0; i < AZStd::size(specializations); ++i) { const AZ::SettingsRegistryInterface::Specializations& specialization = specializations[i]; @@ -337,7 +343,7 @@ namespace AssetProcessor // The purpose of this section is to copy the Gem's SourcePaths from the Global Settings Registry // the local SettingsRegistry. The reason this is needed is so that the call to // `MergeSettingsToRegistry_GemRegistries` below is able to locate each gem's "/Registry" folder - // that will be merged into the bootstrap.game...setreg file + // that will be merged into the bootstrap.game..setreg file // This is used by the GameLauncher applications to read from a single merged .setreg file // containing the settings needed to run a game/simulation without have access to the source code base registry AZStd::vector gemInfos; @@ -407,9 +413,8 @@ namespace AssetProcessor return; } - outputPath += specialization.GetSpecialization(0); // Append configuration - outputPath += '.'; - outputPath += platform; + AZStd::string_view specializationString(specialization.GetSpecialization(0)); + outputPath += specializationString; // Append configuration outputPath += ".setreg"; AZ::IO::SystemFile file; @@ -426,7 +431,10 @@ namespace AssetProcessor } file.Close(); - response.m_outputProducts.emplace_back(outputPath, m_assetType, productSubID + aznumeric_cast(i)); + AZ::u32 hashedSpecialization = static_cast(AZStd::hash{}(specializationString)); + AZ_Assert(hashedSpecialization != 0, "Product ID generation failed for specialization %.*s. This can result in a product ID collision with other builders for this asset.", + AZ_STRING_ARG(specializationString)); + response.m_outputProducts.emplace_back(outputPath, m_assetType, hashedSpecialization); response.m_outputProducts.back().m_dependenciesHandled = true; outputPath.erase(extensionOffset); diff --git a/Code/Tools/SerializeContextTools/SliceConverter.cpp b/Code/Tools/SerializeContextTools/SliceConverter.cpp index 6cfe072f80..d0528a9cad 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.cpp +++ b/Code/Tools/SerializeContextTools/SliceConverter.cpp @@ -81,8 +81,6 @@ namespace AZ // Load the asset catalog so that we can find any nested assets successfully. We also need to tick the tick bus // so that the OnCatalogLoaded event gets processed now, instead of during application shutdown. - AZ::Data::AssetCatalogRequestBus::Broadcast( - &AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@products@/assetcatalog.xml"); application.Tick(); AZStd::string logggingScratchBuffer; diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp index 3f5761270a..c232d4bba7 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -132,7 +133,6 @@ namespace AZ m_createDefaultScene = false; } - AzFramework::AssetCatalogEventBus::Handler::BusConnect(); TickBus::Handler::BusConnect(); // Listen for window system requests (e.g. requests for default window handle) @@ -143,6 +143,20 @@ namespace AZ Render::Bootstrap::DefaultWindowBus::Handler::BusConnect(); Render::Bootstrap::RequestBus::Handler::BusConnect(); + + // If the settings registry isn't available, something earlier in startup will report that failure. + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + // Automatically register the event if it's not registered, because + // this system is initialized before the settings registry has loaded the event list. + AZ::ComponentApplicationLifecycle::RegisterHandler( + *settingsRegistry, m_componentApplicationLifecycleHandler, + [this](AZStd::string_view /*path*/, AZ::SettingsRegistryInterface::Type /*type*/) + { + Initialize(); + }, + "LegacySystemInterfaceCreated"); + } } void BootstrapSystemComponent::Deactivate() @@ -153,7 +167,6 @@ namespace AZ AzFramework::WindowSystemRequestBus::Handler::BusDisconnect(); AzFramework::WindowSystemNotificationBus::Handler::BusDisconnect(); TickBus::Handler::BusDisconnect(); - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); m_brdfTexture = nullptr; RemoveRenderPipeline(); @@ -164,14 +177,14 @@ namespace AZ m_windowHandle = nullptr; } - void BootstrapSystemComponent::OnCatalogLoaded(const char* /*catalogFile*/) + void BootstrapSystemComponent::Initialize() { - if (m_isAssetCatalogLoaded) + if (m_isInitialized) { return; } - m_isAssetCatalogLoaded = true; + m_isInitialized = true; if (!RPI::RPISystemInterface::Get()->IsInitialized()) { @@ -216,7 +229,7 @@ namespace AZ { m_windowHandle = windowHandle; - if (m_isAssetCatalogLoaded) + if (m_isInitialized) { CreateWindowContext(); if (m_createDefaultScene) diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h index 566d19b1a4..74679f5753 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h @@ -8,10 +8,10 @@ #pragma once #include -#include #include +#include +#include -#include #include #include #include @@ -29,7 +29,6 @@ #include #include - namespace AZ { namespace Render @@ -40,7 +39,6 @@ namespace AZ : public Component , public TickBus::Handler , public AzFramework::WindowNotificationBus::Handler - , public AzFramework::AssetCatalogEventBus::Handler , public AzFramework::WindowSystemNotificationBus::Handler , public AzFramework::WindowSystemRequestBus::Handler , public Render::Bootstrap::DefaultWindowBus::Handler @@ -82,13 +80,12 @@ namespace AZ void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; int GetTickOrder() override; - // AzFramework::AssetCatalogEventBus::Handler overrides ... - void OnCatalogLoaded(const char* catalogFile) override; - // AzFramework::WindowSystemNotificationBus::Handler overrides ... void OnWindowCreated(AzFramework::NativeWindowHandle windowHandle) override; private: + void Initialize(); + void CreateDefaultRenderPipeline(); void CreateDefaultScene(); void DestroyDefaultScene(); @@ -105,7 +102,7 @@ namespace AZ RPI::ScenePtr m_defaultScene = nullptr; AZStd::shared_ptr m_defaultFrameworkScene = nullptr; - bool m_isAssetCatalogLoaded = false; + bool m_isInitialized = false; // The id of the render pipeline created by this component RPI::RenderPipelineId m_renderPipelineId; @@ -119,6 +116,8 @@ namespace AZ // Maps AZ scenes to RPI scene weak pointers to allow looking up a ScenePtr instead of a raw Scene* AZStd::unordered_map> m_azSceneToAtomSceneMap; + + AZ::SettingsRegistryInterface::NotifyEventHandler m_componentApplicationLifecycleHandler; }; } // namespace Bootstrap } // namespace Render diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index e8ec77e7bd..ba3bfe4718 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -175,8 +175,6 @@ namespace AtomToolsFramework AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast( &AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized); - AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@products@/assetcatalog.xml"); - if (!AZ::RPI::RPISystemInterface::Get()->IsInitialized()) { AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets(); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.cpp index fc5b31297a..d46e7b27be 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.cpp @@ -47,30 +47,27 @@ namespace AtomToolsFramework void PreviewRendererSystemComponent::Activate() { - AzFramework::AssetCatalogEventBus::Handler::BusConnect(); AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect(); PreviewRendererSystemRequestBus::Handler::BusConnect(); + + AZ::TickBus::QueueFunction( + [this]() + { + if (!m_previewRenderer) + { + m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer( + "PreviewRendererSystemComponent Preview Scene", "PreviewRendererSystemComponent Preview Pipeline")); + } + }); } void PreviewRendererSystemComponent::Deactivate() { PreviewRendererSystemRequestBus::Handler::BusDisconnect(); AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect(); - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); m_previewRenderer.reset(); } - void PreviewRendererSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile) - { - AZ::TickBus::QueueFunction([this](){ - if (!m_previewRenderer) - { - m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer( - "PreviewRendererSystemComponent Preview Scene", "PreviewRendererSystemComponent Preview Pipeline")); - } - }); - } - void PreviewRendererSystemComponent::OnApplicationAboutToStop() { m_previewRenderer.reset(); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.h index 8110d84794..0b145bbdf1 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.h @@ -9,7 +9,6 @@ #pragma once #include -#include #include #include #include @@ -19,7 +18,6 @@ namespace AtomToolsFramework //! System component that manages a global PreviewRenderer. class PreviewRendererSystemComponent final : public AZ::Component - , public AzFramework::AssetCatalogEventBus::Handler , public AzFramework::ApplicationLifecycleEvents::Bus::Handler , public PreviewRendererSystemRequestBus::Handler { @@ -38,9 +36,6 @@ namespace AtomToolsFramework void Deactivate() override; private: - // AzFramework::AssetCatalogEventBus::Handler overrides ... - void OnCatalogLoaded(const char* catalogFile) override; - // AzFramework::ApplicationLifecycleEvents overrides... void OnApplicationAboutToStop() override; diff --git a/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab b/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab index b5aed9d14b..92874574e4 100644 --- a/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab +++ b/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab @@ -581,7 +581,7 @@ { "id": { "materialAssetId": { - "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", "subId": 803645540 } } @@ -593,7 +593,7 @@ "id": { "lodIndex": 0, "materialAssetId": { - "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", "subId": 803645540 } } @@ -608,10 +608,10 @@ "Configuration": { "ModelAsset": { "assetId": { - "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", - "subId": 277333723 + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", + "subId": 277889906 }, - "assetHint": "objects/groudplane/groundplane_521x521m.azmodel" + "assetHint": "objects/groudplane/groundplane_512x512m.azmodel" } } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice b/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice index b4c9eac10f..e0c7c9e456 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice +++ b/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice @@ -836,7 +836,7 @@ - + diff --git a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp index b7ee805b3c..00629eb65d 100644 --- a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp +++ b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp @@ -143,6 +143,7 @@ namespace Blast void BlastSystemComponent::Deactivate() { AZ_PROFILE_FUNCTION(Physics); + AZ::Data::AssetBus::MultiHandler::BusDisconnect(); CrySystemEventBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); BlastSystemRequestBus::Handler::BusDisconnect(); diff --git a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp index c9fc656488..168a3640a8 100644 --- a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp @@ -9,43 +9,40 @@ #include "BundlingSystemComponent.h" #include +#include +#include #include #include +#include #include -#include - -#include -#include - #include + namespace LmbrCentral { const char bundleRoot[] = "@products@"; + // Calls the LoadBundles method + static void ConsoleCommandLoadBundles(const AZ::ConsoleCommandContainer& commandArgs); + // Calls the UnloadBundles method + static void ConsoleCommandUnloadBundles(const AZ::ConsoleCommandContainer& commandArgs); + + AZ_CONSOLEFREEFUNC("loadbundles", ConsoleCommandLoadBundles, AZ::ConsoleFunctorFlags::Null, "Load Asset Bundles"); + AZ_CONSOLEFREEFUNC("unloadbundles", ConsoleCommandUnloadBundles, AZ::ConsoleFunctorFlags::Null, "Unload Asset Bundles"); + void BundlingSystemComponent::Activate() { BundlingSystemRequestBus::Handler::BusConnect(); - CrySystemEventBus::Handler::BusConnect(); AZ::IO::ArchiveNotificationBus::Handler::BusConnect(); } void BundlingSystemComponent::Deactivate() { AZ::IO::ArchiveNotificationBus::Handler::BusDisconnect(); - CrySystemEventBus::Handler::BusDisconnect(); BundlingSystemRequestBus::Handler::BusDisconnect(); } - void BundlingSystemComponent::OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) - { - AZ_UNUSED(systemInitParams); - - system.GetIConsole()->AddCommand("loadbundles", ConsoleCommandLoadBundles); - system.GetIConsole()->AddCommand("unloadbundles", ConsoleCommandUnloadBundles); - } - void BundlingSystemComponent::Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context)) @@ -58,7 +55,7 @@ namespace LmbrCentral AZStd::vector BundlingSystemComponent::GetBundleList(const char* bundlePath, const char* bundleExtension) const { - AZStd::string fileFilter{ AZStd::string::format("*%s",bundleExtension) }; + AZStd::string fileFilter{ AZStd::string::format("*%s", bundleExtension) }; AZStd::vector bundleList; AZ::IO::FileIOBase::GetInstance()->FindFiles(bundlePath, fileFilter.c_str(), [&bundleList](const char* foundPath) -> bool @@ -73,29 +70,28 @@ namespace LmbrCentral return bundleList; } - void BundlingSystemComponent::ConsoleCommandLoadBundles(IConsoleCmdArgs* pCmdArgs) + void ConsoleCommandLoadBundles(const AZ::ConsoleCommandContainer& commandArgs) { const char defaultBundleFolder[] = "bundles"; const char defaultBundleExtension[] = ".pak"; - const char* bundleFolder = pCmdArgs->GetArgCount() > 1 ? pCmdArgs->GetArg(1) : defaultBundleFolder; - const char* bundleExtension = pCmdArgs->GetArgCount() > 2 ? pCmdArgs->GetArg(2) : defaultBundleExtension; + AZ::CVarFixedString bundleFolder = commandArgs.size() > 0 ? AZ::CVarFixedString(commandArgs[0]) : defaultBundleFolder; + AZ::CVarFixedString bundleExtension = commandArgs.size() > 1 ? AZ::CVarFixedString(commandArgs[1]) : defaultBundleExtension; - BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::LoadBundles, bundleFolder, bundleExtension); + BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::LoadBundles, bundleFolder.c_str(), bundleExtension.c_str()); } - void BundlingSystemComponent::ConsoleCommandUnloadBundles(IConsoleCmdArgs* pCmdArgs) + void ConsoleCommandUnloadBundles([[maybe_unused]] const AZ::ConsoleCommandContainer& commandArgs) { - AZ_UNUSED(pCmdArgs); BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::UnloadBundles); } void BundlingSystemComponent::UnloadBundles() { - ISystem* crySystem{ GetISystem() }; - if (!crySystem) + auto archive = AZ::Interface::Get(); + if (!archive) { - AZ_Error("BundlingSystem", false, "Couldn't Get ISystem to unload bundles!"); + AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load bundles!"); return; } if (!m_bundleModeBundles.size()) @@ -106,7 +102,7 @@ namespace LmbrCentral AZStd::lock_guard openBundleLock(m_bundleModeMutex); for (const auto& thisBundle : m_bundleModeBundles) { - if (crySystem->GetIPak()->ClosePack(thisBundle.c_str())) + if (archive->ClosePack(thisBundle.c_str())) { AZ_TracePrintf("BundlingSystem", "Unloaded %s\n",thisBundle.c_str()); } @@ -128,15 +124,8 @@ namespace LmbrCentral return; } - ISystem* crySystem{ GetISystem() }; - if (!crySystem) - { - AZ_Error("BundlingSystem", false, "Couldn't Get ISystem to load bundles!"); - return; - } - - auto cryPak = crySystem->GetIPak(); - if (!cryPak) + auto archive = AZ::Interface::Get(); + if (!archive) { AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load bundles!"); return; @@ -152,8 +141,8 @@ namespace LmbrCentral } } AZStd::string bundlePath; - AzFramework::StringFunc::Path::Join(bundleRoot, thisBundle.c_str(), bundlePath); - if (cryPak->OpenPack(bundleRoot, thisBundle.c_str())) + AZ::StringFunc::Path::Join(bundleRoot, thisBundle.c_str(), bundlePath); + if (archive->OpenPack(bundleRoot, thisBundle.c_str())) { AZ_TracePrintf("BundlingSystem", "Loaded bundle %s\n",bundlePath.c_str()); m_bundleModeBundles.emplace_back(AZStd::move(bundlePath)); @@ -230,28 +219,21 @@ namespace LmbrCentral void BundlingSystemComponent::OpenDependentBundles(const char* bundleName, AZStd::shared_ptr bundleManifest) { - ISystem* crySystem{ GetISystem() }; - if (!crySystem) - { - AZ_Error("BundlingSystem", false, "Couldn't Get ISystem to load dependent bundles for %s", bundleName); - return; - } - - auto cryPak{ crySystem->GetIPak() }; - if (!cryPak) + auto archive = AZ::Interface::Get(); + if (!archive) { AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load dependent bundles for %s", bundleName); return; } AZStd::string folderPath; - AzFramework::StringFunc::Path::GetFolderPath(bundleName, folderPath); + AZ::StringFunc::Path::GetFolderPath(bundleName, folderPath); for (const auto& thisBundle : bundleManifest->GetDependentBundleNames()) { AZStd::string bundlePath; - AzFramework::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath); + AZ::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath); - if (!cryPak->OpenPack(bundleRoot, bundlePath.c_str())) + if (!archive->OpenPack(bundleRoot, bundlePath.c_str())) { // We're not bailing here intentionally - try to open the remaining bundles AZ_Warning("BundlingSystem", false, "Failed to open dependent bundle %s of bundle %s", bundlePath.c_str(), bundleName); @@ -300,28 +282,21 @@ namespace LmbrCentral void BundlingSystemComponent::CloseDependentBundles(const char* bundleName, AZStd::shared_ptr bundleManifest) { - ISystem* crySystem{ GetISystem() }; - if (!crySystem) - { - AZ_Error("BundlingSystem", false, "Couldn't get ISystem to close dependent bundles for %s", bundleName); - return; - } - - auto cryPak{ crySystem->GetIPak() }; - if (!cryPak) + auto archive = AZ::Interface::Get(); + if (!archive) { AZ_Error("BundlingSystem", false, "Couldn't get IArchive to close dependent bundles for %s", bundleName); return; } AZStd::string folderPath; - AzFramework::StringFunc::Path::GetFolderPath(bundleName, folderPath); + AZ::StringFunc::Path::GetFolderPath(bundleName, folderPath); for (const auto& thisBundle : bundleManifest->GetDependentBundleNames()) { AZStd::string bundlePath; - AzFramework::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath); + AZ::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath); - if (!cryPak->ClosePack(bundlePath.c_str())) + if (!archive->ClosePack(bundlePath.c_str())) { // We're not bailing here intentionally - try to close the remaining bundles AZ_Warning("BundlingSystem", false, "Failed to close dependent bundle %s of bundle %s", bundlePath.c_str(), bundleName); diff --git a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h index 15fdf1103c..e7c8775530 100644 --- a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h +++ b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h @@ -19,11 +19,8 @@ #include -#include #include -struct IConsoleCmdArgs; - namespace AzFramework { class AssetBundleManifest; @@ -42,10 +39,9 @@ namespace LmbrCentral * System component for managing bundles */ class BundlingSystemComponent - : public AZ::Component, - public BundlingSystemRequestBus::Handler, - public CrySystemEventBus::Handler, - public AZ::IO::ArchiveNotificationBus::Handler + : public AZ::Component + , public BundlingSystemRequestBus::Handler + , public AZ::IO::ArchiveNotificationBus::Handler { public: AZ_COMPONENT(BundlingSystemComponent, "{0FB7153D-EE80-4B1C-9584-134270401AAF}"); @@ -70,13 +66,6 @@ namespace LmbrCentral void BundleOpened(const char* bundleName, AZStd::shared_ptr bundleManifest, const char* nextBundle, AZStd::shared_ptr bundleCatalog) override; void BundleClosed(const char* bundleName) override; - // CrySystemEventBus - void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) override; - - // Calls the LoadBundles method - static void ConsoleCommandLoadBundles(IConsoleCmdArgs* pCmdArgs); - // Calls the UnloadBundles method - static void ConsoleCommandUnloadBundles(IConsoleCmdArgs* pCmdArgs); AZStd::vector GetBundleList(const char* bundlePath, const char* bundleExtension) const; diff --git a/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp b/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp index e509890efa..027d19fdef 100644 --- a/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp +++ b/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp @@ -83,8 +83,6 @@ namespace LmbrCentral { - static const char* s_assetCatalogFilename = "assetcatalog.xml"; - using LmbrCentralAllocatorScope = AZ::AllocatorScope; // This component boots the required allocators for LmbrCentral everywhere but AssetBuilders @@ -353,8 +351,7 @@ namespace LmbrCentral AZ_Assert(AZ::Data::AssetManager::IsReady(), "Asset manager isn't ready!"); // Add asset types and extensions to AssetCatalog. Uses "AssetCatalogService". - auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler(); - if (assetCatalog) + if (auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler(); assetCatalog) { assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo::Uuid()); assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo::Uuid()); @@ -373,7 +370,6 @@ namespace LmbrCentral assetCatalog->AddExtension("cax"); } - CrySystemEventBus::Handler::BusConnect(); AZ::Data::AssetManagerNotificationBus::Handler::BusConnect(); @@ -445,7 +441,6 @@ namespace LmbrCentral m_unhandledAssetInfo.clear(); AZ::Data::AssetManagerNotificationBus::Handler::BusDisconnect(); - CrySystemEventBus::Handler::BusDisconnect(); // AssetHandler's destructor calls Unregister() m_assetHandlers.clear(); @@ -456,42 +451,6 @@ namespace LmbrCentral } m_allocatorShutdowns.clear(); } - - void LmbrCentralSystemComponent::OnCrySystemPreInitialize([[maybe_unused]] ISystem& system, [[maybe_unused]] const SSystemInitParams& systemInitParams) - { - EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, StartMonitoringAssets); - } - - void LmbrCentralSystemComponent::OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) - { -#if !defined(AZ_MONOLITHIC_BUILD) - // When module is linked dynamically, we must set our gEnv pointer. - // When module is linked statically, we'll share the application's gEnv pointer. - gEnv = system.GetGlobalEnvironment(); -#endif - - // Enable catalog now that application's asset root is set. - if (system.GetGlobalEnvironment()->IsEditor()) - { - // In the editor, we build the catalog by scanning the disk. - if (systemInitParams.pUserCallback) - { - systemInitParams.pUserCallback->OnInitProgress("Refreshing asset catalog..."); - } - } - - // load the catalog from disk (supported over VFS). - EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, LoadCatalog, AZStd::string::format("@products@/%s", s_assetCatalogFilename).c_str()); - } - - void LmbrCentralSystemComponent::OnCrySystemShutdown([[maybe_unused]] ISystem& system) - { - EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, StopMonitoringAssets); - -#if !defined(AZ_MONOLITHIC_BUILD) - gEnv = nullptr; -#endif - } } // namespace LmbrCentral #if !defined(LMBR_CENTRAL_EDITOR) diff --git a/Gems/LmbrCentral/Code/Source/LmbrCentral.h b/Gems/LmbrCentral/Code/Source/LmbrCentral.h index 9a0c327ea7..650061dce6 100644 --- a/Gems/LmbrCentral/Code/Source/LmbrCentral.h +++ b/Gems/LmbrCentral/Code/Source/LmbrCentral.h @@ -15,8 +15,6 @@ #include #include -#include - /*! * \namespace LmbrCentral * LmbrCentral ties together systems from CryEngine and systems from the AZ framework. @@ -49,7 +47,6 @@ namespace LmbrCentral */ class LmbrCentralSystemComponent : public AZ::Component - , private CrySystemEventBus::Handler , private AZ::Data::AssetManagerNotificationBus::Handler { public: @@ -71,13 +68,6 @@ namespace LmbrCentral void Deactivate() override; //////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////// - // CrySystemEvents - void OnCrySystemPreInitialize(ISystem& system, const SSystemInitParams& systemInitParams) override; - void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) override; - void OnCrySystemShutdown(ISystem& system) override; - //////////////////////////////////////////////////////////////////////////// - AZStd::vector > m_assetHandlers; AZStd::vector > m_unhandledAssetInfo; AZStd::vector> m_allocatorShutdowns; diff --git a/Registry/application_lifecycle_events.setreg b/Registry/application_lifecycle_events.setreg new file mode 100644 index 0000000000..0d9cd0f170 --- /dev/null +++ b/Registry/application_lifecycle_events.setreg @@ -0,0 +1,30 @@ +// The Lifecycle events contains the name of the event as a string +// ComponentApplication derived classes +// will set these these keys to a JSON Object indicate an event has occured +// A callback can be registered with the SettingsRegistry +// to be notified when that key is set +// The JSON object that is set will contain any payload data +// related to the event +{ + "O3DE" : { + "Application": { + "LifecycleEvents": { + "SystemComponentsActivated": {}, + "SystemComponentsDeactivated": {}, + "ReflectionManagerAvailable": {}, + "ReflectionManagerUnavailable": {}, + "SystemAllocatorCreated": {}, + "SystemAllocatorPendingDestruction": {}, + "SettingsRegistryAvailable": {}, + "SettingsRegistryUnavailable": {}, + "ConsoleAvailable": {}, + "ConsoleUnavailable": {}, + "GemsLoaded": {}, + "GemsUnloaded": {}, + "FileIOAvailable": {}, + "FileIOUnavailable": {}, + "LegacySystemInterfaceCreated": {} + } + } + } +} diff --git a/cmake/Projects.cmake b/cmake/Projects.cmake index c09fe0fc6f..34ef3efd9b 100644 --- a/cmake/Projects.cmake +++ b/cmake/Projects.cmake @@ -150,22 +150,43 @@ foreach(project ${LY_PROJECTS}) # Get project name o3de_read_json_key(project_name ${full_directory_path}/project.json "project_name") + # The cmake tar command has a bit of a flaw + # Any paths within the archive files it creates are relative to the current working directory. + # That means with the setup of: + # cwd = "/Cache/pc" + # project product assets = "/Cache/pc/*" + # cmake dependency registry files = "/build/bin/Release/Registry/*" + # Running the tar command would result in the assets being placed in the to layout + # correctly, but the registry files + # engine.pak/ + # ../...build/bin/Release/Registry/cmake_dependencies.*.setreg -> Not correct + # project.json -> Correct + # Generate pak for project in release installs cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE install_base_runtime_output_directory) set(install_engine_pak_template [=[ if("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$") set(install_output_folder "${CMAKE_INSTALL_PREFIX}/@install_base_runtime_output_directory@/@PAL_PLATFORM_NAME@/${CMAKE_INSTALL_CONFIG_NAME}/@LY_BUILD_PERMUTATION@") set(install_pak_output_folder "${install_output_folder}/Cache/@LY_ASSET_DEPLOY_ASSET_TYPE@") + set(runtime_output_directory_RELEASE @CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE@) if(NOT DEFINED LY_ASSET_DEPLOY_ASSET_TYPE) set(LY_ASSET_DEPLOY_ASSET_TYPE @LY_ASSET_DEPLOY_ASSET_TYPE@) endif() message(STATUS "Generating ${install_pak_output_folder}/engine.pak from @full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") file(MAKE_DIRECTORY "${install_pak_output_folder}") cmake_path(SET cache_product_path "@full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") + # Copy the generated cmake_dependencies.*.setreg files for loading gems in non-monolithic to the cache + file(GLOB gem_source_paths_setreg "${runtime_output_directory_RELEASE}/Registry/*.setreg") + # The MergeSettingsToRegistry_TargetBuildDependencyRegistry function looks for lowercase "registry" + # So make sure the to copy it to a lowercase path, so that it works on non-case sensitive filesystems + file(MAKE_DIRECTORY "${cache_product_path}/registry") + file(COPY ${gem_source_paths_setreg} DESTINATION "${cache_product_path}/registry") + file(GLOB product_assets "${cache_product_path}/*") - if(product_assets) + list(APPEND pak_artifacts ${product_assets}) + if(pak_artifacts) execute_process( - COMMAND ${CMAKE_COMMAND} -E tar "cf" "${install_pak_output_folder}/engine.pak" --format=zip -- ${product_assets} + COMMAND ${CMAKE_COMMAND} -E tar "cf" "${install_pak_output_folder}/engine.pak" --format=zip -- ${pak_artifacts} WORKING_DIRECTORY "${cache_product_path}" RESULT_VARIABLE archive_creation_result ) From 2b2e5c6367bfa2eddae2b3ffd44150d1c42aabd6 Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Thu, 4 Nov 2021 14:52:35 +0000 Subject: [PATCH 055/177] Fixed client hierarchy construction to check for authority instead of controller Signed-off-by: Sergey Pereslavtsev --- .../Code/Source/Components/NetworkTransformComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp index bd1e1bf0d9..a6e670a835 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp @@ -44,7 +44,7 @@ namespace Multiplayer GetNetBindComponent()->AddEntityCorrectionEventHandler(m_entityCorrectionEventHandler); ParentEntityIdAddEvent(m_parentChangedEventHandler); - if (!HasController()) + if (!GetNetBindComponent()->IsNetEntityRoleAuthority()) { OnParentChanged(GetParentEntityId()); } From 54c1b009024f1b37f4ae4b68d9e0303c43efbad9 Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Thu, 4 Nov 2021 14:55:03 +0000 Subject: [PATCH 056/177] Fixed AR nightly for non-prefab levels where MultiplayerEditorSystemComponent prints an error Signed-off-by: Sergey Pereslavtsev --- .../MultiplayerEditorSystemComponent.cpp | 117 +++++++++--------- 1 file changed, 60 insertions(+), 57 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index 11aca101b3..a33d6bf946 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -231,79 +231,82 @@ namespace Multiplayer void MultiplayerEditorSystemComponent::OnGameEntitiesStarted() { + IMultiplayerTools* mpTools = AZ::Interface::Get(); + if (!editorsv_enabled || !mpTools) + { + return; + } + auto prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); if (!prefabEditorEntityOwnershipInterface) { AZ_Error("MultiplayerEditor", prefabEditorEntityOwnershipInterface != nullptr, "PrefabEditorEntityOwnershipInterface unavailable"); + return; } // BeginGameMode and Prefab Processing have completed at this point - IMultiplayerTools* mpTools = AZ::Interface::Get(); - if (editorsv_enabled && mpTools != nullptr) - { - const AZStd::vector>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData(); + const AZStd::vector>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData(); - AZStd::vector buffer; - AZ::IO::ByteContainerStream byteStream(&buffer); + AZStd::vector buffer; + AZ::IO::ByteContainerStream byteStream(&buffer); - // Serialize Asset information and AssetData into a potentially large buffer - for (const auto& asset : assetData) + // Serialize Asset information and AssetData into a potentially large buffer + for (const auto& asset : assetData) + { + AZ::Data::AssetId assetId = asset.GetId(); + AZStd::string assetHint = asset.GetHint(); + uint32_t hintSize = aznumeric_cast(assetHint.size()); + + byteStream.Write(sizeof(AZ::Data::AssetId), reinterpret_cast(&assetId)); + byteStream.Write(sizeof(uint32_t), reinterpret_cast(&hintSize)); + byteStream.Write(assetHint.size(), assetHint.data()); + AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType()); + } + + const AZ::CVarFixedString remoteAddress = editorsv_serveraddr; + if (editorsv_launch) + { + if (LocalHost != remoteAddress) { - AZ::Data::AssetId assetId = asset.GetId(); - AZStd::string assetHint = asset.GetHint(); - uint32_t hintSize = aznumeric_cast(assetHint.size()); - - byteStream.Write(sizeof(AZ::Data::AssetId), reinterpret_cast(&assetId)); - byteStream.Write(sizeof(uint32_t), reinterpret_cast(&hintSize)); - byteStream.Write(assetHint.size(), assetHint.data()); - AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType()); + AZ_Warning( + "MultiplayerEditor", false, + "Launching EditorServer skipped because incompatible cvars. editorsv_launch=true, meaning you want to launch an editor-server on this machine, but the editorsv_serveraddr is %s instead of the local address (127.0.0.1). " + "Please either set editorsv_launch=false and keep the remote editor-server, or set editorsv_launch=true and editorsv_serveraddr=127.0.0.1.", + remoteAddress.c_str()) + return; } - - const AZ::CVarFixedString remoteAddress = editorsv_serveraddr; - if (editorsv_launch) - { - if (LocalHost != remoteAddress) - { - AZ_Warning( - "MultiplayerEditor", false, - "Launching EditorServer skipped because incompatible cvars. editorsv_launch=true, meaning you want to launch an editor-server on this machine, but the editorsv_serveraddr is %s instead of the local address (127.0.0.1). " - "Please either set editorsv_launch=false and keep the remote editor-server, or set editorsv_launch=true and editorsv_serveraddr=127.0.0.1.", - remoteAddress.c_str()) - return; - } - // Begin listening for MPEditor packets before we launch the editor-server. - // The editor-server will send us (the editor) an "EditorServerReadyForLevelData" packet to let us know it's ready to receive data. - INetworkInterface* editorNetworkInterface = - AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName)); - AZ_Assert(editorNetworkInterface, "MP Editor Network Interface was unregistered before Editor could connect."); - editorNetworkInterface->Listen(editorsv_port); + // Begin listening for MPEditor packets before we launch the editor-server. + // The editor-server will send us (the editor) an "EditorServerReadyForLevelData" packet to let us know it's ready to receive data. + INetworkInterface* editorNetworkInterface = + AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName)); + AZ_Assert(editorNetworkInterface, "MP Editor Network Interface was unregistered before Editor could connect."); + editorNetworkInterface->Listen(editorsv_port); - // Launch the editor-server - m_serverProcess = LaunchEditorServer(); - } - else - { - // Editorsv_launch=false, so we're expecting an editor-server already exists. - // Connect to the editor-server and then send the EditorServerLevelData packet. - INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName)); - AZ_Assert(editorNetworkInterface, "MP Editor Network Interface was unregistered before Editor could connect.") + // Launch the editor-server + m_serverProcess = LaunchEditorServer(); + } + else + { + // Editorsv_launch=false, so we're expecting an editor-server already exists. + // Connect to the editor-server and then send the EditorServerLevelData packet. + INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName)); + AZ_Assert(editorNetworkInterface, "MP Editor Network Interface was unregistered before Editor could connect.") - m_editorConnId = editorNetworkInterface->Connect(AzNetworking::IpAddress(remoteAddress.c_str(), editorsv_port, AzNetworking::ProtocolType::Tcp)); + m_editorConnId = editorNetworkInterface->Connect(AzNetworking::IpAddress(remoteAddress.c_str(), editorsv_port, AzNetworking::ProtocolType::Tcp)); - if (m_editorConnId == AzNetworking::InvalidConnectionId) - { - AZ_Warning( - "MultiplayerEditor", false, - "Editor multiplayer game-mode failed! Could not connect to an editor-server. editorsv_launch is false so we're assuming you're running your own editor-server at editorsv_serveraddr(%s) on editorsv_port(%i). " - "Either set editorsv_launch=true so the editor launches an editor-server for you, or launch your own editor-server by hand before entering game-mode. Remember editor-servers must use editorsv_isDedicated=true.", - remoteAddress.c_str(), - static_cast(editorsv_port)) - return; - } - - SendEditorServerLevelDataPacket(editorNetworkInterface->GetConnectionSet().GetConnection(m_editorConnId)); + if (m_editorConnId == AzNetworking::InvalidConnectionId) + { + AZ_Warning( + "MultiplayerEditor", false, + "Editor multiplayer game-mode failed! Could not connect to an editor-server. editorsv_launch is false so we're assuming you're running your own editor-server at editorsv_serveraddr(%s) on editorsv_port(%i). " + "Either set editorsv_launch=true so the editor launches an editor-server for you, or launch your own editor-server by hand before entering game-mode. Remember editor-servers must use editorsv_isDedicated=true.", + remoteAddress.c_str(), + static_cast(editorsv_port)) + return; } + + SendEditorServerLevelDataPacket(editorNetworkInterface->GetConnectionSet().GetConnection(m_editorConnId)); } } From 627012840d62a8f10fd6a7c595bac0b68d936f4b Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Thu, 4 Nov 2021 11:02:18 -0500 Subject: [PATCH 057/177] Update how Project Filepaths are calculated when not supplied via command line (#5194) * Fixed the return value of the ConvertToAbsolutePath function Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Added the generated cmake_dependencies.*.setreg files to engine.pak (#5073) * Copied the generated cmake_dependencies.*.setreg file to the Cache directory Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removed the platform name from the bootstrap.game.*.setreg Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Update how the project related file paths are determined when not supplied. The project-path determination now goes back to only detecting a "project.json" file. It no longer attempts to detect a "Cache" directory The project-cache-path determination now in addition to checking the project_cache_path key searches for a "Cache" directory. The project-path defaults to executable folder if it cannot be detected. The copying of generated executable folder Registry directory contents to the product cache is now removed after the archive step. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated the invocation of the AssetProcessor in Jenkins to supply an absolute path to the project. The project-path is no longer treated as relative to the engine root, but instead relative to the current working directory at application startup. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Added constant for the storing the name of Cache directory Fixed typos and grammatical errors in the SettingsRegistryMergeUtils.cpp Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Updated UnitTest prepend the EngineRoot path to "AutomatedTesting" when setting the project path. This is needed now that the project-path isn't treated relative to the EngineRoot if it is not absolute. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Fix AssetSeedManagerTest and PlatformAddressedAssetCatalogManagerTest Instead of trying to used the AutomatedTesting directory as the project root, the temp directory created during the test is used as the project root. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Moved the setting of the project cache root folder and project asset platform root folder into the `if (!projectCachePath.empty())` block Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Removing the scan up logic for the "Cache" directory. This is no longer needed to locate the project cache path in a Project Game Release Layout. Because the project path defaults to the executable directory if, it is not found, the Cache directory will be set to the "Cache" directory within the executable directory. Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> --- .../AzCore/Component/ComponentApplication.cpp | 1 - .../Settings/SettingsRegistryMergeUtils.cpp | 414 +++++++++++------- .../Settings/SettingsRegistryMergeUtils.h | 6 +- .../Tests/ArchiveCompressionTests.cpp | 4 +- .../AzFramework/Tests/ArchiveTests.cpp | 4 +- .../AzFramework/Tests/AssetCatalog.cpp | 4 +- .../Application/GameApplication.cpp | 2 +- .../Tests/AssetFileInfoListComparison.cpp | 32 +- .../Tests/AssetSeedManager.cpp | 6 +- .../Tests/ComponentAddRemove.cpp | 4 +- .../Tests/GenericComponentWrapperTest.cpp | 8 +- .../PlatformAddressedAssetCatalogTests.cpp | 11 +- .../AzToolsFramework/Tests/Slices.cpp | 4 +- .../tests/applicationManagerTests.cpp | 4 +- Code/Tools/AssetBundler/tests/tests_main.cpp | 4 +- .../SettingsRegistryBuilder.cpp | 5 +- .../AssetCatalog/AssetCatalogUnitTests.cpp | 4 +- .../tests/AssetProcessorMessagesTests.cpp | 5 +- .../native/tests/AssetProcessorTest.cpp | 5 +- .../native/tests/AssetProcessorTest.h | 4 +- .../AssetProcessorManagerTest.cpp | 4 +- .../Tools/DeltaCataloger/Tests/tests_main.cpp | 4 +- .../Code/Tests/AssetValidationTestShared.h | 2 +- .../Code/Tests/SystemComponentFixture.h | 4 +- .../Code/Tests/EditorPythonBindingsTest.cpp | 4 +- .../Code/Tests/Builders/LevelBuilderTest.cpp | 4 +- .../Code/Tests/Builders/LuaBuilderTests.cpp | 4 +- .../Code/Tests/Builders/SeedBuilderTests.cpp | 4 +- Gems/LyShine/Code/Tests/LyShineEditorTest.cpp | 4 +- .../PrefabBuilder/PrefabBuilderTests.cpp | 4 +- .../SceneBuilder/SceneBuilderPhasesTests.cpp | 4 +- .../Tests/SceneBuilder/SceneBuilderTests.cpp | 4 +- cmake/Projects.cmake | 55 ++- scripts/build/Platform/Linux/asset_linux.sh | 6 +- scripts/build/Platform/Mac/asset_mac.sh | 6 +- .../build/Platform/Windows/asset_windows.cmd | 6 +- 36 files changed, 411 insertions(+), 239 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp index df8db79db0..ad7e44c2ae 100644 --- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp +++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp @@ -214,7 +214,6 @@ namespace AZ // Update old Project path before attempting to merge in new Settings Registry values in order to prevent recursive calls m_oldProjectPath = newProjectPath; - // Merge the project.json file into settings registry under ProjectSettingsRootKey path. // Update all the runtime file paths based on the new "project_path" value. AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry); } diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp index 5458a3fadf..3668ab14fd 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp @@ -29,6 +29,8 @@ namespace AZ::Internal { + static constexpr const char* ProductCacheDirectoryName = "Cache"; + AZ::SettingsRegistryInterface::FixedValueString GetEngineMonikerForProject( SettingsRegistryInterface& settingsRegistry, const AZ::IO::FixedMaxPath& projectJsonPath) { @@ -228,19 +230,20 @@ namespace AZ::Internal namespace AZ::SettingsRegistryMergeUtils { - constexpr AZStd::string_view InternalScanUpEngineRootKey{ "/O3DE/Settings/Internal/engine_root_scan_up_path" }; - constexpr AZStd::string_view InternalScanUpProjectRootKey{ "/O3DE/Settings/Internal/project_root_scan_up_path" }; - AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry) { + static constexpr AZStd::string_view InternalScanUpEngineRootKey{ "/O3DE/Runtime/Internal/engine_root_scan_up_path" }; + using FixedValueString = SettingsRegistryInterface::FixedValueString; + using Type = SettingsRegistryInterface::Type; + AZ::IO::FixedMaxPath engineRoot; // This is the 'external' engine root key, as in passed from command-line or .setreg files. - auto engineRootKey = SettingsRegistryInterface::FixedValueString::format("%s/engine_path", BootstrapSettingsRootKey); + constexpr auto engineRootKey = FixedValueString(BootstrapSettingsRootKey) + "/engine_path"; // Step 1 Run the scan upwards logic once to find the location of the engine.json if it exist // Once this step is run the {InternalScanUpEngineRootKey} is set in the Settings Registry // to have this scan logic only run once InternalScanUpEngineRootKey the supplied registry - if (settingsRegistry.GetType(InternalScanUpEngineRootKey) == SettingsRegistryInterface::Type::NoType) + if (settingsRegistry.GetType(InternalScanUpEngineRootKey) == Type::NoType) { // We can scan up from exe directory to find engine.json, use that for engine root if it exists. engineRoot = Internal::ScanUpRootLocator("engine.json"); @@ -283,14 +286,18 @@ namespace AZ::SettingsRegistryMergeUtils AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry) { - AZ::IO::FixedMaxPath projectRoot; - const auto projectRootKey = SettingsRegistryInterface::FixedValueString::format("%s/project_path", BootstrapSettingsRootKey); + static constexpr AZStd::string_view InternalScanUpProjectRootKey{ "/O3DE/Runtime/Internal/project_root_scan_up_path" }; + using FixedValueString = SettingsRegistryInterface::FixedValueString; + using Type = SettingsRegistryInterface::Type; - // Step 1 Run the scan upwards logic once to find the location of the project.json if it exist + AZ::IO::FixedMaxPath projectRoot; + constexpr auto projectRootKey = FixedValueString(BootstrapSettingsRootKey) + "/project_path"; + + // Step 1 Run the scan upwards logic once to find the location of the closest ancestor project.json // Once this step is run the {InternalScanUpProjectRootKey} is set in the Settings Registry // to have this scan logic only run once for the supplied registry // SettingsRegistryInterface::GetType is used to check if a key is set - if (settingsRegistry.GetType(InternalScanUpProjectRootKey) == SettingsRegistryInterface::Type::NoType) + if (settingsRegistry.GetType(InternalScanUpProjectRootKey) == Type::NoType) { projectRoot = Internal::ScanUpRootLocator("project.json"); // Set the {InternalScanUpProjectRootKey} to make sure this code path isn't called again for this settings registry @@ -305,19 +312,129 @@ namespace AZ::SettingsRegistryMergeUtils } // Step 2 Check the project-path key - // This is the project path root key, as in passed from command-line or .setreg files. - if (settingsRegistry.Get(projectRoot.Native(), projectRootKey)) + // This is the project path root key, as passed from command-line or *.setreg files. + settingsRegistry.Get(projectRoot.Native(), projectRootKey); + return projectRoot; + } + + //! The algorithm that is used to find the project cache is as follows + //! 1. The "{BootstrapSettingsRootKey}/project_cache_path" is checked for the path + //! 2. Otherwise append the ProductCacheDirectoryName constant to the + static AZ::IO::FixedMaxPath FindProjectCachePath(SettingsRegistryInterface& settingsRegistry, const AZ::IO::FixedMaxPath& projectPath) + { + using FixedValueString = SettingsRegistryInterface::FixedValueString; + + constexpr auto projectCachePathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_cache_path"; + + // Step 1 Check the project-cache-path key + if (AZ::IO::FixedMaxPath projectCachePath; settingsRegistry.Get(projectCachePath.Native(), projectCachePathKey)) { - return projectRoot; + return projectCachePath; } - // Step 3 Check for a "Cache" directory by scanning upwards from the executable directory - if (auto candidateRoot = Internal::ScanUpRootLocator("Cache"); - !candidateRoot.empty() && AZ::IO::SystemFile::IsDirectory(candidateRoot.c_str())) + // Step 2 Append the "Cache" directory to the project-path + return projectPath / Internal::ProductCacheDirectoryName; + } + + //! Set the user directory with the provided path or using /user as default + static AZ::IO::FixedMaxPath FindProjectUserPath(SettingsRegistryInterface& settingsRegistry, + const AZ::IO::FixedMaxPath& projectPath) + { + using FixedValueString = SettingsRegistryInterface::FixedValueString; + + // User: root - same as the @user@ alias, this is the starting path for transient data and log files. + constexpr auto projectUserPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_user_path"; + + // Step 1 Check the project-user-path key + if (AZ::IO::FixedMaxPath projectUserPath; settingsRegistry.Get(projectUserPath.Native(), projectUserPathKey)) { - projectRoot = AZStd::move(candidateRoot); + return projectUserPath; + } + + // Step 2 Append the "User" directory to the project-path + return projectPath / "user"; + } + + //! Set the log directory using the settings registry path or using /log as default + static AZ::IO::FixedMaxPath FindProjectLogPath(SettingsRegistryInterface& settingsRegistry, + const AZ::IO::FixedMaxPath& projectUserPath) + { + using FixedValueString = SettingsRegistryInterface::FixedValueString; + + // User: root - same as the @log@ alias, this is the starting path for transient data and log files. + constexpr auto projectLogPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_log_path"; + + // Step 1 Check the project-user-path key + if (AZ::IO::FixedMaxPath projectLogPath; settingsRegistry.Get(projectLogPath.Native(), projectLogPathKey)) + { + return projectLogPath; + } + + // Step 2 Append the "Log" directory to the project-user-path + return projectUserPath / "log"; + } + + // check for a default write storage path, fall back to the if not + static AZ::IO::FixedMaxPath FindDevWriteStoragePath(const AZ::IO::FixedMaxPath& projectUserPath) + { + AZStd::optional devWriteStorage = Utils::GetDevWriteStoragePath(); + return devWriteStorage.has_value() ? *devWriteStorage : projectUserPath; + } + + // check for the project build path, which is a relative path from the project root + // that specifies where the build directory is located + static void SetProjectBuildPath(SettingsRegistryInterface& settingsRegistry, + const AZ::IO::FixedMaxPath& projectPath) + { + if (AZ::IO::FixedMaxPath projectBuildPath; settingsRegistry.Get(projectBuildPath.Native(), ProjectBuildPath)) + { + settingsRegistry.Remove(FilePathKey_ProjectBuildPath); + settingsRegistry.Remove(FilePathKey_ProjectConfigurationBinPath); + AZ::IO::FixedMaxPath buildConfigurationPath = (projectPath / projectBuildPath).LexicallyNormal(); + if (IO::SystemFile::Exists(buildConfigurationPath.c_str())) + { + settingsRegistry.Set(FilePathKey_ProjectBuildPath, buildConfigurationPath.Native()); + } + + // Add the specific build configuration paths to the Settings Registry + // First try /bin/$ and if that path doesn't exist + // try /bin/$/$ + buildConfigurationPath /= "bin"; + if (IO::SystemFile::Exists((buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).c_str())) + { + settingsRegistry.Set(FilePathKey_ProjectConfigurationBinPath, + (buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).Native()); + } + else if (IO::SystemFile::Exists((buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).c_str())) + { + settingsRegistry.Set(FilePathKey_ProjectConfigurationBinPath, + (buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).Native()); + } + } + } + + // Sets the project name within the Settings Registry by looking up the "project_name" + // within the project.json file + static void SetProjectName(SettingsRegistryInterface& settingsRegistry, + const AZ::IO::FixedMaxPath& projectPath) + { + using FixedValueString = SettingsRegistryInterface::FixedValueString; + // Project name - if it was set via merging project.json use that value, otherwise use the project path's folder name. + constexpr auto projectNameKey = FixedValueString(ProjectSettingsRootKey) + "/project_name"; + + // Read the project name from the project.json file if it exists + if (AZ::IO::FixedMaxPath projectJsonPath = projectPath / "project.json"; + AZ::IO::SystemFile::Exists(projectJsonPath.c_str())) + { + settingsRegistry.MergeSettingsFile(projectJsonPath.Native(), + AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey); + } + // If a project name isn't set the default will be set to the final path segment of the project path + if (FixedValueString projectName; !settingsRegistry.Get(projectName, projectNameKey)) + { + projectName = projectPath.Filename().Native(); + settingsRegistry.Set(projectNameKey, projectName); } - return projectRoot; } AZStd::string_view ConfigParserSettings::DefaultCommentPrefixFilter(AZStd::string_view line) @@ -397,7 +514,7 @@ namespace AZ::SettingsRegistryMergeUtils bool MergeSettingsToRegistry_ConfigFile(SettingsRegistryInterface& registry, AZStd::string_view filePath, const ConfigParserSettings& configParserSettings) { - auto configPath = FindEngineRoot(registry) / filePath; + auto configPath = FindProjectRoot(registry) / filePath; IO::FileReader configFile; bool configFileOpened{}; switch (configParserSettings.m_fileReaderClass) @@ -542,19 +659,77 @@ namespace AZ::SettingsRegistryMergeUtils void MergeSettingsToRegistry_AddRuntimeFilePaths(SettingsRegistryInterface& registry) { using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString; - // Binary folder - AZ::IO::FixedMaxPath path = AZ::Utils::GetExecutableDirectory(); - registry.Set(FilePathKey_BinaryFolder, path.LexicallyNormal().Native()); - // Engine root folder - corresponds to the @engroot@ and @engroot@ aliases - AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry); - registry.Set(FilePathKey_EngineRootFolder, engineRoot.LexicallyNormal().Native()); + // Binary folder - corresponds to the @exefolder@ alias + AZ::IO::FixedMaxPath exePath = AZ::Utils::GetExecutableDirectory(); + registry.Set(FilePathKey_BinaryFolder, exePath.LexicallyNormal().Native()); - auto projectPathKey = FixedValueString::format("%s/project_path", BootstrapSettingsRootKey); - SettingsRegistryInterface::FixedValueString projectPathValue; - if (registry.Get(projectPathValue, projectPathKey)) + // Project path - corresponds to the @projectroot@ alias + // NOTE: We make the project-path in the BootstrapSettingsRootKey absolute first + + AZ::IO::FixedMaxPath projectPath = FindProjectRoot(registry); + if (constexpr auto projectPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_path"; + !projectPath.empty()) { - // Cache folder + if (projectPath.IsRelative()) + { + if (auto projectAbsPath = AZ::Utils::ConvertToAbsolutePath(projectPath.Native()); + projectAbsPath.has_value()) + { + projectPath = AZStd::move(*projectAbsPath); + } + } + + projectPath = projectPath.LexicallyNormal(); + AZ_Warning("SettingsRegistryMergeUtils", AZ::IO::SystemFile::Exists(projectPath.c_str()), + R"(Project path "%s" does not exist. Is the "%.*s" registry setting set to a valid absolute path?)" + , projectPath.c_str(), AZ_STRING_ARG(projectPathKey)); + + registry.Set(FilePathKey_ProjectPath, projectPath.Native()); + } + else + { + AZ_TracePrintf("SettingsRegistryMergeUtils", + R"(Project path isn't set in the Settings Registry at "%.*s".)" + " Project-related filepaths will be set relative to the executable directory\n", + AZ_STRING_ARG(projectPathKey)); + registry.Set(FilePathKey_ProjectPath, exePath.Native()); + } + + // Engine root folder - corresponds to the @engroot@ alias + AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry); + if (!engineRoot.empty()) + { + if (engineRoot.IsRelative()) + { + if (auto engineRootAbsPath = AZ::Utils::ConvertToAbsolutePath(engineRoot.Native()); + engineRootAbsPath.has_value()) + { + engineRoot = AZStd::move(*engineRootAbsPath); + } + } + + engineRoot = engineRoot.LexicallyNormal(); + registry.Set(FilePathKey_EngineRootFolder, engineRoot.Native()); + } + + // Cache folder + AZ::IO::FixedMaxPath projectCachePath = FindProjectCachePath(registry, projectPath).LexicallyNormal(); + if (!projectCachePath.empty()) + { + if (projectCachePath.IsRelative()) + { + if (auto projectCacheAbsPath = AZ::Utils::ConvertToAbsolutePath(projectCachePath.Native()); + projectCacheAbsPath.has_value()) + { + projectCachePath = AZStd::move(*projectCacheAbsPath); + } + } + + projectCachePath = projectCachePath.LexicallyNormal(); + registry.Set(FilePathKey_CacheProjectRootFolder, projectCachePath.Native()); + + // Cache/ folder // Get the name of the asset platform assigned by the bootstrap. First check for platform version such as "windows_assets" // and if that's missing just get "assets". FixedValueString assetPlatform; @@ -570,124 +745,67 @@ namespace AZ::SettingsRegistryMergeUtils assetPlatform = AZ::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME); } - // Project path - corresponds to the @projectroot@ alias - // NOTE: Here we append to engineRoot, but if projectPathValue is absolute then engineRoot is discarded. - path = engineRoot / projectPathValue; - - AZ_Warning("SettingsRegistryMergeUtils", AZ::IO::SystemFile::Exists(path.c_str()), - R"(Project path "%s" does not exist. Is the "%.*s" registry setting set to valid absolute path?)" - , path.c_str(), aznumeric_cast(projectPathKey.size()), projectPathKey.data()); - - AZ::IO::FixedMaxPath normalizedProjectPath = path.LexicallyNormal(); - registry.Set(FilePathKey_ProjectPath, normalizedProjectPath.Native()); - - // Set the user directory with the provided path or using project/user as default - auto projectUserPathKey = FixedValueString::format("%s/project_user_path", BootstrapSettingsRootKey); - AZ::IO::FixedMaxPath projectUserPath; - if (!registry.Get(projectUserPath.Native(), projectUserPathKey)) - { - projectUserPath = (normalizedProjectPath / "user").LexicallyNormal(); - } - registry.Set(FilePathKey_ProjectUserPath, projectUserPath.Native()); - - // Set the log directory with the provided path or using project/user/log as default - auto projectLogPathKey = FixedValueString::format("%s/project_log_path", BootstrapSettingsRootKey); - AZ::IO::FixedMaxPath projectLogPath; - if (!registry.Get(projectLogPath.Native(), projectLogPathKey)) - { - projectLogPath = (projectUserPath / "log").LexicallyNormal(); - } - registry.Set(FilePathKey_ProjectLogPath, projectLogPath.Native()); - - // check for a default write storage path, fall back to the project's user/ directory if not - AZStd::optional devWriteStorage = Utils::GetDevWriteStoragePath(); - registry.Set(FilePathKey_DevWriteStorage, devWriteStorage.has_value() - ? devWriteStorage.value() - : projectUserPath.Native()); - - // Set the project in-memory build path if the ProjectBuildPath key has been supplied - if (AZ::IO::FixedMaxPath projectBuildPath; registry.Get(projectBuildPath.Native(), ProjectBuildPath)) - { - registry.Remove(FilePathKey_ProjectBuildPath); - registry.Remove(FilePathKey_ProjectConfigurationBinPath); - AZ::IO::FixedMaxPath buildConfigurationPath = normalizedProjectPath / projectBuildPath; - if (IO::SystemFile::Exists(buildConfigurationPath.c_str())) - { - registry.Set(FilePathKey_ProjectBuildPath, buildConfigurationPath.LexicallyNormal().Native()); - } - - // Add the specific build configuration paths to the Settings Registry - // First try /bin/$ and if that path doesn't exist - // try /bin/$/$ - buildConfigurationPath /= "bin"; - if (IO::SystemFile::Exists((buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).c_str())) - { - registry.Set(FilePathKey_ProjectConfigurationBinPath, - (buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).LexicallyNormal().Native()); - } - else if (IO::SystemFile::Exists((buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).c_str())) - { - registry.Set(FilePathKey_ProjectConfigurationBinPath, - (buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).LexicallyNormal().Native()); - } - - } - - // Project name - if it was set via merging project.json use that value, otherwise use the project path's folder name. - constexpr auto projectNameKey = - FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey) - + "/project_name"; - - // Read the project name from the project.json file if it exists - if (AZ::IO::FixedMaxPath projectJsonPath = normalizedProjectPath / "project.json"; - AZ::IO::SystemFile::Exists(projectJsonPath.c_str())) - { - registry.MergeSettingsFile(projectJsonPath.Native(), - AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey); - } - if (FixedValueString projectName; !registry.Get(projectName, projectNameKey)) - { - projectName = path.Filename().Native(); - registry.Set(projectNameKey, projectName); - } - - // Cache folders - sets up various paths in registry for the cache. - // Make sure the asset platform is set before setting these cache paths. + // Make sure the asset platform is set before setting cache path for the asset platform. if (!assetPlatform.empty()) { - // Cache: project root - no corresponding fileIO alias, but this is where the asset database lives. - // A registry override is accepted using the "project_cache_path" key. - auto projectCacheRootOverrideKey = FixedValueString::format("%s/project_cache_path", BootstrapSettingsRootKey); - // Clear path to make sure that the `project_cache_path` value isn't concatenated to the project path - path.clear(); - if (registry.Get(path.Native(), projectCacheRootOverrideKey)) - { - registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native()); - path /= assetPlatform; - registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native()); - } - else - { - // Cache: root - same as the @products@ alias, this is the starting path for cache files. - path = normalizedProjectPath / "Cache"; - registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native()); - path /= assetPlatform; - registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native()); - } + registry.Set(FilePathKey_CacheRootFolder, (projectCachePath / assetPlatform).Native()); } } - else + + // User folder + AZ::IO::FixedMaxPath projectUserPath = FindProjectUserPath(registry, projectPath); + if (!projectUserPath.empty()) { - // Set the default ProjectUserPath to the /user directory - registry.Set(FilePathKey_ProjectUserPath, (engineRoot / "user").LexicallyNormal().Native()); - AZ_TracePrintf("SettingsRegistryMergeUtils", - R"(Project path isn't set in the Settings Registry at "%.*s". Project-related filepaths will not be set)" "\n", - aznumeric_cast(projectPathKey.size()), projectPathKey.data()); + if (projectUserPath.IsRelative()) + { + if (auto projectUserAbsPath = AZ::Utils::ConvertToAbsolutePath(projectUserPath.Native()); + projectUserAbsPath.has_value()) + { + projectUserPath = AZStd::move(*projectUserAbsPath); + } + } + + projectUserPath = projectUserPath.LexicallyNormal(); + registry.Set(FilePathKey_ProjectUserPath, projectUserPath.Native()); } + // Log folder + if (AZ::IO::FixedMaxPath projectLogPath = FindProjectLogPath(registry, projectUserPath); !projectLogPath.empty()) + { + if (projectLogPath.IsRelative()) + { + if (auto projectLogAbsPath = AZ::Utils::ConvertToAbsolutePath(projectLogPath.Native())) + { + projectLogPath = AZStd::move(*projectLogAbsPath); + } + } + + projectLogPath = projectLogPath.LexicallyNormal(); + registry.Set(FilePathKey_ProjectLogPath, projectLogPath.Native()); + } + + // Developer Write Storage folder + if (AZ::IO::FixedMaxPath devWriteStoragePath = FindDevWriteStoragePath(projectUserPath); !devWriteStoragePath.empty()) + { + if (devWriteStoragePath.IsRelative()) + { + if (auto devWriteStorageAbsPath = AZ::Utils::ConvertToAbsolutePath(devWriteStoragePath.Native())) + { + devWriteStoragePath = AZStd::move(*devWriteStorageAbsPath); + } + } + + devWriteStoragePath = devWriteStoragePath.LexicallyNormal(); + registry.Set(FilePathKey_DevWriteStorage, devWriteStoragePath.Native()); + } + + // Set the project in-memory build path if the ProjectBuildPath key has been supplied + SetProjectBuildPath(registry, projectPath); + // Set the project name using the "project_name" key + SetProjectName(registry, projectPath); + #if !AZ_TRAIT_OS_IS_HOST_OS_PLATFORM // Setup the cache, user, and log paths to platform specific locations when running on non-host platforms - path = engineRoot; if (AZStd::optional nonHostCacheRoot = Utils::GetDefaultAppRootPath(); nonHostCacheRoot) { @@ -696,25 +814,25 @@ namespace AZ::SettingsRegistryMergeUtils } else { - registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native()); - registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native()); + registry.Set(FilePathKey_CacheProjectRootFolder, projectPath.Native()); + registry.Set(FilePathKey_CacheRootFolder, projectPath.Native()); } if (AZStd::optional devWriteStorage = Utils::GetDevWriteStoragePath(); devWriteStorage) { - const AZ::IO::FixedMaxPath devWriteStoragePath(*devWriteStorage); - registry.Set(FilePathKey_DevWriteStorage, devWriteStoragePath.LexicallyNormal().Native()); - registry.Set(FilePathKey_ProjectUserPath, (devWriteStoragePath / "user").LexicallyNormal().Native()); - registry.Set(FilePathKey_ProjectLogPath, (devWriteStoragePath / "user/log").LexicallyNormal().Native()); + const auto devWriteStoragePath = AZ::IO::PathView(*devWriteStorage).LexicallyNormal(); + registry.Set(FilePathKey_DevWriteStorage, devWriteStoragePath.Native()); + registry.Set(FilePathKey_ProjectUserPath, (devWriteStoragePath / "user").Native()); + registry.Set(FilePathKey_ProjectLogPath, (devWriteStoragePath / "user" / "log").Native()); } else { - registry.Set(FilePathKey_DevWriteStorage, path.LexicallyNormal().Native()); - registry.Set(FilePathKey_ProjectUserPath, (path / "user").LexicallyNormal().Native()); - registry.Set(FilePathKey_ProjectLogPath, (path / "user/log").LexicallyNormal().Native()); - } -#endif // AZ_TRAIT_OS_IS_HOST_OS_PLATFORM + registry.Set(FilePathKey_DevWriteStorage, projectPath.Native()); + registry.Set(FilePathKey_ProjectUserPath, (projectPath / "user").Native()); + registry.Set(FilePathKey_ProjectLogPath, (projectPath / "user" / "log").Native()); } +#endif // AZ_TRAIT_OS_IS_HOST_OS_PLATFORM +} void MergeSettingsToRegistry_TargetBuildDependencyRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform, const SettingsRegistryInterface::Specializations& specializations, AZStd::vector* scratchBuffer) diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h index daa64c0343..56eec91813 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h @@ -87,9 +87,9 @@ namespace AZ::SettingsRegistryMergeUtils AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry); //! The algorithm that is used to find the project root is as follows - //! 1. The first time this function is it performs a upward scan for a project.json file from - //! the executable directory and if found stores that path to an internal key. - //! In the same step it injects the path into the front of list of command line parameters + //! 1. The first time this function runs it performs an upward scan for a "project.json" file from + //! the executable directory and stores that path into an internal key. + //! In the same step it injects the path into the back of the command line parameters //! using the --regset="{BootstrapSettingsRootKey}/project_path=" value //! 2. Next the "{BootstrapSettingsRootKey}/project_path" is checked to see if it has a project path set //! diff --git a/Code/Framework/AzFramework/Tests/ArchiveCompressionTests.cpp b/Code/Framework/AzFramework/Tests/ArchiveCompressionTests.cpp index 6cde5b5e84..aa1a27f9b0 100644 --- a/Code/Framework/AzFramework/Tests/ArchiveCompressionTests.cpp +++ b/Code/Framework/AzFramework/Tests/ArchiveCompressionTests.cpp @@ -41,7 +41,9 @@ namespace UnitTest auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_application->Start({}); diff --git a/Code/Framework/AzFramework/Tests/ArchiveTests.cpp b/Code/Framework/AzFramework/Tests/ArchiveTests.cpp index 37babb49a8..ff0e3ab724 100644 --- a/Code/Framework/AzFramework/Tests/ArchiveTests.cpp +++ b/Code/Framework/AzFramework/Tests/ArchiveTests.cpp @@ -45,7 +45,9 @@ namespace UnitTest auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_application->Start({}); diff --git a/Code/Framework/AzFramework/Tests/AssetCatalog.cpp b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp index 9e5c72f74c..8a24d164cd 100644 --- a/Code/Framework/AzFramework/Tests/AssetCatalog.cpp +++ b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp @@ -305,7 +305,9 @@ namespace UnitTest AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); AZ::ComponentApplication::StartupParameters startupParameters; diff --git a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp index f0417d206e..36acf2b063 100644 --- a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp +++ b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp @@ -87,7 +87,7 @@ namespace AzGameFramework AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer); #endif - // Used the lowercase the platform name since the bootstrap.game...setreg is being loaded + // Used the lowercase the platform name since the bootstrap.game..setreg is being loaded // from the asset cache root where all the files are in lowercased from regardless of the filesystem case-sensitivity static constexpr char filename[] = "bootstrap.game." AZ_BUILD_CONFIGURATION_TYPE ".setreg"; diff --git a/Code/Framework/AzToolsFramework/Tests/AssetFileInfoListComparison.cpp b/Code/Framework/AzToolsFramework/Tests/AssetFileInfoListComparison.cpp index ad80b5c39d..af0ae9addb 100644 --- a/Code/Framework/AzToolsFramework/Tests/AssetFileInfoListComparison.cpp +++ b/Code/Framework/AzToolsFramework/Tests/AssetFileInfoListComparison.cpp @@ -57,9 +57,7 @@ namespace UnitTest ArgumentContainer argContainer{ {} }; // Append Command Line override for the Project Cache Path - auto projectCachePathOverride = FixedValueString::format(R"(--project-cache-path="%s")", m_tempDir.GetDirectory()); - auto projectPathOverride = FixedValueString{ R"(--project-path=AutomatedTesting)" }; - argContainer.push_back(projectCachePathOverride.data()); + auto projectPathOverride = FixedValueString::format(R"(--project-path="%s")", m_tempDir.GetDirectory()); argContainer.push_back(projectPathOverride.data()); m_application = new ToolsTestApplication("AssetFileInfoListComparisonTest", aznumeric_caster(argContainer.size()), argContainer.data()); AzToolsFramework::AssetSeedManager assetSeedManager; @@ -100,7 +98,7 @@ namespace UnitTest m_application->Start(AzFramework::Application::Descriptor()); // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is - // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash + // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash // in the unit tests. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize); @@ -223,7 +221,7 @@ namespace UnitTest // AssetFileInfo should contain {2*, 4*, 5} AzToolsFramework::AssetFileInfoList assetFileInfoList; - + ASSERT_TRUE(AZ::Utils::LoadObjectFromFileInPlace(TempFiles[FileIndex::ResultAssetFileInfoList], assetFileInfoList)) << "Unable to read the asset file info list.\n"; EXPECT_EQ(assetFileInfoList.m_fileInfoList.size(), 3); @@ -256,7 +254,7 @@ namespace UnitTest } } - // Verifying that correct assetId are present in the assetFileInfo list + // Verifying that correct assetId are present in the assetFileInfo list AZStd::unordered_set expectedAssetIds{ m_assets[2], m_assets[4], m_assets[5] }; for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList) @@ -298,7 +296,7 @@ namespace UnitTest { firstAssetIdToAssetFileInfoMap[assetFileInfo.m_assetId] = AZStd::move(assetFileInfo); } - + AzToolsFramework::AssetFileInfoList secondAssetFileInfoList; ASSERT_TRUE(AZ::Utils::LoadObjectFromFileInPlace(TempFiles[FileIndex::SecondAssetFileInfoList], secondAssetFileInfoList)) << "Unable to read the asset file info list.\n"; @@ -315,7 +313,7 @@ namespace UnitTest auto foundSecond = secondAssetIdToAssetFileInfoMap.find(assetFileInfo.m_assetId); if (foundSecond != secondAssetIdToAssetFileInfoMap.end()) { - // Even if the asset Id is present in both the AssetFileInfo List, it should match the file hash from the second AssetFileInfo list + // Even if the asset Id is present in both the AssetFileInfo List, it should match the file hash from the second AssetFileInfo list for (int idx = 0; idx < AzToolsFramework::AssetFileInfo::s_arraySize; idx++) { if (foundSecond->second.m_hash[idx] != assetFileInfo.m_hash[idx]) @@ -343,7 +341,7 @@ namespace UnitTest } } - // Verifying that correct assetId are present in the assetFileInfo list + // Verifying that correct assetId are present in the assetFileInfo list AZStd::unordered_set expectedAssetIds{ m_assets[0], m_assets[1], m_assets[2], m_assets[3], m_assets[4], m_assets[5] }; for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList) @@ -403,7 +401,7 @@ namespace UnitTest } } - // Verifying that correct assetId are present in the assetFileInfo list + // Verifying that correct assetId are present in the assetFileInfo list AZStd::unordered_set expectedAssetIds{ m_assets[1], m_assets[2], m_assets[3], m_assets[4] }; for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList) @@ -462,7 +460,7 @@ namespace UnitTest } } - // Verifying that correct assetId are present in the assetFileInfo list + // Verifying that correct assetId are present in the assetFileInfo list AZStd::unordered_set expectedAssetIds{ m_assets[5] }; for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList) @@ -493,7 +491,7 @@ namespace UnitTest EXPECT_EQ(assetFileInfoList.m_fileInfoList.size(), 5); - // Verifying that correct assetId are present in the assetFileInfo list + // Verifying that correct assetId are present in the assetFileInfo list AZStd::unordered_set expectedAssetIds{ m_assets[0], m_assets[1], m_assets[2], m_assets[3], m_assets[4] }; for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList) @@ -601,7 +599,7 @@ namespace UnitTest } } - // Verifying that correct assetId are present in the assetFileInfo list + // Verifying that correct assetId are present in the assetFileInfo list AZStd::unordered_set expectedAssetIds{ m_assets[2] }; for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList) @@ -625,12 +623,12 @@ namespace UnitTest AssetFileInfoListComparison::ComparisonData filePatternComparisonData(AssetFileInfoListComparison::ComparisonType::FilePattern,"$1", "Asset[0-3].txt", AssetFileInfoListComparison::FilePatternType::Regex); filePatternComparisonData.m_firstInput = TempFiles[FileIndex::FirstAssetFileInfoList]; assetFileInfoListComparison.AddComparisonStep(filePatternComparisonData); - + AzToolsFramework::AssetFileInfoListComparison::ComparisonData deltaComparisonData(AzToolsFramework::AssetFileInfoListComparison::ComparisonType::Delta, TempFiles[FileIndex::ResultAssetFileInfoList]); deltaComparisonData.m_firstInput = "$1"; deltaComparisonData.m_secondInput = TempFiles[FileIndex::SecondAssetFileInfoList]; assetFileInfoListComparison.AddComparisonStep(deltaComparisonData); - + ASSERT_TRUE(assetFileInfoListComparison.CompareAndSaveResults().IsSuccess()) << "Multiple Comparison Operation( FilePattern + Delta ) failed.\n"; // Output of the FilePattern Operation should be {0,1,2,3} // Output of the Delta Operation should be {2*,4*,5} @@ -666,7 +664,7 @@ namespace UnitTest } } - // Verifying that correct assetId are present in the assetFileInfo list + // Verifying that correct assetId are present in the assetFileInfo list AZStd::unordered_set expectedAssetIds{ m_assets[2], m_assets[4], m_assets[5] }; for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList) @@ -738,7 +736,7 @@ namespace UnitTest } } - // Verifying that correct assetId are present in the assetFileInfo list + // Verifying that correct assetId are present in the assetFileInfo list AZStd::unordered_set expectedAssetIds{ m_assets[4], m_assets[5] }; for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList) diff --git a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp index 827737f561..f04a0642d1 100644 --- a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp +++ b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp @@ -63,10 +63,8 @@ namespace UnitTest ArgumentContainer argContainer{ {} }; // Append Command Line override for the Project Cache Path - AZ::IO::Path cacheProjectRootFolder{ m_tempDir.GetDirectory() }; - auto projectCachePathOverride = FixedValueString::format(R"(--project-cache-path="%s")", cacheProjectRootFolder.c_str()); - auto projectPathOverride = FixedValueString{ R"(--project-path=AutomatedTesting)" }; - argContainer.push_back(projectCachePathOverride.data()); + auto cacheProjectRootFolder = AZ::IO::Path{ m_tempDir.GetDirectory() } / "Cache"; + auto projectPathOverride = FixedValueString::format(R"(--project-path="%s")", m_tempDir.GetDirectory()); argContainer.push_back(projectPathOverride.data()); m_application = new ToolsTestApplication("AssetSeedManagerTest", aznumeric_caster(argContainer.size()), argContainer.data()); m_assetSeedManager = new AzToolsFramework::AssetSeedManager(); diff --git a/Code/Framework/AzToolsFramework/Tests/ComponentAddRemove.cpp b/Code/Framework/AzToolsFramework/Tests/ComponentAddRemove.cpp index e416efc5c6..9d6c8a4bff 100644 --- a/Code/Framework/AzToolsFramework/Tests/ComponentAddRemove.cpp +++ b/Code/Framework/AzToolsFramework/Tests/ComponentAddRemove.cpp @@ -572,7 +572,9 @@ namespace UnitTest AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); AzFramework::Application::Descriptor descriptor; diff --git a/Code/Framework/AzToolsFramework/Tests/GenericComponentWrapperTest.cpp b/Code/Framework/AzToolsFramework/Tests/GenericComponentWrapperTest.cpp index 54132b974e..dbe3963069 100644 --- a/Code/Framework/AzToolsFramework/Tests/GenericComponentWrapperTest.cpp +++ b/Code/Framework/AzToolsFramework/Tests/GenericComponentWrapperTest.cpp @@ -59,7 +59,9 @@ protected: AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_app.Start(AZ::ComponentApplication::Descriptor()); @@ -184,7 +186,9 @@ public: AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_app.Start(AzFramework::Application::Descriptor()); diff --git a/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp b/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp index 9c226cbb1b..e58e347b2a 100644 --- a/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp @@ -44,10 +44,8 @@ namespace UnitTest ArgumentContainer argContainer{ {} }; // Append Command Line override for the Project Cache Path - AZ::IO::Path cacheProjectRootFolder{ m_tempDir.GetDirectory() }; - auto projectCachePathOverride = FixedValueString::format(R"(--project-cache-path="%s")", cacheProjectRootFolder.c_str()); - auto projectPathOverride = FixedValueString{ R"(--project-path=AutomatedTesting)" }; - argContainer.push_back(projectCachePathOverride.data()); + auto cacheProjectRootFolder = AZ::IO::Path{ m_tempDir.GetDirectory() } / "Cache"; + auto projectPathOverride = FixedValueString::format(R"(--project-path="%s")", m_tempDir.GetDirectory()); argContainer.push_back(projectPathOverride.data()); m_application = new ToolsTestApplication("AddressedAssetCatalogManager", aznumeric_caster(argContainer.size()), argContainer.data()); @@ -195,10 +193,7 @@ namespace UnitTest ArgumentContainer argContainer{ {} }; // Append Command Line override for the Project Cache Path - AZ::IO::Path cacheProjectRootFolder{ m_tempDir.GetDirectory() }; - auto projectCachePathOverride = FixedValueString::format(R"(--project-cache-path="%s")", cacheProjectRootFolder.c_str()); - auto projectPathOverride = FixedValueString{ R"(--project-path=AutomatedTesting)" }; - argContainer.push_back(projectCachePathOverride.data()); + auto projectPathOverride = FixedValueString::format(R"(--project-path="%s")", m_tempDir.GetDirectory()); argContainer.push_back(projectPathOverride.data()); m_application = new ToolsTestApplication("MessageTest", aznumeric_caster(argContainer.size()), argContainer.data()); diff --git a/Code/Framework/AzToolsFramework/Tests/Slices.cpp b/Code/Framework/AzToolsFramework/Tests/Slices.cpp index 1e849de620..9b546357cd 100644 --- a/Code/Framework/AzToolsFramework/Tests/Slices.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Slices.cpp @@ -1059,7 +1059,9 @@ namespace UnitTest AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_app.Start(AzFramework::Application::Descriptor()); diff --git a/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp b/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp index 0d915dcc49..07eae67a81 100644 --- a/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp +++ b/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp @@ -66,7 +66,9 @@ namespace AssetBundler } auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); diff --git a/Code/Tools/AssetBundler/tests/tests_main.cpp b/Code/Tools/AssetBundler/tests/tests_main.cpp index 21a6bf8a6f..86432d730e 100644 --- a/Code/Tools/AssetBundler/tests/tests_main.cpp +++ b/Code/Tools/AssetBundler/tests/tests_main.cpp @@ -106,7 +106,9 @@ namespace AssetBundler } auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath(); diff --git a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp index 7823a0582f..305578219f 100644 --- a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp +++ b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp @@ -431,8 +431,9 @@ namespace AssetProcessor } file.Close(); - AZ::u32 hashedSpecialization = static_cast(AZStd::hash{}(specializationString)); - AZ_Assert(hashedSpecialization != 0, "Product ID generation failed for specialization %.*s. This can result in a product ID collision with other builders for this asset.", + const AZ::u32 hashedSpecialization = static_cast(AZStd::hash{}(specializationString)); + AZ_Assert(hashedSpecialization != 0, "Product ID generation failed for specialization %.*s." + " This can result in a product ID collision with other builders for this asset.", AZ_STRING_ARG(specializationString)); response.m_outputProducts.emplace_back(outputPath, m_assetType, hashedSpecialization); response.m_outputProducts.back().m_dependenciesHandled = true; diff --git a/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp index 2a1c4e4755..5c449e2764 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp @@ -128,7 +128,9 @@ namespace AssetProcessor settingsRegistry->Set(cacheRootKey, m_data->m_temporarySourceDir.absoluteFilePath("Cache").toUtf8().constData()); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - settingsRegistry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + settingsRegistry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry); AssetUtilities::ComputeProjectCacheRoot(m_data->m_cacheRootDir); QString normalizedCacheRoot = AssetUtilities::NormalizeDirectoryPath(m_data->m_cacheRootDir.absolutePath()); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp index 516f6beb3c..6a0da96151 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp @@ -107,12 +107,13 @@ namespace AssetProcessorMessagesTests AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey }; constexpr AZ::SettingsRegistryInterface::FixedValueString projectPathKey{ bootstrapKey + "/project_path" }; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); // Force the branch token into settings registry before starting the application manager. // This avoids writing the asset_processor.setreg file which can cause fileIO errors. - const AZ::IO::FixedMaxPathString enginePath = AZ::Utils::GetEnginePath(); constexpr AZ::SettingsRegistryInterface::FixedValueString branchTokenKey{ bootstrapKey + "/assetProcessor_branch_token" }; AZStd::string token; AZ::StringFunc::AssetPath::CalculateBranchToken(enginePath.c_str(), token); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp index 3249b7e396..2629f7b956 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp @@ -71,12 +71,13 @@ namespace AssetProcessor auto registry = AZ::SettingsRegistry::Get(); auto bootstrapKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey); auto projectPathKey = bootstrapKey + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); // Forcing the branch token into settings registry before starting the application manager. // This avoids writing the asset_processor.setreg file which can cause fileIO errors. - AZ::IO::FixedMaxPathString enginePath = AZ::Utils::GetEnginePath(); auto branchTokenKey = bootstrapKey + "/assetProcessor_branch_token"; AZStd::string token; AzFramework::StringFunc::AssetPath::CalculateBranchToken(enginePath.c_str(), token); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h index 258268c182..719b04610c 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h @@ -50,7 +50,9 @@ namespace AssetProcessor + "/project_path"; if(auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { - settingsRegistry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + settingsRegistry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry); } } diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp index da6e995c97..59ae25601d 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp @@ -192,7 +192,9 @@ void AssetProcessorManagerTest::SetUp() registry->Set(cacheRootKey, tempPath.absoluteFilePath("Cache").toUtf8().constData()); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_data->m_databaseLocationListener.BusConnect(); diff --git a/Code/Tools/DeltaCataloger/Tests/tests_main.cpp b/Code/Tools/DeltaCataloger/Tests/tests_main.cpp index 1a3b86d34b..d5a344b686 100644 --- a/Code/Tools/DeltaCataloger/Tests/tests_main.cpp +++ b/Code/Tools/DeltaCataloger/Tests/tests_main.cpp @@ -45,7 +45,9 @@ protected: AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); AZ::ComponentApplication::Descriptor desc; diff --git a/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h b/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h index 456afd0006..c4872ab425 100644 --- a/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h +++ b/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h @@ -150,7 +150,7 @@ struct AssetValidationTest auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - m_registry.Set(projectPathKey, "AutomatedTesting"); + m_registry.Set(projectPathKey, (AZ::IO::FixedMaxPath(GetEngineRoot()) / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry); // Set the engine root to the temporary directory and re-update the runtime file paths diff --git a/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h b/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h index 034fd2045b..62ebffa7e9 100644 --- a/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h +++ b/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h @@ -61,7 +61,9 @@ namespace EMotionFX constexpr auto projectPathKey = FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; if(auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { - settingsRegistry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + settingsRegistry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry); } } diff --git a/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp b/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp index d630605cbc..9dbbb34e6c 100644 --- a/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp @@ -323,7 +323,9 @@ sys.version auto registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_app.RegisterComponentDescriptor(EditorPythonBindings::PythonSystemComponent::CreateDescriptor()); diff --git a/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp b/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp index e50fbbe56c..697c5c3b06 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp @@ -99,7 +99,9 @@ namespace UnitTest AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_app.Start(m_descriptor); diff --git a/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp b/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp index a9b36d4624..c62ed92c83 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp @@ -31,7 +31,9 @@ namespace UnitTest AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_app.Start(m_descriptor); diff --git a/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp b/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp index f679a3502d..574fd4edfb 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp @@ -22,7 +22,9 @@ class SeedBuilderTests AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_app.Start(AZ::ComponentApplication::Descriptor()); diff --git a/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp b/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp index 60074ac0a0..59be8a85e1 100644 --- a/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp +++ b/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp @@ -85,7 +85,9 @@ protected: AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_app.Start(m_descriptor); diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp index ab6770c3fe..d33b453c01 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp @@ -174,7 +174,9 @@ namespace UnitTest AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); AZ::ComponentApplication::Descriptor desc; diff --git a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp index 2709cd40b7..15d50bcfa3 100644 --- a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp +++ b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp @@ -139,7 +139,9 @@ public: AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_app.Start(AZ::ComponentApplication::Descriptor()); diff --git a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp index 3a7b20553e..a1ca5be766 100644 --- a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp +++ b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp @@ -35,7 +35,9 @@ protected: AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get(); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - registry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry); m_app.Start(AZ::ComponentApplication::Descriptor()); diff --git a/cmake/Projects.cmake b/cmake/Projects.cmake index 34ef3efd9b..2c42533e35 100644 --- a/cmake/Projects.cmake +++ b/cmake/Projects.cmake @@ -132,24 +132,7 @@ function(add_project_json_external_subdirectories project_path) endif() endfunction() -# Add the projects here so the above function is found -foreach(project ${LY_PROJECTS}) - file(REAL_PATH ${project} full_directory_path BASE_DIRECTORY ${CMAKE_SOURCE_DIR}) - string(SHA256 full_directory_hash ${full_directory_path}) - - # Truncate the full_directory_hash down to 8 characters to avoid hitting the Windows 260 character path limit - # when the external subdirectory contains relative paths of significant length - string(SUBSTRING ${full_directory_hash} 0 8 full_directory_hash) - - get_filename_component(project_folder_name ${project} NAME) - list(APPEND LY_PROJECTS_FOLDER_NAME ${project_folder_name}) - add_subdirectory(${project} "${project_folder_name}-${full_directory_hash}") - ly_generate_project_build_path_setreg(${full_directory_path}) - add_project_json_external_subdirectories(${full_directory_path}) - - # Get project name - o3de_read_json_key(project_name ${full_directory_path}/project.json "project_name") - +function(install_project_asset_artifacts project_real_path) # The cmake tar command has a bit of a flaw # Any paths within the archive files it creates are relative to the current working directory. # That means with the setup of: @@ -172,13 +155,12 @@ if("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$") if(NOT DEFINED LY_ASSET_DEPLOY_ASSET_TYPE) set(LY_ASSET_DEPLOY_ASSET_TYPE @LY_ASSET_DEPLOY_ASSET_TYPE@) endif() - message(STATUS "Generating ${install_pak_output_folder}/engine.pak from @full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") + message(STATUS "Generating ${install_pak_output_folder}/engine.pak from @project_real_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") file(MAKE_DIRECTORY "${install_pak_output_folder}") - cmake_path(SET cache_product_path "@full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") + cmake_path(SET cache_product_path "@project_real_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") # Copy the generated cmake_dependencies.*.setreg files for loading gems in non-monolithic to the cache file(GLOB gem_source_paths_setreg "${runtime_output_directory_RELEASE}/Registry/*.setreg") - # The MergeSettingsToRegistry_TargetBuildDependencyRegistry function looks for lowercase "registry" - # So make sure the to copy it to a lowercase path, so that it works on non-case sensitive filesystems + # The MergeSettingsToRegistry_TargetBuildDependencyRegistry function looks for lowercase "registry" directory file(MAKE_DIRECTORY "${cache_product_path}/registry") file(COPY ${gem_source_paths_setreg} DESTINATION "${cache_product_path}/registry") @@ -194,11 +176,40 @@ if("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$") message(STATUS "${install_output_folder}/engine.pak generated") endif() endif() + + # Remove copied .setreg files from the Cache directory + unset(artifacts_to_remove) + foreach(gem_source_path_setreg IN LISTS gem_source_paths_setreg) + cmake_path(GET gem_source_path_setreg FILENAME setreg_filename) + list(APPEND artifacts_to_remove "${cache_product_path}/registry/${setreg_filename}") + endforeach() + file(REMOVE ${artifacts_to_remove}) endif() ]=]) string(CONFIGURE "${install_engine_pak_template}" install_engine_pak_code @ONLY) ly_install_run_code("${install_engine_pak_code}") +endfunction() + +# Add the projects here so the above function is found +foreach(project ${LY_PROJECTS}) + file(REAL_PATH ${project} full_directory_path BASE_DIRECTORY ${CMAKE_SOURCE_DIR}) + string(SHA256 full_directory_hash ${full_directory_path}) + + # Truncate the full_directory_hash down to 8 characters to avoid hitting the Windows 260 character path limit + # when the external subdirectory contains relative paths of significant length + string(SUBSTRING ${full_directory_hash} 0 8 full_directory_hash) + + get_filename_component(project_folder_name ${project} NAME) + list(APPEND LY_PROJECTS_FOLDER_NAME ${project_folder_name}) + add_subdirectory(${project} "${project_folder_name}-${full_directory_hash}") + ly_generate_project_build_path_setreg(${full_directory_path}) + add_project_json_external_subdirectories(${full_directory_path}) + + # Get project name + o3de_read_json_key(project_name ${full_directory_path}/project.json "project_name") + + install_project_asset_artifacts(${full_directory_path}) endforeach() diff --git a/scripts/build/Platform/Linux/asset_linux.sh b/scripts/build/Platform/Linux/asset_linux.sh index df910db646..10f7ee6b6f 100755 --- a/scripts/build/Platform/Linux/asset_linux.sh +++ b/scripts/build/Platform/Linux/asset_linux.sh @@ -9,6 +9,8 @@ set -o errexit # exit on the first failure encountered +SOURCE_DIRECTORY=${PWD} + if [[ ! -d $OUTPUT_DIRECTORY ]]; then echo [ci_build] Error: $OUTPUT_DIRECTORY was not found exit 1 @@ -22,8 +24,8 @@ fi for project in $(echo $CMAKE_LY_PROJECTS | sed "s/;/ /g") do - echo [ci_build] ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$project --platforms=$ASSET_PROCESSOR_PLATFORMS - ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$project --platforms=$ASSET_PROCESSOR_PLATFORMS + echo [ci_build] ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$SOURCE_DIRECTORY/$project --platforms=$ASSET_PROCESSOR_PLATFORMS + ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$SOURCE_DIRECTORY/$project --platforms=$ASSET_PROCESSOR_PLATFORMS done popd diff --git a/scripts/build/Platform/Mac/asset_mac.sh b/scripts/build/Platform/Mac/asset_mac.sh index f70d898e0c..96eaeab5aa 100755 --- a/scripts/build/Platform/Mac/asset_mac.sh +++ b/scripts/build/Platform/Mac/asset_mac.sh @@ -9,6 +9,8 @@ set -o errexit # exit on the first failure encountered +SOURCE_DIRECTORY=${PWD} + if [[ ! -d $OUTPUT_DIRECTORY ]]; then echo [ci_build] Error: $OUTPUT_DIRECTORY was not found exit 1 @@ -22,8 +24,8 @@ fi for project in $(echo $CMAKE_LY_PROJECTS | sed "s/;/ /g") do - echo [ci_build] ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$project --platforms=$ASSET_PROCESSOR_PLATFORMS - ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$project --platforms=$ASSET_PROCESSOR_PLATFORMS + echo [ci_build] ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$SOURCE_DIRECTORY/$project --platforms=$ASSET_PROCESSOR_PLATFORMS + ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$SOURCE_DIRECTORY/$project --platforms=$ASSET_PROCESSOR_PLATFORMS done popd diff --git a/scripts/build/Platform/Windows/asset_windows.cmd b/scripts/build/Platform/Windows/asset_windows.cmd index 8db0e43e31..cc266ba42a 100644 --- a/scripts/build/Platform/Windows/asset_windows.cmd +++ b/scripts/build/Platform/Windows/asset_windows.cmd @@ -9,6 +9,8 @@ REM SETLOCAL EnableDelayedExpansion +SET SOURCE_DIRECTORY=%CD% + IF NOT EXIST %OUTPUT_DIRECTORY% ( ECHO [ci_build] Error: %OUTPUT_DIRECTORY% was not found GOTO :error @@ -21,8 +23,8 @@ IF NOT EXIST %ASSET_PROCESSOR_BINARY% ( ) FOR %%P in (%CMAKE_LY_PROJECTS%) do ( - ECHO [ci_build] %ASSET_PROCESSOR_BINARY% %ASSET_PROCESSOR_OPTIONS% --project-path=%%P --platforms=%ASSET_PROCESSOR_PLATFORMS% - %ASSET_PROCESSOR_BINARY% %ASSET_PROCESSOR_OPTIONS% --project-path=%%P --platforms=%ASSET_PROCESSOR_PLATFORMS% + ECHO [ci_build] %ASSET_PROCESSOR_BINARY% %ASSET_PROCESSOR_OPTIONS% --project-path=%SOURCE_DIRECTORY%/%%P --platforms=%ASSET_PROCESSOR_PLATFORMS% + %ASSET_PROCESSOR_BINARY% %ASSET_PROCESSOR_OPTIONS% --project-path=%SOURCE_DIRECTORY%/%%P --platforms=%ASSET_PROCESSOR_PLATFORMS% IF NOT !ERRORLEVEL!==0 GOTO :popd_error ) From 2f8de3e797a40a4126d546c872f750ad55d76161 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Thu, 4 Nov 2021 09:37:26 -0700 Subject: [PATCH 058/177] Keep gem repos pages in sync Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> --- .../Source/CreateProjectCtrl.cpp | 9 ++++---- .../Source/EngineScreenCtrl.cpp | 18 ++++++++++++++++ .../ProjectManager/Source/EngineScreenCtrl.h | 4 ++++ .../Source/GemCatalog/GemCatalogScreen.cpp | 21 +++++++++++++++---- .../Source/GemRepo/GemRepoScreen.cpp | 5 +++++ .../Source/GemRepo/GemRepoScreen.h | 3 +++ .../Source/UpdateProjectCtrl.cpp | 4 ++++ 7 files changed, 56 insertions(+), 8 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index 6caa9b8a2b..1aad4a7206 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -51,13 +51,11 @@ namespace O3DE::ProjectManager m_gemRepoScreen = new GemRepoScreen(this); m_stack->addWidget(m_gemRepoScreen); + vLayout->addWidget(m_stack); - connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest); - connect( - m_gemRepoScreen, &GemRepoScreen::OnRefresh, - [this]() + connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, [this]() { const QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath(); m_gemCatalogScreen->Refresh(projectTemplatePath + "/Template"); @@ -135,6 +133,9 @@ namespace O3DE::ProjectManager // Gather the enabled gems from the default project template when starting the create new project workflow. ReinitGemCatalogForSelectedTemplate(); + + // make sure the gem repo has the latest details + m_gemRepoScreen->Reinit(); } void CreateProjectCtrl::HandleBackButton() diff --git a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp index f30a8e0daa..9d9110922f 100644 --- a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp @@ -39,6 +39,10 @@ namespace O3DE::ProjectManager m_tabWidget->addTab(m_engineSettingsScreen, tr("General")); m_tabWidget->addTab(m_gemRepoScreen, tr("Gem Repositories")); + + // when tab changes, notify the current screen so it can refresh + connect(m_tabWidget, &QTabWidget::currentChanged, this, &EngineScreenCtrl::TabChanged); + topBarHLayout->addWidget(m_tabWidget); vLayout->addWidget(topBarFrameWidget); @@ -46,6 +50,11 @@ namespace O3DE::ProjectManager setLayout(vLayout); } + void EngineScreenCtrl::TabChanged([[maybe_unused]] int index) + { + NotifyCurrentScreen(); + } + ProjectManagerScreen EngineScreenCtrl::GetScreenEnum() { return ProjectManagerScreen::UpdateProject; @@ -71,6 +80,15 @@ namespace O3DE::ProjectManager return false; } + void EngineScreenCtrl::NotifyCurrentScreen() + { + ScreenWidget* screen = reinterpret_cast(m_tabWidget->currentWidget()); + if (screen) + { + screen->NotifyCurrentScreen(); + } + } + void EngineScreenCtrl::GoToScreen(ProjectManagerScreen screen) { if (screen == m_engineSettingsScreen->GetScreenEnum()) diff --git a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h index b7142ba226..cf0d2a24d0 100644 --- a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h +++ b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h @@ -30,6 +30,10 @@ namespace O3DE::ProjectManager bool IsTab() override; bool ContainsScreen(ProjectManagerScreen screen) override; void GoToScreen(ProjectManagerScreen screen) override; + void NotifyCurrentScreen() override; + + public slots: + void TabChanged(int index); QTabWidget* m_tabWidget = nullptr; EngineSettingsScreen* m_engineSettingsScreen = nullptr; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 8234b80c8e..915c837da4 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include namespace O3DE::ProjectManager @@ -160,6 +159,7 @@ namespace O3DE::ProjectManager { QHash gemInfoHash; + // create a hash with the gem name as key AZ::Outcome, AZStd::string> allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath); if (allGemInfosResult.IsSuccess()) { @@ -170,6 +170,7 @@ namespace O3DE::ProjectManager } } + // add all the gem repos into the hash AZ::Outcome, AZStd::string> allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos(); if (allRepoGemInfosResult.IsSuccess()) { @@ -183,24 +184,32 @@ namespace O3DE::ProjectManager } } - // remove rows for gems that were removed and not project dependencies + // remove gems from the model that no longer exist in the hash and are not project dependencies int i = 0; while (i < m_gemModel->rowCount()) { QModelIndex index = m_gemModel->index(i,0); QString gemName = m_gemModel->GetName(index); - if (!gemInfoHash.contains(gemName) && !m_gemModel->IsAdded(index) && !m_gemModel->IsAddedDependency(index)) + const bool gemFound = gemInfoHash.contains(gemName); + if (!gemFound && !m_gemModel->IsAdded(index) && !m_gemModel->IsAddedDependency(index)) { m_gemModel->removeRow(i); } else { + if (!gemFound && (m_gemModel->IsAdded(index) || !m_gemModel->IsAddedDependency(index))) + { + const QString error = tr("Gem %1 was removed or unregistered, but is still used by the project.").arg(gemName); + AZ_Warning("Project Manager", false, error.toUtf8().constData()); + QMessageBox::warning(this, tr("Gem not found"), error.toUtf8().constData()); + } + gemInfoHash.remove(gemName); i++; } } - // add new rows + // add all gems remaining in the hash that were not removed for(auto iter = gemInfoHash.begin(); iter != gemInfoHash.end(); ++iter) { m_gemModel->AddGem(iter.value()); @@ -208,6 +217,10 @@ namespace O3DE::ProjectManager m_gemModel->UpdateGemDependencies(); m_proxyModel->sort(/*column=*/0); + + // temporary, until we can refresh filter counts + m_proxyModel->ResetFilters(); + m_filterWidget->ResetAllFilters(); } void GemCatalogScreen::OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies) diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp index 30a57417d2..794635a3e3 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp @@ -52,6 +52,11 @@ namespace O3DE::ProjectManager Reinit(); } + void GemRepoScreen::NotifyCurrentScreen() + { + Reinit(); + } + void GemRepoScreen::Reinit() { m_gemRepoModel->clear(); diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h index 0516005eef..eed9a5ec4a 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h @@ -38,6 +38,8 @@ namespace O3DE::ProjectManager GemRepoModel* GetGemRepoModel() const { return m_gemRepoModel; } + void NotifyCurrentScreen() override; + signals: void OnRefresh(); @@ -47,6 +49,7 @@ namespace O3DE::ProjectManager void HandleRefreshAllButton(); void HandleRefreshRepoButton(const QModelIndex& modelIndex); + private: void FillModel(); QFrame* CreateNoReposContent(); diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index 5de3511f84..e76b4093a9 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -43,6 +43,7 @@ namespace O3DE::ProjectManager m_gemRepoScreen = new GemRepoScreen(this); connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &UpdateProjectCtrl::OnChangeScreenRequest); + connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, [this](){ m_gemCatalogScreen->Refresh(m_projectInfo.m_path); }); m_stack = new QStackedWidget(this); m_stack->setObjectName("body"); @@ -101,6 +102,9 @@ namespace O3DE::ProjectManager // Gather the available gems that will be shown in the gem catalog. m_gemCatalogScreen->ReinitForProject(m_projectInfo.m_path); + + // make sure the gem repo has the latest repo details + m_gemRepoScreen->Reinit(); } void UpdateProjectCtrl::OnChangeScreenRequest(ProjectManagerScreen screen) From 6bce0a9a8dd66e85bd7905f7aa4f77ded372a9bd Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Thu, 4 Nov 2021 09:52:35 -0700 Subject: [PATCH 059/177] LYN-7054 + LYN-7704 | Exit Focus Mode when starting Game Mode, correct painting of Prefab capsules in Outliner. (#5280) * Add RefreshAllContainerEntities function to ContainerEntityInterface. It refreshes all registered containers so that listeners can refresh their state. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> * Move Prefab border painting to foreground, and invert foreground painting order. This ensures the Prefab capsules are drawn according to UX. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../ContainerEntityInterface.h | 4 ++++ .../ContainerEntitySystemComponent.cpp | 9 ++++++++ .../ContainerEntitySystemComponent.h | 1 + .../UI/Outliner/EntityOutlinerListModel.cpp | 13 +----------- .../UI/Prefab/PrefabIntegrationManager.cpp | 21 +++++++++++++++++++ .../UI/Prefab/PrefabIntegrationManager.h | 6 ++++++ .../UI/Prefab/PrefabUiHandler.cpp | 2 +- .../UI/Prefab/PrefabUiHandler.h | 7 +++++-- 8 files changed, 48 insertions(+), 15 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h index 2d7d9dc511..67bf64d224 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h @@ -58,6 +58,10 @@ namespace AzToolsFramework //! @return The highest closed entity container id if any, or entityId otherwise. virtual AZ::EntityId FindHighestSelectableEntity(AZ::EntityId entityId) const = 0; + //! Triggers the OnContainerEntityStatusChanged notifications for all registered containers, + //! allowing listeners to update correctly. + virtual void RefreshAllContainerEntities(AzFramework::EntityContextId entityContextId) const = 0; + //! Clears all open state information for Container Entities for the EntityContextId provided. //! Used when context is switched, for example in the case of a new root prefab being loaded //! in place of an old one. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp index 0a27a5cb90..78cf84c6a1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp @@ -142,6 +142,15 @@ namespace AzToolsFramework Clear(editorEntityContextId); } + void ContainerEntitySystemComponent::RefreshAllContainerEntities([[maybe_unused]] AzFramework::EntityContextId entityContextId) const + { + for (AZ::EntityId containerEntityId : m_containers) + { + ContainerEntityNotificationBus::Broadcast( + &ContainerEntityNotificationBus::Events::OnContainerEntityStatusChanged, containerEntityId, m_openContainers.contains(containerEntityId)); + } + } + ContainerEntityOperationResult ContainerEntitySystemComponent::Clear(AzFramework::EntityContextId entityContextId) { // We don't yet support multiple entity contexts, so only clear the default. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h index 7a11e05096..68153a77cb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h @@ -47,6 +47,7 @@ namespace AzToolsFramework ContainerEntityOperationResult SetContainerOpen(AZ::EntityId entityId, bool open) override; bool IsContainerOpen(AZ::EntityId entityId) const override; AZ::EntityId FindHighestSelectableEntity(AZ::EntityId entityId) const override; + void RefreshAllContainerEntities(AzFramework::EntityContextId entityContextId) const override; ContainerEntityOperationResult Clear(AzFramework::EntityContextId entityContextId) override; bool IsUnderClosedContainerEntity(AZ::EntityId entityId) const override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp index 13ec27c1b8..a68a72f00a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp @@ -2180,20 +2180,9 @@ namespace AzToolsFramework void EntityOutlinerItemDelegate::PaintAncestorForegrounds(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { - // Go through ancestors and add them to the stack - AZStd::stack handlerStack; - + // Ancestor foregrounds are painted on top of the childrens'. for (QModelIndex ancestorIndex = index.parent(); ancestorIndex.isValid(); ancestorIndex = ancestorIndex.parent()) { - handlerStack.push(ancestorIndex); - } - - // Apply the ancestor overrides from top to bottom - while (!handlerStack.empty()) - { - QModelIndex ancestorIndex = handlerStack.top(); - handlerStack.pop(); - AZ::EntityId ancestorEntityId(ancestorIndex.data(EntityOutlinerListModel::EntityIdRole).value()); auto ancestorUiHandler = m_editorEntityFrameworkInterface->GetHandler(ancestorEntityId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 951b876347..aa6d82e634 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -55,6 +55,7 @@ #include #include #include +#include #include #include @@ -151,6 +152,7 @@ namespace AzToolsFramework PrefabInstanceContainerNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension); + EditorEntityContextNotificationBus::Handler::BusConnect(); InitializeShortcuts(); } @@ -159,6 +161,7 @@ namespace AzToolsFramework { UninitializeShortcuts(); + EditorEntityContextNotificationBus::Handler::BusDisconnect(); AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect(); AZ::Interface::Unregister(this); PrefabInstanceContainerNotificationBus::Handler::BusDisconnect(); @@ -423,6 +426,24 @@ namespace AzToolsFramework } } + void PrefabIntegrationManager::OnStartPlayInEditorBegin() + { + // Focus on the root prefab (AZ::EntityId() will default to it) + s_prefabFocusPublicInterface->FocusOnOwningPrefab(AZ::EntityId()); + } + + void PrefabIntegrationManager::OnStopPlayInEditor() + { + // Refresh all containers when leaving Game Mode to ensure everything is synced. + QTimer::singleShot( + 0, + [&]() + { + s_containerEntityInterface->RefreshAllContainerEntities(s_editorEntityContextId); + } + ); + } + void PrefabIntegrationManager::ContextMenu_CreatePrefab(AzToolsFramework::EntityIdList selectedEntities) { // Save a reference to our currently active window since it will be diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h index a8d325c4cf..808a0c2408 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,7 @@ namespace AzToolsFramework , public PrefabInstanceContainerNotificationBus::Handler , public PrefabIntegrationInterface , public QObject + , private EditorEntityContextNotificationBus::Handler { public: AZ_CLASS_ALLOCATOR(PrefabIntegrationManager, AZ::SystemAllocator, 0); @@ -76,6 +78,10 @@ namespace AzToolsFramework // EntityOutlinerSourceDropHandlingBus overrides ... void HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const override; + // EditorEntityContextNotificationBus overrides ... + void OnStartPlayInEditorBegin() override; + void OnStopPlayInEditor() override; + // PrefabInstanceContainerNotificationBus overrides ... void OnPrefabComponentActivate(AZ::EntityId entityId) override; void OnPrefabComponentDeactivate(AZ::EntityId entityId) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp index 8bd1b7db04..8b56b26508 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp @@ -185,7 +185,7 @@ namespace AzToolsFramework painter->restore(); } - void PrefabUiHandler::PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index, + void PrefabUiHandler::PaintDescendantForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index, const QModelIndex& descendantIndex) const { if (!painter) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h index 3627449ab4..bb1c646dbe 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h @@ -36,9 +36,12 @@ namespace AzToolsFramework QString GenerateItemTooltip(AZ::EntityId entityId) const override; QIcon GenerateItemIcon(AZ::EntityId entityId) const override; void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; - void PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index, - const QModelIndex& descendantIndex) const override; void PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + void PaintDescendantForeground( + QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index, + const QModelIndex& descendantIndex) const override; bool OnOutlinerItemClick(const QPoint& position, const QStyleOptionViewItem& option, const QModelIndex& index) const override; void OnOutlinerItemCollapse(const QModelIndex& index) const override; bool OnEntityDoubleClick(AZ::EntityId entityId) const override; From 774b7c2593ad7c9b03412b2915e9138a6da64278 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Thu, 4 Nov 2021 11:03:36 -0700 Subject: [PATCH 060/177] Fixed minor logic issue with gem removal warning Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> --- .../Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 915c837da4..4574e8509b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -197,7 +197,7 @@ namespace O3DE::ProjectManager } else { - if (!gemFound && (m_gemModel->IsAdded(index) || !m_gemModel->IsAddedDependency(index))) + if (!gemFound && (m_gemModel->IsAdded(index) || m_gemModel->IsAddedDependency(index))) { const QString error = tr("Gem %1 was removed or unregistered, but is still used by the project.").arg(gemName); AZ_Warning("Project Manager", false, error.toUtf8().constData()); From 9958e5f0128e17bd417022f949d19c1ae5054534 Mon Sep 17 00:00:00 2001 From: amzn-sj Date: Thu, 4 Nov 2021 11:04:01 -0700 Subject: [PATCH 061/177] [MacOS] Launching Editor from ProjectManager and other misc. fixes Signed-off-by: amzn-sj --- .../Asset/AssetSystemComponentHelper_Mac.cpp | 32 ++++++++++-------- .../BundleLauncher/O3DE_SDK_Launcher.cpp | 6 ++++ .../Platform/Linux/ProjectUtils_linux.cpp | 7 ++++ .../Platform/Mac/ProjectUtils_mac.cpp | 33 +++++++++++++++++++ .../Platform/Windows/ProjectUtils_windows.cpp | 7 ++++ .../ProjectManager/Source/ProjectUtils.h | 4 ++- .../ProjectManager/Source/ProjectsScreen.cpp | 4 +-- 7 files changed, 77 insertions(+), 16 deletions(-) diff --git a/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp b/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp index c6f481b65b..a3381dabb8 100644 --- a/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp +++ b/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include #include @@ -24,14 +26,20 @@ namespace AzFramework::AssetSystem::Platform AZ::IO::FixedMaxPath assetProcessorPath{ executableDirectory }; // In Mac the Editor and game is within a bundle, so the path to the sibling app // has to go up from the Contents/MacOS folder the binary is in - assetProcessorPath /= "../../../AssetProcessor.app"; + assetProcessorPath /= "../../../AssetProcessor.app/Contents/MacOS/AssetProcessor"; assetProcessorPath = assetProcessorPath.LexicallyNormal(); if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str())) { - // Check for existence of one under a "bin" directory, i.e. engineRoot is an SDK structure. - assetProcessorPath = - AZ::IO::FixedMaxPath{engineRoot} / "bin" / AZ_TRAIT_OS_PLATFORM_NAME / AZ_BUILD_CONFIGURATION_TYPE / "AssetProcessor.app"; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + if (AZ::IO::FixedMaxPath installedBinariesPath; + settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) + { + // Check for existence of one under a "bin" directory, i.e. engineRoot is an SDK structure. + assetProcessorPath = AZ::IO::FixedMaxPath{ engineRoot } / installedBinariesPath / "AssetProcessor.app/Contents/MacOS/AssetProcessor"; + } + } if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str())) { @@ -39,23 +47,21 @@ namespace AzFramework::AssetSystem::Platform } } - auto fullLaunchCommand = AZ::IO::FixedMaxPathString::format(R"(open -g "%s" --args --start-hidden)", assetProcessorPath.c_str()); + AZStd::string commandLineParams; // Add the engine path to the launch command if not empty if (!engineRoot.empty()) { - fullLaunchCommand += R"( --engine-path=")"; - fullLaunchCommand += engineRoot; - fullLaunchCommand += '"'; + commandLineParams += AZStd::string::format("\"--engine-path=\"%s\"\"", engineRoot.data()); } - // Add the active project path to the launch command if not empty if (!projectPath.empty()) { - fullLaunchCommand += R"( --project-path=")"; - fullLaunchCommand += projectPath; - fullLaunchCommand += '"'; + commandLineParams += AZStd::string::format(" \"--regset=/Amazon/AzCore/Bootstrap/project_path=\"%s\"\"", projectPath.data()); } - return system(fullLaunchCommand.c_str()) == 0; + AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; + processLaunchInfo.m_processExecutableString = AZStd::move(assetProcessorPath.Native()); + processLaunchInfo.m_commandlineParameters = commandLineParams; + return AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); } } diff --git a/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp b/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp index 0c362ac829..4dd7e184e9 100644 --- a/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp +++ b/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp @@ -49,6 +49,12 @@ int main(int argc, char* argv[]) AZStd::unique_ptr shellProcess(AzFramework::ProcessWatcher::LaunchProcess(shellProcessLaunch, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE)); shellProcess->WaitForProcessToExit(120); shellProcess.reset(); + + parameters = AZStd::string::format("-c \"%s/scripts/o3de.sh register --this-engine\"", enginePath.c_str()); + shellProcessLaunch.m_commandlineParameters = parameters; + shellProcess.reset(AzFramework::ProcessWatcher::LaunchProcess(shellProcessLaunch, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE)); + shellProcess->WaitForProcessToExit(120); + shellProcess.reset(); AZ::IO::FixedMaxPath projectManagerPath = installedBinariesFolder/"o3de.app"/"Contents"/"MacOS"/"o3de"; AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp index 0d66009d90..e901d807b4 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp @@ -10,6 +10,8 @@ #include #include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils @@ -94,5 +96,10 @@ namespace O3DE::ProjectManager QProcessEnvironment::systemEnvironment(), QObject::tr("Running get_python script...")); } + + AZ::IO::FixedMaxPath GetEditorDirectory() + { + return AZ::Utils::GetExecutableDirectory(); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp index e36f6cd0c8..b768200398 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp @@ -11,6 +11,9 @@ #include #include +#include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils @@ -104,5 +107,35 @@ namespace O3DE::ProjectManager QProcessEnvironment::systemEnvironment(), QObject::tr("Running get_python script...")); } + + AZ::IO::FixedMaxPath GetEditorDirectory() + { + AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); + AZ::IO::FixedMaxPath editorPath{ executableDirectory }; + editorPath /= "../../../Editor.app/Contents/MacOS"; + editorPath = editorPath.LexicallyNormal(); + if (!AZ::IO::SystemFile::IsDirectory(editorPath.c_str())) + { + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + if (AZ::IO::FixedMaxPath installedBinariesPath; + settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) + { + if (AZ::IO::FixedMaxPath engineRootFolder; + settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder)) + { + editorPath = engineRootFolder / installedBinariesPath / "Editor.app/Contents/MacOS"; + } + } + } + + if (!AZ::IO::SystemFile::IsDirectory(editorPath.c_str())) + { + AZ_Error("ProjectManager", false, "Unable to find the Editor app bundle!"); + } + } + + return editorPath; + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp index 831529d5e4..871f8e9567 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp @@ -14,6 +14,8 @@ #include #include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils @@ -139,5 +141,10 @@ namespace O3DE::ProjectManager QProcessEnvironment::systemEnvironment(), QObject::tr("Running get_python script...")); } + + AZ::IO::FixedMaxPath GetEditorDirectory() + { + return AZ::Utils::GetExecutableDirectory(); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.h b/Code/Tools/ProjectManager/Source/ProjectUtils.h index 1fdf76913e..890d50d2de 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.h +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.h @@ -14,6 +14,7 @@ #include #include +#include #include namespace O3DE::ProjectManager @@ -67,7 +68,8 @@ namespace O3DE::ProjectManager AZ::Outcome GetProjectBuildPath(const QString& projectPath); AZ::Outcome OpenCMakeGUI(const QString& projectPath); AZ::Outcome RunGetPythonScript(const QString& enginePath); - + + AZ::IO::FixedMaxPath GetEditorDirectory(); } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp index cf42da88fa..f86a689e59 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -392,11 +392,11 @@ namespace O3DE::ProjectManager { if (!WarnIfInBuildQueue(projectPath)) { - AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); + AZ::IO::FixedMaxPath executableDirectory = ProjectUtils::GetEditorDirectory(); AZStd::string executableFilename = "Editor"; AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION); auto cmdPath = AZ::IO::FixedMaxPathString::format( - "%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), + "%s --regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), projectPath.toStdString().c_str()); AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; From e43d60583ccc4b0f0fac1357df859cc73d081504 Mon Sep 17 00:00:00 2001 From: Yuriy Toporovskyy Date: Thu, 4 Nov 2021 14:43:05 -0400 Subject: [PATCH 062/177] Fix a bug where a rigid body with multiple shapes would ignore all shapes except the first Signed-off-by: Yuriy Toporovskyy --- Gems/PhysX/Code/Source/Scene/PhysXScene.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp index 22fc665eec..0ae09d704b 100644 --- a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp +++ b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp @@ -139,9 +139,8 @@ namespace PhysX else if (auto* shapeColliderPairList = AZStd::get_if>(&shapeData)) { bool shapeAdded = false; - if (!shapeColliderPairList->empty()) + for (const auto& shapeColliderConfigs : *shapeColliderPairList) { - const auto& shapeColliderConfigs = shapeColliderPairList->front(); auto shapePtr = AZStd::make_shared(*(shapeColliderConfigs.first), *(shapeColliderConfigs.second)); AZStd::visit([shapePtr, &shapeAdded](auto&& body) { @@ -151,8 +150,8 @@ namespace PhysX shapeAdded = true; } }, simulatedBody); - return shapeAdded; } + return shapeAdded; } else if (auto* shape = AZStd::get_if>(&shapeData)) { From add5b17053e772b4c8e7c379deecd7df61e748d5 Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Thu, 4 Nov 2021 12:56:52 -0700 Subject: [PATCH 063/177] Fix minor repeated AMAZON_LINUX in the code comment (#5312) Signed-off-by: Steve Pham <82231385+spham-amzn@users.noreply.github.com> --- Gems/AWSGameLift/cdk/aws_gamelift/fleet_configurations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/AWSGameLift/cdk/aws_gamelift/fleet_configurations.py b/Gems/AWSGameLift/cdk/aws_gamelift/fleet_configurations.py index 59f763498f..72c9d8ea43 100644 --- a/Gems/AWSGameLift/cdk/aws_gamelift/fleet_configurations.py +++ b/Gems/AWSGameLift/cdk/aws_gamelift/fleet_configurations.py @@ -44,7 +44,7 @@ FLEET_CONFIGURATIONS = [ 'build_path': '', # (Conditional) The operating system that the game server binaries are built to run on. # This parameter is required if the parameter build_path is defined. - # Choose from AMAZON_LINUX, AMAZON_LINUX or WINDOWS_2012. + # Choose from AMAZON_LINUX or WINDOWS_2012. 'operating_system': 'WINDOWS_2012' }, # (Optional) Information about the use of a TLS/SSL certificate for a fleet. From 4721ef829835e1d15e413c41cc410ccfa5200ed8 Mon Sep 17 00:00:00 2001 From: AMZN-nggieber <52797929+AMZN-nggieber@users.noreply.github.com> Date: Thu, 4 Nov 2021 13:04:06 -0700 Subject: [PATCH 064/177] License Info is Displayed as Clickable Link in Gem Catalog + Other Inspector Improvements (#5272) Signed-off-by: nggieber --- AutomatedTesting/Gem/PythonCoverage/gem.json | 1 + AutomatedTesting/Gem/gem.json | 5 +- .../Source/GemCatalog/GemInfo.h | 2 + .../Source/GemCatalog/GemInspector.cpp | 133 ++++++++++++------ .../Source/GemCatalog/GemInspector.h | 13 +- .../Source/GemCatalog/GemModel.cpp | 12 ++ .../Source/GemCatalog/GemModel.h | 6 +- .../Source/GemRepo/GemRepoInspector.cpp | 2 +- .../ProjectManager/Source/PythonBindings.cpp | 2 + Gems/AWSClientAuth/gem.json | 1 + Gems/AWSCore/gem.json | 1 + Gems/AWSGameLift/gem.json | 1 + Gems/AWSMetrics/gem.json | 1 + Gems/Achievements/gem.json | 1 + Gems/AssetMemoryAnalyzer/gem.json | 1 + Gems/AssetValidation/gem.json | 1 + Gems/Atom/Asset/ImageProcessingAtom/gem.json | 1 + Gems/Atom/Asset/Shader/gem.json | 1 + Gems/Atom/Bootstrap/gem.json | 1 + Gems/Atom/Component/DebugCamera/gem.json | 1 + Gems/Atom/Feature/Common/gem.json | 1 + Gems/Atom/RHI/DX12/gem.json | 1 + Gems/Atom/RHI/Metal/gem.json | 1 + Gems/Atom/RHI/Null/gem.json | 1 + Gems/Atom/RHI/Vulkan/gem.json | 1 + Gems/Atom/RHI/gem.json | 1 + Gems/Atom/RPI/gem.json | 1 + Gems/Atom/Tools/AtomToolsFramework/gem.json | 1 + Gems/Atom/Tools/MaterialEditor/gem.json | 1 + Gems/Atom/gem.json | 1 + Gems/AtomContent/ReferenceMaterials/gem.json | 13 +- Gems/AtomContent/Sponza/gem.json | 1 + Gems/AtomContent/gem.json | 1 + Gems/AtomLyIntegration/AtomBridge/gem.json | 1 + Gems/AtomLyIntegration/AtomFont/gem.json | 1 + .../AtomLyIntegration/AtomImGuiTools/gem.json | 1 + .../AtomViewportDisplayIcons/gem.json | 1 + .../AtomViewportDisplayInfo/gem.json | 1 + .../AtomLyIntegration/CommonFeatures/gem.json | 1 + Gems/AtomLyIntegration/EMotionFXAtom/gem.json | 1 + Gems/AtomLyIntegration/ImguiAtom/gem.json | 1 + .../DccScriptingInterface/gem.json | 1 + Gems/AtomLyIntegration/gem.json | 1 + Gems/AtomTressFX/gem.json | 11 +- Gems/AudioEngineWwise/gem.json | 1 + Gems/AudioSystem/gem.json | 1 + Gems/BarrierInput/gem.json | 1 + Gems/Blast/gem.json | 1 + Gems/Camera/gem.json | 1 + Gems/CameraFramework/gem.json | 1 + Gems/CertificateManager/gem.json | 1 + Gems/CrashReporting/gem.json | 1 + Gems/CustomAssetExample/gem.json | 1 + Gems/DebugDraw/gem.json | 1 + Gems/DevTextures/gem.json | 1 + Gems/EMotionFX/gem.json | 1 + Gems/EditorPythonBindings/gem.json | 1 + Gems/ExpressionEvaluation/gem.json | 1 + Gems/FastNoise/gem.json | 1 + Gems/GameState/gem.json | 1 + Gems/GameStateSamples/gem.json | 1 + Gems/Gestures/gem.json | 1 + Gems/GradientSignal/gem.json | 1 + Gems/GraphCanvas/gem.json | 1 + Gems/GraphModel/gem.json | 1 + Gems/HttpRequestor/gem.json | 1 + Gems/ImGui/gem.json | 1 + Gems/InAppPurchases/gem.json | 1 + Gems/LandscapeCanvas/gem.json | 1 + Gems/LmbrCentral/gem.json | 1 + Gems/LocalUser/gem.json | 1 + Gems/LyShine/gem.json | 1 + Gems/LyShineExamples/gem.json | 1 + Gems/Maestro/gem.json | 1 + Gems/MessagePopup/gem.json | 1 + Gems/Metastream/gem.json | 1 + Gems/Microphone/gem.json | 1 + Gems/Multiplayer/gem.json | 1 + Gems/MultiplayerCompression/gem.json | 1 + Gems/NvCloth/gem.json | 1 + Gems/PhysX/gem.json | 1 + Gems/PhysXDebug/gem.json | 1 + Gems/Prefab/PrefabBuilder/gem.json | 1 + Gems/Presence/gem.json | 1 + Gems/PrimitiveAssets/gem.json | 1 + Gems/Profiler/gem.json | 1 + Gems/PythonAssetBuilder/gem.json | 1 + Gems/QtForPython/gem.json | 1 + Gems/SaveData/gem.json | 1 + Gems/SceneLoggingExample/gem.json | 1 + Gems/SceneProcessing/gem.json | 1 + Gems/ScriptCanvas/gem.json | 1 + Gems/ScriptCanvasDeveloper/gem.json | 1 + Gems/ScriptCanvasPhysics/gem.json | 1 + Gems/ScriptCanvasTesting/gem.json | 1 + Gems/ScriptEvents/gem.json | 1 + Gems/ScriptedEntityTweener/gem.json | 1 + Gems/SliceFavorites/gem.json | 1 + Gems/StartingPointCamera/gem.json | 1 + Gems/StartingPointInput/gem.json | 1 + Gems/StartingPointMovement/gem.json | 1 + Gems/SurfaceData/gem.json | 1 + Gems/Terrain/gem.json | 1 + Gems/TestAssetBuilder/gem.json | 1 + Gems/TextureAtlas/gem.json | 1 + Gems/TickBusOrderViewer/gem.json | 1 + Gems/Twitch/gem.json | 1 + Gems/UiBasics/gem.json | 1 + Gems/Vegetation/gem.json | 1 + Gems/VideoPlaybackFramework/gem.json | 1 + Gems/VirtualGamepad/gem.json | 1 + Gems/WhiteBox/gem.json | 1 + Templates/AssetGem/Template/gem.json | 3 +- Templates/CppToolGem/Template/gem.json | 3 +- Templates/DefaultGem/Template/gem.json | 3 +- Templates/PythonToolGem/Template/gem.json | 3 +- scripts/o3de/o3de/gem_properties.py | 12 ++ .../o3de/tests/unit_test_gem_properties.py | 28 ++-- 118 files changed, 285 insertions(+), 68 deletions(-) diff --git a/AutomatedTesting/Gem/PythonCoverage/gem.json b/AutomatedTesting/Gem/PythonCoverage/gem.json index 39e327b5e3..b99ce0daad 100644 --- a/AutomatedTesting/Gem/PythonCoverage/gem.json +++ b/AutomatedTesting/Gem/PythonCoverage/gem.json @@ -2,6 +2,7 @@ "gem_name": "PythonCoverage", "display_name": "PythonCoverage", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "A tool for generating gem coverage for Python tests.", diff --git a/AutomatedTesting/Gem/gem.json b/AutomatedTesting/Gem/gem.json index 6c8c7829ce..df197df09d 100644 --- a/AutomatedTesting/Gem/gem.json +++ b/AutomatedTesting/Gem/gem.json @@ -2,10 +2,13 @@ "gem_name": "AutomatedTesting", "display_name": "AutomatedTesting", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "Project Gem for customizing the AutomatedTesting project functionality.", - "canonical_tags": ["Gem"], + "canonical_tags": [ + "Gem" + ], "user_tags": [], "icon_path": "preview.png", "requirements": "" diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h index 8c6d40505a..12cce5a4ea 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h @@ -81,6 +81,8 @@ namespace O3DE::ProjectManager DownloadStatus m_downloadStatus = UnknownDownloadStatus; QStringList m_features; QString m_requirement; + QString m_licenseText; + QString m_licenseLink; QString m_directoryLink; QString m_documentationLink; QString m_version = "Unknown Version"; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp index 3d9a8f6c86..b0b8cca29a 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp @@ -52,6 +52,22 @@ namespace O3DE::ProjectManager Update(selectedIndices[0]); } + void SetLabelElidedText(QLabel* label, QString text) + { + QFontMetrics nameFontMetrics(label->font()); + int labelWidth = label->width(); + + // Don't elide if the widgets are sized too small (sometimes occurs when loading gem catalog) + if (labelWidth > 100) + { + label->setText(nameFontMetrics.elidedText(text, Qt::ElideRight, labelWidth)); + } + else + { + label->setText(text); + } + } + void GemInspector::Update(const QModelIndex& modelIndex) { if (!modelIndex.isValid()) @@ -59,38 +75,52 @@ namespace O3DE::ProjectManager m_mainWidget->hide(); } - m_nameLabel->setText(m_model->GetDisplayName(modelIndex)); - m_creatorLabel->setText(m_model->GetCreator(modelIndex)); + SetLabelElidedText(m_nameLabel, m_model->GetDisplayName(modelIndex)); + SetLabelElidedText(m_creatorLabel, m_model->GetCreator(modelIndex)); m_summaryLabel->setText(m_model->GetSummary(modelIndex)); m_summaryLabel->adjustSize(); + m_licenseLinkLabel->setText(m_model->GetLicenseText(modelIndex)); + m_licenseLinkLabel->SetUrl(m_model->GetLicenseLink(modelIndex)); + m_directoryLinkLabel->SetUrl(m_model->GetDirectoryLink(modelIndex)); m_documentationLinkLabel->SetUrl(m_model->GetDocLink(modelIndex)); if (m_model->HasRequirement(modelIndex)) { - m_reqirementsIconLabel->show(); - m_reqirementsTitleLabel->show(); - m_reqirementsTextLabel->show(); + m_requirementsIconLabel->show(); + m_requirementsTitleLabel->show(); + m_requirementsTextLabel->show(); + m_requirementsMainSpacer->changeSize(0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed); - m_reqirementsTitleLabel->setText("Requirement"); - m_reqirementsTextLabel->setText(m_model->GetRequirement(modelIndex)); + m_requirementsTitleLabel->setText(tr("Requirement")); + m_requirementsTextLabel->setText(m_model->GetRequirement(modelIndex)); } else { - m_reqirementsIconLabel->hide(); - m_reqirementsTitleLabel->hide(); - m_reqirementsTextLabel->hide(); + m_requirementsIconLabel->hide(); + m_requirementsTitleLabel->hide(); + m_requirementsTextLabel->hide(); + m_requirementsMainSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); } // Depending gems - m_dependingGems->Update("Depending Gems", "The following Gems will be automatically enabled with this Gem.", m_model->GetDependingGemNames(modelIndex)); + QStringList dependingGems = m_model->GetDependingGemNames(modelIndex); + if (!dependingGems.isEmpty()) + { + m_dependingGems->Update(tr("Depending Gems"), tr("The following Gems will be automatically enabled with this Gem."), dependingGems); + m_dependingGems->show(); + } + else + { + m_dependingGems->hide(); + } // Additional information - m_versionLabel->setText(QString("Gem Version: %1").arg(m_model->GetVersion(modelIndex))); - m_lastUpdatedLabel->setText(QString("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex))); - m_binarySizeLabel->setText(QString("Binary Size: %1 KB").arg(m_model->GetBinarySizeInKB(modelIndex))); + m_versionLabel->setText(tr("Gem Version: %1").arg(m_model->GetVersion(modelIndex))); + m_lastUpdatedLabel->setText(tr("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex))); + m_binarySizeLabel->setText(tr("Binary Size: %1 KB").arg(m_model->GetBinarySizeInKB(modelIndex))); m_mainWidget->adjustSize(); m_mainWidget->show(); @@ -108,35 +138,51 @@ namespace O3DE::ProjectManager { // Gem name, creator and summary m_nameLabel = CreateStyledLabel(m_mainLayout, 18, s_headerColor); - m_creatorLabel = CreateStyledLabel(m_mainLayout, 12, s_headerColor); + m_creatorLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_headerColor); m_mainLayout->addSpacing(5); // TODO: QLabel seems to have issues determining the right sizeHint() for our font with the given font size. // This results into squeezed elements in the layout in case the text is a little longer than a sentence. - m_summaryLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); + m_summaryLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_headerColor); m_mainLayout->addWidget(m_summaryLabel); m_summaryLabel->setWordWrap(true); m_summaryLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); m_summaryLabel->setOpenExternalLinks(true); m_mainLayout->addSpacing(5); + // License + { + QHBoxLayout* licenseHLayout = new QHBoxLayout(); + licenseHLayout->setMargin(0); + licenseHLayout->setAlignment(Qt::AlignLeft); + m_mainLayout->addLayout(licenseHLayout); + + QLabel* licenseLabel = CreateStyledLabel(licenseHLayout, s_baseFontSize, s_headerColor); + licenseLabel->setText(tr("License: ")); + + m_licenseLinkLabel = new LinkLabel("", QUrl(), s_baseFontSize); + licenseHLayout->addWidget(m_licenseLinkLabel); + + licenseHLayout->addStretch(); + + m_mainLayout->addSpacing(5); + } + // Directory and documentation links { QHBoxLayout* linksHLayout = new QHBoxLayout(); linksHLayout->setMargin(0); m_mainLayout->addLayout(linksHLayout); - QSpacerItem* spacerLeft = new QSpacerItem(0, 0, QSizePolicy::Expanding); - linksHLayout->addSpacerItem(spacerLeft); + linksHLayout->addStretch(); - m_directoryLinkLabel = new LinkLabel("View in Directory"); + m_directoryLinkLabel = new LinkLabel(tr("View in Directory")); linksHLayout->addWidget(m_directoryLinkLabel); linksHLayout->addWidget(new QLabel("|")); - m_documentationLinkLabel = new LinkLabel("Read Documentation"); + m_documentationLinkLabel = new LinkLabel(tr("Read Documentation")); linksHLayout->addWidget(m_documentationLinkLabel); - QSpacerItem* spacerRight = new QSpacerItem(0, 0, QSizePolicy::Expanding); - linksHLayout->addSpacerItem(spacerRight); + linksHLayout->addStretch(); m_mainLayout->addSpacing(8); } @@ -144,34 +190,35 @@ namespace O3DE::ProjectManager // Separating line QFrame* hLine = new QFrame(); hLine->setFrameShape(QFrame::HLine); - hLine->setStyleSheet("color: #666666;"); + hLine->setObjectName("horizontalSeparatingLine"); m_mainLayout->addWidget(hLine); m_mainLayout->addSpacing(10); // Requirements - m_reqirementsTitleLabel = GemInspector::CreateStyledLabel(m_mainLayout, 16, s_headerColor); + m_requirementsTitleLabel = GemInspector::CreateStyledLabel(m_mainLayout, 16, s_headerColor); - QHBoxLayout* requrementsLayout = new QHBoxLayout(); - requrementsLayout->setAlignment(Qt::AlignTop); - requrementsLayout->setMargin(0); - requrementsLayout->setSpacing(0); + QHBoxLayout* requirementsLayout = new QHBoxLayout(); + requirementsLayout->setAlignment(Qt::AlignTop); + requirementsLayout->setMargin(0); + requirementsLayout->setSpacing(0); - m_reqirementsIconLabel = new QLabel(); - m_reqirementsIconLabel->setPixmap(QIcon(":/Warning.svg").pixmap(24, 24)); - requrementsLayout->addWidget(m_reqirementsIconLabel); + m_requirementsIconLabel = new QLabel(); + m_requirementsIconLabel->setPixmap(QIcon(":/Warning.svg").pixmap(24, 24)); + requirementsLayout->addWidget(m_requirementsIconLabel); - m_reqirementsTextLabel = GemInspector::CreateStyledLabel(requrementsLayout, 10, s_textColor); - m_reqirementsTextLabel->setWordWrap(true); - m_reqirementsTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); - m_reqirementsTextLabel->setOpenExternalLinks(true); + m_requirementsTextLabel = GemInspector::CreateStyledLabel(requirementsLayout, 10, s_textColor); + m_requirementsTextLabel->setWordWrap(true); + m_requirementsTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + m_requirementsTextLabel->setOpenExternalLinks(true); - QSpacerItem* reqirementsSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding); - requrementsLayout->addSpacerItem(reqirementsSpacer); + QSpacerItem* requirementsSpacer = new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding); + requirementsLayout->addSpacerItem(requirementsSpacer); - m_mainLayout->addLayout(requrementsLayout); + m_mainLayout->addLayout(requirementsLayout); - m_mainLayout->addSpacing(20); + m_requirementsMainSpacer = new QSpacerItem(0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed); + m_mainLayout->addSpacerItem(m_requirementsMainSpacer); // Depending gems m_dependingGems = new GemsSubWidget(); @@ -181,10 +228,10 @@ namespace O3DE::ProjectManager // Additional information QLabel* additionalInfoLabel = CreateStyledLabel(m_mainLayout, 14, s_headerColor); - additionalInfoLabel->setText("Additional Information"); + additionalInfoLabel->setText(tr("Additional Information")); - m_versionLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); - m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); - m_binarySizeLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); + m_versionLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_textColor); + m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_textColor); + m_binarySizeLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_textColor); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h index 38285577fd..c6548527ab 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h @@ -16,7 +16,7 @@ #include #include -#include +#include #endif QT_FORWARD_DECLARE_CLASS(QVBoxLayout) @@ -36,6 +36,9 @@ namespace O3DE::ProjectManager void Update(const QModelIndex& modelIndex); static QLabel* CreateStyledLabel(QLayout* layout, int fontSize, const QString& colorCodeString); + // Fonts + inline constexpr static int s_baseFontSize = 12; + // Colors inline constexpr static const char* s_headerColor = "#FFFFFF"; inline constexpr static const char* s_textColor = "#DDDDDD"; @@ -57,13 +60,15 @@ namespace O3DE::ProjectManager QLabel* m_nameLabel = nullptr; QLabel* m_creatorLabel = nullptr; QLabel* m_summaryLabel = nullptr; + LinkLabel* m_licenseLinkLabel = nullptr; LinkLabel* m_directoryLinkLabel = nullptr; LinkLabel* m_documentationLinkLabel = nullptr; // Requirements - QLabel* m_reqirementsTitleLabel = nullptr; - QLabel* m_reqirementsIconLabel = nullptr; - QLabel* m_reqirementsTextLabel = nullptr; + QLabel* m_requirementsTitleLabel = nullptr; + QLabel* m_requirementsIconLabel = nullptr; + QLabel* m_requirementsTextLabel = nullptr; + QSpacerItem* m_requirementsMainSpacer = nullptr; // Depending and conflicting gems GemsSubWidget* m_dependingGems = nullptr; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 0b9141486f..3f57aebf71 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -58,6 +58,8 @@ namespace O3DE::ProjectManager item->setData(gemInfo.m_path, RolePath); item->setData(gemInfo.m_requirement, RoleRequirement); item->setData(gemInfo.m_downloadStatus, RoleDownloadStatus); + item->setData(gemInfo.m_licenseText, RoleLicenseText); + item->setData(gemInfo.m_licenseLink, RoleLicenseLink); appendRow(item); @@ -248,6 +250,16 @@ namespace O3DE::ProjectManager return modelIndex.data(RoleRequirement).toString(); } + QString GemModel::GetLicenseText(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleLicenseText).toString(); + } + + QString GemModel::GetLicenseLink(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleLicenseLink).toString(); + } + GemModel* GemModel::GetSourceModel(QAbstractItemModel* model) { GemSortFilterProxyModel* proxyModel = qobject_cast(model); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index 35231cc105..56594ce794 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -50,6 +50,8 @@ namespace O3DE::ProjectManager static QStringList GetFeatures(const QModelIndex& modelIndex); static QString GetPath(const QModelIndex& modelIndex); static QString GetRequirement(const QModelIndex& modelIndex); + static QString GetLicenseText(const QModelIndex& modelIndex); + static QString GetLicenseLink(const QModelIndex& modelIndex); static GemModel* GetSourceModel(QAbstractItemModel* model); static const GemModel* GetSourceModel(const QAbstractItemModel* model); @@ -107,7 +109,9 @@ namespace O3DE::ProjectManager RoleTypes, RolePath, RoleRequirement, - RoleDownloadStatus + RoleDownloadStatus, + RoleLicenseText, + RoleLicenseLink }; QHash m_nameToIndexMap; diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp index d065ab59f8..6655aef86d 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp @@ -99,7 +99,7 @@ namespace O3DE::ProjectManager m_nameLabel->setObjectName("gemRepoInspectorNameLabel"); m_mainLayout->addWidget(m_nameLabel); - m_repoLinkLabel = new LinkLabel(tr("Repo Url"), QUrl(""), 12, this); + m_repoLinkLabel = new LinkLabel(tr("Repo Url"), QUrl(), 12, this); m_mainLayout->addWidget(m_repoLinkLabel); m_mainLayout->addSpacing(5); diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 740393fec0..905139a4f2 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -709,6 +709,8 @@ namespace O3DE::ProjectManager gemInfo.m_requirement = Py_To_String_Optional(data, "requirements", ""); gemInfo.m_creator = Py_To_String_Optional(data, "origin", ""); gemInfo.m_documentationLink = Py_To_String_Optional(data, "documentation_url", ""); + gemInfo.m_licenseText = Py_To_String_Optional(data, "license", "Unspecified License"); + gemInfo.m_licenseLink = Py_To_String_Optional(data, "license_url", ""); if (gemInfo.m_creator.contains("Open 3D Engine")) { diff --git a/Gems/AWSClientAuth/gem.json b/Gems/AWSClientAuth/gem.json index 75c07d025b..d03b86fb45 100644 --- a/Gems/AWSClientAuth/gem.json +++ b/Gems/AWSClientAuth/gem.json @@ -2,6 +2,7 @@ "gem_name": "AWSClientAuth", "display_name": "AWS Client Authorization", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "AWS Client Auth provides client authentication and AWS authorization solution.", diff --git a/Gems/AWSCore/gem.json b/Gems/AWSCore/gem.json index 1bb9da9192..9fb974ccf4 100644 --- a/Gems/AWSCore/gem.json +++ b/Gems/AWSCore/gem.json @@ -2,6 +2,7 @@ "gem_name": "AWSCore", "display_name": "AWS Core", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS Core Gem provides basic shared AWS functionality such as AWS SDK initialization and client configuration, and is automatically added when selecting any AWS feature Gem.", diff --git a/Gems/AWSGameLift/gem.json b/Gems/AWSGameLift/gem.json index a0d7f62cd1..3fe1f15c3d 100644 --- a/Gems/AWSGameLift/gem.json +++ b/Gems/AWSGameLift/gem.json @@ -2,6 +2,7 @@ "gem_name": "AWSGameLift", "display_name": "AWS GameLift", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS GameLift Gem provides a framework to extend O3DE networking layer to work with GameLift resources via GameLift server and client SDK.", diff --git a/Gems/AWSMetrics/gem.json b/Gems/AWSMetrics/gem.json index df16890012..59eae8f3c2 100644 --- a/Gems/AWSMetrics/gem.json +++ b/Gems/AWSMetrics/gem.json @@ -2,6 +2,7 @@ "gem_name": "AWSMetrics", "display_name": "AWS Metrics", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS Metrics Gem provides a solution for AWS metrics submission and analytics.", diff --git a/Gems/Achievements/gem.json b/Gems/Achievements/gem.json index bd643f471a..8180584500 100644 --- a/Gems/Achievements/gem.json +++ b/Gems/Achievements/gem.json @@ -2,6 +2,7 @@ "gem_name": "Achievements", "display_name": "Achievements", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Achievements Gem provides a target platform agnostic interface for retrieving achievement details and unlocking achievements.", diff --git a/Gems/AssetMemoryAnalyzer/gem.json b/Gems/AssetMemoryAnalyzer/gem.json index 902102fa27..443eeced18 100644 --- a/Gems/AssetMemoryAnalyzer/gem.json +++ b/Gems/AssetMemoryAnalyzer/gem.json @@ -2,6 +2,7 @@ "gem_name": "AssetMemoryAnalyzer", "display_name": "Asset Memory Analyzer", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Asset Memory Analyzer Gem provides tools to profile asset memory usage in Open 3D Engine through ImGUI (Immediate Mode Graphical User Interface).", diff --git a/Gems/AssetValidation/gem.json b/Gems/AssetValidation/gem.json index 1e57f60dc4..55cdffb9f3 100644 --- a/Gems/AssetValidation/gem.json +++ b/Gems/AssetValidation/gem.json @@ -2,6 +2,7 @@ "gem_name": "AssetValidation", "display_name": "Asset Validation", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Asset Validation Gem provides seed-related commands to ensure assets have valid seeds for asset bundling.", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/gem.json b/Gems/Atom/Asset/ImageProcessingAtom/gem.json index 1424841256..4fd437d298 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/gem.json +++ b/Gems/Atom/Asset/ImageProcessingAtom/gem.json @@ -2,6 +2,7 @@ "gem_name": "ImageProcessingAtom", "display_name": "Atom Image Processing", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/Asset/Shader/gem.json b/Gems/Atom/Asset/Shader/gem.json index 6d5e9f4dbe..9f59c65f78 100644 --- a/Gems/Atom/Asset/Shader/gem.json +++ b/Gems/Atom/Asset/Shader/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomShader", "display_name": "Atom Shader Builder", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/Bootstrap/gem.json b/Gems/Atom/Bootstrap/gem.json index 5e98a1887d..df615366da 100644 --- a/Gems/Atom/Bootstrap/gem.json +++ b/Gems/Atom/Bootstrap/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_Bootstrap", "display_name": "Atom Bootstrap", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/Component/DebugCamera/gem.json b/Gems/Atom/Component/DebugCamera/gem.json index 586cb37058..8eab74f41d 100644 --- a/Gems/Atom/Component/DebugCamera/gem.json +++ b/Gems/Atom/Component/DebugCamera/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_Component_DebugCamera", "display_name": "Atom Debug Camera Component", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/Feature/Common/gem.json b/Gems/Atom/Feature/Common/gem.json index 36a61fbbbb..84ab07a9f9 100644 --- a/Gems/Atom/Feature/Common/gem.json +++ b/Gems/Atom/Feature/Common/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_Feature_Common", "display_name": "Atom Feature Common", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/RHI/DX12/gem.json b/Gems/Atom/RHI/DX12/gem.json index eb876a1b9f..868acd43db 100644 --- a/Gems/Atom/RHI/DX12/gem.json +++ b/Gems/Atom/RHI/DX12/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_RHI_DX12", "display_name": "Atom RHI DX12", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/RHI/Metal/gem.json b/Gems/Atom/RHI/Metal/gem.json index 8da6bfabec..6e983ba505 100644 --- a/Gems/Atom/RHI/Metal/gem.json +++ b/Gems/Atom/RHI/Metal/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_RHI_Metal", "display_name": "Atom RHI Metal", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/RHI/Null/gem.json b/Gems/Atom/RHI/Null/gem.json index 0870efaae7..e9f22c5fcb 100644 --- a/Gems/Atom/RHI/Null/gem.json +++ b/Gems/Atom/RHI/Null/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_RHI_Null", "display_name": "Atom RHI Null", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/RHI/Vulkan/gem.json b/Gems/Atom/RHI/Vulkan/gem.json index 81e8dd22ea..1dfeb9eafa 100644 --- a/Gems/Atom/RHI/Vulkan/gem.json +++ b/Gems/Atom/RHI/Vulkan/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_RHI_Vulkan", "display_name": "Atom RHI Vulkan", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/RHI/gem.json b/Gems/Atom/RHI/gem.json index 5f916e5224..7b64476c65 100644 --- a/Gems/Atom/RHI/gem.json +++ b/Gems/Atom/RHI/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_RHI", "display_name": "Atom RHI", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/RPI/gem.json b/Gems/Atom/RPI/gem.json index b5a6fd5a1a..885f150508 100644 --- a/Gems/Atom/RPI/gem.json +++ b/Gems/Atom/RPI/gem.json @@ -3,6 +3,7 @@ "display_name": "Atom API", "summary": "", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "canonical_tags": [ diff --git a/Gems/Atom/Tools/AtomToolsFramework/gem.json b/Gems/Atom/Tools/AtomToolsFramework/gem.json index 2b0380bdae..de2a9e06f2 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/gem.json +++ b/Gems/Atom/Tools/AtomToolsFramework/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomToolsFramework", "display_name": "Atom Tools Framework", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/Tools/MaterialEditor/gem.json b/Gems/Atom/Tools/MaterialEditor/gem.json index 85ff434eab..807e3fa65f 100644 --- a/Gems/Atom/Tools/MaterialEditor/gem.json +++ b/Gems/Atom/Tools/MaterialEditor/gem.json @@ -2,6 +2,7 @@ "gem_name": "MaterialEditor", "display_name": "Atom Material Editor", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "Editor for creating, modifying, and previewing materials", diff --git a/Gems/Atom/gem.json b/Gems/Atom/gem.json index 1f5e4a37f3..0f409ad993 100644 --- a/Gems/Atom/gem.json +++ b/Gems/Atom/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom", "display_name": "Atom Renderer", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Atom Renderer Gem provides Atom Renderer and its associated tools (such as Material Editor), utilites, libraries, and interfaces.", diff --git a/Gems/AtomContent/ReferenceMaterials/gem.json b/Gems/AtomContent/ReferenceMaterials/gem.json index f697d50bc4..b75b01e1ae 100644 --- a/Gems/AtomContent/ReferenceMaterials/gem.json +++ b/Gems/AtomContent/ReferenceMaterials/gem.json @@ -5,8 +5,15 @@ "origin": "https://github.com/aws-lumberyard-dev/o3de.git", "type": "Asset", "summary": "Atom Asset Gem with a library of reference materials for StandardPBR (and others in the future)", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "PBR", "Materials"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "PBR", + "Materials" + ], "icon_path": "preview.png", - "dependencies": [] + "dependencies": [], + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt" } diff --git a/Gems/AtomContent/Sponza/gem.json b/Gems/AtomContent/Sponza/gem.json index 64de0e5da0..68749cd5f4 100644 --- a/Gems/AtomContent/Sponza/gem.json +++ b/Gems/AtomContent/Sponza/gem.json @@ -2,6 +2,7 @@ "gem_name": "Sponza", "display_name": "Sponza", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "A standard test scene for Global Illumination (forked from crytek sponza scene)", diff --git a/Gems/AtomContent/gem.json b/Gems/AtomContent/gem.json index 1bffe7d989..400e897a3b 100644 --- a/Gems/AtomContent/gem.json +++ b/Gems/AtomContent/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomContent", "display_name": "Atom Content", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Atom Content Gem provides assets for Atom Renderer and a modified version of the Pixar Look Development Studio.", diff --git a/Gems/AtomLyIntegration/AtomBridge/gem.json b/Gems/AtomLyIntegration/AtomBridge/gem.json index 1d48d8be61..e8ca413023 100644 --- a/Gems/AtomLyIntegration/AtomBridge/gem.json +++ b/Gems/AtomLyIntegration/AtomBridge/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_AtomBridge", "display_name": "Atom Bridge", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/AtomFont/gem.json b/Gems/AtomLyIntegration/AtomFont/gem.json index ed5b488de7..8907ec0979 100644 --- a/Gems/AtomLyIntegration/AtomFont/gem.json +++ b/Gems/AtomLyIntegration/AtomFont/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomFont", "display_name": "Atom Font", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/AtomImGuiTools/gem.json b/Gems/AtomLyIntegration/AtomImGuiTools/gem.json index 564eeedee2..e188ba0cb8 100644 --- a/Gems/AtomLyIntegration/AtomImGuiTools/gem.json +++ b/Gems/AtomLyIntegration/AtomImGuiTools/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomImGuiTools", "display_name": "Atom ImGui", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "", diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json index a2a7ba4b77..5a0763e0da 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomViewportDisplayIcons", "display_name": "Atom Viewport Display Icons", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json index be5cc96f95..639d93d301 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json +++ b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomViewportDisplayInfo", "display_name": "Atom Viewport Display Info", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/CommonFeatures/gem.json b/Gems/AtomLyIntegration/CommonFeatures/gem.json index 30738ad14c..6fa8938332 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/gem.json +++ b/Gems/AtomLyIntegration/CommonFeatures/gem.json @@ -2,6 +2,7 @@ "gem_name": "CommonFeaturesAtom", "display_name": "Common Features Atom", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/gem.json b/Gems/AtomLyIntegration/EMotionFXAtom/gem.json index f921990360..3514d4c1b9 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/gem.json +++ b/Gems/AtomLyIntegration/EMotionFXAtom/gem.json @@ -2,6 +2,7 @@ "gem_name": "EMotionFX_Atom", "display_name": "EMotionFX Atom", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/ImguiAtom/gem.json b/Gems/AtomLyIntegration/ImguiAtom/gem.json index 6b032275b9..fa611d8224 100644 --- a/Gems/AtomLyIntegration/ImguiAtom/gem.json +++ b/Gems/AtomLyIntegration/ImguiAtom/gem.json @@ -2,6 +2,7 @@ "gem_name": "ImguiAtom", "display_name": "Imgui Atom", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json index 1cee5c8298..4cc6fff169 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json @@ -3,6 +3,7 @@ "display_name": "Atom DccScriptingInterface (DCCsi)", "summary": "A python framework for working with various DCC tools and workflows.", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "canonical_tags": [ diff --git a/Gems/AtomLyIntegration/gem.json b/Gems/AtomLyIntegration/gem.json index 00b3a25f74..d9e526aee0 100644 --- a/Gems/AtomLyIntegration/gem.json +++ b/Gems/AtomLyIntegration/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomLyIntegration", "display_name": "Atom O3DE Integration", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Atom O3DE Integration Gem provides components, libraries, and functionality to support and integrate Atom Renderer in Open 3D Engine.", diff --git a/Gems/AtomTressFX/gem.json b/Gems/AtomTressFX/gem.json index d3e1294568..b7588ea374 100644 --- a/Gems/AtomTressFX/gem.json +++ b/Gems/AtomTressFX/gem.json @@ -2,11 +2,18 @@ "gem_name": "AtomTressFX", "display_name": "Atom TressFX", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "Atom TressFX Gem provides a cutting edge hair and fur simulation and rendering in Atom enhancing the AMD TressFX 4.1. The open source TressFX can be found here: https://github.com/GPUOpen-Effects/TressFX", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Physics", "Animation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Physics", + "Animation" + ], "requirements": "", "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/amd/atom-tressfx/" } diff --git a/Gems/AudioEngineWwise/gem.json b/Gems/AudioEngineWwise/gem.json index 0588af1908..2b0af30d40 100644 --- a/Gems/AudioEngineWwise/gem.json +++ b/Gems/AudioEngineWwise/gem.json @@ -2,6 +2,7 @@ "gem_name": "AudioEngineWwise", "display_name": "Wwise Audio Engine", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Wwise Audio Engine Gem provides support for Audiokinetic Wave Works Interactive Sound Engine (Wwise).", diff --git a/Gems/AudioSystem/gem.json b/Gems/AudioSystem/gem.json index 64d4d9af5a..ed028068a1 100644 --- a/Gems/AudioSystem/gem.json +++ b/Gems/AudioSystem/gem.json @@ -2,6 +2,7 @@ "gem_name": "AudioSystem", "display_name": "Audio System", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Audio System Gem provides the Audio Translation Layer (ATL) and Audio Controls Editor, which add support for audio in Open 3D Engine.", diff --git a/Gems/BarrierInput/gem.json b/Gems/BarrierInput/gem.json index 7fdc58e8b3..72d6398550 100644 --- a/Gems/BarrierInput/gem.json +++ b/Gems/BarrierInput/gem.json @@ -2,6 +2,7 @@ "gem_name": "BarrierInput", "display_name": "Barrier Input", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Barrier Input Gem allows the Open 3D Engine to function as a Barrier client so that it can receive input from a remote Barrier server.", diff --git a/Gems/Blast/gem.json b/Gems/Blast/gem.json index d6af47f482..761eb04761 100644 --- a/Gems/Blast/gem.json +++ b/Gems/Blast/gem.json @@ -2,6 +2,7 @@ "gem_name": "Blast", "display_name": "NVIDIA Blast", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The NVIDIA Blast Gem provides tools to author fractured mesh assets in Houdini, and functionality to create realistic destruction simulations in Open 3D Engine.", diff --git a/Gems/Camera/gem.json b/Gems/Camera/gem.json index 9f8ea22412..4cf0747c7b 100644 --- a/Gems/Camera/gem.json +++ b/Gems/Camera/gem.json @@ -2,6 +2,7 @@ "gem_name": "Camera", "display_name": "Camera", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Camera Gem provides a basic camera component that defines a frustum for runtime rendering.", diff --git a/Gems/CameraFramework/gem.json b/Gems/CameraFramework/gem.json index c5520fd5b4..d24014be61 100644 --- a/Gems/CameraFramework/gem.json +++ b/Gems/CameraFramework/gem.json @@ -2,6 +2,7 @@ "gem_name": "CameraFramework", "display_name": "Camera Framework", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Camera Framework Gem provides a base for implementing more complex camera systems.", diff --git a/Gems/CertificateManager/gem.json b/Gems/CertificateManager/gem.json index 968da2788a..11ea14ea5e 100644 --- a/Gems/CertificateManager/gem.json +++ b/Gems/CertificateManager/gem.json @@ -2,6 +2,7 @@ "gem_name": "CertificateManager", "display_name": "Certificate Manager", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Certificate Manager Gem provides access to authentication files for secure game connections from Amazon S3, files on disk, and other 3rd party sources.", diff --git a/Gems/CrashReporting/gem.json b/Gems/CrashReporting/gem.json index 9b8d3a9e0a..b8c75548a4 100644 --- a/Gems/CrashReporting/gem.json +++ b/Gems/CrashReporting/gem.json @@ -2,6 +2,7 @@ "gem_name": "CrashReporting", "display_name": "Crash Reporting", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Crash Reporting Gem provides support for external crash reporting for Open 3D Engine projects.", diff --git a/Gems/CustomAssetExample/gem.json b/Gems/CustomAssetExample/gem.json index ab5ed7002d..89492ee6ea 100644 --- a/Gems/CustomAssetExample/gem.json +++ b/Gems/CustomAssetExample/gem.json @@ -2,6 +2,7 @@ "gem_name": "CustomAssetExample", "display_name": "Custom Asset Example", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Custom Asset Example Gem provides example code for creating a custom asset for Open 3D Engine's asset pipeline.", diff --git a/Gems/DebugDraw/gem.json b/Gems/DebugDraw/gem.json index 7e0da07103..58eff5f87a 100644 --- a/Gems/DebugDraw/gem.json +++ b/Gems/DebugDraw/gem.json @@ -2,6 +2,7 @@ "gem_name": "DebugDraw", "display_name": "Debug Draw", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Debug Draw Gem provides Editor and runtime debug visualization features for Open 3D Engine.", diff --git a/Gems/DevTextures/gem.json b/Gems/DevTextures/gem.json index 8b40badbf1..00cafbd6fb 100644 --- a/Gems/DevTextures/gem.json +++ b/Gems/DevTextures/gem.json @@ -2,6 +2,7 @@ "gem_name": "DevTextures", "display_name": "Dev Textures", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Dev Textures Gem provides a collection of general purpose texture assets useful for prototypes and preproduction.", diff --git a/Gems/EMotionFX/gem.json b/Gems/EMotionFX/gem.json index f1734d854d..ed80517af1 100644 --- a/Gems/EMotionFX/gem.json +++ b/Gems/EMotionFX/gem.json @@ -2,6 +2,7 @@ "gem_name": "EMotionFX", "display_name": "EMotion FX Animation", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The EMotion FX Animation Gem provides Open 3D Engine's animation system for rigged actors and includes Animation Editor, a tool for creating animated behaviors, simulated objects, and colliders for rigged actors.", diff --git a/Gems/EditorPythonBindings/gem.json b/Gems/EditorPythonBindings/gem.json index 13c5800dd7..475483f13b 100644 --- a/Gems/EditorPythonBindings/gem.json +++ b/Gems/EditorPythonBindings/gem.json @@ -2,6 +2,7 @@ "gem_name": "EditorPythonBindings", "display_name": "Editor Python Bindings", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Editor Python Bindings Gem provides Python commands for Open 3D Engine Editor functions.", diff --git a/Gems/ExpressionEvaluation/gem.json b/Gems/ExpressionEvaluation/gem.json index 6bcc666a4d..cd712f4da9 100644 --- a/Gems/ExpressionEvaluation/gem.json +++ b/Gems/ExpressionEvaluation/gem.json @@ -2,6 +2,7 @@ "gem_name": "ExpressionEvaluation", "display_name": "Expression Evaluation", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Expression Evaluation Gem provides a method for parsing and executing string expressions in Open 3D Engine.", diff --git a/Gems/FastNoise/gem.json b/Gems/FastNoise/gem.json index ac59fe804e..d8dfb878b4 100644 --- a/Gems/FastNoise/gem.json +++ b/Gems/FastNoise/gem.json @@ -2,6 +2,7 @@ "gem_name": "FastNoise", "display_name": "Fast Noise", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The FastNoise Gradient Gem uses the third-party, open source FastNoise library to provide a variety of high-performance noise generation algorithms.", diff --git a/Gems/GameState/gem.json b/Gems/GameState/gem.json index 7bb3cf4214..fe3a338997 100644 --- a/Gems/GameState/gem.json +++ b/Gems/GameState/gem.json @@ -2,6 +2,7 @@ "gem_name": "GameState", "display_name": "Game State", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Game State Gem provides a generic framework to determine and manage game states and game state transitions in Open 3D Engine.", diff --git a/Gems/GameStateSamples/gem.json b/Gems/GameStateSamples/gem.json index be982b9c5b..80018ff1a8 100644 --- a/Gems/GameStateSamples/gem.json +++ b/Gems/GameStateSamples/gem.json @@ -2,6 +2,7 @@ "gem_name": "GameStateSamples", "display_name": "Game State Samples", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Game State Samples Gem provides a set of sample game states (built on top of the Game State Gem), including primary user selection, main menu, level loading, level running, and level paused.", diff --git a/Gems/Gestures/gem.json b/Gems/Gestures/gem.json index fcc56b4704..8efd389d53 100644 --- a/Gems/Gestures/gem.json +++ b/Gems/Gestures/gem.json @@ -2,6 +2,7 @@ "gem_name": "Gestures", "display_name": "Gestures", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Gestures Gem provides detection for common gesture-based input actions on iOS and Android devices.", diff --git a/Gems/GradientSignal/gem.json b/Gems/GradientSignal/gem.json index e87ccfe13a..aac4c652c5 100644 --- a/Gems/GradientSignal/gem.json +++ b/Gems/GradientSignal/gem.json @@ -2,6 +2,7 @@ "gem_name": "GradientSignal", "display_name": "Gradient Signal", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Gradient Signal Gem provides a number of components for generating, modifying, and mixing gradient signals.", diff --git a/Gems/GraphCanvas/gem.json b/Gems/GraphCanvas/gem.json index 760bd157df..4762cfef35 100644 --- a/Gems/GraphCanvas/gem.json +++ b/Gems/GraphCanvas/gem.json @@ -2,6 +2,7 @@ "gem_name": "GraphCanvas", "display_name": "Graph Canvas", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Graph Canvas Gem provides a C++ framework for creating custom graphical node based editors for Open 3D Engine.", diff --git a/Gems/GraphModel/gem.json b/Gems/GraphModel/gem.json index 256de75f6c..ad6b592430 100644 --- a/Gems/GraphModel/gem.json +++ b/Gems/GraphModel/gem.json @@ -2,6 +2,7 @@ "gem_name": "GraphModel", "display_name": "Graph Model", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Graph Model Gem provides a generic node graph data model framework for Open 3D Engine.", diff --git a/Gems/HttpRequestor/gem.json b/Gems/HttpRequestor/gem.json index eb1a112b0e..582bfa0071 100644 --- a/Gems/HttpRequestor/gem.json +++ b/Gems/HttpRequestor/gem.json @@ -2,6 +2,7 @@ "gem_name": "HttpRequestor", "display_name": "HTTP Requestor", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The HTTP Requestor Gem provides functionality to make asynchronous HTTP/HTTPS requests and return data through a user-provided call back function.", diff --git a/Gems/ImGui/gem.json b/Gems/ImGui/gem.json index c1d89d1728..dfc12f64b4 100644 --- a/Gems/ImGui/gem.json +++ b/Gems/ImGui/gem.json @@ -2,6 +2,7 @@ "gem_name": "ImGui", "display_name": "Immediate Mode GUI (IMGUI)", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Immediate Mode GUI Gem provides the 3rdParty library IMGUI which can be used to create run time immediate mode overlays for debugging and profiling information in Open 3D Engine.", diff --git a/Gems/InAppPurchases/gem.json b/Gems/InAppPurchases/gem.json index 1f1debd5fb..21febbfeca 100644 --- a/Gems/InAppPurchases/gem.json +++ b/Gems/InAppPurchases/gem.json @@ -2,6 +2,7 @@ "gem_name": "InAppPurchases", "display_name": "In-App Purchases", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The In-App Purchases Gem provides functionality for in app purchases for iOS and Android.", diff --git a/Gems/LandscapeCanvas/gem.json b/Gems/LandscapeCanvas/gem.json index ce0c64b75d..8653da40e1 100644 --- a/Gems/LandscapeCanvas/gem.json +++ b/Gems/LandscapeCanvas/gem.json @@ -2,6 +2,7 @@ "gem_name": "LandscapeCanvas", "display_name": "Landscape Canvas", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Landscape Canvas Gem provides the Landscape Canvas editor, a node-based graph tool for authoring workflows to populate landscape with dynamic vegetation.", diff --git a/Gems/LmbrCentral/gem.json b/Gems/LmbrCentral/gem.json index 9fca421538..a0a6ea813f 100644 --- a/Gems/LmbrCentral/gem.json +++ b/Gems/LmbrCentral/gem.json @@ -2,6 +2,7 @@ "gem_name": "LmbrCentral", "display_name": "O3DE Core (LmbrCentral)", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The O3DE Core (LmbrCentral) Gem provides required code and assets for running Open 3D Engine Editor.", diff --git a/Gems/LocalUser/gem.json b/Gems/LocalUser/gem.json index f86e6e1bf6..5199b4f777 100644 --- a/Gems/LocalUser/gem.json +++ b/Gems/LocalUser/gem.json @@ -2,6 +2,7 @@ "gem_name": "LocalUser", "display_name": "Local User", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Local User Gem provides functionality for mapping local user ids to local player slots and managing local user profiles.", diff --git a/Gems/LyShine/gem.json b/Gems/LyShine/gem.json index e2da8afa4d..f3c3fc2c67 100644 --- a/Gems/LyShine/gem.json +++ b/Gems/LyShine/gem.json @@ -2,6 +2,7 @@ "gem_name": "LyShine", "display_name": "LyShine", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The LyShine Gem provides the runtime UI system and creation tools for Open 3D Engine projects.", diff --git a/Gems/LyShineExamples/gem.json b/Gems/LyShineExamples/gem.json index 122273f6d9..74d2b6fe51 100644 --- a/Gems/LyShineExamples/gem.json +++ b/Gems/LyShineExamples/gem.json @@ -2,6 +2,7 @@ "gem_name": "LyShineExamples", "display_name": "LyShine Examples", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The LyShine Examples Gem provides example code and assets for LyShine, the runtime UI system and editor for Open 3D Engine projects.", diff --git a/Gems/Maestro/gem.json b/Gems/Maestro/gem.json index 5149df7c14..d8f884e271 100644 --- a/Gems/Maestro/gem.json +++ b/Gems/Maestro/gem.json @@ -2,6 +2,7 @@ "gem_name": "Maestro", "display_name": "Maestro Cinematics", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Maestro Cinematics Gem provides Track View, Open 3D Engine's animated sequence and cinematics editor.", diff --git a/Gems/MessagePopup/gem.json b/Gems/MessagePopup/gem.json index 05d36bc2df..fb44dd93a5 100644 --- a/Gems/MessagePopup/gem.json +++ b/Gems/MessagePopup/gem.json @@ -2,6 +2,7 @@ "gem_name": "MessagePopup", "display_name": "Message Popup", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Message Popup Gem provides an example implementation of popup messages using LyShine in Open 3D Engine.", diff --git a/Gems/Metastream/gem.json b/Gems/Metastream/gem.json index 862b17dd1d..309f16b221 100644 --- a/Gems/Metastream/gem.json +++ b/Gems/Metastream/gem.json @@ -2,6 +2,7 @@ "gem_name": "Metastream", "display_name": "Metastream", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Metastream Gem provides functionality for an HTTP server that allows broadcasters to customize game streams with overlays of statistics and event data from a game session.", diff --git a/Gems/Microphone/gem.json b/Gems/Microphone/gem.json index 68492ea786..6e57bec854 100644 --- a/Gems/Microphone/gem.json +++ b/Gems/Microphone/gem.json @@ -2,6 +2,7 @@ "gem_name": "Microphone", "display_name": "Microphone", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Microphone Gem provides support for audio input through microphones.", diff --git a/Gems/Multiplayer/gem.json b/Gems/Multiplayer/gem.json index 47dbddfcbd..f895c78c5d 100644 --- a/Gems/Multiplayer/gem.json +++ b/Gems/Multiplayer/gem.json @@ -2,6 +2,7 @@ "gem_name": "Multiplayer", "display_name": "Multiplayer", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Multiplayer Gem provides a public API for multiplayer functionality such as connecting and hosting.", diff --git a/Gems/MultiplayerCompression/gem.json b/Gems/MultiplayerCompression/gem.json index 7dd31476e3..98156cc404 100644 --- a/Gems/MultiplayerCompression/gem.json +++ b/Gems/MultiplayerCompression/gem.json @@ -2,6 +2,7 @@ "gem_name": "MultiplayerCompression", "display_name": "Multiplayer Compression", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Multiplayer Compression Gem provides an open source Compressor for use with AzNetworking's transport layer.", diff --git a/Gems/NvCloth/gem.json b/Gems/NvCloth/gem.json index 019ce23742..86a70b9373 100644 --- a/Gems/NvCloth/gem.json +++ b/Gems/NvCloth/gem.json @@ -3,6 +3,7 @@ "display_name": "NVIDIA Cloth (NvCloth)", "license": "Apache-2.0 Or MIT", "origin": "Open 3D Engine - o3de.org", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "type": "Code", "summary": "The NVIDIA Cloth Gem provides functionality to create fast, realistic cloth simulation with the NVIDIA Cloth library.", "canonical_tags": [ diff --git a/Gems/PhysX/gem.json b/Gems/PhysX/gem.json index bacbcf2dee..990d7502d8 100644 --- a/Gems/PhysX/gem.json +++ b/Gems/PhysX/gem.json @@ -2,6 +2,7 @@ "gem_name": "PhysX", "display_name": "PhysX", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The PhysX Gem provides physics simulation with NVIDIA PhysX including static and dynamic rigid body simulation, force regions, ragdolls, and dynamic PhysX joints.", diff --git a/Gems/PhysXDebug/gem.json b/Gems/PhysXDebug/gem.json index ece0774210..2d9f4dc24d 100644 --- a/Gems/PhysXDebug/gem.json +++ b/Gems/PhysXDebug/gem.json @@ -2,6 +2,7 @@ "gem_name": "PhysXDebug", "display_name": "PhysX Debug", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The PhysX Debug Gem provides debugging functionality and visualizations for NVIDIA PhysX in Open 3D Engine.", diff --git a/Gems/Prefab/PrefabBuilder/gem.json b/Gems/Prefab/PrefabBuilder/gem.json index ba78f96358..2233a7235e 100644 --- a/Gems/Prefab/PrefabBuilder/gem.json +++ b/Gems/Prefab/PrefabBuilder/gem.json @@ -2,6 +2,7 @@ "gem_name": "PrefabBuilder", "display_name": "Prefab Builder", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Prefab Builder Gem provides an Asset Processor module for prefabs, which are complex assets built by combining smaller entities.", diff --git a/Gems/Presence/gem.json b/Gems/Presence/gem.json index a70953ae4e..624af2e62d 100644 --- a/Gems/Presence/gem.json +++ b/Gems/Presence/gem.json @@ -2,6 +2,7 @@ "gem_name": "Presence", "display_name": "Presence", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Presence Gem provides a target platform agnostic interface for Presence services.", diff --git a/Gems/PrimitiveAssets/gem.json b/Gems/PrimitiveAssets/gem.json index 4ad3cb62ad..0e4c4689dc 100644 --- a/Gems/PrimitiveAssets/gem.json +++ b/Gems/PrimitiveAssets/gem.json @@ -2,6 +2,7 @@ "gem_name": "PrimitiveAssets", "display_name": "Primitive Assets", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Primitive Assets Gem provides primitive shape mesh assets with physics enabled.", diff --git a/Gems/Profiler/gem.json b/Gems/Profiler/gem.json index 2f121d7618..b160bc4b3c 100644 --- a/Gems/Profiler/gem.json +++ b/Gems/Profiler/gem.json @@ -2,6 +2,7 @@ "gem_name": "Profiler", "display_name": "Profiler", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "A collection of utilities for capturing performance data", diff --git a/Gems/PythonAssetBuilder/gem.json b/Gems/PythonAssetBuilder/gem.json index ce30ba9e82..8046104f03 100644 --- a/Gems/PythonAssetBuilder/gem.json +++ b/Gems/PythonAssetBuilder/gem.json @@ -2,6 +2,7 @@ "gem_name": "PythonAssetBuilder", "display_name": "Python Asset Builder", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Python Asset Builder Gem provides functionality to implement custom asset builders in Python for Asset Processor.", diff --git a/Gems/QtForPython/gem.json b/Gems/QtForPython/gem.json index f83be43342..17d3a26f21 100644 --- a/Gems/QtForPython/gem.json +++ b/Gems/QtForPython/gem.json @@ -2,6 +2,7 @@ "gem_name": "QtForPython", "display_name": "Qt for Python", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Qt for Python Gem provides the PySide2 Python libraries to manage Qt widgets.", diff --git a/Gems/SaveData/gem.json b/Gems/SaveData/gem.json index 333b4682d2..1ee5a54cec 100644 --- a/Gems/SaveData/gem.json +++ b/Gems/SaveData/gem.json @@ -2,6 +2,7 @@ "gem_name": "SaveData", "display_name": "Save Data", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Save Data Gem provides a platform independent API to save and load persistent user data in Open 3D Engine projects.", diff --git a/Gems/SceneLoggingExample/gem.json b/Gems/SceneLoggingExample/gem.json index 16961b9c5b..ad950f3992 100644 --- a/Gems/SceneLoggingExample/gem.json +++ b/Gems/SceneLoggingExample/gem.json @@ -2,6 +2,7 @@ "gem_name": "SceneLoggingExample", "display_name": "Scene Logging Example", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Scene Logging Example Gem demonstrates the basics of extending the Open 3D Engine Scene API by adding additional logging to the pipeline.", diff --git a/Gems/SceneProcessing/gem.json b/Gems/SceneProcessing/gem.json index de1dfeb23f..576460d0a9 100644 --- a/Gems/SceneProcessing/gem.json +++ b/Gems/SceneProcessing/gem.json @@ -2,6 +2,7 @@ "gem_name": "SceneProcessing", "display_name": "Scene Processing", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Scene Processing Gem provides Scene Settings, a tool you can use to specify the default settings for processing asset files for actors, meshes, motions, and PhysX.", diff --git a/Gems/ScriptCanvas/gem.json b/Gems/ScriptCanvas/gem.json index 621ea42961..fc8a504917 100644 --- a/Gems/ScriptCanvas/gem.json +++ b/Gems/ScriptCanvas/gem.json @@ -2,6 +2,7 @@ "gem_name": "ScriptCanvas", "display_name": "Script Canvas", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Gem provides Open 3D Engine's visual scripting environment, Script Canvas.", diff --git a/Gems/ScriptCanvasDeveloper/gem.json b/Gems/ScriptCanvasDeveloper/gem.json index ee1bfcec84..51aed9d9fa 100644 --- a/Gems/ScriptCanvasDeveloper/gem.json +++ b/Gems/ScriptCanvasDeveloper/gem.json @@ -2,6 +2,7 @@ "gem_name": "ScriptCanvasDeveloperGem", "display_name": "Script Canvas Developer", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Developer Gem provides a suite of utility features for the development and debugging of Script Canvas systems.", diff --git a/Gems/ScriptCanvasPhysics/gem.json b/Gems/ScriptCanvasPhysics/gem.json index 417fa6893a..2e35e85c46 100644 --- a/Gems/ScriptCanvasPhysics/gem.json +++ b/Gems/ScriptCanvasPhysics/gem.json @@ -2,6 +2,7 @@ "gem_name": "ScriptCanvasPhysics", "display_name": "Script Canvas Physics", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Script Canvas Physics Gem provides Script Canvas nodes for physics scene queries such as raycasts.", diff --git a/Gems/ScriptCanvasTesting/gem.json b/Gems/ScriptCanvasTesting/gem.json index c45d2ac165..5cb718e674 100644 --- a/Gems/ScriptCanvasTesting/gem.json +++ b/Gems/ScriptCanvasTesting/gem.json @@ -2,6 +2,7 @@ "gem_name": "ScriptCanvasTesting", "display_name": "Script Canvas Testing", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Testing Gem provides a framework for testing for and with Script Canvas.", diff --git a/Gems/ScriptEvents/gem.json b/Gems/ScriptEvents/gem.json index 386d9b5614..5640e08489 100644 --- a/Gems/ScriptEvents/gem.json +++ b/Gems/ScriptEvents/gem.json @@ -2,6 +2,7 @@ "gem_name": "ScriptEvents", "display_name": "Script Events", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Script Events Gem provides a framework for creating event assets usable from any scripting solution in Open 3D Engine.", diff --git a/Gems/ScriptedEntityTweener/gem.json b/Gems/ScriptedEntityTweener/gem.json index c51f05df9a..477345093c 100644 --- a/Gems/ScriptedEntityTweener/gem.json +++ b/Gems/ScriptedEntityTweener/gem.json @@ -2,6 +2,7 @@ "gem_name": "ScriptedEntityTweener", "display_name": "Scripted Entity Tweener", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Scripted Entity Tweener Gem provides a script driven animation system for Open 3D Engine projects.", diff --git a/Gems/SliceFavorites/gem.json b/Gems/SliceFavorites/gem.json index 84f42fc1a2..c4536dc042 100644 --- a/Gems/SliceFavorites/gem.json +++ b/Gems/SliceFavorites/gem.json @@ -2,6 +2,7 @@ "gem_name": "SliceFavorites", "display_name": "SliceFavorites", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "Add the ability to favorite a slice to allow easy access and instantiation", diff --git a/Gems/StartingPointCamera/gem.json b/Gems/StartingPointCamera/gem.json index 613eb76267..1033770022 100644 --- a/Gems/StartingPointCamera/gem.json +++ b/Gems/StartingPointCamera/gem.json @@ -2,6 +2,7 @@ "gem_name": "StartingPointCamera", "display_name": "Starting Point Camera", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Camera Gem provides the behaviors used with the Camera Framework Gem to define a camera rig.", diff --git a/Gems/StartingPointInput/gem.json b/Gems/StartingPointInput/gem.json index d2641ea27b..ee1655b794 100644 --- a/Gems/StartingPointInput/gem.json +++ b/Gems/StartingPointInput/gem.json @@ -2,6 +2,7 @@ "gem_name": "StartingPointInput", "display_name": "Starting Point Input", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Input Gem provides functionality to map low-level input events to high-level actions.", diff --git a/Gems/StartingPointMovement/gem.json b/Gems/StartingPointMovement/gem.json index 7def6da768..188d8483bc 100644 --- a/Gems/StartingPointMovement/gem.json +++ b/Gems/StartingPointMovement/gem.json @@ -2,6 +2,7 @@ "gem_name": "StartingPointMovement", "display_name": "Starting Point Movement", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Movement Gem provides a series of Lua scripts that listen and respond to input events and trigger transform operations such as translation and rotation.", diff --git a/Gems/SurfaceData/gem.json b/Gems/SurfaceData/gem.json index 51a134d5df..d16254040f 100644 --- a/Gems/SurfaceData/gem.json +++ b/Gems/SurfaceData/gem.json @@ -2,6 +2,7 @@ "gem_name": "SurfaceData", "display_name": "Surface Data", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Surface Data Gem provides functionality to emit signals or tags from surfaces such as meshes and terrain.", diff --git a/Gems/Terrain/gem.json b/Gems/Terrain/gem.json index cd72a91708..0ca470c4d3 100644 --- a/Gems/Terrain/gem.json +++ b/Gems/Terrain/gem.json @@ -2,6 +2,7 @@ "gem_name": "Terrain", "display_name": "Terrain", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "summary": "The Terrain Gem is an experimental terrain system. The terrain system maps height, color, and surface data to regions of the world, provides gradient-based and shape-based authoring tools and workflows, includes specialized rendering for efficient display, and integrates with physics for physical simulation.", "canonical_tags": [ diff --git a/Gems/TestAssetBuilder/gem.json b/Gems/TestAssetBuilder/gem.json index ba7568005f..68ed706499 100644 --- a/Gems/TestAssetBuilder/gem.json +++ b/Gems/TestAssetBuilder/gem.json @@ -2,6 +2,7 @@ "gem_name": "TestAssetBuilder", "display_name": "Test Asset Builder", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Test Asset Builder Gem is used to feature test Asset Processor.", diff --git a/Gems/TextureAtlas/gem.json b/Gems/TextureAtlas/gem.json index 345e085359..142f0fb082 100644 --- a/Gems/TextureAtlas/gem.json +++ b/Gems/TextureAtlas/gem.json @@ -2,6 +2,7 @@ "gem_name": "TextureAtlas", "display_name": "Texture Atlas", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Texture Atlas Gem provides the formatting for texture atlases from 2D textures for LyShine.", diff --git a/Gems/TickBusOrderViewer/gem.json b/Gems/TickBusOrderViewer/gem.json index dc5cb6f66c..a6a6fd23ac 100644 --- a/Gems/TickBusOrderViewer/gem.json +++ b/Gems/TickBusOrderViewer/gem.json @@ -2,6 +2,7 @@ "gem_name": "TickBusOrderViewer", "display_name": "Tick Bus Order Viewer", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Tick Bus Order Viewer Gem provides a console variable that displays the order of runtime tick events.", diff --git a/Gems/Twitch/gem.json b/Gems/Twitch/gem.json index f45433bc3f..42ec1b971e 100644 --- a/Gems/Twitch/gem.json +++ b/Gems/Twitch/gem.json @@ -2,6 +2,7 @@ "gem_name": "Twitch", "display_name": "Twitch", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Twitch Gem provides access to the Twitch API v5 SDK including social functions, channels, and other APIs.", diff --git a/Gems/UiBasics/gem.json b/Gems/UiBasics/gem.json index 9a1e16a462..bb2416c235 100644 --- a/Gems/UiBasics/gem.json +++ b/Gems/UiBasics/gem.json @@ -2,6 +2,7 @@ "gem_name": "UiBasics", "display_name": "UI Basics", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The UI Basics Gem provides a collection of basic UI prefabs such as image, text, and button, that can be used with LyShine, the Open 3D Engine runtime User Interface system and editor.", diff --git a/Gems/Vegetation/gem.json b/Gems/Vegetation/gem.json index 9dbcc3450b..75416933f0 100644 --- a/Gems/Vegetation/gem.json +++ b/Gems/Vegetation/gem.json @@ -2,6 +2,7 @@ "gem_name": "Vegetation", "display_name": "Vegetation", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Vegetation Gem provides tools to place natural-looking vegetation in Open 3D Engine.", diff --git a/Gems/VideoPlaybackFramework/gem.json b/Gems/VideoPlaybackFramework/gem.json index 9f491f47cb..381738ab4b 100644 --- a/Gems/VideoPlaybackFramework/gem.json +++ b/Gems/VideoPlaybackFramework/gem.json @@ -2,6 +2,7 @@ "gem_name": "VideoPlaybackFramework", "display_name": "Video Playback Framework", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Video Playback Framework Gem provides the interface to play back video.", diff --git a/Gems/VirtualGamepad/gem.json b/Gems/VirtualGamepad/gem.json index c6305f1754..637776ac85 100644 --- a/Gems/VirtualGamepad/gem.json +++ b/Gems/VirtualGamepad/gem.json @@ -2,6 +2,7 @@ "gem_name": "VirtualGamepad", "display_name": "Virtual Gamepad", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Virtual Gamepad Gem provides controls that emulate a gamepad on touch screen devices.", diff --git a/Gems/WhiteBox/gem.json b/Gems/WhiteBox/gem.json index 81e0ad5f88..41865efc56 100644 --- a/Gems/WhiteBox/gem.json +++ b/Gems/WhiteBox/gem.json @@ -2,6 +2,7 @@ "gem_name": "WhiteBox", "display_name": "White Box", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The White Box Gem provides White Box rapid design components for Open 3D Engine.", diff --git a/Templates/AssetGem/Template/gem.json b/Templates/AssetGem/Template/gem.json index 2a688857f2..2a02362256 100644 --- a/Templates/AssetGem/Template/gem.json +++ b/Templates/AssetGem/Template/gem.json @@ -1,7 +1,8 @@ { "gem_name": "${Name}", "display_name": "${Name}", - "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "license": "What license ${Name} uses goes here: i.e. Apache-2.0 Or MIT", + "license_url": "", "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", "type": "Asset", "summary": "A short description of ${Name}.", diff --git a/Templates/CppToolGem/Template/gem.json b/Templates/CppToolGem/Template/gem.json index 518d831e0f..079b7152ff 100644 --- a/Templates/CppToolGem/Template/gem.json +++ b/Templates/CppToolGem/Template/gem.json @@ -1,7 +1,8 @@ { "gem_name": "${Name}", "display_name": "${Name}", - "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "license": "What license ${Name} uses goes here: i.e. Apache-2.0 Or MIT", + "license_url": "", "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", "type": "Code", "summary": "A short description of ${Name}.", diff --git a/Templates/DefaultGem/Template/gem.json b/Templates/DefaultGem/Template/gem.json index 353ad6bf8d..d4ff637bee 100644 --- a/Templates/DefaultGem/Template/gem.json +++ b/Templates/DefaultGem/Template/gem.json @@ -1,7 +1,8 @@ { "gem_name": "${Name}", "display_name": "${Name}", - "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "license": "What license ${Name} uses goes here: i.e. Apache-2.0 Or MIT", + "license_url": "", "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", "type": "Code", "summary": "A short description of ${Name}.", diff --git a/Templates/PythonToolGem/Template/gem.json b/Templates/PythonToolGem/Template/gem.json index 353ad6bf8d..d4ff637bee 100644 --- a/Templates/PythonToolGem/Template/gem.json +++ b/Templates/PythonToolGem/Template/gem.json @@ -1,7 +1,8 @@ { "gem_name": "${Name}", "display_name": "${Name}", - "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "license": "What license ${Name} uses goes here: i.e. Apache-2.0 Or MIT", + "license_url": "", "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", "type": "Code", "summary": "A short description of ${Name}.", diff --git a/scripts/o3de/o3de/gem_properties.py b/scripts/o3de/o3de/gem_properties.py index 39011a213a..faaa4f86ec 100644 --- a/scripts/o3de/o3de/gem_properties.py +++ b/scripts/o3de/o3de/gem_properties.py @@ -54,6 +54,8 @@ def edit_gem_props(gem_path: pathlib.Path = None, new_icon: str = None, new_requirements: str = None, new_documentation_url: str = None, + new_license: str = None, + new_license_url: str = None, new_tags: list or str = None, remove_tags: list or str = None, replace_tags: list or str = None, @@ -94,6 +96,10 @@ def edit_gem_props(gem_path: pathlib.Path = None, update_key_dict['requirements'] = new_requirements if new_documentation_url: update_key_dict['documentation_url'] = new_documentation_url + if new_license: + update_key_dict['license'] = new_license + if new_license_url: + update_key_dict['license_url'] = new_license_url update_key_dict['user_tags'] = update_values_in_key_list(gem_json_data.get('user_tags', []), new_tags, remove_tags, replace_tags) @@ -114,6 +120,8 @@ def _edit_gem_props(args: argparse) -> int: args.gem_icon, args.gem_requirements, args.gem_documentation_url, + args.gem_license, + args.gem_license_url, args.add_tags, args.remove_tags, args.replace_tags) @@ -142,6 +150,10 @@ def add_parser_args(parser): help='Sets the description of the requirements needed to use the gem.') group.add_argument('-gdu', '--gem-documentation-url', type=str, required=False, help='Sets the url for documentation of the gem.') + group.add_argument('-gl', '--gem-license', type=str, required=False, + help='Sets the name for the license of the gem.') + group.add_argument('-glu', '--gem-license-url', type=str, required=False, + help='Sets the url for the license of the gem.') group = parser.add_mutually_exclusive_group(required=False) group.add_argument('-at', '--add-tags', type=str, nargs='*', required=False, help='Adds tag(s) to user_tags property. Can be specified multiple times.') diff --git a/scripts/o3de/tests/unit_test_gem_properties.py b/scripts/o3de/tests/unit_test_gem_properties.py index 5bfbe5573a..dee5811b65 100644 --- a/scripts/o3de/tests/unit_test_gem_properties.py +++ b/scripts/o3de/tests/unit_test_gem_properties.py @@ -18,7 +18,8 @@ TEST_GEM_JSON_PAYLOAD = ''' { "gem_name": "TestGem", "display_name": "TestGem", - "license": "What license TestGem uses goes here: i.e. https://opensource.org/licenses/MIT", + "license": "MIT", + "license_url": "https://opensource.org/licenses/MIT", "origin": "The primary repo for TestGem goes here: i.e. http://www.mydomain.com", "type": "Code", "summary": "A short description of TestGem.", @@ -46,26 +47,30 @@ def init_gem_json_data(request): class TestEditGemProperties: @pytest.mark.parametrize("gem_path, gem_name, gem_new_name, gem_display, gem_origin,\ gem_type, gem_summary, gem_icon, gem_requirements, gem_documentation_url,\ - add_tags, remove_tags, replace_tags, expected_tags, expected_result", [ + gem_license, gem_license_url, add_tags, remove_tags, replace_tags,\ + expected_tags, expected_result", [ pytest.param(pathlib.PurePath('D:/TestProject'), None, 'TestGem2', 'New Gem Name', 'O3DE', 'Code', 'Gem that exercises Default Gem Template', 'new_preview.png', 'Do this extra thing', 'https://o3de.org/docs/user-guide/gems/', + 'Apache 2.0', 'https://www.apache.org/licenses/LICENSE-2.0', ['Physics', 'Rendering', 'Scripting'], None, None, ['TestGem', 'Physics', 'Rendering', 'Scripting'], 0), pytest.param(None, 'TestGem2', None, 'New Gem Name', 'O3DE', 'Asset', 'Gem that exercises Default Gem Template', - 'new_preview.png', 'Do this extra thing', 'https://o3de.org/docs/user-guide/gems/', None, - ['Physics'], None, ['TestGem', 'Rendering', 'Scripting'], 0), + 'new_preview.png', 'Do this extra thing', 'https://o3de.org/docs/user-guide/gems/', + 'Apache 2.0', 'https://www.apache.org/licenses/LICENSE-2.0', + None, ['Physics'], None, ['TestGem', 'Rendering', 'Scripting'], 0), pytest.param(None, 'TestGem2', None, 'New Gem Name', 'O3DE', 'Tool', 'Gem that exercises Default Gem Template', - 'new_preview.png', 'Do this extra thing', 'https://o3de.org/docs/user-guide/gems/', None, - None, ['Animation', 'TestGem'], ['Animation', 'TestGem'], 0) + 'new_preview.png', 'Do this extra thing', 'https://o3de.org/docs/user-guide/gems/', + 'Apache 2.0', 'https://www.apache.org/licenses/LICENSE-2.0', + None, None, ['Animation', 'TestGem'], ['Animation', 'TestGem'], 0) ] ) def test_edit_gem_properties(self, gem_path, gem_name, gem_new_name, gem_display, gem_origin, gem_type, gem_summary, gem_icon, gem_requirements, - gem_documentation_url, add_tags, remove_tags, replace_tags, - expected_tags, expected_result): + gem_documentation_url, gem_license, gem_license_url, add_tags, remove_tags, + replace_tags, expected_tags, expected_result): def get_gem_json_data(gem_path: pathlib.Path) -> dict: return self.gem_json.data @@ -82,7 +87,8 @@ class TestEditGemProperties: patch('o3de.manifest.get_registered', side_effect=get_gem_path) as get_registered_patch: result = gem_properties.edit_gem_props(gem_path, gem_name, gem_new_name, gem_display, gem_origin, gem_type, gem_summary, gem_icon, gem_requirements, - gem_documentation_url, add_tags, remove_tags, replace_tags) + gem_documentation_url, gem_license, gem_license_url, + add_tags, remove_tags, replace_tags) assert result == expected_result if gem_new_name: assert self.gem_json.data.get('gem_name', '') == gem_new_name @@ -100,5 +106,9 @@ class TestEditGemProperties: assert self.gem_json.data.get('requirements', '') == gem_requirements if gem_documentation_url: assert self.gem_json.data.get('documentation_url', '') == gem_documentation_url + if gem_license: + assert self.gem_json.data.get('license', '') == gem_license + if gem_license_url: + assert self.gem_json.data.get('license_url', '') == gem_license_url assert set(self.gem_json.data.get('user_tags', [])) == set(expected_tags) From cc1a2653158d3046c88a4e06bf0c69077dfd6564 Mon Sep 17 00:00:00 2001 From: Adi Bar-Lev <82479970+Adi-Amazon@users.noreply.github.com> Date: Thu, 4 Nov 2021 16:15:18 -0400 Subject: [PATCH 065/177] SpotLight debug draw to include display cone (#5309) * SpotLight debug draw to include display cone Signed-off-by: Adi Bar-Lev <82479970+Adi-Amazon@users.noreply.github.com> * SpotLight debug draw - optimizing Signed-off-by: Adi Bar-Lev <82479970+Adi-Amazon@users.noreply.github.com> --- .../Source/CoreLights/DiskLightDelegate.cpp | 89 ++++++++++--------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp index ebc79abca5..dfb6cf6946 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp @@ -47,53 +47,60 @@ namespace AZ::Render return m_shapeBus->GetRadius() * GetTransform().GetUniformScale(); } - void DiskLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& /*color*/, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const + void DiskLightDelegate::DrawDebugDisplay(const Transform& transform, [[maybe_unused]]const Color&, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const { - if (isSelected) + debugDisplay.PushMatrix(transform); + const float radius = GetConfig()->m_attenuationRadius; + const float shapeRadius = m_shapeBus->GetRadius(); + + auto DrawConicalFrustum = [&debugDisplay](uint32_t numRadiusLines, const Color& color, float brightness, float topRadius, float bottomRadius, float height) { - debugDisplay.PushMatrix(transform); - float radius = GetConfig()->m_attenuationRadius; + const Color displayColor = Color(color.GetAsVector3() * brightness); + debugDisplay.SetColor(displayColor); + debugDisplay.DrawWireDisk(Vector3(0.0, 0.0, height), Vector3::CreateAxisZ(), bottomRadius); - if (GetConfig()->m_enableShutters) + for (uint32_t i = 0; i < numRadiusLines; ++i) { - - float innerRadians = DegToRad(GetConfig()->m_innerShutterAngleDegrees); - float outerRadians = DegToRad(GetConfig()->m_outerShutterAngleDegrees); - - // Draw a cone using the cone angle and attenuation radius - innerRadians = GetMin(innerRadians, outerRadians); - float coneRadiusInner = sin(innerRadians) * radius; - float coneHeightInner = cos(innerRadians) * radius; - float coneRadiusOuter = sin(outerRadians) * radius; - float coneHeightOuter = cos(outerRadians) * radius; - - auto DrawConicalFrustum = [&debugDisplay](uint32_t numRadiusLines, float topRadius, float bottomRadius, float height, float brightness) - { - debugDisplay.SetColor(Color(brightness, brightness, brightness, 1.0f)); - debugDisplay.DrawWireDisk(Vector3(0.0, 0.0, height), Vector3::CreateAxisZ(), bottomRadius); - - for (uint32_t i = 0; i < numRadiusLines; ++i) - { - float radiusLineAngle = float(i) / numRadiusLines * Constants::TwoPi; - debugDisplay.DrawLine( - Vector3(cos(radiusLineAngle) * topRadius, sin(radiusLineAngle) * topRadius, 0), - Vector3(cos(radiusLineAngle) * bottomRadius, sin(radiusLineAngle) * bottomRadius, height) - ); - } - }; - - DrawConicalFrustum(16, m_shapeBus->GetRadius(), m_shapeBus->GetRadius() + coneRadiusInner, coneHeightInner, 1.0f); - DrawConicalFrustum(16, m_shapeBus->GetRadius(), m_shapeBus->GetRadius() + coneRadiusOuter, coneHeightOuter, 0.65f); - + float radiusLineAngle = float(i) / numRadiusLines * Constants::TwoPi; + float cosAngle = cos(radiusLineAngle); + float sinAngle = sin(radiusLineAngle); + debugDisplay.DrawLine( + Vector3(cosAngle * topRadius, sinAngle * topRadius, 0), + Vector3(cosAngle * bottomRadius,sinAngle * bottomRadius, height) + ); } - else - { - debugDisplay.DrawWireDisk(Vector3::CreateZero(), Vector3::CreateAxisZ(), radius); - debugDisplay.DrawArc(Vector3::CreateZero(), radius, 270.0f, 180.0f, 3.0f, 0); - debugDisplay.DrawArc(Vector3::CreateZero(), radius, 0.0f, 180.0f, 3.0f, 1); - } - debugDisplay.PopMatrix(); + }; + + const Color coneColor = isSelected ? Color::CreateOne() : Color(0.0f, 0.75f, 0.75f, 1.0); + const uint32_t innerConeLines = 8; + float innerRadians, outerRadians; + if (GetConfig()->m_enableShutters) + { // With shutters enabled, draw inner and outer debug display frustums + innerRadians = DegToRad(GetConfig()->m_innerShutterAngleDegrees); + outerRadians = DegToRad(GetConfig()->m_outerShutterAngleDegrees); + + // Draw a cone using the cone angle and attenuation radius + innerRadians = GetMin(innerRadians, outerRadians); + + float coneRadiusOuter = sin(outerRadians) * radius; + float coneHeightOuter = cos(outerRadians) * radius; + + // Outer cone frustum 'faded' debug cone + const uint32_t outerConeLines = 9; + DrawConicalFrustum(outerConeLines, coneColor, 0.75f, shapeRadius, shapeRadius + coneRadiusOuter, coneHeightOuter); } + else + { // Generic debug display frustum + const float coneAngle = 25.0f; + innerRadians = DegToRad(coneAngle); // 25 degrees debug display + } + + // Inner cone frustum + float coneRadiusInner = sin(innerRadians) * radius; + float coneHeightInner = cos(innerRadians) * radius; + DrawConicalFrustum(innerConeLines, coneColor, 1.0f, shapeRadius, shapeRadius + coneRadiusInner, coneHeightInner); + + debugDisplay.PopMatrix(); } void DiskLightDelegate::SetEnableShutters(bool enabled) From 5b504086f9f630ac7841ef26cf2662096d1dc567 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Thu, 4 Nov 2021 15:27:00 -0500 Subject: [PATCH 066/177] Prevent infinite recursion with Altitude Gradient. Using the Altitude Gradient as an input to the Height Gradient List can cause infinite recursion since it is both setting and fetching the same height value. Added guards to warn if this occurs and gracefully handles the situation. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../TerrainHeightGradientListComponent.cpp | 37 +++++++++++-------- .../TerrainHeightGradientListComponent.h | 3 ++ 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp index 231d5abc28..06b7320a06 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp @@ -151,24 +151,29 @@ namespace Terrain { float maxSample = 0.0f; terrainExists = false; - - GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f)); - - // Right now, when the list contains multiple entries, we will use the highest point from each gradient. - // This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value - // of 0 outside their data bounds if they're using bounded data. We should examine the possibility of extending the gradient API - // to provide actual bounds so that it's possible to detect if the gradient even 'exists' in an area, at which point we could just - // make this list a prioritized list from top to bottom for any points that overlap. - for (auto& gradientId : m_configuration.m_gradientEntities) + AZ_WarningOnce("Terrain", !m_isRequestInProgress, "Detected cyclic dependences with terrain height entity references"); + if (!m_isRequestInProgress) { - // If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain - // to *not* exist at a specific point. - terrainExists = true; + m_isRequestInProgress = true; + GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f)); - float sample = 0.0f; - GradientSignal::GradientRequestBus::EventResult( - sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); - maxSample = AZ::GetMax(maxSample, sample); + // Right now, when the list contains multiple entries, we will use the highest point from each gradient. + // This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value + // of 0 outside their data bounds if they're using bounded data. We should examine the possibility of extending the gradient + // API to provide actual bounds so that it's possible to detect if the gradient even 'exists' in an area, at which point we + // could just make this list a prioritized list from top to bottom for any points that overlap. + for (auto& gradientId : m_configuration.m_gradientEntities) + { + // If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain + // to *not* exist at a specific point. + terrainExists = true; + + float sample = 0.0f; + GradientSignal::GradientRequestBus::EventResult( + sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); + maxSample = AZ::GetMax(maxSample, sample); + } + m_isRequestInProgress = false; } const float height = AZ::Lerp(m_cachedShapeBounds.GetMin().GetZ(), m_cachedShapeBounds.GetMax().GetZ(), maxSample); diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h index 6c3fd7b820..b5b1a44192 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h @@ -91,6 +91,9 @@ namespace Terrain AZ::Vector2 m_cachedHeightQueryResolution{ 1.0f, 1.0f }; AZ::Aabb m_cachedShapeBounds; + // prevent recursion in case user attaches cyclic dependences + mutable bool m_isRequestInProgress{ false }; + LmbrCentral::DependencyMonitor m_dependencyMonitor; }; } From 20edb35cdc48616be0f87cbe1b548f1e4dc2341a Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu, 4 Nov 2021 15:29:39 -0500 Subject: [PATCH 067/177] [LYN-7245] Fix test thread being created multiple times (#5267) (#5315) * Fix test thread being created multiple times Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Update test to not use a callback Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Add some more comments Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Add back the callback, remove the use of a thread/sleep Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> (cherry picked from commit 73202c209152645a304702f6c7e8d984ebd51dd4) --- .../AssetProcessorManagerTest.cpp | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp index 59ae25601d..f8d758e092 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp @@ -4139,11 +4139,19 @@ struct LockedFileTest switch (message.GetMessageType()) { case SourceFileNotificationMessage::MessageType: - if (const auto sourceFileMessage = azrtti_cast(&message); - sourceFileMessage != nullptr && sourceFileMessage->m_type == SourceFileNotificationMessage::NotificationType::FileRemoved - && m_callback) + if (const auto sourceFileMessage = azrtti_cast(&message); sourceFileMessage != nullptr && + sourceFileMessage->m_type == SourceFileNotificationMessage::NotificationType::FileRemoved) { - m_callback(); + // The File Remove message will occur before an attempt to delete the file + // Wait for more than 1 File Remove message. + // This indicates the AP has attempted to delete the file once, failed to do so and is now retrying + ++m_deleteCounter; + + if(m_deleteCounter > 1 && m_callback) + { + m_callback(); + m_callback = {}; // Unset it to be safe, we only intend to run the callback once + } } break; default: @@ -4167,6 +4175,7 @@ struct LockedFileTest ModtimeScanningTest::TearDown(); } + AZStd::atomic_int m_deleteCounter{ 0 }; AZStd::function m_callback; }; @@ -4206,6 +4215,10 @@ TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeleteFails) TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeletesWhenReleased) { + // This test is intended to verify the AP will successfully retry deleting a source asset + // when one of its product assets is locked temporarily + // We'll lock the file by holding it open + auto theFile = m_data->m_absolutePath[1].toUtf8(); const char* theFileString = theFile.constData(); auto [sourcePath, productPath] = *m_data->m_productPaths.find(theFileString); @@ -4218,19 +4231,22 @@ TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeletesWhenReleased) ASSERT_GT(m_data->m_productPaths.size(), 0); QFile product(productPath); + // Open the file and keep it open to lock it + // We'll start a thread later to unlock the file + // This will allow us to test how AP handles trying to delete a locked file ASSERT_TRUE(product.open(QIODevice::ReadOnly)); // Check if we can delete the file now, if we can't, proceed with the test // If we can, it means the OS running this test doesn't lock open files so there's nothing to test if (!AZ::IO::SystemFile::Delete(productPath.toUtf8().constData())) { - AZStd::thread workerThread; + m_deleteCounter = 0; - m_callback = [&product, &workerThread]() { - workerThread = AZStd::thread([&product]() { - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(60)); - product.close(); - }); + // Set up a callback which will fire after at least 1 retry + // Unlock the file at that point so AP can successfully delete it + m_callback = [&product]() + { + product.close(); }; QMetaObject::invokeMethod( @@ -4240,8 +4256,9 @@ TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeletesWhenReleased) EXPECT_FALSE(QFile::exists(productPath)); EXPECT_EQ(m_data->m_deletedSources.size(), 1); - - workerThread.join(); + + EXPECT_GT(m_deleteCounter, 1); // Make sure the AP tried more than once to delete the file + m_errorAbsorber->ExpectAsserts(0); } else { From e32e1ce572a2e5fd5198da56b4d1b4ec64b1a2db Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Thu, 4 Nov 2021 15:55:37 -0500 Subject: [PATCH 068/177] Fix terrain wireframe refresh. If a terrain layer spawner went outside the world bounds, the debug wireframe wouldn't update correctly because the heights were outside the wireframe sector AABBs. Adjusted the logic to account for this. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../Components/TerrainWorldDebuggerComponent.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp index e140129563..79fd5509f2 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp @@ -218,6 +218,12 @@ namespace Terrain AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution); + // Take the dirty region and adjust the Z values to the world min/max so that even if the dirty region falls outside the current + // world bounds, we still update the wireframe accordingly. + AZ::Aabb dirtyRegion2D = AZ::Aabb::CreateFromMinMaxValues( + dirtyRegion.GetMin().GetX(), dirtyRegion.GetMin().GetY(), worldBounds.GetMin().GetZ(), + dirtyRegion.GetMax().GetX(), dirtyRegion.GetMax().GetY(), worldBounds.GetMax().GetZ()); + // Calculate the world size of each sector. Note that this size actually ends at the last point, not the last square. // So for example, the sector size for 3 points will go from (*--*--*) even though it will be used to draw (*--*--*--). const float xSectorSize = (queryResolution.GetX() * SectorSizeInGridPoints); @@ -230,7 +236,7 @@ namespace Terrain // If we haven't cached anything before, or if the world bounds has changed, clear our cache structure and repopulate it // with WireframeSector entries with the proper AABB sizes. - if (!m_wireframeBounds.IsValid() || !dirtyRegion.IsValid() || !m_wireframeBounds.IsClose(worldBounds)) + if (!m_wireframeBounds.IsValid() || !dirtyRegion2D.IsValid() || !m_wireframeBounds.IsClose(worldBounds)) { m_wireframeBounds = worldBounds; @@ -266,7 +272,7 @@ namespace Terrain // For each sector, if it overlaps with the dirty region, clear it out and recache the wireframe line data. for (auto& sector : m_wireframeSectors) { - if (dirtyRegion.IsValid() && !dirtyRegion.Overlaps(sector.m_aabb)) + if (dirtyRegion2D.IsValid() && !dirtyRegion2D.Overlaps(sector.m_aabb)) { continue; } From c37767f92a0ce4289275713e7d22ec5919934712 Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Thu, 4 Nov 2021 16:30:44 -0500 Subject: [PATCH 069/177] Set window flags so the processing overlay widget always appears on top. Signed-off-by: Chris Galvan --- .../SceneAPI/SceneUI/CommonWidgets/ProcessingOverlayWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Tools/SceneAPI/SceneUI/CommonWidgets/ProcessingOverlayWidget.cpp b/Code/Tools/SceneAPI/SceneUI/CommonWidgets/ProcessingOverlayWidget.cpp index bc9c4679df..a1326b0c82 100644 --- a/Code/Tools/SceneAPI/SceneUI/CommonWidgets/ProcessingOverlayWidget.cpp +++ b/Code/Tools/SceneAPI/SceneUI/CommonWidgets/ProcessingOverlayWidget.cpp @@ -63,7 +63,7 @@ namespace AZ } ProcessingOverlayWidget::ProcessingOverlayWidget(UI::OverlayWidget* overlay, Layout layout, Uuid traceTag) - : QWidget() + : QWidget(nullptr, Qt::Tool | Qt::WindowStaysOnTopHint) , m_traceTag(traceTag) , ui(new Ui::ProcessingOverlayWidget()) , m_overlay(overlay) From 97b0eddcb4f139dcac4c9f716ab1301c2a76cd00 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Thu, 4 Nov 2021 17:56:55 -0500 Subject: [PATCH 070/177] Hook up the "Use Ground Plane" toggle. The "Use Ground Plane" toggle is now functional. When disabled, the terrain layer spawner will say "terrain exists = false" for any point in its bounds unless there's also a Terrain Height Gradient List component with a valid entry. When enabled, it will always say "terrain exists = true", and it will return the min height of the spawner box as the ground plane if there's no valid Terrain Height Gradient List height provider. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../TerrainHeightGradientListComponent.cpp | 17 ++++--- .../Source/TerrainSystem/TerrainSystem.cpp | 44 +++++++++++++------ .../Code/Source/TerrainSystem/TerrainSystem.h | 9 +++- 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp index 231d5abc28..c4415007b5 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp @@ -161,14 +161,17 @@ namespace Terrain // make this list a prioritized list from top to bottom for any points that overlap. for (auto& gradientId : m_configuration.m_gradientEntities) { - // If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain - // to *not* exist at a specific point. - terrainExists = true; + if (gradientId.IsValid()) + { + // If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain + // to *not* exist at a specific point. + terrainExists = true; - float sample = 0.0f; - GradientSignal::GradientRequestBus::EventResult( - sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); - maxSample = AZ::GetMax(maxSample, sample); + float sample = 0.0f; + GradientSignal::GradientRequestBus::EventResult( + sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); + maxSample = AZ::GetMax(maxSample, sample); + } } const float height = AZ::Lerp(m_cachedShapeBounds.GetMin().GetZ(), m_cachedShapeBounds.GetMax().GetZ(), maxSample); diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp index 8d39340b06..20041e2fc8 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp @@ -222,20 +222,31 @@ float TerrainSystem::GetHeightSynchronous(float x, float y, Sampler sampler, boo float TerrainSystem::GetTerrainAreaHeight(float x, float y, bool& terrainExists) const { - AZ::Vector3 inPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ()); - float height = m_currentSettings.m_worldBounds.GetMin().GetZ(); + const float worldMin = m_currentSettings.m_worldBounds.GetMin().GetZ(); + AZ::Vector3 inPosition(x, y, worldMin); + float height = worldMin; + terrainExists = false; AZStd::shared_lock lock(m_areaMutex); - for (auto& [areaId, areaBounds] : m_registeredAreas) + for (auto& [areaId, areaData] : m_registeredAreas) { - inPosition.SetZ(areaBounds.GetMin().GetZ()); - if (areaBounds.Contains(inPosition)) + const float areaMin = areaData.m_areaBounds.GetMin().GetZ(); + inPosition.SetZ(areaMin); + if (areaData.m_areaBounds.Contains(inPosition)) { AZ::Vector3 outPosition; Terrain::TerrainAreaHeightRequestBus::Event( areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, terrainExists); height = outPosition.GetZ(); + if (!terrainExists) + { + // If the terrain height provider doesn't have any data, then check the area's "use ground plane" setting. + // If it's set, then create a default ground plane by saying terrain exists at the minimum height for the area. + // Otherwise, we'll set the height at the terrain world minimum and say it doesn't exist. + terrainExists = areaData.m_useGroundPlane; + height = areaData.m_useGroundPlane ? areaMin : worldMin; + } break; } } @@ -395,12 +406,12 @@ AZ::EntityId TerrainSystem::FindBestAreaEntityAtPosition(float x, float y, AZ::A AZStd::shared_lock lock(m_areaMutex); // The areas are sorted into priority order: the first area that contains inPosition is the most suitable. - for (const auto& [areaId, areaBounds] : m_registeredAreas) + for (const auto& [areaId, areaData] : m_registeredAreas) { - inPosition.SetZ(areaBounds.GetMin().GetZ()); - if (areaBounds.Contains(inPosition)) + inPosition.SetZ(areaData.m_areaBounds.GetMin().GetZ()); + if (areaData.m_areaBounds.Contains(inPosition)) { - bounds = areaBounds; + bounds = areaData.m_areaBounds; return areaId; } } @@ -548,7 +559,12 @@ void TerrainSystem::RegisterArea(AZ::EntityId areaId) AZStd::unique_lock lock(m_areaMutex); AZ::Aabb aabb = AZ::Aabb::CreateNull(); LmbrCentral::ShapeComponentRequestsBus::EventResult(aabb, areaId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); - m_registeredAreas[areaId] = aabb; + + // Cache off whether or not this layer spawner should have a default ground plane when no other terrain height data exists. + bool useGroundPlane = false; + Terrain::TerrainSpawnerRequestBus::EventResult(useGroundPlane, areaId, &Terrain::TerrainSpawnerRequestBus::Events::GetUseGroundPlane); + + m_registeredAreas[areaId] = { aabb, useGroundPlane }; m_dirtyRegion.AddAabb(aabb); m_terrainHeightDirty = true; m_terrainSurfacesDirty = true; @@ -565,10 +581,10 @@ void TerrainSystem::UnregisterArea(AZ::EntityId areaId) m_registeredAreas, [areaId, this](const auto& item) { - auto const& [entityId, aabb] = item; + auto const& [entityId, areaData] = item; if (areaId == entityId) { - m_dirtyRegion.AddAabb(aabb); + m_dirtyRegion.AddAabb(areaData.m_areaBounds); m_terrainHeightDirty = true; m_terrainSurfacesDirty = true; return true; @@ -585,10 +601,10 @@ void TerrainSystem::RefreshArea(AZ::EntityId areaId, AzFramework::Terrain::Terra auto areaAabb = m_registeredAreas.find(areaId); - AZ::Aabb oldAabb = (areaAabb != m_registeredAreas.end()) ? areaAabb->second : AZ::Aabb::CreateNull(); + AZ::Aabb oldAabb = (areaAabb != m_registeredAreas.end()) ? areaAabb->second.m_areaBounds : AZ::Aabb::CreateNull(); AZ::Aabb newAabb = AZ::Aabb::CreateNull(); LmbrCentral::ShapeComponentRequestsBus::EventResult(newAabb, areaId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); - m_registeredAreas[areaId] = newAabb; + m_registeredAreas[areaId].m_areaBounds = newAabb; AZ::Aabb expandedAabb = oldAabb; expandedAabb.AddAabb(newAabb); diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h index 022cd218cc..c6bd19fdda 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h @@ -168,7 +168,14 @@ namespace Terrain bool m_terrainSurfacesDirty = false; AZ::Aabb m_dirtyRegion; + // Cached data for each terrain area to use when looking up terrain data. + struct TerrainAreaData + { + AZ::Aabb m_areaBounds{ AZ::Aabb::CreateNull() }; + bool m_useGroundPlane{ false }; + }; + mutable AZStd::shared_mutex m_areaMutex; - AZStd::map m_registeredAreas; + AZStd::map m_registeredAreas; }; } // namespace Terrain From 97e10d82107684a329c1a07cf3033f5de4259dd6 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Thu, 4 Nov 2021 16:27:05 -0700 Subject: [PATCH 071/177] Fix mock and benchmark interfaces Signed-off-by: kberg-amzn --- Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h | 3 +++ Gems/Multiplayer/Code/Tests/MockInterfaces.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h index 3c3d77e011..9ffdddfd20 100644 --- a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h +++ b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h @@ -270,6 +270,9 @@ namespace Multiplayer [[maybe_unused]] EntityIsMigrating entityIsMigrating) override {} void HandleLocalRpcMessage( [[maybe_unused]] NetworkEntityRpcMessage& message) override {} + void HandleEntitiesExitDomain(const NetEntityIdSet&) override {} + void ForceAssumeAuthority(const ConstNetworkEntityHandle&) override {} + void SetMigrateTimeoutTimeMs(AZ::TimeMs) override {} mutable AZStd::map m_networkEntityMap; diff --git a/Gems/Multiplayer/Code/Tests/MockInterfaces.h b/Gems/Multiplayer/Code/Tests/MockInterfaces.h index 8cebf280b9..f5207d94c8 100644 --- a/Gems/Multiplayer/Code/Tests/MockInterfaces.h +++ b/Gems/Multiplayer/Code/Tests/MockInterfaces.h @@ -87,6 +87,9 @@ namespace UnitTest MOCK_METHOD2(NotifyControllersActivated, void(const Multiplayer::ConstNetworkEntityHandle&, Multiplayer::EntityIsMigrating)); MOCK_METHOD2(NotifyControllersDeactivated, void(const Multiplayer::ConstNetworkEntityHandle&, Multiplayer::EntityIsMigrating)); MOCK_METHOD1(HandleLocalRpcMessage, void(Multiplayer::NetworkEntityRpcMessage&)); + MOCK_METHOD1(HandleEntitiesExitDomain, void(const Multiplayer::NetEntityIdSet&)); + MOCK_METHOD1(ForceAssumeAuthority, void(const Multiplayer::ConstNetworkEntityHandle&)); + MOCK_METHOD1(SetMigrateTimeoutTimeMs, void(AZ::TimeMs)); MOCK_CONST_METHOD0(DebugDraw, void()); }; From dedb367e9affd4e7bd3665955b0e38595fa7b680 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Thu, 4 Nov 2021 16:29:41 -0700 Subject: [PATCH 072/177] Remove lots of mock interface code duplication Signed-off-by: kberg-amzn --- .../Code/Tests/CommonBenchmarkSetup.h | 66 +------------------ 1 file changed, 1 insertion(+), 65 deletions(-) diff --git a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h index 9ffdddfd20..3e4a33290a 100644 --- a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h +++ b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h @@ -212,7 +212,7 @@ namespace Multiplayer } }; - class BenchmarkNetworkEntityManager : public Multiplayer::INetworkEntityManager + class BenchmarkNetworkEntityManager : public MockNetworkEntityManager { public: BenchmarkNetworkEntityManager() : m_authorityTracker(*this) {} @@ -221,58 +221,6 @@ namespace Multiplayer NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() override { return &m_authorityTracker; } MultiplayerComponentRegistry* GetMultiplayerComponentRegistry() override { return &m_multiplayerComponentRegistry; } const HostId& GetHostId() const override { return m_hostId; } - EntityList CreateEntitiesImmediate( - [[maybe_unused]] const PrefabEntityId& prefabEntryId, - [[maybe_unused]] NetEntityRole netEntityRole, - [[maybe_unused]] const AZ::Transform& transform, - [[maybe_unused]] AutoActivate autoActivate) override { - return {}; - } - EntityList CreateEntitiesImmediate( - [[maybe_unused]] const PrefabEntityId& prefabEntryId, - [[maybe_unused]] NetEntityId netEntityId, - [[maybe_unused]] NetEntityRole netEntityRole, - [[maybe_unused]] AutoActivate autoActivate, - [[maybe_unused]] const AZ::Transform& transform) override { - return {}; - } - void SetupNetEntity( - [[maybe_unused]] AZ::Entity* netEntity, - [[maybe_unused]] PrefabEntityId prefabEntityId, - [[maybe_unused]] NetEntityRole netEntityRole) override {} - uint32_t GetEntityCount() const override { return {}; } - void MarkForRemoval( - [[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) override {} - bool IsMarkedForRemoval( - [[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) const override { - return {}; - } - void ClearEntityFromRemovalList( - [[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) override {} - void ClearAllEntities() override {} - void AddEntityMarkedDirtyHandler( - [[maybe_unused]] AZ::Event<>::Handler& entityMarkedDirtyHandle) override {} - void AddEntityNotifyChangesHandler( - [[maybe_unused]] AZ::Event<>::Handler& entityNotifyChangesHandle) override {} - void AddEntityExitDomainHandler( - [[maybe_unused]] EntityExitDomainEvent::Handler& entityExitDomainHandler) override {} - void AddControllersActivatedHandler( - [[maybe_unused]] ControllersActivatedEvent::Handler& controllersActivatedHandler) override {} - void AddControllersDeactivatedHandler( - [[maybe_unused]] ControllersDeactivatedEvent::Handler& controllersDeactivatedHandler) override {} - void NotifyEntitiesDirtied() override {} - void NotifyEntitiesChanged() override {} - void NotifyControllersActivated( - [[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, - [[maybe_unused]] EntityIsMigrating entityIsMigrating) override {} - void NotifyControllersDeactivated( - [[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, - [[maybe_unused]] EntityIsMigrating entityIsMigrating) override {} - void HandleLocalRpcMessage( - [[maybe_unused]] NetworkEntityRpcMessage& message) override {} - void HandleEntitiesExitDomain(const NetEntityIdSet&) override {} - void ForceAssumeAuthority(const ConstNetworkEntityHandle&) override {} - void SetMigrateTimeoutTimeMs(AZ::TimeMs) override {} mutable AZStd::map m_networkEntityMap; @@ -301,18 +249,6 @@ namespace Multiplayer return InvalidNetEntityId; } - [[nodiscard]] AZStd::unique_ptr RequestNetSpawnableInstantiation( - [[maybe_unused]] const AZ::Data::Asset& netSpawnable, - [[maybe_unused]] const AZ::Transform& transform) override - { - return {}; - } - - void Initialize([[maybe_unused]] const HostId& hostId, [[maybe_unused]] AZStd::unique_ptr entityDomain) override {} - bool IsInitialized() const override { return true; } - IEntityDomain* GetEntityDomain() const override { return nullptr; } - void DebugDraw() const override {} - NetworkEntityTracker m_tracker; NetworkEntityAuthorityTracker m_authorityTracker; MultiplayerComponentRegistry m_multiplayerComponentRegistry; From ed06ef7ed24dcffede00e3c2e1ccfcb49b5e989b Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Thu, 4 Nov 2021 16:48:39 -0700 Subject: [PATCH 073/177] Removing ITimeoutHandler to simplify timeout queue interface, removes some unneeded code Signed-off-by: kberg-amzn --- .../DataStructures/TimeoutQueue.cpp | 6 ---- .../DataStructures/TimeoutQueue.h | 20 ------------- .../UdpTransport/UdpFragmentQueue.cpp | 16 +++++----- .../UdpTransport/UdpFragmentQueue.h | 6 ---- .../EntityReplicationManager.h | 2 -- .../EntityReplicationManager.cpp | 30 +++++++++---------- 6 files changed, 21 insertions(+), 59 deletions(-) diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.cpp b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.cpp index b0f316cf50..eb07efe80f 100644 --- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.cpp @@ -122,10 +122,4 @@ namespace AzNetworking m_timeoutItemMap.erase(itemTimeoutId); } } - - void TimeoutQueue::UpdateTimeouts(ITimeoutHandler& timeoutHandler, int32_t maxTimeouts) - { - TimeoutHandler handler([&timeoutHandler](TimeoutQueue::TimeoutItem& item) { return timeoutHandler.HandleTimeout(item); }); - UpdateTimeouts(handler, maxTimeouts); - } } diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h index 63417ea36f..097e45c960 100644 --- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h +++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h @@ -23,8 +23,6 @@ namespace AzNetworking Delete }; - class ITimeoutHandler; - //! @class TimeoutQueue //! @brief class for managing timeout items. class TimeoutQueue @@ -70,11 +68,6 @@ namespace AzNetworking using TimeoutHandler = AZStd::function; void UpdateTimeouts(const TimeoutHandler& timeoutHandler, int32_t maxTimeouts = -1); - //! Updates timeouts for all items, invokes timeout handlers if required. - //! @param timeoutHandler listener instance to call back on for timeouts - //! @param maxTimeouts the maximum number of timeouts to process before breaking iteration - void UpdateTimeouts(ITimeoutHandler& timeoutHandler, int32_t maxTimeouts = -1); - private: struct TimeoutQueueItem @@ -94,19 +87,6 @@ namespace AzNetworking TimeoutItemMap m_timeoutItemMap; TimeoutItemQueue m_timeoutItemQueue; }; - - //! @class ITimeoutHandler - //! @brief interface class for managing timeout items. - class ITimeoutHandler - { - public: - virtual ~ITimeoutHandler() = default; - - //! Handler callback for timed out items. - //! @param item containing registered timeout details - //! @return ETimeoutResult for whether to re-register or discard the timeout params - virtual TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) = 0; - }; } #include diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp index fa4ee78a92..0c710f5a14 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp @@ -20,7 +20,13 @@ namespace AzNetworking void UdpFragmentQueue::Update() { - m_timeoutQueue.UpdateTimeouts(*this); + m_timeoutQueue.UpdateTimeouts([this](TimeoutQueue::TimeoutItem& item) + { + const SequenceId fragmentSequence = static_cast(item.m_userData & 0xFF); + AZLOG(NET_FragmentQueue, "Timing out unreliable fragmented packet %u", static_cast(fragmentSequence)); + m_packetFragments.erase(fragmentSequence); + return TimeoutResult::Delete; + }); } void UdpFragmentQueue::Reset() @@ -163,12 +169,4 @@ namespace AzNetworking return handledPacket; } - - TimeoutResult UdpFragmentQueue::HandleTimeout(TimeoutQueue::TimeoutItem& item) - { - const SequenceId fragmentSequence = static_cast(item.m_userData & 0xFF); - AZLOG(NET_FragmentQueue, "Timing out unreliable fragmented packet %u", static_cast(fragmentSequence)); - m_packetFragments.erase(fragmentSequence); - return TimeoutResult::Delete; - } } diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.h b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.h index 9c929d63e8..5efa767283 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.h +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.h @@ -26,7 +26,6 @@ namespace AzNetworking //! @class UdpFragmentQueue //! @brief Class for reconstructing packet chunks into the original unsegmented packet. class UdpFragmentQueue - : public ITimeoutHandler { public: @@ -51,11 +50,6 @@ namespace AzNetworking private: - //! Handler callback for timed out items. - //! @param item containing registered timeout details - //! @return ETimeoutResult for whether to re-register or discard the timeout params - TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) override; - TimeoutQueue m_timeoutQueue; SequenceGenerator m_sequenceGenerator; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h index 10346ad777..74935d5746 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h @@ -150,7 +150,6 @@ namespace Multiplayer void ClearRemovedReplicators(); class OrphanedEntityRpcs - : public AzNetworking::ITimeoutHandler { public: OrphanedEntityRpcs(EntityReplicationManager& replicationManager); @@ -159,7 +158,6 @@ namespace Multiplayer void AddOrphanedRpc(NetEntityId entityId, NetworkEntityRpcMessage& entityRpcMessage); AZStd::size_t Size() const { return m_entityRpcMap.size(); } private: - AzNetworking::TimeoutResult HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item) override; struct OrphanedRpcs { OrphanedRpcs() = default; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index fa099842c5..583671d1f3 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -912,24 +912,22 @@ namespace Multiplayer ; } - AzNetworking::TimeoutResult EntityReplicationManager::OrphanedEntityRpcs::HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item) - { - NetEntityId timedOutEntityId = aznumeric_cast(item.m_userData); - auto entityRpcsIter = m_entityRpcMap.find(timedOutEntityId); - if (entityRpcsIter != m_entityRpcMap.end()) - { - for (NetworkEntityRpcMessage& rpcMessage : entityRpcsIter->second.m_rpcMessages) - { - m_replicationManager.DispatchOrphanedRpc(rpcMessage, nullptr); - } - m_entityRpcMap.erase(entityRpcsIter); - } - return AzNetworking::TimeoutResult::Delete; - } - void EntityReplicationManager::OrphanedEntityRpcs::Update() { - m_timeoutQueue.UpdateTimeouts(*this); + m_timeoutQueue.UpdateTimeouts([this](AzNetworking::TimeoutQueue::TimeoutItem& item) + { + NetEntityId timedOutEntityId = aznumeric_cast(item.m_userData); + auto entityRpcsIter = m_entityRpcMap.find(timedOutEntityId); + if (entityRpcsIter != m_entityRpcMap.end()) + { + for (NetworkEntityRpcMessage& rpcMessage : entityRpcsIter->second.m_rpcMessages) + { + m_replicationManager.DispatchOrphanedRpc(rpcMessage, nullptr); + } + m_entityRpcMap.erase(entityRpcsIter); + } + return AzNetworking::TimeoutResult::Delete; + }); } bool EntityReplicationManager::OrphanedEntityRpcs::DispatchOrphanedRpcs(EntityReplicator& entityReplicator) From 708582731dc58f99fe71290863878600141ca432 Mon Sep 17 00:00:00 2001 From: Sean Masterson Date: Thu, 4 Nov 2021 16:56:46 -0700 Subject: [PATCH 074/177] Add P0 Deferred Fog Test Signed-off-by: Sean Masterson --- .../Gem/PythonTests/Atom/TestSuite_Main.py | 4 + .../Atom/atom_utils/atom_constants.py | 2 + ...a_AtomEditorComponents_DeferredFogAdded.py | 193 ++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py index cce9a27da6..52e7ec993e 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py @@ -21,6 +21,10 @@ class TestAutomation(EditorTestSuite): class AtomEditorComponents_DecalAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_DecalAdded as test_module + @pytest.mark.test_case_id("C36525658") + class AtomEditorComponents_DeferredFogAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_DeferredFogAdded as test_module + @pytest.mark.test_case_id("C32078119") class AtomEditorComponents_DepthOfFieldAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_DepthOfFieldAdded as test_module diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py index 41ffaa0ce1..0c3410ff33 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py @@ -86,11 +86,13 @@ class AtomComponentProperties: - 'requires' a list of component names as strings required by this component. Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n :param property: From the last element of the property tree path. Default 'name' for component name string. + - 'Enable Deferred Fog' Toggle active state of the component True/False :return: Full property path OR component name if no property specified. """ properties = { 'name': 'Deferred Fog', 'requires': [AtomComponentProperties.postfx_layer()], + 'Enable Deferred Fog': 'Controller|Configuration|Enable Deferred Fog', } return properties[property] diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py new file mode 100644 index 0000000000..71163ece94 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DeferredFogAdded.py @@ -0,0 +1,193 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + + +class Tests: + creation_undo = ( + "UNDO Entity creation success", + "UNDO Entity creation failed") + creation_redo = ( + "REDO Entity creation success", + "REDO Entity creation failed") + deferred_fog_creation = ( + "Deferred Fog Entity successfully created", + "Deferred Fog Entity failed to be created") + deferred_fog_component = ( + "Entity has a Deferred Fog component", + "Entity failed to find Deferred Fog component") + deferred_fog_disabled = ( + "Deferred Fog component disabled", + "Deferred Fog component was not disabled") + postfx_layer_component = ( + "Entity has a PostFX Layer component", + "Entity did not have an PostFX Layer component") + deferred_fog_enabled = ( + "Deferred Fog component enabled", + "Deferred Fog component was not enabled") + enable_deferred_fog_parameter_enabled = ( + "Enable Deferred Fog parameter enabled", + "Enable Deferred Fog parameter was not enabled") + enter_game_mode = ( + "Entered game mode", + "Failed to enter game mode") + exit_game_mode = ( + "Exited game mode", + "Couldn't exit game mode") + is_visible = ( + "Entity is visible", + "Entity was not visible") + is_hidden = ( + "Entity is hidden", + "Entity was not hidden") + entity_deleted = ( + "Entity deleted", + "Entity was not deleted") + deletion_undo = ( + "UNDO deletion success", + "UNDO deletion failed") + deletion_redo = ( + "REDO deletion success", + "REDO deletion failed") + + +def AtomEditorComponents_DeferredFog_AddedToEntity(): + """ + Summary: + Tests the Deferred Fog component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create an Deferred Fog entity with no components. + 2) Add Deferred Fog component to Deferred Fog entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Verify Deferred Fog component not enabled. + 6) Add PostFX Layer component since it is required by the Deferred Fog component. + 7) Verify Deferred Fog component is enabled. + 8) Enable the "Enable Deferred Fog" parameter. + 9) Enter/Exit game mode. + 10) Test IsHidden. + 11) Test IsVisible. + 12) Delete Deferred Fog entity. + 13) UNDO deletion. + 14) REDO deletion. + 15) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper + from Atom.atom_utils.atom_constants import AtomComponentProperties + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + TestHelper.init_idle() + TestHelper.open_level("", "Base") + + # Test steps begin. + # 1. Create an Deferred Fog entity with no components. + deferred_fog_entity = EditorEntity.create_editor_entity(AtomComponentProperties.deferred_fog()) + Report.critical_result(Tests.deferred_fog_creation, deferred_fog_entity.exists()) + + # 2. Add Deferred Fog component to Deferred Fog entity. + deferred_fog_component = deferred_fog_entity.add_component( + AtomComponentProperties.deferred_fog()) + Report.critical_result( + Tests.deferred_fog_component, + deferred_fog_entity.has_component(AtomComponentProperties.deferred_fog())) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not deferred_fog_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, deferred_fog_entity.exists()) + + # 5. Verify Deferred Fog component not enabled. + Report.result(Tests.deferred_fog_disabled, not deferred_fog_component.is_enabled()) + + # 6. Add PostFX Layer component since it is required by the Deferred Fog component. + deferred_fog_entity.add_component(AtomComponentProperties.postfx_layer()) + Report.result( + Tests.postfx_layer_component, + deferred_fog_entity.has_component(AtomComponentProperties.postfx_layer())) + + # 7. Verify Deferred Fog component is enabled. + Report.result(Tests.deferred_fog_enabled, deferred_fog_component.is_enabled()) + + # 8. Enable the "Enable Deferred Fog" parameter. + deferred_fog_component.set_component_property_value( + AtomComponentProperties.deferred_fog('Enable Deferred Fog'), True) + Report.result(Tests.enable_deferred_fog_parameter_enabled, + deferred_fog_component.get_component_property_value( + AtomComponentProperties.deferred_fog('Enable Deferred Fog')) is True) + + # 9. Enter/Exit game mode. + TestHelper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + TestHelper.exit_game_mode(Tests.exit_game_mode) + + # 10. Test IsHidden. + deferred_fog_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, deferred_fog_entity.is_hidden() is True) + + # 11. Test IsVisible. + deferred_fog_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, deferred_fog_entity.is_visible() is True) + + # 12. Delete Deferred Fog entity. + deferred_fog_entity.delete() + Report.result(Tests.entity_deleted, not deferred_fog_entity.exists()) + + # 13. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, deferred_fog_entity.exists()) + + # 14. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not deferred_fog_entity.exists()) + + # 15. Look for errors and asserts. + TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_tracer.asserts: + Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}") + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_DeferredFog_AddedToEntity) From 567702931f8d20131507df5fb38c72120345488c Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Thu, 4 Nov 2021 19:10:20 -0700 Subject: [PATCH 075/177] Updates for the spawnable entity aliases based on provided feedback. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../AzFramework/Spawnable/Spawnable.cpp | 14 +- .../AzFramework/Spawnable/Spawnable.h | 8 +- .../Spawnable/SpawnableAssetHandler.cpp | 2 +- .../Spawnable/SpawnableEntitiesManager.cpp | 43 ++-- .../Spawnable/SpawnableEntitiesManager.h | 16 +- .../SpawnableEntitiesManagerTests.cpp | 196 ++++++++---------- .../Tests/Spawnable/SpawnableTests.cpp | 164 ++++++++------- .../Spawnable/PrefabProcessorContext.cpp | 2 +- .../Prefab/Spawnable/PrefabProcessorContext.h | 2 +- .../Prefab/Spawnable/SpawnableUtils.cpp | 42 ++-- 10 files changed, 246 insertions(+), 243 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp index a3cbf861cb..27e8a85597 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp @@ -32,7 +32,7 @@ namespace AzFramework // EntityAliasVisitorBase // - bool Spawnable::EntityAliasVisitorBase::IsSet(const EntityAliasList* aliases) const + bool Spawnable::EntityAliasVisitorBase::IsValid(const EntityAliasList* aliases) const { return aliases != nullptr; } @@ -139,7 +139,7 @@ namespace AzFramework Spawnable::EntityAliasVisitor::~EntityAliasVisitor() { - if (IsSet()) + if (IsValid()) { Optimize(); @@ -170,9 +170,9 @@ namespace AzFramework return *this; } - bool Spawnable::EntityAliasVisitor::IsSet() const + bool Spawnable::EntityAliasVisitor::IsValid() const { - return EntityAliasVisitorBase::IsSet(m_entityAliasList); + return EntityAliasVisitorBase::IsValid(m_entityAliasList); } bool Spawnable::EntityAliasVisitor::HasAliases() const @@ -413,7 +413,7 @@ namespace AzFramework Spawnable::EntityAliasConstVisitor::~EntityAliasConstVisitor() { - if (IsSet()) + if (IsValid()) { AZ_Assert( m_owner.m_shareState <= ShareState::Read, "Attempting to unlock a read shared spawnable that was not in a read shared mode (%i).", @@ -422,9 +422,9 @@ namespace AzFramework } } - bool Spawnable::EntityAliasConstVisitor::IsSet() const + bool Spawnable::EntityAliasConstVisitor::IsValid() const { - return EntityAliasVisitorBase::IsSet(m_entityAliasList); + return EntityAliasVisitorBase::IsValid(m_entityAliasList); } bool Spawnable::EntityAliasConstVisitor::HasAliases() const diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h index 9058ac7ba4..f0aa2c7806 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h @@ -71,7 +71,7 @@ namespace AzFramework class EntityAliasVisitorBase { protected: - bool IsSet(const EntityAliasList* aliases) const; + bool IsValid(const EntityAliasList* aliases) const; bool HasAliases(const EntityAliasList* aliases) const; bool AreAllSpawnablesReady(const EntityAliasList* aliases) const; @@ -100,7 +100,7 @@ namespace AzFramework EntityAliasVisitor& operator=(const EntityAliasVisitor& rhs) = delete; //! Checks if the visitor was able to retrieve data. This needs to be checked before calling any other functions. - bool IsSet() const; + bool IsValid() const; bool HasAliases() const; bool AreAllSpawnablesReady() const; @@ -153,7 +153,7 @@ namespace AzFramework ~EntityAliasConstVisitor(); //! Checks if the visitor was able to retrieve data. This needs to be checked before calling any other functions. - bool IsSet() const; + bool IsValid() const; bool HasAliases() const; bool AreAllSpawnablesReady() const; @@ -179,7 +179,7 @@ namespace AzFramework Spawnable(const Spawnable& rhs) = delete; Spawnable(Spawnable&& other) = delete; ~Spawnable() override = default; - + Spawnable& operator=(const Spawnable& rhs) = delete; Spawnable& operator=(Spawnable&& other) = delete; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp index 6ef423fa91..eab681da0d 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp @@ -103,7 +103,7 @@ namespace AzFramework const AZ::Data::AssetFilterCB& assetLoadFilterCB) { Spawnable::EntityAliasVisitor aliases = spawnable->TryGetAliases(); - AZ_Assert(aliases.IsSet(), "Newly created Spawnable '%s' was already locked.", asset.GetHint().c_str()); + AZ_Assert(aliases.IsValid(), "Newly created Spawnable '%s' was already locked.", asset.GetHint().c_str()); if (aliases.HasAliases()) { AZ_Assert( diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 33fe1601af..17a18dd5e5 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -300,20 +300,20 @@ namespace AzFramework } } - AZ::Entity* SpawnableEntitiesManager::CloneSingleEntity(const AZ::Entity& entityTemplate, - EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext) + AZ::Entity* SpawnableEntitiesManager::CloneSingleEntity(const AZ::Entity& entityPrototype, + EntityIdMap& prototypeToCloneMap, AZ::SerializeContext& serializeContext) { // If the same ID gets remapped more than once, preserve the original remapping instead of overwriting it. constexpr bool allowDuplicateIds = false; return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( - &entityTemplate, templateToCloneMap, &serializeContext); + &entityPrototype, prototypeToCloneMap, &serializeContext); } AZ::Entity* SpawnableEntitiesManager::CloneSingleAliasedEntity( - const AZ::Entity& entityTemplate, + const AZ::Entity& entityPrototype, const Spawnable::EntityAlias& alias, - EntityIdMap& templateToCloneMap, + EntityIdMap& prototypeToCloneMap, AZ::Entity* previouslySpawnedEntity, AZ::SerializeContext& serializeContext) { @@ -324,26 +324,27 @@ namespace AzFramework { case Spawnable::EntityAliasType::Original: // Behave as the original version. - clone = CloneSingleEntity(entityTemplate, templateToCloneMap, serializeContext); + clone = CloneSingleEntity(entityPrototype, prototypeToCloneMap, serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); return clone; case Spawnable::EntityAliasType::Disable: // Do nothing. return nullptr; case Spawnable::EntityAliasType::Replace: - clone = CloneSingleEntity(*(alias.m_spawnable->GetEntities()[alias.m_targetIndex]), templateToCloneMap, serializeContext); + clone = CloneSingleEntity(*(alias.m_spawnable->GetEntities()[alias.m_targetIndex]), prototypeToCloneMap, serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); return clone; case Spawnable::EntityAliasType::Additional: // The asset handler will have sorted and inserted a Spawnable::EntityAliasType::Original, so the just // spawn the additional entity. - clone = CloneSingleEntity(*(alias.m_spawnable->GetEntities()[alias.m_targetIndex]), templateToCloneMap, serializeContext); + clone = CloneSingleEntity(*(alias.m_spawnable->GetEntities()[alias.m_targetIndex]), prototypeToCloneMap, serializeContext); AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); return clone; case Spawnable::EntityAliasType::Merge: AZ_Assert(previouslySpawnedEntity != nullptr, "Merging components but there's no entity to add to yet."); AppendComponents( - *previouslySpawnedEntity, alias.m_spawnable->GetEntities()[alias.m_targetIndex]->GetComponents(), templateToCloneMap, serializeContext); + *previouslySpawnedEntity, alias.m_spawnable->GetEntities()[alias.m_targetIndex]->GetComponents(), prototypeToCloneMap, + serializeContext); return nullptr; default: AZ_Assert(false, "Unsupported spawnable entity alias type: %i", alias.m_aliasType); @@ -353,17 +354,17 @@ namespace AzFramework void SpawnableEntitiesManager::AppendComponents( AZ::Entity& target, - const AZ::Entity::ComponentArrayType& componentTemplates, - EntityIdMap& templateToCloneMap, + const AZ::Entity::ComponentArrayType& componentPrototypes, + EntityIdMap& prototypeToCloneMap, AZ::SerializeContext& serializeContext) { // Only components are added and entities are looked up so no duplicate entity ids should be encountered. constexpr bool allowDuplicateIds = false; - for (const AZ::Component* component : componentTemplates) + for (const AZ::Component* component : componentPrototypes) { AZ::Component* clone = AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( - component, templateToCloneMap, &serializeContext); + component, prototypeToCloneMap, &serializeContext); AZ_Assert(clone, "Unable to clone component for entity '%s' (%zu).", target.GetName().c_str(), target.GetId()); [[maybe_unused]] bool result = target.AddComponent(clone); AZ_Assert(result, "Unable to add cloned component to entity '%s' (%zu).", target.GetName().c_str(), target.GetId()); @@ -409,7 +410,7 @@ namespace AzFramework if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) { if (Spawnable::EntityAliasConstVisitor aliases = ticket.m_spawnable->TryGetAliasesConst(); - aliases.IsSet() && aliases.AreAllSpawnablesReady()) + aliases.IsValid() && aliases.AreAllSpawnablesReady()) { AZStd::vector& spawnedEntities = ticket.m_spawnedEntities; AZStd::vector& spawnedEntityIndices = ticket.m_spawnedEntityIndices; @@ -417,7 +418,7 @@ namespace AzFramework // Keep track how many entities there were in the array initially size_t spawnedEntitiesInitialCount = spawnedEntities.size(); - // These are 'template' entities we'll be cloning from + // These are 'prototype' entities we'll be cloning from const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities(); uint32_t entitiesToSpawnSize = aznumeric_caster(entitiesToSpawn.size()); @@ -527,7 +528,7 @@ namespace AzFramework if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) { if (Spawnable::EntityAliasConstVisitor aliases = ticket.m_spawnable->TryGetAliasesConst(); - aliases.IsSet() && aliases.AreAllSpawnablesReady()) + aliases.IsValid() && aliases.AreAllSpawnablesReady()) { AZStd::vector& spawnedEntities = ticket.m_spawnedEntities; AZStd::vector& spawnedEntityIndices = ticket.m_spawnedEntityIndices; @@ -538,13 +539,13 @@ namespace AzFramework // Keep track of how many entities there were in the array initially size_t spawnedEntitiesInitialCount = spawnedEntities.size(); - // These are 'template' entities we'll be cloning from + // These are 'prototype' entities we'll be cloning from const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities(); size_t entitiesToSpawnSize = request.m_entityIndices.size(); if (ticket.m_entityIdReferenceMap.empty() || !request.m_referencePreviouslySpawnedEntities) { - // This map keeps track of ids from template (spawnable) to clone (instance) allowing patch ups of fields referring + // This map keeps track of ids from prototype (spawnable) to clone (instance) allowing patch ups of fields referring // to entityIds outside of a given entity. // We pre-generate the full set of entity id to new entity id mappings, so that during the clone operation below, // any entity references that point to a not-yet-cloned entity will still get their ids remapped correctly. @@ -754,7 +755,7 @@ namespace AzFramework // Pre-generate the full set of entity id to new entity id mappings, so that during the clone operation below, // any entity references that point to a not-yet-cloned entity will still get their ids remapped correctly. // This map is intentionally cleared out and regenerated here to ensure that we're starting fresh with mappings that - // match the new set of template entities getting spawned. + // match the new set of prototype entities getting spawned. InitializeEntityIdMappings(entities, ticket.m_entityIdReferenceMap, ticket.m_previouslySpawned); if (ticket.m_loadAll) @@ -819,7 +820,7 @@ namespace AzFramework Ticket& ticket = *request.m_ticket; if (ticket.m_spawnable.IsReady() && request.m_requestId == ticket.m_currentRequestId) { - if (Spawnable::EntityAliasVisitor aliases = ticket.m_spawnable->TryGetAliases(); aliases.IsSet()) + if (Spawnable::EntityAliasVisitor aliases = ticket.m_spawnable->TryGetAliases(); aliases.IsValid()) { for (EntityAliasTypeChange& replacement : request.m_entityAliases) { @@ -921,7 +922,7 @@ namespace AzFramework if (request.m_checkAliasSpawnables) { if (Spawnable::EntityAliasConstVisitor visitor = ticket.m_spawnable->TryGetAliasesConst(); - !visitor.IsSet() || !visitor.AreAllSpawnablesReady()) + !visitor.IsValid() || !visitor.AreAllSpawnablesReady()) { return CommandResult::Requeue; } diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index 09e9b1acfc..d2b5c3c9af 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -96,9 +96,9 @@ namespace AzFramework AZ_CLASS_ALLOCATOR(Ticket, AZ::ThreadPoolAllocator, 0); static constexpr uint32_t Processing = AZStd::numeric_limits::max(); - //! Map of template entity ids to their associated instance ids. - //! Tickets can be used to spawn the same template entities multiple times, in any order, across multiple calls. - //! Since template entities can reference other entities, this map is used to fix up those references across calls + //! Map of prototype entity ids to their associated instance ids. + //! Tickets can be used to spawn the same prototype entities multiple times, in any order, across multiple calls. + //! Since prototype entities can reference other entities, this map is used to fix up those references across calls //! using the following policy: //! - Entities referencing an entity that hasn't been spawned yet will get a reference to the id that *will* be used //! the first time that entity will be spawned. The reference will be invalid until that entity is spawned, but @@ -243,17 +243,17 @@ namespace AzFramework CommandQueueStatus ProcessQueue(Queue& queue); AZ::Entity* CloneSingleEntity( - const AZ::Entity& entityTemplate, EntityIdMap& templateToCloneMap, AZ::SerializeContext& serializeContext); + const AZ::Entity& entityPrototype, EntityIdMap& prototypeToCloneMap, AZ::SerializeContext& serializeContext); AZ::Entity* CloneSingleAliasedEntity( - const AZ::Entity& entityTemplate, + const AZ::Entity& entityPrototype, const Spawnable::EntityAlias& alias, - EntityIdMap& templateToCloneMap, + EntityIdMap& prototypeToCloneMap, AZ::Entity* previouslySpawnedEntity, AZ::SerializeContext& serializeContext); void AppendComponents( AZ::Entity& target, - const AZ::Entity::ComponentArrayType& componentTemplates, - EntityIdMap& templateToCloneMap, + const AZ::Entity::ComponentArrayType& componentPrototypes, + EntityIdMap& prototypeToCloneMap, AZ::SerializeContext& serializeContext); CommandResult ProcessRequest(SpawnAllEntitiesCommand& request); diff --git a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 83c895bc7a..535ceab30a 100644 --- a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -192,6 +192,79 @@ namespace UnitTest } } + static bool AreAllEntitiesReplaced(AzFramework::SpawnableConstEntityContainerView entities) + { + for (const AZ::Entity* entity : entities) + { + if (entity) + { + if (entity->FindComponent() != nullptr || + entity->FindComponent() == nullptr) + { + return false; + } + } + else + { + return false; + } + } + return true; + } + + static bool IsEveryOtherEntityAReplacement(AzFramework::SpawnableConstEntityContainerView entities) + { + bool onAlternative = true; + for (const AZ::Entity* entity : entities) + { + if (entity) + { + if (onAlternative) + { + if (entity->FindComponent() == nullptr || + entity->FindComponent() != nullptr) + { + return false; + } + } + else + { + if (entity->FindComponent() != nullptr || + entity->FindComponent() == nullptr) + { + return false; + } + } + onAlternative = !onAlternative; + } + else + { + return false; + } + } + return true; + } + + static bool AreAllMerged(AzFramework::SpawnableConstEntityContainerView entities) + { + for (const AZ::Entity* entity : entities) + { + if (entity) + { + if (entity->FindComponent() == nullptr || + entity->FindComponent() == nullptr) + { + return false; + } + } + else + { + return false; + } + } + return true; + } + void CreateRecursiveHierarchy() { AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities(); @@ -539,18 +612,7 @@ namespace UnitTest AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) { spawnedEntitiesCount += entities.size(); - for (const AZ::Entity* entity : entities) - { - if (entity) - { - allReplaced = allReplaced && entity->FindComponent() == nullptr; - allReplaced = allReplaced && entity->FindComponent() != nullptr; - } - else - { - allReplaced = false; - } - } + allReplaced = AreAllEntitiesReplaced(entities); }; AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(callback); @@ -574,33 +636,12 @@ namespace UnitTest &target); size_t spawnedEntitiesCount = 0; - bool allReplaced = true; - auto callback = [&spawnedEntitiesCount, &allReplaced]( + bool allAdded = true; + auto callback = [&spawnedEntitiesCount, &allAdded]( AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) { spawnedEntitiesCount += entities.size(); - bool onSource = true; - for (const AZ::Entity* entity : entities) - { - if (entity) - { - if (onSource) - { - allReplaced = allReplaced && entity->FindComponent() != nullptr; - allReplaced = allReplaced && entity->FindComponent() == nullptr; - } - else - { - allReplaced = allReplaced && entity->FindComponent() == nullptr; - allReplaced = allReplaced && entity->FindComponent() != nullptr; - } - onSource = !onSource; - } - else - { - allReplaced = false; - } - } + allAdded = IsEveryOtherEntityAReplacement(entities); }; AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(callback); @@ -608,7 +649,7 @@ namespace UnitTest m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_EQ(8, spawnedEntitiesCount); - EXPECT_TRUE(allReplaced); + EXPECT_TRUE(allAdded); } TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_AllAliasesWithMerge_SourceAndTargetComponentsMerged) @@ -624,23 +665,12 @@ namespace UnitTest &target); size_t spawnedEntitiesCount = 0; - bool allReplaced = true; - auto callback = [&spawnedEntitiesCount, &allReplaced]( + bool allMerged = true; + auto callback = [&spawnedEntitiesCount, &allMerged]( AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) { spawnedEntitiesCount += entities.size(); - for (const AZ::Entity* entity : entities) - { - if (entity) - { - allReplaced = allReplaced && entity->FindComponent() != nullptr; - allReplaced = allReplaced && entity->FindComponent() != nullptr; - } - else - { - allReplaced = false; - } - } + allMerged = AreAllMerged(entities); }; AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(callback); @@ -648,7 +678,7 @@ namespace UnitTest m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_EQ(4, spawnedEntitiesCount); - EXPECT_TRUE(allReplaced); + EXPECT_TRUE(allMerged); } // @@ -1080,18 +1110,7 @@ namespace UnitTest AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) { spawnedEntitiesCount += entities.size(); - for (const AZ::Entity* entity : entities) - { - if (entity) - { - allReplaced = allReplaced && entity->FindComponent() == nullptr; - allReplaced = allReplaced && entity->FindComponent() != nullptr; - } - else - { - allReplaced = false; - } - } + allReplaced = AreAllEntitiesReplaced(entities); }; AzFramework::SpawnEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(callback); @@ -1117,33 +1136,13 @@ namespace UnitTest AZStd::vector indices = { 0, 2, 3, 1 }; size_t spawnedEntitiesCount = 0; - bool allReplaced = true; - auto callback = [&spawnedEntitiesCount, &allReplaced]( + bool allAdded = true; + auto callback = + [&spawnedEntitiesCount, &allAdded]( AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) { spawnedEntitiesCount += entities.size(); - bool onSource = true; - for (const AZ::Entity* entity : entities) - { - if (entity) - { - if (onSource) - { - allReplaced = allReplaced && entity->FindComponent() != nullptr; - allReplaced = allReplaced && entity->FindComponent() == nullptr; - } - else - { - allReplaced = allReplaced && entity->FindComponent() == nullptr; - allReplaced = allReplaced && entity->FindComponent() != nullptr; - } - onSource = !onSource; - } - else - { - allReplaced = false; - } - } + allAdded = IsEveryOtherEntityAReplacement(entities); }; AzFramework::SpawnEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(callback); @@ -1151,7 +1150,7 @@ namespace UnitTest m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_EQ(8, spawnedEntitiesCount); - EXPECT_TRUE(allReplaced); + EXPECT_TRUE(allAdded); } TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_AllAliasesWithMerge_SourceAndTargetComponentsMerged) @@ -1169,23 +1168,12 @@ namespace UnitTest AZStd::vector indices = { 0, 2, 3, 1 }; size_t spawnedEntitiesCount = 0; - bool allReplaced = true; - auto callback = [&spawnedEntitiesCount, &allReplaced]( + bool allMerged = true; + auto callback = [&spawnedEntitiesCount, &allMerged]( AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) { spawnedEntitiesCount += entities.size(); - for (const AZ::Entity* entity : entities) - { - if (entity) - { - allReplaced = allReplaced && entity->FindComponent() != nullptr; - allReplaced = allReplaced && entity->FindComponent() != nullptr; - } - else - { - allReplaced = false; - } - } + allMerged = AreAllMerged(entities); }; AzFramework::SpawnEntitiesOptionalArgs optionalArgs; optionalArgs.m_completionCallback = AZStd::move(callback); @@ -1193,7 +1181,7 @@ namespace UnitTest m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular); EXPECT_EQ(4, spawnedEntitiesCount); - EXPECT_TRUE(allReplaced); + EXPECT_TRUE(allMerged); } // diff --git a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableTests.cpp b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableTests.cpp index c689295f17..94641037f0 100644 --- a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableTests.cpp +++ b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableTests.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include @@ -15,6 +16,8 @@ namespace UnitTest class SpawnableTest : public AllocatorsFixture { public: + static constexpr size_t DefaultEntityAliasTestCount = 8; + void SetUp() override { AllocatorsFixture::SetUp(); @@ -30,25 +33,26 @@ namespace UnitTest AllocatorsFixture::TearDown(); } - void InsertEightEntities() + void InsertEntities(size_t count) { AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities(); - entities.reserve(entities.size() + 8); - for (size_t i = 0; i < 8; ++i) + entities.reserve(entities.size() + count); + for (size_t i = 0; i < count; ++i) { entities.emplace_back(AZStd::make_unique()); } } - void InsertEightEntityAliases( - const AZStd::array& sourceIds, - const AZStd::array& targetIds, - const AZStd::array& aliasTypes, + template + void InsertEntityAliases( + const AZStd::array& sourceIds, + const AZStd::array& targetIds, + const AZStd::array& aliasTypes, bool queueLoad = false) { AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - for (uint32_t i = 0; i < 8; ++i) + for (uint32_t i = 0; i < Count; ++i) { AZ::Data::Asset spawnable( AZ::Data::AssetId(AZ::Uuid("{4CBEC17A-52D6-42D5-9037-F4C05B9CE1D9}"), i), azrtti_typeid()); @@ -56,20 +60,30 @@ namespace UnitTest } } - void InsertEightEntityAliases(bool queueLoad) + template + void InsertEntityAliases(bool queueLoad) { using namespace AzFramework; - InsertEightEntityAliases( - { 0, 1, 2, 3, 4, 5, 6, 7 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, - { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, - Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, - Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace }, - queueLoad); + + AZStd::array ids; + for (uint32_t i=0; i(Count); ++i) + { + ids[i] = i; + } + + AZStd::array aliasTypes; + for (uint32_t i = 0; i < aznumeric_cast(Count); ++i) + { + aliasTypes[i] = Spawnable::EntityAliasType::Replace; + } + + InsertEntityAliases(ids, ids, aliasTypes, queueLoad); } - void InsertEightEntityAliases() + template + void InsertEntityAliases() { - InsertEightEntityAliases(false); + InsertEntityAliases(false); } protected: @@ -84,25 +98,25 @@ namespace UnitTest TEST_F(SpawnableTest, TryGetAliasesConst_GetVisitor_VisitorDataIsAvailable) { AzFramework::Spawnable::EntityAliasConstVisitor visitor = m_spawnable->TryGetAliasesConst(); - EXPECT_TRUE(visitor.IsSet()); + EXPECT_TRUE(visitor.IsValid()); } TEST_F(SpawnableTest, TryGetAliasesConst_VisitorThatIsNotReadShared_VisitorDataIsNotAvailable) { AzFramework::Spawnable::EntityAliasVisitor readWriteVisitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(readWriteVisitor.IsSet()); + ASSERT_TRUE(readWriteVisitor.IsValid()); AzFramework::Spawnable::EntityAliasConstVisitor visitor = m_spawnable->TryGetAliasesConst(); - EXPECT_FALSE(visitor.IsSet()); + EXPECT_FALSE(visitor.IsValid()); } TEST_F(SpawnableTest, TryGetAliasesConst_VisitorThatIsAlreadyReadShared_VisitorDataIsAvailable) { AzFramework::Spawnable::EntityAliasConstVisitor readVisitor = m_spawnable->TryGetAliasesConst(); - ASSERT_TRUE(readVisitor.IsSet()); + ASSERT_TRUE(readVisitor.IsValid()); AzFramework::Spawnable::EntityAliasConstVisitor visitor = m_spawnable->TryGetAliasesConst(); - EXPECT_TRUE(visitor.IsSet()); + EXPECT_TRUE(visitor.IsValid()); } @@ -113,16 +127,16 @@ namespace UnitTest TEST_F(SpawnableTest, TryGetAliases_GetVisitor_VisitorDataIsAvailable) { AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - EXPECT_TRUE(visitor.IsSet()); + EXPECT_TRUE(visitor.IsValid()); } TEST_F(SpawnableTest, TryGetAliasesConst_VisitorThatIsAlreadyShared_VisitorDataNotIsAvailable) { AzFramework::Spawnable::EntityAliasConstVisitor readVisitor = m_spawnable->TryGetAliasesConst(); - ASSERT_TRUE(readVisitor.IsSet()); + ASSERT_TRUE(readVisitor.IsValid()); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - EXPECT_FALSE(visitor.IsSet()); + EXPECT_FALSE(visitor.IsValid()); } @@ -138,17 +152,17 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_HasAliases_EmptyAliasList_ReturnsFalse) { AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); EXPECT_FALSE(visitor.HasAliases()); } - TEST_F(SpawnableTest, EntityAliasVisitor_HasAliases_FilledInAliasList_ReturnsTue) + TEST_F(SpawnableTest, EntityAliasVisitor_HasAliases_FilledInAliasList_ReturnsTrue) { - InsertEightEntities(); - InsertEightEntityAliases(); + InsertEntities(8); + InsertEntityAliases<8>(); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); EXPECT_TRUE(visitor.HasAliases()); } @@ -160,10 +174,10 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_Optimize_SortEntityAliases_AliasesAreSortedBySourceAndTargetId) { - InsertEightEntities(); - InsertEightEntityAliases(); + InsertEntities(DefaultEntityAliasTestCount); + InsertEntityAliases(); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); // Optimize doesn't need to be explicitly called because the setup of the aliases will cause the alias list to be sorted and optimized. @@ -188,15 +202,15 @@ namespace UnitTest SpawnableTest, EntityAliasVisitor_Optimize_RemoveUnused_OnlySecondToLastAliasRemains) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases( + InsertEntities(8); + InsertEntityAliases<8>( { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Disable, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Disable, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original }); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); EXPECT_EQ(1, AZStd::distance(visitor.begin(), visitor.end())); EXPECT_EQ(Spawnable::EntityAliasType::Replace, visitor.begin()->m_aliasType); @@ -206,15 +220,15 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_Optimize_AddAdditional_ThreeAdditionalAliasesAreAdded) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases( + InsertEntities(8); + InsertEntityAliases<8>( { 0, 0, 0, 0, 1, 2, 2, 2 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, { Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional, Spawnable::EntityAliasType::Additional }); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); EXPECT_EQ(11, AZStd::distance(visitor.begin(), visitor.end())); EXPECT_EQ(Spawnable::EntityAliasType::Original, visitor.begin()->m_aliasType); @@ -225,15 +239,15 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_Optimize_OriginalsOnly_AliasListIsEmpty) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases( + InsertEntities(8); + InsertEntityAliases<8>( { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, { Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original }); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); EXPECT_EQ(0, AZStd::distance(visitor.begin(), visitor.end())); } @@ -241,15 +255,15 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_Optimize_MixedOriginals_AllOriginalsRemoved) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases( + InsertEntities(8); + InsertEntityAliases<8>( { 0, 0, 0, 1, 1, 2, 2, 2 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, { Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Disable, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Original }); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); EXPECT_EQ(2, AZStd::distance(visitor.begin(), visitor.end())); EXPECT_EQ(Spawnable::EntityAliasType::Disable, visitor.begin()->m_aliasType); @@ -259,15 +273,15 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_Optimize_MergeAfterOriginal_NoAdditionalOriginalIsInserted) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases( + InsertEntities(8); + InsertEntityAliases<8>( { 0, 0, 1, 1, 2, 2, 2, 2 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, { Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Merge, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Merge, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original, Spawnable::EntityAliasType::Original }); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); EXPECT_EQ(4, AZStd::distance(visitor.begin(), visitor.end())); EXPECT_EQ(Spawnable::EntityAliasType::Original, visitor.begin()->m_aliasType); @@ -284,15 +298,15 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_UpdateAliasType_AllToOriginal_NoAliasesAfterOptimization) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases( + InsertEntities(8); + InsertEntityAliases<8>( { 0, 1, 2, 3, 4, 5, 6, 7 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace }); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); for (uint32_t i = 0; i < 8; ++i) { @@ -317,15 +331,15 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_UpdateAliases_AllToOriginal_NoAliasesAfterOptimization) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases( + InsertEntities(8); + InsertEntityAliases<8>( { 0, 1, 2, 3, 4, 5, 6, 7 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace }); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); auto callback = [](Spawnable::EntityAliasType& aliasType, bool& /*queueLoad*/, const AZ::Data::Asset& /*aliasedSpawnable*/, @@ -348,15 +362,15 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_UpdateAliases_FilterByTag_OnlyOneAliasUpdated) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases( + InsertEntities(8); + InsertEntityAliases<8>( { 0, 1, 2, 3, 4, 5, 6, 7 }, { 0, 1, 2, 3, 4, 5, 6, 7 }, { Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace, Spawnable::EntityAliasType::Replace }); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); bool correctTag = false; size_t numberOfUpdates = 0; @@ -381,11 +395,11 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_AreAllSpawnablesReady_CheckFakeLoadedAssets_ReturnsTrue) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases(); + InsertEntities(DefaultEntityAliasTestCount); + InsertEntityAliases(); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); EXPECT_TRUE(visitor.AreAllSpawnablesReady()); } @@ -393,11 +407,11 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_AreAllSpawnablesReady_CheckFakeNotLoadedAssets_ReturnsFalse) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases(true); + InsertEntities(8); + InsertEntityAliases<8>(true); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); EXPECT_FALSE(visitor.AreAllSpawnablesReady()); } @@ -410,11 +424,11 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_ListTargetSpawnables_ListAllTargetAssets_AllTargetsListed) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases(); + InsertEntities(DefaultEntityAliasTestCount); + InsertEntityAliases(); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); size_t count = 0; bool correctAssets = true; @@ -432,11 +446,11 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_ListTargetSpawnables_ListTaggedTargetAssets_OneAssetListed) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases(); + InsertEntities(DefaultEntityAliasTestCount); + InsertEntityAliases(); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); size_t count = 0; bool correctAsset = false; @@ -459,11 +473,11 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_ListSpawnablesRequiringLoad_AllSetToLoaded_AllTargetsListed) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases(true); + InsertEntities(DefaultEntityAliasTestCount); + InsertEntityAliases(true); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); size_t count = 0; bool correctAssets = true; @@ -481,11 +495,11 @@ namespace UnitTest TEST_F(SpawnableTest, EntityAliasVisitor_ListSpawnablesRequiringLoad_AllSetToNotLoaded_NoTargetsListed) { using namespace AzFramework; - InsertEightEntities(); - InsertEightEntityAliases(false); + InsertEntities(DefaultEntityAliasTestCount); + InsertEntityAliases(false); AzFramework::Spawnable::EntityAliasVisitor visitor = m_spawnable->TryGetAliases(); - ASSERT_TRUE(visitor.IsSet()); + ASSERT_TRUE(visitor.IsValid()); size_t count = 0; auto callback = [&count](const AZ::Data::Asset& /*targetSpawnable*/) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp index 21fa3db52b..efef0f53de 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.cpp @@ -220,7 +220,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils if (it == aliasVisitors.end()) { AzFramework::Spawnable::EntityAliasVisitor visitor = source->m_spawnable.TryGetAliases(); - AZ_Assert(visitor.IsSet(), "Unable to obtain lock for a newly create spawnable."); + AZ_Assert(visitor.IsValid(), "Unable to obtain lock for a newly create spawnable."); it = aliasVisitors.emplace(source->m_spawnable.GetId(), AZStd::move(visitor)).first; } it->second.AddAlias( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h index 8e29deadca..f20c85eb1d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h @@ -30,7 +30,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils Disable, //!< No alias is added. OptionalReplace, //!< At runtime the entity might be replaced. If the alias is disabled the original entity will be spawned. //!< The original entity will be left in the spawnable and a copy is returned. - Replace, //!< At runtime the entity will be replaced. If the alias is disabled nothing will be spawned not. The original + Replace, //!< At runtime the entity will be replaced. If the alias is disabled nothing will be spawned. The original //!< entity is returned and a blank entity is left. Additional, //!< At runtime the alias entity will be added as an additional but unrelated entity with a new entity id. //!< An empty entity will be returned. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp index 7b14ad1228..dfe82167c2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp @@ -32,38 +32,38 @@ namespace AzToolsFramework::Prefab::SpawnableUtils return result; } - AZ::Entity* FindEntity(AZ::EntityId entity, AzToolsFramework::Prefab::Instance& source) + AZ::Entity* FindEntity(AZ::EntityId entityId, AzToolsFramework::Prefab::Instance& source) { AZ::Entity* result = nullptr; source.GetEntities( - [&result, entity](AZStd::unique_ptr& instance) + [&result, entityId](AZStd::unique_ptr& entity) { - if (instance->GetId() != entity) + if (entity->GetId() != entityId) { return true; } else { - result = instance.get(); + result = entity.get(); return false; } }); return result; } - AZ::Entity* FindEntity(AZ::EntityId entity, AzFramework::Spawnable& source) + AZ::Entity* FindEntity(AZ::EntityId entityId, AzFramework::Spawnable& source) { - uint32_t index = AzToolsFramework::Prefab::SpawnableUtils::FindEntityIndex(entity, source); + uint32_t index = AzToolsFramework::Prefab::SpawnableUtils::FindEntityIndex(entityId, source); return index != InvalidEntityIndex ? source.GetEntities()[index].get() : nullptr; } template - AZStd::unique_ptr CloneEntity(AZ::EntityId entity, T& source) + AZStd::unique_ptr CloneEntity(AZ::EntityId entityId, T& source) { - AZ::Entity* target = Internal::FindEntity(entity, source); + AZ::Entity* target = Internal::FindEntity(entityId, source); AZ_Assert( target, "SpawnbleUtils were unable to locate entity with id %zu in Instance or Spawnable for cloning.", - aznumeric_cast(entity)); + aznumeric_cast(entityId)); auto clone = AZStd::make_unique(); static AZ::SerializeContext* sc = GetSerializeContext(); @@ -73,12 +73,12 @@ namespace AzToolsFramework::Prefab::SpawnableUtils return clone; } - AZStd::unique_ptr ReplaceEntityWithPlaceholder(AZ::EntityId entity, AzToolsFramework::Prefab::Instance& source) + AZStd::unique_ptr ReplaceEntityWithPlaceholder(AZ::EntityId entityId, AzToolsFramework::Prefab::Instance& source) { - auto&& [instance, alias] = source.FindInstanceAndAlias(entity); + auto&& [instance, alias] = source.FindInstanceAndAlias(entityId); AZ_Assert( instance, "SpawnbleUtils were unable to locate entity alias with id %zu in Instance '%s' for replacing.", - aznumeric_cast(entity), source.GetTemplateSourcePath().c_str()); + aznumeric_cast(entityId), source.GetTemplateSourcePath().c_str()); EntityOptionalReference entityData = instance->GetEntity(alias); AZ_Assert( @@ -88,17 +88,17 @@ namespace AzToolsFramework::Prefab::SpawnableUtils return instance->ReplaceEntity(AZStd::move(placeholder), alias); } - AZStd::unique_ptr ReplaceEntityWithPlaceholder(AZ::EntityId entity, AzFramework::Spawnable& source) + AZStd::unique_ptr ReplaceEntityWithPlaceholder(AZ::EntityId entityId, AzFramework::Spawnable& source) { - uint32_t index = AzToolsFramework::Prefab::SpawnableUtils::FindEntityIndex(entity, source); + uint32_t index = AzToolsFramework::Prefab::SpawnableUtils::FindEntityIndex(entityId, source); AZ_Assert( index != InvalidEntityIndex, "SpawnbleUtils were unable to locate entity alias with id %zu in Spawnable for replacing.", - aznumeric_cast(entity)); + aznumeric_cast(entityId)); AZStd::unique_ptr original = AZStd::move(source.GetEntities()[index]); AZ_Assert( original, "SpawnbleUtils were unable to locate entity with id %zu in Spawnable for replacing.", - aznumeric_cast(entity)); + aznumeric_cast(entityId)); source.GetEntities()[index] = AZStd::make_unique(original->GetId(), original->GetName()); @@ -107,7 +107,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils template AZStd::pair, AzFramework::Spawnable::EntityAliasType> ApplyAlias( - Source& source, AZ::EntityId entity, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType) + Source& source, AZ::EntityId entityId, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType) { namespace PCU = AzToolsFramework::Prefab::PrefabConversionUtils; using ResultPair = AZStd::pair, AzFramework::Spawnable::EntityAliasType>; @@ -118,14 +118,14 @@ namespace AzToolsFramework::Prefab::SpawnableUtils // No need to do anything as the alias is disabled. return ResultPair(nullptr, AzFramework::Spawnable::EntityAliasType::Disable); case PCU::EntityAliasType::OptionalReplace: - return ResultPair(CloneEntity(entity, source), AzFramework::Spawnable::EntityAliasType::Replace); + return ResultPair(CloneEntity(entityId, source), AzFramework::Spawnable::EntityAliasType::Replace); case PCU::EntityAliasType::Replace: - return ResultPair(ReplaceEntityWithPlaceholder(entity, source), AzFramework::Spawnable::EntityAliasType::Replace); + return ResultPair(ReplaceEntityWithPlaceholder(entityId, source), AzFramework::Spawnable::EntityAliasType::Replace); case PCU::EntityAliasType::Additional: ResultPair(AZStd::make_unique(AZ::Entity::MakeId()), AzFramework::Spawnable::EntityAliasType::Additional); case PCU::EntityAliasType::Merge: // Use the same entity id as the original entity so at runtime the entity ids can be verified to match. - ResultPair(AZStd::make_unique(entity), AzFramework::Spawnable::EntityAliasType::Merge); + ResultPair(AZStd::make_unique(entityId), AzFramework::Spawnable::EntityAliasType::Merge); default: AZ_Assert( false, "Invalid PrefabProcessorContext::EntityAliasType type (%i) provided.", aznumeric_cast(aliasType)); @@ -236,7 +236,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils } else { - AZ_Assert(false, "Entity with id %zu was not found in the source prefab.", static_cast(entity)); + AZ_Assert(false, "Entity with id %llu was not found in the source prefab.", static_cast(entity)); return nullptr; } } From 9eb7972e74934820d9f1cd4eed3eac88de041923 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Mon, 18 Oct 2021 20:48:22 -0700 Subject: [PATCH 076/177] bugfix: main window is deactived for tabbed window preventing the use from dragging tabbed windows issue: https://github.com/o3de/o3de/issues/4658 Signed-off-by: Michael Pollind --- .../AzQtComponents/Components/FancyDocking.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp index 7a76781cd7..99fbba065f 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp @@ -2790,10 +2790,12 @@ namespace AzQtComponents // a docking drag operation (e.g. popup dialog for new level), we // should cancel our drag operation because the mouse release event // will be lost since we lost focus - if (m_dropZoneState.dragging()) - { - clearDraggingState(); - } + #if !defined(AZ_PLATFORM_LINUX) + if (m_dropZoneState.dragging()) + { + clearDraggingState(); + } + #endif break; } } From b3c7bf47f2ad2068713d48c1e2804195dd8a634b Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Wed, 20 Oct 2021 08:52:35 -0700 Subject: [PATCH 077/177] Update Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp Signed-off-by: Michael Pollind mpollind@gmail.com Co-authored-by: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Signed-off-by: Michael Pollind --- .../AzQtComponents/AzQtComponents/Components/FancyDocking.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp index 99fbba065f..9b2cf6c040 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp @@ -2790,7 +2790,7 @@ namespace AzQtComponents // a docking drag operation (e.g. popup dialog for new level), we // should cancel our drag operation because the mouse release event // will be lost since we lost focus - #if !defined(AZ_PLATFORM_LINUX) + #if !defined(Q_OS_LINUX) if (m_dropZoneState.dragging()) { clearDraggingState(); From b42f1b022eb5dd51cc01d893a10e915035a83d10 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Sat, 23 Oct 2021 11:01:07 -0700 Subject: [PATCH 078/177] replace with ungrab mouse event instead of deactivate window Signed-off-by: Michael Pollind --- .../AzQtComponents/Components/FancyDocking.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp index 9b2cf6c040..8af6af0513 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp @@ -2785,17 +2785,15 @@ namespace AzQtComponents RepaintFloatingIndicators(); } break; - case QEvent::WindowDeactivate: + case QEvent::UngrabMouse: // If our main window is deactivated while we are in the middle of // a docking drag operation (e.g. popup dialog for new level), we // should cancel our drag operation because the mouse release event // will be lost since we lost focus - #if !defined(Q_OS_LINUX) - if (m_dropZoneState.dragging()) - { - clearDraggingState(); - } - #endif + if (m_dropZoneState.dragging()) + { + clearDraggingState(); + } break; } } From eddb867ddfbff7d77fd3d2119192e5e695532c56 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Thu, 28 Oct 2021 20:12:47 -0700 Subject: [PATCH 079/177] bugfix: change event to WindowBlocked for FancyDocking Signed-off-by: Michael Pollind --- .../AzQtComponents/AzQtComponents/Components/FancyDocking.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp index 8af6af0513..b6acd14a83 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp @@ -2785,7 +2785,7 @@ namespace AzQtComponents RepaintFloatingIndicators(); } break; - case QEvent::UngrabMouse: + case QEvent::WindowBlocked: // If our main window is deactivated while we are in the middle of // a docking drag operation (e.g. popup dialog for new level), we // should cancel our drag operation because the mouse release event From fbc35a8169dfcadaf622acc862791d6a3744073f Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Thu, 4 Nov 2021 22:09:11 -0700 Subject: [PATCH 080/177] Applied a fix from @jeremyong-az to get ThinObject transmission working. Also added a new test material for ThinObject transmission. Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- .../Atom/Features/PBR/BackLighting.azsli | 9 ++++-- ...rfaceScattering_Transmission_Thin.material | 29 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission_Thin.material diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/BackLighting.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/BackLighting.azsli index a4a747d42c..83cf79ed61 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/BackLighting.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/BackLighting.azsli @@ -50,8 +50,13 @@ float3 GetBackLighting(Surface surface, LightingData lightingData, float3 lightI // Thin object mode, using thin-film assumption proposed by Jimenez J. et al, 2010, "Real-Time Realistic Skin Translucency" // http://www.iryoku.com/translucency/downloads/Real-Time-Realistic-Skin-Translucency.pdf - result = shadowRatio ? float3(0.0, 0.0, 0.0) : TransmissionKernel(surface.transmission.thickness * transmissionParams.w, rcp(transmissionParams.xyz)) * - saturate(dot(-surface.normal, dirToLight)) * lightIntensity * shadowRatio; + float litRatio = 1.0 - shadowRatio; + if (litRatio) + { + result = TransmissionKernel(surface.transmission.thickness * transmissionParams.w, rcp(transmissionParams.xyz)) * + saturate(dot(-surface.normal, dirToLight)) * lightIntensity * litRatio; + } + break; } diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission_Thin.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission_Thin.material new file mode 100644 index 0000000000..ee9bb1f4a5 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/015_SubsurfaceScattering_Transmission_Thin.material @@ -0,0 +1,29 @@ +{ + "description": "", + "parentMaterial": "", + "materialType": "Materials/Types/EnhancedPBR.materialtype", + "materialTypeVersion": 4, + "properties": { + "baseColor": { + "color": [ + 0.027664607390761375, + 0.1926604062318802, + 0.013916227966547012, + 1.0 + ] + }, + "general": { + "doubleSided": true + }, + "subsurfaceScattering": { + "thickness": 0.20000000298023224, + "transmissionMode": "ThinObject", + "transmissionTint": [ + 0.009140154346823692, + 0.19806210696697235, + 0.01095597818493843, + 1.0 + ] + } + } +} \ No newline at end of file From 356fec54901a11f19eb48fa749b1a966001673e1 Mon Sep 17 00:00:00 2001 From: moraaar Date: Fri, 5 Nov 2021 15:17:00 +0000 Subject: [PATCH 081/177] bugfix: resolve crash with FBX Settings (#4813) (#4944) (#5365) -prevent export of ModuleInitISystem and ModuleShutdownISystem Signed-off-by: Michael Pollind Co-authored-by: Michael Pollind --- Code/Legacy/CryCommon/ISystem.h | 4 ++-- Code/Legacy/CryCommon/platform_impl.cpp | 4 ++-- Code/Legacy/CrySystem/SystemInit.cpp | 23 ----------------------- 3 files changed, 4 insertions(+), 27 deletions(-) diff --git a/Code/Legacy/CryCommon/ISystem.h b/Code/Legacy/CryCommon/ISystem.h index 948977d02a..dd29209b24 100644 --- a/Code/Legacy/CryCommon/ISystem.h +++ b/Code/Legacy/CryCommon/ISystem.h @@ -1121,8 +1121,8 @@ inline ISystem* GetISystem() // Description: // This function must be called once by each module at the beginning, to setup global pointers. -extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, const char* moduleName); -extern "C" AZ_DLL_EXPORT void ModuleShutdownISystem(ISystem* pSystem); +void ModuleInitISystem(ISystem* pSystem, const char* moduleName); +void ModuleShutdownISystem(ISystem* pSystem); extern "C" AZ_DLL_EXPORT void InjectEnvironment(void* env); extern "C" AZ_DLL_EXPORT void DetachEnvironment(); diff --git a/Code/Legacy/CryCommon/platform_impl.cpp b/Code/Legacy/CryCommon/platform_impl.cpp index a68a5150db..845a1101a4 100644 --- a/Code/Legacy/CryCommon/platform_impl.cpp +++ b/Code/Legacy/CryCommon/platform_impl.cpp @@ -74,7 +74,7 @@ void InitCRTHandlers() {} ////////////////////////////////////////////////////////////////////////// // This is an entry to DLL initialization function that must be called for each loaded module ////////////////////////////////////////////////////////////////////////// -extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, [[maybe_unused]] const char* moduleName) +void ModuleInitISystem(ISystem* pSystem, [[maybe_unused]] const char* moduleName) { if (gEnv) // Already registered. { @@ -96,7 +96,7 @@ extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, [[maybe_unused } // if pSystem } -extern "C" AZ_DLL_EXPORT void ModuleShutdownISystem([[maybe_unused]] ISystem* pSystem) +void ModuleShutdownISystem([[maybe_unused]] ISystem* pSystem) { // Unregister with AZ environment. AZ::Environment::Detach(); diff --git a/Code/Legacy/CrySystem/SystemInit.cpp b/Code/Legacy/CrySystem/SystemInit.cpp index ac0683cf1b..99cc92ee6a 100644 --- a/Code/Legacy/CrySystem/SystemInit.cpp +++ b/Code/Legacy/CrySystem/SystemInit.cpp @@ -173,8 +173,6 @@ void CryEngineSignalHandler(int signal) ////////////////////////////////////////////////////////////////////////// #if defined(WIN32) || defined(LINUX) || defined(APPLE) -# define DLL_MODULE_INIT_ISYSTEM "ModuleInitISystem" -# define DLL_MODULE_SHUTDOWN_ISYSTEM "ModuleShutdownISystem" # define DLL_INITFUNC_RENDERER "PackageRenderConstructor" # define DLL_INITFUNC_SOUND "CreateSoundSystem" # define DLL_INITFUNC_FONT "CreateCryFontInterface" @@ -188,8 +186,6 @@ void CryEngineSignalHandler(int signal) #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED) #undef AZ_RESTRICTED_SECTION_IMPLEMENTED #else -# define DLL_MODULE_INIT_ISYSTEM (LPCSTR)2 -# define DLL_MODULE_SHUTDOWN_ISYSTEM (LPCSTR)3 # define DLL_INITFUNC_RENDERER (LPCSTR)1 # define DLL_INITFUNC_RENDERER (LPCSTR)1 # define DLL_INITFUNC_SOUND (LPCSTR)1 @@ -445,18 +441,6 @@ AZStd::unique_ptr CSystem::LoadDLL(const char* dllName) return handle; } - ////////////////////////////////////////////////////////////////////////// - // After loading DLL initialize it by calling ModuleInitISystem - ////////////////////////////////////////////////////////////////////////// - AZStd::string moduleName = PathUtil::GetFileName(dllName); - - typedef void*(*PtrFunc_ModuleInitISystem)(ISystem* pSystem, const char* moduleName); - PtrFunc_ModuleInitISystem pfnModuleInitISystem = handle->GetFunction(DLL_MODULE_INIT_ISYSTEM); - if (pfnModuleInitISystem) - { - pfnModuleInitISystem(this, moduleName.c_str()); - } - return handle; } @@ -497,13 +481,6 @@ void CSystem::ShutdownModuleLibraries() #if !defined(AZ_MONOLITHIC_BUILD) for (auto iterator = m_moduleDLLHandles.begin(); iterator != m_moduleDLLHandles.end(); ++iterator) { - typedef void*( * PtrFunc_ModuleShutdownISystem )(ISystem* pSystem); - - PtrFunc_ModuleShutdownISystem pfnModuleShutdownISystem = iterator->second->GetFunction(DLL_MODULE_SHUTDOWN_ISYSTEM); - if (pfnModuleShutdownISystem) - { - pfnModuleShutdownISystem(this); - } if (iterator->second->IsLoaded()) { iterator->second->Unload(); From 9c28d134db2441b6c92eefdb839a8b68d1f8fb7c Mon Sep 17 00:00:00 2001 From: Ken Pruiksma Date: Fri, 5 Nov 2021 10:40:34 -0500 Subject: [PATCH 082/177] Fix for alignment issue with terrain heights. (#5274) Signed-off-by: Ken Pruiksma --- Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli index 72c1af953c..cd680f721a 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli @@ -218,9 +218,10 @@ float4x4 GetObject_WorldMatrix() float GetHeight(float2 origUv) { - float2 uv = clamp(origUv + (ObjectSrg::m_terrainData.m_uvStep * 0.5f), 0.0f, 1.0f); - float height = 0.0f; + float2 halfStep = ObjectSrg::m_terrainData.m_uvStep * 0.5; + float2 uv = origUv * (1.0 - ObjectSrg::m_terrainData.m_uvStep) + halfStep; + float height = 0.0f; if (o_useTerrainSmoothing) { float2 textureSize; From c0ece7d32d8e7ba68e3efadcd47a2f3024a01c78 Mon Sep 17 00:00:00 2001 From: Sean Masterson Date: Fri, 5 Nov 2021 09:49:59 -0700 Subject: [PATCH 083/177] Update to atom_constants.py docstring Signed-off-by: Sean Masterson --- .../Gem/PythonTests/Atom/atom_utils/atom_constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py index 0c3410ff33..70156b9375 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py @@ -85,8 +85,8 @@ class AtomComponentProperties: Deferred Fog component properties. Requires PostFX Layer component. - 'requires' a list of component names as strings required by this component. Use editor_entity_utils EditorEntity.add_components(list) to add this list of requirements.\n + - 'Enable Deferred Fog' Toggle active state of the component True/False :param property: From the last element of the property tree path. Default 'name' for component name string. - - 'Enable Deferred Fog' Toggle active state of the component True/False :return: Full property path OR component name if no property specified. """ properties = { From f8f9fb96d135df9b25083138239dc386182a82bf Mon Sep 17 00:00:00 2001 From: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com> Date: Fri, 5 Nov 2021 10:20:21 -0700 Subject: [PATCH 084/177] ATOM-15086 Image Pipeline Unexpectedly Pre-Multiplying Alpha into Color Channels (#5358) Applied the discard alpha in the begining of converting. Also deleted some unused processing settings and steps. Signed-off-by: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com> --- .../Atom/ImageProcessing/ImageObject.h | 10 - .../Source/BuilderSettings/PresetSettings.cpp | 9 - .../Source/BuilderSettings/PresetSettings.h | 15 +- .../Code/Source/Converters/ColorChart.cpp | 307 ------------------ .../Code/Source/Converters/Cubemap.cpp | 2 +- .../Code/Source/Converters/HighPass.cpp | 100 ------ .../Code/Source/Editor/PresetInfoPopup.cpp | 3 - .../Code/Source/ImageBuilderComponent.cpp | 2 +- .../Code/Source/Processing/ImageConvert.cpp | 182 +++-------- .../Code/Source/Processing/ImageConvert.h | 6 - .../Source/Processing/ImageConvertJob.cpp | 5 +- .../Code/Source/Processing/ImageFlags.h | 2 +- .../Source/Processing/ImageObjectImpl.cpp | 212 ------------ .../Code/Source/Processing/ImageObjectImpl.h | 5 - .../Code/Source/Processing/ImageToProcess.h | 7 - .../Code/Tests/ImageProcessing_Test.cpp | 1 - .../Code/imageprocessing_files.cmake | 2 - 17 files changed, 57 insertions(+), 813 deletions(-) delete mode 100644 Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/ColorChart.cpp delete mode 100644 Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/HighPass.cpp diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageObject.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageObject.h index 5796a0c84d..475c72c0eb 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageObject.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageObject.h @@ -100,13 +100,6 @@ namespace ImageProcessingAtom //compare whether two images are same. return true if they are same. virtual bool CompareImage(const IImageObjectPtr otherImage) const = 0; - // Writes this image to file used for runtime, overwrites any existing file. - // It may write alpha image as attached image into the same file - // outFilePaths will save filenames finally saved to since the image might be split and saved to multiple files - virtual bool SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector& outFilePaths) const = 0; - virtual bool SaveImage(AZ::IO::SystemFileStream& out) const = 0; - virtual bool SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const = 0; - //get total image data size in memory of all mipmaps. Not includs header and flags. virtual AZ::u32 GetTextureMemory() const = 0; @@ -135,9 +128,6 @@ namespace ImageProcessingAtom // The algorithm is based on the Frequency Domain Normal Mapping implementation presented by Neubelt and Pettineo at Siggraph 2013. virtual void GlossFromNormals(bool hasAuthoredGloss) = 0; - //convert gloss map from legacy distribution to new one. New World is still using legacy gloss map. - virtual void ConvertLegacyGloss() = 0; - //clear image with color virtual void ClearColor(float r, float g, float b, float a) = 0; }; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.cpp index 3812b86026..046c4faa87 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.cpp @@ -45,10 +45,7 @@ namespace ImageProcessingAtom ->Field("MinTextureSize", &PresetSettings::m_minTextureSize) ->Field("IsPowerOf2", &PresetSettings::m_isPowerOf2) ->Field("SizeReduceLevel", &PresetSettings::m_sizeReduceLevel) - ->Field("IsColorChart", &PresetSettings::m_isColorChart) - ->Field("HighPassMip", &PresetSettings::m_highPassMip) ->Field("GlossFromNormal", &PresetSettings::m_glossFromNormals) - ->Field("UseLegacyGloss", &PresetSettings::m_isLegacyGloss) ->Field("MipRenormalize", &PresetSettings::m_isMipRenormalize) ->Field("NumberResidentMips", &PresetSettings::m_numResidentMips) ->Field("Swizzle", &PresetSettings::m_swizzle) @@ -200,10 +197,7 @@ namespace ImageProcessingAtom m_maxTextureSize == other.m_maxTextureSize && m_isPowerOf2 == other.m_isPowerOf2 && m_sizeReduceLevel == other.m_sizeReduceLevel && - m_isColorChart == other.m_isColorChart && - m_highPassMip == other.m_highPassMip && m_glossFromNormals == other.m_glossFromNormals && - m_isLegacyGloss == other.m_isLegacyGloss && m_swizzle == other.m_swizzle && m_isMipRenormalize == other.m_isMipRenormalize && m_numResidentMips == other.m_numResidentMips; @@ -239,10 +233,7 @@ namespace ImageProcessingAtom m_maxTextureSize = other.m_maxTextureSize; m_isPowerOf2 = other.m_isPowerOf2; m_sizeReduceLevel = other.m_sizeReduceLevel; - m_isColorChart = other.m_isColorChart; - m_highPassMip = other.m_highPassMip; m_glossFromNormals = other.m_glossFromNormals; - m_isLegacyGloss = other.m_isLegacyGloss; m_swizzle = other.m_swizzle; m_isMipRenormalize = other.m_isMipRenormalize; m_numResidentMips = other.m_numResidentMips; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.h index 941437bbf4..3dc223cf80 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/PresetSettings.h @@ -84,16 +84,7 @@ namespace ImageProcessingAtom //settings for mipmap generation. it's null if this preset disable mipmap. AZStd::unique_ptr m_mipmapSetting; - - //some specific settings - // "colorchart". This is to indicate if need to extract color chart from the image and output the color chart data. - // This is very specific usage for cryEngine. Check ColorChart.cpp for better explanation. - bool m_isColorChart = false; - - //"highpass". Defines which mip level is subtracted when applying the high pass filter - //this is only used for terrain asset. we might remove it later since it can be done with source image directly - AZ::u32 m_highPassMip = 0; - + //"glossfromnormals". Bake normal variance into smoothness stored in alpha channel AZ::u32 m_glossFromNormals = 0; @@ -109,10 +100,6 @@ namespace ImageProcessingAtom //that add up to 64K or lower AZ::u8 m_numResidentMips = 0; - //legacy options might be removed later - //"glosslegacydist". If the gloss map use legacy distribution. NW is still using legacy dist - bool m_isLegacyGloss = false; - //"swizzle". need to be 4 character and each character need to be one of "rgba01" AZStd::string m_swizzle; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/ColorChart.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/ColorChart.cpp deleted file mode 100644 index b6e9cf4c8c..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/ColorChart.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -#include -#include - -namespace ImageProcessingAtom -{ - const int COLORCHART_IMAGE_WIDTH = 78; - const int COLORCHART_IMAGE_HEIGHT = 66; - - // color chart in cry engine is a special image data, with size 78x66, you may see in game screenshot which is defined by a rectangle - // area with a yellow-black dash line boarder - // Create color chart function is to read that block of image data and convert it to a color table then save it to another image - // with size 256x16. - - class C3dLutColorChart - { - public: - C3dLutColorChart() {} - ~C3dLutColorChart() {}; - - //generate default color chart data - void GenerateDefault(); - - //generate color chart data from input image - bool GenerateFromInput(IImageObjectPtr image); - - //ouput the color chart data to an image object - IImageObjectPtr GenerateChartImage(); - - protected: - //extract color chart data from specified location in an image - void ExtractFromImageAt(IImageObjectPtr pImg, AZ::u32 x, AZ::u32 y); - - //find color chart location in an image - static bool FindColorChart(const IImageObjectPtr pImg, AZ::u32& outLocX, AZ::u32& outLocY); - - //if there is a color chart at specified location - static bool IsColorChartAt(AZ::u32 x, AZ::u32 y, void* pData, AZ::u32 pitch); - - private: - enum EPrimaryShades - { - ePS_Red = 16, - ePS_Green = 16, - ePS_Blue = 16, - - ePS_NumColors = ePS_Red * ePS_Green * ePS_Blue - }; - - struct SColor - { - unsigned char r, g, b, _padding; - }; - - typedef AZStd::vector ColorMapping; - - ColorMapping m_mapping; - }; - - void C3dLutColorChart::GenerateDefault() - { - m_mapping.reserve(ePS_NumColors); - - for (int b = 0; b < ePS_Blue; ++b) - { - for (int g = 0; g < ePS_Green; ++g) - { - for (int r = 0; r < ePS_Red; ++r) - { - SColor col; - col.r = static_cast(255 * r / (ePS_Red)); - col.g = static_cast(255 * g / (ePS_Green)); - col.b = static_cast(255 * b / (ePS_Blue)); - int l = 255 - (col.r * 3 + col.g * 6 + col.b) / 10; - col.r = col.g = col.b = (unsigned char)l; - m_mapping.push_back(col); - } - } - } - } - - //find color chart location in a image - bool C3dLutColorChart::FindColorChart(const IImageObjectPtr pImg, AZ::u32& outLocX, AZ::u32& outLocY) - { - const AZ::u32 width = pImg->GetWidth(0); - const AZ::u32 height = pImg->GetHeight(0); - - //the origin image is too small to have a color chart - if (width < COLORCHART_IMAGE_WIDTH || height < COLORCHART_IMAGE_HEIGHT) - { - return false; - } - - AZ::u8* pData; - AZ::u32 pitch; - pImg->GetImagePointer(0, pData, pitch); - - //check all the posible start location on whether there might be a color chart - for (AZ::u32 y = 0; y <= height - COLORCHART_IMAGE_HEIGHT; ++y) - { - for (AZ::u32 x = 0; x <= width - COLORCHART_IMAGE_WIDTH; ++x) - { - if (IsColorChartAt(x, y, pData, pitch)) - { - outLocX = x; - outLocY = y; - return true; - } - } - } - - return false; - } - - bool C3dLutColorChart::GenerateFromInput(IImageObjectPtr image) - { - AZ::u32 outLocX, outLocY; - if (FindColorChart(image, outLocX, outLocY)) - { - ExtractFromImageAt(image, outLocX, outLocY); - return true; - } - return false; - } - - IImageObjectPtr C3dLutColorChart::GenerateChartImage() - { - IImageObjectPtr image(IImageObject::CreateImage(ePS_Red* ePS_Blue, ePS_Green, 1, ePixelFormat_R8G8B8A8)); - - { - AZ::u8* pData; - AZ::u32 pitch; - image->GetImagePointer(0, pData, pitch); - - size_t nSlicePitch = (pitch / ePS_Blue); - AZ::u32 src = 0; - for (int b = 0; b < ePS_Blue; ++b) - { - for (int g = 0; g < ePS_Green; ++g) - { - AZ::u8* p = pData + g * pitch + b * nSlicePitch; - for (int r = 0; r < ePS_Red; ++r) - { - const SColor& c = m_mapping[src]; - p[0] = c.r; - p[1] = c.g; - p[2] = c.b; - p[3] = 255; - ++src; - p += 4; - } - } - } - } - - return image; - } - - void C3dLutColorChart::ExtractFromImageAt(IImageObjectPtr image, AZ::u32 x, AZ::u32 y) - { - int ox = x + 1; - int oy = y + 1; - - AZ::u8* pData; - AZ::u32 pitch; - image->GetImagePointer(0, pData, pitch); - - m_mapping.reserve(ePS_NumColors); - - for (int b = 0; b < ePS_Blue; ++b) - { - int px = ox + ePS_Red * (b % 4); - int py = oy + ePS_Green * (b / 4); - - for (int g = 0; g < ePS_Green; ++g) - { - for (int r = 0; r < ePS_Red; ++r) - { - AZ::u8* p = pData + pitch * (py + g) + (px + r) * 4; - - SColor col; - col.r = p[0]; - col.g = p[1]; - col.b = p[2]; - m_mapping.push_back(col); - } - } - } - } - - //check if image data at location x and y could be a color chart - //based on if the boarder is dash lines with two pixel each segement - //the idea and implementation are both coming from CryEngine. - bool C3dLutColorChart::IsColorChartAt(AZ::u32 x, AZ::u32 y, void* pData, AZ::u32 pitch) - { - struct Color - { - private: - int c[3]; - - public: - Color(AZ::u32 x, AZ::u32 y, void* pPixels, AZ::u32 pitch) - { - const uint8* p = (const uint8*)pPixels + pitch * y + x * 4; - c[0] = p[0]; - c[1] = p[1]; - c[2] = p[2]; - } - - bool isSimilar(const Color& a, int maxDiff) const - { - return - abs(a.c[0] - c[0]) <= maxDiff && - abs(a.c[1] - c[1]) <= maxDiff && - abs(a.c[2] - c[2]) <= maxDiff; - } - }; - - const Color colorRef[2] = - { - Color(x, y, pData, pitch), - Color(x + 2, y, pData, pitch) - }; - - // We require two colors of the border to be at least a bit different - if (colorRef[0].isSimilar(colorRef[1], 15)) - { - return false; - } - - static const int kMaxDiff = 3; - - int refIdx = 0; - //rectangle's top - for (int i = 0; i < COLORCHART_IMAGE_WIDTH; i += 2) - { - if (!colorRef[refIdx].isSimilar(Color(x + i, y, pData, pitch), kMaxDiff) || - !colorRef[refIdx].isSimilar(Color(x + i + 1, y, pData, pitch), kMaxDiff)) - { - return false; - } - refIdx ^= 1; - } - - refIdx = 0; - //left - for (int i = 0; i < COLORCHART_IMAGE_HEIGHT; i += 2) - { - if (!colorRef[refIdx].isSimilar(Color(x, y + i, pData, pitch), kMaxDiff) || - !colorRef[refIdx].isSimilar(Color(x, y + i + 1, pData, pitch), kMaxDiff)) - { - return false; - } - refIdx ^= 1; - } - - refIdx = 0; - //right - for (int i = 0; i < COLORCHART_IMAGE_HEIGHT; i += 2) - { - if (!colorRef[refIdx].isSimilar(Color(x + COLORCHART_IMAGE_WIDTH - 1, y + i, pData, pitch), kMaxDiff) || - !colorRef[refIdx].isSimilar(Color(x + COLORCHART_IMAGE_WIDTH - 1, y + i + 1, pData, pitch), kMaxDiff)) - { - return false; - } - refIdx ^= 1; - } - - refIdx = 0; - //bottom - for (int i = 0; i < COLORCHART_IMAGE_WIDTH; i += 2) - { - if (!colorRef[refIdx].isSimilar(Color(x + i, y + COLORCHART_IMAGE_HEIGHT - 1, pData, pitch), kMaxDiff) || - !colorRef[refIdx].isSimilar(Color(x + i + 1, y + COLORCHART_IMAGE_HEIGHT - 1, pData, pitch), kMaxDiff)) - { - return false; - } - refIdx ^= 1; - } - - return true; - } - - - void ImageToProcess::CreateColorChart() - { - C3dLutColorChart colorChart; - - //get color chart data from source image. - if (!colorChart.GenerateFromInput(m_img)) - { - //if load from image failed then generate default color data - colorChart.GenerateDefault(); - } - - //save color chart data to an image and save as current - m_img = colorChart.GenerateChartImage(); - } -} diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/Cubemap.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/Cubemap.cpp index 4f13c6f9bf..d373f399e1 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/Cubemap.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/Cubemap.cpp @@ -547,7 +547,7 @@ namespace ImageProcessingAtom } //generate box filtered source image mip chain - IImageObjectPtr mippedSourceImage(IImageObject::CreateImage(outWidth, outHeight, maxMipCount, ePixelFormat_R32G32B32A32F)); + IImageObjectPtr mippedSourceImage(IImageObject::CreateImage(outWidth, outHeight, maxMipCount, srcPixelFormat)); mippedSourceImage->CopyPropertiesFrom(m_image->Get()); for (int iSide = 0; iSide < 6; ++iSide) diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/HighPass.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/HighPass.cpp deleted file mode 100644 index e2071fb586..0000000000 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/HighPass.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -#include -#include -#include -#include -#include -#include - -namespace ImageProcessingAtom -{ - // higher mip level is subtracted by lower mip level when applying the [cheap] high pass filter - void ImageToProcess::CreateHighPass(AZ::u32 dwMipDown) - { - //no need to convert if mip go down 0 - if (dwMipDown == 0) - { - return; - } - - const EPixelFormat ePixelFormat = m_img->GetPixelFormat(); - - if (ePixelFormat != ePixelFormat_R32G32B32A32F) - { - AZ_Assert(false, "You need convert the orginal image to ePixelFormat_R32G32B32A32F before call this function"); - return; - } - - - AZ::u32 dwWidth, dwHeight, dwMips; - dwWidth = m_img->GetWidth(0); - dwHeight = m_img->GetHeight(0); - dwMips = m_img->GetMipCount(); - - if (dwMipDown >= dwMips) - { - AZ_Warning("Image Processing", false, "CreateHighPass can't go down %i MIP levels for high pass as there are not\ - enough MIP levels available, going down by %i instead", dwMipDown, dwMips - 1); - dwMipDown = dwMips - 1; - } - - IImageObjectPtr newImage(IImageObject::CreateImage(dwWidth, dwHeight, dwMips, ePixelFormat)); - newImage->CopyPropertiesFrom(m_img); - - IPixelOperationPtr pixelOp = CreatePixelOperation(ePixelFormat); - AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(ePixelFormat)->bitsPerBlock / 8; - - AZ::u32 dstMips = newImage->GetMipCount(); - for (AZ::u32 dstMip = 0; dstMip < dwMipDown; ++dstMip) - { - // linear interpolation - FilterImage(MipGenType::triangle, MipGenEvalType::sum, 0.0f, 0.0f, m_img, dwMipDown, newImage, dstMip, NULL, NULL); - - //substraction - AZ::u8* srcPixelBuf; - AZ::u32 srcPitch; - m_img->GetImagePointer(dstMip, srcPixelBuf, srcPitch); - AZ::u8* dstPixelBuf; - AZ::u32 dstPitch; - newImage->GetImagePointer(dstMip, dstPixelBuf, dstPitch); - const AZ::u32 pixelCount = newImage->GetPixelCount(dstMip); - - for (AZ::u32 i = 0; i < pixelCount; ++i, srcPixelBuf += pixelBytes, dstPixelBuf += pixelBytes) - { - float r1, g1, b1, a1, r2, g2, b2, a2; - pixelOp->GetRGBA(srcPixelBuf, r1, g1, b1, a1); - pixelOp->GetRGBA(dstPixelBuf, r2, g2, b2, a2); - - r2 = AZ::GetClamp(r1 - r2 + 0.5f, 0.0f, 1.0f); - g2 = AZ::GetClamp(g1 - g2 + 0.5f, 0.0f, 1.0f); - b2 = AZ::GetClamp(b1 - b2 + 0.5f, 0.0f, 1.0f); - a2 = AZ::GetClamp(a1 - a2 + 0.5f, 0.0f, 1.0f); - pixelOp->SetRGBA(dstPixelBuf, r2, g2, b2, a2); - } - } - - // mips below the chosen highpass mip are grey - for (AZ::u32 dstMip = dwMipDown; dstMip < dstMips; ++dstMip) - { - AZ::u8* dstPixelBuf; - AZ::u32 dstPitch; - newImage->GetImagePointer(dstMip, dstPixelBuf, dstPitch); - const AZ::u32 pixelCount = newImage->GetPixelCount(dstMip); - - for (AZ::u32 i = 0; i < pixelCount; ++i, dstPixelBuf += pixelBytes) - { - pixelOp->SetRGBA(dstPixelBuf, 0.5f, 0.5f, 0.5f, 1.0f); - } - } - - m_img = newImage; - } -} // namespace ImageProcessingAtom diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/PresetInfoPopup.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/PresetInfoPopup.cpp index a5b942fa58..fe5703ceb6 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/PresetInfoPopup.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/PresetInfoPopup.cpp @@ -82,10 +82,7 @@ namespace ImageProcessingAtomEditor presetInfoText += "\n"; presetInfoText += QString("Suppress Engine Reduce: %1\n").arg(presetSettings->m_suppressEngineReduce ? "True" : "False"); presetInfoText += QString("Discard Alpha: %1\n").arg(presetSettings->m_discardAlpha ? "True" : "False"); - presetInfoText += QString("Is Color Chart: %1\n").arg(presetSettings->m_isColorChart ? "True" : "False"); - presetInfoText += QString("High Pass Mip: %1\n").arg(presetSettings->m_highPassMip); presetInfoText += QString("Gloss From Normal: %1\n").arg(presetSettings->m_glossFromNormals); - presetInfoText += QString("Use Legacy Gloss: %1\n").arg(presetSettings->m_isLegacyGloss ? "True" : "False"); presetInfoText += QString("Mip Re-normalize: %1\n").arg(presetSettings->m_isMipRenormalize ? "True" : "False"); presetInfoText += QString("Resident Mips Number: %1\n").arg(presetSettings->m_numResidentMips); presetInfoText += QString("Swizzle: %1\n").arg(presetSettings->m_swizzle.c_str()); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp index a489c8da5e..90d45ae65a 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp @@ -74,7 +74,7 @@ namespace ImageProcessingAtom builderDescriptor.m_busId = azrtti_typeid(); builderDescriptor.m_createJobFunction = AZStd::bind(&ImageBuilderWorker::CreateJobs, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); builderDescriptor.m_processJobFunction = AZStd::bind(&ImageBuilderWorker::ProcessJob, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); - builderDescriptor.m_version = 25; // [ATOM-16575] + builderDescriptor.m_version = 26; // [ATOM-15086] builderDescriptor.m_analysisFingerprint = ImageProcessingAtom::BuilderSettingManager::Instance()->GetAnalysisFingerprint(); m_imageBuilder.BusConnect(builderDescriptor.m_busId); AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDescriptor); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp index 977359e4f6..5b23009cff 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp @@ -46,7 +46,6 @@ namespace ImageProcessingAtom enum ConvertStep { StepValidateInput = 0, - StepGenerateColorChart, StepConvertToLinear, StepSwizzle, StepCubemapLayout, @@ -55,9 +54,7 @@ namespace ImageProcessingAtom StepMipmap, StepGlossFromNormal, StepPostNormalize, - StepCreateHighPass, StepConvertOutputColorSpace, - StepAlphaImage, StepConvertPixelFormat, StepSaveToFile, StepAll @@ -66,7 +63,6 @@ namespace ImageProcessingAtom [[maybe_unused]] const char ProcessStepNames[StepAll][64] = { "ValidateInput", - "GenerateColorChart", "ConvertToLinear", "Swizzle", "CubemapLayout", @@ -75,9 +71,7 @@ namespace ImageProcessingAtom "Mipmap", "GlossFromNormal", "PostNormalize", - "CreateHighPass", "ConvertOutputColorSpace", - "AlphaImage", "ConvertPixelFormat", "SaveToFile", }; @@ -94,11 +88,6 @@ namespace ImageProcessingAtom return nullptr; } - IImageObjectPtr ImageConvertProcess::GetOutputAlphaImage() - { - return m_alphaImage; - } - IImageObjectPtr ImageConvertProcess::GetOutputIBLSpecularCubemap() { return m_iblSpecularCubemapImage; @@ -180,6 +169,58 @@ namespace ImageProcessingAtom m_image = new ImageToProcess(IImageObjectPtr(m_input->m_inputImage->Clone(mipsToClone))); } + break; + case StepConvertToLinear: + // convert to linear space and the output image pixel format should be rgba32f + ConvertToLinear(); + break; + case StepSwizzle: + { + // swizzle if swizzle was set or decard alpha + bool swizzleWasSet = m_input->m_presetSetting.m_swizzle.size() >= 4; + if (swizzleWasSet || m_input->m_presetSetting.m_discardAlpha) + { + AZStd::string swizzle = "rgba"; + if (swizzleWasSet) + { + swizzle = m_input->m_presetSetting.m_swizzle.substr(0, 4); + } + + if (m_input->m_presetSetting.m_discardAlpha) + { + swizzle[3] = '1'; + } + + m_image->Get()->Swizzle(swizzle.c_str()); + if (!m_input->m_presetSetting.m_discardAlpha) + { + m_alphaContent = EAlphaContent::eAlphaContent_Absent; + } + else + { + m_alphaContent = m_image->Get()->GetAlphaContent(); + } + } + } + break; + case StepCubemapLayout: + // convert cubemap image's layout to vertical strip used in game. + if (IsConvertToCubemap()) + { + if (!m_image->ConvertCubemapLayout(CubemapLayoutVertical)) + { + m_image->Set(nullptr); + } + } + break; + case StepPreNormalize: + // normalize base image before mipmap generation if glossfromnormals is enabled and require normalize + if (m_input->m_presetSetting.m_isMipRenormalize && m_input->m_presetSetting.m_glossFromNormals) + { + // Normalize the base mip map. This has to be done explicitly because we need to disable mip renormalization to + // preserve the normal length when deriving the normal variance + m_image->Get()->NormalizeVectors(0, 1); + } break; case StepGenerateIBL: if (IsConvertToCubemap()) @@ -204,56 +245,6 @@ namespace ImageProcessingAtom m_isFinished = true; } break; - case StepGenerateColorChart: - // GenerateColorChart. - if (m_input->m_presetSetting.m_isColorChart) - { - // Convert to uncompressed format if it's compressed format. For example, loaded from DDS file. - if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_image->Get()->GetPixelFormat())) - { - m_image->ConvertFormat(ePixelFormat_R32G32B32A32F); - } - - m_image->CreateColorChart(); - } - break; - case StepConvertToLinear: - // convert to linear space and the output image pixel format should be rgba32f - ConvertToLinear(); - break; - case StepSwizzle: - // convert texture format. - if (m_input->m_presetSetting.m_swizzle.size() >= 4) - { - m_image->Get()->Swizzle(m_input->m_presetSetting.m_swizzle.substr(0, 4).c_str()); - m_alphaContent = m_image->Get()->GetAlphaContent(); - } - - // convert gloss map (alhpa channel) from legacy distribution to new one - if (m_input->m_presetSetting.m_isLegacyGloss) - { - m_image->Get()->ConvertLegacyGloss(); - } - break; - case StepCubemapLayout: - // convert cubemap image's layout to vertical strip used in game. - if (IsConvertToCubemap()) - { - if (!m_image->ConvertCubemapLayout(CubemapLayoutVertical)) - { - m_image->Set(nullptr); - } - } - break; - case StepPreNormalize: - // normalize base image before mipmap generation if glossfromnormals is enabled and require normalize - if (m_input->m_presetSetting.m_isMipRenormalize && m_input->m_presetSetting.m_glossFromNormals) - { - // Normalize the base mip map. This has to be done explicitly because we need to disable mip renormalization to - // preserve the normal length when deriving the normal variance - m_image->Get()->NormalizeVectors(0, 1); - } - break; case StepMipmap: // generate mipmaps if (IsConvertToCubemap()) @@ -304,20 +295,10 @@ namespace ImageProcessingAtom m_image->Get()->AddImageFlags(EIF_RenormalizedTexture); } break; - case StepCreateHighPass: - if (m_input->m_presetSetting.m_highPassMip > 0) - { - m_image->CreateHighPass(m_input->m_presetSetting.m_highPassMip); - } - break; case StepConvertOutputColorSpace: // convert image from linear space to desired output color space ConvertToOuputColorSpace(); break; - case StepAlphaImage: - // save alpha channel to separate image if it's needed - CreateAlphaImage(); - break; case StepConvertPixelFormat: // convert pixel format ConvertPixelformat(); @@ -411,12 +392,6 @@ namespace ImageProcessingAtom return; } - // don't do any reduce for color chart - if (presetSettings->m_isColorChart) - { - return; - } - // get suitable size for dest pixel format CPixelFormats::GetInstance().GetSuitableImageSize(presetSettings->m_pixelFormat, inputWidth, inputHeight, outWidth, outHeight); @@ -510,52 +485,6 @@ namespace ImageProcessingAtom return true; } - void ImageConvertProcess::CreateAlphaImage() - { - // if alpha content doesn't have alpha or we need to discard alpha, skip - // we won't create alpha image for cubemap too - if (m_alphaContent == EAlphaContent::eAlphaContent_Absent - || m_alphaContent == EAlphaContent::eAlphaContent_OnlyWhite - || m_input->m_presetSetting.m_discardAlpha || IsConvertToCubemap()) - { - return; - } - - // if dest format could save alpha, skip too - if (!CPixelFormats::GetInstance().IsPixelFormatWithoutAlpha(m_input->m_presetSetting.m_pixelFormat)) - { - return; - } - - // now create alpha image - ImageToProcess alphaImage(m_image->Get()); - alphaImage.ConvertFormat(ePixelFormat_A8); - - // validate pixelformatalpha - if (CPixelFormats::GetInstance().IsFormatSingleChannel(m_input->m_presetSetting.m_pixelFormatAlpha)) - { - alphaImage.ConvertFormat(m_input->m_presetSetting.m_pixelFormatAlpha); - } - else - { - //For ASTC compression we need to clear out the alpha to get accurate rgb compression. - if (IsASTCFormat(m_input->m_presetSetting.m_pixelFormat)) - { - alphaImage.ConvertFormat(ePixelFormat_R8G8B8X8); - alphaImage.ConvertFormat(m_input->m_presetSetting.m_pixelFormatAlpha); - } - else - { - AZ_Assert(false, "PixelFormatAlpha only supports single channel pixel formats or ASTC formats"); - } - } - - // get final result and save it to member variable for later use - m_alphaImage = alphaImage.Get(); - - m_image->Get()->AddImageFlags(EIF_AttachedAlpha); - } - // pixel format conversion bool ImageConvertProcess::ConvertPixelformat() { @@ -575,12 +504,6 @@ namespace ImageProcessingAtom m_image->GetCompressOption().rgbWeight = m_input->m_presetSetting.GetColorWeight(); m_image->GetCompressOption().discardAlpha = m_input->m_presetSetting.m_discardAlpha; - //For ASTC compression we need to clear out the alpha to get accurate rgb compression. - if(m_alphaImage && IsASTCFormat(m_input->m_presetSetting.m_pixelFormat)) - { - m_image->GetCompressOption().discardAlpha = true; - } - m_image->ConvertFormat(m_input->m_presetSetting.m_pixelFormat); return true; @@ -762,7 +685,6 @@ namespace ImageProcessingAtom if (ImageProcess##PrivateName::DoesSupport(m_input->m_platform)) \ { \ ImageProcess##PrivateName::PrepareImageForExport(m_image->Get()); \ - ImageProcess##PrivateName::PrepareImageForExport(m_alphaImage); \ } AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS #undef AZ_RESTRICTED_PLATFORM_EXPANSION diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h index eaf05c3280..fe57b1a89f 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h @@ -115,7 +115,6 @@ namespace ImageProcessingAtom //get output images IImageObjectPtr GetOutputImage(); - IImageObjectPtr GetOutputAlphaImage(); IImageObjectPtr GetOutputIBLSpecularCubemap(); IImageObjectPtr GetOutputIBLDiffuseCubemap(); @@ -131,8 +130,6 @@ namespace ImageProcessingAtom //for alpha //to indicate the current alpha channel content EAlphaContent m_alphaContent; - //An image object to hold alpha channel in a separate image - IImageObjectPtr m_alphaImage; //output results of IBL cubemap generation, used in unit tests IImageObjectPtr m_iblSpecularCubemapImage; @@ -171,9 +168,6 @@ namespace ImageProcessingAtom //convert to output color space before compression bool ConvertToOuputColorSpace(); - //create alpha image if it's needed - void CreateAlphaImage(); - //pixel format convertion/compression bool ConvertPixelformat(); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.cpp index 1465e7993f..0e4521ab24 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvertJob.cpp @@ -108,17 +108,14 @@ namespace ImageProcessingAtom } IImageObjectPtr outputImage = m_process->GetOutputImage(); - IImageObjectPtr outputImageAlpha = m_process->GetOutputAlphaImage(); m_output->SetOutputImage(outputImage, ImageConvertOutput::Base); - m_output->SetOutputImage(outputImageAlpha, ImageConvertOutput::Alpha); if (!IsJobCancelled()) { // For preview, combine image output with alpha if any m_output->SetProgress(1.0f / static_cast(m_previewProcessStep)); - IImageObjectPtr combinedImage = MergeOutputImageForPreview(outputImage, outputImageAlpha); - m_output->SetOutputImage(combinedImage, ImageConvertOutput::Preview); + m_output->SetOutputImage(outputImage, ImageConvertOutput::Preview); } m_output->SetReady(true); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h index 99e752c90a..d25533cccd 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageFlags.h @@ -20,7 +20,7 @@ namespace ImageProcessingAtom const static AZ::u32 EIF_Greyscale = 0x8; // hint for the engine (e.g. greyscale light beams can be applied to shadow mask), can be for DXT1 because compression artfacts don't count as color const static AZ::u32 EIF_SupressEngineReduce = 0x10; // info for the engine: don't reduce texture resolution on this texture const static AZ::u32 EIF_UNUSED_BIT = 0x40; // Free to use - const static AZ::u32 EIF_AttachedAlpha = 0x400; // info for the engine: it's a texture with attached alpha channel + const static AZ::u32 EIF_AttachedAlpha = 0x400; // deprecated: info for the engine: it's a texture with attached alpha channel const static AZ::u32 EIF_SRGBRead = 0x800; // info for the engine: if gamma corrected rendering is on, this texture requires SRGBRead (it's not stored in linear) const static AZ::u32 EIF_DontResize = 0x8000; // info for the engine: for dds textures that shouldn't be resized const static AZ::u32 EIF_RenormalizedTexture = 0x10000; // info for the engine: for dds textures that have renormalized color range diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.cpp index 8504f5e074..465d992ef6 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.cpp @@ -316,130 +316,6 @@ namespace ImageProcessingAtom m_mips.clear(); } - //note: there are some unreasonable parts of the save files formats for cry textures. We might need to rethink about - // it for new renderer - bool CImageObject::SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector& outFilePaths) const - { - AZ::IO::SystemFile file; - file.Open(filename, AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY); - - AZ::IO::SystemFileStream fileSaveStream(&file, true); - if (!fileSaveStream.IsOpen()) - { - AZ_Warning("Image Processing", false, "%s: failed to create file %s", __FUNCTION__, filename); - return false; - } - - if (alphaImage) - { - AZ_Assert(HasImageFlags(EIF_AttachedAlpha), "attached alpha image flag wasn't set"); - AZ_Assert(!alphaImage->HasImageFlags(EIF_AttachedAlpha), "alpha image shouldn't have attached alpha image flag"); - - // inherit cubemap and decal image flags to attached alpha image - alphaImage->AddImageFlags(GetImageFlags() & (EIF_Cubemap - | EIF_Decal | EIF_Splitted)); - alphaImage->SetNumPersistentMips(m_numPersistentMips); - } - - bool bOk = SaveImage(fileSaveStream); - bool hasSplitFlag = HasImageFlags(EIF_Splitted); - - //append alpha image data in the end if there is no split - if (bOk && alphaImage && !hasSplitFlag) - { - //4 bytes extension tag, 4 bytes attached alpha tag, then 4 bytes of chunk size - fileSaveStream.Write(sizeof(FOURCC_CExt), &FOURCC_CExt); // marker for the start of O3DE Extended data - fileSaveStream.Write(sizeof(FOURCC_AttC), &FOURCC_AttC); // Attached Channel chunk - - uint32_t size = 0; - uint32_t sizeBytes = sizeof(size); - fileSaveStream.Write(sizeBytes, &size); //size of attached chunk - - //save alpha image and get the size - AZ::IO::SizeType startPos = fileSaveStream.GetCurPos(); - bOk = alphaImage->SaveImage(fileSaveStream); - AZ::IO::SizeType endPos = fileSaveStream.GetCurPos(); - size = static_cast(endPos - startPos); - - //move back to beginning of chunk and write chunk size then move back to end - fileSaveStream.Seek(startPos - sizeBytes, AZ::IO::GenericStream::ST_SEEK_BEGIN); - fileSaveStream.Write(sizeBytes, &size); - fileSaveStream.Seek(endPos, AZ::IO::GenericStream::ST_SEEK_BEGIN); - - // marker for the end of O3DE Extended data - fileSaveStream.Write(sizeof(FOURCC_CEnd), &FOURCC_CEnd); - } - - if (!bOk) - { - AZ::IO::SystemFile::Delete(filename); - return false; - } - - // It's important to maintain the product output sequence. Asset Database/Browser will use the first product to determine the source type! - outFilePaths.push_back(filename); - - // save stand alone products - if (hasSplitFlag) - { - // alpha - if (alphaImage) - { - AZStd::string alphaFile = AZStd::string::format("%s.a", filename); - - AZ::IO::SystemFile outAlphaFile; - outAlphaFile.Open(alphaFile.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY); - - AZ::IO::SystemFileStream alphaFileSaveStream(&outAlphaFile, true); - - if (alphaFileSaveStream.IsOpen()) - { - alphaImage->SaveImage(alphaFileSaveStream); - outFilePaths.push_back(alphaFile); - } - else - { - AZ_Warning("Image Processing", false, "%s: failed to create file %s", __FUNCTION__, alphaFile.c_str()); - } - } - - // mips - AZ::u32 numStreamable = GetMipCount() - m_numPersistentMips; - for (AZ::u32 mip = 0; mip < numStreamable; mip++) - { - AZ::u32 nameIdx = numStreamable - mip; - AZStd::string mipFileName = AZStd::string::format("%s.%d", filename, nameIdx); - SaveMipToFile(mip, mipFileName); - outFilePaths.push_back(mipFileName); - if (alphaImage) - { - AZStd::string mipAlphaFileName = mipFileName + "a"; - alphaImage->SaveMipToFile(mip, mipAlphaFileName); - outFilePaths.push_back(mipAlphaFileName); - } - } - } - - return bOk; - } - - bool CImageObject::SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const - { - AZ::IO::SystemFile saveFile; - saveFile.Open(filename.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY); - - AZ::IO::SystemFileStream saveFileStream(&saveFile, true); - - if (!saveFileStream.IsOpen()) - { - AZ_Warning("Image Processing", false, "%s: failed to create file %s", __FUNCTION__, filename.c_str()); - return false; - } - - saveFileStream.Write(GetMipBufSize(mip), m_mips[mip]->m_pData); - return true; - } - float CImageObject::CalculateAverageBrightness() const { //if it's compressed format, return a default value @@ -642,63 +518,6 @@ namespace ImageProcessingAtom return true; } - bool CImageObject::SaveImage(AZ::IO::SystemFileStream& saveFileStream) const - { - DDS_FILE_DESC_LEGACY desc; - DDS_HEADER_DXT10 exthead; - - desc.dwMagic = FOURCC_DDS; - - if (!BuildSurfaceHeader(desc.header)) - { - return false; - } - - if (desc.header.IsDX10Ext() && !BuildSurfaceExtendedHeader(exthead)) - { - return false; - } - - saveFileStream.Write(sizeof(desc), &desc); - - if (desc.header.IsDX10Ext()) - { - saveFileStream.Write(sizeof(exthead), &exthead); - } - - AZ::u32 faces = 1; - - //for cubemap. export each face and its mipmap - if (HasImageFlags(EIF_Cubemap)) - { - faces = 6; - } - - AZ::u32 mipStart = 0; - if (HasImageFlags(EIF_Splitted)) - { - if (m_numPersistentMips < m_mips.size()) - { - mipStart = (AZ::u32)m_mips.size() - m_numPersistentMips; - } - else - { - AZ_Assert(false, "numPersistentMips wasn't setup correctly"); - } - } - - for (AZ::u32 face = 0; face < faces; face++) - { - for (AZ::u32 mip = mipStart; mip < m_mips.size(); ++mip) - { - const MipLevel& level = *m_mips[mip]; - AZ::u32 faceBufSize = level.m_pitch * level.m_rowCount / faces; - saveFileStream.Write(faceBufSize, level.m_pData + faceBufSize * face); - } - } - return true; - } - void CImageObject::GetExtent(AZ::u32& width, AZ::u32& height, AZ::u32& mipCount) const { mipCount = (AZ::u32)m_mips.size(); @@ -953,35 +772,4 @@ namespace ImageProcessingAtom } } } - - void CImageObject::ConvertLegacyGloss() - { - if (!(CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat))) - { - AZ_Assert(false, "%s function only works with uncompressed pixel format", __FUNCTION__); - return; - } - - //create pixel operation function - IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat); - - //get count of bytes per pixel - AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8; - - const AZ::u32 mips = (AZ::u32)m_mips.size(); - float color[4]; - for (AZ::u32 mip = 0; mip < mips; ++mip) - { - AZ::u8* pixelBuf = m_mips[mip]->m_pData; - const AZ::u32 pixelCount = GetPixelCount(mip); - - for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes) - { - pixelOp->GetRGBA(pixelBuf, color[0], color[1], color[2], color[3]); - // Convert from (1 - s * 0.7)^6 to (1 - s)^2 - color[3] = 1 - pow(1.0f - color[3] * 0.7f, 3.0f); - pixelOp->SetRGBA(pixelBuf, color[0], color[1], color[2], color[3]); - } - } - } } // namespace ImageProcessingAtom diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.h index c8b8ced496..7fa02f464e 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.h @@ -57,10 +57,6 @@ namespace ImageProcessingAtom bool CompareImage(const IImageObjectPtr otherImage) const override; - bool SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector& outFilePaths) const override; - bool SaveImage(AZ::IO::SystemFileStream& out) const override; - bool SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const override; - uint32_t GetTextureMemory() const override; EAlphaContent GetAlphaContent() const override; @@ -79,7 +75,6 @@ namespace ImageProcessingAtom void SetNumPersistentMips(AZ::u32 nMips) override; void GlossFromNormals(bool hasAuthoredGloss) override; - void ConvertLegacyGloss() override; void ClearColor(float r, float g, float b, float a) override; //end virtual functions from IImageObject diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageToProcess.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageToProcess.h index e6fe6ce142..ed0b21b56c 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageToProcess.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageToProcess.h @@ -66,13 +66,6 @@ namespace ImageProcessingAtom bool GammaToLinearRGBA32F(bool bDeGamma); void LinearToGamma(); - // --------------------------------------------------------------------------------- - // Tools for A32B32G32R32F - - void CreateHighPass(uint32 dwMipDown); - - void CreateColorChart(); - //convert various original cubemap layouts to new layout bool ConvertCubemapLayout(CubemapLayoutType newLayout); }; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp index 3e0bbd89eb..d5b795a1fa 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp @@ -988,7 +988,6 @@ namespace UnitTest ASSERT_TRUE(process->IsSucceed()); SaveImageToFile(process->GetOutputImage(), "rgb", 10); - SaveImageToFile(process->GetOutputAlphaImage(), "alpha", 10); process->GetAppendOutputProducts(outProducts); diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake b/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake index aad400518d..a6fe09bfa6 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/imageprocessing_files.cmake @@ -103,8 +103,6 @@ set(FILES Source/Converters/ConvertPixelFormat.cpp Source/Converters/Cubemap.h Source/Converters/Cubemap.cpp - Source/Converters/ColorChart.cpp - Source/Converters/HighPass.cpp Source/Converters/Histogram.cpp Source/Converters/Histogram.h ../External/CubeMapGen/CBBoxInt32.cpp From 807d0d7a5a2fe221fb226716ec6107f144643dd8 Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Fri, 5 Nov 2021 17:32:16 +0000 Subject: [PATCH 085/177] Fixed hierarchies migration. Fixed assert with invalid entity bounds Signed-off-by: Sergey Pereslavtsev --- .../NetworkEntity/NetworkEntityManager.cpp | 74 +++++++++++-------- .../NetworkEntity/NetworkEntityManager.h | 4 +- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index c7582af83f..3240925f6e 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -241,7 +241,13 @@ namespace Multiplayer { AZ::Entity* entity = it->second; NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity); + AZ::Aabb entityBounds = AZ::Interface::Get()->GetEntityWorldBoundsUnion(entity->GetId()); + if (!entityBounds.IsValid()) + { + continue; + } + entityBounds.Expand(AZ::Vector3(0.01f)); if (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority) { @@ -287,41 +293,15 @@ namespace Multiplayer const IEntityDomain::EntitiesNotInDomain& entitiesNotInDomain = m_entityDomain->RetrieveEntitiesNotInDomain(); for (NetEntityId exitingId : entitiesNotInDomain) { - OnEntityExitDomain(exitingId); + OnEntityExitDomain(exitingId, entitiesNotInDomain); } } - void NetworkEntityManager::OnEntityExitDomain(NetEntityId entityId) + void NetworkEntityManager::OnEntityExitDomain(NetEntityId entityId, const IEntityDomain::EntitiesNotInDomain& entitiesNotInDomain) { - bool safeToExit = true; NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(entityId); - // We also need special handling for the NetworkHierarchy as well, since related entities need to be migrated together - NetworkHierarchyRootComponentController* hierarchyRootController = entityHandle.FindController(); - NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController(); - - // Find the root entity - AZ::Entity* hierarchyRootEntity = nullptr; - if (hierarchyRootController) - { - hierarchyRootEntity = hierarchyRootController->GetParent().GetHierarchicalRoot(); - } - else if (hierarchyChildController) - { - hierarchyRootEntity = hierarchyChildController->GetParent().GetHierarchicalRoot(); - } - - if (hierarchyRootEntity) - { - NetEntityId rootNetId = GetNetEntityIdById(hierarchyRootEntity->GetId()); - ConstNetworkEntityHandle rootEntityHandle = GetEntity(rootNetId); - - // Check if the root entity is still tracked by this authority - if (rootEntityHandle.Exists() && rootEntityHandle.GetNetBindComponent()->HasController()) - { - safeToExit = false; - } - } + bool safeToExit = IsHierarchySafeToExit(entityHandle, entitiesNotInDomain); // Validate that we aren't already planning to remove this entity if (safeToExit) @@ -632,4 +612,40 @@ namespace Multiplayer netEntity->GetName().c_str()); } } + + bool NetworkEntityManager::IsHierarchySafeToExit(NetworkEntityHandle& entityHandle, const IEntityDomain::EntitiesNotInDomain& entitiesNotInDomain) + { + bool safeToExit = true; + + // We also need special handling for the NetworkHierarchy as well, since related entities need to be migrated together + NetworkHierarchyRootComponentController* hierarchyRootController = entityHandle.FindController(); + NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController(); + + AZStd::vector hierarchicalEntities; + + // Get the entities in this hierarchy + if (hierarchyRootController) + { + hierarchicalEntities = hierarchyRootController->GetParent().GetHierarchicalEntities(); + } + else if (hierarchyChildController) + { + hierarchicalEntities = hierarchyChildController->GetParent().GetHierarchicalEntities(); + } + + // Check if *all* entities in the hierarchy are ready to migrate. + // If any are still "in domain", keep the whole hierarchy within the current authority for now + for (AZ::Entity* entity : hierarchicalEntities) + { + NetEntityId netEntityId = GetNetEntityIdById(entity->GetId()); + if (netEntityId != InvalidNetEntityId && !entitiesNotInDomain.contains(netEntityId)) + { + safeToExit = false; + break; + } + } + + return safeToExit; + } + } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h index 133c35dce0..56ea2bb72c 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h @@ -84,7 +84,9 @@ namespace Multiplayer void DispatchLocalDeferredRpcMessages(); void UpdateEntityDomain(); - void OnEntityExitDomain(NetEntityId entityId); + void OnEntityExitDomain(NetEntityId entityId, const IEntityDomain::EntitiesNotInDomain& entitiesNotInDomain); + + bool IsHierarchySafeToExit(NetworkEntityHandle& entityHandle, const IEntityDomain::EntitiesNotInDomain& entitiesNotInDomain); //! RootSpawnableNotificationBus //! @{ From 08115fc41fe8ae94933ac73c2fbb33a15eef1836 Mon Sep 17 00:00:00 2001 From: Shirang Jia Date: Fri, 5 Nov 2021 10:36:14 -0700 Subject: [PATCH 086/177] Add platform name to AP log path on S3 (#5316) * Add platform name to AP log path on S3 Signed-off-by: shiranj * Add platform name to AP log path on S3 Signed-off-by: shiranj --- scripts/build/Jenkins/Jenkinsfile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index ff04902861..6c3d697d2a 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -271,7 +271,7 @@ def CheckoutRepo(boolean disableSubmodules = false) { palRm('commitdate') } -def HandleDriveMount(String snapshot, String repositoryName, String projectName, String pipeline, String branchName, String platform, String buildType, String workspace, boolean recreateVolume = false) { +def HandleDriveMount(String snapshot, String repositoryName, String projectName, String pipeline, String branchName, String platform, String buildType, String workspace, boolean recreateVolume = false) { unstash name: 'incremental_build_script' def pythonCmd = '' @@ -435,7 +435,7 @@ def ExportTestScreenshots(Map options, String branchName, String platformName, S } } -def UploadAPLogs(Map options, String branchName, String jobName, String workspace, Map params) { +def UploadAPLogs(Map options, String branchName, String platformName, String jobName, String workspace, Map params) { dir("${workspace}/${ENGINE_REPOSITORY_NAME}") { projects = params.CMAKE_LY_PROJECTS.split(",") projects.each{ project -> @@ -449,7 +449,7 @@ def UploadAPLogs(Map options, String branchName, String jobName, String workspac } def command = "${pythonPath} -u ${s3UploadScriptPath} --base_dir ${apLogsPath} " + "--file_regex \".*\" --bucket ${env.AP_LOGS_S3_BUCKET} " + - "--search_subdirectories True --key_prefix ${env.JENKINS_JOB_NAME}/${branchName}/${env.BUILD_NUMBER}/${jobName} " + + "--search_subdirectories True --key_prefix ${env.JENKINS_JOB_NAME}/${branchName}/${env.BUILD_NUMBER}/${platformName}/${jobName} " + '--extra_args {\\"ACL\\":\\"bucket-owner-full-control\\"}' palSh(command, "Uploading AP logs for job ${jobName} for branch ${branchName}", false) } @@ -519,10 +519,10 @@ def CreateExportTestScreenshotsStage(Map pipelineConfig, String branchName, Stri } } -def CreateUploadAPLogsStage(Map pipelineConfig, String branchName, String jobName, String workspace, Map params) { +def CreateUploadAPLogsStage(Map pipelineConfig, String branchName, String platformName, String jobName, String workspace, Map params) { return { stage("${jobName}_upload_ap_logs") { - UploadAPLogs(pipelineConfig, branchName, jobName, workspace, params) + UploadAPLogs(pipelineConfig, branchName, platformName, jobName, workspace, params) } } } @@ -577,7 +577,7 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar } } if (IsAPLogUpload(branchName, build_job_name)) { - CreateUploadAPLogsStage(pipelineConfig, branchName, build_job_name, envVars['WORKSPACE'], platform.value.build_types[build_job_name].PARAMETERS).call() + CreateUploadAPLogsStage(pipelineConfig, branchName, platform.key, build_job_name, envVars['WORKSPACE'], platform.value.build_types[build_job_name].PARAMETERS).call() } // All other errors will be raised outside the retry block currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE' From 0b061d2e0038bc462de83c1c4a20fdf79637be7c Mon Sep 17 00:00:00 2001 From: Ken Pruiksma Date: Fri, 5 Nov 2021 12:39:58 -0500 Subject: [PATCH 087/177] GHI-5338 - Fixing incorrect calculation of sector bounds for negative values (#5352) Signed-off-by: Ken Pruiksma --- .../Source/TerrainRenderer/TerrainFeatureProcessor.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index 73f1c3967c..55713e696a 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -417,10 +417,10 @@ namespace Terrain m_areaData.m_rebuildSectors = false; m_sectorData.clear(); - const float xFirstPatchStart = terrainBounds.GetMin().GetX() - fmod(terrainBounds.GetMin().GetX(), GridMeters); - const float xLastPatchStart = terrainBounds.GetMax().GetX() - fmod(terrainBounds.GetMax().GetX(), GridMeters); - const float yFirstPatchStart = terrainBounds.GetMin().GetY() - fmod(terrainBounds.GetMin().GetY(), GridMeters); - const float yLastPatchStart = terrainBounds.GetMax().GetY() - fmod(terrainBounds.GetMax().GetY(), GridMeters); + const float xFirstPatchStart = AZStd::floorf(terrainBounds.GetMin().GetX() / GridMeters) * GridMeters; + const float xLastPatchStart = AZStd::floorf(terrainBounds.GetMax().GetX() / GridMeters) * GridMeters; + const float yFirstPatchStart = AZStd::floorf(terrainBounds.GetMin().GetY() / GridMeters) * GridMeters; + const float yLastPatchStart = AZStd::floorf(terrainBounds.GetMax().GetY() / GridMeters) * GridMeters; const auto& materialAsset = m_materialInstance->GetAsset(); const auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg(); @@ -603,7 +603,7 @@ namespace Terrain // For every distance doubling beyond a minDistanceForLod0, we only need half the mesh density. Each LOD // is exactly half the resolution of the last. - const float lodForCamera = floorf(AZ::GetMax(0.0f, log2f(sectorDistance / minDistanceForLod0))); + const float lodForCamera = AZStd::floorf(AZ::GetMax(0.0f, log2f(sectorDistance / minDistanceForLod0))); // All cameras should render the same LOD so effects like shadows are consistent. lodChoice = AZ::GetMin(lodChoice, aznumeric_cast(lodForCamera)); From 0e885b826788fdee0ce35a4f47ec0d6c654c38c9 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Fri, 5 Nov 2021 11:38:41 -0700 Subject: [PATCH 088/177] [Linux] Fix crash from accessing an invalid AZ::EnvironmentVariable During asset processing, no `RPISystem` component is created, so nothing so nothing calls `ShaderSystem::Init()`, so nothing calls `ShaderReloadDebugTracker::Init()`. Consequently, the AZ::EnvironmentVariables that are used during `ShaderReloadDebugTracker::IsEnabled()` never got created, causing a read from a nullptr at runtime. This fixes that issue by making `IsEnabled()` call `CreateVariable()` on the variables it needs if they are not valid. In addition, it changes the call to `CreateVariable()` to initialize the variable's values directly, to ensure they are only initialized once. It also switches to use `AZ::Crc32` so that the variable's id is computed at compile time. Signed-off-by: Chris Burel --- .../RPI.Public/Shader/ShaderReloadDebugTracker.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderReloadDebugTracker.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderReloadDebugTracker.cpp index 3e7ff826cf..fd1f9a845f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderReloadDebugTracker.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/ShaderReloadDebugTracker.cpp @@ -15,8 +15,8 @@ namespace AZ { namespace ShaderReloadDebugTrackerInternal { - static const char EnabledVariableName[] = "ShaderReloadDebugTracker enabled"; - static const char IndentVariableName[] = "ShaderReloadDebugTracker indent"; + static constexpr char EnabledVariableName[] = "ShaderReloadDebugTracker enabled"; + static constexpr char IndentVariableName[] = "ShaderReloadDebugTracker indent"; static EnvironmentVariable s_enabled; static EnvironmentVariable s_indent; @@ -24,11 +24,7 @@ namespace AZ void ShaderReloadDebugTracker::Init() { - ShaderReloadDebugTrackerInternal::s_enabled = AZ::Environment::CreateVariable(ShaderReloadDebugTrackerInternal::EnabledVariableName); - ShaderReloadDebugTrackerInternal::s_indent = AZ::Environment::CreateVariable(ShaderReloadDebugTrackerInternal::IndentVariableName); - - ShaderReloadDebugTrackerInternal::s_enabled.Get() = false; - ShaderReloadDebugTrackerInternal::s_indent.Get() = 0; + MakeReady(); } void ShaderReloadDebugTracker::Shutdown() @@ -41,8 +37,8 @@ namespace AZ { if (!ShaderReloadDebugTrackerInternal::s_enabled.IsValid()) { - ShaderReloadDebugTrackerInternal::s_enabled = AZ::Environment::FindVariable(ShaderReloadDebugTrackerInternal::EnabledVariableName); - ShaderReloadDebugTrackerInternal::s_indent = AZ::Environment::FindVariable(ShaderReloadDebugTrackerInternal::IndentVariableName); + ShaderReloadDebugTrackerInternal::s_enabled = AZ::Environment::CreateVariable(AZ::Crc32(ShaderReloadDebugTrackerInternal::EnabledVariableName), false); + ShaderReloadDebugTrackerInternal::s_indent = AZ::Environment::CreateVariable(AZ::Crc32(ShaderReloadDebugTrackerInternal::IndentVariableName), 0); } } From 6cba64f2265d06ef4244aa387c9e6b2164f093a9 Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Fri, 5 Nov 2021 13:47:24 -0500 Subject: [PATCH 089/177] Fix issue with Server Launcher debug console not accepting keystrokes (#5325) * Fix issue with debug console ignoring some keys This problem was reported for Server only, the Enter/Backspace keys were being ignored in the ImGui Debug Console. This wasn't an issue if the Server had loaded a map. The problem was with XConsole explicitly setting a bool in dedicated server mode. This caused text input to be processed by XConsole code and not passed further along to DebugConsole where it should have been handling it via ImGui. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Fix missing runtime dependency of ServerLauncher ServerLauncher in non-monolithic config was missing a runtime dependency on Legacy::CrySystem. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- Code/LauncherUnified/launcher_generator.cmake | 6 ++++++ Code/Legacy/CrySystem/XConsole.cpp | 5 ----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Code/LauncherUnified/launcher_generator.cmake b/Code/LauncherUnified/launcher_generator.cmake index 5c9ee68e27..550a67bc49 100644 --- a/Code/LauncherUnified/launcher_generator.cmake +++ b/Code/LauncherUnified/launcher_generator.cmake @@ -76,6 +76,12 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC Legacy::CrySystem ) + if(PAL_TRAIT_BUILD_SERVER_SUPPORTED) + set(server_runtime_dependencies + Legacy::CrySystem + ) + endif() + endif() ################################################################################ diff --git a/Code/Legacy/CrySystem/XConsole.cpp b/Code/Legacy/CrySystem/XConsole.cpp index 9f60a9dd8d..22c61d3f22 100644 --- a/Code/Legacy/CrySystem/XConsole.cpp +++ b/Code/Legacy/CrySystem/XConsole.cpp @@ -333,11 +333,6 @@ void CXConsole::Init(ISystem* pSystem) m_nLoadingBackTexID = -1; - if (gEnv->IsDedicated()) - { - m_bConsoleActive = true; - } - REGISTER_COMMAND("ConsoleShow", &ConsoleShow, VF_NULL, "Opens the console"); REGISTER_COMMAND("ConsoleHide", &ConsoleHide, VF_NULL, "Closes the console"); From d484d358d166e5f82eab109f6670b4129b03bf8b Mon Sep 17 00:00:00 2001 From: "rgba16f [Amazon]" <82187279+rgba16f@users.noreply.github.com> Date: Fri, 5 Nov 2021 14:00:40 -0500 Subject: [PATCH 090/177] Modify AtomDebugDisplayViewportInterface::DrawWireCircle2d to account for viewport aspect ratio (#5375) Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> --- .../Code/Source/AtomDebugDisplayViewportInterface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp index 39d8933863..1ccc36ab4d 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp @@ -800,7 +800,8 @@ namespace AZ::AtomBridge const float startAngle = DegToRad(startAngleDegrees); const float stopAngle = DegToRad(sweepAngleDegrees) + startAngle; SingleColorDynamicSizeLineHelper lines(1+static_cast(sweepAngleDegrees/angularStepDegrees)); - AZ::Vector3 radiusV3 = AZ::Vector3(radius); + float aspectRadius = radius / GetAspectRatio(); + AZ::Vector3 radiusV3 = AZ::Vector3(aspectRadius, radius, radius); AZ::Vector3 pos = AZ::Vector3(center.GetX(), center.GetY(), z); CreateAxisAlignedArc( lines, From 988561920adeec83b4b3e6f597e8386807ec2ed8 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Fri, 5 Nov 2021 12:26:10 -0700 Subject: [PATCH 091/177] Sets up the event scheduler system component for hierarchy tests Signed-off-by: kberg-amzn --- Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h index 249837b484..1b64efc5e2 100644 --- a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h +++ b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -119,6 +120,8 @@ namespace Multiplayer m_mockTime = AZStd::make_unique>(); AZ::Interface::Register(m_mockTime.get()); + m_eventScheduler = AZStd::make_unique(); + m_mockNetworkTime = AZStd::make_unique>(); AZ::Interface::Register(m_mockNetworkTime.get()); @@ -170,6 +173,7 @@ namespace Multiplayer AZ::Interface::Unregister(m_mockMultiplayer.get()); AZ::Interface::Unregister(m_mockComponentApplicationRequests.get()); + m_eventScheduler.reset(); m_mockTime.reset(); m_mockNetworkEntityManager.reset(); @@ -204,6 +208,7 @@ namespace Multiplayer AZStd::unique_ptr> m_mockMultiplayer; AZStd::unique_ptr m_mockNetworkEntityManager; AZStd::unique_ptr> m_mockTime; + AZStd::unique_ptr m_eventScheduler; AZStd::unique_ptr> m_mockNetworkTime; AZStd::unique_ptr> m_mockConnection; From 989952e106cc0326c1566832b36094f23e9214bd Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Fri, 5 Nov 2021 12:28:06 -0700 Subject: [PATCH 092/177] Fix comment Signed-off-by: kberg-amzn --- .../Code/Include/Multiplayer/EntityDomains/IEntityDomain.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/EntityDomains/IEntityDomain.h b/Gems/Multiplayer/Code/Include/Multiplayer/EntityDomains/IEntityDomain.h index 4b1bbbdba8..b650f9e1d9 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/EntityDomains/IEntityDomain.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/EntityDomains/IEntityDomain.h @@ -34,7 +34,7 @@ namespace Multiplayer //! This method will be invoked whenever we unexpectedly lose the authoritative entity replicator for an entity. //! This gives our entity domain a chance to determine whether or not it should assume authority in this instance. - //! @param entityHandle the network entity handle of the entity that has lost it's authoritative replicator + //! @param entityHandle the network entity handle of the entity that has lost its authoritative replicator virtual void HandleLossOfAuthoritativeReplicator(const ConstNetworkEntityHandle& entityHandle) = 0; //! Debug draw to visualize host entity domains. From eecf6ab920ea3688e2430e7ac885380685610492 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Fri, 5 Nov 2021 12:30:19 -0700 Subject: [PATCH 093/177] Create a nightly job that validates project-centric/engine-prebuilt (#5287) * adds a test_install_profile_vs2019_pipe job to validate a project can build from the SDK Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * missed escaping these variables and breaks runtime dependencines in the install layout Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * Changes to PIPELINE_ENV_OVERRIDE Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * Tries to propagate ENV variables from pipeline jobs to jobs under it Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * Fixes typo Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * echoing a var to understand why is not going to the right path Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * put the COMMAND_CWD in the wrong job Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * adding similar jobs for linux/mac Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * dont pass an empty LY_PROJECTS Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * cmd -> sh, copy-paste mistake Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * inverting check in linux/mac Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * more fixes for linux/mac Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * fixing script paths for macos Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * more fixes for linux/mac Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * Test use of %% instead of !! for windows builds Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * fixes typo Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- scripts/build/Jenkins/Jenkinsfile | 4 +- .../build/Platform/Linux/build_config.json | 43 +++++++++++++++++ scripts/build/Platform/Linux/build_linux.sh | 5 +- scripts/build/Platform/Linux/env_linux.sh | 5 ++ scripts/build/Platform/Mac/build_config.json | 45 +++++++++++++++++ scripts/build/Platform/Mac/build_mac.sh | 5 +- scripts/build/Platform/Mac/env_mac.sh | 5 ++ .../build/Platform/Windows/build_config.json | 48 ++++++++++++------- .../build/Platform/Windows/build_windows.cmd | 19 ++++---- .../build/Platform/Windows/env_windows.cmd | 5 ++ 10 files changed, 157 insertions(+), 27 deletions(-) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 6c3d697d2a..8eddb61c33 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -551,9 +551,11 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar CreateSetupStage(pipelineConfig, snapshot, repositoryName, projectName, pipelineName, branchName, platform.key, build_job.key, envVars, onlyMountEBSVolume).call() if(build_job.value.steps) { //this is a pipe with many steps so create all the build stages + pipelineEnvVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, build_job.value.PIPELINE_ENV ?: EMPTY_JSON, pipelineName) build_job.value.steps.each { build_step -> build_job_name = build_step - envVars = GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, platform.value.build_types[build_step].PIPELINE_ENV ?: EMPTY_JSON, pipelineName) + // This addition of maps makes it that the right operand will override entries if they overlap with the left operand + envVars = pipelineEnvVars + GetBuildEnvVars(platform.value.PIPELINE_ENV ?: EMPTY_JSON, platform.value.build_types[build_step].PIPELINE_ENV ?: EMPTY_JSON, pipelineName) try { CreateBuildStage(pipelineConfig, platform.key, build_step, envVars).call() } diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index c0307de23d..8551c3dcb3 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -214,5 +214,48 @@ "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } + }, + "install_profile": { + "TAGS": [], + "COMMAND": "build_linux.sh", + "PARAMETERS": { + "CONFIGURATION": "profile", + "OUTPUT_DIRECTORY": "build/linux", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_PARALLEL_LINK_JOBS=4 -DLY_DISABLE_TEST_MODULES=TRUE", + "CMAKE_TARGET": "install" + } + }, + "install_profile_pipe": { + "TAGS": [ + "nightly-incremental", + "nightly-clean" + ], + "PIPELINE_ENV": { + "PROJECT_REPOSITORY_NAME": "TestProject" + }, + "steps": [ + "install_profile", + "project_generate", + "project_engineinstall_profile" + ] + }, + "project_generate": { + "TAGS": [], + "COMMAND": "python_linux.sh", + "PARAMETERS": { + "SCRIPT_PATH": "install/scripts/o3de.py", + "SCRIPT_PARAMETERS": "create-project -pp ${WORKSPACE}/${PROJECT_REPOSITORY_NAME} --force" + } + }, + "project_engineinstall_profile": { + "TAGS": [], + "COMMAND": "build_linux.sh", + "PARAMETERS": { + "COMMAND_CWD": "${WORKSPACE}/${PROJECT_REPOSITORY_NAME}", + "CONFIGURATION": "profile", + "OUTPUT_DIRECTORY": "build/linux", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_PARALLEL_LINK_JOBS=4 -DCMAKE_MODULE_PATH=${WORKSPACE}/o3de/install/cmake", + "CMAKE_TARGET": "all" + } } } diff --git a/scripts/build/Platform/Linux/build_linux.sh b/scripts/build/Platform/Linux/build_linux.sh index fd73e17a12..ab51913550 100755 --- a/scripts/build/Platform/Linux/build_linux.sh +++ b/scripts/build/Platform/Linux/build_linux.sh @@ -17,7 +17,10 @@ SOURCE_DIRECTORY=${PWD} pushd $OUTPUT_DIRECTORY LAST_CONFIGURE_CMD_FILE=ci_last_configure_cmd.txt -CONFIGURE_CMD="cmake ${SOURCE_DIRECTORY} ${CMAKE_OPTIONS} ${EXTRA_CMAKE_OPTIONS} -DLY_3RDPARTY_PATH=${LY_3RDPARTY_PATH} -DLY_PROJECTS='${CMAKE_LY_PROJECTS}'" +CONFIGURE_CMD="cmake ${SOURCE_DIRECTORY} ${CMAKE_OPTIONS} ${EXTRA_CMAKE_OPTIONS} -DLY_3RDPARTY_PATH=${LY_3RDPARTY_PATH}" +if [[ -n "$CMAKE_LY_PROJECTS" ]]; then + CONFIGURE_CMD="${CONFIGURE_CMD} -DLY_PROJECTS='${CMAKE_LY_PROJECTS}'" +fi if [[ ! -e "CMakeCache.txt" ]]; then echo [ci_build] First run, generating RUN_CONFIGURE=1 diff --git a/scripts/build/Platform/Linux/env_linux.sh b/scripts/build/Platform/Linux/env_linux.sh index a03b9642fb..059bb119ff 100755 --- a/scripts/build/Platform/Linux/env_linux.sh +++ b/scripts/build/Platform/Linux/env_linux.sh @@ -18,3 +18,8 @@ if ! command -v ninja &> /dev/null; then echo "[ci_build] Ninja not found" exit 1 fi + +if [[ -n "${COMMAND_CWD}" ]]; then + echo $(eval echo [ci_build] Changing CWD to $COMMAND_CWD) + cd $(eval echo ${COMMAND_CWD}) +fi diff --git a/scripts/build/Platform/Mac/build_config.json b/scripts/build/Platform/Mac/build_config.json index b57cc522bc..34b02a1aec 100644 --- a/scripts/build/Platform/Mac/build_config.json +++ b/scripts/build/Platform/Mac/build_config.json @@ -162,5 +162,50 @@ "SCRIPT_PATH": "scripts/build/package/package.py", "SCRIPT_PARAMETERS": "--platform Mac --type all" } + }, + "install_profile": { + "TAGS": [], + "COMMAND": "build_mac.sh", + "PARAMETERS": { + "CONFIGURATION": "profile", + "OUTPUT_DIRECTORY": "build/mac", + "CMAKE_OPTIONS": "-G Xcode -DLY_DISABLE_TEST_MODULES=TRUE", + "CMAKE_LY_PROJECTS": "", + "CMAKE_TARGET": "install" + } + }, + "install_profile_pipe": { + "TAGS": [ + "nightly-incremental", + "nightly-clean" + ], + "PIPELINE_ENV": { + "PROJECT_REPOSITORY_NAME": "TestProject" + }, + "steps": [ + "install_profile", + "project_generate", + "project_engineinstall_profile" + ] + }, + "project_generate": { + "TAGS": [], + "COMMAND": "python_mac.sh", + "PARAMETERS": { + "SCRIPT_PATH": "install/O3DE_SDK.app/Contents/Engine/scripts/o3de.py", + "SCRIPT_PARAMETERS": "create-project -pp ${WORKSPACE}/${PROJECT_REPOSITORY_NAME} --force" + } + }, + "project_engineinstall_profile": { + "TAGS": [], + "COMMAND": "build_mac.sh", + "PARAMETERS": { + "COMMAND_CWD": "${WORKSPACE}/${PROJECT_REPOSITORY_NAME}", + "CONFIGURATION": "profile", + "OUTPUT_DIRECTORY": "build/mac", + "CMAKE_OPTIONS": "-G Xcode -DCMAKE_MODULE_PATH=${WORKSPACE}/o3de/install/O3DE_SDK.app/Contents/Engine/cmake", + "CMAKE_LY_PROJECTS": "", + "CMAKE_TARGET": "ALL_BUILD" + } } } diff --git a/scripts/build/Platform/Mac/build_mac.sh b/scripts/build/Platform/Mac/build_mac.sh index cb271212d6..169e91c24f 100755 --- a/scripts/build/Platform/Mac/build_mac.sh +++ b/scripts/build/Platform/Mac/build_mac.sh @@ -17,7 +17,10 @@ SOURCE_DIRECTORY=${PWD} pushd $OUTPUT_DIRECTORY LAST_CONFIGURE_CMD_FILE=ci_last_configure_cmd.txt -CONFIGURE_CMD="cmake ${SOURCE_DIRECTORY} ${CMAKE_OPTIONS} ${EXTRA_CMAKE_OPTIONS} -DLY_3RDPARTY_PATH=${LY_3RDPARTY_PATH} -DLY_PROJECTS='${CMAKE_LY_PROJECTS}'" +CONFIGURE_CMD="cmake ${SOURCE_DIRECTORY} ${CMAKE_OPTIONS} ${EXTRA_CMAKE_OPTIONS} -DLY_3RDPARTY_PATH=${LY_3RDPARTY_PATH}" +if [[ -n "$CMAKE_LY_PROJECTS" ]]; then + CONFIGURE_CMD="${CONFIGURE_CMD} -DLY_PROJECTS='${CMAKE_LY_PROJECTS}'" +fi if [[ ! -e "CMakeCache.txt" ]]; then echo [ci_build] First run, generating RUN_CONFIGURE=1 diff --git a/scripts/build/Platform/Mac/env_mac.sh b/scripts/build/Platform/Mac/env_mac.sh index 2c974a1efe..f5fd9f5773 100755 --- a/scripts/build/Platform/Mac/env_mac.sh +++ b/scripts/build/Platform/Mac/env_mac.sh @@ -13,3 +13,8 @@ if ! command -v cmake &> /dev/null; then echo "[ci_build] CMake not found" exit 1 fi + +if [[ -n "${COMMAND_CWD}" ]]; then + echo $(eval echo [ci_build] Changing CWD to $COMMAND_CWD) + cd $(eval echo ${COMMAND_CWD}) +fi diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index b185a845ec..b0bb79b5dd 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -56,7 +56,7 @@ "COMMAND": "python_windows.cmd", "PARAMETERS": { "SCRIPT_PATH": "scripts/build/ci_build_metrics.py", - "SCRIPT_PARAMETERS": "--platform=Windows --repository=!REPOSITORY_NAME! --jobname=!JOB_NAME! --jobnumber=!BUILD_NUMBER! --jobnode=!NODE_LABEL! --changelist=!CHANGE_ID!" + "SCRIPT_PARAMETERS": "--platform=Windows --repository=%REPOSITORY_NAME% --jobname=%JOB_NAME% --jobnumber=%BUILD_NUMBER% --jobnode=%NODE_LABEL% --changelist=%CHANGE_ID%" } }, "windows_packaging_all": { @@ -88,7 +88,7 @@ "CONFIGURATION": "profile", "SCRIPT_PATH": "scripts/build/TestImpactAnalysis/tiaf_driver.py", "SCRIPT_PARAMETERS": - "--config=\"!OUTPUT_DIRECTORY!/bin/TestImpactFramework/profile/Persistent/tiaf.json\" --src-branch=!BRANCH_NAME! --dst-branch=!CHANGE_TARGET! --commit=!CHANGE_ID! --s3-bucket=!TEST_IMPACT_S3_BUCKET! --mars-index-prefix=jonawals --s3-top-level-dir=!REPOSITORY_NAME! --build-number=!BUILD_NUMBER! --suite=main --test-failure-policy=continue" + "--config=\"%OUTPUT_DIRECTORY%/bin/TestImpactFramework/profile/Persistent/tiaf.json\" --src-branch=%BRANCH_NAME% --dst-branch=%CHANGE_TARGET% --commit=%CHANGE_ID% --s3-bucket=%TEST_IMPACT_S3_BUCKET% --mars-index-prefix=jonawals --s3-top-level-dir=%REPOSITORY_NAME% --build-number=%BUILD_NUMBER% --suite=main --test-failure-policy=continue" } }, "debug_vs2019": { @@ -131,7 +131,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_TEST_IMPACT_INSTRUMENTATION_BIN=!TEST_IMPACT_WIN_BINARY!", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_TEST_IMPACT_INSTRUMENTATION_BIN=%TEST_IMPACT_WIN_BINARY%", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -337,16 +337,12 @@ } }, "install_profile_vs2019": { - "TAGS": [ - "nightly-incremental", - "nightly-clean" - ], + "TAGS": [], "COMMAND": "build_windows.cmd", "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_DISABLE_TEST_MODULES=TRUE", - "CMAKE_LY_PROJECTS": "", "CMAKE_TARGET": "INSTALL", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" } @@ -363,14 +359,35 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"!WIX! \"", - "EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=!INSTALLER_DOWNLOAD_URL! -DLY_INSTALLER_LICENSE_URL=!INSTALLER_DOWNLOAD_URL!/license", - "CPACK_BUCKET": "!INSTALLER_BUCKET!", - "CMAKE_LY_PROJECTS": "", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"%WIX% \"", + "EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=%INSTALLER_DOWNLOAD_URL% -DLY_INSTALLER_LICENSE_URL=%INSTALLER_DOWNLOAD_URL%/license", + "CPACK_BUCKET": "%INSTALLER_BUCKET%", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" } }, + "install_profile_vs2019_pipe": { + "TAGS": [ + "nightly-incremental", + "nightly-clean" + ], + "PIPELINE_ENV": { + "PROJECT_REPOSITORY_NAME": "TestProject" + }, + "steps": [ + "install_profile_vs2019", + "project_generate", + "project_engineinstall_profile_vs2019" + ] + }, + "project_generate": { + "TAGS": [], + "COMMAND": "python_windows.cmd", + "PARAMETERS": { + "SCRIPT_PATH": "install\\scripts\\o3de.py", + "SCRIPT_PARAMETERS": "create-project -pp %WORKSPACE%\\%PROJECT_REPOSITORY_NAME% --force" + } + }, "project_enginesource_profile_vs2019": { "TAGS": [ "project" @@ -382,8 +399,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_MODULE_PATH=!WORKSPACE!/o3de/cmake", - "CMAKE_LY_PROJECTS": "", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_MODULE_PATH=%WORKSPACE%/o3de/cmake", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" } @@ -395,10 +411,10 @@ }, "COMMAND": "build_windows.cmd", "PARAMETERS": { + "COMMAND_CWD": "%WORKSPACE%\\%PROJECT_REPOSITORY_NAME%", "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_MODULE_PATH=!WORKSPACE!/o3de/install/cmake", - "CMAKE_LY_PROJECTS": "", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_MODULE_PATH=%WORKSPACE%/o3de/install/cmake", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" } diff --git a/scripts/build/Platform/Windows/build_windows.cmd b/scripts/build/Platform/Windows/build_windows.cmd index b5a0245d1a..b9e862e04f 100644 --- a/scripts/build/Platform/Windows/build_windows.cmd +++ b/scripts/build/Platform/Windows/build_windows.cmd @@ -9,6 +9,13 @@ REM SETLOCAL EnableDelayedExpansion +REM Jenkins reports MSB8029 when TMP/TEMP is not defined, define a dummy folder +SET TMP=%cd%/temp +SET TEMP=%cd%/temp +IF NOT EXIST %TMP% ( + MKDIR temp +) + CALL %~dp0env_windows.cmd IF NOT EXIST "%OUTPUT_DIRECTORY%" ( @@ -25,18 +32,14 @@ IF ERRORLEVEL 1 ( exit /b 1 ) -REM Jenkins reports MSB8029 when TMP/TEMP is not defined, define a dummy folder -SET TMP=%cd%/temp -SET TEMP=%cd%/temp -IF NOT EXIST %TMP% ( - MKDIR temp -) - REM Compute half the amount of processors so some jobs can run SET /a HALF_PROCESSORS = NUMBER_OF_PROCESSORS / 2 SET LAST_CONFIGURE_CMD_FILE=ci_last_configure_cmd.txt -SET CONFIGURE_CMD=cmake %SOURCE_DIRECTORY% %CMAKE_OPTIONS% %EXTRA_CMAKE_OPTIONS% -DLY_3RDPARTY_PATH="%LY_3RDPARTY_PATH%" -DLY_PROJECTS=%CMAKE_LY_PROJECTS% +SET CONFIGURE_CMD=cmake %SOURCE_DIRECTORY% %CMAKE_OPTIONS% %EXTRA_CMAKE_OPTIONS% -DLY_3RDPARTY_PATH="%LY_3RDPARTY_PATH%" +IF NOT "%CMAKE_LY_PROJECTS%"=="" ( + SET CONFIGURE_CMD=!CONFIGURE_CMD! -DLY_PROJECTS="%CMAKE_LY_PROJECTS%" +) IF NOT EXIST CMakeCache.txt ( ECHO [ci_build] First run, generating SET RUN_CONFIGURE=1 diff --git a/scripts/build/Platform/Windows/env_windows.cmd b/scripts/build/Platform/Windows/env_windows.cmd index 1c54e36bfc..f11d394519 100644 --- a/scripts/build/Platform/Windows/env_windows.cmd +++ b/scripts/build/Platform/Windows/env_windows.cmd @@ -13,6 +13,11 @@ IF NOT %ERRORLEVEL%==0 ( GOTO :error ) +IF NOT "%COMMAND_CWD%"=="" ( + ECHO [ci_build] Changing CWD to %COMMAND_CWD% + CD %COMMAND_CWD% +) + EXIT /b 0 :error From 139915990849e2297ddff0194cc6d8a439cd0cfc Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Fri, 5 Nov 2021 14:39:11 -0500 Subject: [PATCH 094/177] Fix Assert Absorber being leaked due to one of the tests setting m_errorAbsorber to nullptr without deleting the object (#5176) (#5348) Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> (cherry picked from commit 916fb413c93f92d3c4aea46dac4d7908b48a10fa) --- .../AssetProcessor/native/tests/AssetProcessorTest.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h index 719b04610c..8f26155fd3 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h @@ -25,7 +25,7 @@ namespace AssetProcessor : public ::testing::Test { protected: - UnitTestUtils::AssertAbsorber* m_errorAbsorber; + AZStd::unique_ptr m_errorAbsorber{}; FileStatePassthrough m_fileStateCache; void SetUp() override @@ -40,7 +40,7 @@ namespace AssetProcessor m_ownsSysAllocator = true; AZ::AllocatorInstance::Create(); } - m_errorAbsorber = new UnitTestUtils::AssertAbsorber(); + m_errorAbsorber = AZStd::make_unique(); m_application = AZStd::make_unique(); @@ -62,8 +62,8 @@ namespace AssetProcessor AssetUtilities::ResetAssetRoot(); m_application.reset(); - delete m_errorAbsorber; - m_errorAbsorber = nullptr; + m_errorAbsorber.reset(); + if (m_ownsSysAllocator) { AZ::AllocatorInstance::Destroy(); From 060c2178522e0c9a5d3664370593a43ec81a17b6 Mon Sep 17 00:00:00 2001 From: Jonny Gallowy Date: Fri, 5 Nov 2021 15:00:35 -0500 Subject: [PATCH 095/177] Fixed .bat chain for launching maya for AtomContent gems Signed-off-by: Jonny Gallowy --- .../ReferenceMaterials/Tools/Launch_Cmd.bat | 9 +++-- .../ReferenceMaterials/Tools/Launch_Maya.bat | 11 +++---- .../ReferenceMaterials/Tools/Project_Env.bat | 33 +++++++++---------- Gems/AtomContent/Sponza/Tools/Launch_Cmd.bat | 7 ++-- Gems/AtomContent/Sponza/Tools/Launch_Maya.bat | 8 +++-- Gems/AtomContent/Sponza/Tools/Project_Env.bat | 33 +++++++++---------- .../Tools/Dev/Windows/Env_Maya.bat | 2 +- 7 files changed, 48 insertions(+), 55 deletions(-) diff --git a/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Cmd.bat b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Cmd.bat index d100c9ddc7..0b94be5bea 100644 --- a/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Cmd.bat +++ b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Cmd.bat @@ -1,4 +1,6 @@ @echo off +:: Keep changes local +SETLOCAL enableDelayedExpansion REM REM Copyright (c) Contributors to the Open 3D Engine Project @@ -13,7 +15,7 @@ REM :: Puts you in the CMD within the dev environment :: Set up window -TITLE O3DE Asset Gem Cmd +TITLE O3DE DCC Scripting Interface Cmd :: Use obvious color to prevent confusion (Grey with Yellow Text) COLOR 8E @@ -21,15 +23,12 @@ COLOR 8E cd %~dp0 PUSHD %~dp0 -:: Keep changes local -SETLOCAL enableDelayedExpansion - CALL %~dp0\Project_Env.bat echo. echo _____________________________________________________________________ echo. -echo ~ O3DE Asset Gem CMD ... +echo ~ O3DE %O3DE_PROJECT% Asset Gem CMD ... echo _____________________________________________________________________ echo. diff --git a/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat index b9a6b399f3..c0f5f589d7 100644 --- a/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat +++ b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat @@ -1,6 +1,3 @@ -:: Launches maya wityh a bunch of local hooks for Lumberyard -:: ToDo: move all of this to a .json data driven boostrapping system - @echo off REM @@ -37,7 +34,7 @@ echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% IF EXIST "%~dp0Project_Env.bat" CALL %~dp0Project_Env.bat echo ________________________________ -echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard... +echo Launching Maya %DCCSI_MAYA_VERSION% for O3DE: %O#DE_PROJECT%... :::: Set Maya native project acess to this project ::set MAYA_PROJECT=%LY_PROJECT% @@ -47,8 +44,10 @@ echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard... Set MAYA_VP2_DEVICE_OVERRIDE = VirtualDeviceDx11 :: Default to the right version of Maya if we can detect it... and launch -IF EXIST "%MAYA_LOCATION%\bin\Maya.exe" ( - start "" "%MAYA_LOCATION%\bin\Maya.exe" %* +echo MAYA_BIN_PATH = %MAYA_BIN_PATH% + +IF EXIST "%MAYA_BIN_PATH%\Maya.exe" ( + start "" "%MAYA_BIN_PATH%\Maya.exe" %* ) ELSE ( Where maya.exe 2> NUL IF ERRORLEVEL 1 ( diff --git a/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat b/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat index 6e2c8b5914..134e238384 100644 --- a/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat +++ b/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat @@ -29,23 +29,23 @@ PUSHD %~dp0 set ABS_PATH=%~dp0 :: project name as a str tag -IF "%LY_PROJECT_NAME%"=="" ( - for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set LY_PROJECT_NAME=%%~nxJ +IF "%O3DE_PROJECT%"=="" ( + for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set O3DE_PROJECT=%%~nxJ ) echo. echo _____________________________________________________________________ echo. -echo ~ Setting up O3DE %LY_PROJECT_NAME% Environment ... +echo ~ Setting up O3DE %O3DE_PROJECT% Environment ... echo _____________________________________________________________________ echo. -echo LY_PROJECT_NAME = %LY_PROJECT_NAME% +echo O3DE_PROJECT = %O3DE_PROJECT% :: if the user has set up a custom env call it :: this should allow the user to locally -:: set env hooks like LY_DEV or LY_PROJECT +:: set env hooks like O3DE_DEV or O3DE_PROJECT_PATH IF EXIST "%~dp0User_Env.bat" CALL %~dp0User_Env.bat -echo LY_DEV = %LY_DEV% +echo O3DE_DEV = %O3DE_DEV% :: Constant Vars (Global) :: global debug flag (propogates) @@ -74,23 +74,20 @@ echo DCCSI_LOGLEVEL = %DCCSI_LOGLEVEL% IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=2020) echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% -:: LY_PROJECT is ideally treated as a full path in the env launchers +:: O3DE_PROJECT_PATH is ideally treated as a full path in the env launchers :: do to changes in o3de, external engine/project/gem folder structures, etc. -IF "%LY_PROJECT%"=="" ( - for %%i in ("%~dp0..") do set "LY_PROJECT=%%~fi" +IF "%O3DE_PROJECT_PATH%"=="" ( + for %%i in ("%~dp0..") do set "O3DE_PROJECT_PATH=%%~fi" ) -echo LY_PROJECT = %LY_PROJECT% - -:: this is here for archaic reasons, WILL DEPRECATE -IF "%LY_PROJECT_PATH%"=="" (set LY_PROJECT_PATH=%LY_PROJECT%) -echo LY_PROJECT_PATH = %LY_PROJECT_PATH% +echo O3DE_PROJECT_PATH = %O3DE_PROJECT_PATH% :: Change to root Lumberyard dev dir -:: You must set this in a User_Env.bat to match youe engine repo location! -IF "%LY_DEV%"=="" (set LY_DEV=C:\Depot\o3de-engine) -echo LY_DEV = %LY_DEV% +IF "%O3DE_DEV%"=="" echo ~ You must set O3DE_DEV in a User_Env.bat to match your local engine repo! +IF "%O3DE_DEV%"=="" echo ~ Using default O3DE_DEV=C:\Depot\o3de-engine +IF "%O3DE_DEV%"=="" (set O3DE_DEV=C:\Depot\o3de-engine) +echo O3DE_DEV = %O3DE_DEV% -CALL %LY_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Env_Maya.bat +CALL %O3DE_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Tools\Dev\Windows\Env_Maya.bat :: Restore original directory popd diff --git a/Gems/AtomContent/Sponza/Tools/Launch_Cmd.bat b/Gems/AtomContent/Sponza/Tools/Launch_Cmd.bat index 99c2c12c51..0b94be5bea 100644 --- a/Gems/AtomContent/Sponza/Tools/Launch_Cmd.bat +++ b/Gems/AtomContent/Sponza/Tools/Launch_Cmd.bat @@ -1,4 +1,6 @@ @echo off +:: Keep changes local +SETLOCAL enableDelayedExpansion REM REM Copyright (c) Contributors to the Open 3D Engine Project @@ -21,15 +23,12 @@ COLOR 8E cd %~dp0 PUSHD %~dp0 -:: Keep changes local -SETLOCAL enableDelayedExpansion - CALL %~dp0\Project_Env.bat echo. echo _____________________________________________________________________ echo. -echo ~ LY DCC Scripting Interface CMD ... +echo ~ O3DE %O3DE_PROJECT% Asset Gem CMD ... echo _____________________________________________________________________ echo. diff --git a/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat b/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat index d774adf79b..c0f5f589d7 100644 --- a/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat +++ b/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat @@ -34,7 +34,7 @@ echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% IF EXIST "%~dp0Project_Env.bat" CALL %~dp0Project_Env.bat echo ________________________________ -echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard... +echo Launching Maya %DCCSI_MAYA_VERSION% for O3DE: %O#DE_PROJECT%... :::: Set Maya native project acess to this project ::set MAYA_PROJECT=%LY_PROJECT% @@ -44,8 +44,10 @@ echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard... Set MAYA_VP2_DEVICE_OVERRIDE = VirtualDeviceDx11 :: Default to the right version of Maya if we can detect it... and launch -IF EXIST "%MAYA_LOCATION%\bin\Maya.exe" ( - start "" "%MAYA_LOCATION%\bin\Maya.exe" %* +echo MAYA_BIN_PATH = %MAYA_BIN_PATH% + +IF EXIST "%MAYA_BIN_PATH%\Maya.exe" ( + start "" "%MAYA_BIN_PATH%\Maya.exe" %* ) ELSE ( Where maya.exe 2> NUL IF ERRORLEVEL 1 ( diff --git a/Gems/AtomContent/Sponza/Tools/Project_Env.bat b/Gems/AtomContent/Sponza/Tools/Project_Env.bat index 6e2c8b5914..134e238384 100644 --- a/Gems/AtomContent/Sponza/Tools/Project_Env.bat +++ b/Gems/AtomContent/Sponza/Tools/Project_Env.bat @@ -29,23 +29,23 @@ PUSHD %~dp0 set ABS_PATH=%~dp0 :: project name as a str tag -IF "%LY_PROJECT_NAME%"=="" ( - for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set LY_PROJECT_NAME=%%~nxJ +IF "%O3DE_PROJECT%"=="" ( + for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set O3DE_PROJECT=%%~nxJ ) echo. echo _____________________________________________________________________ echo. -echo ~ Setting up O3DE %LY_PROJECT_NAME% Environment ... +echo ~ Setting up O3DE %O3DE_PROJECT% Environment ... echo _____________________________________________________________________ echo. -echo LY_PROJECT_NAME = %LY_PROJECT_NAME% +echo O3DE_PROJECT = %O3DE_PROJECT% :: if the user has set up a custom env call it :: this should allow the user to locally -:: set env hooks like LY_DEV or LY_PROJECT +:: set env hooks like O3DE_DEV or O3DE_PROJECT_PATH IF EXIST "%~dp0User_Env.bat" CALL %~dp0User_Env.bat -echo LY_DEV = %LY_DEV% +echo O3DE_DEV = %O3DE_DEV% :: Constant Vars (Global) :: global debug flag (propogates) @@ -74,23 +74,20 @@ echo DCCSI_LOGLEVEL = %DCCSI_LOGLEVEL% IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=2020) echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% -:: LY_PROJECT is ideally treated as a full path in the env launchers +:: O3DE_PROJECT_PATH is ideally treated as a full path in the env launchers :: do to changes in o3de, external engine/project/gem folder structures, etc. -IF "%LY_PROJECT%"=="" ( - for %%i in ("%~dp0..") do set "LY_PROJECT=%%~fi" +IF "%O3DE_PROJECT_PATH%"=="" ( + for %%i in ("%~dp0..") do set "O3DE_PROJECT_PATH=%%~fi" ) -echo LY_PROJECT = %LY_PROJECT% - -:: this is here for archaic reasons, WILL DEPRECATE -IF "%LY_PROJECT_PATH%"=="" (set LY_PROJECT_PATH=%LY_PROJECT%) -echo LY_PROJECT_PATH = %LY_PROJECT_PATH% +echo O3DE_PROJECT_PATH = %O3DE_PROJECT_PATH% :: Change to root Lumberyard dev dir -:: You must set this in a User_Env.bat to match youe engine repo location! -IF "%LY_DEV%"=="" (set LY_DEV=C:\Depot\o3de-engine) -echo LY_DEV = %LY_DEV% +IF "%O3DE_DEV%"=="" echo ~ You must set O3DE_DEV in a User_Env.bat to match your local engine repo! +IF "%O3DE_DEV%"=="" echo ~ Using default O3DE_DEV=C:\Depot\o3de-engine +IF "%O3DE_DEV%"=="" (set O3DE_DEV=C:\Depot\o3de-engine) +echo O3DE_DEV = %O3DE_DEV% -CALL %LY_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Env_Maya.bat +CALL %O3DE_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Tools\Dev\Windows\Env_Maya.bat :: Restore original directory popd diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Maya.bat b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Maya.bat index 5e3c600124..0ed46e9400 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Maya.bat +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Tools/Dev/Windows/Env_Maya.bat @@ -46,7 +46,7 @@ echo DCCSI_PY_VERSION_RELEASE = %DCCSI_PY_VERSION_RELEASE% echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% :::: Set Maya native project acess to this project -IF "%MAYA_PROJECT%"=="" (set MAYA_PROJECT=%O3DE_PROJECT%) +IF "%MAYA_PROJECT%"=="" (set MAYA_PROJECT=%O3DE_PROJECT_PATH%) echo MAYA_PROJECT = %MAYA_PROJECT% :: maya sdk path From 2206d2d8f10c5a2b7b9e18425c52e42af86e40e6 Mon Sep 17 00:00:00 2001 From: Brian Herrera Date: Fri, 5 Nov 2021 12:57:03 -0700 Subject: [PATCH 096/177] Add restricted folder to gitignore Signed-off-by: brianherrera --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3a9b8f6f8e..5f6172cd76 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ Editor/EditorEventLog.xml Editor/EditorLayout.xml **/*egg-info/** **/*egg-link +**/[Rr]estricted UserSettings.xml [Uu]ser/ FrameCapture/** From a2efc587ccbfee147e562348082386cfbe852d44 Mon Sep 17 00:00:00 2001 From: Ken Pruiksma Date: Fri, 5 Nov 2021 15:31:31 -0500 Subject: [PATCH 097/177] Rendered World Size in the Terrain World Render component set to invisible (#5378) It's not currently hooked up, so setting invisible for now until it does something. Signed-off-by: Ken Pruiksma --- .../Code/Source/Components/TerrainWorldRendererComponent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp index 9cd9ce9fb2..5afaedc517 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldRendererComponent.cpp @@ -46,6 +46,7 @@ namespace Terrain ->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_4096Meters, "4 Kilometers") ->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_8192Meters, "8 Kilometers") ->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_16384Meters, "16 Kilometers") + ->Attribute(AZ::Edit::Attributes::Visibility, false) // Keeping invisible until it's hooked up under the hood ; } } From 7d1e1474b5075e70064db25d22cce15509bddf22 Mon Sep 17 00:00:00 2001 From: Jonny Gallowy Date: Fri, 5 Nov 2021 15:50:54 -0500 Subject: [PATCH 098/177] fixed typo Signed-off-by: Jonny Gallowy --- Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat | 2 +- Gems/AtomContent/Sponza/Tools/Launch_Maya.bat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat index c0f5f589d7..0af25905a0 100644 --- a/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat +++ b/Gems/AtomContent/ReferenceMaterials/Tools/Launch_Maya.bat @@ -34,7 +34,7 @@ echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% IF EXIST "%~dp0Project_Env.bat" CALL %~dp0Project_Env.bat echo ________________________________ -echo Launching Maya %DCCSI_MAYA_VERSION% for O3DE: %O#DE_PROJECT%... +echo Launching Maya %DCCSI_MAYA_VERSION% for O3DE: %O3DE_PROJECT%... :::: Set Maya native project acess to this project ::set MAYA_PROJECT=%LY_PROJECT% diff --git a/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat b/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat index c0f5f589d7..0af25905a0 100644 --- a/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat +++ b/Gems/AtomContent/Sponza/Tools/Launch_Maya.bat @@ -34,7 +34,7 @@ echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION% IF EXIST "%~dp0Project_Env.bat" CALL %~dp0Project_Env.bat echo ________________________________ -echo Launching Maya %DCCSI_MAYA_VERSION% for O3DE: %O#DE_PROJECT%... +echo Launching Maya %DCCSI_MAYA_VERSION% for O3DE: %O3DE_PROJECT%... :::: Set Maya native project acess to this project ::set MAYA_PROJECT=%LY_PROJECT% From 5138170d8c037104b50b65dc15e116ef55528b2c Mon Sep 17 00:00:00 2001 From: Jonny Gallowy Date: Fri, 5 Nov 2021 15:54:57 -0500 Subject: [PATCH 099/177] fixed typo Signed-off-by: Jonny Gallowy --- Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat | 2 +- Gems/AtomContent/Sponza/Tools/Project_Env.bat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat b/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat index 134e238384..ddf934d206 100644 --- a/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat +++ b/Gems/AtomContent/ReferenceMaterials/Tools/Project_Env.bat @@ -81,7 +81,7 @@ IF "%O3DE_PROJECT_PATH%"=="" ( ) echo O3DE_PROJECT_PATH = %O3DE_PROJECT_PATH% -:: Change to root Lumberyard dev dir +:: Change to root O3DE dev dir IF "%O3DE_DEV%"=="" echo ~ You must set O3DE_DEV in a User_Env.bat to match your local engine repo! IF "%O3DE_DEV%"=="" echo ~ Using default O3DE_DEV=C:\Depot\o3de-engine IF "%O3DE_DEV%"=="" (set O3DE_DEV=C:\Depot\o3de-engine) diff --git a/Gems/AtomContent/Sponza/Tools/Project_Env.bat b/Gems/AtomContent/Sponza/Tools/Project_Env.bat index 134e238384..ddf934d206 100644 --- a/Gems/AtomContent/Sponza/Tools/Project_Env.bat +++ b/Gems/AtomContent/Sponza/Tools/Project_Env.bat @@ -81,7 +81,7 @@ IF "%O3DE_PROJECT_PATH%"=="" ( ) echo O3DE_PROJECT_PATH = %O3DE_PROJECT_PATH% -:: Change to root Lumberyard dev dir +:: Change to root O3DE dev dir IF "%O3DE_DEV%"=="" echo ~ You must set O3DE_DEV in a User_Env.bat to match your local engine repo! IF "%O3DE_DEV%"=="" echo ~ Using default O3DE_DEV=C:\Depot\o3de-engine IF "%O3DE_DEV%"=="" (set O3DE_DEV=C:\Depot\o3de-engine) From 46f4935ee44693b17e884237810d0428bb4825a6 Mon Sep 17 00:00:00 2001 From: Mike Chang Date: Fri, 5 Nov 2021 14:04:05 -0700 Subject: [PATCH 100/177] Update to licenses/notices script (#5214) This updates the licenses script to pull in all PackageInfo.json files with a specific argument, then follows each license file defined and writes the contents to a file. In this mode, if a packageinfo file is found, it will only grab the license file path defined within. Also has the following features: * Generalizes the function and variable names for non-license specific references * Sorts os.walk to maintain consistent ordering * Uses an ordered dict for the output, also to maintain ordering if using Python below 3.7 * Adds an additional json config file to have specific exclusion rules for 3p packages * Adds a package creation function and config file entry * Allow multipath scans, optional use of gitignore, merged license file scan Signed-off-by: Mike Chang --- scripts/license_scanner/license_scanner.py | 140 ++++++++++++++------ scripts/license_scanner/scanner_config.json | 3 + 2 files changed, 103 insertions(+), 40 deletions(-) diff --git a/scripts/license_scanner/license_scanner.py b/scripts/license_scanner/license_scanner.py index c0e3c1f1ba..2936e343f9 100644 --- a/scripts/license_scanner/license_scanner.py +++ b/scripts/license_scanner/license_scanner.py @@ -6,6 +6,7 @@ # import argparse +from collections import OrderedDict import fnmatch import json import os @@ -24,15 +25,19 @@ class LicenseScanner: """ DEFAULT_CONFIG_FILE = 'scanner_config.json' + DEFAULT_EXCLUDE_FILE = '.gitignore' + DEFAULT_PACKAGE_INFO_FILE = 'PackageInfo.json' def __init__(self, config_file=None): self.config_file = config_file self.config_data = self._load_config() - self.license_regex = self._load_license_regex() + self.file_regex = self._load_file_regex(self.config_data['license_patterns']) + self.package_info = self._load_file_regex(self.config_data['package_patterns']) + self.excluded_directories = self._load_file_regex(self.config_data['excluded_directories']) def _load_config(self): """Load config from the provided file. Sets default file if one is not provided.""" - if self.config_file is None: + if not self.config_file: script_directory = os.path.dirname(os.path.abspath(__file__)) # Default file expected in same dir as script self.config_file = os.path.join(script_directory, self.DEFAULT_CONFIG_FILE) @@ -43,45 +48,68 @@ class LicenseScanner: print('Config file cannot be found') raise - def _load_license_regex(self): + def _load_file_regex(self, patterns): """Returns regex object with case-insensitive matching from the list of filename patterns.""" regex_patterns = [] - for pattern in self.config_data['license_patterns']: + for pattern in patterns: regex_patterns.append(fnmatch.translate(pattern)) + + if not regex_patterns: + print(f'Warning: No patterns from {patterns} found') + return None + return re.compile('|'.join(regex_patterns), re.IGNORECASE) - def scan(self, path=os.curdir): - """Scan directory tree for filenames matching license_regex. + def scan(self, paths=os.curdir): + """Scan directory tree for filenames matching file_regex, package info, and exclusion files. - :param path: Path of the directory to run scanner - :return: Package paths and their corresponding license file contents - :rtype: dict + :param paths: Paths of the directory to run scanner + :return: Package paths and their corresponding file contents + :rtype: Ordered dict """ - licenses = 0 - license_files = {} + files = 0 + matching_files = OrderedDict() + excluded_directories = None - for dirpath, dirnames, filenames in os.walk(path): - for file in filenames: - if self.license_regex.match(file): - license_file_content = self._get_license_file_contents(os.path.join(dirpath, file)) - rel_dirpath = os.path.relpath(dirpath, path) # Limit path inside scanned directory - license_files[rel_dirpath] = license_file_content - licenses += 1 - print(f'License file: {os.path.join(dirpath, file)}') + if not self.package_info: + self.package_info = self.DEFAULT_PACKAGE_INFO_FILE - # Remove directories that should not be scanned - for dir in self.config_data['excluded_directories']: - if dir in dirnames: - dirnames.remove(dir) - print(f'{licenses} license files found.') - return license_files + if not self.excluded_directories: + print(f'No excluded directory in config, looking for {self.DEFAULT_EXCLUDE_FILE} instead') - def _get_license_file_contents(self, filepath): + for path in paths: + for dirpath, dirnames, filenames in os.walk(path, topdown=True): + dirnames.sort(key=str.casefold) # Ensure that results are sorted + for file in filenames: + if self.file_regex.match(file) or self.package_info.match(file): + file_path = os.path.join(dirpath, file) + matching_file_content = self._get_file_contents(file_path) + matching_files[file_path] = matching_file_content + files += 1 + print(f'Matching file: {file_path}') + if self.package_info.match(file): + dirnames[:] = [] # Stop scanning subdirectories if package info file found + if self.DEFAULT_EXCLUDE_FILE in file and not self.excluded_directories: + ignore_list = self._get_file_contents(os.path.join(dirpath, file)).splitlines() + ignore_list.append('.git') # .gitignore doesn't usually have .git in its exclusions + excluded_directories = self._load_file_regex(ignore_list) + + # Remove directories that should not be scanned + if self.excluded_directories: + excluded_directories = self.excluded_directories + for dir in dirnames: + if excluded_directories.match(dir): + dirnames.remove(dir) + + print(f'{files} files found.') + return matching_files + + def _get_file_contents(self, filepath): try: with open(filepath, encoding='utf8') as f: return f.read() except UnicodeDecodeError: - print(f'Unable to read license file: {filepath}') + print(f'Unable to read file: {filepath}') pass def create_license_file(self, licenses, filepath='NOTICES.txt'): @@ -89,18 +117,44 @@ class LicenseScanner: :param licenses: Dict with package paths and their corresponding license file contents :param filepath: Path to write the file - """ - package_separator = '------------------------------------' - with open(filepath, 'w', encoding='utf8') as f: + """ + license_separator = '------------------------------------' + with open(filepath, 'w', encoding='utf8') as lf: for directory, license in licenses.items(): - license_output = '\n\n'.join([ - f'{package_separator}', - f'Package path: {directory}', - 'License:', - f'{license}\n' - ]) - f.write(license_output) + if not self.package_info.match(os.path.basename(directory)): + license_output = '\n\n'.join([ + f'{license_separator}', + f'Package path: {os.path.relpath(directory)}', + 'License:', + f'{license}\n' + ]) + lf.write(license_output) return None + + def create_package_file(self, packages, filepath='SPDX-Licenses.json', get_contents=False): + """Creates file with all the provided SPDX package info summaries in json. + Optional dirpath parameter will follow the license file path in the package info and return its contents in a dictionary + + :param licenses: Dict with package info paths and their corresponding file contents + :param filepath: Path to write the file + :param dirpath: Root path for packages + :rtype: Ordered dict + """ + licenses = OrderedDict() + package_json = [] + + with open(filepath, 'w', encoding='utf8') as pf: + for directory, package in packages.items(): + if self.package_info.match(os.path.basename(directory)): + package_obj = json.loads(package) + package_json.append(package_obj) + if get_contents: + license_path = os.path.join(os.path.dirname(directory), pathlib.Path(package_obj['LicenseFile'])) + licenses[license_path] = self._get_file_contents(license_path) + else: + licenses[directory] = package + pf.write(json.dumps(package_json, indent=4)) + return licenses def parse_args(): @@ -108,7 +162,8 @@ def parse_args(): description='Script to run LicenseScanner and generate license file') parser.add_argument('--config-file', '-c', type=pathlib.Path, help='Config file for LicenseScanner') parser.add_argument('--license-file-path', '-l', type=pathlib.Path, help='Create license file in the provided path') - parser.add_argument('--scan-path', '-s', default=os.curdir, type=pathlib.Path, help='Path to scan') + parser.add_argument('--package-file-path', '-p', type=pathlib.Path, help='Create package summary file in the provided path') + parser.add_argument('--scan-path', '-s', default=os.curdir, type=pathlib.Path, nargs='+', help='Path to scan, multiple space separated paths can be used') return parser.parse_args() @@ -116,10 +171,15 @@ def main(): try: args = parse_args() ls = LicenseScanner(args.config_file) - licenses = ls.scan(args.scan_path) + scanned_path_data = ls.scan(args.scan_path) if args.license_file_path: - ls.create_license_file(licenses, args.license_file_path) + ls.create_license_file(scanned_path_data, args.license_file_path) + if args.package_file_path: + ls.create_package_file(scanned_path_data, args.package_file_path) + if args.license_file_path and args.package_file_path: + license_files = ls.create_package_file(scanned_path_data, args.package_file_path, True) + ls.create_license_file(license_files, args.license_file_path) except FileNotFoundError as e: print(f'Type: {type(e).__name__}, Error: {e}') return 1 diff --git a/scripts/license_scanner/scanner_config.json b/scripts/license_scanner/scanner_config.json index b5863a7d31..2e8f5206db 100644 --- a/scripts/license_scanner/scanner_config.json +++ b/scripts/license_scanner/scanner_config.json @@ -8,5 +8,8 @@ "license_patterns": [ "LICENSE*", "COPYING*" + ], + "package_patterns": [ + "PackageInfo.json" ] } From 6b6eb2c93638b498185b8f9b2bcc51d0f4366494 Mon Sep 17 00:00:00 2001 From: Jeremy Ong Date: Fri, 5 Nov 2021 15:34:35 -0600 Subject: [PATCH 101/177] Remove markers that occupy <2us for 99% of events Signed-off-by: Jeremy Ong --- .../AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.cpp | 2 -- Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp | 3 --- Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp | 2 -- 3 files changed, 7 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.cpp b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.cpp index 230bf959f6..c05590ca91 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.cpp +++ b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobManagerWorkStealing.cpp @@ -457,8 +457,6 @@ void JobManagerWorkStealing::ProcessJobsInternal(ThreadInfo* info, Job* suspende else { //attempt to steal a job from another thread's queue - AZ_PROFILE_SCOPE(AzCore, "JobManagerWorkStealing::ProcessJobsInternal:WorkStealing"); - unsigned int numStealAttempts = 0; const unsigned int maxStealAttempts = (unsigned int)m_workerThreads.size() * 3; //try every thread a few times before giving up while (!job) diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp index 11ae78c69a..eb4fb46cc9 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp @@ -152,8 +152,6 @@ namespace AZ ResultCode FrameScheduler::ImportScopeProducer(ScopeProducer& scopeProducer) { - AZ_PROFILE_SCOPE(RHI, "FrameScheduler: ImportScopeProducer"); - if (!ValidateIsProcessing()) { return RHI::ResultCode::InvalidOperation; @@ -266,7 +264,6 @@ namespace AZ // Execute all queued resource invalidations, which will mark SRG's for compilation. { - AZ_PROFILE_SCOPE(RHI, "Invalidate Resources"); ResourceInvalidateBus::ExecuteQueuedEvents(); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp index d9f98c11d3..5001951377 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp @@ -216,8 +216,6 @@ namespace AZ void RasterPass::CompileResources(const RHI::FrameGraphCompileContext& context) { - AZ_PROFILE_SCOPE(RPI, "RasterPass: CompileResources"); - if (m_shaderResourceGroup == nullptr) { return; From 4ce39ea1675e6ba2b102bef02e4226f5b99ed081 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Fri, 5 Nov 2021 17:17:11 -0700 Subject: [PATCH 102/177] Fix tags, downloads, and several vector copies Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> --- .../Source/CreateProjectCtrl.cpp | 12 +-- .../Source/DownloadController.cpp | 3 +- .../Source/DownloadController.h | 2 +- .../GemCatalog/GemCatalogHeaderWidget.cpp | 24 +++--- .../GemCatalog/GemCatalogHeaderWidget.h | 2 +- .../Source/GemCatalog/GemCatalogScreen.cpp | 78 +++++++++++++++---- .../Source/GemCatalog/GemCatalogScreen.h | 4 +- .../Source/GemCatalog/GemInspector.cpp | 8 +- .../Source/GemCatalog/GemInspector.h | 2 +- .../Source/GemCatalog/GemModel.cpp | 32 ++++---- .../Source/GemCatalog/GemModel.h | 4 +- .../Source/GemRepo/GemRepoInspector.cpp | 2 +- .../Source/GemRepo/GemRepoModel.cpp | 12 +-- .../Source/GemRepo/GemRepoModel.h | 2 +- .../ProjectManager/Source/GemsSubWidget.cpp | 6 +- .../ProjectManager/Source/GemsSubWidget.h | 4 +- .../Tools/ProjectManager/Source/TagWidget.cpp | 39 +++++++--- Code/Tools/ProjectManager/Source/TagWidget.h | 22 +++++- .../Source/UpdateProjectCtrl.cpp | 3 +- .../ProjectManager/Source/UpdateProjectCtrl.h | 1 + 20 files changed, 171 insertions(+), 91 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp index 1aad4a7206..6b19e839d7 100644 --- a/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp @@ -55,11 +55,7 @@ namespace O3DE::ProjectManager vLayout->addWidget(m_stack); connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest); - connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, [this]() - { - const QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath(); - m_gemCatalogScreen->Refresh(projectTemplatePath + "/Template"); - }); + connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, m_gemCatalogScreen, &GemCatalogScreen::Refresh); // When there are multiple project templates present, we re-gather the gems when changing the selected the project template. connect(m_newProjectSettingsScreen, &NewProjectSettingsScreen::OnTemplateSelectionChanged, this, [=](int oldIndex, [[maybe_unused]] int newIndex) @@ -257,6 +253,12 @@ namespace O3DE::ProjectManager { if (m_newProjectSettingsScreen->Validate()) { + if (!m_gemCatalogScreen->GetDownloadController()->IsDownloadQueueEmpty()) + { + QMessageBox::critical(this, tr("Gems downloading"), tr("You must wait for gems to finish downloading before continuing.")); + return; + } + ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo(); QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath(); diff --git a/Code/Tools/ProjectManager/Source/DownloadController.cpp b/Code/Tools/ProjectManager/Source/DownloadController.cpp index 224b90299c..a8eecffd78 100644 --- a/Code/Tools/ProjectManager/Source/DownloadController.cpp +++ b/Code/Tools/ProjectManager/Source/DownloadController.cpp @@ -82,8 +82,9 @@ namespace O3DE::ProjectManager succeeded = false; } + const QString gemName = m_gemNames[0]; m_gemNames.erase(m_gemNames.begin()); - emit Done(succeeded); + emit Done(succeeded, gemName); if (!m_gemNames.empty()) { diff --git a/Code/Tools/ProjectManager/Source/DownloadController.h b/Code/Tools/ProjectManager/Source/DownloadController.h index 11ceaacddb..8afe8ef029 100644 --- a/Code/Tools/ProjectManager/Source/DownloadController.h +++ b/Code/Tools/ProjectManager/Source/DownloadController.h @@ -58,7 +58,7 @@ namespace O3DE::ProjectManager signals: void StartGemDownload(const QString& gemName); - void Done(bool success = true); + void Done(bool success, const QString& gemName); void GemDownloadProgress(int percentage); private: diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 5d65c740af..0db2b088c1 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -145,7 +145,7 @@ namespace O3DE::ProjectManager } else { - tagContainer->Update(ConvertFromModelIndices(tagIndices)); + tagContainer->Update(GetTagsFromModelIndices(tagIndices)); label->setText(QString("%1 %2").arg(tagIndices.size()).arg(tagIndices.size() == 1 ? singularTitle : pluralTitle)); widget->show(); } @@ -234,17 +234,23 @@ namespace O3DE::ProjectManager for (int downloadingGemNumber = 0; downloadingGemNumber < downloadQueue.size(); ++downloadingGemNumber) { QHBoxLayout* nameProgressLayout = new QHBoxLayout(); - TagWidget* newTag = new TagWidget(downloadQueue[downloadingGemNumber]); + + const QString& gemName = downloadQueue[downloadingGemNumber]; + TagWidget* newTag = new TagWidget({gemName, gemName}); nameProgressLayout->addWidget(newTag); + QLabel* progress = new QLabel(downloadingGemNumber == 0? QString("%1%").arg(downloadProgress) : tr("Queued")); nameProgressLayout->addWidget(progress); + QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); nameProgressLayout->addSpacerItem(spacer); - QLabel* cancelText = new QLabel(QString("Cancel").arg(downloadQueue[downloadingGemNumber])); + + QLabel* cancelText = new QLabel(QString("Cancel")); cancelText->setTextInteractionFlags(Qt::LinksAccessibleByMouse); connect(cancelText, &QLabel::linkActivated, this, &CartOverlayWidget::OnCancelDownloadActivated); nameProgressLayout->addWidget(cancelText); downloadingItemLayout->addLayout(nameProgressLayout); + QProgressBar* downloadProgessBar = new QProgressBar(); downloadingItemLayout->addWidget(downloadProgessBar); downloadProgessBar->setValue(downloadingGemNumber == 0 ? downloadProgress : 0); @@ -255,7 +261,7 @@ namespace O3DE::ProjectManager } }; - auto downloadEnded = [=](bool /*success*/) + auto downloadEnded = [=](bool /*success*/, const QString& /*gemName*/) { update(0); // update the list to remove the gem that has finished }; @@ -265,15 +271,15 @@ namespace O3DE::ProjectManager update(0); } - QStringList CartOverlayWidget::ConvertFromModelIndices(const QVector& gems) const + QVector CartOverlayWidget::GetTagsFromModelIndices(const QVector& gems) const { - QStringList gemNames; - gemNames.reserve(gems.size()); + QVector tags; + tags.reserve(gems.size()); for (const QModelIndex& modelIndex : gems) { - gemNames.push_back(GemModel::GetDisplayName(modelIndex)); + tags.push_back({ GemModel::GetDisplayName(modelIndex), GemModel::GetName(modelIndex) }); } - return gemNames; + return tags; } CartButton::CartButton(GemModel* gemModel, DownloadController* downloadController, QWidget* parent) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index 4d17259840..6da78cce7a 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -36,7 +36,7 @@ namespace O3DE::ProjectManager CartOverlayWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr); private: - QStringList ConvertFromModelIndices(const QVector& gems) const; + QVector GetTagsFromModelIndices(const QVector& gems) const; using GetTagIndicesCallback = AZStd::function()>; void CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 4574e8509b..b0e4fa73e1 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -49,6 +49,7 @@ namespace O3DE::ProjectManager connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged); connect(m_headerWidget, &GemCatalogHeaderWidget::OpenGemsRepo, this, &GemCatalogScreen::HandleOpenGemRepo); connect(m_headerWidget, &GemCatalogHeaderWidget::AddGem, this, &GemCatalogScreen::OnAddGemClicked); + connect(m_downloadController, &DownloadController::Done, this, &GemCatalogScreen::OnGemDownloadResult); QHBoxLayout* hLayout = new QHBoxLayout(); hLayout->setMargin(0); @@ -58,7 +59,7 @@ namespace O3DE::ProjectManager m_gemInspector = new GemInspector(m_gemModel, this); m_gemInspector->setFixedWidth(240); - connect(m_gemInspector, &GemInspector::TagClicked, this, &GemCatalogScreen::SelectGem); + connect(m_gemInspector, &GemInspector::TagClicked, [=](const Tag& tag) { SelectGem(tag.id); }); QWidget* filterWidget = new QWidget(this); filterWidget->setFixedWidth(240); @@ -86,6 +87,7 @@ namespace O3DE::ProjectManager void GemCatalogScreen::ReinitForProject(const QString& projectPath) { + m_projectPath = projectPath; m_gemModel->Clear(); m_gemsToRegisterWithProject.clear(); FillModel(projectPath); @@ -155,15 +157,15 @@ namespace O3DE::ProjectManager } } - void GemCatalogScreen::Refresh(const QString& projectPath) + void GemCatalogScreen::Refresh() { QHash gemInfoHash; // create a hash with the gem name as key - AZ::Outcome, AZStd::string> allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath); + const AZ::Outcome, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(m_projectPath); if (allGemInfosResult.IsSuccess()) { - QVector gemInfos = allGemInfosResult.GetValue(); + const QVector& gemInfos = allGemInfosResult.GetValue(); for (const GemInfo& gemInfo : gemInfos) { gemInfoHash.insert(gemInfo.m_name, gemInfo); @@ -171,10 +173,10 @@ namespace O3DE::ProjectManager } // add all the gem repos into the hash - AZ::Outcome, AZStd::string> allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos(); + const AZ::Outcome, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos(); if (allRepoGemInfosResult.IsSuccess()) { - const QVector allRepoGemInfos = allRepoGemInfosResult.GetValue(); + const QVector& allRepoGemInfos = allRepoGemInfosResult.GetValue(); for (const GemInfo& gemInfo : allRepoGemInfos) { if (!gemInfoHash.contains(gemInfo.m_name)) @@ -310,20 +312,22 @@ namespace O3DE::ProjectManager void GemCatalogScreen::FillModel(const QString& projectPath) { - AZ::Outcome, AZStd::string> allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath); + m_projectPath = projectPath; + + const AZ::Outcome, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath); if (allGemInfosResult.IsSuccess()) { // Add all available gems to the model. - const QVector allGemInfos = allGemInfosResult.GetValue(); + const QVector& allGemInfos = allGemInfosResult.GetValue(); for (const GemInfo& gemInfo : allGemInfos) { m_gemModel->AddGem(gemInfo); } - AZ::Outcome, AZStd::string> allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos(); + const AZ::Outcome, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos(); if (allRepoGemInfosResult.IsSuccess()) { - const QVector allRepoGemInfos = allRepoGemInfosResult.GetValue(); + const QVector& allRepoGemInfos = allRepoGemInfosResult.GetValue(); for (const GemInfo& gemInfo : allRepoGemInfos) { // do not add gems that have already been downloaded @@ -342,10 +346,10 @@ namespace O3DE::ProjectManager m_notificationsEnabled = false; // Gather enabled gems for the given project. - auto enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath); + const auto& enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath); if (enabledGemNamesResult.IsSuccess()) { - const QVector enabledGemNames = enabledGemNamesResult.GetValue(); + const QVector& enabledGemNames = enabledGemNamesResult.GetValue(); for (const AZStd::string& enabledGemName : enabledGemNames) { const QModelIndex modelIndex = m_gemModel->FindIndexByNameString(enabledGemName.c_str()); @@ -405,12 +409,24 @@ namespace O3DE::ProjectManager for (const QModelIndex& modelIndex : toBeAdded) { - const QString gemPath = GemModel::GetPath(modelIndex); + const QString& gemPath = GemModel::GetPath(modelIndex); + + // make sure any remote gems we added were downloaded successfully + if (GemModel::GetGemOrigin(modelIndex) == GemInfo::Remote && GemModel::GetDownloadStatus(modelIndex) != GemInfo::Downloaded) + { + QMessageBox::critical( + nullptr, "Cannot add gem that isn't downloaded", + tr("Cannot add gem %1 to project because it isn't downloaded yet or failed to download.") + .arg(GemModel::GetDisplayName(modelIndex))); + + return EnableDisableGemsResult::Failed; + } + const AZ::Outcome result = pythonBindings->AddGemToProject(gemPath, projectPath); if (!result.IsSuccess()) { - QMessageBox::critical(nullptr, "Operation failed", - QString("Cannot add gem %1 to project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str())); + QMessageBox::critical(nullptr, "Failed to add gem to project", + tr("Cannot add gem %1 to project.

Error:
%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str())); return EnableDisableGemsResult::Failed; } @@ -428,8 +444,8 @@ namespace O3DE::ProjectManager const AZ::Outcome result = pythonBindings->RemoveGemFromProject(gemPath, projectPath); if (!result.IsSuccess()) { - QMessageBox::critical(nullptr, "Operation failed", - QString("Cannot remove gem %1 from project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str())); + QMessageBox::critical(nullptr, "Failed to remove gem from project", + tr("Cannot remove gem %1 from project.

Error:
%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str())); return EnableDisableGemsResult::Failed; } @@ -443,6 +459,34 @@ namespace O3DE::ProjectManager emit ChangeScreenRequest(ProjectManagerScreen::GemRepos); } + void GemCatalogScreen::OnGemDownloadResult(bool succeeded, const QString& gemName) + { + if (succeeded) + { + // refresh the information for downloaded gems + const AZ::Outcome, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(m_projectPath); + if (allGemInfosResult.IsSuccess()) + { + // we should find the gem name now in all gem infos + for (const GemInfo& gemInfo : allGemInfosResult.GetValue()) + { + if (gemInfo.m_name == gemName) + { + QModelIndex index = m_gemModel->FindIndexByNameString(gemName); + if (index.isValid()) + { + m_gemModel->setData(index, GemInfo::Downloaded, GemModel::RoleDownloadStatus); + m_gemModel->setData(index, gemInfo.m_path, GemModel::RolePath); + m_gemModel->setData(index, gemInfo.m_path, GemModel::RoleDirectoryLink); + } + + return; + } + } + } + } + } + ProjectManagerScreen GemCatalogScreen::GetScreenEnum() { return ProjectManagerScreen::GemCatalog; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 69ec85586d..6acf43b3eb 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -33,7 +33,6 @@ namespace O3DE::ProjectManager ProjectManagerScreen GetScreenEnum() override; void ReinitForProject(const QString& projectPath); - void Refresh(const QString& projectPath); enum class EnableDisableGemsResult { @@ -50,6 +49,8 @@ namespace O3DE::ProjectManager void OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies); void OnAddGemClicked(); void SelectGem(const QString& gemName); + void OnGemDownloadResult(bool succeeded, const QString& gemName); + void Refresh(); protected: void hideEvent(QHideEvent* event) override; @@ -76,5 +77,6 @@ namespace O3DE::ProjectManager DownloadController* m_downloadController = nullptr; bool m_notificationsEnabled = true; QSet m_gemsToRegisterWithProject; + QString m_projectPath = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp index b0b8cca29a..afbac189ad 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp @@ -106,10 +106,10 @@ namespace O3DE::ProjectManager } // Depending gems - QStringList dependingGems = m_model->GetDependingGemNames(modelIndex); - if (!dependingGems.isEmpty()) + const QVector& dependingGemTags = m_model->GetDependingGemTags(modelIndex); + if (!dependingGemTags.isEmpty()) { - m_dependingGems->Update(tr("Depending Gems"), tr("The following Gems will be automatically enabled with this Gem."), dependingGems); + m_dependingGems->Update(tr("Depending Gems"), tr("The following Gems will be automatically enabled with this Gem."), dependingGemTags); m_dependingGems->show(); } else @@ -222,7 +222,7 @@ namespace O3DE::ProjectManager // Depending gems m_dependingGems = new GemsSubWidget(); - connect(m_dependingGems, &GemsSubWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); }); + connect(m_dependingGems, &GemsSubWidget::TagClicked, this, [=](const Tag& tag){ emit TagClicked(tag); }); m_mainLayout->addWidget(m_dependingGems); m_mainLayout->addSpacing(20); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h index c6548527ab..9a6ad84dea 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h @@ -44,7 +44,7 @@ namespace O3DE::ProjectManager inline constexpr static const char* s_textColor = "#DDDDDD"; signals: - void TagClicked(const QString& tag); + void TagClicked(const Tag& tag); private slots: void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 7c217db65b..88c54de0b3 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -65,7 +65,6 @@ namespace O3DE::ProjectManager appendRow(item); const QModelIndex modelIndex = index(rowCount()-1, 0); - m_nameToIndexMap[gemInfo.m_displayName] = modelIndex; m_nameToIndexMap[gemInfo.m_name] = modelIndex; } @@ -178,18 +177,6 @@ namespace O3DE::ProjectManager return {}; } - void GemModel::FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames) - { - for (QString& name : inOutGemNames) - { - QModelIndex modelIndex = FindIndexByNameString(name); - if (modelIndex.isValid()) - { - name = GetDisplayName(modelIndex); - } - } - } - QStringList GemModel::GetDependingGems(const QModelIndex& modelIndex) { return modelIndex.data(RoleDependingGems).toStringList(); @@ -209,16 +196,23 @@ namespace O3DE::ProjectManager } } - QStringList GemModel::GetDependingGemNames(const QModelIndex& modelIndex) + QVector GemModel::GetDependingGemTags(const QModelIndex& modelIndex) { - QStringList result = GetDependingGems(modelIndex); - if (result.isEmpty()) + QVector tags; + + QStringList dependingGemNames = GetDependingGems(modelIndex); + tags.reserve(dependingGemNames.size()); + + for (QString& gemName : dependingGemNames) { - return {}; + const QModelIndex& dependingIndex = FindIndexByNameString(gemName); + if (dependingIndex.isValid()) + { + tags.push_back({ GetDisplayName(dependingIndex), GetName(dependingIndex) }); + } } - FindGemDisplayNamesByNameStrings(result); - return result; + return tags; } QString GemModel::GetVersion(const QModelIndex& modelIndex) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index e548d9d3f7..e25a1c7703 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -10,6 +10,7 @@ #if !defined(Q_MOC_RUN) #include +#include #include #include #include @@ -58,7 +59,7 @@ namespace O3DE::ProjectManager void UpdateGemDependencies(); QModelIndex FindIndexByNameString(const QString& nameString) const; - QStringList GetDependingGemNames(const QModelIndex& modelIndex); + QVector GetDependingGemTags(const QModelIndex& modelIndex); bool HasDependentGems(const QModelIndex& modelIndex) const; static QString GetName(const QModelIndex& modelIndex); @@ -113,7 +114,6 @@ namespace O3DE::ProjectManager void OnRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last); private: - void FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames); void GetAllDependingGems(const QModelIndex& modelIndex, QSet& inOutGems); QStringList GetDependingGems(const QModelIndex& modelIndex); diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp index 6655aef86d..f816e86733 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp @@ -86,7 +86,7 @@ namespace O3DE::ProjectManager } // Included Gems - m_includedGems->Update(tr("Included Gems"), "", m_model->GetIncludedGemNames(modelIndex)); + m_includedGems->Update(tr("Included Gems"), "", m_model->GetIncludedGemTags(modelIndex)); m_mainWidget->adjustSize(); m_mainWidget->show(); diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp index 7a9617e6c1..6189b6d8bf 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp @@ -103,17 +103,17 @@ namespace O3DE::ProjectManager return modelIndex.data(RoleIncludedGems).toStringList(); } - QStringList GemRepoModel::GetIncludedGemNames(const QModelIndex& modelIndex) + QVector GemRepoModel::GetIncludedGemTags(const QModelIndex& modelIndex) { - QStringList gemNames; - QVector gemInfos = GetIncludedGemInfos(modelIndex); - + QVector tags; + const QVector& gemInfos = GetIncludedGemInfos(modelIndex); + tags.reserve(gemInfos.size()); for (const GemInfo& gemInfo : gemInfos) { - gemNames.append(gemInfo.m_displayName); + tags.append({ gemInfo.m_displayName, gemInfo.m_name }); } - return gemNames; + return tags; } QVector GemRepoModel::GetIncludedGemInfos(const QModelIndex& modelIndex) diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h index f36b66ca48..66fe972a95 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h @@ -40,7 +40,7 @@ namespace O3DE::ProjectManager static QString GetPath(const QModelIndex& modelIndex); static QStringList GetIncludedGemPaths(const QModelIndex& modelIndex); - static QStringList GetIncludedGemNames(const QModelIndex& modelIndex); + static QVector GetIncludedGemTags(const QModelIndex& modelIndex); static QVector GetIncludedGemInfos(const QModelIndex& modelIndex); static bool IsEnabled(const QModelIndex& modelIndex); diff --git a/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp b/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp index 8b7b183008..2572a39db3 100644 --- a/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemsSubWidget.cpp @@ -33,14 +33,14 @@ namespace O3DE::ProjectManager m_layout->addWidget(m_textLabel); m_tagWidget = new TagContainerWidget(); - connect(m_tagWidget, &TagContainerWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); }); + connect(m_tagWidget, &TagContainerWidget::TagClicked, this, [=](const Tag& tag){ emit TagClicked(tag); }); m_layout->addWidget(m_tagWidget); } - void GemsSubWidget::Update(const QString& title, const QString& text, const QStringList& gemNames) + void GemsSubWidget::Update(const QString& title, const QString& text, const QVector& tags) { m_titleLabel->setText(title); m_textLabel->setText(text); - m_tagWidget->Update(gemNames); + m_tagWidget->Update(tags); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemsSubWidget.h b/Code/Tools/ProjectManager/Source/GemsSubWidget.h index a9fabf5e92..5e670b930a 100644 --- a/Code/Tools/ProjectManager/Source/GemsSubWidget.h +++ b/Code/Tools/ProjectManager/Source/GemsSubWidget.h @@ -26,10 +26,10 @@ namespace O3DE::ProjectManager public: GemsSubWidget(QWidget* parent = nullptr); - void Update(const QString& title, const QString& text, const QStringList& gemNames); + void Update(const QString& title, const QString& text, const QVector& tags); signals: - void TagClicked(const QString& tag); + void TagClicked(const Tag& tag); private: QLabel* m_titleLabel = nullptr; diff --git a/Code/Tools/ProjectManager/Source/TagWidget.cpp b/Code/Tools/ProjectManager/Source/TagWidget.cpp index 39231ace4b..007f0839d1 100644 --- a/Code/Tools/ProjectManager/Source/TagWidget.cpp +++ b/Code/Tools/ProjectManager/Source/TagWidget.cpp @@ -12,15 +12,16 @@ namespace O3DE::ProjectManager { - TagWidget::TagWidget(const QString& text, QWidget* parent) - : QLabel(text, parent) + TagWidget::TagWidget(const Tag& tag, QWidget* parent) + : QLabel(tag.text, parent) + , m_tag(tag) { setObjectName("TagWidget"); } void TagWidget::mousePressEvent([[maybe_unused]] QMouseEvent* event) { - emit(TagClicked(text())); + emit TagClicked(m_tag); } TagContainerWidget::TagContainerWidget(QWidget* parent) @@ -39,20 +40,34 @@ namespace O3DE::ProjectManager void TagContainerWidget::Update(const QStringList& tags) { - FlowLayout* flowLayout = static_cast(layout()); + Clear(); - // remove old tags + foreach (const QString& tag, tags) + { + TagWidget* tagWidget = new TagWidget({tag, tag}); + connect(tagWidget, &TagWidget::TagClicked, this, [=](const Tag& clickedTag){ emit TagClicked(clickedTag); }); + layout()->addWidget(tagWidget); + } + } + + void TagContainerWidget::Update(const QVector& tags) + { + Clear(); + + foreach (const Tag& tag, tags) + { + TagWidget* tagWidget = new TagWidget(tag); + connect(tagWidget, &TagWidget::TagClicked, this, [=](const Tag& clickedTag){ emit TagClicked(clickedTag); }); + layout()->addWidget(tagWidget); + } + } + + void TagContainerWidget::Clear() + { QLayoutItem* layoutItem = nullptr; while ((layoutItem = layout()->takeAt(0)) != nullptr) { layoutItem->widget()->deleteLater(); } - - foreach (const QString& tag, tags) - { - TagWidget* tagWidget = new TagWidget(tag); - connect(tagWidget, &TagWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); }); - flowLayout->addWidget(tagWidget); - } } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/TagWidget.h b/Code/Tools/ProjectManager/Source/TagWidget.h index 7b4a5b1aaa..fce6eaf863 100644 --- a/Code/Tools/ProjectManager/Source/TagWidget.h +++ b/Code/Tools/ProjectManager/Source/TagWidget.h @@ -10,12 +10,19 @@ #if !defined(Q_MOC_RUN) #include -#include #include +#include +#include #endif namespace O3DE::ProjectManager { + struct Tag + { + QString text; + QString id; + }; + // Single tag class TagWidget : public QLabel @@ -23,14 +30,17 @@ namespace O3DE::ProjectManager Q_OBJECT // AUTOMOC public: - explicit TagWidget(const QString& text, QWidget* parent = nullptr); + explicit TagWidget(const Tag& id, QWidget* parent = nullptr); ~TagWidget() = default; signals: - void TagClicked(const QString& tag); + void TagClicked(const Tag& tag); protected: void mousePressEvent(QMouseEvent* event) override; + + private: + Tag m_tag; }; // Widget containing multiple tags, automatically wrapping based on the size @@ -43,9 +53,13 @@ namespace O3DE::ProjectManager explicit TagContainerWidget(QWidget* parent = nullptr); ~TagContainerWidget() = default; + void Update(const QVector& tags); void Update(const QStringList& tags); signals: - void TagClicked(const QString& tag); + void TagClicked(const Tag& tag); + + private: + void Clear(); }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index e76b4093a9..fb16484961 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -43,7 +43,7 @@ namespace O3DE::ProjectManager m_gemRepoScreen = new GemRepoScreen(this); connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &UpdateProjectCtrl::OnChangeScreenRequest); - connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, [this](){ m_gemCatalogScreen->Refresh(m_projectInfo.m_path); }); + connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, m_gemCatalogScreen, &GemCatalogScreen::Refresh); m_stack = new QStackedWidget(this); m_stack->setObjectName("body"); @@ -163,6 +163,7 @@ namespace O3DE::ProjectManager QMessageBox::critical(this, tr("Gems downloading"), tr("You must wait for gems to finish downloading before continuing.")); return; } + // Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project. const GemCatalogScreen::EnableDisableGemsResult result = m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path); if (result == GemCatalogScreen::EnableDisableGemsResult::Failed) diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h index 5fef296ee7..ee6fc792f2 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.h @@ -26,6 +26,7 @@ namespace O3DE::ProjectManager class UpdateProjectCtrl : public ScreenWidget { + Q_OBJECT public: explicit UpdateProjectCtrl(QWidget* parent = nullptr); ~UpdateProjectCtrl() = default; From 21c02b195f9e5ee39b6e9d90aa8a41a69a50a7dd Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Fri, 5 Nov 2021 17:24:08 -0700 Subject: [PATCH 103/177] Update signals/slots to match upstream changes Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> --- Code/Tools/ProjectManager/Source/DownloadController.cpp | 6 +++--- Code/Tools/ProjectManager/Source/DownloadController.h | 2 +- .../Source/GemCatalog/GemCatalogHeaderWidget.cpp | 2 +- .../ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp | 2 +- .../ProjectManager/Source/GemCatalog/GemCatalogScreen.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/DownloadController.cpp b/Code/Tools/ProjectManager/Source/DownloadController.cpp index a8eecffd78..d30e1bbc7c 100644 --- a/Code/Tools/ProjectManager/Source/DownloadController.cpp +++ b/Code/Tools/ProjectManager/Source/DownloadController.cpp @@ -82,13 +82,13 @@ namespace O3DE::ProjectManager succeeded = false; } - const QString gemName = m_gemNames[0]; + QString gemName = m_gemNames.front(); m_gemNames.erase(m_gemNames.begin()); - emit Done(succeeded, gemName); + emit Done(gemName, succeeded); if (!m_gemNames.empty()) { - emit StartGemDownload(m_gemNames[0]); + emit StartGemDownload(m_gemNames.front()); } else { diff --git a/Code/Tools/ProjectManager/Source/DownloadController.h b/Code/Tools/ProjectManager/Source/DownloadController.h index 8afe8ef029..5b2d230379 100644 --- a/Code/Tools/ProjectManager/Source/DownloadController.h +++ b/Code/Tools/ProjectManager/Source/DownloadController.h @@ -58,7 +58,7 @@ namespace O3DE::ProjectManager signals: void StartGemDownload(const QString& gemName); - void Done(bool success, const QString& gemName); + void Done(const QString& gemName, bool success = true); void GemDownloadProgress(int percentage); private: diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 0db2b088c1..71d5086fff 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -261,7 +261,7 @@ namespace O3DE::ProjectManager } }; - auto downloadEnded = [=](bool /*success*/, const QString& /*gemName*/) + auto downloadEnded = [=](const QString& /*gemName*/, bool /*success*/) { update(0); // update the list to remove the gem that has finished }; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index b0e4fa73e1..79935ed235 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -459,7 +459,7 @@ namespace O3DE::ProjectManager emit ChangeScreenRequest(ProjectManagerScreen::GemRepos); } - void GemCatalogScreen::OnGemDownloadResult(bool succeeded, const QString& gemName) + void GemCatalogScreen::OnGemDownloadResult(const QString& gemName, bool succeeded) { if (succeeded) { diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index 6acf43b3eb..da6d2efa7b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -49,7 +49,7 @@ namespace O3DE::ProjectManager void OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies); void OnAddGemClicked(); void SelectGem(const QString& gemName); - void OnGemDownloadResult(bool succeeded, const QString& gemName); + void OnGemDownloadResult(const QString& gemName, bool succeeded = true); void Refresh(); protected: From 0295aa7070154d53c00b8ac4680d23ebb9a4e202 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Fri, 5 Nov 2021 17:31:01 -0700 Subject: [PATCH 104/177] revert change to cancel label href Signed-off-by: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> --- .../ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp index 71d5086fff..8c875e4846 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.cpp @@ -245,7 +245,7 @@ namespace O3DE::ProjectManager QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); nameProgressLayout->addSpacerItem(spacer); - QLabel* cancelText = new QLabel(QString("Cancel")); + QLabel* cancelText = new QLabel(QString("Cancel").arg(gemName)); cancelText->setTextInteractionFlags(Qt::LinksAccessibleByMouse); connect(cancelText, &QLabel::linkActivated, this, &CartOverlayWidget::OnCancelDownloadActivated); nameProgressLayout->addWidget(cancelText); From 621194e593bf26d85e709b61c77b7923c637773a Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Sat, 6 Nov 2021 07:25:42 -0500 Subject: [PATCH 105/177] StandardMultilayerPBR_ForwardPass.azsl. the type of input.m_normal is (#5385) float Github issue: https://github.com/o3de/o3de/issues/2522 Change to float3. The current ASV tests are not affected but using the RPI/Mesh example with Mesh: objects/suzanne.azmodel and the material: StandardMultilayerPbrTestCases/005_UseDisplacement.material Showed a clear improvement. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../Materials/Types/StandardMultilayerPBR_ForwardPass.azsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl index a53dab7a01..8f58c6dd33 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl @@ -153,7 +153,7 @@ struct StandardMaterialInputs float2 m_vertexUv[UvSetCount]; float3x3 m_uvMatrix; - float m_normal; + float3 m_normal; float3 m_tangents[UvSetCount]; float3 m_bitangents[UvSetCount]; From db22b1125f09ec54fb47ab95265b5f212efa646c Mon Sep 17 00:00:00 2001 From: Andre Mitchell Date: Sun, 7 Nov 2021 11:47:30 -0500 Subject: [PATCH 106/177] Update GraphModelIntegrationTests's GetNodesFromGraphNodeIds test to reflect new behavior of the function. GetNodesFromGraphNodeIds no longer adds null pointers to the list it returns, so the test was updated to reflect that only one item - the valid item - should be returned from the function. Signed-off-by: Andre Mitchell --- Gems/GraphModel/Code/Tests/GraphModelIntegrationTest.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Gems/GraphModel/Code/Tests/GraphModelIntegrationTest.cpp b/Gems/GraphModel/Code/Tests/GraphModelIntegrationTest.cpp index 683442a34e..cf110c05cb 100644 --- a/Gems/GraphModel/Code/Tests/GraphModelIntegrationTest.cpp +++ b/Gems/GraphModel/Code/Tests/GraphModelIntegrationTest.cpp @@ -173,16 +173,11 @@ namespace GraphModelIntegrationTest }; GraphModel::NodePtrList retrievedNodes; GraphModelIntegration::GraphControllerRequestBus::EventResult(retrievedNodes, m_sceneId, &GraphModelIntegration::GraphControllerRequests::GetNodesFromGraphNodeIds, nodeIds); - EXPECT_EQ(nodeIds.size(), retrievedNodes.size()); + // Test that only one node was found. + EXPECT_EQ(retrievedNodes.size(), 1); // Test the first node in the list should be our valid test node EXPECT_EQ(retrievedNodes[0], testNode); - - // Test the second node should be a nullptr since it was an invalid NodeId - EXPECT_EQ(retrievedNodes[1], nullptr); - - // Test the third node should also be a nullptr since it was a valid NodeId but one that doesn't exist in the scene - EXPECT_EQ(retrievedNodes[2], nullptr); } TEST_F(GraphModelIntegrationTests, ExtendableSlotsWithDifferentMinimumValues) From 00cf9ebfd801b0fde709fdda3b9fe6219d1deefd Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Sun, 7 Nov 2021 18:36:38 -0600 Subject: [PATCH 107/177] Queuing inspector invalidate all to boost performance opening/closing several documents Signed-off-by: Guthrie Adams --- .../Code/Source/Inspector/InspectorPropertyGroupWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp index 6b9aee17f7..af71954737 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorPropertyGroupWidget.cpp @@ -43,7 +43,7 @@ namespace AtomToolsFramework m_propertyEditor->Setup(context, instanceNotificationHandler, false); m_propertyEditor->AddInstance(instance, instanceClassId, nullptr, instanceToCompare); m_propertyEditor->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - m_propertyEditor->InvalidateAll(); + m_propertyEditor->QueueInvalidation(AzToolsFramework::PropertyModificationRefreshLevel::Refresh_EntireTree); m_layout->addWidget(m_propertyEditor); setLayout(m_layout); From 803039c302558335035b33f50f4dbcfb1435a653 Mon Sep 17 00:00:00 2001 From: moraaar Date: Mon, 8 Nov 2021 09:14:01 +0000 Subject: [PATCH 108/177] Fixed casing of .fbx.asset info files in cloth to match the fbx (#5371) Signed-off-by: moraaar --- .../{cloth_blinds.fbx.assetinfo => Cloth_Blinds.fbx.assetinfo} | 0 ...nds_broken.fbx.assetinfo => Cloth_Blinds_Broken.fbx.assetinfo} | 0 ...four.fbx.assetinfo => Cloth_Locked_Corners_Four.fbx.assetinfo} | 0 ...s_two.fbx.assetinfo => Cloth_Locked_Corners_Two.fbx.assetinfo} | 0 ..._locked_edge.fbx.assetinfo => Cloth_Locked_Edge.fbx.assetinfo} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename Gems/NvCloth/Assets/Objects/cloth/Environment/{cloth_blinds.fbx.assetinfo => Cloth_Blinds.fbx.assetinfo} (100%) rename Gems/NvCloth/Assets/Objects/cloth/Environment/{cloth_blinds_broken.fbx.assetinfo => Cloth_Blinds_Broken.fbx.assetinfo} (100%) rename Gems/NvCloth/Assets/Objects/cloth/Environment/{cloth_locked_corners_four.fbx.assetinfo => Cloth_Locked_Corners_Four.fbx.assetinfo} (100%) rename Gems/NvCloth/Assets/Objects/cloth/Environment/{cloth_locked_corners_two.fbx.assetinfo => Cloth_Locked_Corners_Two.fbx.assetinfo} (100%) rename Gems/NvCloth/Assets/Objects/cloth/Environment/{cloth_locked_edge.fbx.assetinfo => Cloth_Locked_Edge.fbx.assetinfo} (100%) diff --git a/Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_blinds.fbx.assetinfo b/Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Blinds.fbx.assetinfo similarity index 100% rename from Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_blinds.fbx.assetinfo rename to Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Blinds.fbx.assetinfo diff --git a/Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_blinds_broken.fbx.assetinfo b/Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Blinds_Broken.fbx.assetinfo similarity index 100% rename from Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_blinds_broken.fbx.assetinfo rename to Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Blinds_Broken.fbx.assetinfo diff --git a/Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_locked_corners_four.fbx.assetinfo b/Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Locked_Corners_Four.fbx.assetinfo similarity index 100% rename from Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_locked_corners_four.fbx.assetinfo rename to Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Locked_Corners_Four.fbx.assetinfo diff --git a/Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_locked_corners_two.fbx.assetinfo b/Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Locked_Corners_Two.fbx.assetinfo similarity index 100% rename from Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_locked_corners_two.fbx.assetinfo rename to Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Locked_Corners_Two.fbx.assetinfo diff --git a/Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_locked_edge.fbx.assetinfo b/Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Locked_Edge.fbx.assetinfo similarity index 100% rename from Gems/NvCloth/Assets/Objects/cloth/Environment/cloth_locked_edge.fbx.assetinfo rename to Gems/NvCloth/Assets/Objects/cloth/Environment/Cloth_Locked_Edge.fbx.assetinfo From 425e172079eff05ddee18c8b46958ce08a4cdf46 Mon Sep 17 00:00:00 2001 From: moraaar Date: Mon, 8 Nov 2021 09:14:26 +0000 Subject: [PATCH 109/177] Cloth automated tests only check for cloth gem errors and warnings (#5374) Signed-off-by: moraaar --- .../NvCloth_AddClothSimulationToActor.py | 19 +++++++++++-------- .../tests/NvCloth_AddClothSimulationToMesh.py | 19 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToActor.py b/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToActor.py index 357067b176..e4150f7af8 100644 --- a/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToActor.py +++ b/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToActor.py @@ -50,6 +50,7 @@ def NvCloth_AddClothSimulationToActor(): # Constants FRAMES_IN_GAME_MODE = 200 + CLOTH_GEM_ERROR_WARNING_LIST = ["Cloth", "NvCloth", "ClothComponentMesh", "ActorClothSkinning", "ActorClothSkinning", "TangentSpaceHelper", "MeshAssetHelper", "ActorAssetHelper", "ClothDebugDisplay"] helper.init_idle() # 1) Load the level @@ -64,14 +65,16 @@ def NvCloth_AddClothSimulationToActor(): general.idle_wait_frames(FRAMES_IN_GAME_MODE) # 5) Verify there are no errors and warnings in the logs - success_condition = not (section_tracer.has_errors or section_tracer.has_warnings) - Report.result(Tests.no_errors_and_warnings_found, success_condition) - if not success_condition: - if section_tracer.has_warnings: - Report.info(f"Warnings found: {section_tracer.warnings}") - if section_tracer.has_errors: - Report.info(f"Errors found: {section_tracer.errors}") - Report.failure(Tests.no_errors_and_warnings_found) + has_errors_or_warnings = False + for error_msg in section_tracer.errors: + if error_msg.window in CLOTH_GEM_ERROR_WARNING_LIST: + has_errors_or_warnings = True + Report.info(f"Cloth error found: {error_msg}") + for warning_msg in section_tracer.warnings: + if warning_msg.window in CLOTH_GEM_ERROR_WARNING_LIST: + has_errors_or_warnings = True + Report.info(f"Cloth warning found: {warning_msg}") + Report.result(Tests.no_errors_and_warnings_found, not has_errors_or_warnings) # 6) Exit game mode helper.exit_game_mode(Tests.exit_game_mode) diff --git a/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToMesh.py b/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToMesh.py index 0f9d8448f7..707f745cea 100644 --- a/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToMesh.py +++ b/AutomatedTesting/Gem/PythonTests/NvCloth/tests/NvCloth_AddClothSimulationToMesh.py @@ -50,6 +50,7 @@ def NvCloth_AddClothSimulationToMesh(): # Constants FRAMES_IN_GAME_MODE = 200 + CLOTH_GEM_ERROR_WARNING_LIST = ["Cloth", "NvCloth", "ClothComponentMesh", "ActorClothSkinning", "ActorClothSkinning", "TangentSpaceHelper", "MeshAssetHelper", "ActorAssetHelper", "ClothDebugDisplay"] helper.init_idle() # 1) Load the level @@ -64,14 +65,16 @@ def NvCloth_AddClothSimulationToMesh(): general.idle_wait_frames(FRAMES_IN_GAME_MODE) # 5) Verify there are no errors and warnings in the logs - success_condition = not (section_tracer.has_errors or section_tracer.has_warnings) - Report.result(Tests.no_errors_and_warnings_found, success_condition) - if not success_condition: - if section_tracer.has_warnings: - Report.info(f"Warnings found: {section_tracer.warnings}") - if section_tracer.has_errors: - Report.info(f"Errors found: {section_tracer.errors}") - Report.failure(Tests.no_errors_and_warnings_found) + has_errors_or_warnings = False + for error_msg in section_tracer.errors: + if error_msg.window in CLOTH_GEM_ERROR_WARNING_LIST: + has_errors_or_warnings = True + Report.info(f"Cloth error found: {error_msg}") + for warning_msg in section_tracer.warnings: + if warning_msg.window in CLOTH_GEM_ERROR_WARNING_LIST: + has_errors_or_warnings = True + Report.info(f"Cloth warning found: {warning_msg}") + Report.result(Tests.no_errors_and_warnings_found, not has_errors_or_warnings) # 6) Exit game mode helper.exit_game_mode(Tests.exit_game_mode) From 783186fa7e4a1d6b0ff8f0f9c129b343b4e900bf Mon Sep 17 00:00:00 2001 From: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> Date: Mon, 8 Nov 2021 09:19:40 +0000 Subject: [PATCH 110/177] Update default camera orbit behavior (#5301) * add new default orbit point behavior Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * add default orbit distance to settings registry Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * add new default orbit point behavior Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * add default orbit distance to settings registry Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * expose default orbit distance to editor settings menu and update how we display default camera position Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * add improve orbit changes for focus Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> --- .../EditorModularViewportCameraComposer.cpp | 15 +++++--- .../EditorPreferencesPageViewportCamera.cpp | 34 +++++++------------ .../EditorPreferencesPageViewportCamera.h | 12 ++++--- Code/Editor/EditorViewportSettings.cpp | 23 +++++++++---- Code/Editor/EditorViewportSettings.h | 9 +++-- Code/Editor/EditorViewportWidget.cpp | 10 +++--- .../AzFramework/Viewport/CameraInput.cpp | 12 +++++-- .../AzFramework/Viewport/CameraInput.h | 2 +- 8 files changed, 68 insertions(+), 49 deletions(-) diff --git a/Code/Editor/EditorModularViewportCameraComposer.cpp b/Code/Editor/EditorModularViewportCameraComposer.cpp index ce4a0a2e33..08ca0df221 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.cpp +++ b/Code/Editor/EditorModularViewportCameraComposer.cpp @@ -174,7 +174,7 @@ namespace SandboxEditor return SandboxEditor::CameraScrollSpeed(); }; - const auto pivotFn = [] + const auto pivotFn = []() -> AZStd::optional { // use the manipulator transform as the pivot point AZStd::optional entityPivot; @@ -187,8 +187,13 @@ namespace SandboxEditor return entityPivot->GetTranslation(); } - // otherwise just use the identity - return AZ::Vector3::CreateZero(); + return AZStd::nullopt; + }; + + const auto orbitFn = [pivotFn](const AZ::Vector3& pivotFallback = AZ::Vector3::CreateZero()) + { + // return the pivot otherwise use the fallback + return pivotFn().value_or(pivotFallback); }; m_firstPersonFocusCamera = @@ -199,9 +204,9 @@ namespace SandboxEditor m_orbitCamera = AZStd::make_shared(SandboxEditor::CameraOrbitChannelId()); m_orbitCamera->SetPivotFn( - [pivotFn]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction) + [orbitFn]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction) { - return pivotFn(); + return orbitFn(position + direction * SandboxEditor::CameraDefaultOrbitDistance()); }); m_orbitRotateCamera = AZStd::make_shared(SandboxEditor::CameraOrbitLookChannelId()); diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.cpp b/Code/Editor/EditorPreferencesPageViewportCamera.cpp index 2f6e6b1b9d..c81eac0414 100644 --- a/Code/Editor/EditorPreferencesPageViewportCamera.cpp +++ b/Code/Editor/EditorPreferencesPageViewportCamera.cpp @@ -61,7 +61,7 @@ static AZStd::vector GetEditorInputNames() void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serialize) { serialize.Class() - ->Version(3) + ->Version(4) ->Field("TranslateSpeed", &CameraMovementSettings::m_translateSpeed) ->Field("RotateSpeed", &CameraMovementSettings::m_rotateSpeed) ->Field("BoostMultiplier", &CameraMovementSettings::m_boostMultiplier) @@ -76,9 +76,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial ->Field("OrbitYawRotationInverted", &CameraMovementSettings::m_orbitYawRotationInverted) ->Field("PanInvertedX", &CameraMovementSettings::m_panInvertedX) ->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY) - ->Field("DefaultPositionX", &CameraMovementSettings::m_defaultCameraPositionX) - ->Field("DefaultPositionY", &CameraMovementSettings::m_defaultCameraPositionY) - ->Field("DefaultPositionZ", &CameraMovementSettings::m_defaultCameraPositionZ); + ->Field("DefaultPosition", &CameraMovementSettings::m_defaultPosition) + ->Field("DefaultOrbitDistance", &CameraMovementSettings::m_defaultOrbitDistance); serialize.Class() ->Version(2) @@ -159,14 +158,12 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_captureCursorLook, "Camera Capture Look Cursor", "Should the cursor be captured (hidden) while performing free look") ->DataElement( - AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionX, "Default X Camera Position", - "Default X Camera Position when a level is opened") + AZ::Edit::UIHandlers::Vector3, &CameraMovementSettings::m_defaultPosition, "Default Camera Position", + "Default Camera Position when a level is first opened") ->DataElement( - AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionY, "Default Y Camera Position", - "Default Y Camera Position when a level is opened") - ->DataElement( - AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionZ, "Default Z Camera Position", - "Default Z Camera Position when a level is opened"); + AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultOrbitDistance, "Default Orbit Distance", + "The default distance to orbit about when there is no entity selected") + ->Attribute(AZ::Edit::Attributes::Min, minValue); editContext->Class("Camera Input Settings", "") ->DataElement( @@ -283,12 +280,8 @@ void CEditorPreferencesPage_ViewportCamera::OnApply() SandboxEditor::SetCameraOrbitYawRotationInverted(m_cameraMovementSettings.m_orbitYawRotationInverted); SandboxEditor::SetCameraPanInvertedX(m_cameraMovementSettings.m_panInvertedX); SandboxEditor::SetCameraPanInvertedY(m_cameraMovementSettings.m_panInvertedY); - SandboxEditor::SetDefaultCameraEditorPosition( - AZ::Vector3( - m_cameraMovementSettings.m_defaultCameraPositionX, - m_cameraMovementSettings.m_defaultCameraPositionY, - m_cameraMovementSettings.m_defaultCameraPositionZ - )); + SandboxEditor::SetCameraDefaultEditorPosition(m_cameraMovementSettings.m_defaultPosition); + SandboxEditor::SetCameraDefaultOrbitDistance(m_cameraMovementSettings.m_defaultOrbitDistance); SandboxEditor::SetCameraTranslateForwardChannelId(m_cameraInputSettings.m_translateForwardChannelId); SandboxEditor::SetCameraTranslateBackwardChannelId(m_cameraInputSettings.m_translateBackwardChannelId); @@ -325,11 +318,8 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings() m_cameraMovementSettings.m_orbitYawRotationInverted = SandboxEditor::CameraOrbitYawRotationInverted(); m_cameraMovementSettings.m_panInvertedX = SandboxEditor::CameraPanInvertedX(); m_cameraMovementSettings.m_panInvertedY = SandboxEditor::CameraPanInvertedY(); - - AZ::Vector3 defaultCameraPosition = SandboxEditor::DefaultEditorCameraPosition(); - m_cameraMovementSettings.m_defaultCameraPositionX = defaultCameraPosition.GetX(); - m_cameraMovementSettings.m_defaultCameraPositionY = defaultCameraPosition.GetY(); - m_cameraMovementSettings.m_defaultCameraPositionZ = defaultCameraPosition.GetZ(); + m_cameraMovementSettings.m_defaultPosition = SandboxEditor::CameraDefaultEditorPosition(); + m_cameraMovementSettings.m_defaultOrbitDistance = SandboxEditor::CameraDefaultOrbitDistance(); m_cameraInputSettings.m_translateForwardChannelId = SandboxEditor::CameraTranslateForwardChannelId().GetName(); m_cameraInputSettings.m_translateBackwardChannelId = SandboxEditor::CameraTranslateBackwardChannelId().GetName(); diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.h b/Code/Editor/EditorPreferencesPageViewportCamera.h index a2705bfd24..41816de51c 100644 --- a/Code/Editor/EditorPreferencesPageViewportCamera.h +++ b/Code/Editor/EditorPreferencesPageViewportCamera.h @@ -9,9 +9,12 @@ #pragma once #include "Include/IPreferencesPage.h" + +#include #include #include #include + #include inline AZ::Crc32 EditorPropertyVisibility(const bool enabled) @@ -43,6 +46,7 @@ private: { AZ_TYPE_INFO(CameraMovementSettings, "{60B8C07E-5F48-4171-A50B-F45558B5CCA1}") + AZ::Vector3 m_defaultPosition; float m_translateSpeed; float m_rotateSpeed; float m_scrollSpeed; @@ -50,16 +54,14 @@ private: float m_panSpeed; float m_boostMultiplier; float m_rotateSmoothness; - bool m_rotateSmoothing; float m_translateSmoothness; - bool m_translateSmoothing; + float m_defaultOrbitDistance; bool m_captureCursorLook; bool m_orbitYawRotationInverted; bool m_panInvertedX; bool m_panInvertedY; - float m_defaultCameraPositionX; - float m_defaultCameraPositionY; - float m_defaultCameraPositionZ; + bool m_rotateSmoothing; + bool m_translateSmoothing; AZ::Crc32 RotateSmoothingVisibility() const { diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp index 8f2be1de6c..ae188c7d98 100644 --- a/Code/Editor/EditorViewportSettings.cpp +++ b/Code/Editor/EditorViewportSettings.cpp @@ -38,6 +38,7 @@ namespace SandboxEditor constexpr AZStd::string_view CameraTranslateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/TranslateSmoothing"; constexpr AZStd::string_view CameraRotateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/RotateSmoothing"; constexpr AZStd::string_view CameraCaptureCursorLookSetting = "/Amazon/Preferences/Editor/Camera/CaptureCursorLook"; + constexpr AZStd::string_view CameraDefaultOrbitDistanceSetting = "/Amazon/Preferences/Editor/Camera/DefaultOrbitDistance"; constexpr AZStd::string_view CameraTranslateForwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateForwardId"; constexpr AZStd::string_view CameraTranslateBackwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateBackwardId"; constexpr AZStd::string_view CameraTranslateLeftIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateLeftId"; @@ -114,15 +115,15 @@ namespace SandboxEditor return AZStd::make_unique(); } - AZ::Vector3 DefaultEditorCameraPosition() + AZ::Vector3 CameraDefaultEditorPosition() { - float xPosition = aznumeric_cast(GetRegistry(CameraDefaultStartingPositionX, 0.0)); - float yPosition = aznumeric_cast(GetRegistry(CameraDefaultStartingPositionY, -10.0)); - float zPosition = aznumeric_cast(GetRegistry(CameraDefaultStartingPositionZ, 4.0)); - return AZ::Vector3(xPosition, yPosition, zPosition); + return AZ::Vector3( + aznumeric_cast(GetRegistry(CameraDefaultStartingPositionX, 0.0)), + aznumeric_cast(GetRegistry(CameraDefaultStartingPositionY, -10.0)), + aznumeric_cast(GetRegistry(CameraDefaultStartingPositionZ, 4.0))); } - void SetDefaultCameraEditorPosition(const AZ::Vector3 defaultCameraPosition) + void SetCameraDefaultEditorPosition(const AZ::Vector3& defaultCameraPosition) { SetRegistry(CameraDefaultStartingPositionX, defaultCameraPosition.GetX()); SetRegistry(CameraDefaultStartingPositionY, defaultCameraPosition.GetY()); @@ -359,6 +360,16 @@ namespace SandboxEditor SetRegistry(CameraCaptureCursorLookSetting, capture); } + float CameraDefaultOrbitDistance() + { + return aznumeric_cast(GetRegistry(CameraDefaultOrbitDistanceSetting, 20.0)); + } + + void SetCameraDefaultOrbitDistance(const float distance) + { + SetRegistry(CameraDefaultOrbitDistanceSetting, distance); + } + AzFramework::InputChannelId CameraTranslateForwardChannelId() { return AzFramework::InputChannelId( diff --git a/Code/Editor/EditorViewportSettings.h b/Code/Editor/EditorViewportSettings.h index 9c3f0a46e5..fe1253ed0c 100644 --- a/Code/Editor/EditorViewportSettings.h +++ b/Code/Editor/EditorViewportSettings.h @@ -33,9 +33,6 @@ namespace SandboxEditor //! event will fire when a value in the settings registry (editorpreferences.setreg) is modified. SANDBOX_API AZStd::unique_ptr CreateEditorViewportSettingsCallbacks(); - SANDBOX_API AZ::Vector3 DefaultEditorCameraPosition(); - SANDBOX_API void SetDefaultCameraEditorPosition(AZ::Vector3 defaultCameraPosition); - SANDBOX_API AZ::u64 MaxItemsShownInAssetBrowserSearch(); SANDBOX_API void SetMaxItemsShownInAssetBrowserSearch(AZ::u64 numberOfItemsShown); @@ -105,6 +102,12 @@ namespace SandboxEditor SANDBOX_API bool CameraCaptureCursorForLook(); SANDBOX_API void SetCameraCaptureCursorForLook(bool capture); + SANDBOX_API AZ::Vector3 CameraDefaultEditorPosition(); + SANDBOX_API void SetCameraDefaultEditorPosition(const AZ::Vector3& position); + + SANDBOX_API float CameraDefaultOrbitDistance(); + SANDBOX_API void SetCameraDefaultOrbitDistance(float distance); + SANDBOX_API AzFramework::InputChannelId CameraTranslateForwardChannelId(); SANDBOX_API void SetCameraTranslateForwardChannelId(AZStd::string_view cameraTranslateForwardId); diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp index 8e6983a9d7..5c5ab87058 100644 --- a/Code/Editor/EditorViewportWidget.cpp +++ b/Code/Editor/EditorViewportWidget.cpp @@ -620,9 +620,9 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event) break; case eNotify_OnEndNewScene: - PopDisableRendering(); - { + PopDisableRendering(); + Matrix34 viewTM; viewTM.SetIdentity(); @@ -638,9 +638,9 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event) break; case eNotify_OnEndTerrainCreate: - PopDisableRendering(); - { + PopDisableRendering(); + Matrix34 viewTM; viewTM.SetIdentity(); @@ -2527,7 +2527,7 @@ bool EditorViewportSettings::StickySelectEnabled() const AZ::Vector3 EditorViewportSettings::DefaultEditorCameraPosition() const { - return SandboxEditor::DefaultEditorCameraPosition(); + return SandboxEditor::CameraDefaultEditorPosition(); } AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once); diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index ddee63e191..26c9db64aa 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -806,12 +806,20 @@ namespace AzFramework [[maybe_unused]] float scrollDelta, [[maybe_unused]] float deltaTime) { + const auto pivot = m_pivotFn(); + + if (!pivot.has_value()) + { + EndActivation(); + return targetCamera; + } + if (Beginning()) { // as the camera starts, record the camera we would like to end up as - m_nextCamera.m_offset = m_offsetFn(m_pivotFn().GetDistance(targetCamera.Translation())); + m_nextCamera.m_offset = m_offsetFn(pivot.value().GetDistance(targetCamera.Translation())); const auto angles = - EulerAngles(AZ::Matrix3x3::CreateFromMatrix3x4(AZ::Matrix3x4::CreateLookAt(targetCamera.Translation(), m_pivotFn()))); + EulerAngles(AZ::Matrix3x3::CreateFromMatrix3x4(AZ::Matrix3x4::CreateLookAt(targetCamera.Translation(), pivot.value()))); m_nextCamera.m_pitch = angles.GetX(); m_nextCamera.m_yaw = angles.GetZ(); m_nextCamera.m_pivot = targetCamera.m_pivot; diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h index 2b7cc3ea9e..2d02bb0b6d 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h @@ -651,7 +651,7 @@ namespace AzFramework class FocusCameraInput : public CameraInput { public: - using PivotFn = AZStd::function; + using PivotFn = AZStd::function()>; FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn); From a1d9a2cc586a641c94a9ce18e6a1ae87cdd07f3c Mon Sep 17 00:00:00 2001 From: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> Date: Mon, 8 Nov 2021 10:56:51 +0100 Subject: [PATCH 111/177] Asset Browser Tests (#4948) * Added AssetBrowser Tests Signed-off-by: igarri * Added Entries to test AssetBrowser Signed-off-by: igarri * Added Print info. Signed-off-by: igarri * Added more folders Signed-off-by: igarri * Added Asset Browser Tests for the Search View Signed-off-by: igarri * Fixed Entry creation Signed-off-by: igarri * Removed optimize Signed-off-by: igarri * Cleanup AssetBrowserModel Signed-off-by: igarri * RowCount made public Signed-off-by: igarri * Delegated entry creation to RootAssetBrowserEntry and added Code review feedback Signed-off-by: igarri * removed unused helper class and fixed demo tests Signed-off-by: igarri * Fixed bus connections Signed-off-by: igarri * Refactored test environment and added basic tests Signed-off-by: igarri * Applied some code review feedback and added basic tests Signed-off-by: igarri * fixed naming Signed-off-by: igarri * Refactored Tests Signed-off-by: igarri * removed pointer reset, now handled by the AssetBrowserComponent Signed-off-by: igarri * Fixed conversion unsigned-signed Signed-off-by: igarri * Cleaned includes Signed-off-by: igarri * fixed test setup Signed-off-by: igarri * Fixed unused variables Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> * Added printer function Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> * cleaned up code Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> * Added Test to check the correctness of the setup Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> * Fixed basic tests Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> * Fixed Tests Signed-off-by: AMZN-Igarri <82394219+AMZN-Igarri@users.noreply.github.com> --- .../AssetBrowser/AssetBrowserTableModel.h | 4 +- .../Tests/UI/AssetBrowserTests.cpp | 344 ++++++++++++++++++ .../Tests/aztoolsframeworktests_files.cmake | 1 + 3 files changed, 347 insertions(+), 2 deletions(-) create mode 100644 Code/Framework/AzToolsFramework/Tests/UI/AssetBrowserTests.cpp diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h index 4048156b60..ee1ec49456 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h @@ -40,9 +40,9 @@ namespace AzToolsFramework QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QModelIndex parent(const QModelIndex& child) const override; QModelIndex sibling(int row, int column, const QModelIndex& idx) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; protected: - int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override; //////////////////////////////////////////////////////////////////// @@ -55,7 +55,7 @@ namespace AzToolsFramework private slots: void SourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); private: - AZ::u64 m_numberOfItemsDisplayed = 0; + AZ::u64 m_numberOfItemsDisplayed = 50; int m_displayedItemsCounter = 0; QPointer m_filterModel; QMap m_indexMap; diff --git a/Code/Framework/AzToolsFramework/Tests/UI/AssetBrowserTests.cpp b/Code/Framework/AzToolsFramework/Tests/UI/AssetBrowserTests.cpp new file mode 100644 index 0000000000..ae5ea5f0ae --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/UI/AssetBrowserTests.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + // Test fixture for the AssetBrowser model that uses a QAbstractItemModelTester to validate the state of the model + // when QAbstractItemModel signals fire. Tests will exit with a fatal error if an invalid state is detected. + class AssetBrowserTest + : public ToolsApplicationFixture + , public testing::WithParamInterface + { + protected: + enum class AssetEntryType + { + Root, + Folder, + Source, + Product + }; + + enum class FolderType + { + Root, + File + }; + + void SetUpEditorFixtureImpl() override; + void TearDownEditorFixtureImpl() override; + + //! Creates a Mock Scan Folder + void AddScanFolder(AZ::s64 folderID, AZStd::string folderPath, AZStd::string displayName, FolderType folderType = FolderType::File); + + //! Creates a Source entry from a mock file + AZ::Uuid CreateSourceEntry( + AZ::s64 fileID, AZ::s64 parentFolderID, AZStd::string filename, AssetEntryType sourceType = AssetEntryType::Source); + + //! Creates a product from a given sourceEntry + void CreateProduct(AZ::s64 productID, AZ::Uuid sourceUuid, AZStd::string productName); + + void SetupAssetBrowser(); + void PrintModel(const QAbstractItemModel* model, AZStd::function printer); + QModelIndex GetModelIndex(const QAbstractItemModel* model, int targetDepth, int row = 0); + AZStd::shared_ptr GetRootEntry(); + AZStd::vector GetVectorFromFormattedString(const QString& formattedString); + + protected: + QString m_assetBrowserHierarchy = QString(); + + AZStd::unique_ptr m_searchWidget; + AZStd::unique_ptr m_assetBrowserComponent; + + AZStd::unique_ptr m_filterModel; + AZStd::unique_ptr m_tableModel; + + AZStd::unique_ptr m_modelTesterAssetBrowser; + AZStd::unique_ptr m_modelTesterFilterModel; + AZStd::unique_ptr m_modelTesterTableModel; + + QVector m_folderIds = { 13, 14, 15 }; + QVector m_sourceIDs = { 1, 2, 3, 4, 5 }; + QVector m_productIDs = { 1, 2, 3, 4, 5 }; + }; + + void AssetBrowserTest::SetUpEditorFixtureImpl() + { + GetApplication()->RegisterComponentDescriptor(AzToolsFramework::EditorEntityContextComponent::CreateDescriptor()); + + m_assetBrowserComponent = AZStd::make_unique(); + m_assetBrowserComponent->Activate(); + + m_filterModel = AZStd::make_unique(); + m_tableModel = AZStd::make_unique(); + + m_filterModel->setSourceModel(m_assetBrowserComponent->GetAssetBrowserModel()); + m_tableModel->setSourceModel(m_filterModel.get()); + + m_modelTesterAssetBrowser = AZStd::make_unique(m_assetBrowserComponent->GetAssetBrowserModel()); + m_modelTesterFilterModel = AZStd::make_unique(m_filterModel.get()); + m_modelTesterTableModel = AZStd::make_unique(m_tableModel.get()); + m_searchWidget = AZStd::make_unique(); + + // Setup String filters + m_searchWidget->Setup(true, true); + m_filterModel->SetFilter(m_searchWidget->GetFilter()); + + SetupAssetBrowser(); + } + + void AssetBrowserTest::TearDownEditorFixtureImpl() + { + m_modelTesterAssetBrowser.reset(); + m_modelTesterFilterModel.reset(); + m_modelTesterTableModel.reset(); + + m_tableModel.reset(); + m_filterModel.reset(); + m_assetBrowserComponent->Deactivate(); + + m_assetBrowserComponent.reset(); + m_searchWidget.reset(); + } + + void AssetBrowserTest::AddScanFolder( + AZ::s64 folderID, AZStd::string folderPath, AZStd::string displayName, FolderType folderType /*= FolderType::File*/) + { + AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry scanFolder = AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry(); + scanFolder.m_scanFolderID = folderID; + scanFolder.m_scanFolder = folderPath; + scanFolder.m_displayName = displayName; + scanFolder.m_isRoot = folderType == FolderType::Root; + GetRootEntry()->AddScanFolder(scanFolder); + } + + AZ::Uuid AssetBrowserTest::CreateSourceEntry( + AZ::s64 fileID, AZ::s64 parentFolderID, AZStd::string filename, AssetEntryType sourceType /*= AssetEntryType::Source*/) + { + AzToolsFramework::AssetDatabase::FileDatabaseEntry entry = AzToolsFramework::AssetDatabase::FileDatabaseEntry(); + entry.m_scanFolderPK = parentFolderID; + entry.m_fileID = fileID; + entry.m_fileName = filename; + entry.m_isFolder = sourceType == AssetEntryType::Folder; + GetRootEntry()->AddFile(entry); + + if (!entry.m_isFolder) + { + AzToolsFramework::AssetBrowser::SourceWithFileID entrySource = AzToolsFramework::AssetBrowser::SourceWithFileID(); + entrySource.first = entry.m_fileID; + entrySource.second = AzToolsFramework::AssetDatabase::SourceDatabaseEntry(); + entrySource.second.m_scanFolderPK = parentFolderID; + entrySource.second.m_sourceName = filename; + entrySource.second.m_sourceID = fileID; + entrySource.second.m_sourceGuid = AZ::Uuid::CreateRandom(); + + GetRootEntry()->AddSource(entrySource); + + return entrySource.second.m_sourceGuid; + } + + return AZ::Uuid::CreateNull(); + } + + void AssetBrowserTest::CreateProduct(AZ::s64 productID, AZ::Uuid sourceUuid, AZStd::string productName) + { + AzToolsFramework::AssetBrowser::ProductWithUuid product = AzToolsFramework::AssetBrowser::ProductWithUuid(); + product.first = sourceUuid; + product.second = AzToolsFramework::AssetDatabase::ProductDatabaseEntry(); + product.second.m_productID = productID; + + product.second.m_subID = aznumeric_cast(productID); + product.second.m_productName = productName; + + GetRootEntry()->AddProduct(product); + } + + void AssetBrowserTest::SetupAssetBrowser() + { + // RootEntries : 1 | Folders : 4 | SourceEntries : 5 | ProductEntries : 9 + m_assetBrowserHierarchy = R"( + D: + \ + dev + o3de + GameProject + Assets + Source_1 + Product_1_1 + Product_1_0 + Source_0 + Product_0_3 + Product_0_2 + Product_0_1 + Product_0_0 + Scripts + Source_3 + Source_2 + Product_2_2 + Product_2_1 + Product_2_0 + Misc + Source_4 + Product_4_2 + Product_4_1 + Product_4_0 )"; + + namespace AzAssetBrowser = AzToolsFramework::AssetBrowser; + + AddScanFolder(m_folderIds.at(2), "D:/dev/o3de/GameProject/Misc", "Misc"); + AZ::Uuid sourceUuid_4 = CreateSourceEntry(m_sourceIDs.at(4), m_folderIds.at(2), "Source_4"); + CreateProduct(m_productIDs.at(0), sourceUuid_4, "Product_4_0"); + CreateProduct(m_productIDs.at(1), sourceUuid_4, "Product_4_1"); + CreateProduct(m_productIDs.at(2), sourceUuid_4, "Product_4_2"); + + AddScanFolder(m_folderIds.at(1), "D:/dev/o3de/GameProject/Scripts", "Scripts"); + + AZ::Uuid sourceUuid_2 = CreateSourceEntry(m_sourceIDs.at(2), m_folderIds.at(1), "Source_2"); + CreateProduct(m_productIDs.at(0), sourceUuid_2, "Product_2_0"); + CreateProduct(m_productIDs.at(1), sourceUuid_2, "Product_2_1"); + CreateProduct(m_productIDs.at(2), sourceUuid_2, "Product_2_2"); + + CreateSourceEntry(m_sourceIDs.at(3), m_folderIds.at(1), "Source_3"); + + AddScanFolder(m_folderIds.at(0), "D:/dev/o3de/GameProject/Assets", "Assets"); + + AZ::Uuid sourceUuid_0 = CreateSourceEntry(m_sourceIDs.at(0), m_folderIds.at(0), "Source_0"); + CreateProduct(m_productIDs.at(0), sourceUuid_0, "Product_0_0"); + CreateProduct(m_productIDs.at(1), sourceUuid_0, "Product_0_1"); + CreateProduct(m_productIDs.at(2), sourceUuid_0, "Product_0_2"); + CreateProduct(m_productIDs.at(3), sourceUuid_0, "Product_0_3"); + + AZ::Uuid sourceUuid_1 = CreateSourceEntry(m_sourceIDs.at(1), m_folderIds.at(0), "Source_1"); + CreateProduct(m_productIDs.at(0), sourceUuid_1, "Product_1_0"); + CreateProduct(m_productIDs.at(1), sourceUuid_1, "Product_1_1"); + } + + void AssetBrowserTest::PrintModel(const QAbstractItemModel* model, AZStd::function printer) + { + AZStd::deque> indices; + indices.push_back({ model->index(0, 0), 0 }); + while (!indices.empty()) + { + auto [index, depth] = indices.front(); + indices.pop_front(); + + QString indentString; + for (int i = 0; i < depth; ++i) + { + indentString += " "; + } + const QString message = indentString + index.data(Qt::DisplayRole).toString(); + printer(message); + + for (int i = 0; i < model->rowCount(index); ++i) + { + indices.emplace_front(model->index(i, 0, index), depth + 1); + } + } + } + + QModelIndex AssetBrowserTest::GetModelIndex(const QAbstractItemModel* model, int targetDepth, int row) + { + AZStd::deque> indices; + indices.push_back({ model->index(0, 0), 0 }); + while (!indices.empty()) + { + auto [index, depth] = indices.front(); + indices.pop_front(); + + for (int i = 0; i < model->rowCount(index); ++i) + { + if (depth + 1 == targetDepth && row == i) + { + return model->index(i, 0, index); + } + indices.emplace_front(model->index(i, 0, index), depth + 1); + } + } + return QModelIndex(); + } + + AZStd::shared_ptr AssetBrowserTest::GetRootEntry() + { + return m_assetBrowserComponent->GetAssetBrowserModel()->GetRootEntry(); + } + + AZStd::vector AssetBrowserTest::GetVectorFromFormattedString(const QString& formattedString) + { + AZStd::vector hierarchySections; + QStringList splittedList = formattedString.split('\n', Qt::SkipEmptyParts); + + for (auto& str : splittedList) + { + str.replace(" ", ""); + hierarchySections.push_back(str); + } + return hierarchySections; + } + + TEST_F(AssetBrowserTest, CheckCorrectNumberOfEntriesInTableView) + { + m_filterModel->FilterUpdatedSlotImmediate(); + const int tableViewRowcount = m_tableModel->rowCount(); + + // RowCount should be 17 -> 5 SourceEntries + 12 ProductEntries) + EXPECT_EQ(tableViewRowcount, 17); + } + + TEST_F(AssetBrowserTest, CheckCorrectNumberOfEntriesInTableViewAfterStringFilter) + { + /* + *-Source_1 + * | + * |-product_1_0 + * |-product_1_1 + * + * + * Matching entries = 3 + */ + + // Apply string filter + m_searchWidget->SetTextFilter(QString("source_1")); + m_filterModel->FilterUpdatedSlotImmediate(); + + const int tableViewRowcount = m_tableModel->rowCount(); + EXPECT_EQ(tableViewRowcount, 3); + } + + TEST_F(AssetBrowserTest, CheckScanFolderAddition) + { + EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 1); + const int newFolderId = 20; + AddScanFolder(newFolderId, "E:/TestFolder/TestFolder2", "TestFolder"); + + // Since the folder is empty it shouldn't be added to the model. + EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 1); + + CreateSourceEntry(123, newFolderId, "DummyFile"); + + // When we add a file to the folder it should be added to the model + EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 2); + } + +} // namespace UnitTest diff --git a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake index 008c09188b..d597c9b570 100644 --- a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake +++ b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake @@ -123,6 +123,7 @@ set(FILES UI/EntityIdQLineEditTests.cpp UI/EntityOutlinerTests.cpp UI/EntityPropertyEditorTests.cpp + UI/AssetBrowserTests.cpp UndoStack.cpp Viewport/ClusterTests.cpp Viewport/ViewportEditorModeTests.cpp From c0d36399db2c7e14c0c5e4886621bb669d6aed88 Mon Sep 17 00:00:00 2001 From: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> Date: Mon, 8 Nov 2021 13:37:35 +0000 Subject: [PATCH 112/177] Improvements to feedback for default camera orbit point (when no entity is selected) (#5397) * improvements to camera orbit feedback Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * minor tidy-up before publishing PR Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * updates following review feedback Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> --- .../EditorModularViewportCameraComposer.cpp | 113 ++++++++++++++++-- .../EditorModularViewportCameraComposer.h | 14 +++ 2 files changed, 119 insertions(+), 8 deletions(-) diff --git a/Code/Editor/EditorModularViewportCameraComposer.cpp b/Code/Editor/EditorModularViewportCameraComposer.cpp index 08ca0df221..72fd37605a 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.cpp +++ b/Code/Editor/EditorModularViewportCameraComposer.cpp @@ -13,9 +13,32 @@ #include #include #include +#include #include #include +AZ_CVAR( + bool, + ed_cameraPinDefaultOrbit, + true, + nullptr, + AZ::ConsoleFunctorFlags::Null, + "Sets whether the default orbit point moves with the camera or not"); +AZ_CVAR( + bool, + ed_cameraDefaultOrbitAxesOrtho, + true, + nullptr, + AZ::ConsoleFunctorFlags::Null, + "Sets whether to draw the default orbit point as orthographic or not"); +AZ_CVAR( + float, + ed_cameraDefaultOrbitFadeDuration, + 0.5f, + nullptr, + AZ::ConsoleFunctorFlags::Null, + "Sets how long the default orbit point should take to appear and disappear"); + namespace SandboxEditor { static AzFramework::TranslateCameraInputChannelIds BuildTranslateCameraInputChannelIds() @@ -190,12 +213,6 @@ namespace SandboxEditor return AZStd::nullopt; }; - const auto orbitFn = [pivotFn](const AZ::Vector3& pivotFallback = AZ::Vector3::CreateZero()) - { - // return the pivot otherwise use the fallback - return pivotFn().value_or(pivotFallback); - }; - m_firstPersonFocusCamera = AZStd::make_shared(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusLook); @@ -204,9 +221,26 @@ namespace SandboxEditor m_orbitCamera = AZStd::make_shared(SandboxEditor::CameraOrbitChannelId()); m_orbitCamera->SetPivotFn( - [orbitFn]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction) + [this, pivotFn](const AZ::Vector3& position, const AZ::Vector3& direction) { - return orbitFn(position + direction * SandboxEditor::CameraDefaultOrbitDistance()); + // return the pivot + if (auto pivot = pivotFn()) + { + return pivot.value(); + } + + // start ticking and drawing (for the default pivot) + AZ::TickBus::Handler::BusConnect(); + AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId()); + + m_defaultOrbiting = true; + // calculate the default orbit point + if (!ed_cameraPinDefaultOrbit || m_orbitCamera->Beginning()) + { + m_defaultOrbitPoint = position + direction * SandboxEditor::CameraDefaultOrbitDistance(); + } + + return m_defaultOrbitPoint; }); m_orbitRotateCamera = AZStd::make_shared(SandboxEditor::CameraOrbitLookChannelId()); @@ -311,4 +345,67 @@ namespace SandboxEditor m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame); } } + + void EditorModularViewportCameraComposer::OnTick(const float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + { + const float delta = [duration = &ed_cameraDefaultOrbitFadeDuration, deltaTime] { + if (*duration == 0.0f) { + return 1.0f; + } + return deltaTime / *duration; + }(); + + if (m_defaultOrbiting) + { + m_defaultOrbitOpacity = AZStd::min(m_defaultOrbitOpacity + delta, 1.0f); + } + else + { + m_defaultOrbitOpacity = AZStd::max(m_defaultOrbitOpacity - delta, 0.0f); + if (m_defaultOrbitOpacity == 0.0f) + { + AZ::TickBus::Handler::BusDisconnect(); + AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect(); + } + } + + m_defaultOrbiting = false; + } + + static void DrawTransformAxis( + AzFramework::DebugDisplayRequests& display, + const AzFramework::CameraState& cameraState, + const AZ::Vector3& pivot, + const float axisLength, + const float alpha) + { + const int prevState = display.GetState(); + + display.DepthWriteOff(); + display.DepthTestOff(); + display.CullOff(); + + const float orthoScale = + ed_cameraDefaultOrbitAxesOrtho ? AzToolsFramework::CalculateScreenToWorldMultiplier(pivot, cameraState) : 1.0f; + + display.SetColor(AZ::Color::CreateFromVector3AndFloat(AZ::Colors::Red.GetAsVector3(), alpha)); + display.DrawLine(pivot, pivot + AZ::Vector3::CreateAxisX() * axisLength * orthoScale); + display.SetColor(AZ::Color::CreateFromVector3AndFloat(AZ::Colors::LawnGreen.GetAsVector3(), alpha)); + display.DrawLine(pivot, pivot + AZ::Vector3::CreateAxisY() * axisLength * orthoScale); + display.SetColor(AZ::Color::CreateFromVector3AndFloat(AZ::Colors::Blue.GetAsVector3(), alpha)); + display.DrawLine(pivot, pivot + AZ::Vector3::CreateAxisZ() * axisLength * orthoScale); + + display.DepthWriteOn(); + display.DepthTestOn(); + display.CullOn(); + + display.SetState(prevState); + } + + void EditorModularViewportCameraComposer::DisplayViewport( + [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + DrawTransformAxis( + debugDisplay, AzToolsFramework::GetCameraState(viewportInfo.m_viewportId), m_defaultOrbitPoint, 1.0f, m_defaultOrbitOpacity); + } } // namespace SandboxEditor diff --git a/Code/Editor/EditorModularViewportCameraComposer.h b/Code/Editor/EditorModularViewportCameraComposer.h index 9cfd6f3554..6cd5df533c 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.h +++ b/Code/Editor/EditorModularViewportCameraComposer.h @@ -9,6 +9,8 @@ #pragma once #include +#include +#include #include #include #include @@ -20,6 +22,8 @@ namespace SandboxEditor class EditorModularViewportCameraComposer : private EditorModularViewportCameraComposerNotificationBus::Handler , private Camera::EditorCameraNotificationBus::Handler + , private AzFramework::ViewportDebugDisplayEventBus::Handler + , private AZ::TickBus::Handler { public: SANDBOX_API explicit EditorModularViewportCameraComposer(AzFramework::ViewportId viewportId); @@ -29,6 +33,12 @@ namespace SandboxEditor SANDBOX_API AZStd::shared_ptr CreateModularViewportCameraController(); private: + // AzFramework::ViewportDebugDisplayEventBus overrides ... + void DisplayViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; + + // AZ::TickBus overrides ... + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + //! Setup all internal camera inputs. void SetupCameras(); @@ -52,5 +62,9 @@ namespace SandboxEditor AZStd::shared_ptr m_orbitFocusCamera; AzFramework::ViewportId m_viewportId; + + float m_defaultOrbitOpacity = 0.0f; //!< The default orbit axes opacity (to fade in and out). + AZ::Vector3 m_defaultOrbitPoint = AZ::Vector3::CreateZero(); //!< The orbit point to use when no entity is selected. + bool m_defaultOrbiting = false; //!< Is the camera default orbiting (orbiting when there's no selected entity). }; } // namespace SandboxEditor From 5a39361f777a4aa8e217bf3a899d2a3bf12e70bb Mon Sep 17 00:00:00 2001 From: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com> Date: Mon, 8 Nov 2021 07:49:30 -0800 Subject: [PATCH 113/177] =?UTF-8?q?ATOM-16747=20RPISystemInterface::GetDef?= =?UTF-8?q?aultScene=20returns=20the=20scene=20crea=E2=80=A6=20(#5153)=20(?= =?UTF-8?q?#5389)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ATOM-16747 RPISystemInterface::GetDefaultScene returns the scene created by PreviewRenderer but not the Main Scene Deprecate GetDefaultScene() function. Update all the places which use GetDefaultScene to use Scene::GetFeatureProcessorFromEntityId or GetMainScene. Tested with Editor, UI Editor, Material Editor, game launcher. Signed-off-by: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com> (cherry picked from commit 8da6bea0733aa43c19937fc8ba46d5ab3e514968) --- .../Code/Source/BootstrapSystemComponent.cpp | 1 + .../Code/Source/LuxCore/LuxCoreTexture.cpp | 10 +++-- .../Code/Include/Atom/RPI.Public/RPISystem.h | 3 +- .../Atom/RPI.Public/RPISystemInterface.h | 12 ++++-- .../RPI/Code/Include/Atom/RPI.Public/Scene.h | 17 +++++--- .../Atom/RPI.Reflect/System/SceneDescriptor.h | 4 ++ .../RPI/Code/Source/RPI.Public/Culling.cpp | 4 +- .../RPI/Code/Source/RPI.Public/RPISystem.cpp | 41 ++++++++++++++----- .../Atom/RPI/Code/Source/RPI.Public/Scene.cpp | 25 +++++++++-- .../PreviewRenderer/PreviewRenderer.cpp | 1 + .../MaterialEditorViewportInputController.cpp | 4 +- .../RotateEnvironmentBehavior.cpp | 3 +- .../Viewport/MaterialViewportRenderer.cpp | 1 + .../Code/Source/AtomBridgeSystemComponent.cpp | 6 +-- .../AtomDebugDisplayViewportInterface.cpp | 3 +- .../AtomDebugDisplayViewportInterface.h | 2 +- .../AtomLyIntegration/AtomFont/FFont.h | 12 ------ ...eGlobalIlluminationComponentController.cpp | 6 +-- .../Source/Grid/GridComponentController.cpp | 2 +- .../DisplayMapperComponentController.cpp | 3 +- .../SkinnedMesh/SkinnedMeshDebugDisplay.h | 2 +- .../Tools/EMStudio/AnimViewportRenderer.cpp | 4 +- .../Components/BlastSystemComponent.cpp | 25 ++++++----- Gems/LyShine/Code/Source/Draw2d.cpp | 12 +++--- Gems/LyShine/Code/Source/LyShine.cpp | 4 +- Gems/LyShine/Code/Source/UiRenderer.cpp | 13 +++--- Gems/LyShine/Code/Source/UiRenderer.h | 4 +- 27 files changed, 133 insertions(+), 91 deletions(-) diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp index c232d4bba7..84fc58718c 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp @@ -272,6 +272,7 @@ namespace AZ // Create and register a scene with all available feature processors RPI::SceneDescriptor sceneDesc; + sceneDesc.m_nameId = AZ::Name("Main"); AZ::RPI::ScenePtr atomScene = RPI::Scene::CreateScene(sceneDesc); atomScene->EnableAllFeatureProcessors(); atomScene->Activate(); diff --git a/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexture.cpp b/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexture.cpp index aa906a06f7..1fd8f0d91c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexture.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/LuxCore/LuxCoreTexture.cpp @@ -32,7 +32,7 @@ namespace AZ { if (m_rtPipeline) { - AZ::RPI::RPISystemInterface::Get()->GetDefaultScene()->RemoveRenderPipeline(m_rtPipeline->GetId()); + m_rtPipeline->RemoveFromScene(); m_rtPipeline = nullptr; } @@ -111,8 +111,12 @@ namespace AZ parentPass->SetSourceTexture(m_texture, RHI::Format::R8G8B8A8_UNORM); break; } - - AZ::RPI::RPISystemInterface::Get()->GetDefaultScene()->AddRenderPipeline(m_rtPipeline); + + const auto mainScene = AZ::RPI::RPISystemInterface::Get()->GetSceneByName(AZ::Name("RPI")); + if (mainScene) + { + mainScene->AddRenderPipeline(m_rtPipeline); + } } bool LuxCoreTexture::IsIBLTexture() diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h index 4914e4b6fe..92370c5a82 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h @@ -70,7 +70,8 @@ namespace AZ void InitializeSystemAssets() override; void RegisterScene(ScenePtr scene) override; void UnregisterScene(ScenePtr scene) override; - ScenePtr GetScene(const SceneId& sceneId) const override; + Scene* GetScene(const SceneId& sceneId) const override; + Scene* GetSceneByName(const AZ::Name& name) const override; ScenePtr GetDefaultScene() const override; RenderPipelinePtr GetRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle) override; Data::Asset GetCommonShaderAssetForSrgs() const override; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystemInterface.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystemInterface.h index fe81596bd7..3f30d498cf 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystemInterface.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystemInterface.h @@ -13,6 +13,7 @@ #include +#include #include namespace AZ @@ -46,11 +47,14 @@ namespace AZ //! Unregister a scene from RPISystem. The scene won't be simulated or rendered. virtual void UnregisterScene(ScenePtr scene) = 0; - // [GFX TODO] to be removed when we have scene setup in AZ Core - virtual ScenePtr GetDefaultScene() const = 0; - + //! Deprecated. Use GetSceneByName(name), GetSceneForEntityContextId(entityContextId) or Scene::GetSceneForEntityId(AZ::EntityId entityId) instead + AZ_DEPRECATED(virtual ScenePtr GetDefaultScene() const = 0;, "This method has been deprecated. Please use GetSceneByName(name), GetSceneForEntityContextId(entityContextId) or Scene::GetSceneForEntityId(AZ::EntityId entityId) instead."); + //! Get scene by using scene id. - virtual ScenePtr GetScene(const SceneId& sceneId) const = 0; + virtual Scene* GetScene(const SceneId& sceneId) const = 0; + + //! Get scene by using scene name. + virtual Scene* GetSceneByName(const AZ::Name& name) const = 0; //! Get the render pipeline created for a window virtual RenderPipelinePtr GetRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle) = 0; 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 fc81368331..f86383101a 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h @@ -80,6 +80,9 @@ namespace AZ //! Gets the RPI::Scene for a given entityContextId. //! May return nullptr if there is no RPI::Scene created for that entityContext. static Scene* GetSceneForEntityContextId(AzFramework::EntityContextId entityContextId); + + //! Gets the RPI::Scene for a given entityId. + static Scene* GetSceneForEntityId(AZ::EntityId entityId); ~Scene(); @@ -135,6 +138,8 @@ namespace AZ const SceneId& GetId() const; + AZ::Name GetName() const; + //! Set default pipeline by render pipeline ID. //! It returns true if the default render pipeline was set from the input ID. //! If the specified render pipeline doesn't exist in this scene then it won't do anything and returns false. @@ -245,6 +250,9 @@ namespace AZ // The uuid to identify this scene. SceneId m_id; + // Scene's name which is set at initialization. Can be empty + AZ::Name m_name; + bool m_activated = false; bool m_taskGraphActive = false; // update during tick, to ensure it only changes on frame boundaries @@ -286,13 +294,10 @@ namespace AZ template FeatureProcessorType* Scene::GetFeatureProcessorForEntity(AZ::EntityId entityId) { - // Find the entity context for the entity ID. - AzFramework::EntityContextId entityContextId = AzFramework::EntityContextId::CreateNull(); - AzFramework::EntityIdContextQueryBus::EventResult(entityContextId, entityId, &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId); - - if (!entityContextId.IsNull()) + RPI::Scene* renderScene = GetSceneForEntityId(entityId); + if (renderScene) { - return GetFeatureProcessorForEntityContextId(entityContextId); + return renderScene->GetFeatureProcessor(); } return nullptr; }; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/System/SceneDescriptor.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/System/SceneDescriptor.h index 2072568d59..eb7a2b6cb7 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/System/SceneDescriptor.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/System/SceneDescriptor.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include @@ -25,6 +26,9 @@ namespace AZ //! List of feature processors which the scene will initially enable. AZStd::vector m_featureProcessorNames; + + //! A name used as scene id. It can be used to search a registered scene via RPISystemInterface::GetScene() + AZ::Name m_nameId; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index 348f0aa57a..ac6b10694e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -699,9 +699,7 @@ namespace AZ m_parentScene = parentScene; AZ_Assert(m_visScene == nullptr, "IVisibilityScene already created for this RPI::Scene"); - char sceneIdBuf[40] = ""; - m_parentScene->GetId().ToString(sceneIdBuf); - AZ::Name visSceneName(AZStd::string::format("RenderCullScene[%s]", sceneIdBuf)); + AZ::Name visSceneName(AZStd::string::format("RenderCullScene[%s]", m_parentScene->GetName().GetCStr())); m_visScene = AZ::Interface::Get()->CreateVisibilityScene(visSceneName); #ifdef AZ_CULL_DEBUG_ENABLED diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp index eb74a81e3e..943966c13b 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp @@ -159,6 +159,11 @@ namespace AZ AZ_Assert(false, "Scene was already registered"); return; } + else if (!scene->GetName().IsEmpty() && scene->GetName() == sceneItem->GetName()) + { + // only report a warning if there is a scene with duplicated name + AZ_Warning("RPISystem", false, "There is a registered scene with same name [%s]", scene->GetName().GetCStr()); + } } m_scenes.push_back(scene); @@ -177,11 +182,35 @@ namespace AZ AZ_Assert(false, "Can't unregister scene which wasn't registered"); } - ScenePtr RPISystem::GetScene(const SceneId& sceneId) const + Scene* RPISystem::GetScene(const SceneId& sceneId) const { for (const auto& scene : m_scenes) { if (scene->GetId() == sceneId) + { + return scene.get(); + } + } + return nullptr; + } + + Scene* RPISystem::GetSceneByName(const AZ::Name& name) const + { + for (const auto& scene : m_scenes) + { + if (scene->GetName() == name) + { + return scene.get(); + } + } + return nullptr; + } + + ScenePtr RPISystem::GetDefaultScene() const + { + for (const auto& scene : m_scenes) + { + if (scene->GetName() == AZ::Name("Main")) { return scene; } @@ -189,16 +218,6 @@ namespace AZ return nullptr; } - ScenePtr RPISystem::GetDefaultScene() const - { - if (m_scenes.size() > 0) - { - return m_scenes[0]; - } - return nullptr; - } - - RenderPipelinePtr RPISystem::GetRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle) { RenderPipelinePtr renderPipeline; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index c5d82c6f29..41fefda656 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -45,7 +45,9 @@ namespace AZ auto shaderAsset = RPISystemInterface::Get()->GetCommonShaderAssetForSrgs(); scene->m_srg = ShaderResourceGroup::Create(shaderAsset, sceneSrgLayout->GetName()); } - + + scene->m_name = sceneDescriptor.m_nameId; + return ScenePtr(scene); } @@ -83,10 +85,23 @@ namespace AZ return nullptr; } + Scene* Scene::GetSceneForEntityId(AZ::EntityId entityId) + { + // Find the entity context for the entity ID. + AzFramework::EntityContextId entityContextId = AzFramework::EntityContextId::CreateNull(); + AzFramework::EntityIdContextQueryBus::EventResult(entityContextId, entityId, &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId); + + if (!entityContextId.IsNull()) + { + return GetSceneForEntityContextId(entityContextId); + } + return nullptr; + } + Scene::Scene() { - m_id = Uuid::CreateRandom(); + m_id = AZ::Uuid::CreateRandom(); m_cullingScene = aznew CullingScene(); SceneRequestBus::Handler::BusConnect(m_id); m_drawFilterTagRegistry = RHI::DrawFilterTagRegistry::Create(); @@ -299,7 +314,6 @@ namespace AZ // Force to update the lookup table since adding render pipeline would effect any pipeline states created before pass system tick RebuildPipelineStatesLookup(); - AZ_Assert(!m_id.IsNull(), "RPI::Scene needs to have a valid uuid."); SceneNotificationBus::Event(m_id, &SceneNotification::OnRenderPipelineAdded, pipeline); } @@ -785,6 +799,11 @@ namespace AZ { return m_id; } + + AZ::Name Scene::GetName() const + { + return m_name; + } bool Scene::SetDefaultRenderPipeline(const RenderPipelineId& pipelineId) { diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp index 2b39a87623..27d468e64b 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRenderer.cpp @@ -43,6 +43,7 @@ namespace AtomToolsFramework &PreviewerFeatureProcessorProviderBus::Handler::GetRequiredFeatureProcessors, featureProcessors); AZ::RPI::SceneDescriptor sceneDesc; + sceneDesc.m_nameId = AZ::Name("PreviewRenderer"); sceneDesc.m_featureProcessorNames.assign(featureProcessors.begin(), featureProcessors.end()); m_scene = AZ::RPI::Scene::CreateScene(sceneDesc); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp index 932e2e7436..1784405ffa 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/MaterialEditorViewportInputController.cpp @@ -279,9 +279,9 @@ namespace MaterialEditor // reset environment AZ::Transform iblTransform = AZ::Transform::CreateIdentity(); AZ::TransformBus::Event(m_iblEntityId, &AZ::TransformBus::Events::SetLocalTM, iblTransform); + const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateIdentity(); - AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); - auto skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor(); + auto skyBoxFeatureProcessorInterface = AZ::RPI::Scene::GetFeatureProcessorForEntity(m_iblEntityId); skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix); if (m_behavior) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.cpp index 21d7dee51b..17fb3f3e52 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/InputController/RotateEnvironmentBehavior.cpp @@ -25,8 +25,7 @@ namespace MaterialEditor m_iblEntityId, &MaterialEditorViewportInputControllerRequestBus::Handler::GetIblEntityId); AZ_Assert(m_iblEntityId.IsValid(), "Failed to find m_iblEntityId"); - AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); - m_skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor(); + m_skyBoxFeatureProcessorInterface = AZ::RPI::Scene::GetFeatureProcessorForEntity(m_iblEntityId); } void RotateEnvironmentBehavior::TickInternal(float x, float y, float z) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp index 6d6fd377e3..4ad87492e3 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportRenderer.cpp @@ -67,6 +67,7 @@ namespace MaterialEditor // Create and register a scene with all available feature processors AZ::RPI::SceneDescriptor sceneDesc; + sceneDesc.m_nameId = AZ::Name("MaterialViewport"); m_scene = AZ::RPI::Scene::CreateScene(sceneDesc); m_scene->EnableAllFeatureProcessors(); diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp index b439ac475c..d822b16486 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomBridgeSystemComponent.cpp @@ -108,7 +108,7 @@ namespace AZ { m_dynamicDrawManager.reset(); AZ::RPI::ViewportContextManagerNotificationsBus::Handler::BusDisconnect(); - RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get(); + RPI::Scene* scene = AZ::RPI::Scene::GetSceneForEntityContextId(m_entityContextId); // Check if scene is emptry since scene might be released already when running AtomSampleViewer if (scene) { @@ -157,9 +157,9 @@ namespace AZ void AtomBridgeSystemComponent::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) { - AZ_UNUSED(bootstrapScene); // Make default AtomDebugDisplayViewportInterface - AZStd::shared_ptr mainEntityDebugDisplay = AZStd::make_shared(AzFramework::g_defaultSceneEntityDebugDisplayId); + AZStd::shared_ptr mainEntityDebugDisplay = + AZStd::make_shared(AzFramework::g_defaultSceneEntityDebugDisplayId, bootstrapScene); m_activeViewportsList[AzFramework::g_defaultSceneEntityDebugDisplayId] = mainEntityDebugDisplay; } diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp index 1ccc36ab4d..12e4ccd8ad 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.cpp @@ -256,12 +256,11 @@ namespace AZ::AtomBridge viewportContextPtr->ConnectSceneChangedHandler(m_sceneChangeHandler); } - AtomDebugDisplayViewportInterface::AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress) + AtomDebugDisplayViewportInterface::AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress, RPI::Scene* scene) { ResetRenderState(); m_viewportId = defaultInstanceAddress; m_defaultInstance = true; - RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get(); InitInternal(scene, nullptr); } diff --git a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h index 07f4efdf50..5f902c5884 100644 --- a/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h +++ b/Gems/AtomLyIntegration/AtomBridge/Code/Source/AtomDebugDisplayViewportInterface.h @@ -124,7 +124,7 @@ namespace AZ::AtomBridge AZ_RTTI(AtomDebugDisplayViewportInterface, "{09AF6A46-0100-4FBF-8F94-E6B221322D14}", AzFramework::DebugDisplayRequestBus::Handler); explicit AtomDebugDisplayViewportInterface(AZ::RPI::ViewportContextPtr viewportContextPtr); - explicit AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress); + explicit AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress, RPI::Scene* scene); ~AtomDebugDisplayViewportInterface(); void ResetRenderState(); diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h index 2cc8a67cfa..ff6ad7c151 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h +++ b/Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/FFont.h @@ -133,18 +133,6 @@ namespace AZ typedef std::vector FontEffects; typedef FontEffects::iterator FontEffectsIterator; - struct FontPipelineStateMapKey - { - AZ::RPI::SceneId m_sceneId; // which scene pipeline state is attached to (via Render Pipeline) - AZ::RHI::DrawListTag m_drawListTag; // which render pass this pipeline draws in by default - - bool operator<(const FontPipelineStateMapKey& other) const - { - return m_sceneId < other.m_sceneId - || (m_sceneId == other.m_sceneId && m_drawListTag < other.m_drawListTag); - } - }; - struct FontShaderData { AZ::RHI::ShaderInputNameIndex m_imageInputIndex = "m_texture"; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp index ee322c8afd..e844219811 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationComponentController.cpp @@ -48,11 +48,7 @@ namespace AZ void DiffuseGlobalIlluminationComponentController::Activate(EntityId entityId) { - AZ_UNUSED(entityId); - - const RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); - m_featureProcessor = scene->GetFeatureProcessor(); - + m_featureProcessor = AZ::RPI::Scene::GetFeatureProcessorForEntity(entityId); OnConfigChanged(); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp index b4131894f4..2611021359 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Grid/GridComponentController.cpp @@ -79,7 +79,7 @@ namespace AZ m_entityId = entityId; m_dirty = true; - RPI::ScenePtr scene = RPI::RPISystemInterface::Get()->GetDefaultScene(); + RPI::Scene* scene = RPI::Scene::GetSceneForEntityId(m_entityId); if (scene) { AZ::RPI::SceneNotificationBus::Handler::BusConnect(scene->GetId()); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp index e86c909121..a0577c6240 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp @@ -357,8 +357,7 @@ namespace AZ void DisplayMapperComponentController::OnConfigChanged() { // Register the configuration with the AcesDisplayMapperFeatureProcessor for this scene. - const AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get(); - DisplayMapperFeatureProcessorInterface* fp = scene->GetFeatureProcessor(); + DisplayMapperFeatureProcessorInterface* fp = AZ::RPI::Scene::GetFeatureProcessorForEntity(m_entityId); DisplayMapperConfigurationDescriptor desc; desc.m_operationType = m_configuration.m_displayMapperOperation; desc.m_ldrGradingLutEnabled = m_configuration.m_ldrColorGradingLutEnabled; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkinnedMesh/SkinnedMeshDebugDisplay.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkinnedMesh/SkinnedMeshDebugDisplay.h index e789b6dc80..5b060198dc 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkinnedMesh/SkinnedMeshDebugDisplay.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SkinnedMesh/SkinnedMeshDebugDisplay.h @@ -48,7 +48,7 @@ namespace AZ // CVar for toggling the display of the scene stats int r_skinnedMeshDisplaySceneStats = 0; // SceneId to query for the stats - RPI::SceneId m_sceneId = RPI::SceneId::CreateNull(); + RPI::SceneId m_sceneId; }; }// namespace Render }// namespace AZ diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp index faa033956a..eb99b8171a 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp @@ -60,6 +60,7 @@ namespace EMStudio // Create and register a scene with all available feature processors AZ::RPI::SceneDescriptor sceneDesc; + sceneDesc.m_nameId = AZ::Name("AnimViewport"); m_scene = AZ::RPI::Scene::CreateScene(sceneDesc); m_scene->EnableAllFeatureProcessors(); @@ -213,8 +214,7 @@ namespace EMStudio AZ::TransformBus::Event(m_iblEntity->GetId(), &AZ::TransformBus::Events::SetLocalTM, iblTransform); const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateIdentity(); - AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); - auto skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor(); + auto skyBoxFeatureProcessorInterface = m_scene->GetFeatureProcessor(); skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix); } diff --git a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp index 00629eb65d..4c0f15463a 100644 --- a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp +++ b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp @@ -255,19 +255,22 @@ namespace Blast BlastFamilyComponentRequestBus::Broadcast( &BlastFamilyComponentRequests::FillDebugRenderBuffer, buffer, m_debugRenderMode); - // This is a system component, and thus is not associated with a specific scene, so use the default scene + // This is a system component, and thus is not associated with a specific scene, so use the bootstrap scene // for the debug drawing - const auto defaultScene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); - auto drawQueue = AZ::RPI::AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(defaultScene); - - for (DebugLine& line : buffer.m_lines) + const auto mainScene = AZ::RPI::RPISystemInterface::Get()->GetSceneByName(AZ::Name("Main")); + if (mainScene) { - AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArguments; - drawArguments.m_verts = &line.m_p0; - drawArguments.m_vertCount = 2; - drawArguments.m_colors = &line.m_color; - drawArguments.m_colorCount = 1; - drawQueue->DrawLines(drawArguments); + auto drawQueue = AZ::RPI::AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(mainScene); + + for (DebugLine& line : buffer.m_lines) + { + AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArguments; + drawArguments.m_verts = &line.m_p0; + drawArguments.m_vertCount = 2; + drawArguments.m_colors = &line.m_color; + drawArguments.m_colorCount = 1; + drawQueue->DrawLines(drawArguments); + } } } } diff --git a/Gems/LyShine/Code/Source/Draw2d.cpp b/Gems/LyShine/Code/Source/Draw2d.cpp index b0539900e2..3476c530b2 100644 --- a/Gems/LyShine/Code/Source/Draw2d.cpp +++ b/Gems/LyShine/Code/Source/Draw2d.cpp @@ -65,7 +65,7 @@ CDraw2d::~CDraw2d() } //////////////////////////////////////////////////////////////////////////////////////////////////// -void CDraw2d::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene) +void CDraw2d::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) { // At this point the RPI is ready for use @@ -74,16 +74,16 @@ void CDraw2d::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapSc AZ::Data::Instance shader = AZ::RPI::LoadCriticalShader(shaderFilepath); // Set scene to be associated with the dynamic draw context - AZ::RPI::ScenePtr scene; + AZ::RPI::Scene* scene = nullptr; if (m_viewportContext) { // Use scene associated with the specified viewport context - scene = m_viewportContext->GetRenderScene(); + scene = m_viewportContext->GetRenderScene().get(); } else { - // No viewport context specified, use default scene - scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); + // No viewport context specified, use main scene + scene = bootstrapScene; } AZ_Assert(scene != nullptr, "Attempting to create a DynamicDrawContext for a viewport context that has not been associated with a scene yet."); @@ -113,7 +113,7 @@ void CDraw2d::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapSc else { // Render target support is disabled - m_dynamicDraw->SetOutputScope(scene.get()); + m_dynamicDraw->SetOutputScope(scene); } m_dynamicDraw->EndInit(); diff --git a/Gems/LyShine/Code/Source/LyShine.cpp b/Gems/LyShine/Code/Source/LyShine.cpp index 2cee4e92eb..19c6e4281e 100644 --- a/Gems/LyShine/Code/Source/LyShine.cpp +++ b/Gems/LyShine/Code/Source/LyShine.cpp @@ -653,12 +653,12 @@ void CLyShine::OnRenderTick() } //////////////////////////////////////////////////////////////////////////////////////////////////// -void CLyShine::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene) +void CLyShine::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) { // Load cursor if its path was set before RPI was initialized LoadUiCursor(); - LyShinePassDataRequestBus::Handler::BusConnect(AZ::RPI::RPISystemInterface::Get()->GetDefaultScene()->GetId()); + LyShinePassDataRequestBus::Handler::BusConnect(bootstrapScene->GetId()); } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Gems/LyShine/Code/Source/UiRenderer.cpp b/Gems/LyShine/Code/Source/UiRenderer.cpp index 3111f31615..98b376ec8b 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.cpp +++ b/Gems/LyShine/Code/Source/UiRenderer.cpp @@ -52,7 +52,7 @@ bool UiRenderer::IsReady() return m_isRPIReady; } -void UiRenderer::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene) +void UiRenderer::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) { // At this point the RPI is ready for use @@ -64,16 +64,17 @@ void UiRenderer::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstra if (m_viewportContext) { // Create a new scene based on the user specified viewport context - m_scene = CreateScene(m_viewportContext); + m_ownedScene = CreateScene(m_viewportContext); + m_scene = m_ownedScene.get(); } else { // No viewport context specified, use default scene - m_scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene(); + m_scene = bootstrapScene; } // Create a dynamic draw context for UI Canvas drawing for the scene - m_dynamicDraw = CreateDynamicDrawContext(m_scene, uiShader); + m_dynamicDraw = CreateDynamicDrawContext(uiShader); if (m_dynamicDraw) { @@ -93,6 +94,7 @@ AZ::RPI::ScenePtr UiRenderer::CreateScene(AZStd::shared_ptrEnableAllFeatureProcessors(); // LYSHINE_ATOM_TODO - have a UI pipeline and enable only needed fps @@ -116,7 +118,6 @@ AZ::RPI::ScenePtr UiRenderer::CreateScene(AZStd::shared_ptr UiRenderer::CreateDynamicDrawContext( - AZ::RPI::ScenePtr scene, AZ::Data::Instance uiShader) { // Find the pass that renders the UI canvases after the rtt passes @@ -144,7 +145,7 @@ AZ::RHI::Ptr UiRenderer::CreateDynamicDrawContext( else { // Render target support is disabled - dynamicDraw->SetOutputScope(m_scene.get()); + dynamicDraw->SetOutputScope(m_scene); } dynamicDraw->EndInit(); diff --git a/Gems/LyShine/Code/Source/UiRenderer.h b/Gems/LyShine/Code/Source/UiRenderer.h index 3be3c832d3..0e15d41907 100644 --- a/Gems/LyShine/Code/Source/UiRenderer.h +++ b/Gems/LyShine/Code/Source/UiRenderer.h @@ -152,7 +152,6 @@ private: // member functions //! Create a dynamic draw context for this renderer AZ::RHI::Ptr CreateDynamicDrawContext( - AZ::RPI::ScenePtr scene, AZ::Data::Instance uiShader); //! Bind the global white texture for all the texture units we use @@ -175,7 +174,8 @@ protected: // attributes // Set by user when viewport context is not the main/default viewport AZStd::shared_ptr m_viewportContext; - AZ::RPI::ScenePtr m_scene; + AZ::RPI::ScenePtr m_ownedScene; + AZ::RPI::Scene* m_scene = nullptr; #ifndef _RELEASE int m_debugTextureDataRecordLevel = 0; From 652e35b0ca2314e825ebd48a266e5b58caa5920a Mon Sep 17 00:00:00 2001 From: srikappa-amzn <82230713+srikappa-amzn@users.noreply.github.com> Date: Mon, 8 Nov 2021 21:31:57 +0530 Subject: [PATCH 114/177] Fix camera transforms being reset when switching to default editor camera (#5326) Signed-off-by: srikappa-amzn --- Code/Editor/EditorViewportWidget.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp index 5c5ab87058..290f0fd17f 100644 --- a/Code/Editor/EditorViewportWidget.cpp +++ b/Code/Editor/EditorViewportWidget.cpp @@ -2021,10 +2021,6 @@ void EditorViewportWidget::SetDefaultCamera() GetViewManager()->SetCameraObjectId(GUID_NULL); SetName(m_defaultViewName); - // Set the default Editor Camera position. - m_defaultViewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition())); - SetViewTM(m_defaultViewTM); - // Synchronize the configured editor viewport FOV to the default camera if (m_viewPane) { @@ -2041,6 +2037,10 @@ void EditorViewportWidget::SetDefaultCamera() atomViewportRequests->PushView(contextName, m_defaultView); } + // Set the default Editor Camera position. + m_defaultViewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition())); + SetViewTM(m_defaultViewTM); + PostCameraSet(); } From 14a120627415a0b295ba4d486b00467d71540b55 Mon Sep 17 00:00:00 2001 From: nggieber Date: Mon, 8 Nov 2021 08:17:28 -0800 Subject: [PATCH 115/177] Add additional info handling and proper display for gems Signed-off-by: nggieber --- .../Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp | 3 ++- Code/Tools/ProjectManager/Source/PythonBindings.cpp | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp index b0b8cca29a..6c61e280f3 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp @@ -120,7 +120,8 @@ namespace O3DE::ProjectManager // Additional information m_versionLabel->setText(tr("Gem Version: %1").arg(m_model->GetVersion(modelIndex))); m_lastUpdatedLabel->setText(tr("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex))); - m_binarySizeLabel->setText(tr("Binary Size: %1 KB").arg(m_model->GetBinarySizeInKB(modelIndex))); + const int binarySize = m_model->GetBinarySizeInKB(modelIndex); + m_binarySizeLabel->setText(tr("Binary Size: %1").arg(binarySize ? tr("%1 KB").arg(binarySize) : tr("Unknown"))); m_mainWidget->adjustSize(); m_mainWidget->show(); diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 905139a4f2..4a73b39ea0 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -53,6 +53,8 @@ namespace Platform #define Py_To_String(obj) pybind11::str(obj).cast().c_str() #define Py_To_String_Optional(dict, key, default_string) dict.contains(key) ? Py_To_String(dict[key]) : default_string +#define Py_To_Int(obj) obj.cast() +#define Py_To_Int_Optional(dict, key, default_int) dict.contains(key) ? Py_To_Int(dict[key]) : default_int #define QString_To_Py_String(value) pybind11::str(value.toStdString()) #define QString_To_Py_Path(value) m_pathlib.attr("Path")(value.toStdString()) @@ -705,7 +707,9 @@ namespace O3DE::ProjectManager // optional gemInfo.m_displayName = Py_To_String_Optional(data, "display_name", gemInfo.m_name); gemInfo.m_summary = Py_To_String_Optional(data, "summary", ""); - gemInfo.m_version = ""; + gemInfo.m_version = Py_To_String_Optional(data, "version", gemInfo.m_version); + gemInfo.m_lastUpdatedDate = Py_To_String_Optional(data, "last_updated", gemInfo.m_lastUpdatedDate); + gemInfo.m_binarySizeInKB = Py_To_Int_Optional(data, "binary_size", gemInfo.m_binarySizeInKB); gemInfo.m_requirement = Py_To_String_Optional(data, "requirements", ""); gemInfo.m_creator = Py_To_String_Optional(data, "origin", ""); gemInfo.m_documentationLink = Py_To_String_Optional(data, "documentation_url", ""); From 97bb01122fd0c02b5ba354ad41ba58dffaee873f Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Mon, 8 Nov 2021 11:01:02 -0600 Subject: [PATCH 116/177] Add missing runtime dependency to atom ly integration Signed-off-by: Guthrie Adams --- Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt index 95331a2f3f..7b685ece13 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt @@ -111,6 +111,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) RUNTIME_DEPENDENCIES Gem::Atom_RPI.Editor Gem::Atom_Feature_Common.Editor + Gem::AtomToolsFramework.Editor Legacy::EditorCommon ) From 16f59809837ed9f1f5cf3a2bf145915c565287f5 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Mon, 8 Nov 2021 09:11:13 -0800 Subject: [PATCH 117/177] Fix Prefab builder test (#5377) We were destructively moving the DOM template into the builder, leaving the Prefab system with an invalid DOM when teardown occurs. For now, this just copies the document to fix this specific test failure, but we may want to consider making `FindTemplateDom` return a const document and requiring that mutations get routed through the prefab system component to avoid similar situations in the future. Signed-off-by: nvsickle --- Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp index d33b453c01..2ed05b4dfe 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp @@ -102,7 +102,9 @@ namespace UnitTest prefabBuilderComponent.Activate(); AZStd::vector jobProducts; - auto&& prefabDom = prefabSystemComponentInterface->FindTemplateDom(parentInstance->GetTemplateId()); + // Make a copy of the template DOM, as the prefab system still owns the existing template + AzToolsFramework::Prefab::PrefabDom prefabDom; + prefabDom.CopyFrom(prefabSystemComponentInterface->FindTemplateDom(parentInstance->GetTemplateId()), prefabDom.GetAllocator(), false); ASSERT_TRUE(prefabBuilderComponent.ProcessPrefab({AZ::Crc32("pc")}, "parent.prefab", "unused", AZ::Uuid(), prefabDom, jobProducts)); From 18847fb3ccc4f65a8c57530f368c1414df20cd6f Mon Sep 17 00:00:00 2001 From: hershey5045 <43485729+hershey5045@users.noreply.github.com> Date: Mon, 8 Nov 2021 11:09:07 -0800 Subject: [PATCH 118/177] Improve handling of cached data limits. (#5232) * Remove size limit on cached profile regions. Signed-off-by: rbarrand <43485729+hershey5045@users.noreply.github.com> * Discard excess profiling data once limit has been reached. Warn users only the first time the limit is reached. Signed-off-by: rbarrand <43485729+hershey5045@users.noreply.github.com> * Fix bug that was clearing cached time regions instead of the cached time regions map. Signed-off-by: rbarrand <43485729+hershey5045@users.noreply.github.com> --- Gems/Profiler/Code/Source/CpuProfilerImpl.cpp | 28 ++++++++++++++++--- Gems/Profiler/Code/Source/CpuProfilerImpl.h | 6 ++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Gems/Profiler/Code/Source/CpuProfilerImpl.cpp b/Gems/Profiler/Code/Source/CpuProfilerImpl.cpp index c88afdecd0..f0d39d48e4 100644 --- a/Gems/Profiler/Code/Source/CpuProfilerImpl.cpp +++ b/Gems/Profiler/Code/Source/CpuProfilerImpl.cpp @@ -282,7 +282,7 @@ namespace Profiler m_stackLevel = 0; m_cachedTimeRegionMap.clear(); m_timeRegionStack.clear(); - m_cachedTimeRegions.clear(); + ResetCachedData(); } timeRegion.m_stackDepth = aznumeric_cast(m_stackLevel); @@ -329,8 +329,21 @@ namespace Profiler { return; } - // Add an entry to the cached region - m_cachedTimeRegions.push_back(timeRegionCached); + // Add an entry to the cached region. Discard excess data in case there is too much to handle. + if (m_cachedTimeRegions.size() < TimeRegionStackSize) + { + m_cachedTimeRegions.push_back(timeRegionCached); + } + // Warn only once per thread if the cached data limit has been reached. + else if (!m_cachedDataLimitReached) + { + AZ_Warning( + "Profiler", false, + "Limit for profiling data has been reached by thread %i. Excess data will be discarded. Considering moving or reducing " + "profiler markers to prevent data loss.", + m_executingThreadId); + m_cachedDataLimitReached = true; + } // If the stack is empty, add it to the local cache map. Only gets called when the stack is empty // NOTE: this is where the largest overhead will be, but due to it only being called when the stack is empty @@ -354,7 +367,7 @@ namespace Profiler } // Clear the cached regions - m_cachedTimeRegions.clear(); + ResetCachedData(); } } @@ -371,10 +384,17 @@ namespace Profiler m_cachedTimeRegionMap.clear(); m_hitSizeLimitMap.clear(); } + m_cachedTimeRegionMutex.unlock(); } } + void CpuTimingLocalStorage::ResetCachedData() + { + m_cachedTimeRegions.clear(); + m_cachedDataLimitReached = false; + } + // --- CpuProfilingStatisticsSerializer --- CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializer(const AZStd::ring_buffer& continuousData) diff --git a/Gems/Profiler/Code/Source/CpuProfilerImpl.h b/Gems/Profiler/Code/Source/CpuProfilerImpl.h index 1046b72cff..6611fbd1e5 100644 --- a/Gems/Profiler/Code/Source/CpuProfilerImpl.h +++ b/Gems/Profiler/Code/Source/CpuProfilerImpl.h @@ -50,6 +50,9 @@ namespace Profiler // Tries to flush the map to the passed parameter, only if the thread's mutex is unlocked void TryFlushCachedMap(CpuProfiler::ThreadTimeRegionMap& cachedRegionMap); + // Clears m_cachedTimeRegions and resets m_cachedDataLimitReached flag. + void ResetCachedData(); + AZStd::thread_id m_executingThreadId; // Keeps track of the current thread's stack depth uint32_t m_stackLevel = 0u; @@ -75,6 +78,9 @@ namespace Profiler // Keep track of the regions that have hit the size limit so we don't have to lock to check AZStd::map m_hitSizeLimitMap; + + // Keeps track of the first time cached data limit was reached. + bool m_cachedDataLimitReached = false; }; //! CpuProfiler will keep track of the registered threads, and From 6242d1ee8e8a08ad8b7a13125292b8a386503f71 Mon Sep 17 00:00:00 2001 From: Sean Masterson Date: Mon, 8 Nov 2021 11:19:30 -0800 Subject: [PATCH 119/177] Added P0 test for Diffuse Probe Grid component Signed-off-by: Sean Masterson --- .../Gem/PythonTests/Atom/TestSuite_Main.py | 4 + .../Atom/atom_utils/atom_constants.py | 2 +- ...mEditorComponents_DiffuseProbeGridAdded.py | 190 ++++++++++++++++++ 3 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py diff --git a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py index 52e7ec993e..d183ca12be 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/TestSuite_Main.py @@ -29,6 +29,10 @@ class TestAutomation(EditorTestSuite): class AtomEditorComponents_DepthOfFieldAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_DepthOfFieldAdded as test_module + @pytest.mark.test_case_id("C36525659") + class AtomEditorComponents_DiffuseProbeGridAdded(EditorSharedTest): + from Atom.tests import hydra_AtomEditorComponents_DiffuseProbeGridAdded as test_module + @pytest.mark.test_case_id("C32078120") class AtomEditorComponents_DirectionalLightAdded(EditorSharedTest): from Atom.tests import hydra_AtomEditorComponents_DirectionalLightAdded as test_module diff --git a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py index 70156b9375..817ff1fad0 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/atom_utils/atom_constants.py @@ -115,7 +115,7 @@ class AtomComponentProperties: return properties[property] @staticmethod - def diffuse_probe(property: str = 'name') -> str: + def diffuse_probe_grid(property: str = 'name') -> str: """ Diffuse Probe Grid component properties. Requires one of 'shapes'. - 'shapes' a list of supported shapes as component names. diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py new file mode 100644 index 0000000000..9d2a8004f9 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py @@ -0,0 +1,190 @@ +""" +Copyright (c) Contributors to the Open 3D Engine Project. +For complete copyright and license terms please see the LICENSE at the root of this distribution. + +SPDX-License-Identifier: Apache-2.0 OR MIT +""" + + +class Tests: + creation_undo = ( + "UNDO Entity creation success", + "UNDO Entity creation failed") + creation_redo = ( + "REDO Entity creation success", + "REDO Entity creation failed") + diffuse_probe_grid_creation = ( + "Diffuse Probe Grid Entity successfully created", + "Diffuse Probe Grid Entity failed to be created") + diffuse_probe_grid_component = ( + "Entity has a Diffuse Probe Grid component", + "Entity failed to find Diffuse Probe Grid component") + diffuse_probe_grid_disabled = ( + "Diffuse Probe Grid component disabled", + "Diffuse Probe Grid component was not disabled") + box_shape_component = ( + "Entity has a Box Shape component", + "Entity did not have a Box Shape component") + diffuse_probe_grid_enabled = ( + "Diffuse Probe Grid component enabled", + "Diffuse Probe Grid component was not enabled") + enter_game_mode = ( + "Entered game mode", + "Failed to enter game mode") + exit_game_mode = ( + "Exited game mode", + "Couldn't exit game mode") + is_visible = ( + "Entity is visible", + "Entity was not visible") + is_hidden = ( + "Entity is hidden", + "Entity was not hidden") + entity_deleted = ( + "Entity deleted", + "Entity was not deleted") + deletion_undo = ( + "UNDO deletion success", + "UNDO deletion failed") + deletion_redo = ( + "REDO deletion success", + "REDO deletion failed") + + +def AtomEditorComponents_DiffuseProbeGrid_AddedToEntity(): + """ + Summary: + Tests the Diffuse Probe Grid component can be added to an entity and has the expected functionality. + + Test setup: + - Wait for Editor idle loop. + - Open the "Base" level. + + Expected Behavior: + The component can be added, used in game mode, hidden/shown, deleted, and has accurate required components. + Creation and deletion undo/redo should also work. + + Test Steps: + 1) Create a Diffuse Probe Grid entity with no components. + 2) Add a Diffuse Probe Grid component to Diffuse Probe Grid entity. + 3) UNDO the entity creation and component addition. + 4) REDO the entity creation and component addition. + 5) Verify Diffuse Probe Grid component not enabled. + 6) Add Shape component since it is required by the Diffuse Probe Grid component. + 7) Verify Diffuse Probe Grid component is enabled. + 8) Enter/Exit game mode. + 9) Test IsHidden. + 10) Test IsVisible. + 11) Delete Diffuse Probe Grid entity. + 12) UNDO deletion. + 13) REDO deletion. + 14) Look for errors. + + :return: None + """ + + import azlmbr.legacy.general as general + import azlmbr.render as render + + from editor_python_test_tools.editor_entity_utils import EditorEntity + from editor_python_test_tools.utils import Report, Tracer, TestHelper + from Atom.atom_utils.atom_constants import AtomComponentProperties + + with Tracer() as error_tracer: + # Test setup begins. + # Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level. + TestHelper.init_idle() + TestHelper.open_level("", "Base") + + # Test steps begin. + # 1. Create a Diffuse Probe Grid entity with no components. + diffuse_probe_grid_entity = EditorEntity.create_editor_entity(AtomComponentProperties.diffuse_probe_grid()) + Report.critical_result(Tests.diffuse_probe_grid_creation, diffuse_probe_grid_entity.exists()) + + # 2. Add a Diffuse Probe Grid component to Diffuse Probe Grid entity. + diffuse_probe_grid_component = diffuse_probe_grid_entity.add_component(AtomComponentProperties.diffuse_probe_grid()) + Report.critical_result( + Tests.diffuse_probe_grid_component, + diffuse_probe_grid_entity.has_component(AtomComponentProperties.diffuse_probe_grid())) + + # 3. UNDO the entity creation and component addition. + # -> UNDO component addition. + general.undo() + # -> UNDO naming entity. + general.undo() + # -> UNDO selecting entity. + general.undo() + # -> UNDO entity creation. + general.undo() + general.idle_wait_frames(1) + Report.result(Tests.creation_undo, not diffuse_probe_grid_entity.exists()) + + # 4. REDO the entity creation and component addition. + # -> REDO entity creation. + general.redo() + # -> REDO selecting entity. + general.redo() + # -> REDO naming entity. + general.redo() + # -> REDO component addition. + general.redo() + general.idle_wait_frames(1) + Report.result(Tests.creation_redo, diffuse_probe_grid_entity.exists()) + + # 5. Verify Diffuse Probe Grid component not enabled. + Report.result(Tests.diffuse_probe_grid_disabled, not diffuse_probe_grid_component.is_enabled()) + + # 6. Add Shape component since it is required by the Diffuse Probe Grid component. + for shape in AtomComponentProperties.diffuse_probe_grid('shapes'): + diffuse_probe_grid_entity.add_component(shape) + test_shape = ( + f"Entity has a {shape} component", + f"Entity did not have a {shape} component") + Report.result(test_shape, diffuse_probe_grid_entity.has_component(shape)) + + # 7. Check if required shape allows Diffuse Probe Grid to be enabled + Report.result(Tests.diffuse_probe_grid_enabled, diffuse_probe_grid_component.is_enabled()) + + # Undo to remove each added shape except the last one and verify Diffuse Probe Grid is not enabled. + if not (shape == AtomComponentProperties.diffuse_probe_grid('shapes')[-1]): + general.undo() + TestHelper.wait_for_condition(lambda: not diffuse_probe_grid_entity.has_component(shape), 1.0) + Report.result(Tests.diffuse_probe_grid_disabled, not diffuse_probe_grid_component.is_enabled()) + + # 8. Enter/Exit game mode. + TestHelper.enter_game_mode(Tests.enter_game_mode) + general.idle_wait_frames(1) + TestHelper.exit_game_mode(Tests.exit_game_mode) + + # 9. Test IsHidden. + diffuse_probe_grid_entity.set_visibility_state(False) + Report.result(Tests.is_hidden, diffuse_probe_grid_entity.is_hidden() is True) + + # 10. Test IsVisible. + diffuse_probe_grid_entity.set_visibility_state(True) + general.idle_wait_frames(1) + Report.result(Tests.is_visible, diffuse_probe_grid_entity.is_visible() is True) + + # 11. Delete Diffuse Probe Grid entity. + diffuse_probe_grid_entity.delete() + Report.result(Tests.entity_deleted, not diffuse_probe_grid_entity.exists()) + + # 12. UNDO deletion. + general.undo() + Report.result(Tests.deletion_undo, diffuse_probe_grid_entity.exists()) + + # 13. REDO deletion. + general.redo() + Report.result(Tests.deletion_redo, not diffuse_probe_grid_entity.exists()) + + # 14. Look for errors or asserts. + TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_tracer.asserts: + Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}") + + +if __name__ == "__main__": + from editor_python_test_tools.utils import Report + Report.start_test(AtomEditorComponents_DiffuseProbeGrid_AddedToEntity) From cd508415ef6a478d71c0a93fd02b763f119d7f1d Mon Sep 17 00:00:00 2001 From: Sean Masterson Date: Mon, 8 Nov 2021 11:57:21 -0800 Subject: [PATCH 120/177] Removed unused tuple. Signed-off-by: Sean Masterson --- .../tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py index 9d2a8004f9..6a7989fe1d 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py @@ -22,9 +22,6 @@ class Tests: diffuse_probe_grid_disabled = ( "Diffuse Probe Grid component disabled", "Diffuse Probe Grid component was not disabled") - box_shape_component = ( - "Entity has a Box Shape component", - "Entity did not have a Box Shape component") diffuse_probe_grid_enabled = ( "Diffuse Probe Grid component enabled", "Diffuse Probe Grid component was not enabled") From c9f9a83c57af7c464e6e7d2b3fef1183aac1616e Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Mon, 8 Nov 2021 12:00:15 -0800 Subject: [PATCH 121/177] Further PR feedback on the Spawnble Entity Aliases. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../Spawnable/SpawnableEntitiesManager.cpp | 2 +- .../SpawnableEntitiesManagerTests.cpp | 12 +- .../Prefab/Spawnable/SpawnableUtils.cpp | 118 +++++++++++------- .../Prefab/Spawnable/SpawnableUtils.h | 6 +- 4 files changed, 80 insertions(+), 58 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 17a18dd5e5..1e863877a4 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -944,7 +944,7 @@ namespace AzFramework { for (AZ::Entity* entity : request.m_ticket->m_spawnedEntities) { - if (entity != nullptr && !entity->GetComponents().empty()) + if (entity != nullptr) { // Setting it to 0 is needed to avoid the infinite loop between GameEntityContext and SpawnableEntitiesManager. entity->SetSpawnTicketId(0); diff --git a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp index 535ceab30a..0dc00f81dd 100644 --- a/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp +++ b/Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp @@ -607,7 +607,7 @@ namespace UnitTest &target); size_t spawnedEntitiesCount = 0; - bool allReplaced = true; + bool allReplaced = false; auto callback = [&spawnedEntitiesCount, &allReplaced]( AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) { @@ -636,7 +636,7 @@ namespace UnitTest &target); size_t spawnedEntitiesCount = 0; - bool allAdded = true; + bool allAdded = false; auto callback = [&spawnedEntitiesCount, &allAdded]( AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) { @@ -665,7 +665,7 @@ namespace UnitTest &target); size_t spawnedEntitiesCount = 0; - bool allMerged = true; + bool allMerged = false; auto callback = [&spawnedEntitiesCount, &allMerged]( AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) { @@ -1105,7 +1105,7 @@ namespace UnitTest AZStd::vector indices = { 0, 2, 3, 1 }; size_t spawnedEntitiesCount = 0; - bool allReplaced = true; + bool allReplaced = false; auto callback = [&spawnedEntitiesCount, &allReplaced]( AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) { @@ -1136,7 +1136,7 @@ namespace UnitTest AZStd::vector indices = { 0, 2, 3, 1 }; size_t spawnedEntitiesCount = 0; - bool allAdded = true; + bool allAdded = false; auto callback = [&spawnedEntitiesCount, &allAdded]( AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) @@ -1168,7 +1168,7 @@ namespace UnitTest AZStd::vector indices = { 0, 2, 3, 1 }; size_t spawnedEntitiesCount = 0; - bool allMerged = true; + bool allMerged = false; auto callback = [&spawnedEntitiesCount, &allMerged]( AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp index dfe82167c2..ab3c52f6d0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp @@ -170,7 +170,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils AzToolsFramework::Prefab::Instance& source, AZStd::string targetPrefabName, AzToolsFramework::Prefab::Instance& target, - AZ::EntityId entity, + AZ::EntityId entityId, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, uint32_t tag, @@ -178,28 +178,35 @@ namespace AzToolsFramework::Prefab::SpawnableUtils { using namespace AzToolsFramework::Prefab::PrefabConversionUtils; - AliasPath alias = source.GetAliasPathRelativeToInstance(entity); + AliasPath alias = source.GetAliasPathRelativeToInstance(entityId); if (!alias.empty()) { - auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entity, aliasType); + auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entityId, aliasType); + if (replacement) + { + AZ::Entity* result = replacement.get(); + target.AddEntity(AZStd::move(replacement), alias.Filename().Native()); - AZ::Entity* result = replacement.get(); - target.AddEntity(AZStd::move(replacement), alias.Filename().Native()); + EntityAliasStore store; + store.m_aliasType = storedAliasType; + store.m_source.emplace(AZStd::move(sourcePrefabName), AZStd::move(alias)); + store.m_target.emplace( + AZStd::move(targetPrefabName), target.GetAliasPathRelativeToInstance(result->GetId())); + store.m_loadBehavior = loadBehavior; + store.m_tag = tag; + context.RegisterSpawnableEntityAlias(AZStd::move(store)); - EntityAliasStore store; - store.m_aliasType = storedAliasType; - store.m_source.emplace(AZStd::move(sourcePrefabName), AZStd::move(alias)); - store.m_target.emplace( - AZStd::move(targetPrefabName), target.GetAliasPathRelativeToInstance(result->GetId())); - store.m_loadBehavior = loadBehavior; - store.m_tag = tag; - context.RegisterSpawnableEntityAlias(AZStd::move(store)); - - return result; + return result; + } + else + { + AZ_Assert(false, "A replacement for entity with id %zu could not be created.", static_cast(entityId)); + return nullptr; + } } else { - AZ_Assert(false, "Entity with id %zu was not found in the source prefab.", static_cast(entity)); + AZ_Assert(false, "Entity with id %zu was not found in the source prefab.", static_cast(entityId)); return nullptr; } } @@ -208,7 +215,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils AZStd::string sourcePrefabName, AzToolsFramework::Prefab::Instance& source, AzFramework::Spawnable& target, - AZ::EntityId entity, + AZ::EntityId entityId, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, uint32_t tag, @@ -216,17 +223,58 @@ namespace AzToolsFramework::Prefab::SpawnableUtils { using namespace AzToolsFramework::Prefab::PrefabConversionUtils; - AliasPath alias = source.GetAliasPathRelativeToInstance(entity); + AliasPath alias = source.GetAliasPathRelativeToInstance(entityId); if (!alias.empty()) { - auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entity, aliasType); + auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entityId, aliasType); + if (replacement) + { + AZ::Entity* result = replacement.get(); + target.GetEntities().push_back(AZStd::move(replacement)); + EntityAliasStore store; + store.m_aliasType = storedAliasType; + store.m_source.emplace(AZStd::move(sourcePrefabName), AZStd::move(alias)); + store.m_target.emplace(target, result->GetId()); + store.m_tag = tag; + store.m_loadBehavior = loadBehavior; + context.RegisterSpawnableEntityAlias(AZStd::move(store)); + + return result; + } + else + { + AZ_Assert(false, "A replacement for entity with id %zu could not be created.", static_cast(entityId)); + return nullptr; + } + } + else + { + AZ_Assert(false, "Entity with id %llu was not found in the source prefab.", static_cast(entityId)); + return nullptr; + } + } + + AZ::Entity* CreateEntityAlias( + AzFramework::Spawnable& source, + AzFramework::Spawnable& target, + AZ::EntityId entityId, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, + uint32_t tag, + AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context) + { + using namespace AzToolsFramework::Prefab::PrefabConversionUtils; + + auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entityId, aliasType); + if (replacement) + { AZ::Entity* result = replacement.get(); target.GetEntities().push_back(AZStd::move(replacement)); - + EntityAliasStore store; store.m_aliasType = storedAliasType; - store.m_source.emplace(AZStd::move(sourcePrefabName), AZStd::move(alias)); + store.m_source.emplace(source, entityId); store.m_target.emplace(target, result->GetId()); store.m_tag = tag; store.m_loadBehavior = loadBehavior; @@ -236,37 +284,11 @@ namespace AzToolsFramework::Prefab::SpawnableUtils } else { - AZ_Assert(false, "Entity with id %llu was not found in the source prefab.", static_cast(entity)); + AZ_Assert(false, "A replacement for entity with id %zu could not be created.", static_cast(entityId)); return nullptr; } } - AZ::Entity* CreateEntityAlias( - AzFramework::Spawnable& source, - AzFramework::Spawnable& target, - AZ::EntityId entity, - AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, - AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, - uint32_t tag, - AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context) - { - using namespace AzToolsFramework::Prefab::PrefabConversionUtils; - - auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entity, aliasType); - AZ::Entity* result = replacement.get(); - target.GetEntities().push_back(AZStd::move(replacement)); - - EntityAliasStore store; - store.m_aliasType = storedAliasType; - store.m_source.emplace(source, entity); - store.m_target.emplace(target, result->GetId()); - store.m_tag = tag; - store.m_loadBehavior = loadBehavior; - context.RegisterSpawnableEntityAlias(AZStd::move(store)); - - return result; - } - uint32_t FindEntityIndex(AZ::EntityId entity, const AzFramework::Spawnable& spawnable) { auto begin = spawnable.GetEntities().begin(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h index 892b83455d..ea8a49857e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h @@ -36,7 +36,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils AzToolsFramework::Prefab::Instance& source, AZStd::string targetPrefabName, AzToolsFramework::Prefab::Instance& target, - AZ::EntityId entity, + AZ::EntityId entityId, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, uint32_t tag, @@ -45,7 +45,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils AZStd::string sourcePrefabName, AzToolsFramework::Prefab::Instance& source, AzFramework::Spawnable& target, - AZ::EntityId entity, + AZ::EntityId entityId, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, uint32_t tag, @@ -53,7 +53,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils AZ::Entity* CreateEntityAlias( AzFramework::Spawnable& source, AzFramework::Spawnable& target, - AZ::EntityId entity, + AZ::EntityId entityId, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, uint32_t tag, From d1805dbd1c79e4fa2fc369574f03ff2d20a8001a Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Tue, 2 Nov 2021 13:05:54 -0700 Subject: [PATCH 122/177] [android_compat_fixes] fixed Android NDK r23 duplicate __ANDROID_API__ define Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- cmake/Platform/Android/Configurations_android.cmake | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmake/Platform/Android/Configurations_android.cmake b/cmake/Platform/Android/Configurations_android.cmake index 69c481b50a..239d13e982 100644 --- a/cmake/Platform/Android/Configurations_android.cmake +++ b/cmake/Platform/Android/Configurations_android.cmake @@ -12,6 +12,11 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") include(cmake/Platform/Common/Configurations_common.cmake) include(cmake/Platform/Common/Clang/Configurations_clang.cmake) + set(_android_api_define) + if(${LY_TOOLCHAIN_NDK_PKG_MAJOR} VERSION_LESS "23") + set(_android_api_define __ANDROID_API__=${LY_TOOLCHAIN_NDK_API_LEVEL}) + endif() + ly_append_configurations_options( DEFINES LINUX64 @@ -22,9 +27,9 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") MOBILE _HAS_C9X ENABLE_TYPE_INFO - __ANDROID_API__=${LY_TOOLCHAIN_NDK_API_LEVEL} NDK_REV_MAJOR=${LY_TOOLCHAIN_NDK_PKG_MAJOR} NDK_REV_MINOR=${LY_TOOLCHAIN_NDK_PKG_MINOR} + ${_android_api_define} COMPILATION -femulated-tls # All accesses to TLS variables are converted to calls to __emutls_get_address in the runtime library From e532193459a6dd48362509be87d6df761610e9b8 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Tue, 2 Nov 2021 13:05:54 -0700 Subject: [PATCH 123/177] [android_compat_fixes] added support for versioned Android 'cmdline-tools' (the 'tools' replacement) Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- .../Tools/Platform/Android/android_support.py | 19 +++++++++++++++++-- .../Android/generate_android_project.py | 9 ++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/cmake/Tools/Platform/Android/android_support.py b/cmake/Tools/Platform/Android/android_support.py index 54f1a4c8f4..de33b59907 100755 --- a/cmake/Tools/Platform/Android/android_support.py +++ b/cmake/Tools/Platform/Android/android_support.py @@ -1548,17 +1548,32 @@ class AndroidSDKResolver(object): self.version = LooseVersion(available_update_components[1]) self.available = available_update_components[2] - def __init__(self, android_sdk_path): + def __init__(self, android_sdk_path, command_line_tools_version): self.android_sdk_path = android_sdk_path or os.environ.get(ANDROID_SDK_ENV_NAME) if not self.android_sdk_path: raise common.LmbrCmdError(f"Android SDK path not set or it was not passed into the command to generate the android project") if not os.path.isdir(self.android_sdk_path): raise common.LmbrCmdError(f"Android SDK path {self.android_sdk_path} is not valid") + + sdk_root = pathlib.Path(self.android_sdk_path) + + tools_path = sdk_root / 'cmdline-tools' + if tools_path.exists(): + tools_path = tools_path / command_line_tools_version + if not tools_path.exists(): + raise common.LmbrCmdError(f"The desired version of the Android 'cmdline-tools' ({command_line_tools_version}) is not detected") + else: + tools_path = sdk_root / 'tools' + + ext = '' if platform.system() == 'Windows': - self.sdk_manager_path = pathlib.Path(self.android_sdk_path) / 'tools' / 'bin' / 'sdkmanager.bat' + ext = '.bat' else: raise common.LmbrCmdError(f"This tool is not supported on the current platform {platform.system()}") + + self.sdk_manager_path = tools_path / 'bin' / f'sdkmanager{ext}' + if not self.sdk_manager_path.is_file(): raise common.LmbrCmdError(f"Android SDK path {self.android_sdk_path} is not valid or complete. Missing {self.sdk_manager_path}") diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index d8f1021590..572ad1cd37 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -102,6 +102,7 @@ def build_optional_signing_profile(store_file, store_password, key_alias, key_pa ANDROID_SDK_ARGUMENT_NAME = '--android-sdk-path' ANDROID_SDK_PLATFORM_ARGUMENT_NAME = '--android-sdk-platform' ANDROID_SDK_PREFERRED_TOOL_VER = '--android-sdk-build-tool-version' +ANDROID_SDK_COMMAND_LINE_TOOLS_VER = '--android-sdk-command-line-tools-version' ANDROID_NATIVE_API_LEVEL = '--android-native-api-level' @@ -185,6 +186,11 @@ def main(args): default=-1) # Override arguments + parser.add_argument(ANDROID_SDK_COMMAND_LINE_TOOLS_VER, + default='latest', + help='The android SDK command line tools version.', + required=False) + parser.add_argument(ANDROID_SDK_PREFERRED_TOOL_VER, help='The android SDK build tools version.', required=False) @@ -304,7 +310,8 @@ def main(args): f"({android_gradle_plugin_version}).") # Use the SDK Resolver to make sure the build tools and ndk - android_sdk = android_support.AndroidSDKResolver(android_sdk_path=parsed_args.get_argument(ANDROID_SDK_ARGUMENT_NAME)) + android_sdk = android_support.AndroidSDKResolver(android_sdk_path=parsed_args.get_argument(ANDROID_SDK_ARGUMENT_NAME), + command_line_tools_version=parsed_args.get_argument(ANDROID_SDK_COMMAND_LINE_TOOLS_VER)) # If no SDK platform is provided, check for any installed one if android_sdk_platform_version < 0: From 98dd24dce981c2b3c3d362cf96973c58e39b422c Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Tue, 2 Nov 2021 13:05:54 -0700 Subject: [PATCH 124/177] [android_compat_fixes] updated max Gradle version to latest point release of 7.0 to fix volume query bug during sync in some configurations Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- cmake/Tools/Platform/Android/generate_android_project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index 572ad1cd37..efe7096c6a 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -25,7 +25,7 @@ from cmake.Tools.Platform.Android import android_support GRADLE_ARGUMENT_NAME = '--gradle-install-path' GRADLE_MIN_VERSION = LooseVersion('6.5') -GRADLE_MAX_VERSION = LooseVersion('7.0.0') +GRADLE_MAX_VERSION = LooseVersion('7.0.2') GRADLE_VERSION_REGEX = re.compile(r"Gradle\s(\d+.\d+.?\d*)") GRADLE_EXECUTABLE = 'gradle.bat' if platform.system() == 'Windows' else 'gradle' From 255bf7cfbadd8113c5bea54ce9b60897a0d1a721 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Tue, 2 Nov 2021 13:05:54 -0700 Subject: [PATCH 125/177] [android_compat_fixes] updated min Android Gradle plugin version to latest point release of 4.2 Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- cmake/Tools/Platform/Android/android_support.py | 2 +- cmake/Tools/Platform/Android/generate_android_project.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/Tools/Platform/Android/android_support.py b/cmake/Tools/Platform/Android/android_support.py index de33b59907..a16c11f647 100755 --- a/cmake/Tools/Platform/Android/android_support.py +++ b/cmake/Tools/Platform/Android/android_support.py @@ -33,7 +33,7 @@ from cmake.Tools import common ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP = { - '4.2.0': {'min_gradle_version': '6.7.1', + '4.2.2': {'min_gradle_version': '6.7.1', 'sdk_build': '30.0.2', 'default_ndk': '21.4.7075529', 'min_cmake_version': '3.20'} diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index efe7096c6a..0ab213cfc5 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -114,7 +114,7 @@ MIN_NATIVE_API_LEVEL = 24 # The minimum Native API level that is supported ANDROID_NDK_PLATFORM_ARGUMENT_NAME = '--android-ndk-version' ANDROID_GRADLE_PLUGIN_ARGUMENT_NAME = '--gradle-plugin-version' -ANDROID_GRADLE_MIN_PLUGIN_VERSION = LooseVersion("4.2.0") +ANDROID_GRADLE_MIN_PLUGIN_VERSION = LooseVersion("4.2.2") # Constants for asset-related options for APK generation INCLUDE_APK_ASSETS_ARGUMENT_NAME = "--include-apk-assets" From ea52fc93efc6224648878a367c61db42a78dffc2 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Tue, 2 Nov 2021 13:05:54 -0700 Subject: [PATCH 126/177] [android_compat_fixes] fixed issue with Gradle task chaining for some custom copy tasks Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- cmake/Tools/Platform/Android/android_support.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/Tools/Platform/Android/android_support.py b/cmake/Tools/Platform/Android/android_support.py index a16c11f647..1495aa281a 100755 --- a/cmake/Tools/Platform/Android/android_support.py +++ b/cmake/Tools/Platform/Android/android_support.py @@ -358,7 +358,7 @@ CUSTOM_GRADLE_COPY_NATIVE_CONFIG_FORMAT_STR = """ into 'outputs/native-lib/{abi}' }} - compile{config}Sources.dependsOn copyNativeLibs{config} + merge{config}JniLibFolders.dependsOn copyNativeLibs{config} copyNativeLibs{config}.mustRunAfter {{ tasks.findAll {{ task->task.name.contains('externalNativeBuild{config}') }} @@ -388,7 +388,7 @@ CUSTOM_GRADLE_COPY_REGISTRY_FOLDER_FORMAT_STR = """ include ('*.setreg') }} - compile{config}Sources.dependsOn copyRegistryFolder{config} + merge{config}Assets.dependsOn copyRegistryFolder{config} """ CUSTOM_GRADLE_COPY_REGISTRY_FOLDER_DEPENDENCY_FORMAT_STR = """ From c5cd7f7fb8ca03e2ae7f92dcf051d9d06de8aa7f Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Tue, 2 Nov 2021 13:05:54 -0700 Subject: [PATCH 127/177] [android_compat_fixes] replaced deprecated 'jcenter' repo with 'mavenCentral' in Android project generator template Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- Code/Tools/Android/ProjectBuilder/root.build.gradle.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Tools/Android/ProjectBuilder/root.build.gradle.in b/Code/Tools/Android/ProjectBuilder/root.build.gradle.in index 14606bee7b..c0102c9d21 100644 --- a/Code/Tools/Android/ProjectBuilder/root.build.gradle.in +++ b/Code/Tools/Android/ProjectBuilder/root.build.gradle.in @@ -8,7 +8,7 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:${ANDROID_GRADLE_PLUGIN_VERSION}' @@ -21,7 +21,7 @@ buildscript { allprojects { repositories { google() - jcenter() + mavenCentral() } } From 8b9dfe022ddc4af7f63ba63f63917b50b9f366f0 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Tue, 2 Nov 2021 13:05:54 -0700 Subject: [PATCH 128/177] [android_compat_fixes] increased debug logging around adb calls in Android deployment script Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- cmake/Tools/Platform/Android/android_deployment.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmake/Tools/Platform/Android/android_deployment.py b/cmake/Tools/Platform/Android/android_deployment.py index f1a9fe92fb..91662eecaa 100755 --- a/cmake/Tools/Platform/Android/android_deployment.py +++ b/cmake/Tools/Platform/Android/android_deployment.py @@ -180,16 +180,20 @@ class AndroidDeployment(object): call_arguments.extend(['-s', device_id]) call_arguments.extend(arg_list) + logging.debug(f"adb command: {subprocess.list2cmdline(call_arguments)}") try: output = subprocess.check_output(call_arguments, shell=True, stderr=subprocess.PIPE).decode(common.DEFAULT_TEXT_READ_ENCODING, common.ENCODING_ERROR_HANDLINGS) + logging.debug(f"adb output:\n{output}") return output except subprocess.CalledProcessError as err: - raise common.LmbrCmdError(err.stderr.decode(common.DEFAULT_TEXT_READ_ENCODING, - common.ENCODING_ERROR_HANDLINGS)) + std_out = err.stdout.decode(common.DEFAULT_TEXT_READ_ENCODING, common.ENCODING_ERROR_HANDLINGS) + std_err = err.stderr.decode(common.DEFAULT_TEXT_READ_ENCODING, common.ENCODING_ERROR_HANDLINGS) + logging.debug(f"adb returned non-zero.\noutput:\n{std_out}\nerror:\n{std_err}\n") + raise common.LmbrCmdError(std_err) def adb_shell(self, command, device_id): """ @@ -224,19 +228,15 @@ class AndroidDeployment(object): shell_command.append(path) - logging.debug(f"Testing {device_id}: ls {' '.join(shell_command)}") raw_output = self.adb_shell(command=' '.join(shell_command), device_id=device_id) if not raw_output: - logging.debug('adb_ls: No output given') return False, None if raw_output is None or any([error for error in error_messages if error in raw_output]): - logging.debug('adb_ls: Error message found') status = False else: - logging.debug('adb_ls: Command was successful') status = True return status, raw_output From f68b550ca984df13e6170c4f676ef741aff21b74 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Tue, 2 Nov 2021 13:05:54 -0700 Subject: [PATCH 129/177] [android_compat_fixes] replaced a couple try/except cases that were looking for the wrong exception when invoking adb_* functions Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- cmake/Tools/Platform/Android/android_deployment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/Tools/Platform/Android/android_deployment.py b/cmake/Tools/Platform/Android/android_deployment.py index 91662eecaa..1d02a086dc 100755 --- a/cmake/Tools/Platform/Android/android_deployment.py +++ b/cmake/Tools/Platform/Android/android_deployment.py @@ -347,7 +347,7 @@ class AndroidDeployment(object): try: timestamp_string = self.adb_shell(command=f'cat {remote_file_path}', device_id=device_id).strip() - except (subprocess.CalledProcessError, AttributeError): + except (common.LmbrCmdError, AttributeError): return None if not timestamp_string: @@ -463,7 +463,7 @@ class AndroidDeployment(object): try: self.adb_call(arg_list=['push', str(path_to_deploy), target_path], device_id=target_device) - except subprocess.CalledProcessError as err: + except common.LmbrCmdError as err: # Something went wrong, clean up before leaving self.adb_shell(command=f'rm -rf {output_target}', device_id=target_device) From 27d354b1f363c2bb6eb57b642aaad9affa90c7a3 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Tue, 2 Nov 2021 13:05:54 -0700 Subject: [PATCH 130/177] [android_compat_fixes] fixed issue where asset cached was getting deleted when regenerating an existing Android Gradle project with --overwrite-existing Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- cmake/Tools/Platform/Android/android_support.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/Tools/Platform/Android/android_support.py b/cmake/Tools/Platform/Android/android_support.py index 1495aa281a..97aae7bf21 100755 --- a/cmake/Tools/Platform/Android/android_support.py +++ b/cmake/Tools/Platform/Android/android_support.py @@ -30,6 +30,7 @@ if ROOT_DEV_PATH not in sys.path: sys.path.append(ROOT_DEV_PATH) from cmake.Tools import common +from cmake.Tools.layout_tool import remove_link ANDROID_GRADLE_PLUGIN_COMPATIBILITY_MAP = { @@ -767,6 +768,8 @@ class AndroidProjectGenerator(object): # We must always delete 'src' any existing copied AzAndroid projects since building may pick up stale java sources lumberyard_app_src = az_android_dst_path / 'src' if lumberyard_app_src.exists(): + # special case the 'assets' directory before cleaning the whole directory tree + remove_link(lumberyard_app_src / 'main' / 'assets') common.remove_dir_path(lumberyard_app_src) logging.debug("Copying AzAndroid to '%s'", az_android_dst_path.resolve()) From aabbf51d3a991288febf651d81130c3edb060e48 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Mon, 8 Nov 2021 11:01:56 -0800 Subject: [PATCH 131/177] [android_compat_fixes] added support for non-NDK distributed Vulkan validation layer library paths (required for Android NDK r23+) Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- .../Platform/Android/VkValidation_android.cmake | 10 +++++++++- cmake/Tools/Platform/Android/android_support.py | 9 ++++++++- .../Tools/Platform/Android/generate_android_project.py | 8 +++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/cmake/3rdParty/Platform/Android/VkValidation_android.cmake b/cmake/3rdParty/Platform/Android/VkValidation_android.cmake index dc1c918aad..dbc6db88a1 100644 --- a/cmake/3rdParty/Platform/Android/VkValidation_android.cmake +++ b/cmake/3rdParty/Platform/Android/VkValidation_android.cmake @@ -6,4 +6,12 @@ # # -set(VKVALIDATION_RUNTIME_DEPENDENCIES $<$>:${LY_NDK_DIR}/sources/third_party/vulkan/src/build-android/jniLibs/arm64-v8a/libVkLayer_khronos_validation.so>) +set(LY_ANDROID_VULKAN_VALIDATION_PATH "${LY_NDK_DIR}/sources/third_party/vulkan/src/build-android/jniLibs" CACHE PATH "Path to the Vulkan Validation Layers libs for Android") + +if(NOT EXISTS ${LY_ANDROID_VULKAN_VALIDATION_PATH}) + message(FATAL_ERROR + "Unable to locate the Android Vulkan validation layer libs at ${LY_ANDROID_VULKAN_VALIDATION_PATH}. " + "If using NDK r23 or above, these libs are distributed separately via https://github.com/KhronosGroup/Vulkan-ValidationLayers") +endif() + +set(VKVALIDATION_RUNTIME_DEPENDENCIES $<$>:${LY_ANDROID_VULKAN_VALIDATION_PATH}/arm64-v8a/libVkLayer_khronos_validation.so>) diff --git a/cmake/Tools/Platform/Android/android_support.py b/cmake/Tools/Platform/Android/android_support.py index 97aae7bf21..054897262c 100755 --- a/cmake/Tools/Platform/Android/android_support.py +++ b/cmake/Tools/Platform/Android/android_support.py @@ -471,7 +471,7 @@ class AndroidProjectGenerator(object): def __init__(self, engine_root, build_dir, android_sdk_path, build_tool, android_sdk_platform, android_native_api_level, android_ndk, project_path, third_party_path, cmake_version, override_cmake_path, override_gradle_path, gradle_version, gradle_plugin_version, - override_ninja_path, include_assets_in_apk, asset_mode, asset_type, signing_config, native_build_path, is_test_project=False, + override_ninja_path, include_assets_in_apk, asset_mode, asset_type, signing_config, native_build_path, vulkan_validation_path, is_test_project=False, overwrite_existing=True, unity_build_enabled=False): """ Initialize the object with all the required parameters needed to create an Android Project. The parameters should be verified before initializing this object @@ -495,6 +495,8 @@ class AndroidProjectGenerator(object): :param asset_mode: :param asset_type: :param signing_config: Optional signing configuration arguments + :param native_build_path: Override the native build staging path in gradle + :param vulkan_validation_path: Override the path to where the Vulkan Validation Layers libraries are (required when using NDK r23+) :param is_test_project: Flag to indicate if this is a unit test runner project. (If true, project_path, asset_mode, asset_type, and include_assets_in_apk are ignored) :param overwrite_existing: Flag to overwrite existing project files when being generated, or skip if they already exist. """ @@ -535,6 +537,8 @@ class AndroidProjectGenerator(object): self.native_build_path = native_build_path + self.vulkan_validation_path = vulkan_validation_path + self.asset_mode = asset_mode self.asset_type = asset_type @@ -820,6 +824,9 @@ class AndroidProjectGenerator(object): f'"-DLY_3RDPARTY_PATH={template_third_party_path}"', f'"-DLY_UNITY_BUILD={template_unity_build}"'] + if self.vulkan_validation_path: + cmake_argument_list.append(f'"-DLY_ANDROID_VULKAN_VALIDATION_PATH={pathlib.PurePath(self.vulkan_validation_path).as_posix()}"') + if not self.is_test_project: cmake_argument_list.append(f'"-DLY_PROJECTS={pathlib.PurePath(self.project_path).as_posix()}"') else: diff --git a/cmake/Tools/Platform/Android/generate_android_project.py b/cmake/Tools/Platform/Android/generate_android_project.py index 0ab213cfc5..fe0d787d38 100755 --- a/cmake/Tools/Platform/Android/generate_android_project.py +++ b/cmake/Tools/Platform/Android/generate_android_project.py @@ -223,6 +223,11 @@ def main(args): default=None, required=False) + parser.add_argument('--vulkan-validation-path', + help='Override path to where the Vulkan Validation Layers libraries are. Required for use with NDK r23+', + default=None, + required=False) + # Asset Options parser.add_argument(INCLUDE_APK_ASSETS_ARGUMENT_NAME, action='store_true', @@ -409,7 +414,8 @@ def main(args): is_test_project=is_test_project, overwrite_existing=parsed_args.overwrite_existing, unity_build_enabled=parsed_args.enable_unity_build, - native_build_path=parsed_args.native_build_path) + native_build_path=parsed_args.native_build_path, + vulkan_validation_path=parsed_args.vulkan_validation_path) generator.execute() From c0dd9ac26bc2973b8243d102d9de9ef8a00ff42b Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Mon, 8 Nov 2021 12:25:10 -0800 Subject: [PATCH 132/177] [android_compat_fixes] fixed issues with running Android project generation scripts on Unix systems Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- cmake/Tools/Platform/Android/android_support.py | 5 +---- cmake/Tools/common.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/cmake/Tools/Platform/Android/android_support.py b/cmake/Tools/Platform/Android/android_support.py index 054897262c..f52cc2736c 100755 --- a/cmake/Tools/Platform/Android/android_support.py +++ b/cmake/Tools/Platform/Android/android_support.py @@ -621,7 +621,7 @@ class AndroidProjectGenerator(object): gradle_wrapper_cmd.extend(['wrapper', '-p', str(self.build_dir.resolve())]) proc_result = subprocess.run(gradle_wrapper_cmd, - shell=True) + shell=(platform.system() == 'Windows')) if proc_result.returncode != 0: raise common.LmbrCmdError("Gradle was unable to generate a gradle wrapper for this project (code {}): {}" .format(proc_result.returncode, proc_result.stderr or ""), @@ -1579,9 +1579,6 @@ class AndroidSDKResolver(object): ext = '' if platform.system() == 'Windows': ext = '.bat' - else: - raise common.LmbrCmdError(f"This tool is not supported on the current platform {platform.system()}") - self.sdk_manager_path = tools_path / 'bin' / f'sdkmanager{ext}' if not self.sdk_manager_path.is_file(): diff --git a/cmake/Tools/common.py b/cmake/Tools/common.py index 02db0730d6..cd42d8c818 100755 --- a/cmake/Tools/common.py +++ b/cmake/Tools/common.py @@ -30,6 +30,9 @@ ENCODING_ERROR_HANDLINGS = 'ignore' # What to do if we encounter any encodin DEFAULT_PAK_ROOT = 'Pak' # The default Pak root folder under engine root where the game paks are built if platform.system() == 'Windows': + class PlatformError(WindowsError): + pass + # Re-use microsoft error codes since this script is meant to only run on windows host platforms ERROR_CODE_FILE_NOT_FOUND = 2 ERROR_CODE_ERROR_NOT_SUPPORTED = 50 @@ -37,6 +40,9 @@ if platform.system() == 'Windows': ERROR_CODE_CANNOT_COPY = 266 ERROR_CODE_ERROR_DIRECTORY = 267 else: + class PlatformError(Exception): + pass + # Posix does not match any of the following errors to specific codes, so just the standard '1' ERROR_CODE_FILE_NOT_FOUND = 1 ERROR_CODE_ERROR_NOT_SUPPORTED = 1 @@ -309,7 +315,7 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too # Extract the version and verify version_output = subprocess.check_output([tool_source, tool_version_argument], - shell=True, + shell=(platform.system() == 'Windows'), stderr=subprocess.PIPE).decode(DEFAULT_TEXT_READ_ENCODING, ENCODING_ERROR_HANDLINGS) version_match = tool_version_regex.search(version_output) @@ -330,13 +336,13 @@ def verify_tool(override_tool_path, tool_name, tool_filename, argument_name, too return result_version, resolved_override_tool_path except CalledProcessError as e: - error_msg = e.output.decode(DEFAULT_TEXT_READ_ENCODING, + error_msg = e.stderr.decode(DEFAULT_TEXT_READ_ENCODING, ENCODING_ERROR_HANDLINGS) raise LmbrCmdError(f"{tool_name} cannot be resolved or there was a problem determining its version number. " f"Either make sure its in the system path environment or a valid path is passed in " f"through the {argument_name} argument.\n{error_msg}", ERROR_CODE_ERROR_NOT_SUPPORTED) - except (WindowsError, RuntimeError) as e: + except (PlatformError, RuntimeError) as e: logging.error(f"Call to '{tool_source}' resulted in error: {e}") raise LmbrCmdError(f"{tool_name} cannot be resolved or there was a problem determining its version number. " f"Either make sure its in the system path environment or a valid path is passed in " @@ -552,7 +558,7 @@ class CommandLineExec(object): call_args.append(str(arguments)) logging.debug("exec(%s)", subprocess.list2cmdline(call_args)) result = subprocess.run(call_args, - shell=True, + shell=(platform.system() == 'Windows'), capture_output=capture_stdout, stderr=subprocess.DEVNULL if not capture_stdout and suppress_stderr else None, encoding='utf-8', From 901aa217b83ad2a86e212d30dd3aee708ea90d1a Mon Sep 17 00:00:00 2001 From: Sean Masterson Date: Mon, 8 Nov 2021 13:10:55 -0800 Subject: [PATCH 133/177] removed unused import and fixed line length Signed-off-by: Sean Masterson --- .../tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py index 6a7989fe1d..f42c091057 100644 --- a/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py +++ b/AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_DiffuseProbeGridAdded.py @@ -81,7 +81,6 @@ def AtomEditorComponents_DiffuseProbeGrid_AddedToEntity(): """ import azlmbr.legacy.general as general - import azlmbr.render as render from editor_python_test_tools.editor_entity_utils import EditorEntity from editor_python_test_tools.utils import Report, Tracer, TestHelper @@ -99,7 +98,8 @@ def AtomEditorComponents_DiffuseProbeGrid_AddedToEntity(): Report.critical_result(Tests.diffuse_probe_grid_creation, diffuse_probe_grid_entity.exists()) # 2. Add a Diffuse Probe Grid component to Diffuse Probe Grid entity. - diffuse_probe_grid_component = diffuse_probe_grid_entity.add_component(AtomComponentProperties.diffuse_probe_grid()) + diffuse_probe_grid_component = diffuse_probe_grid_entity.add_component( + AtomComponentProperties.diffuse_probe_grid()) Report.critical_result( Tests.diffuse_probe_grid_component, diffuse_probe_grid_entity.has_component(AtomComponentProperties.diffuse_probe_grid())) From 13e83e948809474387295ed2e7061ecadb1682fd Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Mon, 8 Nov 2021 13:34:12 -0800 Subject: [PATCH 134/177] Build fix. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../AzFramework/Spawnable/Spawnable.cpp | 26 ++++++++++++------- .../AzFramework/Spawnable/Spawnable.h | 2 ++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp index 27e8a85597..6259a57ab5 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp @@ -532,21 +532,29 @@ namespace AzFramework void Spawnable::Reflect(AZ::ReflectContext* context) { + EntityAlias::Reflect(context); + if (auto* serializeContext = azrtti_cast(context); serializeContext != nullptr) { - serializeContext->Class() - ->Version(1) - ->Field("Spawnable", &Spawnable::EntityAlias::m_spawnable) - ->Field("Tag", &Spawnable::EntityAlias::m_tag) - ->Field("Source Index", &Spawnable::EntityAlias::m_sourceIndex) - ->Field("Target Index", &Spawnable::EntityAlias::m_targetIndex) - ->Field("Alias Type", &Spawnable::EntityAlias::m_aliasType) - ->Field("Queue Load", &Spawnable::EntityAlias::m_queueLoad); - serializeContext->Class()->Version(2) ->Field("Meta data", &Spawnable::m_metaData) ->Field("Entity aliases", &Spawnable::m_entityAliases) ->Field("Entities", &Spawnable::m_entities); } } + + void Spawnable::EntityAlias::Reflect(AZ::ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context); serializeContext != nullptr) + { + serializeContext->Class() + ->Version(1) + ->Field("Spawnable", &EntityAlias::m_spawnable) + ->Field("Tag", &EntityAlias::m_tag) + ->Field("Source Index", &EntityAlias::m_sourceIndex) + ->Field("Target Index", &EntityAlias::m_targetIndex) + ->Field("Alias Type", &EntityAlias::m_aliasType) + ->Field("Queue Load", &EntityAlias::m_queueLoad); + } + } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h index f0aa2c7806..f029246847 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h @@ -62,6 +62,8 @@ namespace AzFramework uint32_t m_targetIndex{ 0 }; //!< The index of the entity in the target spawnable that will be used to replace the original. EntityAliasType m_aliasType{ EntityAliasType::Original }; //!< The kind of replacement. bool m_queueLoad{ false }; //!< Whether or not to automatically queue the spawnable for loading. + + static void Reflect(AZ::ReflectContext* context); }; using EntityList = AZStd::vector>; From c119947f06a2b1657b47af100b8cf18489d324cc Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Mon, 8 Nov 2021 13:40:30 -0800 Subject: [PATCH 135/177] Removed tabs Signed-off-by: santorac <55155825+santorac@users.noreply.github.com> --- Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h | 2 +- Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp | 2 +- Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h index 85c7182acd..9138c0b418 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RPISystem.h @@ -125,7 +125,7 @@ namespace AZ ScriptTimePoint m_startTime; float m_currentSimulationTime = 0.0f; - + RPISystemDescriptor m_descriptor; // Reference to the shader asset that is used diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp index 17b7a4e332..2b32503838 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp @@ -286,7 +286,7 @@ namespace AZ return aznumeric_cast(currentTime); } - + void RPISystem::RenderTick() { if (!m_systemAssetsInitialized) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 5199669152..3e501c3d93 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -412,7 +412,7 @@ namespace AZ } //[GFX TODO]: the completion job should start here } - + void Scene::Simulate(RHI::JobPolicy jobPolicy, float simulationTime) { AZ_PROFILE_SCOPE(RPI, "Scene: Simulate"); From 976c6abb9089026ea164c5fd8c5852df2d5bd407 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Mon, 8 Nov 2021 13:34:12 -0800 Subject: [PATCH 136/177] Build fix. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../AzFramework/Spawnable/Spawnable.cpp | 26 ++++++++++++------- .../AzFramework/Spawnable/Spawnable.h | 2 ++ .../Spawnable/SpawnableAssetHandler.cpp | 2 +- .../Spawnable/SpawnableEntitiesManager.cpp | 2 -- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp index 27e8a85597..6259a57ab5 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.cpp @@ -532,21 +532,29 @@ namespace AzFramework void Spawnable::Reflect(AZ::ReflectContext* context) { + EntityAlias::Reflect(context); + if (auto* serializeContext = azrtti_cast(context); serializeContext != nullptr) { - serializeContext->Class() - ->Version(1) - ->Field("Spawnable", &Spawnable::EntityAlias::m_spawnable) - ->Field("Tag", &Spawnable::EntityAlias::m_tag) - ->Field("Source Index", &Spawnable::EntityAlias::m_sourceIndex) - ->Field("Target Index", &Spawnable::EntityAlias::m_targetIndex) - ->Field("Alias Type", &Spawnable::EntityAlias::m_aliasType) - ->Field("Queue Load", &Spawnable::EntityAlias::m_queueLoad); - serializeContext->Class()->Version(2) ->Field("Meta data", &Spawnable::m_metaData) ->Field("Entity aliases", &Spawnable::m_entityAliases) ->Field("Entities", &Spawnable::m_entities); } } + + void Spawnable::EntityAlias::Reflect(AZ::ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context); serializeContext != nullptr) + { + serializeContext->Class() + ->Version(1) + ->Field("Spawnable", &EntityAlias::m_spawnable) + ->Field("Tag", &EntityAlias::m_tag) + ->Field("Source Index", &EntityAlias::m_sourceIndex) + ->Field("Target Index", &EntityAlias::m_targetIndex) + ->Field("Alias Type", &EntityAlias::m_aliasType) + ->Field("Queue Load", &EntityAlias::m_queueLoad); + } + } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h index f0aa2c7806..f029246847 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/Spawnable.h @@ -62,6 +62,8 @@ namespace AzFramework uint32_t m_targetIndex{ 0 }; //!< The index of the entity in the target spawnable that will be used to replace the original. EntityAliasType m_aliasType{ EntityAliasType::Original }; //!< The kind of replacement. bool m_queueLoad{ false }; //!< Whether or not to automatically queue the spawnable for loading. + + static void Reflect(AZ::ReflectContext* context); }; using EntityList = AZStd::vector>; diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp index eab681da0d..411ee56687 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableAssetHandler.cpp @@ -97,7 +97,7 @@ namespace AzFramework void SpawnableAssetHandler::ResolveEntityAliases( Spawnable* spawnable, - const AZ::Data::Asset& asset, + [[maybe_unused]] const AZ::Data::Asset& asset, AZStd::chrono::milliseconds streamingDeadline, AZ::IO::IStreamerTypes::Priority streamingPriority, const AZ::Data::AssetFilterCB& assetLoadFilterCB) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 1e863877a4..37570caec7 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -317,8 +317,6 @@ namespace AzFramework AZ::Entity* previouslySpawnedEntity, AZ::SerializeContext& serializeContext) { - using ResultType = AZStd::pair; - AZ::Entity* clone = nullptr; switch (alias.m_aliasType) { From b3295ffeb3504bca26213bb6b9a28a138c83e7ba Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Mon, 8 Nov 2021 14:54:43 -0800 Subject: [PATCH 137/177] Fixed several issues with compilation of Spawnable Entities Aliases. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../Tests/Mocks/MockSpawnableEntitiesInterface.h | 10 +++++++++- .../Prefab/Spawnable/SpawnableUtils.cpp | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h b/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h index a437545adf..4d04fa5bc9 100644 --- a/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h +++ b/Code/Framework/AzFramework/Tests/Mocks/MockSpawnableEntitiesInterface.h @@ -36,7 +36,7 @@ namespace AzFramework MOCK_METHOD3( SpawnEntities, - void(EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs)); + void(EntitySpawnTicket& ticket, AZStd::vector entityIndices, SpawnEntitiesOptionalArgs optionalArgs)); MOCK_METHOD2(DespawnAllEntities, void(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs)); @@ -49,6 +49,13 @@ namespace AzFramework ReloadSpawnable, void(EntitySpawnTicket& ticket, AZ::Data::Asset spawnable, ReloadSpawnableOptionalArgs optionalArgs)); + MOCK_METHOD3( + UpdateEntityAliasTypes, + void( + EntitySpawnTicket& ticket, + AZStd::vector updatedAliases, + UpdateEntityAliasTypesOptionalArgs optionalArgs)); + MOCK_METHOD3( ListEntities, void(EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs)); @@ -61,6 +68,7 @@ namespace AzFramework void(EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs)); MOCK_METHOD3(Barrier, void(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs)); + MOCK_METHOD3(LoadBarrier, void(EntitySpawnTicket& ticket, BarrierCallback completionCallback, LoadBarrierOptionalArgs optionalArgs)); MOCK_METHOD1(CreateTicket, AZStd::pair(AZ::Data::Asset&& spawnable)); MOCK_METHOD1(DestroyTicket, void(void* ticket)); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp index ab3c52f6d0..5e552aad3f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -297,7 +298,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils { if ((*it)->GetId() == entity) { - return AZStd::distance(begin, it); + return aznumeric_caster(AZStd::distance(begin, it)); } } return InvalidEntityIndex; From 3b05e6ab1ee8f989463f5608955a104cf533f94c Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Tue, 9 Nov 2021 02:13:09 -0800 Subject: [PATCH 138/177] bugfix: correctly center quick access bar (#5396) Signed-off-by: Michael Pollind --- Code/Editor/CryEdit.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index b47febd5ed..5bcb77797c 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -3827,7 +3827,8 @@ void CCryEditApp::OnOpenQuickAccessBar() } QRect geo = m_pQuickAccessBar->geometry(); - geo.moveCenter(MainWindow::instance()->geometry().center()); + auto mainWindow = MainWindow::instance(); + geo.moveCenter(mainWindow->mapToGlobal(mainWindow->geometry().center())); m_pQuickAccessBar->setGeometry(geo); m_pQuickAccessBar->setVisible(true); m_pQuickAccessBar->setFocus(); From f918b12b5acfd790caebddec79807bd8eb3332f1 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Tue, 9 Nov 2021 13:07:20 +0100 Subject: [PATCH 139/177] EMotion FX: Taskify dual quat skinning deformer (#5368) * Moved from job system to task graph for the multi-threaded dual quaternion skinning deformer. * Prepare the task graph at init time and reuse it at runtime. Signed-off-by: Benjamin Jillich --- .../EMotionFX/Source/DualQuatSkinDeformer.cpp | 75 ++++++++++++++----- .../EMotionFX/Source/DualQuatSkinDeformer.h | 3 + 2 files changed, 58 insertions(+), 20 deletions(-) diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/DualQuatSkinDeformer.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/DualQuatSkinDeformer.cpp index 2d5d8e2a46..afd570c102 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/DualQuatSkinDeformer.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Source/DualQuatSkinDeformer.cpp @@ -27,6 +27,8 @@ namespace EMotionFX DualQuatSkinDeformer::DualQuatSkinDeformer(Mesh* mesh) : MeshDeformer(mesh) { + AZ::TaskGraphActiveInterface* taskGraphActiveInterface = AZ::Interface::Get(); + m_useTaskGraph = taskGraphActiveInterface && taskGraphActiveInterface->IsTaskGraphActive(); } DualQuatSkinDeformer::~DualQuatSkinDeformer() @@ -79,9 +81,8 @@ namespace EMotionFX { const Actor* actor = actorInstance->GetActor(); const Pose* pose = actorInstance->GetTransformData()->GetCurrentPose(); - const uint32 numVertices = m_mesh->GetNumVertices(); - // pre-calculate the skinning matrices + // Calculate the skinning matrices based on the current pose. for (BoneInfo& boneInfo : m_bones) { const size_t nodeIndex = boneInfo.m_nodeNr; @@ -89,27 +90,38 @@ namespace EMotionFX boneInfo.m_dualQuat.FromRotationTranslation(skinTransform.m_rotation, skinTransform.m_position); } - AZ::JobCompletion jobCompletion; - - // Split up the skinned vertices into batches. - const AZ::u32 numBatches = aznumeric_caster(ceilf(aznumeric_cast(numVertices) / aznumeric_cast(s_numVerticesPerBatch))); - for (AZ::u32 batchIndex = 0; batchIndex < numBatches; ++batchIndex) + if (m_useTaskGraph) { - const AZ::u32 startVertex = batchIndex * s_numVerticesPerBatch; - const AZ::u32 endVertex = AZStd::min(startVertex + s_numVerticesPerBatch, numVertices); - - // Create a job for every batch and skin them simultaneously. - AZ::JobContext* jobContext = nullptr; - AZ::Job* job = AZ::CreateJobFunction([this, startVertex, endVertex]() - { - SkinRange(m_mesh, startVertex, endVertex, m_bones); - }, /*isAutoDelete=*/true, jobContext); - - job->SetDependent(&jobCompletion); - job->Start(); + // Skin the vertices by executing the task graph. + AZ::TaskGraphEvent finishedEvent; + m_taskGraph.Submit(&finishedEvent); + finishedEvent.Wait(); } + else + { + AZ::JobCompletion jobCompletion; - jobCompletion.StartAndWaitForCompletion(); + // Split up the skinned vertices into batches. + const uint32 numVertices = m_mesh->GetNumVertices(); + const AZ::u32 numBatches = aznumeric_caster(ceilf(aznumeric_cast(numVertices) / aznumeric_cast(s_numVerticesPerBatch))); + for (AZ::u32 batchIndex = 0; batchIndex < numBatches; ++batchIndex) + { + const AZ::u32 startVertex = batchIndex * s_numVerticesPerBatch; + const AZ::u32 endVertex = AZStd::min(startVertex + s_numVerticesPerBatch, numVertices); + + // Create a job for every batch and skin them simultaneously. + AZ::JobContext* jobContext = nullptr; + AZ::Job* job = AZ::CreateJobFunction([this, startVertex, endVertex]() + { + SkinRange(m_mesh, startVertex, endVertex, m_bones); + }, /*isAutoDelete=*/true, jobContext); + + job->SetDependent(&jobCompletion); + job->Start(); + } + + jobCompletion.StartAndWaitForCompletion(); + } } void DualQuatSkinDeformer::SkinRange(Mesh* mesh, AZ::u32 startVertex, AZ::u32 endVertex, const AZStd::vector& boneInfos) @@ -340,5 +352,28 @@ namespace EMotionFX } } } + + if (m_useTaskGraph) + { + // Prepare the task graph + // Split up the to be skinned vertices into batches. As the mesh does not change at runtime, the task graph can + // be prepared at init time and be reused at runtime. + const uint32 numVertices = m_mesh->GetNumVertices(); + const AZ::u32 numBatches = aznumeric_caster(ceilf(aznumeric_cast(numVertices) / aznumeric_cast(s_numVerticesPerBatch))); + for (AZ::u32 batchIndex = 0; batchIndex < numBatches; ++batchIndex) + { + const AZ::u32 startVertex = batchIndex * s_numVerticesPerBatch; + const AZ::u32 endVertex = AZStd::min(startVertex + s_numVerticesPerBatch, numVertices); + + // Create a task for every batch and skin them simultaneously. + AZ::TaskDescriptor taskDescriptor{"DualQuatSkinRange", "Animation"}; + m_taskGraph.AddTask( + taskDescriptor, + [this, startVertex, endVertex]() + { + SkinRange(m_mesh, startVertex, endVertex, m_bones); + }); + } + } } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/DualQuatSkinDeformer.h b/Gems/EMotionFX/Code/EMotionFX/Source/DualQuatSkinDeformer.h index 434f2920ee..95312c5b83 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Source/DualQuatSkinDeformer.h +++ b/Gems/EMotionFX/Code/EMotionFX/Source/DualQuatSkinDeformer.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include "EMotionFXConfig.h" #include @@ -138,6 +139,8 @@ namespace EMotionFX //! Number of vertices per batch/job used for multi-threaded software skinning. static constexpr AZ::u32 s_numVerticesPerBatch = 10000; + AZ::TaskGraph m_taskGraph; + bool m_useTaskGraph = true; /** * Default constructor. From 0ce2a7b2f48f3929b83303de0cfe7c9dc8105542 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Tue, 9 Nov 2021 13:07:48 +0100 Subject: [PATCH 140/177] EMotion FX: Apply motion extraction only in game-mode and for anim editor entities (#5401) * Motion extraction should only be applied when in game mode or for entities that belong to the Animation Editor. We don't want our entities to move in editor mode. * Moved code into separate function that actually applies the trajectory delta to the entity/character controller. Signed-off-by: Benjamin Jillich --- .../Integration/System/SystemComponent.cpp | 205 ++++++++++-------- .../Integration/System/SystemComponent.h | 7 + 2 files changed, 123 insertions(+), 89 deletions(-) diff --git a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp index 38af4232ba..755db41996 100644 --- a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp @@ -65,36 +65,38 @@ #if defined(EMOTIONFXANIMATION_EDITOR) // EMFX tools / editor includes // Qt -# include +#include // EMStudio tools and main window registration -# include -# include -# include -# include -# include -# include +#include +#include +#include +#include +#include +#include // EMStudio plugins -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include #endif // EMOTIONFXANIMATION_EDITOR #include @@ -589,14 +591,12 @@ namespace EMotionFX #endif REGISTER_CVAR2("emfx_updateEnabled", &CVars::emfx_updateEnabled, 1, VF_DEV_ONLY, "Enable main EMFX update"); - REGISTER_CVAR2("emfx_actorRenderEnabled", &CVars::emfx_actorRenderEnabled, 1, VF_DEV_ONLY, "Enable ActorRenderNode rendering"); } ////////////////////////////////////////////////////////////////////////// void SystemComponent::OnCrySystemShutdown(ISystem&) { gEnv->pConsole->UnregisterVariable("emfx_updateEnabled"); - gEnv->pConsole->UnregisterVariable("emfx_actorRenderEnabled"); #if !defined(AZ_MONOLITHIC_BUILD) gEnv = nullptr; @@ -620,75 +620,102 @@ namespace EMotionFX { // Main EMotionFX runtime update. GetEMotionFX().Update(delta); - } - const ActorManager* actorManager = GetEMotionFX().GetActorManager(); - const size_t numActorInstances = actorManager->GetNumActorInstances(); - for (size_t i = 0; i < numActorInstances; ++i) - { - const ActorInstance* actorInstance = actorManager->GetActorInstance(i); + bool inGameMode = true; +#if defined (EMOTIONFXANIMATION_EDITOR) + // Check if we are in game mode. + IEditor* editor = nullptr; + AzToolsFramework::EditorRequestBus::BroadcastResult(editor, &AzToolsFramework::EditorRequests::GetEditor); + inGameMode = editor->IsInGameMode(); +#endif - if (actorInstance && actorInstance->GetIsEnabled() && actorInstance->GetIsOwnedByRuntime()) + // Apply the motion extraction deltas to the character controller / entity transform for all entities. + const ActorManager* actorManager = GetEMotionFX().GetActorManager(); + const size_t numActorInstances = actorManager->GetNumActorInstances(); + for (size_t i = 0; i < numActorInstances; ++i) { - AZ::Entity* entity = actorInstance->GetEntity(); - const Actor* actor = actorInstance->GetActor(); + ActorInstance* actorInstance = actorManager->GetActorInstance(i); - if (entity && actor && actor->GetMotionExtractionNode()) + // Apply motion extraction only in game mode or in case the actor instance belongs to the Animation Editor. + const bool applyMotionExtraction = inGameMode || !actorInstance->GetIsOwnedByRuntime(); + if (applyMotionExtraction) { - const AZ::EntityId entityId = entity->GetId(); - - // Check if we have any physics character controllers. - bool hasCustomMotionExtractionController = false; - bool hasPhysicsController = false; - - Physics::CharacterRequestBus::EventResult(hasPhysicsController, entityId, &Physics::CharacterRequests::IsPresent); - if (!hasPhysicsController) - { - hasCustomMotionExtractionController = MotionExtractionRequestBus::FindFirstHandler(entityId) != nullptr; - } - - // If we have a physics controller. - if (hasCustomMotionExtractionController || hasPhysicsController) - { - const float deltaTimeInv = (delta > 0.0f) ? (1.0f / delta) : 0.0f; - - AZ::Transform currentTransform = AZ::Transform::CreateIdentity(); - AZ::TransformBus::EventResult(currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM); - - const AZ::Vector3 actorInstancePosition = actorInstance->GetWorldSpaceTransform().m_position; - const AZ::Vector3 positionDelta = actorInstancePosition - currentTransform.GetTranslation(); - - if (hasPhysicsController) - { - Physics::CharacterRequestBus::Event( - entityId, &Physics::CharacterRequests::AddVelocity, positionDelta * deltaTimeInv); - } - else if (hasCustomMotionExtractionController) - { - MotionExtractionRequestBus::Event(entityId, &MotionExtractionRequestBus::Events::ExtractMotion, positionDelta, delta); - AZ::TransformBus::EventResult(currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM); - } - - // Update the entity rotation. - const AZ::Quaternion actorInstanceRotation = actorInstance->GetWorldSpaceTransform().m_rotation; - const AZ::Quaternion currentRotation = currentTransform.GetRotation(); - if (!currentRotation.IsClose(actorInstanceRotation, AZ::Constants::FloatEpsilon)) - { - AZ::Transform newTransform = currentTransform; - newTransform.SetRotation(actorInstanceRotation); - AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTM, newTransform); - } - } - else // There is no physics controller, just use EMotion FX's actor instance transform directly. - { - const AZ::Transform newTransform = actorInstance->GetWorldSpaceTransform().ToAZTransform(); - AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTM, newTransform); - } + actorInstance->SetMotionExtractionEnabled(true); + ApplyMotionExtraction(actorInstance, delta); + } + else + { + actorInstance->SetMotionExtractionEnabled(false); } } } } + void SystemComponent::ApplyMotionExtraction(const ActorInstance* actorInstance, float timeDelta) + { + AZ_Assert(actorInstance, "Cannot apply motion extraction. Actor instance is not valid."); + AZ_Assert(actorInstance->GetActor(), "Cannot apply motion extraction. Actor instance is not linked to a valid actor."); + + AZ::Entity* entity = actorInstance->GetEntity(); + const Actor* actor = actorInstance->GetActor(); + if (!actorInstance->GetIsEnabled() || + !entity || + !actor->GetMotionExtractionNode()) + { + return; + } + + const AZ::EntityId entityId = entity->GetId(); + + // Check if we have any physics character controllers. + bool hasCustomMotionExtractionController = false; + bool hasPhysicsController = false; + + Physics::CharacterRequestBus::EventResult(hasPhysicsController, entityId, &Physics::CharacterRequests::IsPresent); + if (!hasPhysicsController) + { + hasCustomMotionExtractionController = MotionExtractionRequestBus::FindFirstHandler(entityId) != nullptr; + } + + // If we have a physics controller. + if (hasCustomMotionExtractionController || hasPhysicsController) + { + const float deltaTimeInv = (timeDelta > 0.0f) ? (1.0f / timeDelta) : 0.0f; + + AZ::Transform currentTransform = AZ::Transform::CreateIdentity(); + AZ::TransformBus::EventResult(currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM); + + const AZ::Vector3 actorInstancePosition = actorInstance->GetWorldSpaceTransform().m_position; + const AZ::Vector3 positionDelta = actorInstancePosition - currentTransform.GetTranslation(); + + if (hasPhysicsController) + { + Physics::CharacterRequestBus::Event( + entityId, &Physics::CharacterRequests::AddVelocity, positionDelta * deltaTimeInv); + } + else if (hasCustomMotionExtractionController) + { + MotionExtractionRequestBus::Event(entityId, &MotionExtractionRequestBus::Events::ExtractMotion, positionDelta, timeDelta); + AZ::TransformBus::EventResult(currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM); + } + + // Update the entity rotation. + const AZ::Quaternion actorInstanceRotation = actorInstance->GetWorldSpaceTransform().m_rotation; + const AZ::Quaternion currentRotation = currentTransform.GetRotation(); + if (!currentRotation.IsClose(actorInstanceRotation, AZ::Constants::FloatEpsilon)) + { + AZ::Transform newTransform = currentTransform; + newTransform.SetRotation(actorInstanceRotation); + AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTM, newTransform); + } + } + else // There is no physics controller, just use EMotion FX's actor instance transform directly. + { + const AZ::Transform newTransform = actorInstance->GetWorldSpaceTransform().ToAZTransform(); + AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTM, newTransform); + } + } + int SystemComponent::GetTickOrder() { return AZ::TICK_ANIMATION; diff --git a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h index 3ef626c947..5be9afbbaa 100644 --- a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h +++ b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.h @@ -124,6 +124,13 @@ namespace EMotionFX AZ::u32 m_numThreads; private: + //! Synchronize the actor instance location with the entity or character controller. + //! In case no character controller component is available, the entity will be moved + //! to the actor instance position. The spatial difference between the entity and the + //! actor instance will be calculated in case a character controller is present, and the + //! velocity will be applied to it to move it towards the actor instance. + void ApplyMotionExtraction(const ActorInstance* actorInstance, float timeDelta); + AZStd::vector > m_assetHandlers; AZStd::unique_ptr m_eventHandler; AZStd::unique_ptr m_renderBackendManager; From 669ac8bb61c87a8b52f8de143868584641f6b6f1 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Tue, 9 Nov 2021 13:07:59 +0100 Subject: [PATCH 141/177] EMotion FX: Fix for the Y motion coordinate not being editable in blend space nodes (#5406) Signed-off-by: Benjamin Jillich --- .../BlendSpaceMotionContainerHandler.cpp | 59 ++++++++----------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/BlendSpaceMotionContainerHandler.cpp b/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/BlendSpaceMotionContainerHandler.cpp index 8cee46133e..f0a6e80bf7 100644 --- a/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/BlendSpaceMotionContainerHandler.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/PropertyWidgets/BlendSpaceMotionContainerHandler.cpp @@ -41,47 +41,36 @@ namespace EMotionFX layout->addWidget(m_labelMotion, row, column); column++; - // Motion position x - QHBoxLayout* layoutX = new QHBoxLayout(); - layoutX->setAlignment(Qt::AlignRight); + const auto makeSpinbox = [row, &column, layout, motionId = motionId.c_str()](const QString& text, const QString& color) + { + auto* axisLayout = new QHBoxLayout(); + axisLayout->setAlignment(Qt::AlignRight); - QLabel* labelX = new QLabel("X"); - labelX->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - labelX->setStyleSheet("QLabel { font-weight: bold; color : red; }"); - layoutX->addWidget(labelX); + auto* axisLabel = new QLabel(text); + axisLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + axisLabel->setStyleSheet(QString("QLabel { font-weight: bold; color : %1; }").arg(color)); + axisLayout->addWidget(axisLabel); - m_spinboxX = new AzQtComponents::DoubleSpinBox(); - m_spinboxX->setSingleStep(0.1); - m_spinboxX->setDecimals(4); - m_spinboxX->setRange(-FLT_MAX, FLT_MAX); - m_spinboxX->setProperty("motionId", motionId.c_str()); - m_spinboxX->setKeyboardTracking(false); - layoutX->addWidget(m_spinboxX); + auto* spinbox = new AzQtComponents::DoubleSpinBox(); + spinbox->setSingleStep(0.1); + spinbox->setDecimals(4); + spinbox->setRange(-FLT_MAX, FLT_MAX); + spinbox->setProperty("motionId", motionId); + spinbox->setKeyboardTracking(false); + axisLayout->addWidget(spinbox); - layout->addLayout(layoutX, row, column); - column++; + layout->addLayout(axisLayout, row, column); + column++; + + return spinbox; + }; + + // Motion coordinate spinboxes. + m_spinboxX = makeSpinbox("X", "red"); - // Motion position y if (showYFields) { - QHBoxLayout* layoutY = new QHBoxLayout(); - layoutY->setAlignment(Qt::AlignRight); - - QLabel* labelY = new QLabel("Y"); - labelY->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - labelY->setStyleSheet("QLabel { font-weight: bold; color : green; }"); - layoutY->addWidget(labelY); - - m_spinboxY = new AzQtComponents::DoubleSpinBox(); - m_spinboxY->setSingleStep(0.1); - m_spinboxY->setDecimals(4); - m_spinboxY->setRange(-FLT_MAX, FLT_MAX); - m_spinboxY->setProperty("motionId", motionId.c_str()); - m_spinboxX->setKeyboardTracking(false); - layoutY->addWidget(m_spinboxY); - - layout->addLayout(layoutY, row, column); - column++; + m_spinboxY = makeSpinbox("Y", "green"); } else { From 1e6518abab70b82805a491c3732c39725d4939f9 Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Tue, 9 Nov 2021 14:50:44 +0000 Subject: [PATCH 142/177] Build fix after the merge from dev Signed-off-by: Sergey Pereslavtsev --- .../NetworkEntity/NetworkEntityManager.cpp | 30 ++----------------- .../NetworkEntity/NetworkEntityManager.h | 1 + 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index 280cdaa163..6885d78075 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -231,35 +231,9 @@ namespace Multiplayer { for (NetEntityId exitingId : entitiesNotInDomain) { - bool safeToExit = true; NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(exitingId); - // We need special handling for the NetworkHierarchy as well, since related entities need to be migrated together - NetworkHierarchyRootComponentController* hierarchyRootController = entityHandle.FindController(); - NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController(); - - // Find the root entity - AZ::Entity* hierarchyRootEntity = nullptr; - if (hierarchyRootController) - { - hierarchyRootEntity = hierarchyRootController->GetParent().GetHierarchicalRoot(); - } - else if (hierarchyChildController) - { - hierarchyRootEntity = hierarchyChildController->GetParent().GetHierarchicalRoot(); - } - - if (hierarchyRootEntity) - { - NetEntityId rootNetId = GetNetEntityIdById(hierarchyRootEntity->GetId()); - ConstNetworkEntityHandle rootEntityHandle = GetEntity(rootNetId); - - // Check if the root entity is still tracked by this authority - if (rootEntityHandle.Exists() && rootEntityHandle.GetNetBindComponent()->HasController()) - { - safeToExit = false; - } - } + bool safeToExit = IsHierarchySafeToExit(entityHandle, entitiesNotInDomain);; // Validate that we aren't already planning to remove this entity if (safeToExit) @@ -637,7 +611,7 @@ namespace Multiplayer } } - bool NetworkEntityManager::IsHierarchySafeToExit(NetworkEntityHandle& entityHandle, const IEntityDomain::EntitiesNotInDomain& entitiesNotInDomain) + bool NetworkEntityManager::IsHierarchySafeToExit(NetworkEntityHandle& entityHandle, const NetEntityIdSet& entitiesNotInDomain) { bool safeToExit = true; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h index 7c6fdd94f9..8327d95a39 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h @@ -99,6 +99,7 @@ namespace Multiplayer private: void RemoveEntities(); NetEntityId NextId(); + bool IsHierarchySafeToExit(NetworkEntityHandle& entityHandle, const NetEntityIdSet& entitiesNotInDomain); NetworkEntityTracker m_networkEntityTracker; NetworkEntityAuthorityTracker m_networkEntityAuthorityTracker; From 7fce5e52a457aa1379aec96ff8818bc93306a4a5 Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Tue, 9 Nov 2021 17:36:01 +0000 Subject: [PATCH 143/177] PR feedback addressing Signed-off-by: Sergey Pereslavtsev --- .../Editor/MultiplayerEditorSystemComponent.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index a33d6bf946..8f11985cba 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -234,6 +234,8 @@ namespace Multiplayer IMultiplayerTools* mpTools = AZ::Interface::Get(); if (!editorsv_enabled || !mpTools) { + // Early out if Editor server is not enabled. + // This allows to avoid printing an error about missing PrefabEditorEntityOwnershipInterface for non-prefab levels. return; } @@ -270,12 +272,12 @@ namespace Multiplayer { AZ_Warning( "MultiplayerEditor", false, - "Launching EditorServer skipped because incompatible cvars. editorsv_launch=true, meaning you want to launch an editor-server on this machine, but the editorsv_serveraddr is %s instead of the local address (127.0.0.1). " - "Please either set editorsv_launch=false and keep the remote editor-server, or set editorsv_launch=true and editorsv_serveraddr=127.0.0.1.", + "Launching editor server skipped because of incompatible settings. " + "When using editorsv_launch=true editorsv_serveraddr must be set to local address (127.0.0.1) instead %s", remoteAddress.c_str()) return; } - + // Begin listening for MPEditor packets before we launch the editor-server. // The editor-server will send us (the editor) an "EditorServerReadyForLevelData" packet to let us know it's ready to receive data. INetworkInterface* editorNetworkInterface = @@ -299,8 +301,7 @@ namespace Multiplayer { AZ_Warning( "MultiplayerEditor", false, - "Editor multiplayer game-mode failed! Could not connect to an editor-server. editorsv_launch is false so we're assuming you're running your own editor-server at editorsv_serveraddr(%s) on editorsv_port(%i). " - "Either set editorsv_launch=true so the editor launches an editor-server for you, or launch your own editor-server by hand before entering game-mode. Remember editor-servers must use editorsv_isDedicated=true.", + "Could not connect to a server at editorsv_serveraddr(%s) on editorsv_port(%i). Check server is active or use editorsv_launch to auto-launch a server.", remoteAddress.c_str(), static_cast(editorsv_port)) return; From cc2513f224bf2d42fa268c50040ec82d5f88e04c Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Tue, 9 Nov 2021 09:46:32 -0800 Subject: [PATCH 144/177] Linux build fix Spawnable Entity Aliases. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h index f20c85eb1d..d35a09a574 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h @@ -50,7 +50,6 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils struct EntityAliasSpawnableLink { - EntityAliasSpawnableLink() = default; EntityAliasSpawnableLink(AzFramework::Spawnable& spawnable, AZ::EntityId index); AzFramework::Spawnable& m_spawnable; @@ -59,7 +58,6 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils struct EntityAliasPrefabLink { - EntityAliasPrefabLink() = default; EntityAliasPrefabLink(AZStd::string prefabName, AzToolsFramework::Prefab::AliasPath alias); AZStd::string m_prefabName; From 85e9ca4692481d57df8b90abdda689879dca46dc Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Tue, 9 Nov 2021 11:09:22 -0800 Subject: [PATCH 145/177] Fixes for clang-12 (#5435) Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- scripts/build/Platform/Linux/build_config.json | 4 ++-- .../build_node/Platform/Linux/package-list.ubuntu-bionic.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index 2e08c2f83d..103f243aae 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -221,7 +221,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_PARALLEL_LINK_JOBS=4 -DLY_DISABLE_TEST_MODULES=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_PARALLEL_LINK_JOBS=4 -DLY_DISABLE_TEST_MODULES=TRUE", "CMAKE_TARGET": "install" } }, @@ -254,7 +254,7 @@ "COMMAND_CWD": "${WORKSPACE}/${PROJECT_REPOSITORY_NAME}", "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_PARALLEL_LINK_JOBS=4 -DCMAKE_MODULE_PATH=${WORKSPACE}/o3de/install/cmake", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_PARALLEL_LINK_JOBS=4 -DCMAKE_MODULE_PATH=${WORKSPACE}/o3de/install/cmake", "CMAKE_TARGET": "all" } } diff --git a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt index a09d823950..2af55ab180 100644 --- a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt +++ b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-bionic.txt @@ -2,7 +2,7 @@ # Build Tools Packages cmake/3.20.1-0kitware1ubuntu18.04.1 # For cmake -clang-6.0 # For Ninja Build System +clang-12 # For Ninja Build System ninja-build # For the compiler and its dependencies java-11-amazon-corretto-jdk # For Jenkins and Android From 61fa2eac32b25ca5172a387991c431da8632c24c Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Tue, 9 Nov 2021 11:10:03 -0800 Subject: [PATCH 146/177] Better compiler detection on Linux (#5376) * Better compiler detection on Linux Moving EngineFinder.cmake to cmake/ in the templates Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * skipping detection if compiler is passed through environment Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * Fixes condition, needs to be in quotes since is the value of the sttring Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- CMakeLists.txt | 1 + .../DefaultProject/Template/CMakeLists.txt | 3 +- .../Template/cmake/CompilerSettings.cmake | 13 +++++++ .../Template/{ => cmake}/EngineFinder.cmake | 0 .../Platform/Linux/CompilerSettings.cmake | 34 +++++++++++++++++++ Templates/DefaultProject/template.json | 16 +++++++-- .../MinimalProject/Template/CMakeLists.txt | 3 +- .../Template/cmake/CompilerSettings.cmake | 13 +++++++ .../Template/{ => cmake}/EngineFinder.cmake | 0 .../Platform/Linux/CompilerSettings.cmake | 34 +++++++++++++++++++ Templates/MinimalProject/template.json | 16 +++++++-- cmake/CompilerSettings.cmake | 13 +++++++ cmake/Platform/Linux/CompilerSettings.cmake | 34 +++++++++++++++++++ 13 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 Templates/DefaultProject/Template/cmake/CompilerSettings.cmake rename Templates/DefaultProject/Template/{ => cmake}/EngineFinder.cmake (100%) create mode 100644 Templates/DefaultProject/Template/cmake/Platform/Linux/CompilerSettings.cmake create mode 100644 Templates/MinimalProject/Template/cmake/CompilerSettings.cmake rename Templates/MinimalProject/Template/{ => cmake}/EngineFinder.cmake (100%) create mode 100644 Templates/MinimalProject/Template/cmake/Platform/Linux/CompilerSettings.cmake create mode 100644 cmake/CompilerSettings.cmake create mode 100644 cmake/Platform/Linux/CompilerSettings.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index e659270f84..f61a9561e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ include(cmake/Version.cmake) include(cmake/OutputDirectory.cmake) if(NOT PROJECT_NAME) + include(cmake/CompilerSettings.cmake) project(O3DE LANGUAGES C CXX VERSION ${LY_VERSION_STRING} diff --git a/Templates/DefaultProject/Template/CMakeLists.txt b/Templates/DefaultProject/Template/CMakeLists.txt index 76e8f227be..ae4bb662a3 100644 --- a/Templates/DefaultProject/Template/CMakeLists.txt +++ b/Templates/DefaultProject/Template/CMakeLists.txt @@ -10,11 +10,12 @@ if(NOT PROJECT_NAME) cmake_minimum_required(VERSION 3.20) + include(cmake/CompilerSettings.cmake) project(${Name} LANGUAGES C CXX VERSION 1.0.0.0 ) - include(EngineFinder.cmake OPTIONAL) + include(cmake/EngineFinder.cmake OPTIONAL) find_package(o3de REQUIRED) o3de_initialize() else() diff --git a/Templates/DefaultProject/Template/cmake/CompilerSettings.cmake b/Templates/DefaultProject/Template/cmake/CompilerSettings.cmake new file mode 100644 index 0000000000..cf6614e4a5 --- /dev/null +++ b/Templates/DefaultProject/Template/cmake/CompilerSettings.cmake @@ -0,0 +1,13 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +# File to tweak compiler settings before compiler detection happens (before project() is called) +# We dont have PAL enabled at this point, so we can only use pure-CMake variables +if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Linux") + include(cmake/Platform/${CMAKE_HOST_SYSTEM_NAME}/CompilerSettings.cmake) +endif() diff --git a/Templates/DefaultProject/Template/EngineFinder.cmake b/Templates/DefaultProject/Template/cmake/EngineFinder.cmake similarity index 100% rename from Templates/DefaultProject/Template/EngineFinder.cmake rename to Templates/DefaultProject/Template/cmake/EngineFinder.cmake diff --git a/Templates/DefaultProject/Template/cmake/Platform/Linux/CompilerSettings.cmake b/Templates/DefaultProject/Template/cmake/Platform/Linux/CompilerSettings.cmake new file mode 100644 index 0000000000..9bb629c53b --- /dev/null +++ b/Templates/DefaultProject/Template/cmake/Platform/Linux/CompilerSettings.cmake @@ -0,0 +1,34 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +if(NOT CMAKE_C_COMPILER AND NOT CMAKE_CXX_COMPILER AND NOT "$ENV{CC}" AND NOT "$ENV{CXX}") + set(path_search + /bin + /usr/bin + /usr/local/bin + /sbin + /usr/sbin + /usr/local/sbin + ) + list(TRANSFORM path_search APPEND "/clang-[0-9]*") + file(GLOB clang_versions ${path_search}) + if(clang_versions) + # Find and pick the highest installed version + list(SORT clang_versions COMPARE NATURAL) + list(GET clang_versions 0 clang_higher_version_path) + string(REGEX MATCH "clang-([0-9.]*)" clang_higher_version ${clang_higher_version_path}) + if(CMAKE_MATCH_1) + set(CMAKE_C_COMPILER clang-${CMAKE_MATCH_1}) + set(CMAKE_CXX_COMPILER clang++-${CMAKE_MATCH_1}) + else() + message(FATAL_ERROR "Clang not found, please install clang") + endif() + else() + message(FATAL_ERROR "Clang not found, please install clang") + endif() +endif() diff --git a/Templates/DefaultProject/template.json b/Templates/DefaultProject/template.json index 1e84ea8424..a36926f632 100644 --- a/Templates/DefaultProject/template.json +++ b/Templates/DefaultProject/template.json @@ -181,8 +181,20 @@ "isOptional": false }, { - "file": "EngineFinder.cmake", - "origin": "EngineFinder.cmake", + "file": "cmake/EngineFinder.cmake", + "origin": "cmake/EngineFinder.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "cmake/CompilerSettings.cmake", + "origin": "cmake/CompilerSettings.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "cmake/Platform/Linux/CompilerSettings.cmake", + "origin": "cmake/Platform/Linux/CompilerSettings.cmake", "isTemplated": false, "isOptional": false }, diff --git a/Templates/MinimalProject/Template/CMakeLists.txt b/Templates/MinimalProject/Template/CMakeLists.txt index 76e8f227be..ae4bb662a3 100644 --- a/Templates/MinimalProject/Template/CMakeLists.txt +++ b/Templates/MinimalProject/Template/CMakeLists.txt @@ -10,11 +10,12 @@ if(NOT PROJECT_NAME) cmake_minimum_required(VERSION 3.20) + include(cmake/CompilerSettings.cmake) project(${Name} LANGUAGES C CXX VERSION 1.0.0.0 ) - include(EngineFinder.cmake OPTIONAL) + include(cmake/EngineFinder.cmake OPTIONAL) find_package(o3de REQUIRED) o3de_initialize() else() diff --git a/Templates/MinimalProject/Template/cmake/CompilerSettings.cmake b/Templates/MinimalProject/Template/cmake/CompilerSettings.cmake new file mode 100644 index 0000000000..cf6614e4a5 --- /dev/null +++ b/Templates/MinimalProject/Template/cmake/CompilerSettings.cmake @@ -0,0 +1,13 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +# File to tweak compiler settings before compiler detection happens (before project() is called) +# We dont have PAL enabled at this point, so we can only use pure-CMake variables +if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Linux") + include(cmake/Platform/${CMAKE_HOST_SYSTEM_NAME}/CompilerSettings.cmake) +endif() diff --git a/Templates/MinimalProject/Template/EngineFinder.cmake b/Templates/MinimalProject/Template/cmake/EngineFinder.cmake similarity index 100% rename from Templates/MinimalProject/Template/EngineFinder.cmake rename to Templates/MinimalProject/Template/cmake/EngineFinder.cmake diff --git a/Templates/MinimalProject/Template/cmake/Platform/Linux/CompilerSettings.cmake b/Templates/MinimalProject/Template/cmake/Platform/Linux/CompilerSettings.cmake new file mode 100644 index 0000000000..9bb629c53b --- /dev/null +++ b/Templates/MinimalProject/Template/cmake/Platform/Linux/CompilerSettings.cmake @@ -0,0 +1,34 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +if(NOT CMAKE_C_COMPILER AND NOT CMAKE_CXX_COMPILER AND NOT "$ENV{CC}" AND NOT "$ENV{CXX}") + set(path_search + /bin + /usr/bin + /usr/local/bin + /sbin + /usr/sbin + /usr/local/sbin + ) + list(TRANSFORM path_search APPEND "/clang-[0-9]*") + file(GLOB clang_versions ${path_search}) + if(clang_versions) + # Find and pick the highest installed version + list(SORT clang_versions COMPARE NATURAL) + list(GET clang_versions 0 clang_higher_version_path) + string(REGEX MATCH "clang-([0-9.]*)" clang_higher_version ${clang_higher_version_path}) + if(CMAKE_MATCH_1) + set(CMAKE_C_COMPILER clang-${CMAKE_MATCH_1}) + set(CMAKE_CXX_COMPILER clang++-${CMAKE_MATCH_1}) + else() + message(FATAL_ERROR "Clang not found, please install clang") + endif() + else() + message(FATAL_ERROR "Clang not found, please install clang") + endif() +endif() diff --git a/Templates/MinimalProject/template.json b/Templates/MinimalProject/template.json index 21608e9204..4260e71527 100644 --- a/Templates/MinimalProject/template.json +++ b/Templates/MinimalProject/template.json @@ -173,8 +173,20 @@ "isOptional": false }, { - "file": "EngineFinder.cmake", - "origin": "EngineFinder.cmake", + "file": "cmake/EngineFinder.cmake", + "origin": "cmake/EngineFinder.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "cmake/CompilerSettings.cmake", + "origin": "cmake/CompilerSettings.cmake", + "isTemplated": false, + "isOptional": false + }, + { + "file": "cmake/Platform/Linux/CompilerSettings.cmake", + "origin": "cmake/Platform/Linux/CompilerSettings.cmake", "isTemplated": false, "isOptional": false }, diff --git a/cmake/CompilerSettings.cmake b/cmake/CompilerSettings.cmake new file mode 100644 index 0000000000..cf6614e4a5 --- /dev/null +++ b/cmake/CompilerSettings.cmake @@ -0,0 +1,13 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +# File to tweak compiler settings before compiler detection happens (before project() is called) +# We dont have PAL enabled at this point, so we can only use pure-CMake variables +if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Linux") + include(cmake/Platform/${CMAKE_HOST_SYSTEM_NAME}/CompilerSettings.cmake) +endif() diff --git a/cmake/Platform/Linux/CompilerSettings.cmake b/cmake/Platform/Linux/CompilerSettings.cmake new file mode 100644 index 0000000000..9bb629c53b --- /dev/null +++ b/cmake/Platform/Linux/CompilerSettings.cmake @@ -0,0 +1,34 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +if(NOT CMAKE_C_COMPILER AND NOT CMAKE_CXX_COMPILER AND NOT "$ENV{CC}" AND NOT "$ENV{CXX}") + set(path_search + /bin + /usr/bin + /usr/local/bin + /sbin + /usr/sbin + /usr/local/sbin + ) + list(TRANSFORM path_search APPEND "/clang-[0-9]*") + file(GLOB clang_versions ${path_search}) + if(clang_versions) + # Find and pick the highest installed version + list(SORT clang_versions COMPARE NATURAL) + list(GET clang_versions 0 clang_higher_version_path) + string(REGEX MATCH "clang-([0-9.]*)" clang_higher_version ${clang_higher_version_path}) + if(CMAKE_MATCH_1) + set(CMAKE_C_COMPILER clang-${CMAKE_MATCH_1}) + set(CMAKE_CXX_COMPILER clang++-${CMAKE_MATCH_1}) + else() + message(FATAL_ERROR "Clang not found, please install clang") + endif() + else() + message(FATAL_ERROR "Clang not found, please install clang") + endif() +endif() From 92f1883caef8c53c08c905c9432056b3ad25041c Mon Sep 17 00:00:00 2001 From: brianherrera Date: Tue, 9 Nov 2021 10:02:11 -0800 Subject: [PATCH 147/177] Remove timeout in the mount step We no longer need a timeout here. A timeout mechanism was added to the mount script to raise an exeception if the EBS volume is not mounted in the configured timeframe. This also causes a bug with the retry mechanism where Jenkins will hit this timeout in the event the node goes offline during the setup stage instead of raising an exception. Signed-off-by: brianherrera --- scripts/build/Jenkins/Jenkinsfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index 8eddb61c33..beb4a21620 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -281,9 +281,7 @@ def HandleDriveMount(String snapshot, String repositoryName, String projectName, if(recreateVolume) { palSh("${pythonCmd} ${INCREMENTAL_BUILD_SCRIPT_PATH} --action delete --repository_name ${repositoryName} --project ${projectName} --pipeline ${pipeline} --branch ${branchName} --platform ${platform} --build_type ${buildType}", 'Deleting volume', winSlashReplacement=false) } - timeout(5) { - palSh("${pythonCmd} ${INCREMENTAL_BUILD_SCRIPT_PATH} --action mount --snapshot ${snapshot} --repository_name ${repositoryName} --project ${projectName} --pipeline ${pipeline} --branch ${branchName} --platform ${platform} --build_type ${buildType}", 'Mounting volume', winSlashReplacement=false) - } + palSh("${pythonCmd} ${INCREMENTAL_BUILD_SCRIPT_PATH} --action mount --snapshot ${snapshot} --repository_name ${repositoryName} --project ${projectName} --pipeline ${pipeline} --branch ${branchName} --platform ${platform} --build_type ${buildType}", 'Mounting volume', winSlashReplacement=false) if(env.IS_UNIX) { sh label: 'Setting volume\'s ownership', From aa229976f37bb836c5398a8fc62f3574d9f10b3a Mon Sep 17 00:00:00 2001 From: Roman <69218254+amzn-rhhong@users.noreply.github.com> Date: Tue, 9 Nov 2021 13:42:56 -0800 Subject: [PATCH 148/177] Render Colliders (#5434) * Render Colliders Signed-off-by: rhhong * CR feedback Signed-off-by: rhhong --- .../Source/Viewport/RenderViewportWidget.cpp | 12 + .../Tools/EMStudio/AnimViewportRenderer.cpp | 39 ++-- .../Tools/EMStudio/AnimViewportRenderer.h | 4 +- .../Tools/EMStudio/AnimViewportToolBar.cpp | 6 + .../Tools/EMStudio/AnimViewportWidget.cpp | 19 ++ .../Code/Tools/EMStudio/AnimViewportWidget.h | 8 + .../EMStudioSDK/Source/EMStudioPlugin.h | 11 +- .../Source/RenderPlugin/RenderOptions.cpp | 10 + .../Source/RenderPlugin/RenderWidget.cpp | 2 +- .../Source/RenderPlugin/ViewportPluginBus.h | 23 ++ .../MotionWindow/MotionWindowPlugin.cpp | 2 +- .../Source/MotionWindow/MotionWindowPlugin.h | 2 +- .../Source/Editor/ColliderContainerWidget.cpp | 131 ++++++++++- .../Source/Editor/ColliderContainerWidget.h | 18 +- .../Cloth/ClothJointInspectorPlugin.cpp | 17 +- .../Plugins/Cloth/ClothJointInspectorPlugin.h | 3 +- .../HitDetectionJointInspectorPlugin.cpp | 20 +- .../HitDetectionJointInspectorPlugin.h | 3 +- .../Ragdoll/RagdollNodeInspectorPlugin.cpp | 215 +++++++++++++++++- .../Ragdoll/RagdollNodeInspectorPlugin.h | 29 ++- .../SimulatedObject/SimulatedObjectWidget.cpp | 138 +++++++++-- .../SimulatedObject/SimulatedObjectWidget.h | 5 +- .../Rendering/RenderActorSettings.h | 10 + 23 files changed, 655 insertions(+), 72 deletions(-) create mode 100644 Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/ViewportPluginBus.h diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp index 9672abfd99..bed4778e8e 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/RenderViewportWidget.cpp @@ -137,6 +137,18 @@ namespace AtomToolsFramework m_viewportContext->SetRenderScene(nullptr); return; } + + // Check if the scene already has an atom scene attached. In this case we don't need to create a new atom scene. + if (auto existingScene = scene->FindSubsystem()) + { + m_viewportContext->SetRenderScene(*existingScene); + if (auto auxGeomFP = existingScene->get()->GetFeatureProcessor()) + { + m_auxGeom = auxGeomFP->GetOrCreateDrawQueueForView(m_defaultCamera.get()); + } + return; + } + AZ::RPI::ScenePtr atomScene; auto initializeScene = [&](AZ::Render::Bootstrap::Request* bootstrapRequests) { diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp index 4c17eaf06a..a32af31d6d 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.cpp @@ -24,8 +24,6 @@ #include #include -#include -#include #include #include #include @@ -122,22 +120,15 @@ namespace EMStudio const AZ::Render::LightingPreset* preset = lightingPresetAsset->GetDataAs(); SetLightingPreset(preset); - // Create grid + // Create the ground plane AzFramework::EntityContextRequestBus::EventResult( - m_gridEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportGrid"); - AZ_Assert(m_gridEntity != nullptr, "Failed to create grid entity."); + m_groundEntity, entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "ViewportModel"); + AZ_Assert(m_groundEntity != nullptr, "Failed to create model entity."); - AZ::Render::GridComponentConfig gridConfig; - gridConfig.m_secondarySpacing = m_renderOptions->GetGridUnitSize(); - gridConfig.m_axisColor = m_renderOptions->GetMainAxisColor(); - gridConfig.m_primaryColor = m_renderOptions->GetGridColor(); - gridConfig.m_secondaryColor = m_renderOptions->GetSubStepColor(); - auto gridComponent = m_gridEntity->CreateComponent(AZ::Render::GridComponentTypeId); - gridComponent->SetConfiguration(gridConfig); - - m_gridEntity->CreateComponent(azrtti_typeid()); - m_gridEntity->Init(); - m_gridEntity->Activate(); + m_groundEntity->CreateComponent(AZ::Render::MeshComponentTypeId); + m_groundEntity->CreateComponent(AZ::Render::MaterialComponentTypeId); + m_groundEntity->CreateComponent(azrtti_typeid()); + m_groundEntity->Activate(); Reinit(); } @@ -147,7 +138,7 @@ namespace EMStudio // Destroy all the entity we created. m_entityContext->DestroyEntity(m_iblEntity); m_entityContext->DestroyEntity(m_postProcessEntity); - m_entityContext->DestroyEntity(m_gridEntity); + m_entityContext->DestroyEntity(m_groundEntity); for (AZ::Entity* entity : m_actorEntities) { m_entityContext->DestroyEntity(entity); @@ -220,6 +211,11 @@ namespace EMStudio } } + AZStd::shared_ptr AnimViewportRenderer::GetFrameworkScene() const + { + return m_frameworkScene; + } + void AnimViewportRenderer::ResetEnvironment() { // Reset environment @@ -229,6 +225,15 @@ namespace EMStudio const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateIdentity(); auto skyBoxFeatureProcessorInterface = m_scene->GetFeatureProcessor(); skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix); + + // Reset ground entity + AZ::Transform groundTransform = AZ::Transform::CreateIdentity(); + AZ::TransformBus::Event(m_groundEntity->GetId(), &AZ::TransformBus::Events::SetLocalTM, groundTransform); + + auto modelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath( + "objects/groudplane/groundplane_512x512m.azmodel", AZ::RPI::AssetUtils::TraceLevel::Assert); + AZ::Render::MeshComponentRequestBus::Event( + m_groundEntity->GetId(), &AZ::Render::MeshComponentRequestBus::Events::SetModelAsset, modelAsset); } void AnimViewportRenderer::ReinitActorEntities() diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h index df69856355..27e62bb130 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportRenderer.h @@ -56,6 +56,8 @@ namespace EMStudio void UpdateActorRenderFlag(EMotionFX::ActorRenderFlagBitset renderFlags); + AZStd::shared_ptr GetFrameworkScene() const; + private: // This function resets the light, camera and other environment settings. @@ -83,7 +85,7 @@ namespace EMStudio AZ::Entity* m_postProcessEntity = nullptr; AZ::Entity* m_iblEntity = nullptr; - AZ::Entity* m_gridEntity = nullptr; + AZ::Entity* m_groundEntity = nullptr; AZStd::vector m_actorEntities; const RenderOptions* m_renderOptions; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp index 50cd088f5d..3b53103537 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp @@ -48,6 +48,12 @@ namespace EMStudio CreateViewOptionEntry(contextMenu, "Joint Orientations", EMotionFX::ActorRenderFlag::RENDER_NODEORIENTATION); CreateViewOptionEntry(contextMenu, "Actor Bind Pose", EMotionFX::ActorRenderFlag::RENDER_ACTORBINDPOSE); contextMenu->addSeparator(); + CreateViewOptionEntry(contextMenu, "Hit Detection Colliders", EMotionFX::ActorRenderFlag::RENDER_HITDETECTION_COLLIDERS); + CreateViewOptionEntry(contextMenu, "Ragdoll Colliders", EMotionFX::ActorRenderFlag::RENDER_RAGDOLL_COLLIDERS); + CreateViewOptionEntry(contextMenu, "Ragdoll Joint Limits", EMotionFX::ActorRenderFlag::RENDER_RAGDOLL_JOINTLIMITS); + CreateViewOptionEntry(contextMenu, "Cloth Colliders", EMotionFX::ActorRenderFlag::RENDER_CLOTH_COLLIDERS); + CreateViewOptionEntry(contextMenu, "Simulated Object Colliders", EMotionFX::ActorRenderFlag::RENDER_SIMULATEDOBJECT_COLLIDERS); + CreateViewOptionEntry(contextMenu, "Simulated Joints", EMotionFX::ActorRenderFlag::RENDER_SIMULATEJOINTS); } // Add the camera button diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp index cf7341e9ab..cd362bdaf5 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.cpp @@ -35,6 +35,7 @@ namespace EMStudio setStyleSheet(QString::fromUtf8("")); m_renderer = AZStd::make_unique(GetViewportContext(), m_plugin->GetRenderOptions()); + SetScene(m_renderer->GetFrameworkScene(), false); LoadRenderFlags(); SetupCameras(); @@ -42,11 +43,13 @@ namespace EMStudio Reinit(); AnimViewportRequestBus::Handler::BusConnect(); + ViewportPluginRequestBus::Handler::BusConnect(); } AnimViewportWidget::~AnimViewportWidget() { SaveRenderFlags(); + ViewportPluginRequestBus::Handler::BusDisconnect(); AnimViewportRequestBus::Handler::BusDisconnect(); } @@ -173,6 +176,7 @@ namespace EMStudio { RenderViewportWidget::OnTick(deltaTime, time); CalculateCameraProjection(); + RenderCustomPluginData(); } void AnimViewportWidget::CalculateCameraProjection() @@ -191,6 +195,16 @@ namespace EMStudio viewportContext->GetDefaultView()->SetViewToClipMatrix(viewToClipMatrix); } + void AnimViewportWidget::RenderCustomPluginData() + { + const size_t numPlugins = GetPluginManager()->GetNumActivePlugins(); + for (size_t i = 0; i < numPlugins; ++i) + { + EMStudioPlugin* plugin = GetPluginManager()->GetActivePlugin(i); + plugin->Render(m_renderFlags); + } + } + void AnimViewportWidget::ToggleRenderFlag(EMotionFX::ActorRenderFlag flag) { m_renderFlags[flag] = !m_renderFlags[flag]; @@ -224,4 +238,9 @@ namespace EMStudio settings.setValue(name, (bool)m_renderFlags[i]); } } + + AZ::s32 AnimViewportWidget::GetViewportId() const + { + return GetViewportContext()->GetId(); + } } // namespace EMStudio diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h index 7a8ae2cf52..069aebb73e 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportWidget.h @@ -10,6 +10,8 @@ #include #include #include + +#include #include #include @@ -21,6 +23,7 @@ namespace EMStudio class AnimViewportWidget : public AtomToolsFramework::RenderViewportWidget , private AnimViewportRequestBus::Handler + , private ViewportPluginRequestBus::Handler { public: AnimViewportWidget(AtomRenderPlugin* parentPlugin); @@ -34,6 +37,8 @@ namespace EMStudio void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; void CalculateCameraProjection(); + void RenderCustomPluginData(); + void SetupCameras(); void SetupCameraController(); @@ -45,6 +50,9 @@ namespace EMStudio void SetCameraViewMode(CameraViewMode mode); void ToggleRenderFlag(EMotionFX::ActorRenderFlag flag); + // ViewportPluginRequestBus::Handler overrides + AZ::s32 GetViewportId() const; + static constexpr float CameraDistance = 2.0f; AtomRenderPlugin* m_plugin; diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioPlugin.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioPlugin.h index 0de4ab946c..8ae84b6e49 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioPlugin.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioPlugin.h @@ -15,6 +15,7 @@ #include #include #include +#include #include "EMStudioConfig.h" #include #include @@ -92,7 +93,15 @@ namespace EMStudio uint32 m_screenHeight; }; - virtual void Render(RenderPlugin* renderPlugin, RenderInfo* renderInfo) { MCORE_UNUSED(renderPlugin); MCORE_UNUSED(renderInfo); } + //! Deprecated: LegacyRender will call EMotionFX::DebugDraw that tied to OpenGL render. + //! It will be removed after OpenGLPlugin and GLWidget is gone. + virtual void LegacyRender(RenderPlugin* renderPlugin, RenderInfo* renderInfo) { MCORE_UNUSED(renderPlugin); MCORE_UNUSED(renderInfo); } + + //! Render function will call atom auxGeom internally to render. This is the replacement for LegacyRender function. + virtual void Render(EMotionFX::ActorRenderFlagBitset renderFlags) + { + AZ_UNUSED(renderFlags); + }; virtual PluginOptions* GetOptions() { return nullptr; } diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp index 496781d316..d1e3dc5ebc 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp @@ -1072,6 +1072,16 @@ namespace EMStudio settings.m_staticAABBColor = m_staticAABBColor; settings.m_skeletonColor = m_skeletonColor; settings.m_lineSkeletonColor = m_lineSkeletonColor; + + settings.m_hitDetectionColliderColor = m_hitDetectionColliderColor; + settings.m_selectedHitDetectionColliderColor = m_selectedHitDetectionColliderColor; + settings.m_ragdollColliderColor = m_ragdollColliderColor; + settings.m_selectedRagdollColliderColor = m_selectedRagdollColliderColor; + settings.m_violatedJointLimitColor = m_violatedJointLimitColor; + settings.m_clothColliderColor = m_clothColliderColor; + settings.m_selectedClothColliderColor = m_selectedClothColliderColor; + settings.m_simulatedObjectColliderColor = m_simulatedObjectColliderColor; + settings.m_selectedSimulatedObjectColliderColor = m_selectedSimulatedObjectColliderColor; } void RenderOptions::OnGridUnitSizeChangedCallback() const diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.cpp index 429a0bffae..4a02e75fbd 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderWidget.cpp @@ -1054,7 +1054,7 @@ namespace EMStudio EMStudioPlugin* plugin = GetPluginManager()->GetActivePlugin(i); EMStudioPlugin::RenderInfo renderInfo(renderUtil, m_camera, m_width, m_height); - plugin->Render(m_plugin, &renderInfo); + plugin->LegacyRender(m_plugin, &renderInfo); } RenderDebugDraw(); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/ViewportPluginBus.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/ViewportPluginBus.h new file mode 100644 index 0000000000..27953fac4b --- /dev/null +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/ViewportPluginBus.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +namespace EMStudio +{ + class ViewportPluginRequests + : public AZ::EBusTraits + { + public: + virtual AZ::s32 GetViewportId() const = 0; + }; + + using ViewportPluginRequestBus = AZ::EBus; +} diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MotionWindow/MotionWindowPlugin.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MotionWindow/MotionWindowPlugin.cpp index 87c6a601aa..912947e302 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MotionWindow/MotionWindowPlugin.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MotionWindow/MotionWindowPlugin.cpp @@ -667,7 +667,7 @@ namespace EMStudio } - void MotionWindowPlugin::Render(RenderPlugin* renderPlugin, EMStudioPlugin::RenderInfo* renderInfo) + void MotionWindowPlugin::LegacyRender(RenderPlugin* renderPlugin, EMStudioPlugin::RenderInfo* renderInfo) { MCommon::RenderUtil* renderUtil = renderInfo->m_renderUtil; diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MotionWindow/MotionWindowPlugin.h b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MotionWindow/MotionWindowPlugin.h index 7f4ebc3e78..84c2ccee9b 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MotionWindow/MotionWindowPlugin.h +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MotionWindow/MotionWindowPlugin.h @@ -61,7 +61,7 @@ namespace EMStudio bool Init() override; EMStudioPlugin* Clone() override; - void Render(RenderPlugin* renderPlugin, EMStudioPlugin::RenderInfo* renderInfo) override; + void LegacyRender(RenderPlugin* renderPlugin, EMStudioPlugin::RenderInfo* renderInfo) override; void ReInit(); diff --git a/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.cpp index fea4f23c6a..13696298dc 100644 --- a/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -610,7 +612,7 @@ namespace EMotionFX return QWidget::sizeHint() + QSize(0, s_layoutSpacing); } - void ColliderContainerWidget::RenderColliders(const AzPhysics::ShapeColliderPairList& colliders, + void ColliderContainerWidget::LegacyRenderColliders(const AzPhysics::ShapeColliderPairList& colliders, const ActorInstance* actorInstance, const Node* node, EMStudio::EMStudioPlugin::RenderInfo* renderInfo, @@ -630,7 +632,6 @@ namespace EMotionFX const Transform colliderOffsetTransform(collider.first->m_position, collider.first->m_rotation); const Transform& actorInstanceGlobalTransform = actorInstance->GetWorldSpaceTransform(); const Transform& emfxNodeGlobalTransform = actorInstance->GetTransformData()->GetCurrentPose()->GetModelSpaceTransform(nodeIndex); - const Transform emfxColliderGlobalTransformNoScale = colliderOffsetTransform * emfxNodeGlobalTransform * actorInstanceGlobalTransform; const AZ::TypeId colliderType = collider.second->RTTI_GetType(); @@ -638,7 +639,7 @@ namespace EMotionFX { Physics::SphereShapeConfiguration* sphere = static_cast(collider.second.get()); - // LY Physics scaling rules: The maximum component from the node scale will be multiplied by the radius of the sphere. + // O3DE Physics scaling rules: The maximum component from the node scale will be multiplied by the radius of the sphere. const float radius = sphere->m_radius * MCore::Max3(static_cast(worldScale.GetX()), static_cast(worldScale.GetY()), static_cast(worldScale.GetZ())); renderUtil->RenderWireframeSphere(radius, emfxColliderGlobalTransformNoScale.ToAZTransform(), colliderColor); @@ -647,7 +648,7 @@ namespace EMotionFX { Physics::CapsuleShapeConfiguration* capsule = static_cast(collider.second.get()); - // LY Physics scaling rules: The maximum of the X/Y scale components of the node scale will be multiplied by the radius of the capsule. The Z component of the entity scale will be multiplied by the height of the capsule. + // O3DE Physics scaling rules: The maximum of the X/Y scale components of the node scale will be multiplied by the radius of the capsule. The Z component of the entity scale will be multiplied by the height of the capsule. const float radius = capsule->m_radius * MCore::Max(static_cast(worldScale.GetX()), static_cast(worldScale.GetY())); const float height = capsule->m_height * static_cast(worldScale.GetZ()); @@ -657,7 +658,7 @@ namespace EMotionFX { Physics::BoxShapeConfiguration* box = static_cast(collider.second.get()); - // LY Physics scaling rules: Each component of the box dimensions will be scaled by the node's world scale. + // O3DE Physics scaling rules: Each component of the box dimensions will be scaled by the node's world scale. AZ::Vector3 dimensions = box->m_dimensions; dimensions *= worldScale; @@ -666,7 +667,8 @@ namespace EMotionFX } } - void ColliderContainerWidget::RenderColliders(PhysicsSetup::ColliderConfigType colliderConfigType, + void ColliderContainerWidget::LegacyRenderColliders( + PhysicsSetup::ColliderConfigType colliderConfigType, const MCore::RGBAColor& defaultColor, const MCore::RGBAColor& selectedColor, EMStudio::RenderPlugin* renderPlugin, @@ -701,7 +703,7 @@ namespace EMotionFX { const bool jointSelected = selectedJointIndices.empty() || selectedJointIndices.find(joint->GetNodeIndex()) != selectedJointIndices.end(); const AzPhysics::ShapeColliderPairList& colliders = nodeConfig.m_shapes; - RenderColliders(colliders, actorInstance, joint, renderInfo, jointSelected ? selectedColor : defaultColor); + LegacyRenderColliders(colliders, actorInstance, joint, renderInfo, jointSelected ? selectedColor : defaultColor); } } } @@ -711,6 +713,121 @@ namespace EMotionFX renderUtil->EnableLighting(oldLightingEnabled); } + void ColliderContainerWidget::RenderColliders( + const AzPhysics::ShapeColliderPairList& colliders, + const ActorInstance* actorInstance, + const Node* node, + const AZ::Color& colliderColor) + { + const size_t nodeIndex = node->GetNodeIndex(); + + for (const auto& collider : colliders) + { +#ifndef EMFX_SCALE_DISABLED + const AZ::Vector3& worldScale = actorInstance->GetTransformData()->GetCurrentPose()->GetModelSpaceTransform(nodeIndex).m_scale; +#else + const AZ::Vector3 worldScale = AZ::Vector3::CreateOne(); +#endif + + const Transform colliderOffsetTransform(collider.first->m_position, collider.first->m_rotation); + const Transform& actorInstanceGlobalTransform = actorInstance->GetWorldSpaceTransform(); + const Transform& emfxNodeGlobalTransform = + actorInstance->GetTransformData()->GetCurrentPose()->GetModelSpaceTransform(nodeIndex); + const Transform emfxColliderGlobalTransformNoScale = + colliderOffsetTransform * emfxNodeGlobalTransform * actorInstanceGlobalTransform; + + AZ::s32 viewportId = -1; + EMStudio::ViewportPluginRequestBus::BroadcastResult(viewportId, &EMStudio::ViewportPluginRequestBus::Events::GetViewportId); + AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; + AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, viewportId); + AzFramework::DebugDisplayRequests* debugDisplay = nullptr; + debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); + if (!debugDisplay) + { + return; + } + + const AZ::TypeId colliderType = collider.second->RTTI_GetType(); + if (colliderType == azrtti_typeid()) + { + Physics::SphereShapeConfiguration* sphere = static_cast(collider.second.get()); + + // O3DE Physics scaling rules: The maximum component from the node scale will be multiplied by the radius of the sphere. + const float radius = sphere->m_radius * + MCore::Max3(static_cast(worldScale.GetX()), static_cast(worldScale.GetY()), + static_cast(worldScale.GetZ())); + + debugDisplay->DepthTestOff(); + debugDisplay->SetColor(colliderColor); + debugDisplay->DrawWireSphere(emfxColliderGlobalTransformNoScale.m_position, radius); + } + else if (colliderType == azrtti_typeid()) + { + Physics::CapsuleShapeConfiguration* capsule = static_cast(collider.second.get()); + + // O3DE Physics scaling rules: The maximum of the X/Y scale components of the node scale will be multiplied by the radius of + // the capsule. The Z component of the entity scale will be multiplied by the height of the capsule. + const float radius = + capsule->m_radius * MCore::Max(static_cast(worldScale.GetX()), static_cast(worldScale.GetY())); + const float height = capsule->m_height * static_cast(worldScale.GetZ()); + + debugDisplay->DepthTestOff(); + debugDisplay->SetColor(colliderColor); + debugDisplay->DrawWireCapsule( + emfxColliderGlobalTransformNoScale.m_position, emfxColliderGlobalTransformNoScale.ToAZTransform().GetBasisZ(), radius, height); + } + else if (colliderType == azrtti_typeid()) + { + Physics::BoxShapeConfiguration* box = static_cast(collider.second.get()); + + // O3DE Physics scaling rules: Each component of the box dimensions will be scaled by the node's world scale. + AZ::Vector3 dimensions = box->m_dimensions; + dimensions *= worldScale; + + debugDisplay->DepthTestOff(); + debugDisplay->SetColor(colliderColor); + debugDisplay->DrawWireBox( + emfxColliderGlobalTransformNoScale.m_position, emfxColliderGlobalTransformNoScale.m_position + dimensions); + } + } + } + + void ColliderContainerWidget::RenderColliders(PhysicsSetup::ColliderConfigType colliderConfigType, + const AZ::Color& defaultColor, const AZ::Color& selectedColor) + { + if (colliderConfigType == PhysicsSetup::Unknown) + { + return; + } + + const AZStd::unordered_set& selectedJointIndices = EMStudio::GetManager()->GetSelectedJointIndices(); + + const ActorManager* actorManager = GetEMotionFX().GetActorManager(); + const size_t actorInstanceCount = actorManager->GetNumActorInstances(); + for (size_t i = 0; i < actorInstanceCount; ++i) + { + const ActorInstance* actorInstance = actorManager->GetActorInstance(i); + const Actor* actor = actorInstance->GetActor(); + const AZStd::shared_ptr& physicsSetup = actor->GetPhysicsSetup(); + const Physics::CharacterColliderConfiguration* colliderConfig = physicsSetup->GetColliderConfigByType(colliderConfigType); + + if (colliderConfig) + { + for (const Physics::CharacterColliderNodeConfiguration& nodeConfig : colliderConfig->m_nodes) + { + const Node* joint = actor->GetSkeleton()->FindNodeByName(nodeConfig.m_name.c_str()); + if (joint) + { + const bool jointSelected = + selectedJointIndices.empty() || selectedJointIndices.find(joint->GetNodeIndex()) != selectedJointIndices.end(); + const AzPhysics::ShapeColliderPairList& colliders = nodeConfig.m_shapes; + RenderColliders(colliders, actorInstance, joint, jointSelected ? selectedColor : defaultColor); + } + } + } + } + } + /////////////////////////////////////////////////////////////////////////// ColliderContainerWidget::ColliderEditedCallback::ColliderEditedCallback(ColliderContainerWidget* parent, bool executePreUndo, bool executePreCommand) diff --git a/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.h b/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.h index f4641e4ad6..82e6d151d5 100644 --- a/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.h +++ b/Gems/EMotionFX/Code/Source/Editor/ColliderContainerWidget.h @@ -152,18 +152,32 @@ namespace EMotionFX * @param[in] renderInfo Needed to access the render util. * @param[in] colliderColor The collider color. */ - static void RenderColliders(const AzPhysics::ShapeColliderPairList& colliders, + //! Deprecated: remove after openglrenderwidget is gone. + static void LegacyRenderColliders(const AzPhysics::ShapeColliderPairList& colliders, const ActorInstance* actorInstance, const Node* node, EMStudio::EMStudioPlugin::RenderInfo* renderInfo, const MCore::RGBAColor& colliderColor); - static void RenderColliders(PhysicsSetup::ColliderConfigType colliderConfigType, + //! Deprecated: remove after openglrenderwidget is gone. + static void LegacyRenderColliders( + PhysicsSetup::ColliderConfigType colliderConfigType, const MCore::RGBAColor& defaultColor, const MCore::RGBAColor& selectedColor, EMStudio::RenderPlugin* renderPlugin, EMStudio::EMStudioPlugin::RenderInfo* renderInfo); + static void RenderColliders( + const AzPhysics::ShapeColliderPairList& colliders, + const ActorInstance* actorInstance, + const Node* node, + const AZ::Color& colliderColor); + + static void RenderColliders( + PhysicsSetup::ColliderConfigType colliderConfigType, + const AZ::Color& defaultColor, + const AZ::Color& selectedColor); + static int s_layoutSpacing; signals: diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.cpp index e1a58f1f44..161cc2ac95 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -172,7 +173,7 @@ namespace EMotionFX ColliderHelpers::ClearColliders(selectedRowIndices, PhysicsSetup::Cloth); } - void ClothJointInspectorPlugin::Render(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) + void ClothJointInspectorPlugin::LegacyRender(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) { EMStudio::RenderViewWidget* activeViewWidget = renderPlugin->GetActiveViewWidget(); if (!activeViewWidget) @@ -188,10 +189,22 @@ namespace EMotionFX const EMStudio::RenderOptions* renderOptions = renderPlugin->GetRenderOptions(); - ColliderContainerWidget::RenderColliders(PhysicsSetup::Cloth, + ColliderContainerWidget::LegacyRenderColliders(PhysicsSetup::Cloth, renderOptions->GetClothColliderColor(), renderOptions->GetSelectedClothColliderColor(), renderPlugin, renderInfo); } + + void ClothJointInspectorPlugin::Render(EMotionFX::ActorRenderFlagBitset renderFlags) + { + const bool renderColliders = renderFlags[RENDER_CLOTH_COLLIDERS]; + if (!renderColliders) + { + return; + } + + const AZ::Render::RenderActorSettings& settings = EMotionFX::GetRenderActorSettings(); + ColliderContainerWidget::RenderColliders(PhysicsSetup::Cloth, settings.m_clothColliderColor, settings.m_selectedClothColliderColor); + } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.h b/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.h index 6a920fa10d..c1b3e9ad13 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.h +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Cloth/ClothJointInspectorPlugin.h @@ -47,7 +47,8 @@ namespace EMotionFX // SkeletonOutlinerNotificationBus overrides void OnContextMenu(QMenu* menu, const QModelIndexList& selectedRowIndices) override; - void Render(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) override; + void LegacyRender(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) override; + void Render(EMotionFX::ActorRenderFlagBitset renderFlags) override; static bool IsJointInCloth(const QModelIndex& index); public slots: diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.cpp index b7cf3ea13d..0f0eb89bfc 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -157,7 +158,7 @@ namespace EMotionFX ColliderHelpers::ClearColliders(selectedRowIndices, PhysicsSetup::HitDetection); } - void HitDetectionJointInspectorPlugin::Render(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) + void HitDetectionJointInspectorPlugin::LegacyRender(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) { EMStudio::RenderViewWidget* activeViewWidget = renderPlugin->GetActiveViewWidget(); if (!activeViewWidget) @@ -173,10 +174,25 @@ namespace EMotionFX const EMStudio::RenderOptions* renderOptions = renderPlugin->GetRenderOptions(); - ColliderContainerWidget::RenderColliders(PhysicsSetup::HitDetection, + ColliderContainerWidget::LegacyRenderColliders(PhysicsSetup::HitDetection, renderOptions->GetHitDetectionColliderColor(), renderOptions->GetSelectedHitDetectionColliderColor(), renderPlugin, renderInfo); } + + void HitDetectionJointInspectorPlugin::Render(EMotionFX::ActorRenderFlagBitset renderFlags) + { + const bool renderColliders = renderFlags[EMotionFX::ActorRenderFlag::RENDER_HITDETECTION_COLLIDERS]; + if (!renderColliders) + { + return; + } + + const AZ::Render::RenderActorSettings& settings = EMotionFX::GetRenderActorSettings(); + + ColliderContainerWidget::RenderColliders( + PhysicsSetup::HitDetection, settings.m_hitDetectionColliderColor, + settings.m_selectedHitDetectionColliderColor); + } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.h b/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.h index 5454fc56f9..4dc61f9b9e 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.h +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.h @@ -43,7 +43,8 @@ namespace EMotionFX // SkeletonOutlinerNotificationBus overrides void OnContextMenu(QMenu* menu, const QModelIndexList& selectedRowIndices) override; - void Render(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) override; + void LegacyRender(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) override; + void Render(EMotionFX::ActorRenderFlagBitset renderFlags) override; public slots: void OnAddCollider(); diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.cpp index cb5c525cec..e76cf42c44 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -17,12 +18,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -412,7 +415,7 @@ namespace EMotionFX } } - void RagdollNodeInspectorPlugin::Render(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) + void RagdollNodeInspectorPlugin::LegacyRender(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) { EMStudio::RenderViewWidget* activeViewWidget = renderPlugin->GetActiveViewWidget(); if (!activeViewWidget) @@ -435,14 +438,19 @@ namespace EMotionFX for (size_t i = 0; i < actorInstanceCount; ++i) { ActorInstance* actorInstance = GetActorManager().GetActorInstance(i); - RenderRagdoll(actorInstance, renderColliders, renderJointLimits, renderPlugin, renderInfo); + LegacyRenderRagdoll(actorInstance, renderColliders, renderJointLimits, renderPlugin, renderInfo); } renderUtil->RenderLines(); renderUtil->EnableLighting(oldLightingEnabled); } - void RagdollNodeInspectorPlugin::RenderRagdoll(ActorInstance* actorInstance, bool renderColliders, bool renderJointLimits, EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) + void RagdollNodeInspectorPlugin::LegacyRenderRagdoll( + ActorInstance* actorInstance, + bool renderColliders, + bool renderJointLimits, + EMStudio::RenderPlugin* renderPlugin, + RenderInfo* renderInfo) { const Actor* actor = actorInstance->GetActor(); const Skeleton* skeleton = actor->GetSkeleton(); @@ -495,11 +503,12 @@ namespace EMotionFX if (renderColliders) { - const Physics::CharacterColliderNodeConfiguration* colliderNodeConfig = colliderConfig.FindNodeConfigByName(joint->GetNameString()); + const Physics::CharacterColliderNodeConfiguration* colliderNodeConfig = + colliderConfig.FindNodeConfigByName(joint->GetNameString()); if (colliderNodeConfig) { const AzPhysics::ShapeColliderPairList& colliders = colliderNodeConfig->m_shapes; - ColliderContainerWidget::RenderColliders(colliders, actorInstance, joint, renderInfo, finalColor); + ColliderContainerWidget::LegacyRenderColliders(colliders, actorInstance, joint, renderInfo, finalColor); } } @@ -511,15 +520,15 @@ namespace EMotionFX const Node* ragdollParentNode = physicsSetup->FindRagdollParentNode(joint); if (ragdollParentNode) { - RenderJointLimit(*jointLimitConfig, actorInstance, joint, ragdollParentNode, renderPlugin, renderInfo, finalColor); - RenderJointFrame(*jointLimitConfig, actorInstance, joint, ragdollParentNode, renderInfo, finalColor); + LegacyRenderJointLimit(*jointLimitConfig, actorInstance, joint, ragdollParentNode, renderPlugin, renderInfo, finalColor); + LegacyRenderJointFrame(*jointLimitConfig, actorInstance, joint, ragdollParentNode, renderInfo, finalColor); } } } } } - void RagdollNodeInspectorPlugin::RenderJointLimit( + void RagdollNodeInspectorPlugin::LegacyRenderJointLimit( const AzPhysics::JointConfiguration& configuration, const ActorInstance* actorInstance, const Node* node, @@ -567,7 +576,7 @@ namespace EMotionFX } } - void RagdollNodeInspectorPlugin::RenderJointFrame( + void RagdollNodeInspectorPlugin::LegacyRenderJointFrame( const AzPhysics::JointConfiguration& configuration, const ActorInstance* actorInstance, const Node* node, @@ -585,4 +594,192 @@ namespace EMotionFX renderInfo->m_renderUtil->RenderArrow(0.1f, jointChildWorldSpaceTransformNoScale.m_position, MCore::GetRight(jointChildWorldSpaceTransformNoScale.ToAZTransform()), color); } + + void RagdollNodeInspectorPlugin::Render(EMotionFX::ActorRenderFlagBitset renderFlags) + { + const bool renderColliders = renderFlags[RENDER_RAGDOLL_COLLIDERS]; + const bool renderJointLimits = renderFlags[RENDER_RAGDOLL_JOINTLIMITS]; + if (!renderColliders && !renderJointLimits) + { + return; + } + + const size_t actorInstanceCount = GetActorManager().GetNumActorInstances(); + for (size_t i = 0; i < actorInstanceCount; ++i) + { + ActorInstance* actorInstance = GetActorManager().GetActorInstance(i); + RenderRagdoll(actorInstance, renderColliders, renderJointLimits); + } + } + + void RagdollNodeInspectorPlugin::RenderRagdoll( + ActorInstance* actorInstance, + bool renderColliders, + bool renderJointLimits) + { + const Actor* actor = actorInstance->GetActor(); + const Skeleton* skeleton = actor->GetSkeleton(); + const size_t numNodes = skeleton->GetNumNodes(); + const AZStd::shared_ptr& physicsSetup = actor->GetPhysicsSetup(); + const Physics::RagdollConfiguration& ragdollConfig = physicsSetup->GetRagdollConfig(); + const AZStd::vector& ragdollNodes = ragdollConfig.m_nodes; + const Physics::CharacterColliderConfiguration& colliderConfig = ragdollConfig.m_colliders; + const RagdollInstance* ragdollInstance = actorInstance->GetRagdollInstance(); + + const AZ::Render::RenderActorSettings& settings = EMotionFX::GetRenderActorSettings(); + const AZ::Color& violatedColor = settings.m_violatedJointLimitColor; + const AZ::Color& defaultColor = settings.m_ragdollColliderColor; + const AZ::Color& selectedColor = settings.m_selectedRagdollColliderColor; + + const AZStd::unordered_set& selectedJointIndices = EMStudio::GetManager()->GetSelectedJointIndices(); + + for (size_t nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex) + { + const Node* joint = skeleton->GetNode(nodeIndex); + const size_t jointIndex = joint->GetNodeIndex(); + + AZ::Outcome ragdollNodeIndex = AZ::Failure(); + if (ragdollInstance) + { + ragdollNodeIndex = ragdollInstance->GetRagdollNodeIndex(jointIndex); + } + else + { + ragdollNodeIndex = ragdollConfig.FindNodeConfigIndexByName(joint->GetNameString()); + } + + if (!ragdollNodeIndex.IsSuccess()) + { + continue; + } + + const bool jointSelected = selectedJointIndices.empty() || selectedJointIndices.find(jointIndex) != selectedJointIndices.end(); + + AZ::Color finalColor; + if (jointSelected) + { + finalColor = selectedColor; + } + else + { + finalColor = defaultColor; + } + + const Physics::RagdollNodeConfiguration& ragdollNode = ragdollNodes[ragdollNodeIndex.GetValue()]; + + if (renderColliders) + { + const Physics::CharacterColliderNodeConfiguration* colliderNodeConfig = + colliderConfig.FindNodeConfigByName(joint->GetNameString()); + if (colliderNodeConfig) + { + const AzPhysics::ShapeColliderPairList& colliders = colliderNodeConfig->m_shapes; + ColliderContainerWidget::RenderColliders(colliders, actorInstance, joint, finalColor); + } + } + + if (renderJointLimits && jointSelected) + { + const AZStd::shared_ptr& jointLimitConfig = ragdollNode.m_jointConfig; + if (jointLimitConfig) + { + const Node* ragdollParentNode = physicsSetup->FindRagdollParentNode(joint); + if (ragdollParentNode) + { + RenderJointLimit(*jointLimitConfig, actorInstance, joint, ragdollParentNode, finalColor, violatedColor); + RenderJointFrame(*jointLimitConfig, actorInstance, joint, ragdollParentNode, finalColor); + } + } + } + } + } + + void RagdollNodeInspectorPlugin::RenderJointLimit( + const AzPhysics::JointConfiguration& configuration, + const ActorInstance* actorInstance, + const Node* node, + const Node* parentNode, + const AZ::Color& regularColor, + const AZ::Color& violatedColor) + { + const size_t nodeIndex = node->GetNodeIndex(); + const size_t parentNodeIndex = parentNode->GetNodeIndex(); + const Transform& actorInstanceWorldTransform = actorInstance->GetWorldSpaceTransform(); + const Pose* currentPose = actorInstance->GetTransformData()->GetCurrentPose(); + const AZ::Quaternion& parentOrientation = currentPose->GetModelSpaceTransform(parentNodeIndex).m_rotation; + const AZ::Quaternion& childOrientation = currentPose->GetModelSpaceTransform(nodeIndex).m_rotation; + + m_vertexBuffer.clear(); + m_indexBuffer.clear(); + m_lineBuffer.clear(); + m_lineValidityBuffer.clear(); + if (auto* jointHelpers = AZ::Interface::Get()) + { + jointHelpers->GenerateJointLimitVisualizationData( + configuration, parentOrientation, childOrientation, s_scale, s_angularSubdivisions, s_radialSubdivisions, m_vertexBuffer, + m_indexBuffer, m_lineBuffer, m_lineValidityBuffer); + } + + Transform jointModelSpaceTransform = currentPose->GetModelSpaceTransform(parentNodeIndex); + jointModelSpaceTransform.m_position = currentPose->GetModelSpaceTransform(nodeIndex).m_position; + const Transform jointGlobalTransformNoScale = jointModelSpaceTransform * actorInstanceWorldTransform; + + const size_t numLineBufferEntries = m_lineBuffer.size(); + if (m_lineValidityBuffer.size() * 2 != numLineBufferEntries) + { + AZ_ErrorOnce("EMotionFX", false, "Unexpected buffer size in joint limit visualization for node %s", node->GetName()); + return; + } + + AZ::s32 viewportId = -1; + EMStudio::ViewportPluginRequestBus::BroadcastResult(viewportId, &EMStudio::ViewportPluginRequestBus::Events::GetViewportId); + AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; + AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, viewportId); + AzFramework::DebugDisplayRequests* debugDisplay = nullptr; + debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); + if (!debugDisplay) + { + return; + } + + for (size_t i = 0; i < numLineBufferEntries; i += 2) + { + const AZ::Color& lineColor = m_lineValidityBuffer[i / 2] ? regularColor : violatedColor; + debugDisplay->DrawLine( + jointGlobalTransformNoScale.TransformPoint(m_lineBuffer[i]), + jointGlobalTransformNoScale.TransformPoint(m_lineBuffer[i + 1]), lineColor.GetAsVector4(), lineColor.GetAsVector4() + ); + } + } + + void RagdollNodeInspectorPlugin::RenderJointFrame( + const AzPhysics::JointConfiguration& configuration, + const ActorInstance* actorInstance, + const Node* node, + const Node* parentNode, + const AZ::Color& color) + { + AZ_UNUSED(parentNode); + + const Transform& actorInstanceWorldSpaceTransform = actorInstance->GetWorldSpaceTransform(); + const Pose* currentPose = actorInstance->GetTransformData()->GetCurrentPose(); + const Transform childJointLocalSpaceTransform(AZ::Vector3::CreateZero(), configuration.m_childLocalRotation); + const Transform childModelSpaceTransform = + childJointLocalSpaceTransform * currentPose->GetModelSpaceTransform(node->GetNodeIndex()); + const Transform jointChildWorldSpaceTransformNoScale = (childModelSpaceTransform * actorInstanceWorldSpaceTransform); + AZ::Vector3 dir = jointChildWorldSpaceTransformNoScale.ToAZTransform().GetBasisX(); + + AZ::s32 viewportId = -1; + EMStudio::ViewportPluginRequestBus::BroadcastResult(viewportId, &EMStudio::ViewportPluginRequestBus::Events::GetViewportId); + AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; + AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, viewportId); + AzFramework::DebugDisplayRequests* debugDisplay = nullptr; + debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); + if (!debugDisplay) + { + return; + } + debugDisplay->SetColor(color); + debugDisplay->DrawArrow(jointChildWorldSpaceTransformNoScale.m_position, jointChildWorldSpaceTransformNoScale.m_position + dir, 0.1f); + } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h index 36a33c1cd3..d3b27da5ce 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h @@ -54,9 +54,10 @@ namespace EMotionFX static void AddCollider(const QModelIndexList& modelIndices, const AZ::TypeId& colliderType); static void CopyColliders(const QModelIndexList& modelIndices, PhysicsSetup::ColliderConfigType copyFrom); - void Render(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) override; - void RenderRagdoll(ActorInstance* actorInstance, bool renderColliders, bool renderJointLimits, EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo); - void RenderJointLimit( + //! Deprecated: All legacy render function is tied to openGL. Will be removed after openGLPlugin is completely removed. + void LegacyRender(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) override; + void LegacyRenderRagdoll(ActorInstance* actorInstance, bool renderColliders, bool renderJointLimits, EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo); + void LegacyRenderJointLimit( const AzPhysics::JointConfiguration& jointConfiguration, const ActorInstance* actorInstance, const Node* node, @@ -64,7 +65,7 @@ namespace EMotionFX EMStudio::RenderPlugin* renderPlugin, EMStudio::EMStudioPlugin::RenderInfo* renderInfo, const MCore::RGBAColor& color); - void RenderJointFrame( + void LegacyRenderJointFrame( const AzPhysics::JointConfiguration& jointConfiguration, const ActorInstance* actorInstance, const Node* node, @@ -72,6 +73,26 @@ namespace EMotionFX EMStudio::EMStudioPlugin::RenderInfo* renderInfo, const MCore::RGBAColor& color); + //! Those function replaces legacyRender function and calls atom auxGeom render internally. + void Render(EMotionFX::ActorRenderFlagBitset renderFlags) override; + void RenderRagdoll( + ActorInstance* actorInstance, + bool renderColliders, + bool renderJointLimits); + void RenderJointLimit( + const AzPhysics::JointConfiguration& jointConfiguration, + const ActorInstance* actorInstance, + const Node* node, + const Node* parentNode, + const AZ::Color& regularColor, + const AZ::Color& violatedColor); + void RenderJointFrame( + const AzPhysics::JointConfiguration& jointConfiguration, + const ActorInstance* actorInstance, + const Node* node, + const Node* parentNode, + const AZ::Color& color); + public slots: void OnAddToRagdoll(); void OnAddCollider(); diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.cpp b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.cpp index 48104707de..695603cecb 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.cpp +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -330,6 +333,11 @@ namespace EMotionFX const Actor* actor = selectedRowIndices[0].data(SkeletonModel::ROLE_ACTOR_POINTER).value(); const SimulatedObjectSetup* simulatedObjectSetup = actor->GetSimulatedObjectSetup().get(); + if (!simulatedObjectSetup) + { + AZ_Assert(false, "Expected a simulated object setup on the actor."); + return; + } AZStd::unordered_set addToCandidates; for (const QModelIndex& index : selectedRowIndices) @@ -477,7 +485,7 @@ namespace EMotionFX // -------------------------------------------------- Rendering ------------------------------------------------------------- - void SimulatedObjectWidget::Render(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) + void SimulatedObjectWidget::LegacyRender(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) { if (!m_actor || !m_actorInstance) { @@ -501,7 +509,92 @@ namespace EMotionFX ActorInstance* actorInstance = GetActorManager().GetActorInstance(actorInstanceIndex); const Actor* actor = actorInstance->GetActor(); const SimulatedObjectSetup* setup = actor->GetSimulatedObjectSetup().get(); - AZ_Assert(setup, "Expected a simulated object setup on the actor instance."); + if (!setup) + { + AZ_Assert(false, "Expected a simulated object setup on the actor instance."); + return; + } + + const size_t objectCount = setup->GetNumSimulatedObjects(); + for (size_t objectIndex = 0; objectIndex < objectCount; ++objectIndex) + { + const SimulatedObject* object = setup->GetSimulatedObject(objectIndex); + const size_t simulatedJointCount = object->GetNumSimulatedJoints(); + for (size_t simulatedJointIndex = 0; simulatedJointIndex < simulatedJointCount; ++simulatedJointIndex) + { + const SimulatedJoint* simulatedJoint = object->GetSimulatedJoint(simulatedJointIndex); + const size_t skeletonJointIndex = simulatedJoint->GetSkeletonJointIndex(); + if (selectedJointIndices.find(skeletonJointIndex) != selectedJointIndices.end()) + { + LegacyRenderJointRadius(simulatedJoint, actorInstance, AZ::Color(1.0f, 0.0f, 1.0f, 1.0f)); + } + } + } + } + } + + const bool renderColliders = activeViewWidget->GetRenderFlag(EMStudio::RenderViewWidget::RENDER_SIMULATEDOBJECT_COLLIDERS); + if (renderColliders) + { + const EMStudio::RenderOptions* renderOptions = renderPlugin->GetRenderOptions(); + ColliderContainerWidget::LegacyRenderColliders(PhysicsSetup::SimulatedObjectCollider, + renderOptions->GetSimulatedObjectColliderColor(), + renderOptions->GetSelectedSimulatedObjectColliderColor(), + renderPlugin, + renderInfo); + } + } + + void SimulatedObjectWidget::LegacyRenderJointRadius(const SimulatedJoint* joint, ActorInstance* actorInstance, const AZ::Color& color) + { +#ifndef EMFX_SCALE_DISABLED + const float scale = actorInstance->GetWorldSpaceTransform().m_scale.GetX(); +#else + const float scale = 1.0f; +#endif + + const float radius = joint->GetCollisionRadius() * scale; + if (radius <= AZ::Constants::FloatEpsilon) + { + return; + } + + AZ_Assert(joint->GetSkeletonJointIndex() != InvalidIndex, "Expected skeletal joint index to be valid."); + const EMotionFX::Transform jointTransform = + actorInstance->GetTransformData()->GetCurrentPose()->GetWorldSpaceTransform(joint->GetSkeletonJointIndex()); + + DebugDraw& debugDraw = GetDebugDraw(); + DebugDraw::ActorInstanceData* drawData = debugDraw.GetActorInstanceData(actorInstance); + drawData->Lock(); + drawData->DrawWireframeSphere(jointTransform.m_position, radius, color, jointTransform.m_rotation, 12, 12); + drawData->Unlock(); + } + + void SimulatedObjectWidget::Render(EMotionFX::ActorRenderFlagBitset renderFlags) + { + if (!m_actor || !m_actorInstance) + { + return; + } + + const AZ::Render::RenderActorSettings& settings = EMotionFX::GetRenderActorSettings(); + const bool renderSimulatedJoints = renderFlags[RENDER_SIMULATEJOINTS]; + const AZStd::unordered_set& selectedJointIndices = EMStudio::GetManager()->GetSelectedJointIndices(); + if (renderSimulatedJoints && !selectedJointIndices.empty()) + { + // Render the joint radius. + const size_t actorInstanceCount = GetActorManager().GetNumActorInstances(); + for (size_t actorInstanceIndex = 0; actorInstanceIndex < actorInstanceCount; ++actorInstanceIndex) + { + ActorInstance* actorInstance = GetActorManager().GetActorInstance(actorInstanceIndex); + const Actor* actor = actorInstance->GetActor(); + const SimulatedObjectSetup* setup = actor->GetSimulatedObjectSetup().get(); + if (!setup) + { + AZ_Assert(false, "Expected a simulated object setup on the actor instance."); + return; + } + const size_t objectCount = setup->GetNumSimulatedObjects(); for (size_t objectIndex = 0; objectIndex < objectCount; ++objectIndex) { @@ -520,25 +613,21 @@ namespace EMotionFX } } - const bool renderColliders = activeViewWidget->GetRenderFlag(EMStudio::RenderViewWidget::RENDER_SIMULATEDOBJECT_COLLIDERS); + const bool renderColliders = renderFlags[RENDER_SIMULATEDOBJECT_COLLIDERS]; if (renderColliders) { - const EMStudio::RenderOptions* renderOptions = renderPlugin->GetRenderOptions(); ColliderContainerWidget::RenderColliders(PhysicsSetup::SimulatedObjectCollider, - renderOptions->GetSimulatedObjectColliderColor(), - renderOptions->GetSelectedSimulatedObjectColliderColor(), - renderPlugin, - renderInfo); + settings.m_simulatedObjectColliderColor, settings.m_selectedSimulatedObjectColliderColor); } } - void SimulatedObjectWidget::RenderJointRadius(const SimulatedJoint* joint, ActorInstance* actorInstance, const AZ::Color& color) + void SimulatedObjectWidget::RenderJointRadius(const SimulatedJoint* joint, ActorInstance* actorInstance, const AZ::Color& color) { - #ifndef EMFX_SCALE_DISABLED - const float scale = actorInstance->GetWorldSpaceTransform().m_scale.GetX(); - #else - const float scale = 1.0f; - #endif +#ifndef EMFX_SCALE_DISABLED + const float scale = actorInstance->GetWorldSpaceTransform().m_scale.GetX(); +#else + const float scale = 1.0f; +#endif const float radius = joint->GetCollisionRadius() * scale; if (radius <= AZ::Constants::FloatEpsilon) @@ -547,12 +636,21 @@ namespace EMotionFX } AZ_Assert(joint->GetSkeletonJointIndex() != InvalidIndex, "Expected skeletal joint index to be valid."); - const EMotionFX::Transform jointTransform = actorInstance->GetTransformData()->GetCurrentPose()->GetWorldSpaceTransform(joint->GetSkeletonJointIndex()); + const EMotionFX::Transform jointTransform = + actorInstance->GetTransformData()->GetCurrentPose()->GetWorldSpaceTransform(joint->GetSkeletonJointIndex()); - DebugDraw& debugDraw = GetDebugDraw(); - DebugDraw::ActorInstanceData* drawData = debugDraw.GetActorInstanceData(actorInstance); - drawData->Lock(); - drawData->DrawWireframeSphere(jointTransform.m_position, radius, color, jointTransform.m_rotation, 12, 12); - drawData->Unlock(); + AZ::s32 viewportId = -1; + EMStudio::ViewportPluginRequestBus::BroadcastResult(viewportId, &EMStudio::ViewportPluginRequestBus::Events::GetViewportId); + AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; + AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, viewportId); + AzFramework::DebugDisplayRequests* debugDisplay = nullptr; + debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); + if (!debugDisplay) + { + return; + } + + debugDisplay->SetColor(color); + debugDisplay->DrawWireSphere(jointTransform.m_position, radius); } } // namespace EMotionFX diff --git a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.h b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.h index 955c97ce13..9662fbc150 100644 --- a/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.h +++ b/Gems/EMotionFX/Code/Source/Editor/Plugins/SimulatedObject/SimulatedObjectWidget.h @@ -59,8 +59,9 @@ namespace EMotionFX bool Init() override; void Reinit(); - // Render - void Render(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) override; + void LegacyRender(EMStudio::RenderPlugin* renderPlugin, RenderInfo* renderInfo) override; + void LegacyRenderJointRadius(const SimulatedJoint* joint, ActorInstance* actorInstance, const AZ::Color& color); + void Render(EMotionFX::ActorRenderFlagBitset renderFlags) override; void RenderJointRadius(const SimulatedJoint* joint, ActorInstance* actorInstance, const AZ::Color& color); SimulatedObjectModel* GetSimulatedObjectModel() const; diff --git a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorSettings.h b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorSettings.h index 3e4f5ff5c9..c397690b16 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorSettings.h +++ b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorSettings.h @@ -27,6 +27,16 @@ namespace AZ::Render float m_tangentsScale = 1.0f; float m_wireframeScale = 1.0f; + AZ::Color m_hitDetectionColliderColor{0.44f, 0.44f, 0.44f, 1.0f}; + AZ::Color m_selectedHitDetectionColliderColor{ 0.3f, 0.56f, 0.88f, 1.0f }; + AZ::Color m_ragdollColliderColor{ 0.44f, 0.44f, 0.44f, 1.0f }; + AZ::Color m_selectedRagdollColliderColor{ 0.96f, 0.65f, 0.14f, 1.0f }; + AZ::Color m_violatedJointLimitColor{ 1.0f, 0.0f, 0.0f, 1.0f }; + AZ::Color m_clothColliderColor{ 0.44f, 0.44f, 0.44f, 1.0f }; + AZ::Color m_selectedClothColliderColor{ 0.6f, 0.46f, 1.0f, 1.0f }; + AZ::Color m_simulatedObjectColliderColor{ 0.44f, 0.44f, 0.44f, 1.0f }; + AZ::Color m_selectedSimulatedObjectColliderColor{ 1.0, 0.34f, 0.87f, 1.0f }; + AZ::Color m_vertexNormalsColor{ 0.0f, 1.0f, 0.0f, 1.0f }; AZ::Color m_faceNormalsColor{ 0.5f, 0.5f, 1.0f, 1.0f }; AZ::Color m_tangentsColor{ 1.0f, 0.0f, 0.0f, 1.0f }; From e1d53395feafd8e6174aec8ab239e436f1a9629d Mon Sep 17 00:00:00 2001 From: Mikhail Naumov <82239319+AMZN-mnaumov@users.noreply.github.com> Date: Tue, 9 Nov 2021 16:05:15 -0600 Subject: [PATCH 149/177] Propagation Optimization (#5355) * Merge changes Signed-off-by: Mikhail Naumov * removing leftover immediate flag Signed-off-by: Mikhail Naumov --- .../Instance/InstanceToTemplateInterface.h | 2 +- .../Instance/InstanceToTemplatePropagator.cpp | 2 +- .../Instance/InstanceToTemplatePropagator.h | 2 +- .../Instance/InstanceUpdateExecutor.cpp | 4 +- .../Prefab/Instance/InstanceUpdateExecutor.h | 2 +- .../InstanceUpdateExecutorInterface.h | 2 +- .../Prefab/PrefabPublicHandler.cpp | 142 +++++++++--------- .../Prefab/PrefabSystemComponent.cpp | 9 +- .../Prefab/PrefabSystemComponent.h | 4 +- .../Prefab/PrefabSystemComponentInterface.h | 2 +- .../AzToolsFramework/Prefab/PrefabUndo.cpp | 12 +- .../AzToolsFramework/Prefab/PrefabUndo.h | 7 +- .../Prefab/PrefabUndoHelpers.cpp | 2 +- .../Tests/UI/EntityOutlinerTests.cpp | 2 +- 14 files changed, 96 insertions(+), 98 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h index b944ef159a..05c74912cc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h @@ -49,7 +49,7 @@ namespace AzToolsFramework //! @param instanceToExclude An optional reference to an instance of the template being updated that should not be refreshes as part of propagation. //! Defaults to nullopt, which means that all instances will be refreshed. //! @return True if the template was patched correctly, false if the operation failed. - virtual bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0; + virtual bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, InstanceOptionalConstReference instanceToExclude = AZStd::nullopt) = 0; virtual void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp index 6b281bcbae..609d99855e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp @@ -156,7 +156,7 @@ namespace AzToolsFramework } } - bool InstanceToTemplatePropagator::PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, InstanceOptionalReference instanceToExclude) + bool InstanceToTemplatePropagator::PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, InstanceOptionalConstReference instanceToExclude) { PrefabDom& templateDomReference = m_prefabSystemComponentInterface->FindTemplateDom(templateId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.h index 75acb410c9..aff8ec5d7b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.h @@ -33,7 +33,7 @@ namespace AzToolsFramework InstanceOptionalReference GetTopMostInstanceInHierarchy(AZ::EntityId entityId) override; - bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override; + bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, InstanceOptionalConstReference instanceToExclude = AZStd::nullopt) override; void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp index 9ef74167a6..d5cf7a9144 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.cpp @@ -52,7 +52,7 @@ namespace AzToolsFramework AZ::Interface::Unregister(this); } - void InstanceUpdateExecutor::AddTemplateInstancesToQueue(TemplateId instanceTemplateId, InstanceOptionalReference instanceToExclude) + void InstanceUpdateExecutor::AddTemplateInstancesToQueue(TemplateId instanceTemplateId, InstanceOptionalConstReference instanceToExclude) { auto findInstancesResult = m_templateInstanceMapperInterface->FindInstancesOwnedByTemplate(instanceTemplateId); @@ -66,7 +66,7 @@ namespace AzToolsFramework return; } - Instance* instanceToExcludePtr = nullptr; + const Instance* instanceToExcludePtr = nullptr; if (instanceToExclude.has_value()) { instanceToExcludePtr = &(instanceToExclude->get()); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.h index ee461eae88..2bd321beb4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutor.h @@ -31,7 +31,7 @@ namespace AzToolsFramework explicit InstanceUpdateExecutor(int instanceCountToUpdateInBatch = 0); - void AddTemplateInstancesToQueue(TemplateId instanceTemplateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override; + void AddTemplateInstancesToQueue(TemplateId instanceTemplateId, InstanceOptionalConstReference instanceToExclude = AZStd::nullopt) override; bool UpdateTemplateInstancesInQueue() override; virtual void RemoveTemplateInstanceFromQueue(const Instance* instance) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h index 8ad032e1d0..6460c1a566 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceUpdateExecutorInterface.h @@ -23,7 +23,7 @@ namespace AzToolsFramework virtual ~InstanceUpdateExecutorInterface() = default; // Add all Instances of Template with given Id into a queue for updating them later. - virtual void AddTemplateInstancesToQueue(TemplateId instanceTemplateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0; + virtual void AddTemplateInstancesToQueue(TemplateId instanceTemplateId, InstanceOptionalConstReference instanceToExclude = AZStd::nullopt) = 0; // Update Instances in the waiting queue. virtual bool UpdateTemplateInstancesInQueue() = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 422ee790c4..4d826d9700 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -777,7 +777,7 @@ namespace AzToolsFramework linkUpdate->SetParent(undoBatch); linkUpdate->Capture(patch, linkId); - linkUpdate->Redo(parentInstance); + linkUpdate->Redo(parentInstance->get()); } void PrefabPublicHandler::Internal_HandleEntityChange( @@ -788,8 +788,7 @@ namespace AzToolsFramework PrefabUndoEntityUpdate* state = aznew PrefabUndoEntityUpdate(AZStd::to_string(static_cast(entityId))); state->SetParent(undoBatch); state->Capture(beforeState, afterState, entityId); - - state->Redo(instance); + state->Redo(instance->get()); } void PrefabPublicHandler::Internal_HandleInstanceChange( @@ -1167,6 +1166,60 @@ namespace AzToolsFramework ScopedUndoBatch undoBatch("Delete Selected"); + AZ_PROFILE_SCOPE(AzToolsFramework, "Internal::DeleteEntities:UndoCaptureAndPurgeEntities"); + + Prefab::PrefabDom instanceDomBefore; + m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, commonOwningInstance->get()); + + if (deleteDescendants) + { + AZStd::vector entities; + AZStd::vector instances; + + PrefabOperationResult retrieveEntitiesAndInstancesOutcome = + RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entities, instances); + + if (!retrieveEntitiesAndInstancesOutcome.IsSuccess()) + { + return AZStd::move(retrieveEntitiesAndInstancesOutcome); + } + + for (AZ::Entity* entity : entities) + { + commonOwningInstance->get().DetachEntity(entity->GetId()).release(); + AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, entity->GetId()); + } + + for (auto& nestedInstance : instances) + { + AZStd::unique_ptr outInstance = + commonOwningInstance->get().DetachNestedInstance(nestedInstance->GetInstanceAlias()); + RemoveLink(outInstance, commonOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch()); + outInstance.reset(); + } + } + else + { + for (AZ::EntityId entityId : entityIdsNoFocusContainer) + { + InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); + // If this is the container entity, it actually represents the instance so get its owner + if (owningInstance->get().GetContainerEntityId() == entityId) + { + auto instancePtr = commonOwningInstance->get().DetachNestedInstance(owningInstance->get().GetInstanceAlias()); + RemoveLink(instancePtr, commonOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch()); + } + else + { + commonOwningInstance->get().DetachEntity(entityId); + AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, entityId); + } + } + } + + Prefab::PrefabDom instanceDomAfter; + m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, commonOwningInstance->get()); + // In order to undo DeleteSelected, we have to create a selection command which selects the current selection // and then add the deletion as children. // Commands always execute themselves first and then their children (when going forwards) @@ -1174,81 +1227,22 @@ namespace AzToolsFramework EntityIdList selectedEntities; ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities); SelectionCommand* selCommand = aznew SelectionCommand(selectedEntities, "Delete Entities"); + selCommand->SetParent(undoBatch.GetUndoBatch()); + AZ_PROFILE_SCOPE(AzToolsFramework, "Internal::DeleteEntities:RunRedo"); + selCommand->RunRedo(); // We insert a "deselect all" command before we delete the entities. This ensures the delete operations aren't changing // selection state, which triggers expensive UI updates. By deselecting up front, we are able to do those expensive // UI updates once at the start instead of once for each entity. - { - EntityIdList deselection; - SelectionCommand* deselectAllCommand = aznew SelectionCommand(deselection, "Deselect Entities"); - deselectAllCommand->SetParent(selCommand); - } - - { - AZ_PROFILE_SCOPE(AzToolsFramework, "Internal::DeleteEntities:UndoCaptureAndPurgeEntities"); - - Prefab::PrefabDom instanceDomBefore; - m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, commonOwningInstance->get()); - - if (deleteDescendants) - { - AZStd::vector entities; - AZStd::vector instances; - - PrefabOperationResult retrieveEntitiesAndInstancesOutcome = - RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entities, instances); - - if (!retrieveEntitiesAndInstancesOutcome.IsSuccess()) - { - return AZStd::move(retrieveEntitiesAndInstancesOutcome); - } - - for (AZ::Entity* entity : entities) - { - commonOwningInstance->get().DetachEntity(entity->GetId()).release(); - AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, entity->GetId()); - } - - for (auto& nestedInstance : instances) - { - AZStd::unique_ptr outInstance = commonOwningInstance->get().DetachNestedInstance(nestedInstance->GetInstanceAlias()); - RemoveLink(outInstance, commonOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch()); - outInstance.reset(); - } - } - else - { - for (AZ::EntityId entityId : entityIdsNoFocusContainer) - { - InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); - // If this is the container entity, it actually represents the instance so get its owner - if (owningInstance->get().GetContainerEntityId() == entityId) - { - auto instancePtr = commonOwningInstance->get().DetachNestedInstance(owningInstance->get().GetInstanceAlias()); - RemoveLink(instancePtr, commonOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch()); - } - else - { - commonOwningInstance->get().DetachEntity(entityId); - AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationRequests::DeleteEntity, entityId); - } - } - } - - Prefab::PrefabDom instanceDomAfter; - m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, commonOwningInstance->get()); - - PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance deletion"); - command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId()); - command->SetParent(selCommand); - } - - selCommand->SetParent(undoBatch.GetUndoBatch()); - { - AZ_PROFILE_SCOPE(AzToolsFramework, "Internal::DeleteEntities:RunRedo"); - selCommand->RunRedo(); - } + EntityIdList deselection; + SelectionCommand* deselectAllCommand = aznew SelectionCommand(deselection, "Deselect Entities"); + deselectAllCommand->SetParent(undoBatch.GetUndoBatch()); + PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance deletion"); + command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId()); + command->SetParent(undoBatch.GetUndoBatch()); + command->Redo(commonOwningInstance->get()); + return AZ::Success(); } @@ -1338,7 +1332,7 @@ namespace AzToolsFramework command->SetParent(undoBatch.GetUndoBatch()); { AZ_PROFILE_SCOPE(AzToolsFramework, "Internal::DetachPrefab:RunRedo"); - command->RunRedo(); + command->Redo(parentInstance); } instancePtr->DetachNestedInstances( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index 1f00b952b1..62d7a6ac1d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -159,11 +159,9 @@ namespace AzToolsFramework newInstance->SetTemplateId(newTemplateId); } } - - void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId, InstanceOptionalReference instanceToExclude) + + void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId, InstanceOptionalConstReference instanceToExclude) { - UpdatePrefabInstances(templateId, instanceToExclude); - auto templateIdToLinkIdsIterator = m_templateToLinkIdsMap.find(templateId); if (templateIdToLinkIdsIterator != m_templateToLinkIdsMap.end()) { @@ -174,6 +172,7 @@ namespace AzToolsFramework templateIdToLinkIdsIterator->second.end())); UpdateLinkedInstances(linkIdsToUpdateQueue); } + UpdatePrefabInstances(templateId, instanceToExclude); } void PrefabSystemComponent::UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom) @@ -191,7 +190,7 @@ namespace AzToolsFramework } } - void PrefabSystemComponent::UpdatePrefabInstances(TemplateId templateId, InstanceOptionalReference instanceToExclude) + void PrefabSystemComponent::UpdatePrefabInstances(TemplateId templateId, InstanceOptionalConstReference instanceToExclude) { m_instanceUpdateExecutor.AddTemplateInstancesToQueue(templateId, instanceToExclude); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h index 7b18d64b08..99efaa89d6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h @@ -231,7 +231,7 @@ namespace AzToolsFramework */ void UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom) override; - void PropagateTemplateChanges(TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override; + void PropagateTemplateChanges(TemplateId templateId, InstanceOptionalConstReference instanceToExclude = AZStd::nullopt) override; /** * Updates all Instances owned by a Template. @@ -240,7 +240,7 @@ namespace AzToolsFramework * @param instanceToExclude An optional reference to an instance of the template being updated that should not be refreshed * as part of propagation.Defaults to nullopt, which means that all instances will be refreshed. */ - void UpdatePrefabInstances(TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt); + void UpdatePrefabInstances(TemplateId templateId, InstanceOptionalConstReference instanceToExclude = AZStd::nullopt); private: AZ_DISABLE_COPY_MOVE(PrefabSystemComponent); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h index 761d66fd52..ce75930cb6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h @@ -67,7 +67,7 @@ namespace AzToolsFramework virtual PrefabDom& FindTemplateDom(TemplateId templateId) = 0; virtual void UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom) = 0; - virtual void PropagateTemplateChanges(TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0; + virtual void PropagateTemplateChanges(TemplateId templateId, InstanceOptionalConstReference instanceToExclude = AZStd::nullopt) = 0; virtual AZStd::unique_ptr InstantiatePrefab( AZ::IO::PathView filePath, InstanceOptionalReference parent = AZStd::nullopt) = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp index 385e9b149b..dc799c235a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp @@ -51,6 +51,10 @@ namespace AzToolsFramework m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId); } + void PrefabUndoInstance::Redo(InstanceOptionalConstReference instance) + { + m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, instance); + } //PrefabEntityUpdateUndo PrefabUndoEntityUpdate::PrefabUndoEntityUpdate(const AZStd::string& undoOperationName) @@ -110,10 +114,10 @@ namespace AzToolsFramework m_templateId); } - void PrefabUndoEntityUpdate::Redo(InstanceOptionalReference instanceToExclude) + void PrefabUndoEntityUpdate::Redo(InstanceOptionalConstReference instance) { [[maybe_unused]] bool isPatchApplicationSuccessful = - m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, instanceToExclude); + m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, instance); AZ_Error( "Prefab", isPatchApplicationSuccessful, @@ -310,12 +314,12 @@ namespace AzToolsFramework UpdateLink(m_linkDomNext); } - void PrefabUndoLinkUpdate::Redo(InstanceOptionalReference instanceToExclude) + void PrefabUndoLinkUpdate::Redo(InstanceOptionalConstReference instanceToExclude) { UpdateLink(m_linkDomNext, instanceToExclude); } - void PrefabUndoLinkUpdate::UpdateLink(PrefabDom& linkDom, InstanceOptionalReference instanceToExclude) + void PrefabUndoLinkUpdate::UpdateLink(PrefabDom& linkDom, InstanceOptionalConstReference instanceToExclude) { LinkReference link = m_prefabSystemComponentInterface->FindLink(m_linkId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h index 0af94f86cc..c8cf195966 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.h @@ -53,6 +53,7 @@ namespace AzToolsFramework void Undo() override; void Redo() override; + void Redo(InstanceOptionalConstReference instance); }; //! handles entity updates, such as when the values on an entity change @@ -72,7 +73,7 @@ namespace AzToolsFramework void Undo() override; void Redo() override; //! Overload to allow to apply the change, but prevent instanceToExclude from being refreshed. - void Redo(InstanceOptionalReference instanceToExclude); + void Redo(InstanceOptionalConstReference instanceToExclude); private: InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr; @@ -137,10 +138,10 @@ namespace AzToolsFramework void Undo() override; void Redo() override; //! Overload to allow to apply the change, but prevent instanceToExclude from being refreshed. - void Redo(InstanceOptionalReference instanceToExclude); + void Redo(InstanceOptionalConstReference instanceToExclude); private: - void UpdateLink(PrefabDom& linkDom, InstanceOptionalReference instanceToExclude = AZStd::nullopt); + void UpdateLink(PrefabDom& linkDom, InstanceOptionalConstReference instanceToExclude = AZStd::nullopt); LinkId m_linkId; PrefabDom m_linkDomNext; //data for delete/update diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp index 9c44fc7ffd..f8b34d114b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndoHelpers.cpp @@ -26,7 +26,7 @@ namespace AzToolsFramework PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage); state->Capture(instanceDomBeforeUpdate, instanceDomAfterUpdate, instance.GetTemplateId()); state->SetParent(undoBatch); - state->Redo(); + state->Redo(instance); } LinkId CreateLink( diff --git a/Code/Framework/AzToolsFramework/Tests/UI/EntityOutlinerTests.cpp b/Code/Framework/AzToolsFramework/Tests/UI/EntityOutlinerTests.cpp index fa8de64c62..78cdcf9d38 100644 --- a/Code/Framework/AzToolsFramework/Tests/UI/EntityOutlinerTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/UI/EntityOutlinerTests.cpp @@ -82,8 +82,8 @@ namespace UnitTest } auto transform = aznew AzToolsFramework::Components::TransformComponent; - transform->SetParent(parentId); entity->AddComponent(transform); + transform->SetParent(parentId); entity->Activate(); From d3b8b761fd70e7fd1213d379e61c6fe0be4b6876 Mon Sep 17 00:00:00 2001 From: lsemp3d <58790905+lsemp3d@users.noreply.github.com> Date: Tue, 9 Nov 2021 14:51:10 -0800 Subject: [PATCH 150/177] Improved messaging on assert about argument types lacking reflection for scripting Signed-off-by: lsemp3d <58790905+lsemp3d@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp b/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp index b03d413507..f50506e0e0 100644 --- a/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp +++ b/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp @@ -3408,7 +3408,14 @@ LUA_API const Node* lua_getDummyNode() const BehaviorParameter* arg = method->GetArgument(iArg); BehaviorClass* argClass = nullptr; LuaLoadFromStack fromStack = FromLuaStack(context, arg, argClass); - AZ_Assert(fromStack, "Argument %s for Method %s doesn't have support to be converted to Lua!", arg->m_name, method->m_name.c_str()); + AZ_Assert(fromStack, + "The argument type: %s for method: %s is not serialized and/or reflected for scripting.\n" + "Make sure %s is added to the serialization context and reflected to the Behavior Context\n" + "For example, verify these two exist and are being called in a Reflect function:\n" + "serializeContext->Class<%s>();\n" + "behaviorContext->Class<%s>();\n" + "%s will not be available for scripting unless these requirements are met." + , arg->m_name, method->m_name.c_str(), arg->m_name, arg->m_name, arg->m_name, method->m_name.c_str()); m_fromLua.push_back(AZStd::make_pair(fromStack, argClass)); } From 934c0f2ec7d2b27fa12dc2e240d9a7a444de64a2 Mon Sep 17 00:00:00 2001 From: lsemp3d <58790905+lsemp3d@users.noreply.github.com> Date: Tue, 9 Nov 2021 15:25:06 -0800 Subject: [PATCH 151/177] Made SerializeContext and BehaviorContext consistent with their class name Signed-off-by: lsemp3d <58790905+lsemp3d@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp b/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp index f50506e0e0..45f7876993 100644 --- a/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp +++ b/Code/Framework/AzCore/AzCore/Script/ScriptContext.cpp @@ -3410,7 +3410,7 @@ LUA_API const Node* lua_getDummyNode() LuaLoadFromStack fromStack = FromLuaStack(context, arg, argClass); AZ_Assert(fromStack, "The argument type: %s for method: %s is not serialized and/or reflected for scripting.\n" - "Make sure %s is added to the serialization context and reflected to the Behavior Context\n" + "Make sure %s is added to the SerializeContext and reflected to the BehaviorContext\n" "For example, verify these two exist and are being called in a Reflect function:\n" "serializeContext->Class<%s>();\n" "behaviorContext->Class<%s>();\n" From 0dca09b0395765707f03663e159e96dc929d54cb Mon Sep 17 00:00:00 2001 From: Vincent Liu <5900509+onecent1101@users.noreply.github.com> Date: Tue, 9 Nov 2021 16:56:06 -0800 Subject: [PATCH 152/177] Update AWS gem info content (#5281) * Update AWS gem info content Signed-off-by: liug * Tweak requirement statement based on feedback Signed-off-by: liug --- Gems/AWSClientAuth/gem.json | 1 - Gems/AWSCore/gem.json | 4 +--- Gems/AWSGameLift/gem.json | 5 +++-- Gems/AWSMetrics/gem.json | 1 - 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Gems/AWSClientAuth/gem.json b/Gems/AWSClientAuth/gem.json index d03b86fb45..9c7188f103 100644 --- a/Gems/AWSClientAuth/gem.json +++ b/Gems/AWSClientAuth/gem.json @@ -15,7 +15,6 @@ "SDK" ], "icon_path": "preview.png", - "requirements": "", "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-client-auth/", "dependencies": [ "AWSCore", diff --git a/Gems/AWSCore/gem.json b/Gems/AWSCore/gem.json index 9fb974ccf4..3bced07e8d 100644 --- a/Gems/AWSCore/gem.json +++ b/Gems/AWSCore/gem.json @@ -15,7 +15,5 @@ "SDK" ], "icon_path": "preview.png", - "requirements": "", - "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-core/", - "dependencies": [] + "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-core/" } diff --git a/Gems/AWSGameLift/gem.json b/Gems/AWSGameLift/gem.json index 3fe1f15c3d..1ac65c4526 100644 --- a/Gems/AWSGameLift/gem.json +++ b/Gems/AWSGameLift/gem.json @@ -12,10 +12,11 @@ "user_tags": [ "AWS", "Framework", - "Network" + "Network", + "SDK" ], "icon_path": "preview.png", - "requirements": "", + "requirements": "Users will need to enable the Multiplayer gem to support the AWSGameLift feature.", "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-gamelift/", "dependencies": [ "AWSCore" diff --git a/Gems/AWSMetrics/gem.json b/Gems/AWSMetrics/gem.json index 59eae8f3c2..054c3624c4 100644 --- a/Gems/AWSMetrics/gem.json +++ b/Gems/AWSMetrics/gem.json @@ -15,7 +15,6 @@ "SDK" ], "icon_path": "preview.png", - "requirements": "", "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/aws/aws-metrics/", "dependencies": [ "AWSCore" From eb775a48dc58dae31e9b5c3ec9cce4155c91c080 Mon Sep 17 00:00:00 2001 From: bosnichd Date: Wed, 10 Nov 2021 08:06:27 -0700 Subject: [PATCH 153/177] Make InputDeviceId's constructor constexpr. (#5433) * Make InputDeviceId's constructor constexpr. See also https://github.com/o3de/o3de/pull/4220 Signed-off-by: bosnichd * Updates based on review feedback. Signed-off-by: bosnichd --- .../Input/Channels/InputChannelId.cpp | 24 ------- .../Input/Channels/InputChannelId.h | 55 ++++++++------- .../Devices/Gamepad/InputDeviceGamepad.cpp | 8 --- .../Devices/Gamepad/InputDeviceGamepad.h | 12 ++-- .../Input/Devices/InputDeviceId.cpp | 67 ------------------- .../AzFramework/Input/Devices/InputDeviceId.h | 67 +++++++++++++------ .../Devices/Keyboard/InputDeviceKeyboard.cpp | 3 - .../Devices/Keyboard/InputDeviceKeyboard.h | 2 +- .../Devices/Motion/InputDeviceMotion.cpp | 3 - .../Input/Devices/Motion/InputDeviceMotion.h | 2 +- .../Input/Devices/Mouse/InputDeviceMouse.cpp | 12 ---- .../Input/Devices/Mouse/InputDeviceMouse.h | 8 +-- .../Input/Devices/Touch/InputDeviceTouch.cpp | 3 - .../Input/Devices/Touch/InputDeviceTouch.h | 2 +- .../InputDeviceVirtualKeyboard.cpp | 3 - .../InputDeviceVirtualKeyboard.h | 2 +- .../AzFramework/Tests/InputTests.cpp | 18 +++++ 17 files changed, 107 insertions(+), 184 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp index 496d89d767..ef39e50cb8 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.cpp @@ -27,28 +27,4 @@ namespace AzFramework ; } } - - //////////////////////////////////////////////////////////////////////////////////////////////// - const char* InputChannelId::GetName() const - { - return m_name.c_str(); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - const AZ::Crc32& InputChannelId::GetNameCrc32() const - { - return m_crc32; - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - bool InputChannelId::operator==(const InputChannelId& other) const - { - return (m_crc32 == other.m_crc32); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - bool InputChannelId::operator!=(const InputChannelId& other) const - { - return !(*this == other); - } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h index a4d8ac83eb..62d79285f5 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Channels/InputChannelId.h @@ -39,53 +39,58 @@ namespace AzFramework //////////////////////////////////////////////////////////////////////////////////////////// //! Constructor - //! \param[in] name Name of the input channel (will be ignored if exceeds MAX_NAME_LENGTH) + //! \param[in] name Name of the input channel (will be truncated if exceeds MAX_NAME_LENGTH) explicit constexpr InputChannelId(AZStd::string_view name = "") - : m_name(name) - , m_crc32(name) + : m_name(name.substr(0, MAX_NAME_LENGTH)) + , m_crc32(name.substr(0, MAX_NAME_LENGTH)) { } - constexpr InputChannelId(const InputChannelId& other) = default; - constexpr InputChannelId(InputChannelId&& other) = default; - constexpr InputChannelId& operator=(const InputChannelId& other) - { - m_name = other.m_name; - m_crc32 = other.m_crc32; - return *this; - } - constexpr InputChannelId& operator=(InputChannelId&& other) - { - m_name = AZStd::move(other.m_name); - m_crc32 = AZStd::move(other.m_crc32); - other.m_crc32 = 0; - return *this; - } + //////////////////////////////////////////////////////////////////////////////////////////// + // Default copying and moving + AZ_DEFAULT_COPY_MOVE(InputChannelId); + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Default destructor ~InputChannelId() = default; //////////////////////////////////////////////////////////////////////////////////////////// //! Access to the input channel's name //! \return Name of the input channel - const char* GetName() const; + constexpr const char* GetName() const + { + return m_name.c_str(); + } //////////////////////////////////////////////////////////////////////////////////////////// //! Access to the crc32 of the input channel's name //! \return crc32 of the input channel name - const AZ::Crc32& GetNameCrc32() const; + constexpr const AZ::Crc32& GetNameCrc32() const + { + return m_crc32; + } //////////////////////////////////////////////////////////////////////////////////////////// - ///@{ //! Equality comparison operator //! \param[in] other Another instance of the class to compare for equality - bool operator==(const InputChannelId& other) const; - bool operator!=(const InputChannelId& other) const; - ///@} + constexpr bool operator==(const InputChannelId& other) const + { + return m_crc32 == other.m_crc32; + } + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Inequality comparison operator + //! \param[in] other Another instance of the class to compare for inequality + constexpr bool operator!=(const InputChannelId& other) const + { + return !(*this == other); + } private: //////////////////////////////////////////////////////////////////////////////////////////// // Variables AZStd::fixed_string m_name; //!< Name of the input channel - AZ::Crc32 m_crc32; //!< Crc32 of the input channel + AZ::Crc32 m_crc32; //!< Crc32 of the input channel name }; } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp index 9759a95733..c7629c7afb 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.cpp @@ -14,14 +14,6 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { - //////////////////////////////////////////////////////////////////////////////////////////////// - const char* InputDeviceGamepad::Name("gamepad"); - const InputDeviceId InputDeviceGamepad::IdForIndex0(Name, 0); - const InputDeviceId InputDeviceGamepad::IdForIndex1(Name, 1); - const InputDeviceId InputDeviceGamepad::IdForIndex2(Name, 2); - const InputDeviceId InputDeviceGamepad::IdForIndex3(Name, 3); - const InputDeviceId InputDeviceGamepad::IdForIndexN(AZ::u32 n) { return InputDeviceId(Name, n); } - //////////////////////////////////////////////////////////////////////////////////////////////// bool InputDeviceGamepad::IsGamepadDevice(const InputDeviceId& inputDeviceId) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h index 7be498830b..b143709621 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h @@ -32,16 +32,16 @@ namespace AzFramework public: //////////////////////////////////////////////////////////////////////////////////////////// //! The name used to identify any game-pad input device - static const char* Name; + static constexpr inline const char* Name{"gamepad"}; //////////////////////////////////////////////////////////////////////////////////////////// //! The id used to identify a game-pad input device with a specific index ///@{ - static const InputDeviceId IdForIndex0; - static const InputDeviceId IdForIndex1; - static const InputDeviceId IdForIndex2; - static const InputDeviceId IdForIndex3; - static const InputDeviceId IdForIndexN(AZ::u32 n); + static constexpr inline InputDeviceId IdForIndex0{Name, 0}; + static constexpr inline InputDeviceId IdForIndex1{Name, 1}; + static constexpr inline InputDeviceId IdForIndex2{Name, 2}; + static constexpr inline InputDeviceId IdForIndex3{Name, 3}; + static constexpr inline InputDeviceId IdForIndexN(AZ::u32 n) { return InputDeviceId(Name, n); } ///@} //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/InputDeviceId.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/InputDeviceId.cpp index fa0955bd16..7f9a0039ac 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/InputDeviceId.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/InputDeviceId.cpp @@ -29,71 +29,4 @@ namespace AzFramework ; } } - - //////////////////////////////////////////////////////////////////////////////////////////////// - InputDeviceId::InputDeviceId(const char* name, AZ::u32 index) - : m_crc32(name) - , m_index(index) - { - memset(m_name, 0, AZ_ARRAY_SIZE(m_name)); - azstrncpy(m_name, NAME_BUFFER_SIZE, name, MAX_NAME_LENGTH); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - InputDeviceId::InputDeviceId(const InputDeviceId& other) - : m_crc32(other.m_crc32) - , m_index(other.m_index) - { - memset(m_name, 0, AZ_ARRAY_SIZE(m_name)); - azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - InputDeviceId& InputDeviceId::operator=(const InputDeviceId& other) - { - azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name); - m_crc32 = other.m_crc32; - m_index = other.m_index; - return *this; - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - const char* InputDeviceId::GetName() const - { - return m_name; - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - const AZ::Crc32& InputDeviceId::GetNameCrc32() const - { - return m_crc32; - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - AZ::u32 InputDeviceId::GetIndex() const - { - return m_index; - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - bool InputDeviceId::operator==(const InputDeviceId& other) const - { - return (m_crc32 == other.m_crc32) && (m_index == other.m_index); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - bool InputDeviceId::operator!=(const InputDeviceId& other) const - { - return !(*this == other); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - bool InputDeviceId::operator<(const InputDeviceId& other) const - { - if (m_index == other.m_index) - { - return m_crc32 < other.m_crc32; - } - return m_index < other.m_index; - } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/InputDeviceId.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/InputDeviceId.h index 6d2aa8b9fd..ffc1745c3b 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/InputDeviceId.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/InputDeviceId.h @@ -11,6 +11,7 @@ #include #include #include +#include //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework @@ -22,8 +23,7 @@ namespace AzFramework public: //////////////////////////////////////////////////////////////////////////////////////////// // Constants - static const int NAME_BUFFER_SIZE = 64; - static const int MAX_NAME_LENGTH = NAME_BUFFER_SIZE - 1; + static constexpr int MAX_NAME_LENGTH = 64; //////////////////////////////////////////////////////////////////////////////////////////// // Allocator @@ -41,17 +41,16 @@ namespace AzFramework //! Constructor //! \param[in] name Name of the input device (will be truncated if exceeds MAX_NAME_LENGTH) //! \param[in] index Index of the input device (optional) - explicit InputDeviceId(const char* name, AZ::u32 index = 0); + explicit constexpr InputDeviceId(AZStd::string_view name, AZ::u32 index = 0) + : m_name(name.substr(0, MAX_NAME_LENGTH)) + , m_crc32(name.substr(0, MAX_NAME_LENGTH)) + , m_index(index) + { + } //////////////////////////////////////////////////////////////////////////////////////////// - //! Copy constructor - //! \param[in] other Another instance of the class to copy from - InputDeviceId(const InputDeviceId& other); - - //////////////////////////////////////////////////////////////////////////////////////////// - //! Copy assignment operator - //! \param[in] other Another instance of the class to copy from - InputDeviceId& operator=(const InputDeviceId& other); + // Default copying and moving + AZ_DEFAULT_COPY_MOVE(InputDeviceId); //////////////////////////////////////////////////////////////////////////////////////////// //! Default destructor @@ -60,12 +59,18 @@ namespace AzFramework //////////////////////////////////////////////////////////////////////////////////////////// //! Access to the input device's name //! \return Name of the input device - const char* GetName() const; + constexpr const char* GetName() const + { + return m_name.c_str(); + } //////////////////////////////////////////////////////////////////////////////////////////// //! Access to the crc32 of the input device's name //! \return crc32 of the input device name - const AZ::Crc32& GetNameCrc32() const; + constexpr const AZ::Crc32& GetNameCrc32() const + { + return m_crc32; + } //////////////////////////////////////////////////////////////////////////////////////////// //! Access to the input device's index. Used for differentiating between multiple instances @@ -75,27 +80,45 @@ namespace AzFramework //! at startup using indicies 0->3. As gamepads connect/disconnect at runtime we assign the //! appropriate (system dependent) local user id (see InputDevice::GetAssignedLocalUserId). //! \return Index of the input device - AZ::u32 GetIndex() const; + constexpr AZ::u32 GetIndex() const + { + return m_index; + } //////////////////////////////////////////////////////////////////////////////////////////// - ///@{ //! Equality comparison operator //! \param[in] other Another instance of the class to compare for equality - bool operator==(const InputDeviceId& other) const; - bool operator!=(const InputDeviceId& other) const; - ///@} + constexpr bool operator==(const InputDeviceId& other) const + { + return (m_crc32 == other.m_crc32) && (m_index == other.m_index); + } + + //////////////////////////////////////////////////////////////////////////////////////////// + //! Inequality comparison operator + //! \param[in] other Another instance of the class to compare for inequality + constexpr bool operator!=(const InputDeviceId& other) const + { + return !(*this == other); + } //////////////////////////////////////////////////////////////////////////////////////////// //! Less than comparison operator //! \param[in] other Another instance of the class to compare - bool operator<(const InputDeviceId& other) const; + constexpr bool operator<(const InputDeviceId& other) const + { + if (m_index == other.m_index) + { + return m_crc32 < other.m_crc32; + } + return m_index < other.m_index; + } private: //////////////////////////////////////////////////////////////////////////////////////////// // Variables - char m_name[NAME_BUFFER_SIZE]; //!< Name of the input device - AZ::Crc32 m_crc32; //!< Crc32 of the input device - AZ::u32 m_index; //!< Index of the input device + AZStd::fixed_string m_name; //!< Name of the input device + AZ::Crc32 m_crc32; //!< Crc32 of the input device name + AZ::u32 m_index; //!< Index of the input device }; } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp index 2a76cc6e1b..08674ea92f 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.cpp @@ -15,9 +15,6 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputDeviceId InputDeviceKeyboard::Id("keyboard"); - //////////////////////////////////////////////////////////////////////////////////////////////// bool InputDeviceKeyboard::IsKeyboardDevice(const InputDeviceId& inputDeviceId) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h index c0c2f5d92b..f8300eefdf 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h @@ -33,7 +33,7 @@ namespace AzFramework public: //////////////////////////////////////////////////////////////////////////////////////////// //! The id used to identify the primary physical keyboard input device - static const InputDeviceId Id; + static constexpr inline InputDeviceId Id{"keyboard"}; //////////////////////////////////////////////////////////////////////////////////////////// //! Check whether an input device id identifies a physical keyboard (regardless of index) diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp index d4057bc5d3..fec51349c9 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.cpp @@ -14,9 +14,6 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputDeviceId InputDeviceMotion::Id("motion"); - //////////////////////////////////////////////////////////////////////////////////////////////// bool InputDeviceMotion::IsMotionDevice(const InputDeviceId& inputDeviceId) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h index f0fc976963..14783e01ca 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Motion/InputDeviceMotion.h @@ -28,7 +28,7 @@ namespace AzFramework public: //////////////////////////////////////////////////////////////////////////////////////////// //! The id used to identify the primary motion input device - static const InputDeviceId Id; + static constexpr inline InputDeviceId Id{"motion"}; //////////////////////////////////////////////////////////////////////////////////////////// //! Check whether an input device id identifies a motion device (regardless of index) diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp index 39504d9352..d869fa1f83 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.cpp @@ -15,18 +15,6 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { - //////////////////////////////////////////////////////////////////////////////////////////////// - const AZ::u32 InputDeviceMouse::MovementSampleRateDefault = 60; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const AZ::u32 InputDeviceMouse::MovementSampleRateQueueAll = std::numeric_limits::max(); - - //////////////////////////////////////////////////////////////////////////////////////////////// - const AZ::u32 InputDeviceMouse::MovementSampleRateAccumulateAll = 0; - - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputDeviceId InputDeviceMouse::Id("mouse"); - //////////////////////////////////////////////////////////////////////////////////////////////// bool InputDeviceMouse::IsMouseDevice(const InputDeviceId& inputDeviceId) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h index 3c519a3edb..8ad2088d2b 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Mouse/InputDeviceMouse.h @@ -31,23 +31,23 @@ namespace AzFramework //////////////////////////////////////////////////////////////////////////////////////////// //! Default sample rate for raw mouse movement events that aims to strike a balance between //! responsiveness and performance. - static const AZ::u32 MovementSampleRateDefault; + static constexpr inline AZ::u32 MovementSampleRateDefault{60}; //////////////////////////////////////////////////////////////////////////////////////////// //! Sample rate for raw mouse movement that will cause all events received in the same frame //! to be queued and dispatched as individual events. This results in maximum responsiveness //! but may potentially impact performance depending how many events happen over each frame. - static const AZ::u32 MovementSampleRateQueueAll; + static constexpr inline AZ::u32 MovementSampleRateQueueAll{std::numeric_limits::max()}; //////////////////////////////////////////////////////////////////////////////////////////// //! Sample rate for raw mouse movement that will cause all events received in the same frame //! to be accumulated and dispatched as a single event. Optimal for performance, but results //! in sluggish/unresponsive mouse movement, especially when running at low frame rates. - static const AZ::u32 MovementSampleRateAccumulateAll; + static constexpr inline AZ::u32 MovementSampleRateAccumulateAll{0}; //////////////////////////////////////////////////////////////////////////////////////////// //! The id used to identify the primary mouse input device - static const InputDeviceId Id; + static constexpr inline InputDeviceId Id{"mouse"}; //////////////////////////////////////////////////////////////////////////////////////////// //! Check whether an input device id identifies a mouse (regardless of index) diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp index 2695e3bb08..395ebcecb5 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.cpp @@ -15,9 +15,6 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputDeviceId InputDeviceTouch::Id("touch"); - //////////////////////////////////////////////////////////////////////////////////////////////// bool InputDeviceTouch::IsTouchDevice(const InputDeviceId& inputDeviceId) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h index 12b8aded4b..6e5e0c7ca9 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/Touch/InputDeviceTouch.h @@ -25,7 +25,7 @@ namespace AzFramework public: //////////////////////////////////////////////////////////////////////////////////////////// //! The id used to identify the primary touch input device - static const InputDeviceId Id; + static constexpr inline InputDeviceId Id{"touch"}; //////////////////////////////////////////////////////////////////////////////////////////// //! Check whether an input device id identifies a touch device (regardless of index) diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp index bcbdeaa478..5cfc4ea480 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.cpp @@ -14,9 +14,6 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { - //////////////////////////////////////////////////////////////////////////////////////////////// - const InputDeviceId InputDeviceVirtualKeyboard::Id("virtual_keyboard"); - //////////////////////////////////////////////////////////////////////////////////////////////// bool InputDeviceVirtualKeyboard::IsVirtualKeyboardDevice(const InputDeviceId& inputDeviceId) { diff --git a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h index abe1312733..c9f6286128 100644 --- a/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h +++ b/Code/Framework/AzFramework/AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h @@ -25,7 +25,7 @@ namespace AzFramework public: //////////////////////////////////////////////////////////////////////////////////////////// //! The id used to identify the primary virtual keyboard input device - static const InputDeviceId Id; + static constexpr inline InputDeviceId Id{"virtual_keyboard"}; //////////////////////////////////////////////////////////////////////////////////////////// //! Check whether an input device id identifies a virtual keyboard (regardless of index) diff --git a/Code/Framework/AzFramework/Tests/InputTests.cpp b/Code/Framework/AzFramework/Tests/InputTests.cpp index 9942912622..5fba7f5796 100644 --- a/Code/Framework/AzFramework/Tests/InputTests.cpp +++ b/Code/Framework/AzFramework/Tests/InputTests.cpp @@ -48,6 +48,24 @@ namespace InputUnitTests AZStd::unique_ptr m_inputSystemComponent; }; + //////////////////////////////////////////////////////////////////////////////////////////////// + TEST_F(InputTest, InputChannelId_ConstExpression_CopyConstructorSuccessfull) + { + constexpr InputChannelId testInputChannelId1("TestInputChannelId"); + constexpr InputChannelId testInputChannelId2(testInputChannelId1); + static_assert(testInputChannelId1 == testInputChannelId2); + EXPECT_EQ(testInputChannelId1, testInputChannelId2); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + TEST_F(InputTest, InputDeviceId_ConstExpression_CopyConstructorSuccessfull) + { + constexpr InputDeviceId testInputDeviceId1("TestInputDeviceId"); + constexpr InputDeviceId testInputDeviceId2(testInputDeviceId1); + static_assert(testInputDeviceId1 == testInputDeviceId2); + EXPECT_EQ(testInputDeviceId1, testInputDeviceId2); + } + //////////////////////////////////////////////////////////////////////////////////////////////// TEST_F(InputTest, InputContext_InitWithDataStruct_InitializationSuccessfull) { From 838970206b54795b8e4c7c2a2fc3ec3fc4cc239a Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Wed, 10 Nov 2021 16:23:43 +0100 Subject: [PATCH 154/177] Animation Editor: Remove preview label (#5449) Signed-off-by: Benjamin Jillich --- Code/Editor/LyViewPaneNames.h | 2 +- Code/Editor/MainWindow.cpp | 2 +- .../Source/MorphTargetsWindow/MorphTargetGroupWidget.cpp | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Code/Editor/LyViewPaneNames.h b/Code/Editor/LyViewPaneNames.h index 1761235f4b..5b32b66741 100644 --- a/Code/Editor/LyViewPaneNames.h +++ b/Code/Editor/LyViewPaneNames.h @@ -44,7 +44,7 @@ namespace LyViewPane static const char* const SubstanceEditor = "Substance Editor"; static const char* const VegetationEditor = "Vegetation Editor"; static const char* const LandscapeCanvas = "Landscape Canvas"; - static const char* const AnimationEditor = "EMotion FX Animation Editor (PREVIEW)"; + static const char* const AnimationEditor = "EMotion FX Animation Editor"; static const char* const PhysXConfigurationEditor = "PhysX Configuration (PREVIEW)"; static const char* const SliceRelationships = "Slice Relationship View (PREVIEW)"; diff --git a/Code/Editor/MainWindow.cpp b/Code/Editor/MainWindow.cpp index ed72cd9170..528b4eba6b 100644 --- a/Code/Editor/MainWindow.cpp +++ b/Code/Editor/MainWindow.cpp @@ -1061,7 +1061,7 @@ void MainWindow::InitActions() if (emfxEnabled.value) { QAction* action = am->AddAction(ID_OPEN_EMOTIONFX_EDITOR, tr("Animation Editor")) - .SetToolTip(tr("Open Animation Editor (PREVIEW)")) + .SetToolTip(tr("Open Animation Editor")) .SetIcon(QIcon(":/EMotionFX/EMFX_icon_32x32.png")) .SetApplyHoverEffect(); QObject::connect(action, &QAction::triggered, this, []() { diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetGroupWidget.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetGroupWidget.cpp index 2fec1f424f..59434da666 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetGroupWidget.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetGroupWidget.cpp @@ -15,7 +15,6 @@ #include #include - namespace EMStudio { From 8b5fe4a015d6c9818ed5da9a0d3740de8d7dad72 Mon Sep 17 00:00:00 2001 From: rgba16f <82187279+rgba16f@users.noreply.github.com> Date: Wed, 10 Nov 2021 10:26:03 -0600 Subject: [PATCH 155/177] Move PipelineStateCache validation of set uniqeness to only be active in debug builds (#5472) Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> --- Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp index 0c06887dd6..79c05c37da 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp @@ -41,9 +41,12 @@ namespace AZ AZ_Assert(readOnlyCache.empty(), "Inactive library has pipeline states in its global entry."); } +#if defined(AZ_DEBUG_BUILD) + // the PipelineStateSet is expensive to duplicate, only do this in debug. PipelineStateSet readOnlyCacheCopy = readOnlyCache; AZ_Assert(AZStd::unique(readOnlyCacheCopy.begin(), readOnlyCacheCopy.end()) == readOnlyCacheCopy.end(), "'%d' Duplicates existed in the read-only cache!", readOnlyCache.size() - readOnlyCacheCopy.size()); +#endif } m_threadLibrarySet.ForEach([this](const ThreadLibrarySet& threadLibrarySet) From ab37eb138ca0fb106ac3ed39eaa7322eef3549fe Mon Sep 17 00:00:00 2001 From: Scott Romero <24445312+AMZN-ScottR@users.noreply.github.com> Date: Wed, 10 Nov 2021 08:43:13 -0800 Subject: [PATCH 156/177] [development] removed CryLibrary (#5474) * [redcode_crylibrary] replaced CrySystem loading in launcher and editor with new custom wrapper that uses AZ::DynamicModuleHandle Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> * [redcode_crylibrary] removed all remaining references to CryLibrary Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> * [redcode_crylibrary] migrate CrySystem loading to use AZ::DynamicModuleHandle directly instead Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> * [redcode_crylibrary] clean up of CrySystemModuleHandle and old CrySystem module [un]init functions Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> * [redcode_crylibrary] added trailing newline to DllMain.cpp in CrySystem Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- Code/Editor/GameEngine.cpp | 29 +-- Code/Editor/GameEngine.h | 10 +- Code/Editor/MainStatusBar.cpp | 1 - Code/LauncherUnified/Launcher.cpp | 153 +------------- .../Platform/Linux/Launcher_Linux.cpp | 13 -- .../Platform/Windows/Launcher_Windows.cpp | 19 -- Code/Legacy/CryCommon/CryLibrary.cpp | 47 ----- Code/Legacy/CryCommon/CryLibrary.h | 193 ------------------ Code/Legacy/CryCommon/ISystem.h | 10 +- Code/Legacy/CryCommon/WinBase.cpp | 1 - Code/Legacy/CryCommon/crycommon_files.cmake | 2 - Code/Legacy/CryCommon/platform_impl.cpp | 26 --- Code/Legacy/CrySystem/DllMain.cpp | 6 +- Code/Legacy/CrySystem/System.cpp | 1 - Code/Legacy/CrySystem/System.h | 5 - Code/Legacy/CrySystem/SystemInit.cpp | 1 - Code/Legacy/CrySystem/SystemWin32.cpp | 34 --- 17 files changed, 24 insertions(+), 527 deletions(-) delete mode 100644 Code/Legacy/CryCommon/CryLibrary.cpp delete mode 100644 Code/Legacy/CryCommon/CryLibrary.h diff --git a/Code/Editor/GameEngine.cpp b/Code/Editor/GameEngine.cpp index 5f3545e593..ff247c1e6c 100644 --- a/Code/Editor/GameEngine.cpp +++ b/Code/Editor/GameEngine.cpp @@ -49,9 +49,6 @@ #include "Include/IObjectManager.h" #include "ActionManager.h" -// Including this too early will result in a linker error -#include - // Implementation of System Callback structure. struct SSystemUserCallback : public ISystemUserCallback @@ -242,8 +239,7 @@ private: AZ_PUSH_DISABLE_WARNING(4273, "-Wunknown-warning-option") CGameEngine::CGameEngine() - : m_gameDll(nullptr) - , m_bIgnoreUpdates(false) + : m_bIgnoreUpdates(false) , m_ePendingGameMode(ePGM_NotPending) , m_modalWindowDismisser(nullptr) AZ_POP_DISABLE_WARNING @@ -253,7 +249,7 @@ AZ_POP_DISABLE_WARNING m_bInGameMode = false; m_bSimulationMode = false; m_bSyncPlayerPosition = true; - m_hSystemHandle = nullptr; + m_hSystemHandle.reset(nullptr); m_bJustCreated = false; m_levelName = "Untitled"; m_levelExtension = EditorUtils::LevelFile::GetDefaultFileExtension(); @@ -268,18 +264,10 @@ AZ_POP_DISABLE_WARNING GetIEditor()->UnregisterNotifyListener(this); m_pISystem->GetIMovieSystem()->SetCallback(nullptr); - if (m_gameDll) - { - CryFreeLibrary(m_gameDll); - } - delete m_pISystem; m_pISystem = nullptr; - if (m_hSystemHandle) - { - CryFreeLibrary(m_hSystemHandle); - } + m_hSystemHandle.reset(nullptr); delete m_pSystemUserCallback; } @@ -347,18 +335,19 @@ AZ::Outcome CGameEngine::Init( HWND hwndForInputSystem) { m_pSystemUserCallback = new SSystemUserCallback(logo); - m_hSystemHandle = CryLoadLibraryDefName("CrySystem"); - if (!m_hSystemHandle) + constexpr const char* crySystemLibraryName = AZ_TRAIT_OS_DYNAMIC_LIBRARY_PREFIX "CrySystem" AZ_TRAIT_OS_DYNAMIC_LIBRARY_EXTENSION; + + m_hSystemHandle = AZ::DynamicModuleHandle::Create(crySystemLibraryName); + if (!m_hSystemHandle->Load(true)) { - auto errorMessage = AZStd::string::format("%s Loading Failed", CryLibraryDefName("CrySystem")); + auto errorMessage = AZStd::string::format("%s Loading Failed", crySystemLibraryName); Error(errorMessage.c_str()); return AZ::Failure(errorMessage); } PFNCREATESYSTEMINTERFACE pfnCreateSystemInterface = - (PFNCREATESYSTEMINTERFACE)CryGetProcAddress(m_hSystemHandle, "CreateSystemInterface"); - + m_hSystemHandle->GetFunction("CreateSystemInterface"); SSystemInitParams sip; diff --git a/Code/Editor/GameEngine.h b/Code/Editor/GameEngine.h index 4d183cc38e..4104e9aeac 100644 --- a/Code/Editor/GameEngine.h +++ b/Code/Editor/GameEngine.h @@ -28,6 +28,8 @@ struct IInitializeUIInfo; #include #include +#include + class ThreadedOnErrorHandler : public QObject { Q_OBJECT @@ -124,11 +126,6 @@ public: return s_pakModifyMutex; } - inline HMODULE GetGameModule() const - { - return m_gameDll; - } - private: void SetGameMode(bool inGame); void SwitchToInGame(); @@ -150,8 +147,7 @@ private: AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING Matrix34 m_playerViewTM; struct SSystemUserCallback* m_pSystemUserCallback; - HMODULE m_hSystemHandle; - HMODULE m_gameDll; + AZStd::unique_ptr m_hSystemHandle; enum EPendingGameMode { ePGM_NotPending, diff --git a/Code/Editor/MainStatusBar.cpp b/Code/Editor/MainStatusBar.cpp index acc7f664df..65502eaba5 100644 --- a/Code/Editor/MainStatusBar.cpp +++ b/Code/Editor/MainStatusBar.cpp @@ -27,7 +27,6 @@ // Editor #include "MainStatusBarItems.h" -#include "CryLibrary.h" #include "ProcessInfo.h" diff --git a/Code/LauncherUnified/Launcher.cpp b/Code/LauncherUnified/Launcher.cpp index 0f832ff1a5..c37f70c3b7 100644 --- a/Code/LauncherUnified/Launcher.cpp +++ b/Code/LauncherUnified/Launcher.cpp @@ -25,7 +25,6 @@ #include -#include #include #include #include @@ -80,146 +79,6 @@ namespace } } -#if AZ_TRAIT_LAUNCHER_USE_CRY_DYNAMIC_MODULE_HANDLE - // mimics AZ::DynamicModuleHandle but uses CryLibrary under the hood, - // which is necessary to properly load legacy Cry libraries on some platforms - class DynamicModuleHandle - { - public: - AZ_CLASS_ALLOCATOR(DynamicModuleHandle, AZ::OSAllocator, 0) - - static AZStd::unique_ptr Create(const char* fullFileName) - { - return AZStd::unique_ptr(aznew DynamicModuleHandle(fullFileName)); - } - - DynamicModuleHandle(const DynamicModuleHandle&) = delete; - DynamicModuleHandle& operator=(const DynamicModuleHandle&) = delete; - - ~DynamicModuleHandle() - { - Unload(); - } - - // argument is strictly to match the API of AZ::DynamicModuleHandle - bool Load(bool unused) - { - AZ_UNUSED(unused); - - if (IsLoaded()) - { - return true; - } - - m_moduleHandle = CryLoadLibrary(m_fileName.c_str()); - return IsLoaded(); - } - - bool Unload() - { - if (!IsLoaded()) - { - return false; - } - - return CryFreeLibrary(m_moduleHandle); - } - - bool IsLoaded() const - { - return m_moduleHandle != nullptr; - } - - const AZ::OSString& GetFilename() const - { - return m_fileName; - } - - template - Function GetFunction(const char* functionName) const - { - if (IsLoaded()) - { - return reinterpret_cast(CryGetProcAddress(m_moduleHandle, functionName)); - } - else - { - return nullptr; - } - } - - - private: - DynamicModuleHandle(const char* fileFullName) - : m_fileName() - , m_moduleHandle(nullptr) - { - m_fileName = AZ::OSString::format("%s%s%s", - CrySharedLibraryPrefix, fileFullName, CrySharedLibraryExtension); - } - - AZ::OSString m_fileName; - HMODULE m_moduleHandle; - }; -#else - // mimics AZ::DynamicModuleHandle but also calls InjectEnvironmentFunction on - // the loaded module which is necessary to properly load legacy Cry libraries - class DynamicModuleHandle - { - public: - AZ_CLASS_ALLOCATOR(DynamicModuleHandle, AZ::OSAllocator, 0); - - static AZStd::unique_ptr Create(const char* fullFileName) - { - return AZStd::unique_ptr(aznew DynamicModuleHandle(fullFileName)); - } - - bool Load(bool isInitializeFunctionRequired) - { - const bool loaded = m_moduleHandle->Load(isInitializeFunctionRequired); - if (loaded) - { - // We need to inject the environment first thing so that allocators are available immediately - InjectEnvironmentFunction injectEnv = GetFunction(INJECT_ENVIRONMENT_FUNCTION); - if (injectEnv) - { - auto env = AZ::Environment::GetInstance(); - injectEnv(env); - } - } - return loaded; - } - - bool Unload() - { - bool unloaded = m_moduleHandle->Unload(); - if (unloaded) - { - DetachEnvironmentFunction detachEnv = GetFunction(DETACH_ENVIRONMENT_FUNCTION); - if (detachEnv) - { - detachEnv(); - } - } - return unloaded; - } - - template - Function GetFunction(const char* functionName) const - { - return m_moduleHandle->GetFunction(functionName); - } - - private: - DynamicModuleHandle(const char* fileFullName) - : m_moduleHandle(AZ::DynamicModuleHandle::Create(fileFullName)) - { - } - - AZStd::unique_ptr m_moduleHandle; - }; -#endif // AZ_TRAIT_LAUNCHER_USE_CRY_DYNAMIC_MODULE_HANDLE - void RunMainLoop(AzGameFramework::GameApplication& gameApplication) { // Ideally we'd just call GameApplication::RunMainLoop instead, but @@ -649,13 +508,13 @@ namespace O3DELauncher // Create CrySystem. #if !defined(AZ_MONOLITHIC_BUILD) - AZStd::unique_ptr crySystemLibrary; - PFNCREATESYSTEMINTERFACE CreateSystemInterface = nullptr; - - crySystemLibrary = DynamicModuleHandle::Create("CrySystem"); - if (crySystemLibrary->Load(false)) + constexpr const char* crySystemLibraryName = AZ_TRAIT_OS_DYNAMIC_LIBRARY_PREFIX "CrySystem" AZ_TRAIT_OS_DYNAMIC_LIBRARY_EXTENSION; + AZStd::unique_ptr crySystemLibrary = AZ::DynamicModuleHandle::Create(crySystemLibraryName); + if (crySystemLibrary->Load(true)) { - CreateSystemInterface = crySystemLibrary->GetFunction("CreateSystemInterface"); + PFNCREATESYSTEMINTERFACE CreateSystemInterface = + crySystemLibrary->GetFunction("CreateSystemInterface"); + if (CreateSystemInterface) { systemInitParams.pSystem = CreateSystemInterface(systemInitParams); diff --git a/Code/LauncherUnified/Platform/Linux/Launcher_Linux.cpp b/Code/LauncherUnified/Platform/Linux/Launcher_Linux.cpp index 3030dcc740..e210a59738 100644 --- a/Code/LauncherUnified/Platform/Linux/Launcher_Linux.cpp +++ b/Code/LauncherUnified/Platform/Linux/Launcher_Linux.cpp @@ -12,8 +12,6 @@ #include // for AZ_MAX_PATH_LEN #include -#include - #include #include #include @@ -86,17 +84,6 @@ int main(int argc, char** argv) using namespace O3DELauncher; -#if !defined(AZ_MONOLITHIC_BUILD) - char exePath[AZ_MAX_PATH_LEN] = { 0 }; - if (readlink("/proc/self/exe", exePath, AZ_MAX_PATH_LEN) == -1) - { - return static_cast(ReturnCode::ErrExePath); - } - - char* runDir = dirname(exePath); - SetModulePath(runDir); -#endif // !defined(AZ_MONOLITHIC_BUILD) - PlatformMainInfo mainInfo; mainInfo.m_updateResourceLimits = IncreaseResourceLimits; diff --git a/Code/LauncherUnified/Platform/Windows/Launcher_Windows.cpp b/Code/LauncherUnified/Platform/Windows/Launcher_Windows.cpp index d1e6a69e5e..0d0ca3e8b0 100644 --- a/Code/LauncherUnified/Platform/Windows/Launcher_Windows.cpp +++ b/Code/LauncherUnified/Platform/Windows/Launcher_Windows.cpp @@ -7,7 +7,6 @@ */ #include -#include #include #include @@ -43,24 +42,6 @@ int APIENTRY WinMain([[maybe_unused]] HINSTANCE hInstance, [[maybe_unused]] HINS MessageBoxA(0, GetReturnCodeString(status), "Error", MB_OK | MB_DEFAULT_DESKTOP_ONLY | MB_ICONERROR); } -#if !defined(AZ_MONOLITHIC_BUILD) - - { - // HACK HACK HACK - is this still needed?!?! - // CrySystem module can get loaded multiple times (even from within CrySystem itself) - // so we will release it as many times as it takes until it actually unloads. - void* hModule = CryLoadLibraryDefName("CrySystem"); - if (hModule) - { - // loop until we fail (aka unload the DLL) - while (CryFreeLibrary(hModule)) - { - ; - } - } - } -#endif // !defined(AZ_MONOLITHIC_BUILD) - // there is no way to transfer ownership of the allocator to the component application // without altering the app descriptor, so it must be destroyed here AZ::AllocatorInstance::Destroy(); diff --git a/Code/Legacy/CryCommon/CryLibrary.cpp b/Code/Legacy/CryCommon/CryLibrary.cpp deleted file mode 100644 index 07c74f5b97..0000000000 --- a/Code/Legacy/CryCommon/CryLibrary.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -#include - -#if !defined(AZ_RESTRICTED_PLATFORM) && defined(WIN32) - -HMODULE CryLoadLibrary(const char* libName) -{ - HMODULE module = ::LoadLibraryA(libName); - if (module != NULL) - { - // We need to inject the environment first thing so that allocators are available immediately - InjectEnvironmentFunction injectEnv = reinterpret_cast(::GetProcAddress(module, INJECT_ENVIRONMENT_FUNCTION)); - if (injectEnv) - { - auto env = AZ::Environment::GetInstance(); - injectEnv(env); - } - } - - return module; -} - -// Cry code seems to have used void* as their abstraction for HMODULE across -// platforms. -bool CryFreeLibrary(void* lib) -{ - if (lib != NULL) - { - DetachEnvironmentFunction detachEnv = reinterpret_cast(::GetProcAddress((HMODULE)lib, DETACH_ENVIRONMENT_FUNCTION)); - if (detachEnv) - { - detachEnv(); - } - return ::FreeLibrary((HMODULE)lib) != FALSE; - } - return false; -} - -#endif diff --git a/Code/Legacy/CryCommon/CryLibrary.h b/Code/Legacy/CryCommon/CryLibrary.h deleted file mode 100644 index 995ba0abc5..0000000000 --- a/Code/Legacy/CryCommon/CryLibrary.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -#pragma once - - -/*! - CryLibrary - - Convenience-Macros which abstract the use of DLLs/shared libraries in a platform independent way. - A short explanation of the different macros follows: - - CrySharedLibrarySupported: - This macro can be used to test if the current active platform supports shared library calls. The default - value is false. This gets redefined if a certain platform (WIN32 or LINUX) is desired. - - CrySharedLibraryPrefix: - The default prefix which will get prepended to library names in calls to CryLoadLibraryDefName - (see below). - - CrySharedLibraryExtension: - The default extension which will get appended to library names in calls to CryLoadLibraryDefName - (see below). - - CryLoadLibrary(libName): - Loads a shared library. - - CryLoadLibraryDefName(libName): - Loads a shared library. The platform-specific default library prefix and extension are appended to the libName. - This allows writing of somewhat platform-independent library loading code and is therefore the function - which should be used most of the time, unless some special extensions are used (e.g. for plugins). - - CryGetProcAddress(libHandle, procName): - Import function from the library presented by libHandle. - - CryFreeLibrary(libHandle): - Unload the library presented by libHandle. - - HISTORY: - 03.03.2004 MarcoK - - initial version - - added to CryPlatform -*/ - -#include -#include -#include - -#define INJECT_ENVIRONMENT_FUNCTION "InjectEnvironment" -#define DETACH_ENVIRONMENT_FUNCTION "DetachEnvironment" -using InjectEnvironmentFunction = void(*)(void*); -using DetachEnvironmentFunction = void(*)(); - -#if defined(AZ_RESTRICTED_PLATFORM) - #include AZ_RESTRICTED_FILE(CryLibrary_h) -#elif defined(WIN32) - #if !defined(WIN32_LEAN_AND_MEAN) - #define WIN32_LEAN_AND_MEAN - #endif - - HMODULE CryLoadLibrary(const char* libName); - - // Cry code seems to have used void* as their abstraction for HMODULE across - // platforms. - bool CryFreeLibrary(void* lib); - - #define CRYLIBRARY_H_TRAIT_USE_WINDOWS_DLL 1 -#elif ((defined(LINUX) || AZ_TRAIT_OS_PLATFORM_APPLE)) - #include - #include - #include - #include "platform.h" - #include - -// for compatibility with code written for windows - #define CrySharedLibrarySupported true - #define CrySharedLibraryPrefix "lib" -#if AZ_TRAIT_OS_PLATFORM_APPLE - #include - #define CrySharedLibraryExtension ".dylib" -#else - #define CrySharedLibraryExtension ".so" -#endif - - #define CryGetProcAddress(libHandle, procName) ::dlsym(libHandle, procName) - #define HMODULE void* -static const char* gEnvName("MODULE_PATH"); - -inline const char* GetModulePath() -{ - return getenv(gEnvName); -} - -inline void SetModulePath(const char* pModulePath) -{ - setenv(gEnvName, pModulePath ? pModulePath : "", true); -} - -// bInModulePath is only ever set to false in RC, because rc needs to load dlls from a $PATH that -// it has modified to include .. -inline HMODULE CryLoadLibrary(const char* libName, bool bLazy = false, bool bInModulePath = true) -{ - const char* libPath = nullptr; - libPath = libName; - -#if !defined(AZ_PLATFORM_ANDROID) - if (bInModulePath) - { - char exePath[MAX_PATH + 1] = { 0 }; - const char* modulePath = GetModulePath(); - if (!modulePath) - { - modulePath = "."; - #if defined(LINUX) - int len = readlink("/proc/self/exe", exePath, MAX_PATH); - if (len != -1) - { - exePath[len] = 0; - modulePath = dirname(exePath); - } - #elif AZ_TRAIT_OS_PLATFORM_APPLE - uint32_t bufsize = MAX_PATH; - if (_NSGetExecutablePath(exePath, &bufsize) == 0) - { - exePath[bufsize] = 0; - modulePath = dirname(exePath); - } - #endif - } - char pathBuffer[MAX_PATH] = {0}; - sprintf_s(pathBuffer, "%s/%s", modulePath, libName); - libPath = pathBuffer; - } -#endif - - HMODULE module; - #if defined(LINUX) && !defined(ANDROID) - module = ::dlopen(libPath, (bLazy ? RTLD_LAZY : RTLD_NOW) | RTLD_DEEPBIND); - #else - module = ::dlopen(libPath, bLazy ? RTLD_LAZY : RTLD_NOW); - #endif - AZ_Warning("LMBR", module, "Can't load library [%s]: %s", libName, dlerror()); - - if (module) - { - // We need to inject the environment first thing so that allocators are available immediately - InjectEnvironmentFunction injectEnv = reinterpret_cast(CryGetProcAddress(module, INJECT_ENVIRONMENT_FUNCTION)); - if (injectEnv) - { - injectEnv(AZ::Environment::GetInstance()); - } - } - - return module; -} - -inline bool CryFreeLibrary(void* lib) -{ - if (lib) - { - DetachEnvironmentFunction detachEnv = reinterpret_cast(CryGetProcAddress(lib, DETACH_ENVIRONMENT_FUNCTION)); - if (detachEnv) - { - detachEnv(); - } - return (::dlclose(lib) == 0); - } - return false; -} -#endif - -#if CRYLIBRARY_H_TRAIT_USE_WINDOWS_DLL -#define CrySharedLibrarySupported true -#define CrySharedLibraryPrefix "" -#define CrySharedLibraryExtension ".dll" -#define CryGetProcAddress(libHandle, procName) ::GetProcAddress((HMODULE)(libHandle), procName) -#elif !defined(CrySharedLibrarySupported) -#define CrySharedLibrarySupported false -#define CrySharedLibraryPrefix "" -#define CrySharedLibraryExtension "" -#define CryLoadLibrary(libName) NULL -#define CryGetProcAddress(libHandle, procName) NULL -#define CryFreeLibrary(libHandle) -#define GetModuleHandle(x) 0 -#endif -#define CryLibraryDefName(libName) CrySharedLibraryPrefix libName CrySharedLibraryExtension -#define CryLoadLibraryDefName(libName) CryLoadLibrary(CryLibraryDefName(libName)) diff --git a/Code/Legacy/CryCommon/ISystem.h b/Code/Legacy/CryCommon/ISystem.h index 103e7b50c2..a243f00ea2 100644 --- a/Code/Legacy/CryCommon/ISystem.h +++ b/Code/Legacy/CryCommon/ISystem.h @@ -1104,23 +1104,15 @@ inline ISystem* GetISystem() // This function must be called once by each module at the beginning, to setup global pointers. void ModuleInitISystem(ISystem* pSystem, const char* moduleName); void ModuleShutdownISystem(ISystem* pSystem); -extern "C" AZ_DLL_EXPORT void InjectEnvironment(void* env); -extern "C" AZ_DLL_EXPORT void DetachEnvironment(); void* GetModuleInitISystemSymbol(); void* GetModuleShutdownISystemSymbol(); -void* GetInjectEnvironmentSymbol(); -void* GetDetachEnvironmentSymbol(); #define PREVENT_MODULE_AND_ENVIRONMENT_SYMBOL_STRIPPING \ AZ_UNUSED(GetModuleInitISystemSymbol()); \ - AZ_UNUSED(GetModuleShutdownISystemSymbol()); \ - AZ_UNUSED(GetInjectEnvironmentSymbol()); \ - AZ_UNUSED(GetDetachEnvironmentSymbol()); + AZ_UNUSED(GetModuleShutdownISystemSymbol()); -extern bool g_bProfilerEnabled; - // Summary: // Interface of the DLL. extern "C" diff --git a/Code/Legacy/CryCommon/WinBase.cpp b/Code/Legacy/CryCommon/WinBase.cpp index 9f7cdfd609..cb1b2aadfd 100644 --- a/Code/Legacy/CryCommon/WinBase.cpp +++ b/Code/Legacy/CryCommon/WinBase.cpp @@ -73,7 +73,6 @@ unsigned int g_EnableMultipleAssert = 0;//set to something else than 0 if to ena #include #include #include -#include "CryLibrary.h" #endif #if defined(APPLE) diff --git a/Code/Legacy/CryCommon/crycommon_files.cmake b/Code/Legacy/CryCommon/crycommon_files.cmake index c1cdcf094a..b3db694ca1 100644 --- a/Code/Legacy/CryCommon/crycommon_files.cmake +++ b/Code/Legacy/CryCommon/crycommon_files.cmake @@ -100,8 +100,6 @@ set(FILES CryAssert_iOS.h CryAssert_Linux.h CryAssert_Mac.h - CryLibrary.cpp - CryLibrary.h Linux32Specific.h Linux64Specific.h Linux_Win32Wrapper.h diff --git a/Code/Legacy/CryCommon/platform_impl.cpp b/Code/Legacy/CryCommon/platform_impl.cpp index 845a1101a4..6e61e64c4b 100644 --- a/Code/Legacy/CryCommon/platform_impl.cpp +++ b/Code/Legacy/CryCommon/platform_impl.cpp @@ -102,22 +102,6 @@ void ModuleShutdownISystem([[maybe_unused]] ISystem* pSystem) AZ::Environment::Detach(); } -extern "C" AZ_DLL_EXPORT void InjectEnvironment(void* env) -{ - static bool injected = false; - if (!injected) - { - AZ::Environment::Attach(reinterpret_cast(env)); - AZ::AllocatorManager::Instance(); // Force the AllocatorManager to instantiate and register any allocators defined in data sections - injected = true; - } -} - -extern "C" AZ_DLL_EXPORT void DetachEnvironment() -{ - AZ::Environment::Detach(); -} - void* GetModuleInitISystemSymbol() { return reinterpret_cast(&ModuleInitISystem); @@ -126,16 +110,6 @@ void* GetModuleShutdownISystemSymbol() { return reinterpret_cast(&ModuleShutdownISystem); } -void* GetInjectEnvironmentSymbol() -{ - return reinterpret_cast(&InjectEnvironment); -} -void* GetDetachEnvironmentSymbol() -{ - return reinterpret_cast(&DetachEnvironment); -} - -bool g_bProfilerEnabled = false; ////////////////////////////////////////////////////////////////////////// // global random number generator used by cry_random functions diff --git a/Code/Legacy/CrySystem/DllMain.cpp b/Code/Legacy/CrySystem/DllMain.cpp index 4a31cb51a0..cbfec93ed9 100644 --- a/Code/Legacy/CrySystem/DllMain.cpp +++ b/Code/Legacy/CrySystem/DllMain.cpp @@ -12,6 +12,8 @@ #include #include "DebugCallStack.h" +#include // for AZ_DECLARE_MODULE_INITIALIZATION + #if defined(AZ_RESTRICTED_PLATFORM) #undef AZ_RESTRICTED_SECTION #define DLLMAIN_CPP_SECTION_1 1 @@ -67,7 +69,7 @@ CRYSYSTEM_API ISystem* CreateSystemInterface(const SSystemInitParams& startupPar // We must attach to the environment prior to allocating CSystem, as opposed to waiting // for ModuleInitISystem(), because the log message sink uses buses. - // Environment should have been attached via InjectEnvironment + // Environment should have been attached via InitializeDynamicModule AZ_Assert(AZ::Environment::IsReady(), "Environment is not attached, must be attached before CreateSystemInterface can be called"); pSystem = new CSystem(startupParams.pSharedEnvironment); @@ -115,3 +117,5 @@ CRYSYSTEM_API ISystem* CreateSystemInterface(const SSystemInitParams& startupPar } }; +// declare the functions used by AZ::DynamicModule to [un]initialize the library here +AZ_DECLARE_MODULE_INITIALIZATION diff --git a/Code/Legacy/CrySystem/System.cpp b/Code/Legacy/CrySystem/System.cpp index 65facba4dd..a6ffdec4e8 100644 --- a/Code/Legacy/CrySystem/System.cpp +++ b/Code/Legacy/CrySystem/System.cpp @@ -16,7 +16,6 @@ #include #include #include -#include "CryLibrary.h" #include #include #include diff --git a/Code/Legacy/CrySystem/System.h b/Code/Legacy/CrySystem/System.h index a10c1f8ed8..fd5c917a94 100644 --- a/Code/Legacy/CrySystem/System.h +++ b/Code/Legacy/CrySystem/System.h @@ -85,10 +85,6 @@ class CWatchdogThread; #endif -#if defined(LINUX) - #include "CryLibrary.h" -#endif - #ifdef WIN32 using WIN_HMODULE = void*; #else @@ -226,7 +222,6 @@ public: void Quit() override; bool IsQuitting() const override; void ShutdownFileSystem(); // used to cleanup any file resources, such as cache handle. - void SetAffinity(); const char* GetUserName() override; int GetApplicationInstance() override; int GetApplicationLogInstance(const char* logFilePath) override; diff --git a/Code/Legacy/CrySystem/SystemInit.cpp b/Code/Legacy/CrySystem/SystemInit.cpp index d9ba3bb4c9..5bef42dc3e 100644 --- a/Code/Legacy/CrySystem/SystemInit.cpp +++ b/Code/Legacy/CrySystem/SystemInit.cpp @@ -30,7 +30,6 @@ #define SYSTEMINIT_CPP_SECTION_17 17 #endif -#include "CryLibrary.h" #include "CryPath.h" #include diff --git a/Code/Legacy/CrySystem/SystemWin32.cpp b/Code/Legacy/CrySystem/SystemWin32.cpp index 4f0a154afe..2303ff286e 100644 --- a/Code/Legacy/CrySystem/SystemWin32.cpp +++ b/Code/Legacy/CrySystem/SystemWin32.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include // for AZ_MAX_PATH_LEN #include @@ -70,39 +69,6 @@ const char* g_szModuleGroups[][2] = { {"CrySystem.dll", g_szGroupCore} }; -////////////////////////////////////////////////////////////////////////// -void CSystem::SetAffinity() -{ - // the following code is only for Windows -#ifdef WIN32 - // set the process affinity - ICVar* pcvAffinityMask = GetIConsole()->GetCVar("sys_affinity"); - if (!pcvAffinityMask) - { - pcvAffinityMask = REGISTER_INT("sys_affinity", 0, VF_NULL, ""); - } - - if (pcvAffinityMask) - { - unsigned nAffinity = pcvAffinityMask->GetIVal(); - if (nAffinity) - { - typedef BOOL (WINAPI * FnSetProcessAffinityMask)(IN HANDLE hProcess, IN DWORD_PTR dwProcessAffinityMask); - HMODULE hKernel = CryLoadLibrary ("kernel32.dll"); - if (hKernel) - { - FnSetProcessAffinityMask SetProcessAffinityMask = (FnSetProcessAffinityMask)GetProcAddress(hKernel, "SetProcessAffinityMask"); - if (SetProcessAffinityMask && !SetProcessAffinityMask(GetCurrentProcess(), nAffinity)) - { - GetILog()->LogError("Error: Cannot set affinity mask %d, error code %d", nAffinity, GetLastError()); - } - FreeLibrary (hKernel); - } - } - } -#endif -} - #if defined(WIN32) #pragma pack(push,1) struct PEHeader_DLL From 21f9a789c13d17341be3a04d38360c9d5f06cab9 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Wed, 10 Nov 2021 08:57:39 -0800 Subject: [PATCH 157/177] Merged the Editor.Camera.Tests with the Editor.Tests (#5463) * Merged the Edtiror.Camera.Tests witht eh Editor.Tests Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * Adds dependency to Camera.Editor gem which is used by the test Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * Inheirts from TraceBusHook instead of adding the default env to the test Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * makes order consistent between Setup/Teardown Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * adds missing header for non-unity builds Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * Removes dependency to Camera gem Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- Code/Editor/CMakeLists.txt | 31 +--------- Code/Editor/EditorEnvironment.cpp | 2 +- .../Camera/editor_lib_camera_test_files.cmake | 11 ---- .../Lib/Tests/Camera/test_EditorCamera.cpp | 57 +++++++------------ Code/Editor/Lib/Tests/test_Main.cpp | 11 +++- Code/Editor/editor_lib_test_files.cmake | 1 + .../AzTest/AzTest/GemTestEnvironment.cpp | 5 ++ .../AzTest/AzTest/GemTestEnvironment.h | 2 +- 8 files changed, 39 insertions(+), 81 deletions(-) delete mode 100644 Code/Editor/Lib/Tests/Camera/editor_lib_camera_test_files.cmake diff --git a/Code/Editor/CMakeLists.txt b/Code/Editor/CMakeLists.txt index bdfac373eb..693799e08b 100644 --- a/Code/Editor/CMakeLists.txt +++ b/Code/Editor/CMakeLists.txt @@ -249,38 +249,9 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) RUNTIME_DEPENDENCIES Gem::LmbrCentral ) + ly_add_googletest( NAME Legacy::EditorLib.Tests ) - ly_add_target( - NAME EditorLib.Camera.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} - NAMESPACE Legacy - FILES_CMAKE - Lib/Tests/Camera/editor_lib_camera_test_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - . - BUILD_DEPENDENCIES - PRIVATE - AZ::AzCore - AZ::AzTest - AZ::AzToolsFramework - AZ::AzTestShared - Legacy::EditorLib - Gem::Camera.Editor - Gem::AtomToolsFramework.Static - RUNTIME_DEPENDENCIES - Legacy::EditorLib - ) - - ly_add_source_properties( - SOURCES Lib/Tests/Camera/test_EditorCamera.cpp - PROPERTY COMPILE_DEFINITIONS - VALUES CAMERA_EDITOR_MODULE="$" - ) - - ly_add_googletest( - NAME Legacy::EditorLib.Camera.Tests - ) endif() diff --git a/Code/Editor/EditorEnvironment.cpp b/Code/Editor/EditorEnvironment.cpp index 463fec8d08..2d675275ec 100644 --- a/Code/Editor/EditorEnvironment.cpp +++ b/Code/Editor/EditorEnvironment.cpp @@ -17,7 +17,7 @@ void SetEditorEnvironment(SSystemGlobalEnvironment* pEnv) void AttachEditorAZEnvironment(AZ::EnvironmentInstance azEnv) { - AZ::Environment::Attach(azEnv, true); + AZ::Environment::Attach(azEnv); } void DetachEditorAZEnvironment() diff --git a/Code/Editor/Lib/Tests/Camera/editor_lib_camera_test_files.cmake b/Code/Editor/Lib/Tests/Camera/editor_lib_camera_test_files.cmake deleted file mode 100644 index 69d3e37f2d..0000000000 --- a/Code/Editor/Lib/Tests/Camera/editor_lib_camera_test_files.cmake +++ /dev/null @@ -1,11 +0,0 @@ -# -# Copyright (c) Contributors to the Open 3D Engine Project. -# For complete copyright and license terms please see the LICENSE at the root of this distribution. -# -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# - -set(FILES - test_EditorCamera.cpp -) diff --git a/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp b/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp index 1a44d43370..637b9c44c5 100644 --- a/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp +++ b/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp @@ -17,43 +17,33 @@ namespace UnitTest { - class EditorCameraTestEnvironment : public AZ::Test::GemTestEnvironment - { - // AZ::Test::GemTestEnvironment overrides ... - void AddGemsAndComponents() override; - }; - - void EditorCameraTestEnvironment::AddGemsAndComponents() - { - AddDynamicModulePaths({ CAMERA_EDITOR_MODULE }); - AddComponentDescriptors({ AzToolsFramework::Components::TransformComponent::CreateDescriptor() }); - } - class EditorCameraFixture : public ::testing::Test { public: + AZ::ComponentApplication* m_application = nullptr; AtomToolsFramework::ModularCameraViewportContext* m_cameraViewportContextView = nullptr; AZStd::unique_ptr m_editorModularViewportCameraComposer; - AZStd::unique_ptr m_editorLibHandle; AzFramework::ViewportControllerListPtr m_controllerList; - AZStd::unique_ptr m_entity; + AZ::Entity* m_entity = nullptr; + AZ::ComponentDescriptor* m_transformComponent = nullptr; static const AzFramework::ViewportId TestViewportId; void SetUp() override { - m_editorLibHandle = AZ::DynamicModuleHandle::Create("EditorLib"); - [[maybe_unused]] const bool loaded = m_editorLibHandle->Load(true); - AZ_Assert(loaded, "EditorLib could not be loaded"); + m_application = aznew AZ::ComponentApplication; + AZ::ComponentApplication::Descriptor appDesc; + m_entity = m_application->Create(appDesc); + m_transformComponent = AzToolsFramework::Components::TransformComponent::CreateDescriptor(); + m_application->RegisterComponentDescriptor(m_transformComponent); - m_controllerList = AZStd::make_shared(); - m_controllerList->RegisterViewportContext(TestViewportId); - - m_entity = AZStd::make_unique(); m_entity->Init(); m_entity->CreateComponent(); m_entity->Activate(); + m_controllerList = AZStd::make_shared(); + m_controllerList->RegisterViewportContext(TestViewportId); + m_editorModularViewportCameraComposer = AZStd::make_unique(TestViewportId); auto controller = m_editorModularViewportCameraComposer->CreateModularViewportCameraController(); @@ -72,8 +62,17 @@ namespace UnitTest { m_editorModularViewportCameraComposer.reset(); m_cameraViewportContextView = nullptr; - m_entity.reset(); - m_editorLibHandle = {}; + + if (m_application) + { + m_application->UnregisterComponentDescriptor(m_transformComponent); + delete m_transformComponent; + m_transformComponent = nullptr; + + m_application->Destroy(); + delete m_application; + m_application = nullptr; + } } }; @@ -211,15 +210,3 @@ namespace UnitTest EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity())); } } // namespace UnitTest - -// required to support running integration tests with the Camera Gem -AZTEST_EXPORT int AZ_UNIT_TEST_HOOK_NAME(int argc, char** argv) -{ - ::testing::InitGoogleMock(&argc, argv); - AZ::Test::printUnusedParametersWarning(argc, argv); - AZ::Test::addTestEnvironments({ new UnitTest::EditorCameraTestEnvironment() }); - int result = RUN_ALL_TESTS(); - return result; -} - -IMPLEMENT_TEST_EXECUTABLE_MAIN(); diff --git a/Code/Editor/Lib/Tests/test_Main.cpp b/Code/Editor/Lib/Tests/test_Main.cpp index 6250c540db..91369ee8b6 100644 --- a/Code/Editor/Lib/Tests/test_Main.cpp +++ b/Code/Editor/Lib/Tests/test_Main.cpp @@ -9,12 +9,13 @@ #include "EditorDefs.h" #include #include +#include #include #include class EditorLibTestEnvironment - : public AZ::Test::ITestEnvironment + : public ::UnitTest::TraceBusHook { public: ~EditorLibTestEnvironment() override = default; @@ -22,16 +23,20 @@ public: protected: void SetupEnvironment() override { + ::UnitTest::TraceBusHook::SetupEnvironment(); + AZ::Environment::Create(nullptr); - AttachEditorAZEnvironment(AZ::Environment::GetInstance()); AZ::AllocatorInstance::Create(); + AttachEditorAZEnvironment(AZ::Environment::GetInstance()); } void TeardownEnvironment() override { - AZ::AllocatorInstance::Destroy(); DetachEditorAZEnvironment(); + AZ::AllocatorInstance::Destroy(); AZ::Environment::Destroy(); + + ::UnitTest::TraceBusHook::TeardownEnvironment(); } }; diff --git a/Code/Editor/editor_lib_test_files.cmake b/Code/Editor/editor_lib_test_files.cmake index 2ae3d22c19..17f36228db 100644 --- a/Code/Editor/editor_lib_test_files.cmake +++ b/Code/Editor/editor_lib_test_files.cmake @@ -22,6 +22,7 @@ set(FILES Lib/Tests/test_DisplaySettingsPythonBindings.cpp Lib/Tests/test_ViewportManipulatorController.cpp Lib/Tests/test_ModularViewportCameraController.cpp + Lib/Tests/Camera/test_EditorCamera.cpp DisplaySettingsPythonFuncs.cpp DisplaySettingsPythonFuncs.h ) diff --git a/Code/Framework/AzTest/AzTest/GemTestEnvironment.cpp b/Code/Framework/AzTest/AzTest/GemTestEnvironment.cpp index dae1683bd5..de7f8f17c2 100644 --- a/Code/Framework/AzTest/AzTest/GemTestEnvironment.cpp +++ b/Code/Framework/AzTest/AzTest/GemTestEnvironment.cpp @@ -140,6 +140,11 @@ namespace AZ void GemTestEnvironment::TeardownEnvironment() { + for (AZ::ComponentDescriptor* descriptor : m_parameters->m_componentDescriptors) + { + m_application->UnregisterComponentDescriptor(descriptor); + } + const AZ::Entity::ComponentArrayType& components = m_gemEntity->GetComponents(); for (auto itComponent = components.rbegin(); itComponent != components.rend(); ++itComponent) { diff --git a/Code/Framework/AzTest/AzTest/GemTestEnvironment.h b/Code/Framework/AzTest/AzTest/GemTestEnvironment.h index 9008c1ab54..ad495e40c4 100644 --- a/Code/Framework/AzTest/AzTest/GemTestEnvironment.h +++ b/Code/Framework/AzTest/AzTest/GemTestEnvironment.h @@ -18,7 +18,7 @@ namespace AZ /// A test environment which is intended to facilitate writing unit tests which require components from a gem. class GemTestEnvironment - : public UnitTest::TraceBusHook + : public ::UnitTest::TraceBusHook { public: GemTestEnvironment(); From f5c00e9e72778944ff243388c6f77418b421369b Mon Sep 17 00:00:00 2001 From: SJ Date: Wed, 10 Nov 2021 09:31:49 -0800 Subject: [PATCH 158/177] Fix Editor crashes on asset failures (#5421) * 1. Add nullptr checks to prevent crashes when non-critical shaders fail to compile. 2. Add a higher "launch_ap_timeout" for Mac because launching a newly built/downloaded AP can take a while. Signed-off-by: amzn-sj * Fix one more nullptr dereference. Signed-off-by: amzn-sj --- .../PostProcessing/BlendColorGradingLutsPass.cpp | 6 +++++- .../ReflectionProbeFeatureProcessor.cpp | 7 ++++++- .../RPI.Public/Pass/FullscreenTrianglePass.cpp | 6 ++++++ Registry/Platform/Mac/bootstrap_overrides.setreg | 12 ++++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 Registry/Platform/Mac/bootstrap_overrides.setreg diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp index f74837bd9a..ca93898d5e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/BlendColorGradingLutsPass.cpp @@ -46,7 +46,11 @@ namespace AZ void BlendColorGradingLutsPass::InitializeShaderVariant() { - AZ_Assert(m_shader != nullptr, "BlendColorGradingLutsPass %s has a null shader when calling InitializeShaderVariant.", GetPathName().GetCStr()); + if (m_shader == nullptr) + { + AZ_Assert(false, "BlendColorGradingLutsPass %s has a null shader when calling InitializeShaderVariant.", GetPathName().GetCStr()); + return; + } // Total variations is MaxBlendLuts plus one for the fallback case that none of the LUTs are found, // and hence zero LUTs are blended resulting in an identity LUT. diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp index 52d089ae0d..ae63ff1dde 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp @@ -431,7 +431,12 @@ namespace AZ { // load shader shader = RPI::LoadCriticalShader(filePath); - AZ_Error("ReflectionProbeFeatureProcessor", shader, "Failed to find asset for shader [%s]", filePath); + + if (shader == nullptr) + { + AZ_Error("ReflectionProbeFeatureProcessor", false, "Failed to find asset for shader [%s]", filePath); + return; + } // store drawlist tag drawListTag = shader->GetDrawListTag(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/FullscreenTrianglePass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/FullscreenTrianglePass.cpp index 0bd32344d6..36d851f8d8 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/FullscreenTrianglePass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/FullscreenTrianglePass.cpp @@ -142,6 +142,12 @@ namespace AZ RHI::DrawLinear draw = RHI::DrawLinear(); draw.m_vertexCount = 3; + if (m_shader == nullptr) + { + AZ_Error("PassSystem", false, "[FullscreenTrianglePass]: Shader not loaded!"); + return; + } + RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor; // [GFX TODO][ATOM-872] The pass should be able to drive the shader variant diff --git a/Registry/Platform/Mac/bootstrap_overrides.setreg b/Registry/Platform/Mac/bootstrap_overrides.setreg new file mode 100644 index 0000000000..4e1ca76724 --- /dev/null +++ b/Registry/Platform/Mac/bootstrap_overrides.setreg @@ -0,0 +1,12 @@ +{ + "Amazon": { + "AzCore": { + "Bootstrap": { + // The first time an application is launched on MacOS, each + // dynamic library is inspected by the OS before being loaded. + // This can take a while on some Macs. + "launch_ap_timeout": 300 + } + } + } +} From 528a7478769bc947210f6a66c447c037ee70eb6d Mon Sep 17 00:00:00 2001 From: bosnichd Date: Wed, 10 Nov 2021 11:28:21 -0700 Subject: [PATCH 159/177] Don't allocate memory when processing WM_INPUT messages. (#5491) This change was made years ago in CrySystem (see CSystem::HandleMessage in System.cpp), but looks like it never made it into the NativeWindow_Windows version. Signed-off-by: bosnichd --- .../Windows/AzFramework/Windowing/NativeWindow_Windows.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp b/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp index 3bd0abab8d..80428e2557 100644 --- a/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp +++ b/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp @@ -11,6 +11,7 @@ #include #include +#include #include namespace AzFramework @@ -234,14 +235,14 @@ namespace AzFramework const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER); GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &rawInputSize, rawInputHeaderSize); - LPBYTE rawInputBytes = new BYTE[rawInputSize]; + AZStd::array rawInputBytesArray; + LPBYTE rawInputBytes = rawInputBytesArray.data(); GetRawInputData((HRAWINPUT)lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize); RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes; AzFramework::RawInputNotificationBusWindows::Broadcast( &AzFramework::RawInputNotificationBusWindows::Events::OnRawInputEvent, *rawInput); - delete [] rawInputBytes; break; } case WM_CHAR: From b038faf9caf6d6ba0683fa83f7f1a1170c3a7a99 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Thu, 21 Oct 2021 16:04:38 -0500 Subject: [PATCH 160/177] material editor loads source data Signed-off-by: Guthrie Adams --- .../RPI.Edit/Material/MaterialSourceData.h | 25 ++- .../RPI.Edit/Material/MaterialSourceData.cpp | 209 +++++++++++++----- .../Material/MaterialTypeAssetCreator.cpp | 1 + .../Code/Source/Document/MaterialDocument.cpp | 2 +- 4 files changed, 172 insertions(+), 65 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h index a67477f061..739c4341a9 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h @@ -31,6 +31,7 @@ namespace AZ static constexpr const char UvGroupName[] = "uvSets"; class MaterialAsset; + class MaterialAssetCreator; //! This is a simple data structure for serializing in/out material source files. class MaterialSourceData final @@ -78,15 +79,33 @@ namespace AZ //! Creates a MaterialAsset from the MaterialSourceData content. //! @param assetId ID for the MaterialAsset - //! @param materialSourceFilePath Indicates the path of the .material file that the MaterialSourceData represents. Used for resolving file-relative paths. + //! @param materialSourceFilePath Indicates the path of the .material file that the MaterialSourceData represents. Used for + //! resolving file-relative paths. //! @param elevateWarnings Indicates whether to treat warnings as errors //! @param includeMaterialPropertyNames Indicates whether to save material property names into the material asset file Outcome> CreateMaterialAsset( Data::AssetId assetId, AZStd::string_view materialSourceFilePath = "", bool elevateWarnings = true, - bool includeMaterialPropertyNames = true - ) const; + bool includeMaterialPropertyNames = true) const; + + //! Creates a MaterialAsset from the MaterialSourceData content. + //! @param assetId ID for the MaterialAsset + //! @param materialSourceFilePath Indicates the path of the .material file that the MaterialSourceData represents. Used for + //! resolving file-relative paths. + //! @param elevateWarnings Indicates whether to treat warnings as errors + //! @param includeMaterialPropertyNames Indicates whether to save material property names into the material asset file + Outcome> CreateMaterialAssetFromSourceData( + Data::AssetId assetId, + AZStd::string_view materialSourceFilePath = "", + bool elevateWarnings = true, + bool includeMaterialPropertyNames = true) const; + + private: + static void ApplyMaterialSourceDataPropertiesToAssetCreator( + AZ::RPI::MaterialAssetCreator& materialAssetCreator, + const AZStd::string_view& materialSourceFilePath, + const MaterialSourceData& materialSourceData); }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp index 1467b017d5..2a76befdf4 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -126,7 +127,8 @@ namespace AZ return changesWereApplied ? ApplyVersionUpdatesResult::UpdatesApplied : ApplyVersionUpdatesResult::NoUpdates; } - Outcome > MaterialSourceData::CreateMaterialAsset(Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const + Outcome> MaterialSourceData::CreateMaterialAsset( + Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const { MaterialAssetCreator materialAssetCreator; materialAssetCreator.SetElevateWarnings(elevateWarnings); @@ -172,66 +174,7 @@ namespace AZ materialAssetCreator.Begin(assetId, *parentMaterialAsset.GetValue().Get(), includeMaterialPropertyNames); } - for (auto& group : m_properties) - { - for (auto& property : group.second) - { - MaterialPropertyId propertyId{ group.first, property.first }; - if (!property.second.m_value.IsValid()) - { - AZ_Warning("Material source data", false, "Source data for material property value is invalid."); - } - else - { - MaterialPropertyIndex propertyIndex = materialAssetCreator.m_materialPropertiesLayout->FindPropertyIndex(propertyId.GetFullName()); - if (propertyIndex.IsValid()) - { - const MaterialPropertyDescriptor* propertyDescriptor = materialAssetCreator.m_materialPropertiesLayout->GetPropertyDescriptor(propertyIndex); - switch (propertyDescriptor->GetDataType()) - { - case MaterialPropertyDataType::Image: - { - Outcome> imageAssetResult = MaterialUtils::GetImageAssetReference(materialSourceFilePath, property.second.m_value.GetValue()); - - if (imageAssetResult.IsSuccess()) - { - auto& imageAsset = imageAssetResult.GetValue(); - // Load referenced images when load material - imageAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad); - materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset); - } - else - { - materialAssetCreator.ReportError("Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(), property.second.m_value.GetValue().data()); - } - } - break; - case MaterialPropertyDataType::Enum: - { - AZ::Name enumName = AZ::Name(property.second.m_value.GetValue()); - uint32_t enumValue = propertyDescriptor->GetEnumValue(enumName); - if (enumValue == MaterialPropertyDescriptor::InvalidEnumValue) - { - materialAssetCreator.ReportError("Enum value '%s' couldn't be found in the 'enumValues' list", enumName.GetCStr()); - } - else - { - materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), enumValue); - } - } - break; - default: - materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), property.second.m_value); - break; - } - } - else - { - materialAssetCreator.ReportWarning("Can not find property id '%s' in MaterialPropertyLayout", propertyId.GetFullName().GetStringView().data()); - } - } - } - } + ApplyMaterialSourceDataPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath, *this); Data::Asset material; if (materialAssetCreator.End(material)) @@ -244,5 +187,149 @@ namespace AZ } } + Outcome> MaterialSourceData::CreateMaterialAssetFromSourceData( + Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const + { + MaterialAssetCreator materialAssetCreator; + materialAssetCreator.SetElevateWarnings(elevateWarnings); + + MaterialTypeSourceData materialTypeSourceData; + AZStd::string materialTypeSourcePath = AssetUtils::ResolvePathReference(materialSourceFilePath, m_materialType); + if (!AZ::RPI::JsonUtils::LoadObjectFromFile(materialTypeSourcePath, materialTypeSourceData)) + { + return Failure(); + } + + materialTypeSourceData.ResolveUvEnums(); + + auto materialTypeAsset = + materialTypeSourceData.CreateMaterialTypeAsset(AZ::Uuid::CreateRandom(), materialTypeSourcePath, elevateWarnings); + if (!materialTypeAsset.IsSuccess()) + { + return Failure(); + } + + materialAssetCreator.Begin(assetId, *materialTypeAsset.GetValue().Get(), includeMaterialPropertyNames); + + AZStd::vector parentMaterialSourceDataVec; + + AZStd::string parentMaterialPath = m_parentMaterial; + AZStd::string parentMaterialSourcePath = AssetUtils::ResolvePathReference(materialSourceFilePath, parentMaterialPath); + while (!parentMaterialPath.empty()) + { + MaterialSourceData parentMaterialSourceData; + if (!AZ::RPI::JsonUtils::LoadObjectFromFile(parentMaterialSourcePath, parentMaterialSourceData)) + { + return Failure(); + } + + // Make sure the parent material has the same material type + auto materialTypeIdOutcome1 = AssetUtils::MakeAssetId(materialSourceFilePath, m_materialType, 0); + auto materialTypeIdOutcome2 = AssetUtils::MakeAssetId(parentMaterialSourcePath, parentMaterialSourceData.m_materialType, 0); + if (!materialTypeIdOutcome1.IsSuccess() || !materialTypeIdOutcome2.IsSuccess() || + materialTypeIdOutcome1.GetValue() != materialTypeIdOutcome2.GetValue()) + { + AZ_Error("MaterialSourceData", false, "This material and its parent material do not share the same material type."); + return Failure(); + } + + parentMaterialPath = parentMaterialSourceData.m_parentMaterial; + parentMaterialSourcePath = AssetUtils::ResolvePathReference(parentMaterialSourcePath, parentMaterialPath); + parentMaterialSourceDataVec.push_back(parentMaterialSourceData); + } + + AZStd::reverse(parentMaterialSourceDataVec.begin(), parentMaterialSourceDataVec.end()); + for (const auto& parentMaterialSourceData : parentMaterialSourceDataVec) + { + ApplyMaterialSourceDataPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath, parentMaterialSourceData); + } + + ApplyMaterialSourceDataPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath, *this); + + Data::Asset material; + if (materialAssetCreator.End(material)) + { + return Success(material); + } + else + { + return Failure(); + } + } + + void MaterialSourceData::ApplyMaterialSourceDataPropertiesToAssetCreator( + AZ::RPI::MaterialAssetCreator& materialAssetCreator, + const AZStd::string_view& materialSourceFilePath, + const MaterialSourceData& materialSourceData) + { + for (auto& group : materialSourceData.m_properties) + { + for (auto& property : group.second) + { + MaterialPropertyId propertyId{ group.first, property.first }; + if (!property.second.m_value.IsValid()) + { + AZ_Warning("Material source data", false, "Source data for material property value is invalid."); + } + else + { + MaterialPropertyIndex propertyIndex = + materialAssetCreator.m_materialPropertiesLayout->FindPropertyIndex(propertyId.GetFullName()); + if (propertyIndex.IsValid()) + { + const MaterialPropertyDescriptor* propertyDescriptor = + materialAssetCreator.m_materialPropertiesLayout->GetPropertyDescriptor(propertyIndex); + switch (propertyDescriptor->GetDataType()) + { + case MaterialPropertyDataType::Image: + { + Outcome> imageAssetResult = MaterialUtils::GetImageAssetReference( + materialSourceFilePath, property.second.m_value.GetValue()); + + if (imageAssetResult.IsSuccess()) + { + auto& imageAsset = imageAssetResult.GetValue(); + // Load referenced images when load material + imageAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad); + materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset); + } + else + { + materialAssetCreator.ReportError( + "Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(), + property.second.m_value.GetValue().data()); + } + } + break; + case MaterialPropertyDataType::Enum: + { + AZ::Name enumName = AZ::Name(property.second.m_value.GetValue()); + uint32_t enumValue = propertyDescriptor->GetEnumValue(enumName); + if (enumValue == MaterialPropertyDescriptor::InvalidEnumValue) + { + materialAssetCreator.ReportError( + "Enum value '%s' couldn't be found in the 'enumValues' list", enumName.GetCStr()); + } + else + { + materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), enumValue); + } + } + break; + default: + materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), property.second.m_value); + break; + } + } + else + { + materialAssetCreator.ReportWarning( + "Can not find property id '%s' in MaterialPropertyLayout", propertyId.GetFullName().GetStringView().data()); + } + } + } + } + } + } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp index 46086dfecc..c81cf31d09 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp @@ -43,6 +43,7 @@ namespace AZ return false; } + m_asset->PostLoadInit(); m_asset->SetReady(); m_materialShaderResourceGroupLayout = nullptr; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index 26c2a6145e..f22a40a77b 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -716,7 +716,7 @@ namespace MaterialEditor // we can create the asset dynamically from the source data. // Long term, the material document should not be concerned with assets at all. The viewport window should be the // only thing concerned with assets or instances. - auto createResult = m_materialSourceData.CreateMaterialAsset(Uuid::CreateRandom(), m_absolutePath, true); + auto createResult = m_materialSourceData.CreateMaterialAssetFromSourceData(Uuid::CreateRandom(), m_absolutePath, true); if (!createResult) { AZ_Error("MaterialDocument", false, "Material asset could not be created from source data: '%s'.", m_absolutePath.c_str()); From b72970201b7334a7b218d08e79bd8d4bbb1e9f12 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Fri, 22 Oct 2021 01:30:12 -0500 Subject: [PATCH 161/177] cleanup and setting asset preload flags Signed-off-by: Guthrie Adams --- .../RPI.Edit/Material/MaterialSourceData.h | 6 +-- .../RPI.Edit/Material/MaterialSourceData.cpp | 38 +++++++++---------- .../Material/MaterialTypeSourceData.cpp | 29 ++++++++------ 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h index 739c4341a9..17dc4556fb 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h @@ -102,10 +102,8 @@ namespace AZ bool includeMaterialPropertyNames = true) const; private: - static void ApplyMaterialSourceDataPropertiesToAssetCreator( - AZ::RPI::MaterialAssetCreator& materialAssetCreator, - const AZStd::string_view& materialSourceFilePath, - const MaterialSourceData& materialSourceData); + void ApplyPropertiesToAssetCreator( + AZ::RPI::MaterialAssetCreator& materialAssetCreator, const AZStd::string_view& materialSourceFilePath) const; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp index 2a76befdf4..877e12ab2e 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -174,7 +174,7 @@ namespace AZ materialAssetCreator.Begin(assetId, *parentMaterialAsset.GetValue().Get(), includeMaterialPropertyNames); } - ApplyMaterialSourceDataPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath, *this); + ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath); Data::Asset material; if (materialAssetCreator.End(material)) @@ -211,21 +211,21 @@ namespace AZ materialAssetCreator.Begin(assetId, *materialTypeAsset.GetValue().Get(), includeMaterialPropertyNames); - AZStd::vector parentMaterialSourceDataVec; + AZStd::vector parentSourceDataStack; - AZStd::string parentMaterialPath = m_parentMaterial; - AZStd::string parentMaterialSourcePath = AssetUtils::ResolvePathReference(materialSourceFilePath, parentMaterialPath); - while (!parentMaterialPath.empty()) + AZStd::string parentSourceRelPath = m_parentMaterial; + AZStd::string parentSourceAbsPath = AssetUtils::ResolvePathReference(materialSourceFilePath, parentSourceRelPath); + while (!parentSourceRelPath.empty()) { - MaterialSourceData parentMaterialSourceData; - if (!AZ::RPI::JsonUtils::LoadObjectFromFile(parentMaterialSourcePath, parentMaterialSourceData)) + MaterialSourceData parentSourceData; + if (!AZ::RPI::JsonUtils::LoadObjectFromFile(parentSourceAbsPath, parentSourceData)) { return Failure(); } // Make sure the parent material has the same material type auto materialTypeIdOutcome1 = AssetUtils::MakeAssetId(materialSourceFilePath, m_materialType, 0); - auto materialTypeIdOutcome2 = AssetUtils::MakeAssetId(parentMaterialSourcePath, parentMaterialSourceData.m_materialType, 0); + auto materialTypeIdOutcome2 = AssetUtils::MakeAssetId(parentSourceAbsPath, parentSourceData.m_materialType, 0); if (!materialTypeIdOutcome1.IsSuccess() || !materialTypeIdOutcome2.IsSuccess() || materialTypeIdOutcome1.GetValue() != materialTypeIdOutcome2.GetValue()) { @@ -233,18 +233,18 @@ namespace AZ return Failure(); } - parentMaterialPath = parentMaterialSourceData.m_parentMaterial; - parentMaterialSourcePath = AssetUtils::ResolvePathReference(parentMaterialSourcePath, parentMaterialPath); - parentMaterialSourceDataVec.push_back(parentMaterialSourceData); + parentSourceDataStack.push_back(parentSourceData); + parentSourceRelPath = parentSourceData.m_parentMaterial; + parentSourceAbsPath = AssetUtils::ResolvePathReference(parentSourceAbsPath, parentSourceRelPath); } - AZStd::reverse(parentMaterialSourceDataVec.begin(), parentMaterialSourceDataVec.end()); - for (const auto& parentMaterialSourceData : parentMaterialSourceDataVec) + while (!parentSourceDataStack.empty()) { - ApplyMaterialSourceDataPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath, parentMaterialSourceData); + parentSourceDataStack.back().ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath); + parentSourceDataStack.pop_back(); } - ApplyMaterialSourceDataPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath, *this); + ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath); Data::Asset material; if (materialAssetCreator.End(material)) @@ -257,12 +257,10 @@ namespace AZ } } - void MaterialSourceData::ApplyMaterialSourceDataPropertiesToAssetCreator( - AZ::RPI::MaterialAssetCreator& materialAssetCreator, - const AZStd::string_view& materialSourceFilePath, - const MaterialSourceData& materialSourceData) + void MaterialSourceData::ApplyPropertiesToAssetCreator( + AZ::RPI::MaterialAssetCreator& materialAssetCreator, const AZStd::string_view& materialSourceFilePath) const { - for (auto& group : materialSourceData.m_properties) + for (auto& group : m_properties) { for (auto& property : group.second) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index b208a49111..dc8b378b85 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -351,11 +351,14 @@ namespace AZ for (const ShaderVariantReferenceData& shaderRef : m_shaderCollection) { const auto& shaderFile = shaderRef.m_shaderFilePath; - const auto& shaderAsset = AssetUtils::LoadAsset(materialTypeSourceFilePath, shaderFile, 0); + auto shaderAssetResult = AssetUtils::LoadAsset(materialTypeSourceFilePath, shaderFile, 0); - if (shaderAsset) + if (shaderAssetResult) { - auto optionsLayout = shaderAsset.GetValue()->GetShaderOptionGroupLayout(); + auto shaderAsset = shaderAssetResult.GetValue(); + shaderAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad); + + auto optionsLayout = shaderAsset->GetShaderOptionGroupLayout(); ShaderOptionGroup options{ optionsLayout }; for (auto& iter : shaderRef.m_shaderOptionValues) { @@ -366,12 +369,11 @@ namespace AZ } materialTypeAssetCreator.AddShader( - shaderAsset.GetValue(), options.GetShaderVariantId(), - shaderRef.m_shaderTag.IsEmpty() ? Uuid::CreateRandom().ToString() : shaderRef.m_shaderTag - ); + shaderAsset, options.GetShaderVariantId(), + shaderRef.m_shaderTag.IsEmpty() ? Uuid::CreateRandom().ToString() : shaderRef.m_shaderTag); // Gather UV names - const ShaderInputContract& shaderInputContract = shaderAsset.GetValue()->GetInputContract(); + const ShaderInputContract& shaderInputContract = shaderAsset->GetInputContract(); for (const ShaderInputContract::StreamChannelInfo& channel : shaderInputContract.m_streamChannels) { const RHI::ShaderSemantic& semantic = channel.m_semantic; @@ -451,15 +453,20 @@ namespace AZ { case MaterialPropertyDataType::Image: { - Outcome> imageAssetResult = MaterialUtils::GetImageAssetReference(materialTypeSourceFilePath, property.m_value.GetValue()); + auto imageAssetResult = MaterialUtils::GetImageAssetReference( + materialTypeSourceFilePath, property.m_value.GetValue()); - if (imageAssetResult.IsSuccess()) + if (imageAssetResult) { - materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAssetResult.GetValue()); + auto imageAsset = imageAssetResult.GetValue(); + imageAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad); + materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset); } else { - materialTypeAssetCreator.ReportError("Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(), property.m_value.GetValue().data()); + materialTypeAssetCreator.ReportError( + "Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(), + property.m_value.GetValue().data()); } } break; From 67299419423c4e18a6fe8e1790b0030d18ff7e29 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Mon, 25 Oct 2021 12:35:19 -0500 Subject: [PATCH 162/177] adding ifdef to compare loading vs creating material type assets Signed-off-by: Guthrie Adams --- .../Code/Source/RPI.Edit/Material/MaterialSourceData.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp index 877e12ab2e..f1a41d0548 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -193,6 +193,7 @@ namespace AZ MaterialAssetCreator materialAssetCreator; materialAssetCreator.SetElevateWarnings(elevateWarnings); +#if 0 MaterialTypeSourceData materialTypeSourceData; AZStd::string materialTypeSourcePath = AssetUtils::ResolvePathReference(materialSourceFilePath, m_materialType); if (!AZ::RPI::JsonUtils::LoadObjectFromFile(materialTypeSourcePath, materialTypeSourceData)) @@ -208,6 +209,13 @@ namespace AZ { return Failure(); } +#else + auto materialTypeAsset = AssetUtils::LoadAsset(materialSourceFilePath, m_materialType); + if (!materialTypeAsset.IsSuccess()) + { + return Failure(); + } +#endif materialAssetCreator.Begin(assetId, *materialTypeAsset.GetValue().Get(), includeMaterialPropertyNames); From 6ea951214c62797c8d6a1d6b1f63900475bb09ee Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Tue, 26 Oct 2021 12:24:37 -0500 Subject: [PATCH 163/177] Moving material type asset PostInit call to be consistent with material asset Signed-off-by: Guthrie Adams --- .../Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp | 4 ++++ .../Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp index 76634201eb..48654d7769 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp @@ -188,6 +188,10 @@ namespace AZ void MaterialTypeAsset::SetReady() { m_status = AssetStatus::Ready; + + // If this was created dynamically using MaterialTypeAssetCreator (which is what calls SetReady()), + // we need to connect to the AssetBus for reloads. + PostLoadInit(); } bool MaterialTypeAsset::PostLoadInit() diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp index c81cf31d09..46086dfecc 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp @@ -43,7 +43,6 @@ namespace AZ return false; } - m_asset->PostLoadInit(); m_asset->SetReady(); m_materialShaderResourceGroupLayout = nullptr; From 5a8f95a414aecece9dd6d92b3b9b44b790cf2fbd Mon Sep 17 00:00:00 2001 From: Mike Chang Date: Wed, 10 Nov 2021 11:21:49 -0800 Subject: [PATCH 164/177] Fix Wix root and installer url config (#5500) Signed-off-by: changml --- scripts/build/Platform/Windows/build_config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index b0bb79b5dd..3260c9af79 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -359,8 +359,8 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"%WIX% \"", - "EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=%INSTALLER_DOWNLOAD_URL% -DLY_INSTALLER_LICENSE_URL=%INSTALLER_DOWNLOAD_URL%/license", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"!WIX! \"", + "EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=!INSTALLER_DOWNLOAD_URL! -DLY_INSTALLER_LICENSE_URL=!INSTALLER_DOWNLOAD_URL!/license", "CPACK_BUCKET": "%INSTALLER_BUCKET%", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" From 61d0ec9d6bee259e632d543b8f8f31534fce0b87 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed, 10 Nov 2021 13:22:47 -0600 Subject: [PATCH 165/177] Remove debug messages. (#5429) Leaving in the print for absorbed asserts to avoid running into future situations where important asserts are accidentally absorbed Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> --- .../native/AssetDatabase/AssetDatabase.cpp | 5 +---- .../tests/assetmanager/AssetProcessorManagerTest.cpp | 10 ---------- .../AssetProcessor/native/unittests/UnitTestRunner.h | 9 ++++----- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/Code/Tools/AssetProcessor/native/AssetDatabase/AssetDatabase.cpp b/Code/Tools/AssetProcessor/native/AssetDatabase/AssetDatabase.cpp index 8f7d542fcc..9be991eb0b 100644 --- a/Code/Tools/AssetProcessor/native/AssetDatabase/AssetDatabase.cpp +++ b/Code/Tools/AssetProcessor/native/AssetDatabase/AssetDatabase.cpp @@ -1067,15 +1067,12 @@ namespace AssetProcessor if (dropAllTables) { - AZ_TracePrintf("AssetDatabase", "Closing existing db connection\n"); // Temporary debug output to help with tracking down a crash // drop all tables by destroying the entire database. m_databaseConnection->Close(); - AZ_TracePrintf("AssetDatabase", "Getting db file path\n"); // Temporary debug output to help with tracking down a crash AZStd::string dbFilePath = GetAssetDatabaseFilePath(); if (dbFilePath != ":memory:") { - AZ_TracePrintf("AssetDatabase", "Deleting existing db %s\n", dbFilePath.c_str()); // Temporary debug output to help with tracking down a crash // you cannot delete a memory database, but it drops all data when you close it anyway. if (!AZ::IO::SystemFile::Delete(dbFilePath.c_str())) { @@ -1085,7 +1082,7 @@ namespace AssetProcessor return false; } } - AZ_TracePrintf("AssetDatabase", "Re-opening connection\n"); // Temporary debug output to help with tracking down a crash + if (!m_databaseConnection->Open(dbFilePath, IsReadOnly())) { delete m_databaseConnection; diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp index eef3a87797..93344caf6e 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp @@ -243,9 +243,7 @@ void AssetProcessorManagerTest::SetUp() ASSERT_TRUE(m_mockApplicationManager->RegisterAssetRecognizerAsBuilder(rec)); m_mockApplicationManager->BusConnect(); - AZ_Printf("UnitTest", "Allocating APM\n") m_assetProcessorManager.reset(new AssetProcessorManager_Test(m_config.get())); - AZ_Printf("UnitTest", "APM ready\n"); m_errorAbsorber->Clear(); m_isIdling = false; @@ -4468,9 +4466,7 @@ AssetBuilderSDK::AssetBuilderDesc MockBuilderInfoHandler::CreateBuilderDesc(cons void FingerprintTest::SetUp() { - AZ_Printf("FingerprintTest", "SetUp start\n"); AssetProcessorManagerTest::SetUp(); - AZ_Printf("FingerprintTest", "SetUp self\n"); // We don't want the mock application manager to provide builder descriptors, mockBuilderInfoHandler will provide our own m_mockApplicationManager->BusDisconnect(); @@ -4489,23 +4485,18 @@ void FingerprintTest::SetUp() }); ASSERT_TRUE(UnitTestUtils::CreateDummyFile(m_absolutePath, "")); - AZ_Printf("FingerprintTest", "SetUp end\n"); } void FingerprintTest::TearDown() { - AZ_Printf("FingerprintTest", "TearDown start\n"); m_jobResults = AZStd::vector{}; m_mockBuilderInfoHandler = {}; - AZ_Printf("FingerprintTest", "TearDown parent\n"); AssetProcessorManagerTest::TearDown(); - AZ_Printf("FingerprintTest", "TearDown end\n"); } void FingerprintTest::RunFingerprintTest(QString builderFingerprint, QString jobFingerprint, bool expectedResult) { - AZ_Printf("FingerprintTest", "Fingerprint Test Start\n"); m_mockBuilderInfoHandler.m_builderDesc.m_analysisFingerprint = builderFingerprint.toUtf8().data(); m_mockBuilderInfoHandler.m_jobFingerprint = jobFingerprint; QMetaObject::invokeMethod(m_assetProcessorManager.get(), "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, m_absolutePath)); @@ -4514,7 +4505,6 @@ void FingerprintTest::RunFingerprintTest(QString builderFingerprint, QString job ASSERT_EQ(m_mockBuilderInfoHandler.m_createJobsCount, 1); ASSERT_EQ(m_jobResults.size(), 1); ASSERT_EQ(m_jobResults[0].m_autoFail, expectedResult); - AZ_Printf("FingerprintTest", "Fingerprint Test End\n"); } TEST_F(FingerprintTest, FingerprintChecking_JobFingerprint_NoBuilderFingerprint) diff --git a/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h b/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h index 0c4357f84a..7f3f3c859d 100644 --- a/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h +++ b/Code/Tools/AssetProcessor/native/unittests/UnitTestRunner.h @@ -156,7 +156,6 @@ namespace UnitTestUtils bool OnPreWarning([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override { - UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message); ++m_numWarningsAbsorbed; if (m_debugMessages) { @@ -167,7 +166,9 @@ namespace UnitTestUtils bool OnPreAssert([[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override { - UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message); + // Print out absorbed asserts since asserts are pretty important and accidentally absorbing unintended ones can lead to difficult-to-detect issues + UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, "Absorbed Assert: %s\n", message); + ++m_numAssertsAbsorbed; if (m_debugMessages) { @@ -178,7 +179,6 @@ namespace UnitTestUtils bool OnPreError([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override { - UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message); ++m_numErrorsAbsorbed; if (m_debugMessages) { @@ -187,9 +187,8 @@ namespace UnitTestUtils return true; // I handled this, do not forward it } - bool OnPrintf(const char* /*window*/, const char* message) override + bool OnPrintf(const char* /*window*/, const char* /*message*/) override { - UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message); ++m_numMessagesAbsorbed; return true; } From fbebf04161b67ab3700bbd6c7fcd5e2e3016ebc5 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Thu, 4 Nov 2021 12:27:18 -0500 Subject: [PATCH 166/177] cherry-pick c0acbe7b Signed-off-by: Guthrie Adams --- .../RPI.Edit/Material/MaterialSourceData.h | 3 +- .../Include/Atom/RPI.Reflect/AssetCreator.h | 3 +- .../RPI.Edit/Material/MaterialSourceData.cpp | 61 +++++++++++------- .../Material/MaterialTypeSourceData.cpp | 3 - .../AtomToolsDocumentSystemComponent.cpp | 14 +++-- .../AtomToolsDocumentSystemComponent.h | 4 +- .../Code/Source/Document/MaterialDocument.cpp | 63 ++++++++++--------- .../Code/Source/Document/MaterialDocument.h | 11 +--- 8 files changed, 86 insertions(+), 76 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h index 17dc4556fb..77bf45023d 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h @@ -99,7 +99,8 @@ namespace AZ Data::AssetId assetId, AZStd::string_view materialSourceFilePath = "", bool elevateWarnings = true, - bool includeMaterialPropertyNames = true) const; + bool includeMaterialPropertyNames = true, + AZStd::unordered_set* sourceDependencies = nullptr) const; private: void ApplyPropertiesToAssetCreator( diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/AssetCreator.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/AssetCreator.h index abdbe9cdce..79d43d0b8d 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/AssetCreator.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/AssetCreator.h @@ -118,7 +118,7 @@ namespace AZ ResetIssueCounts(); // Because the asset creator can be used multiple times - m_asset = Data::AssetManager::Instance().CreateAsset(assetId, AZ::Data::AssetLoadBehavior::PreLoad); + m_asset = Data::Asset(assetId, aznew AssetDataT, AZ::Data::AssetLoadBehavior::PreLoad); m_beginCalled = true; if (!m_asset) @@ -138,6 +138,7 @@ namespace AZ } else { + Data::AssetManager::Instance().AssignAssetData(m_asset); result = AZStd::move(m_asset); success = true; } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp index f1a41d0548..c912826026 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -188,14 +188,20 @@ namespace AZ } Outcome> MaterialSourceData::CreateMaterialAssetFromSourceData( - Data::AssetId assetId, AZStd::string_view materialSourceFilePath, bool elevateWarnings, bool includeMaterialPropertyNames) const + Data::AssetId assetId, + AZStd::string_view materialSourceFilePath, + bool elevateWarnings, + bool includeMaterialPropertyNames, + AZStd::unordered_set* sourceDependencies) const { - MaterialAssetCreator materialAssetCreator; - materialAssetCreator.SetElevateWarnings(elevateWarnings); + const auto materialTypeSourcePath = AssetUtils::ResolvePathReference(materialSourceFilePath, m_materialType); + const auto materialTypeAssetId = AssetUtils::MakeAssetId(materialTypeSourcePath, 0); + if (!materialTypeAssetId.IsSuccess()) + { + return Failure(); + } -#if 0 MaterialTypeSourceData materialTypeSourceData; - AZStd::string materialTypeSourcePath = AssetUtils::ResolvePathReference(materialSourceFilePath, m_materialType); if (!AZ::RPI::JsonUtils::LoadObjectFromFile(materialTypeSourcePath, materialTypeSourceData)) { return Failure(); @@ -203,21 +209,16 @@ namespace AZ materialTypeSourceData.ResolveUvEnums(); - auto materialTypeAsset = - materialTypeSourceData.CreateMaterialTypeAsset(AZ::Uuid::CreateRandom(), materialTypeSourcePath, elevateWarnings); + const auto materialTypeAsset = + materialTypeSourceData.CreateMaterialTypeAsset(materialTypeAssetId.GetValue(), materialTypeSourcePath, elevateWarnings); if (!materialTypeAsset.IsSuccess()) { return Failure(); } -#else - auto materialTypeAsset = AssetUtils::LoadAsset(materialSourceFilePath, m_materialType); - if (!materialTypeAsset.IsSuccess()) - { - return Failure(); - } -#endif - materialAssetCreator.Begin(assetId, *materialTypeAsset.GetValue().Get(), includeMaterialPropertyNames); + AZStd::unordered_set dependencies; + dependencies.insert(materialSourceFilePath); + dependencies.insert(materialTypeSourcePath); AZStd::vector parentSourceDataStack; @@ -225,6 +226,13 @@ namespace AZ AZStd::string parentSourceAbsPath = AssetUtils::ResolvePathReference(materialSourceFilePath, parentSourceRelPath); while (!parentSourceRelPath.empty()) { + if (dependencies.find(parentSourceAbsPath) != dependencies.end()) + { + return Failure(); + } + + dependencies.insert(parentSourceAbsPath); + MaterialSourceData parentSourceData; if (!AZ::RPI::JsonUtils::LoadObjectFromFile(parentSourceAbsPath, parentSourceData)) { @@ -232,20 +240,22 @@ namespace AZ } // Make sure the parent material has the same material type - auto materialTypeIdOutcome1 = AssetUtils::MakeAssetId(materialSourceFilePath, m_materialType, 0); - auto materialTypeIdOutcome2 = AssetUtils::MakeAssetId(parentSourceAbsPath, parentSourceData.m_materialType, 0); - if (!materialTypeIdOutcome1.IsSuccess() || !materialTypeIdOutcome2.IsSuccess() || - materialTypeIdOutcome1.GetValue() != materialTypeIdOutcome2.GetValue()) + const auto parentTypeAssetId = AssetUtils::MakeAssetId(parentSourceAbsPath, parentSourceData.m_materialType, 0); + if (!parentTypeAssetId || parentTypeAssetId.GetValue() != materialTypeAssetId.GetValue()) { AZ_Error("MaterialSourceData", false, "This material and its parent material do not share the same material type."); return Failure(); } - parentSourceDataStack.push_back(parentSourceData); parentSourceRelPath = parentSourceData.m_parentMaterial; parentSourceAbsPath = AssetUtils::ResolvePathReference(parentSourceAbsPath, parentSourceRelPath); + parentSourceDataStack.emplace_back(AZStd::move(parentSourceData)); } + MaterialAssetCreator materialAssetCreator; + materialAssetCreator.SetElevateWarnings(elevateWarnings); + materialAssetCreator.Begin(assetId, *materialTypeAsset.GetValue().Get(), includeMaterialPropertyNames); + while (!parentSourceDataStack.empty()) { parentSourceDataStack.back().ApplyPropertiesToAssetCreator(materialAssetCreator, materialSourceFilePath); @@ -257,12 +267,15 @@ namespace AZ Data::Asset material; if (materialAssetCreator.End(material)) { + if (sourceDependencies) + { + sourceDependencies->insert(dependencies.begin(), dependencies.end()); + } + return Success(material); } - else - { - return Failure(); - } + + return Failure(); } void MaterialSourceData::ApplyPropertiesToAssetCreator( diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index dc8b378b85..d5551e89ae 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -356,8 +356,6 @@ namespace AZ if (shaderAssetResult) { auto shaderAsset = shaderAssetResult.GetValue(); - shaderAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad); - auto optionsLayout = shaderAsset->GetShaderOptionGroupLayout(); ShaderOptionGroup options{ optionsLayout }; for (auto& iter : shaderRef.m_shaderOptionValues) @@ -459,7 +457,6 @@ namespace AZ if (imageAssetResult) { auto imageAsset = imageAssetResult.GetValue(); - imageAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad); materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset); } else diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.cpp index 5652e9fe23..00de2a7c4c 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.cpp @@ -159,7 +159,7 @@ namespace AtomToolsFramework void AtomToolsDocumentSystemComponent::OnDocumentExternallyModified(const AZ::Uuid& documentId) { - m_documentIdsToReopen.insert(documentId); + m_documentIdsWithExternalChanges.insert(documentId); if (!AZ::TickBus::Handler::BusIsConnected()) { AZ::TickBus::Handler::BusConnect(); @@ -168,7 +168,7 @@ namespace AtomToolsFramework void AtomToolsDocumentSystemComponent::OnDocumentDependencyModified(const AZ::Uuid& documentId) { - m_documentIdsToReopen.insert(documentId); + m_documentIdsWithDependencyChanges.insert(documentId); if (!AZ::TickBus::Handler::BusIsConnected()) { AZ::TickBus::Handler::BusConnect(); @@ -177,7 +177,7 @@ namespace AtomToolsFramework void AtomToolsDocumentSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { - for (const AZ::Uuid& documentId : m_documentIdsToReopen) + for (const AZ::Uuid& documentId : m_documentIdsWithExternalChanges) { AZStd::string documentPath; AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath); @@ -191,6 +191,8 @@ namespace AtomToolsFramework continue; } + m_documentIdsWithDependencyChanges.erase(documentId); + AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount); bool openResult = false; @@ -204,7 +206,7 @@ namespace AtomToolsFramework } } - for (const AZ::Uuid& documentId : m_documentIdsToReopen) + for (const AZ::Uuid& documentId : m_documentIdsWithDependencyChanges) { AZStd::string documentPath; AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath); @@ -231,8 +233,8 @@ namespace AtomToolsFramework } } - m_documentIdsToReopen.clear(); - m_documentIdsToReopen.clear(); + m_documentIdsWithDependencyChanges.clear(); + m_documentIdsWithExternalChanges.clear(); AZ::TickBus::Handler::BusDisconnect(); } diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.h index 9c556a07e7..a0f5eb085d 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.h @@ -85,8 +85,8 @@ namespace AtomToolsFramework AZStd::intrusive_ptr m_settings; AZStd::function m_documentCreator; AZStd::unordered_map> m_documentMap; - AZStd::unordered_set m_documentIdsToRebuild; - AZStd::unordered_set m_documentIdsToReopen; + AZStd::unordered_set m_documentIdsWithExternalChanges; + AZStd::unordered_set m_documentIdsWithDependencyChanges; const size_t m_maxMessageBoxLineCount = 15; }; } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index f22a40a77b..265bca0860 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -552,26 +552,26 @@ namespace MaterialEditor } } - void MaterialDocument::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID) + void MaterialDocument::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, [[maybe_unused]] AZ::Uuid sourceUUID) { - if (m_sourceAssetId.m_guid == sourceUUID) + auto sourcePath = AZ::RPI::AssetUtils::ResolvePathReference(scanFolder, relativePath); + + if (m_absolutePath == sourcePath) { // ignore notifications caused by saving the open document if (!m_saveTriggeredInternally) { AZ_TracePrintf("MaterialDocument", "Material document changed externally: '%s'.\n", m_absolutePath.c_str()); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentExternallyModified, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( + &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentExternallyModified, m_id); } m_saveTriggeredInternally = false; } - } - - void MaterialDocument::OnAssetReloaded(AZ::Data::Asset asset) - { - if (m_dependentAssetIds.find(asset->GetId()) != m_dependentAssetIds.end()) + else if (m_sourceDependencies.find(sourcePath) != m_sourceDependencies.end()) { AZ_TracePrintf("MaterialDocument", "Material document dependency changed: '%s'.\n", m_absolutePath.c_str()); - AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentDependencyModified, m_id); + AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast( + &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentDependencyModified, m_id); } } @@ -641,7 +641,6 @@ namespace MaterialEditor return false; } - m_sourceAssetId = sourceAssetInfo.m_assetId; m_relativePath = sourceAssetInfo.m_relativePath; if (!AzFramework::StringFunc::Path::Normalize(m_relativePath)) { @@ -716,14 +715,15 @@ namespace MaterialEditor // we can create the asset dynamically from the source data. // Long term, the material document should not be concerned with assets at all. The viewport window should be the // only thing concerned with assets or instances. - auto createResult = m_materialSourceData.CreateMaterialAssetFromSourceData(Uuid::CreateRandom(), m_absolutePath, true); - if (!createResult) + auto materialAssetResult = + m_materialSourceData.CreateMaterialAssetFromSourceData(Uuid::CreateRandom(), m_absolutePath, true, true, &m_sourceDependencies); + if (!materialAssetResult) { AZ_Error("MaterialDocument", false, "Material asset could not be created from source data: '%s'.", m_absolutePath.c_str()); return false; } - m_materialAsset = createResult.GetValue(); + m_materialAsset = materialAssetResult.GetValue(); if (!m_materialAsset.IsReady()) { AZ_Error("MaterialDocument", false, "Material asset is not ready: '%s'.", m_absolutePath.c_str()); @@ -737,28 +737,35 @@ namespace MaterialEditor return false; } - // track material type asset to notify when dependencies change - m_dependentAssetIds.insert(materialTypeAsset->GetId()); - AZ::Data::AssetBus::MultiHandler::BusConnect(materialTypeAsset->GetId()); - AZStd::array_view parentPropertyValues = materialTypeAsset->GetDefaultPropertyValues(); AZ::Data::Asset parentMaterialAsset; if (!m_materialSourceData.m_parentMaterial.empty()) { - // There is a parent for this material - auto parentMaterialResult = AssetUtils::LoadAsset(m_absolutePath, m_materialSourceData.m_parentMaterial); - if (!parentMaterialResult) + AZ::RPI::MaterialSourceData parentMaterialSourceData; + const auto parentMaterialFilePath = AssetUtils::ResolvePathReference(m_absolutePath, m_materialSourceData.m_parentMaterial); + if (!AZ::RPI::JsonUtils::LoadObjectFromFile(parentMaterialFilePath, parentMaterialSourceData)) { - AZ_Error("MaterialDocument", false, "Parent material asset could not be loaded: '%s'.", m_materialSourceData.m_parentMaterial.c_str()); + AZ_Error("MaterialDocument", false, "Material parent source data could not be loaded for: '%s'.", parentMaterialFilePath.c_str()); return false; } - parentMaterialAsset = parentMaterialResult.GetValue(); - parentPropertyValues = parentMaterialAsset->GetPropertyValues(); + const auto parentMaterialAssetIdResult = AssetUtils::MakeAssetId(parentMaterialFilePath, 0); + if (!parentMaterialAssetIdResult) + { + AZ_Error("MaterialDocument", false, "Material parent asset ID could not be created: '%s'.", parentMaterialFilePath.c_str()); + return false; + } - // track parent material asset to notify when dependencies change - m_dependentAssetIds.insert(parentMaterialAsset->GetId()); - AZ::Data::AssetBus::MultiHandler::BusConnect(parentMaterialAsset->GetId()); + auto parentMaterialAssetResult = m_materialSourceData.CreateMaterialAssetFromSourceData( + parentMaterialAssetIdResult.GetValue(), parentMaterialFilePath, true, true, &m_sourceDependencies); + if (!parentMaterialAssetResult) + { + AZ_Error("MaterialDocument", false, "Material parent asset could not be created from source data: '%s'.", parentMaterialFilePath.c_str()); + return false; + } + + parentMaterialAsset = parentMaterialAssetResult.GetValue(); + parentPropertyValues = parentMaterialAsset->GetPropertyValues(); } // Creating a material from a material asset will fail if a texture is referenced but not loaded @@ -908,15 +915,13 @@ namespace MaterialEditor void MaterialDocument::Clear() { AZ::TickBus::Handler::BusDisconnect(); - AZ::Data::AssetBus::MultiHandler::BusDisconnect(); AzToolsFramework::AssetSystemBus::Handler::BusDisconnect(); m_materialAsset = {}; m_materialInstance = {}; m_absolutePath.clear(); m_relativePath.clear(); - m_sourceAssetId = {}; - m_dependentAssetIds.clear(); + m_sourceDependencies.clear(); m_saveTriggeredInternally = {}; m_compilePending = {}; m_properties.clear(); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h index 14538ba867..ceb3190f26 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h @@ -29,7 +29,6 @@ namespace MaterialEditor : public AtomToolsFramework::AtomToolsDocument , public MaterialDocumentRequestBus::Handler , private AZ::TickBus::Handler - , private AZ::Data::AssetBus::MultiHandler , private AzToolsFramework::AssetSystemBus::Handler { public: @@ -105,11 +104,6 @@ namespace MaterialEditor void SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceUUID) override; ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - // AZ::Data::AssetBus::Router overrides... - void OnAssetReloaded(AZ::Data::Asset asset) override; - ////////////////////////////////////////////////////////////////////////// - bool SavePropertiesToSourceData( const AZStd::string& exportPath, AZ::RPI::MaterialSourceData& sourceData, PropertyFilterFunction propertyFilter) const; @@ -138,11 +132,8 @@ namespace MaterialEditor // Material instance being edited AZ::Data::Instance m_materialInstance; - // Asset used to open document - AZ::Data::AssetId m_sourceAssetId; - // Set of assets that can trigger a document reload - AZStd::unordered_set m_dependentAssetIds; + AZStd::unordered_set m_sourceDependencies; // Track if document saved itself last to skip external modification notification bool m_saveTriggeredInternally = false; From 086859d49acfc50ad44b13c6b8403b838430b3bd Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Thu, 4 Nov 2021 18:20:27 -0500 Subject: [PATCH 167/177] updated comments and error messages Signed-off-by: Guthrie Adams --- .../RPI.Edit/Material/MaterialSourceData.h | 1 + .../RPI.Edit/Material/MaterialSourceData.cpp | 25 +++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h index 77bf45023d..53d3072370 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialSourceData.h @@ -95,6 +95,7 @@ namespace AZ //! resolving file-relative paths. //! @param elevateWarnings Indicates whether to treat warnings as errors //! @param includeMaterialPropertyNames Indicates whether to save material property names into the material asset file + //! @param sourceDependencies if not null, will be populated with a set of all of the loaded material and material type paths Outcome> CreateMaterialAssetFromSourceData( Data::AssetId assetId, AZStd::string_view materialSourceFilePath = "", diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp index c912826026..7c37d894d4 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -198,12 +198,14 @@ namespace AZ const auto materialTypeAssetId = AssetUtils::MakeAssetId(materialTypeSourcePath, 0); if (!materialTypeAssetId.IsSuccess()) { + AZ_Error("MaterialSourceData", false, "Failed to create material type asset ID: '%s'.", materialTypeSourcePath.c_str()); return Failure(); } MaterialTypeSourceData materialTypeSourceData; if (!AZ::RPI::JsonUtils::LoadObjectFromFile(materialTypeSourcePath, materialTypeSourceData)) { + AZ_Error("MaterialSourceData", false, "Failed to load MaterialTypeSourceData: '%s'.", materialTypeSourcePath.c_str()); return Failure(); } @@ -213,45 +215,58 @@ namespace AZ materialTypeSourceData.CreateMaterialTypeAsset(materialTypeAssetId.GetValue(), materialTypeSourcePath, elevateWarnings); if (!materialTypeAsset.IsSuccess()) { + AZ_Error("MaterialSourceData", false, "Failed to create material type asset from source data: '%s'.", materialTypeSourcePath.c_str()); return Failure(); } + // Track all of the material and material type assets loaded while trying to create a material asset from source data. This will + // be used for evaluating circular dependencies and returned for external monitoring or other use. AZStd::unordered_set dependencies; dependencies.insert(materialSourceFilePath); dependencies.insert(materialTypeSourcePath); + // Load and build a stack of MaterialSourceData from all of the parent materials in the hierarchy. Properties from the source + // data will be applied in reverse to the asset creator. AZStd::vector parentSourceDataStack; AZStd::string parentSourceRelPath = m_parentMaterial; AZStd::string parentSourceAbsPath = AssetUtils::ResolvePathReference(materialSourceFilePath, parentSourceRelPath); while (!parentSourceRelPath.empty()) { - if (dependencies.find(parentSourceAbsPath) != dependencies.end()) + if (!dependencies.insert(parentSourceAbsPath).second) { + AZ_Error("MaterialSourceData", false, "Detected circular dependency between materials: '%s' and '%s'.", materialSourceFilePath, parentSourceAbsPath.c_str()); return Failure(); } - dependencies.insert(parentSourceAbsPath); - MaterialSourceData parentSourceData; if (!AZ::RPI::JsonUtils::LoadObjectFromFile(parentSourceAbsPath, parentSourceData)) { + AZ_Error("MaterialSourceData", false, "Failed to load MaterialSourceData for parent material: '%s'.", parentSourceAbsPath.c_str()); return Failure(); } - // Make sure the parent material has the same material type + // Make sure that all materials in the hierarchy share the same material type const auto parentTypeAssetId = AssetUtils::MakeAssetId(parentSourceAbsPath, parentSourceData.m_materialType, 0); - if (!parentTypeAssetId || parentTypeAssetId.GetValue() != materialTypeAssetId.GetValue()) + if (!parentTypeAssetId) + { + AZ_Error("MaterialSourceData", false, "Parent material asset ID isn't valid: '%s'.", parentSourceAbsPath.c_str()); + return Failure(); + } + + if (parentTypeAssetId.GetValue() != materialTypeAssetId.GetValue()) { AZ_Error("MaterialSourceData", false, "This material and its parent material do not share the same material type."); return Failure(); } + // Get the location of the next parent material and push the source data onto the stack parentSourceRelPath = parentSourceData.m_parentMaterial; parentSourceAbsPath = AssetUtils::ResolvePathReference(parentSourceAbsPath, parentSourceRelPath); parentSourceDataStack.emplace_back(AZStd::move(parentSourceData)); } + // Create the material asset from all the previously loaded source data MaterialAssetCreator materialAssetCreator; materialAssetCreator.SetElevateWarnings(elevateWarnings); materialAssetCreator.Begin(assetId, *materialTypeAsset.GetValue().Get(), includeMaterialPropertyNames); From 287f08a33c0cdec26212878be7e658b071371e34 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Fri, 5 Nov 2021 22:06:31 -0500 Subject: [PATCH 168/177] Fix parent material loading Signed-off-by: Guthrie Adams --- .../RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp | 2 +- .../MaterialEditor/Code/Source/Document/MaterialDocument.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp index 7c37d894d4..e5466a173f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -235,7 +235,7 @@ namespace AZ { if (!dependencies.insert(parentSourceAbsPath).second) { - AZ_Error("MaterialSourceData", false, "Detected circular dependency between materials: '%s' and '%s'.", materialSourceFilePath, parentSourceAbsPath.c_str()); + AZ_Error("MaterialSourceData", false, "Detected circular dependency between materials: '%s' and '%s'.", materialSourceFilePath.data(), parentSourceAbsPath.c_str()); return Failure(); } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index 265bca0860..ea97a3f2c0 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -756,8 +756,8 @@ namespace MaterialEditor return false; } - auto parentMaterialAssetResult = m_materialSourceData.CreateMaterialAssetFromSourceData( - parentMaterialAssetIdResult.GetValue(), parentMaterialFilePath, true, true, &m_sourceDependencies); + auto parentMaterialAssetResult = parentMaterialSourceData.CreateMaterialAssetFromSourceData( + parentMaterialAssetIdResult.GetValue(), parentMaterialFilePath, true, true); if (!parentMaterialAssetResult) { AZ_Error("MaterialDocument", false, "Material parent asset could not be created from source data: '%s'.", parentMaterialFilePath.c_str()); From c73b6bbe27e7b8e721bef4231c7b3a4fe449b426 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Sun, 7 Nov 2021 15:04:46 -0600 Subject: [PATCH 169/177] Changing lua material functor script loading code to pass the correct sub ID for a compiled script asset Signed-off-by: Guthrie Adams --- .../Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp index b230953f8c..37bd5a19f2 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp @@ -137,7 +137,7 @@ namespace AZ } else if (!m_luaSourceFile.empty()) { - auto loadOutcome = RPI::AssetUtils::LoadAsset(materialTypeSourceFilePath, m_luaSourceFile); + auto loadOutcome = RPI::AssetUtils::LoadAsset(materialTypeSourceFilePath, m_luaSourceFile, ScriptAsset::CompiledAssetSubId); if (!loadOutcome) { AZ_Error("LuaMaterialFunctorSourceData", false, "Could not load script file '%s'", m_luaSourceFile.c_str()); From 824da6cabf402beaf6cdd2f3e80f1668743fc0ca Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 10 Nov 2021 10:54:53 -0600 Subject: [PATCH 170/177] update comment and error message Signed-off-by: Guthrie Adams --- .../RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp | 5 ++++- .../RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp index 37bd5a19f2..14ef3bb17d 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/LuaMaterialFunctorSourceData.cpp @@ -137,7 +137,10 @@ namespace AZ } else if (!m_luaSourceFile.empty()) { - auto loadOutcome = RPI::AssetUtils::LoadAsset(materialTypeSourceFilePath, m_luaSourceFile, ScriptAsset::CompiledAssetSubId); + // The sub ID for script assets must be explicit. + // LUA source files output a compiled as well as an uncompiled asset, sub Ids of 1 and 2. + auto loadOutcome = + RPI::AssetUtils::LoadAsset(materialTypeSourceFilePath, m_luaSourceFile, ScriptAsset::CompiledAssetSubId); if (!loadOutcome) { AZ_Error("LuaMaterialFunctorSourceData", false, "Could not load script file '%s'", m_luaSourceFile.c_str()); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp index e5466a173f..d6308ec655 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialSourceData.cpp @@ -250,7 +250,7 @@ namespace AZ const auto parentTypeAssetId = AssetUtils::MakeAssetId(parentSourceAbsPath, parentSourceData.m_materialType, 0); if (!parentTypeAssetId) { - AZ_Error("MaterialSourceData", false, "Parent material asset ID isn't valid: '%s'.", parentSourceAbsPath.c_str()); + AZ_Error("MaterialSourceData", false, "Parent material asset ID wasn't found: '%s'.", parentSourceAbsPath.c_str()); return Failure(); } From 04c6f3cc94bc057dedab6ae54a6f284421ded699 Mon Sep 17 00:00:00 2001 From: santorac <55155825+santorac@users.noreply.github.com> Date: Wed, 3 Nov 2021 23:56:59 -0700 Subject: [PATCH 171/177] cherry-pick 48f3bb7d Signed-off-by: Guthrie Adams --- .../Code/Source/RPI.Reflect/Material/ShaderCollection.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp index 87076891dd..16813cb6b7 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/ShaderCollection.cpp @@ -126,8 +126,8 @@ namespace AZ } ShaderCollection::Item::Item() + : m_renderStatesOverlay(RHI::GetInvalidRenderStates()) { - m_renderStatesOverlay = RHI::GetInvalidRenderStates(); } ShaderCollection::Item& ShaderCollection::operator[](size_t i) @@ -156,7 +156,8 @@ namespace AZ } ShaderCollection::Item::Item(const Data::Asset& shaderAsset, const AZ::Name& shaderTag, ShaderVariantId variantId) - : m_shaderAsset(shaderAsset) + : m_renderStatesOverlay(RHI::GetInvalidRenderStates()) + , m_shaderAsset(shaderAsset) , m_shaderVariantId(variantId) , m_shaderTag(shaderTag) , m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId) @@ -164,7 +165,8 @@ namespace AZ } ShaderCollection::Item::Item(Data::Asset&& shaderAsset, const AZ::Name& shaderTag, ShaderVariantId variantId) - : m_shaderAsset(AZStd::move(shaderAsset)) + : m_renderStatesOverlay(RHI::GetInvalidRenderStates()) + , m_shaderAsset(AZStd::move(shaderAsset)) , m_shaderVariantId(variantId) , m_shaderTag(shaderTag) , m_shaderOptionGroup(shaderAsset->GetShaderOptionGroupLayout(), variantId) From cebf6e2de3dbb809e10a2df070e4390481f3c170 Mon Sep 17 00:00:00 2001 From: rgba16f <82187279+rgba16f@users.noreply.github.com> Date: Wed, 10 Nov 2021 14:08:55 -0600 Subject: [PATCH 172/177] Atom CPU threading optimization (#5481) * Small change to make the RasterPass Scope jobs split the work more evenly over the cores Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * Update with PR feedback. Remove scale of EstimatedItemCount, modify the command list cost threshold instead Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> --- .../Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h | 2 +- .../Include/Atom/RHI.Reflect/Metal/PlatformLimitsDescriptor.h | 2 +- .../Include/Atom/RHI.Reflect/Vulkan/PlatformLimitsDescriptor.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h b/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h index 63c2d69ea2..1a447693c2 100644 --- a/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h +++ b/Gems/Atom/RHI/DX12/Code/Include/Atom/RHI.Reflect/DX12/PlatformLimitsDescriptor.h @@ -40,7 +40,7 @@ namespace AZ uint32_t m_swapChainsPerCommandList = 8; // The maximum cost that can be associated with a single command list. - uint32_t m_commandListCostThresholdMin = 1000; + uint32_t m_commandListCostThresholdMin = 250; // The maximum number of command lists per scope. uint32_t m_commandListsPerScopeMax = 16; diff --git a/Gems/Atom/RHI/Metal/Code/Include/Atom/RHI.Reflect/Metal/PlatformLimitsDescriptor.h b/Gems/Atom/RHI/Metal/Code/Include/Atom/RHI.Reflect/Metal/PlatformLimitsDescriptor.h index 375b532d39..bd52c37a67 100644 --- a/Gems/Atom/RHI/Metal/Code/Include/Atom/RHI.Reflect/Metal/PlatformLimitsDescriptor.h +++ b/Gems/Atom/RHI/Metal/Code/Include/Atom/RHI.Reflect/Metal/PlatformLimitsDescriptor.h @@ -31,7 +31,7 @@ namespace AZ uint32_t m_swapChainsPerCommandList = 8; // The maximum cost that can be associated with a single command list. - uint32_t m_commandListCostThresholdMin = 1000; + uint32_t m_commandListCostThresholdMin = 250; // The maximum number of command lists per scope. uint32_t m_commandListsPerScopeMax = 16; diff --git a/Gems/Atom/RHI/Vulkan/Code/Include/Atom/RHI.Reflect/Vulkan/PlatformLimitsDescriptor.h b/Gems/Atom/RHI/Vulkan/Code/Include/Atom/RHI.Reflect/Vulkan/PlatformLimitsDescriptor.h index 5e41da9627..eaa4356796 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Include/Atom/RHI.Reflect/Vulkan/PlatformLimitsDescriptor.h +++ b/Gems/Atom/RHI/Vulkan/Code/Include/Atom/RHI.Reflect/Vulkan/PlatformLimitsDescriptor.h @@ -33,7 +33,7 @@ namespace AZ uint32_t m_swapChainsPerCommandList = 8; // The maximum cost that can be associated with a single command list. - uint32_t m_commandListCostThresholdMin = 1000; + uint32_t m_commandListCostThresholdMin = 250; // The maximum number of command lists per scope. uint32_t m_commandListsPerScopeMax = 16; From 6f80aab6991524c5ce5667dc8e50b79162b398cd Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 10 Nov 2021 14:42:55 -0600 Subject: [PATCH 173/177] Updating parent path usage has part of cherry picked from stabilization combining source data changes with relative path changes Signed-off-by: Guthrie Adams --- .../Code/Source/Document/MaterialDocument.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index ea97a3f2c0..8635d1eb3e 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -742,25 +742,24 @@ namespace MaterialEditor if (!m_materialSourceData.m_parentMaterial.empty()) { AZ::RPI::MaterialSourceData parentMaterialSourceData; - const auto parentMaterialFilePath = AssetUtils::ResolvePathReference(m_absolutePath, m_materialSourceData.m_parentMaterial); - if (!AZ::RPI::JsonUtils::LoadObjectFromFile(parentMaterialFilePath, parentMaterialSourceData)) + if (!AZ::RPI::JsonUtils::LoadObjectFromFile(m_materialSourceData.m_parentMaterial, parentMaterialSourceData)) { - AZ_Error("MaterialDocument", false, "Material parent source data could not be loaded for: '%s'.", parentMaterialFilePath.c_str()); + AZ_Error("MaterialDocument", false, "Material parent source data could not be loaded for: '%s'.", m_materialSourceData.m_parentMaterial.c_str()); return false; } - const auto parentMaterialAssetIdResult = AssetUtils::MakeAssetId(parentMaterialFilePath, 0); + const auto parentMaterialAssetIdResult = AssetUtils::MakeAssetId(m_materialSourceData.m_parentMaterial, 0); if (!parentMaterialAssetIdResult) { - AZ_Error("MaterialDocument", false, "Material parent asset ID could not be created: '%s'.", parentMaterialFilePath.c_str()); + AZ_Error("MaterialDocument", false, "Material parent asset ID could not be created: '%s'.", m_materialSourceData.m_parentMaterial.c_str()); return false; } auto parentMaterialAssetResult = parentMaterialSourceData.CreateMaterialAssetFromSourceData( - parentMaterialAssetIdResult.GetValue(), parentMaterialFilePath, true, true); + parentMaterialAssetIdResult.GetValue(), m_materialSourceData.m_parentMaterial, true, true); if (!parentMaterialAssetResult) { - AZ_Error("MaterialDocument", false, "Material parent asset could not be created from source data: '%s'.", parentMaterialFilePath.c_str()); + AZ_Error("MaterialDocument", false, "Material parent asset could not be created from source data: '%s'.", m_materialSourceData.m_parentMaterial.c_str()); return false; } From 41a64b1346233bfc418209dc5e6c2d2f80787747 Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 10 Nov 2021 14:56:17 -0600 Subject: [PATCH 174/177] Added a null pointer check to EMFX system component Material editor and other tools load runtime dependencies from game projects. The EMFX system component is checking for game mode by getting a pointer to the editor interface, which will not exist outside of the main O3DE editor. Signed-off-by: Guthrie Adams --- .../Code/Source/Integration/System/SystemComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp index 755db41996..3535bcd942 100644 --- a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp @@ -626,7 +626,7 @@ namespace EMotionFX // Check if we are in game mode. IEditor* editor = nullptr; AzToolsFramework::EditorRequestBus::BroadcastResult(editor, &AzToolsFramework::EditorRequests::GetEditor); - inGameMode = editor->IsInGameMode(); + inGameMode = editor && editor->IsInGameMode(); #endif // Apply the motion extraction deltas to the character controller / entity transform for all entities. From efe78d035ffb860add3b3538aba0c773ea78114f Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed, 10 Nov 2021 15:48:58 -0600 Subject: [PATCH 175/177] Updated project path to use absolute path (#5459) Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> --- .../assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py index ff362c732c..35d7982ded 100755 --- a/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py +++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/ap_fixtures/bundler_batch_setup_fixture.py @@ -158,7 +158,7 @@ def bundler_batch_setup_fixture(request, workspace, asset_processor, timeout) -> else: cmd.append(f"--{key}") if append_defaults: - cmd.append(f"--project-path={workspace.project}") + cmd.append(f"--project-path={os.path.join(workspace.paths.engine_root(), workspace.project)}") return cmd # ****** From 246f174528ff106f2bb6f32141b96635a71c282b Mon Sep 17 00:00:00 2001 From: Guthrie Adams Date: Wed, 10 Nov 2021 16:02:12 -0600 Subject: [PATCH 176/177] inverted game mode ptr check Signed-off-by: Guthrie Adams --- .../Code/Source/Integration/System/SystemComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp index 3535bcd942..b7fa03998e 100644 --- a/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/System/SystemComponent.cpp @@ -626,7 +626,7 @@ namespace EMotionFX // Check if we are in game mode. IEditor* editor = nullptr; AzToolsFramework::EditorRequestBus::BroadcastResult(editor, &AzToolsFramework::EditorRequests::GetEditor); - inGameMode = editor && editor->IsInGameMode(); + inGameMode = !editor || editor->IsInGameMode(); #endif // Apply the motion extraction deltas to the character controller / entity transform for all entities. From 9b393d16fe3fa85a62355a2baaa047cd45fb414a Mon Sep 17 00:00:00 2001 From: Allen Jackson <23512001+jackalbe@users.noreply.github.com> Date: Wed, 10 Nov 2021 17:34:20 -0600 Subject: [PATCH 177/177] [lyn3736] adding init files to module paths (#5111) * fixing class names in PYI files Signed-off-by: jackalbe <23512001+jackalbe@users.noreply.github.com> --- .../Code/Source/PythonLogSymbolsComponent.cpp | 5 +++++ Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Gems/EditorPythonBindings/Code/Source/PythonLogSymbolsComponent.cpp b/Gems/EditorPythonBindings/Code/Source/PythonLogSymbolsComponent.cpp index d39ca83000..0be51909f4 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonLogSymbolsComponent.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonLogSymbolsComponent.cpp @@ -739,6 +739,11 @@ namespace EditorPythonBindings moduleParts.pop_back(); AzFramework::StringFunc::Append(targetModule, ".pyi"); + // create an __init__.py file as the base module path + AZStd::string initModule; + AzFramework::StringFunc::Join(initModule, moduleParts.begin(), moduleParts.end(), '.'); + OpenInitFileAt(initModule); + AZStd::string modulePath; AzFramework::StringFunc::Append(modulePath, m_basePath.c_str()); AzFramework::StringFunc::Append(modulePath, AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); diff --git a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp index df246d230d..120e0de220 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonProxyObject.cpp @@ -846,7 +846,7 @@ namespace EditorPythonBindings { return ConstructPythonProxyObjectByTypename(behaviorClassName, pythonArgs); }); - PythonSymbolEventBus::QueueBroadcast(&PythonSymbolEventBus::Events::LogClassWithName, subModuleName, behaviorClass, properSyntax); + PythonSymbolEventBus::QueueBroadcast(&PythonSymbolEventBus::Events::LogClassWithName, subModuleName, behaviorClass, syntaxName.value()); } else {