From 8ab409e4d257e4d8d1ee12b16437e3069fce5992 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Mon, 20 Sep 2021 23:36:41 -0700 Subject: [PATCH 01/24] Implemented the PrefabViewportFocusPathHandler class, added a breadcrumb widget to the viewport toolbar to display the currently focused prefab path. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- Code/Editor/ViewportTitleDlg.cpp | 4 +- Code/Editor/ViewportTitleDlg.h | 3 + Code/Editor/ViewportTitleDlg.ui | 29 +++++--- .../Prefab/PrefabFocusHandler.cpp | 68 ++++++++++++++++++- .../Prefab/PrefabFocusHandler.h | 11 +++ .../Prefab/PrefabFocusInterface.h | 9 +++ .../Prefab/PrefabFocusNotificationBus.h | 25 +++++++ .../Prefab/PrefabViewportFocusPathHandler.cpp | 54 +++++++++++++++ .../Prefab/PrefabViewportFocusPathHandler.h | 40 +++++++++++ .../aztoolsframework_files.cmake | 3 + 10 files changed, 231 insertions(+), 15 deletions(-) create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h diff --git a/Code/Editor/ViewportTitleDlg.cpp b/Code/Editor/ViewportTitleDlg.cpp index 65d6de8944..0c5c45e5c8 100644 --- a/Code/Editor/ViewportTitleDlg.cpp +++ b/Code/Editor/ViewportTitleDlg.cpp @@ -292,8 +292,6 @@ void CViewportTitleDlg::SetViewPane(CLayoutViewPane* pViewPane) ////////////////////////////////////////////////////////////////////////// void CViewportTitleDlg::OnInitDialog() { - m_ui->m_titleBtn->setText(m_title); - // Add a child parented to us that listens for r_displayInfo changes. auto displayInfoHelper = new CViewportTitleDlgDisplayInfoHelper(this); connect(displayInfoHelper, &CViewportTitleDlgDisplayInfoHelper::ViewportInfoStatusUpdated, this, &CViewportTitleDlg::UpdateDisplayInfo); @@ -314,13 +312,13 @@ void CViewportTitleDlg::OnInitDialog() m_cameraSpeed->setFixedWidth(width); + m_prefabViewportFocusPathHandler.Initialize(m_ui->m_prefabFocusPath, m_ui->m_prefabFocusBackButton); } ////////////////////////////////////////////////////////////////////////// void CViewportTitleDlg::SetTitle(const QString& title) { m_title = title; - m_ui->m_titleBtn->setText(m_title); } ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Editor/ViewportTitleDlg.h b/Code/Editor/ViewportTitleDlg.h index 4a2a454907..dfdf567a39 100644 --- a/Code/Editor/ViewportTitleDlg.h +++ b/Code/Editor/ViewportTitleDlg.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -176,6 +177,8 @@ protected: QWidgetAction* m_gridSizeActionWidget = nullptr; QWidgetAction* m_angleSizeActionWidget = nullptr; + AzToolsFramework::Prefab::PrefabViewportFocusPathHandler m_prefabViewportFocusPathHandler; + QScopedPointer m_ui; }; diff --git a/Code/Editor/ViewportTitleDlg.ui b/Code/Editor/ViewportTitleDlg.ui index f0679dd2a1..e67f04b68a 100644 --- a/Code/Editor/ViewportTitleDlg.ui +++ b/Code/Editor/ViewportTitleDlg.ui @@ -42,22 +42,25 @@ 0 - + + + + :/Breadcrumb/img/UI20/Breadcrumb/arrow_left-default.svg:/Breadcrumb/img/UI20/Breadcrumb/arrow_left-default.svg + + + + Back + + + + + 0 0 - - Qt::NoContextMenu - - - Static - - - 11 - @@ -129,6 +132,12 @@ + + AzQtComponents::BreadCrumbs + QWidget +
AzQtComponents/Components/Widgets/Breadcrumbs.h
+ 1 +
AzQtComponents::ButtonDivider QWidget diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 2338c8892b..276d76f848 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace AzToolsFramework::Prefab { @@ -23,12 +24,14 @@ namespace AzToolsFramework::Prefab "Instance Entity Mapper Interface could not be found. " "Check that it is being correctly initialized."); + EditorEntityContextNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); } PrefabFocusHandler::~PrefabFocusHandler() { AZ::Interface::Unregister(this); + EditorEntityContextNotificationBus::Handler::BusDisconnect(); } PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId) @@ -55,8 +58,28 @@ namespace AzToolsFramework::Prefab if (!focusedInstance.has_value()) { - return AZ::Failure(AZStd::string( - "Prefab Focus Handler: Couldn't find owning instance of entityId provided.")); + return AZ::Failure(AZStd::string("Prefab Focus Handler: Couldn't find owning instance of entityId provided.")); + } + + if (&m_focusedInstance->get() != &focusedInstance->get()) + { + m_focusedInstance = focusedInstance; + m_focusedTemplateId = focusedInstance->get().GetTemplateId(); + s_focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); + + RefreshInstanceFocusList(); + + PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); + } + + return AZ::Success(); + } + + PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex(int index) + { + if (index < 0 || index >= m_instanceFocusVector.size()) + { + return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex.")); } m_focusedInstance = focusedInstance; @@ -68,7 +91,13 @@ namespace AzToolsFramework::Prefab focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); } + RefreshInstanceFocusList(); + + PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); + } + return AZ::Success(); + } TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId() @@ -93,4 +122,39 @@ namespace AzToolsFramework::Prefab return instance.has_value() && (&instance->get() == &m_focusedInstance->get()); } + const AZ::IO::Path& PrefabFocusHandler::GetPrefabFocusPath() + { + return m_instanceFocusPath; + } + + void PrefabFocusHandler::OnEntityStreamLoadSuccess() + { + // Focus on the root prefab (AZ::EntityId() will default to it) + FocusOnOwningPrefab(AZ::EntityId()); + } + + void PrefabFocusHandler::RefreshInstanceFocusList() + { + m_instanceFocusVector.clear(); + m_instanceFocusPath.clear(); + + AZStd::list instanceFocusList; + + // Use a support list to easily push front while traversing the prefab hierarchy + InstanceOptionalReference currentInstance = m_focusedInstance; + while (currentInstance.has_value()) + { + instanceFocusList.push_front(currentInstance); + + currentInstance = currentInstance->get().GetParentInstance(); + } + + // Populate internals using the support list + for (auto& instance : instanceFocusList) + { + m_instanceFocusPath.Append(instance->get().GetContainerEntity()->get().GetName()); + m_instanceFocusVector.emplace_back(instance); + } + } + } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index fa7727894d..35ef9aba61 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -10,6 +10,7 @@ #include +#include #include #include #include @@ -21,6 +22,7 @@ namespace AzToolsFramework::Prefab //! Handles Prefab Focus mode, determining which prefab file entity changes will target. class PrefabFocusHandler final : private PrefabFocusInterface + , private EditorEntityContextNotificationBus::Handler { public: AZ_CLASS_ALLOCATOR(PrefabFocusHandler, AZ::SystemAllocator, 0); @@ -30,13 +32,22 @@ namespace AzToolsFramework::Prefab // PrefabFocusInterface override ... PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override; + PrefabFocusOperationResult FocusOnPathIndex(int index) override; TemplateId GetFocusedPrefabTemplateId() override; InstanceOptionalReference GetFocusedPrefabInstance() override; bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) override; + const AZ::IO::Path& GetPrefabFocusPath() override; + + // EditorEntityContextNotificationBus... + void OnEntityStreamLoadSuccess() override; private: + void RefreshInstanceFocusList(); + InstanceOptionalReference m_focusedInstance; TemplateId m_focusedTemplateId; + AZStd::vector m_instanceFocusVector; + AZ::IO::Path m_instanceFocusPath; InstanceEntityMapperInterface* m_instanceEntityMapperInterface; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h index 833a5ef7c8..9f695f2bf7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h @@ -28,6 +28,10 @@ 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 instance at position index of the current path. + //! @param index The index of the instance in the current path that we want the prefab system to focus on. + virtual PrefabFocusOperationResult FocusOnPathIndex(int index) = 0; + //! Returns the template id of the instance the prefab system is focusing on. virtual TemplateId GetFocusedPrefabTemplateId() = 0; @@ -38,6 +42,11 @@ namespace AzToolsFramework::Prefab //! @param entityId The entityId of the queried entity. //! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise. virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) = 0; + + //! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants. + //! @param entityId The entityId of the queried entity. + //! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise. + virtual const AZ::IO::Path& GetPrefabFocusPath() = 0; }; } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h new file mode 100644 index 0000000000..5eda6ff2e3 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h @@ -0,0 +1,25 @@ +/* + * 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 AzToolsFramework::Prefab +{ + class PrefabFocusNotifications : public AZ::EBusTraits + { + public: + virtual ~PrefabFocusNotifications() = default; + + virtual void OnPrefabFocusChanged() = 0; + }; + + using PrefabFocusNotificationBus = AZ::EBus; + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp new file mode 100644 index 0000000000..3ca3ec990f --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp @@ -0,0 +1,54 @@ +/* + * 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 AzToolsFramework::Prefab +{ + PrefabViewportFocusPathHandler::~PrefabViewportFocusPathHandler() + { + // Disconnect from Prefab Focus Notifications + PrefabFocusNotificationBus::Handler::BusDisconnect(); + } + + void PrefabViewportFocusPathHandler::Initialize(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton) + { + // Get reference to the PrefabFocusInterface handler + m_prefabFocusInterface = AZ::Interface::Get(); + if (m_prefabFocusInterface == nullptr) + { + AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabViewportFocusPathHandler construction."); + return; + } + + // Connect to Prefab Focus Notifications + PrefabFocusNotificationBus::Handler::BusConnect(); + + // Initialize Widgets + m_breadcrumbsWidget = breadcrumbsWidget; + m_backButton = backButton; + + m_backButton->setEnabled(false); + + connect( + m_breadcrumbsWidget, &AzQtComponents::BreadCrumbs::linkClicked, this, + [&](const QString&, int linkIndex) + { + m_prefabFocusInterface->FocusOnPathIndex(linkIndex); + } + ); + } + + void PrefabViewportFocusPathHandler::OnPrefabFocusChanged() + { + // Push new Path + m_breadcrumbsWidget->pushPath(m_prefabFocusInterface->GetPrefabFocusPath().c_str()); + } + +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h new file mode 100644 index 0000000000..63e5579c00 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h @@ -0,0 +1,40 @@ +/* + * 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 +#include + +namespace AzToolsFramework::Prefab +{ + class PrefabFocusInterface; + + class PrefabViewportFocusPathHandler + : public PrefabFocusNotificationBus::Handler + , private QObject + { + public: + ~PrefabViewportFocusPathHandler(); + + void Initialize(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton); + + // PrefabFocusNotificationBus... + void OnPrefabFocusChanged() override; + + private: + AzQtComponents::BreadCrumbs* m_breadcrumbsWidget = nullptr; + QToolButton* m_backButton = nullptr; + + PrefabFocusInterface* m_prefabFocusInterface = nullptr; + }; +} // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 88febef8c2..019ae21674 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -635,6 +635,7 @@ set(FILES Prefab/PrefabFocusHandler.h Prefab/PrefabFocusHandler.cpp Prefab/PrefabFocusInterface.h + Prefab/PrefabFocusNotificationBus.h Prefab/PrefabIdTypes.h Prefab/PrefabLoader.h Prefab/PrefabLoader.cpp @@ -733,6 +734,8 @@ set(FILES UI/Prefab/PrefabIntegrationInterface.h UI/Prefab/PrefabUiHandler.h UI/Prefab/PrefabUiHandler.cpp + UI/Prefab/PrefabViewportFocusPathHandler.h + UI/Prefab/PrefabViewportFocusPathHandler.cpp PythonTerminal/ScriptHelpDialog.cpp PythonTerminal/ScriptHelpDialog.h PythonTerminal/ScriptHelpDialog.ui From 59d7cd60583a782a50b564cc4ed436e21b992247 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 21 Sep 2021 15:01:16 -0700 Subject: [PATCH 02/24] Enable back button and introduce behavior Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Prefab/PrefabFocusHandler.cpp | 5 +++++ .../Prefab/PrefabFocusHandler.h | 1 + .../Prefab/PrefabFocusInterface.h | 8 +++++--- .../Prefab/PrefabViewportFocusPathHandler.cpp | 19 ++++++++++++++++++- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 276d76f848..f203d70e06 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -127,6 +127,11 @@ namespace AzToolsFramework::Prefab return m_instanceFocusPath; } + const int PrefabFocusHandler::GetPrefabFocusPathLength() + { + return m_instanceFocusVector.size(); + } + void PrefabFocusHandler::OnEntityStreamLoadSuccess() { // Focus on the root prefab (AZ::EntityId() will default to it) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index 35ef9aba61..6e8e3ab3e8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -37,6 +37,7 @@ namespace AzToolsFramework::Prefab InstanceOptionalReference GetFocusedPrefabInstance() override; bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) override; const AZ::IO::Path& GetPrefabFocusPath() override; + const int GetPrefabFocusPathLength() override; // EditorEntityContextNotificationBus... void OnEntityStreamLoadSuccess() override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h index 9f695f2bf7..0084a2ef2e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h @@ -43,10 +43,12 @@ namespace AzToolsFramework::Prefab //! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise. virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) = 0; - //! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants. - //! @param entityId The entityId of the queried entity. - //! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise. + //! Returns the path from the root instance to the currently focused instance. + //! @return A path composed from the names of the container entities for the instance path. virtual const AZ::IO::Path& GetPrefabFocusPath() = 0; + + //! Returns the size of the path to the currently focused instance. + virtual const int GetPrefabFocusPathLength() = 0; }; } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp index 3ca3ec990f..dba5090170 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp @@ -34,8 +34,9 @@ namespace AzToolsFramework::Prefab m_breadcrumbsWidget = breadcrumbsWidget; m_backButton = backButton; - m_backButton->setEnabled(false); + m_backButton->setVisible(false); + // If a part of the path is clicked, focus on that instance connect( m_breadcrumbsWidget, &AzQtComponents::BreadCrumbs::linkClicked, this, [&](const QString&, int linkIndex) @@ -43,12 +44,28 @@ namespace AzToolsFramework::Prefab m_prefabFocusInterface->FocusOnPathIndex(linkIndex); } ); + + // The back button will allow user to go one level up + connect(m_backButton, &QToolButton::clicked, this, + [&]() + { + int length = m_prefabFocusInterface->GetPrefabFocusPathLength(); + + if (length > 1) + { + m_prefabFocusInterface->FocusOnPathIndex(length - 2); + } + } + ); } void PrefabViewportFocusPathHandler::OnPrefabFocusChanged() { // Push new Path m_breadcrumbsWidget->pushPath(m_prefabFocusInterface->GetPrefabFocusPath().c_str()); + + // Only show the back icon button if the path has more than one element + m_backButton->setVisible(m_prefabFocusInterface->GetPrefabFocusPathLength() > 1); } } // namespace AzToolsFramework::Prefab From e2c4dbe2772de00112831e06effaf94e08352f50 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 21 Sep 2021 15:02:02 -0700 Subject: [PATCH 03/24] Fix warning for numeric cast. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzToolsFramework/Prefab/PrefabFocusHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index f203d70e06..a8b8712510 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -129,7 +129,7 @@ namespace AzToolsFramework::Prefab const int PrefabFocusHandler::GetPrefabFocusPathLength() { - return m_instanceFocusVector.size(); + return aznumeric_cast(m_instanceFocusVector.size()); } void PrefabFocusHandler::OnEntityStreamLoadSuccess() From 7a0be4ff6538c9bdbd380d905d0ad5678b9b0d5b Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 21 Sep 2021 17:09:31 -0700 Subject: [PATCH 04/24] Refresh delegate painting in the Outliner even if the widget is not in focus. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../FocusMode/FocusModeNotificationBus.h | 27 +++++++++++++++++++ .../FocusMode/FocusModeSystemComponent.cpp | 7 +++++ .../UI/Outliner/EntityOutlinerTreeView.cpp | 9 +++++++ .../UI/Outliner/EntityOutlinerTreeView.hxx | 5 ++++ .../Prefab/PrefabViewportFocusPathHandler.cpp | 14 ++++++---- .../aztoolsframework_files.cmake | 1 + 6 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h new file mode 100644 index 0000000000..e42c02fe3b --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h @@ -0,0 +1,27 @@ +/* + * 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 AzToolsFramework +{ + class FocusModeNotifications + : public AZ::EBusTraits + { + public: + virtual ~FocusModeNotifications() = default; + + virtual void OnEditorFocusChanged(AZ::EntityId entityId) = 0; + }; + + using FocusModeNotificationBus = AZ::EBus; + +} // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index e08191032d..817006413d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -9,6 +9,7 @@ #include #include +#include #include namespace AzToolsFramework @@ -64,7 +65,13 @@ namespace AzToolsFramework void FocusModeSystemComponent::SetFocusRoot(AZ::EntityId entityId) { + if (m_focusRoot == entityId) + { + return; + } + m_focusRoot = entityId; + FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, m_focusRoot); // TODO - If m_focusRoot != AZ::EntityId(), activate focus mode via ViewportEditorModeTrackerInterface; else, deactivate focus mode } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp index 0549c600d3..73276973bb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp @@ -38,10 +38,14 @@ namespace AzToolsFramework AZ_Assert((m_editorEntityFrameworkInterface != nullptr), "EntityOutlinerTreeView requires a EditorEntityFrameworkInterface instance on Construction."); + + FocusModeNotificationBus::Handler::BusConnect(); } EntityOutlinerTreeView::~EntityOutlinerTreeView() { + FocusModeNotificationBus::Handler::BusDisconnect(); + ClearQueuedMouseEvent(); } @@ -261,6 +265,11 @@ namespace AzToolsFramework StyledTreeView::StartCustomDrag(indexListSorted, supportedActions); } + + void EntityOutlinerTreeView::OnEditorFocusChanged([[maybe_unused]] AZ::EntityId entityId) + { + viewport()->repaint(); + } } #include diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx index 66cd082407..8d71b3ed31 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.hxx @@ -15,6 +15,7 @@ #include #include +#include #include #endif @@ -35,6 +36,7 @@ namespace AzToolsFramework //! of other entities. If the selection updates instantly, this would never be possible. class EntityOutlinerTreeView : public AzQtComponents::StyledTreeView + , private FocusModeNotificationBus::Handler { Q_OBJECT; public: @@ -60,6 +62,9 @@ namespace AzToolsFramework void dragMoveEvent(QDragMoveEvent* event) override; void dropEvent(QDropEvent* event) override; + // FocusModeNotificationBus overrides ... + void OnEditorFocusChanged(AZ::EntityId entityId) override; + //! Renders the left side of the item: appropriate background, branch lines, icons. void drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp index dba5090170..2c5bc361ea 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp @@ -34,11 +34,8 @@ namespace AzToolsFramework::Prefab m_breadcrumbsWidget = breadcrumbsWidget; m_backButton = backButton; - m_backButton->setVisible(false); - // If a part of the path is clicked, focus on that instance - connect( - m_breadcrumbsWidget, &AzQtComponents::BreadCrumbs::linkClicked, this, + connect(m_breadcrumbsWidget, &AzQtComponents::BreadCrumbs::linkClicked, this, [&](const QString&, int linkIndex) { m_prefabFocusInterface->FocusOnPathIndex(linkIndex); @@ -65,7 +62,14 @@ namespace AzToolsFramework::Prefab m_breadcrumbsWidget->pushPath(m_prefabFocusInterface->GetPrefabFocusPath().c_str()); // Only show the back icon button if the path has more than one element - m_backButton->setVisible(m_prefabFocusInterface->GetPrefabFocusPathLength() > 1); + if(m_prefabFocusInterface->GetPrefabFocusPathLength() > 1) + { + m_backButton->show(); + } + else + { + m_backButton->hide(); + } } } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 019ae21674..459198c3ec 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -150,6 +150,7 @@ set(FILES Fingerprinting/TypeFingerprinter.h Fingerprinting/TypeFingerprinter.cpp FocusMode/FocusModeInterface.h + FocusMode/FocusModeNotificationBus.h FocusMode/FocusModeSystemComponent.h FocusMode/FocusModeSystemComponent.cpp Logger/TraceLogger.cpp From 55e2684471d6dd98722bf50bb363974e62b63ab7 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 22 Sep 2021 14:13:50 -0700 Subject: [PATCH 05/24] Refactor Prefab Focus function to move reused code to helper. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Prefab/PrefabFocusHandler.cpp | 37 +++++++++---------- .../Prefab/PrefabFocusHandler.h | 1 + 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index a8b8712510..e2f5660bb7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -56,23 +56,7 @@ namespace AzToolsFramework::Prefab focusedInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId); } - if (!focusedInstance.has_value()) - { - return AZ::Failure(AZStd::string("Prefab Focus Handler: Couldn't find owning instance of entityId provided.")); - } - - if (&m_focusedInstance->get() != &focusedInstance->get()) - { - m_focusedInstance = focusedInstance; - m_focusedTemplateId = focusedInstance->get().GetTemplateId(); - s_focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); - - RefreshInstanceFocusList(); - - PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); - } - - return AZ::Success(); + return FocusOnPrefabInstance(focusedInstance); } PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex(int index) @@ -82,8 +66,22 @@ namespace AzToolsFramework::Prefab return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex.")); } - m_focusedInstance = focusedInstance; - m_focusedTemplateId = focusedInstance->get().GetTemplateId(); + InstanceOptionalReference focusedInstance = m_instanceFocusVector[index]; + + return FocusOnPrefabInstance(focusedInstance); + } + + PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstance(InstanceOptionalReference focusedInstance) + { + if (!focusedInstance.has_value()) + { + return AZ::Failure(AZStd::string("Prefab Focus Handler: invalid instance to focus on.")); + } + + if (&m_focusedInstance->get() != &focusedInstance->get()) + { + m_focusedInstance = focusedInstance; + m_focusedTemplateId = focusedInstance->get().GetTemplateId(); FocusModeInterface* focusModeInterface = AZ::Interface::Get(); if (focusModeInterface) @@ -97,7 +95,6 @@ namespace AzToolsFramework::Prefab } return AZ::Success(); - } TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index 6e8e3ab3e8..7dd22eda08 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -43,6 +43,7 @@ namespace AzToolsFramework::Prefab void OnEntityStreamLoadSuccess() override; private: + PrefabFocusOperationResult FocusOnPrefabInstance(InstanceOptionalReference focusedInstance); void RefreshInstanceFocusList(); InstanceOptionalReference m_focusedInstance; From be88be4bb897e86d6f7c20c3981c82de7a4fcfc4 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 22 Sep 2021 16:12:11 -0700 Subject: [PATCH 06/24] Minor fixes to pass AR Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- Code/Editor/ViewportTitleDlg.ui | 171 +++++++++--------- .../Prefab/PrefabFocusHandler.cpp | 17 +- .../Prefab/PrefabViewportFocusPathHandler.cpp | 19 +- .../Prefab/PrefabViewportFocusPathHandler.h | 1 + 4 files changed, 105 insertions(+), 103 deletions(-) diff --git a/Code/Editor/ViewportTitleDlg.ui b/Code/Editor/ViewportTitleDlg.ui index e67f04b68a..9b02876054 100644 --- a/Code/Editor/ViewportTitleDlg.ui +++ b/Code/Editor/ViewportTitleDlg.ui @@ -28,7 +28,7 @@ 29 - + 10 @@ -42,19 +42,18 @@ 0 - - - - :/Breadcrumb/img/UI20/Breadcrumb/arrow_left-default.svg:/Breadcrumb/img/UI20/Breadcrumb/arrow_left-default.svg - - - - Back - - - + + + Up one level + + + + :/Breadcrumb/img/UI20/Breadcrumb/arrow_left-default.svg:/Breadcrumb/img/UI20/Breadcrumb/arrow_left-default.svg + + +
- + 0 @@ -64,70 +63,78 @@ - - - - :/Menu/camera.svg:/Menu/camera.svg - - - - Camera settings - - - - - - - Debug information - - - - :/Menu/debug.svg:/Menu/debug.svg - - - - true - - - - - - - Toggle viewport helpers - - - - :/Menu/helpers.svg:/Menu/helpers.svg - - - - true - - - - - - - Viewport resolution - - - - :/Menu/resolution.svg:/Menu/resolution.svg - - - + + + Qt::Horizontal + + + + 40 + 20 + + + - - - Other settings - - - - :/Menu/menu.svg:/Menu/menu.svg - - - + + + Camera settings + + + + :/Menu/camera.svg:/Menu/camera.svg + + + + + + + Debug information + + + + :/Menu/debug.svg:/Menu/debug.svg + + + true + + + + + + + Toggle viewport helpers + + + + :/Menu/helpers.svg:/Menu/helpers.svg + + + true + + + + + + + Viewport resolution + + + + :/Menu/resolution.svg:/Menu/resolution.svg + + + + + + + Other settings + + + + :/Menu/menu.svg:/Menu/menu.svg + + @@ -138,15 +145,9 @@
AzQtComponents/Components/Widgets/Breadcrumbs.h
1 - - AzQtComponents::ButtonDivider - QWidget -
AzQtComponents/Components/ButtonDivider.h
- 1 -
- - - - + + + + diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index e2f5660bb7..9ee05425ef 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -83,11 +84,17 @@ namespace AzToolsFramework::Prefab m_focusedInstance = focusedInstance; m_focusedTemplateId = focusedInstance->get().GetTemplateId(); - FocusModeInterface* focusModeInterface = AZ::Interface::Get(); - if (focusModeInterface) - { - focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId()); - } + AZ::EntityId containerEntityId = focusedInstance->get().GetContainerEntityId(); + + // Select the container entity + AzToolsFramework::SelectEntity(containerEntityId); + + // Focus on the descendants of the container entity + FocusModeInterface* focusModeInterface = AZ::Interface::Get(); + if (focusModeInterface) + { + focusModeInterface->SetFocusRoot(containerEntityId); + } RefreshInstanceFocusList(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp index 2c5bc361ea..c83c0016be 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp @@ -11,6 +11,12 @@ namespace AzToolsFramework::Prefab { + PrefabViewportFocusPathHandler::PrefabViewportFocusPathHandler() + { + // Connect to Prefab Focus Notifications + PrefabFocusNotificationBus::Handler::BusConnect(); + } + PrefabViewportFocusPathHandler::~PrefabViewportFocusPathHandler() { // Disconnect from Prefab Focus Notifications @@ -27,9 +33,6 @@ namespace AzToolsFramework::Prefab return; } - // Connect to Prefab Focus Notifications - PrefabFocusNotificationBus::Handler::BusConnect(); - // Initialize Widgets m_breadcrumbsWidget = breadcrumbsWidget; m_backButton = backButton; @@ -60,16 +63,6 @@ namespace AzToolsFramework::Prefab { // Push new Path m_breadcrumbsWidget->pushPath(m_prefabFocusInterface->GetPrefabFocusPath().c_str()); - - // Only show the back icon button if the path has more than one element - if(m_prefabFocusInterface->GetPrefabFocusPathLength() > 1) - { - m_backButton->show(); - } - else - { - m_backButton->hide(); - } } } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h index 63e5579c00..c874c7249f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h @@ -24,6 +24,7 @@ namespace AzToolsFramework::Prefab , private QObject { public: + PrefabViewportFocusPathHandler(); ~PrefabViewportFocusPathHandler(); void Initialize(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton); From f012407506e2fb508107dfe53c25e5171c1e3c5b Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Fri, 24 Sep 2021 18:49:17 -0700 Subject: [PATCH 07/24] Addressed multiple minor CR comments. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../FocusMode/FocusModeNotificationBus.h | 8 ++++++-- .../Prefab/PrefabFocusHandler.cpp | 15 +++++++-------- .../AzToolsFramework/Prefab/PrefabFocusHandler.h | 14 +++++++------- .../Prefab/PrefabFocusInterface.h | 10 +++++----- .../Prefab/PrefabFocusNotificationBus.h | 11 ++++++++--- .../UI/Prefab/PrefabViewportFocusPathHandler.cpp | 4 +--- .../UI/Prefab/PrefabViewportFocusPathHandler.h | 2 +- 7 files changed, 35 insertions(+), 29 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h index e42c02fe3b..a16bb9a3ef 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h @@ -13,12 +13,16 @@ namespace AzToolsFramework { + //! Used to notify when the editor focus changes. class FocusModeNotifications : public AZ::EBusTraits { - public: - virtual ~FocusModeNotifications() = default; + protected: + ~FocusModeNotifications() = default; + public: + //! Triggered when the editor focus is changed to a different entity. + //! @param entityId The entity the focus has been moved to. virtual void OnEditorFocusChanged(AZ::EntityId entityId) = 0; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 7d4ca36458..6135f35df5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -79,7 +79,7 @@ namespace AzToolsFramework::Prefab return AZ::Failure(AZStd::string("Prefab Focus Handler: invalid instance to focus on.")); } - if (&m_focusedInstance->get() != &focusedInstance->get()) + if (!m_focusedInstance.has_value() || &m_focusedInstance->get() != &focusedInstance->get()) { m_focusedInstance = focusedInstance; m_focusedTemplateId = focusedInstance->get().GetTemplateId(); @@ -90,8 +90,7 @@ namespace AzToolsFramework::Prefab AzToolsFramework::SelectEntity(containerEntityId); // Focus on the descendants of the container entity - FocusModeInterface* focusModeInterface = AZ::Interface::Get(); - if (focusModeInterface) + if (FocusModeInterface* focusModeInterface = AZ::Interface::Get()) { focusModeInterface->SetFocusRoot(containerEntityId); } @@ -104,17 +103,17 @@ namespace AzToolsFramework::Prefab return AZ::Success(); } - TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId() + TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId() const { return m_focusedTemplateId; } - InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance() + InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance() const { return m_focusedInstance; } - bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) + bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) const { if (!m_focusedInstance.has_value()) { @@ -132,12 +131,12 @@ namespace AzToolsFramework::Prefab return instance.has_value() && (&instance->get() == &m_focusedInstance->get()); } - const AZ::IO::Path& PrefabFocusHandler::GetPrefabFocusPath() + const AZ::IO::Path& PrefabFocusHandler::GetPrefabFocusPath() const { return m_instanceFocusPath; } - const int PrefabFocusHandler::GetPrefabFocusPathLength() + const int PrefabFocusHandler::GetPrefabFocusPathLength() const { return aznumeric_cast(m_instanceFocusVector.size()); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index 7dd22eda08..0362e2d66d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -30,16 +30,16 @@ namespace AzToolsFramework::Prefab PrefabFocusHandler(); ~PrefabFocusHandler(); - // PrefabFocusInterface override ... + // PrefabFocusInterface overrides ... PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override; PrefabFocusOperationResult FocusOnPathIndex(int index) override; - TemplateId GetFocusedPrefabTemplateId() override; - InstanceOptionalReference GetFocusedPrefabInstance() override; - bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) override; - const AZ::IO::Path& GetPrefabFocusPath() override; - const int GetPrefabFocusPathLength() override; + TemplateId GetFocusedPrefabTemplateId() const override; + InstanceOptionalReference GetFocusedPrefabInstance() const override; + bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const override; + const AZ::IO::Path& GetPrefabFocusPath() const override; + const int GetPrefabFocusPathLength() const override; - // EditorEntityContextNotificationBus... + // EditorEntityContextNotificationBus overrides ... void OnEntityStreamLoadSuccess() override; private: diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h index 0084a2ef2e..3efd2d4deb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h @@ -33,22 +33,22 @@ namespace AzToolsFramework::Prefab virtual PrefabFocusOperationResult FocusOnPathIndex(int index) = 0; //! Returns the template id of the instance the prefab system is focusing on. - virtual TemplateId GetFocusedPrefabTemplateId() = 0; + virtual TemplateId GetFocusedPrefabTemplateId() const = 0; //! Returns a reference to the instance the prefab system is focusing on. - virtual InstanceOptionalReference GetFocusedPrefabInstance() = 0; + virtual InstanceOptionalReference GetFocusedPrefabInstance() const = 0; //! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants. //! @param entityId The entityId of the queried entity. //! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise. - virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) = 0; + virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0; //! Returns the path from the root instance to the currently focused instance. //! @return A path composed from the names of the container entities for the instance path. - virtual const AZ::IO::Path& GetPrefabFocusPath() = 0; + virtual const AZ::IO::Path& GetPrefabFocusPath() const = 0; //! Returns the size of the path to the currently focused instance. - virtual const int GetPrefabFocusPathLength() = 0; + virtual const int GetPrefabFocusPathLength() const = 0; }; } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h index 5eda6ff2e3..aa27d87013 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h @@ -9,14 +9,19 @@ #pragma once #include +#include namespace AzToolsFramework::Prefab { - class PrefabFocusNotifications : public AZ::EBusTraits + //! Used to notify when the editor focus changes. + class PrefabFocusNotifications + : public AZ::EBusTraits { - public: - virtual ~PrefabFocusNotifications() = default; + protected: + ~PrefabFocusNotifications() = default; + public: + //! Triggered when the editor focus is changed to a different prefab. virtual void OnPrefabFocusChanged() = 0; }; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp index c83c0016be..c1dd66ec7f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp @@ -49,9 +49,7 @@ namespace AzToolsFramework::Prefab connect(m_backButton, &QToolButton::clicked, this, [&]() { - int length = m_prefabFocusInterface->GetPrefabFocusPathLength(); - - if (length > 1) + if (int length = m_prefabFocusInterface->GetPrefabFocusPathLength(); length > 1) { m_prefabFocusInterface->FocusOnPathIndex(length - 2); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h index c874c7249f..5ce76d78ec 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h @@ -29,7 +29,7 @@ namespace AzToolsFramework::Prefab void Initialize(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton); - // PrefabFocusNotificationBus... + // PrefabFocusNotificationBus overrides ... void OnPrefabFocusChanged() override; private: From ce334f9443ff37b4ba7b615f92e634a45d11fbc2 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 28 Sep 2021 17:48:36 -0700 Subject: [PATCH 08/24] Correctly set focused entity in case the root prefab instance is focused. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Prefab/PrefabFocusHandler.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index c17b58486a..9bfe64bc84 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -84,11 +84,20 @@ namespace AzToolsFramework::Prefab m_focusedInstance = focusedInstance; m_focusedTemplateId = focusedInstance->get().GetTemplateId(); - AZ::EntityId containerEntityId = focusedInstance->get().GetContainerEntityId(); + AZ::EntityId containerEntityId; - // Select the container entity - AzToolsFramework::SelectEntity(containerEntityId); + if (focusedInstance->get().GetParentInstance() != AZStd::nullopt) + { + containerEntityId = focusedInstance->get().GetContainerEntityId(); + // Select the container entity + AzToolsFramework::SelectEntity(containerEntityId); + } + else + { + containerEntityId = AZ::EntityId(); + } + // Focus on the descendants of the container entity if (FocusModeInterface* focusModeInterface = AZ::Interface::Get()) { @@ -96,7 +105,6 @@ namespace AzToolsFramework::Prefab } RefreshInstanceFocusList(); - PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged); } From c204aa1abe9818a2624c4cb62c20616c20bb2eb9 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 28 Sep 2021 18:16:02 -0700 Subject: [PATCH 09/24] Change notification buses to be addressed on EntityContextId, and connect the handlers to the default id. Request interfaces should not need that id as they can retrieve it from the entityId. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../FocusMode/FocusModeNotificationBus.h | 15 ++++++++++++--- .../Prefab/PrefabFocusHandler.cpp | 4 ++++ .../Prefab/PrefabFocusNotificationBus.h | 13 ++++++++++--- .../UI/Outliner/EntityOutlinerTreeView.cpp | 7 ++++++- .../UI/Prefab/PrefabViewportFocusPathHandler.cpp | 10 ++++++++-- 5 files changed, 40 insertions(+), 9 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h index a16bb9a3ef..ab8629ac85 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeNotificationBus.h @@ -11,19 +11,28 @@ #include #include +#include + namespace AzToolsFramework { //! Used to notify when the editor focus changes. class FocusModeNotifications : public AZ::EBusTraits { - protected: - ~FocusModeNotifications() = default; - public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + using BusIdType = AzFramework::EntityContextId; + ////////////////////////////////////////////////////////////////////////// + //! Triggered when the editor focus is changed to a different entity. //! @param entityId The entity the focus has been moved to. virtual void OnEditorFocusChanged(AZ::EntityId entityId) = 0; + + protected: + ~FocusModeNotifications() = default; }; using FocusModeNotificationBus = AZ::EBus; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 9bfe64bc84..3ce8065ae5 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -96,6 +96,10 @@ namespace AzToolsFramework::Prefab else { containerEntityId = AZ::EntityId(); + + // Clear the selection + AzToolsFramework::SelectEntities({}); + } // Focus on the descendants of the container entity diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h index aa27d87013..31bab719e2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusNotificationBus.h @@ -17,12 +17,19 @@ namespace AzToolsFramework::Prefab class PrefabFocusNotifications : public AZ::EBusTraits { - protected: - ~PrefabFocusNotifications() = default; - public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + using BusIdType = AzFramework::EntityContextId; + ////////////////////////////////////////////////////////////////////////// + //! Triggered when the editor focus is changed to a different prefab. virtual void OnPrefabFocusChanged() = 0; + + protected: + ~PrefabFocusNotifications() = default; }; using PrefabFocusNotificationBus = AZ::EBus; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp index 6a40b3be3a..4364ae1efc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerTreeView.cpp @@ -39,7 +39,12 @@ namespace AzToolsFramework AZ_Assert((m_editorEntityFrameworkInterface != nullptr), "EntityOutlinerTreeView requires a EditorEntityFrameworkInterface instance on Construction."); - FocusModeNotificationBus::Handler::BusConnect(); + + AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); + + FocusModeNotificationBus::Handler::BusConnect(editorEntityContextId); viewport()->setMouseTracking(true); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp index c1dd66ec7f..09d0579dab 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp @@ -6,15 +6,21 @@ * */ -#include #include +#include +#include + namespace AzToolsFramework::Prefab { PrefabViewportFocusPathHandler::PrefabViewportFocusPathHandler() { // Connect to Prefab Focus Notifications - PrefabFocusNotificationBus::Handler::BusConnect(); + AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); + + PrefabFocusNotificationBus::Handler::BusConnect(editorEntityContextId); } PrefabViewportFocusPathHandler::~PrefabViewportFocusPathHandler() From 9a325c657de678bd59f2d08aa58771cf84fd9e31 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 28 Sep 2021 18:41:46 -0700 Subject: [PATCH 10/24] Add EntityContextId argument to focus interface functions that don't deal with entityIds. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../FocusMode/FocusModeInterface.h | 4 ++-- .../FocusMode/FocusModeSystemComponent.cpp | 4 ++-- .../FocusMode/FocusModeSystemComponent.h | 4 ++-- .../Prefab/PrefabFocusHandler.cpp | 11 ++++++----- .../Prefab/PrefabFocusHandler.h | 10 +++++----- .../Prefab/PrefabFocusInterface.h | 12 +++++++----- .../Prefab/PrefabViewportFocusPathHandler.cpp | 17 ++++++++--------- .../UI/Prefab/PrefabViewportFocusPathHandler.h | 4 ++++ 8 files changed, 36 insertions(+), 30 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h index a4b90f95b7..d7da1da8b3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h @@ -26,11 +26,11 @@ namespace AzToolsFramework virtual void SetFocusRoot(AZ::EntityId entityId) = 0; //! Clears the Editor focus, allowing the user to select the whole level again. - virtual void ClearFocusRoot() = 0; + virtual void ClearFocusRoot(AzFramework::EntityContextId entityContextId) = 0; //! Returns the entity id of the root of the current Editor focus. //! @return The entity id of the root of the Editor focus, or an invalid entity id if no focus is set. - virtual AZ::EntityId GetFocusRoot() = 0; + virtual AZ::EntityId GetFocusRoot(AzFramework::EntityContextId entityContextId) = 0; //! Returns whether the entity id provided is part of the focused sub-tree. virtual bool IsInFocusSubTree(AZ::EntityId entityId) const = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp index f6afc3bff5..d1c4d37821 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.cpp @@ -76,12 +76,12 @@ namespace AzToolsFramework // TODO - If m_focusRoot != AZ::EntityId(), activate focus mode via ViewportEditorModeTrackerInterface; else, deactivate focus mode } - void FocusModeSystemComponent::ClearFocusRoot() + void FocusModeSystemComponent::ClearFocusRoot([[maybe_unused]] AzFramework::EntityContextId entityContextId) { SetFocusRoot(AZ::EntityId()); } - AZ::EntityId FocusModeSystemComponent::GetFocusRoot() + AZ::EntityId FocusModeSystemComponent::GetFocusRoot([[maybe_unused]] AzFramework::EntityContextId entityContextId) { return m_focusRoot; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h index 27ddebe1ed..dabaa6aaf4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeSystemComponent.h @@ -40,8 +40,8 @@ namespace AzToolsFramework // FocusModeInterface overrides ... void SetFocusRoot(AZ::EntityId entityId) override; - void ClearFocusRoot() override; - AZ::EntityId GetFocusRoot() override; + void ClearFocusRoot(AzFramework::EntityContextId entityContextId) override; + AZ::EntityId GetFocusRoot(AzFramework::EntityContextId entityContextId) override; bool IsInFocusSubTree(AZ::EntityId entityId) const override; private: diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 3ce8065ae5..4e58bbdf05 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -60,7 +60,7 @@ namespace AzToolsFramework::Prefab return FocusOnPrefabInstance(focusedInstance); } - PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex(int index) + PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index) { if (index < 0 || index >= m_instanceFocusVector.size()) { @@ -115,12 +115,13 @@ namespace AzToolsFramework::Prefab return AZ::Success(); } - TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId() const + TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const { return m_focusedTemplateId; } - InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance() const + InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance( + [[maybe_unused]] AzFramework::EntityContextId entityContextId) const { return m_focusedInstance; } @@ -143,12 +144,12 @@ namespace AzToolsFramework::Prefab return instance.has_value() && (&instance->get() == &m_focusedInstance->get()); } - const AZ::IO::Path& PrefabFocusHandler::GetPrefabFocusPath() const + const AZ::IO::Path& PrefabFocusHandler::GetPrefabFocusPath([[maybe_unused]] AzFramework::EntityContextId entityContextId) const { return m_instanceFocusPath; } - const int PrefabFocusHandler::GetPrefabFocusPathLength() const + const int PrefabFocusHandler::GetPrefabFocusPathLength([[maybe_unused]] AzFramework::EntityContextId entityContextId) const { return aznumeric_cast(m_instanceFocusVector.size()); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h index 0362e2d66d..2ccec36882 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h @@ -32,12 +32,12 @@ namespace AzToolsFramework::Prefab // PrefabFocusInterface overrides ... PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override; - PrefabFocusOperationResult FocusOnPathIndex(int index) override; - TemplateId GetFocusedPrefabTemplateId() const override; - InstanceOptionalReference GetFocusedPrefabInstance() const override; + PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override; + TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const override; + InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const override; bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const override; - const AZ::IO::Path& GetPrefabFocusPath() const override; - const int GetPrefabFocusPathLength() const override; + const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const override; + const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const override; // EditorEntityContextNotificationBus overrides ... void OnEntityStreamLoadSuccess() override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h index 3efd2d4deb..1c0f4f85e9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusInterface.h @@ -11,6 +11,8 @@ #include #include +#include + #include #include @@ -30,13 +32,13 @@ namespace AzToolsFramework::Prefab //! Set the focused prefab instance to the instance at position index of the current path. //! @param index The index of the instance in the current path that we want the prefab system to focus on. - virtual PrefabFocusOperationResult FocusOnPathIndex(int index) = 0; + virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0; //! Returns the template id of the instance the prefab system is focusing on. - virtual TemplateId GetFocusedPrefabTemplateId() const = 0; + virtual TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const = 0; //! Returns a reference to the instance the prefab system is focusing on. - virtual InstanceOptionalReference GetFocusedPrefabInstance() const = 0; + virtual InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const = 0; //! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants. //! @param entityId The entityId of the queried entity. @@ -45,10 +47,10 @@ namespace AzToolsFramework::Prefab //! Returns the path from the root instance to the currently focused instance. //! @return A path composed from the names of the container entities for the instance path. - virtual const AZ::IO::Path& GetPrefabFocusPath() const = 0; + virtual const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const = 0; //! Returns the size of the path to the currently focused instance. - virtual const int GetPrefabFocusPathLength() const = 0; + virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0; }; } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp index 09d0579dab..6b0de5dc53 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp @@ -8,19 +8,18 @@ #include -#include #include namespace AzToolsFramework::Prefab { PrefabViewportFocusPathHandler::PrefabViewportFocusPathHandler() { - // Connect to Prefab Focus Notifications - AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + // Get default EntityContextId AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( - editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); + m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); - PrefabFocusNotificationBus::Handler::BusConnect(editorEntityContextId); + // Connect to Prefab Focus Notifications + PrefabFocusNotificationBus::Handler::BusConnect(m_editorEntityContextId); } PrefabViewportFocusPathHandler::~PrefabViewportFocusPathHandler() @@ -47,7 +46,7 @@ namespace AzToolsFramework::Prefab connect(m_breadcrumbsWidget, &AzQtComponents::BreadCrumbs::linkClicked, this, [&](const QString&, int linkIndex) { - m_prefabFocusInterface->FocusOnPathIndex(linkIndex); + m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex); } ); @@ -55,9 +54,9 @@ namespace AzToolsFramework::Prefab connect(m_backButton, &QToolButton::clicked, this, [&]() { - if (int length = m_prefabFocusInterface->GetPrefabFocusPathLength(); length > 1) + if (int length = m_prefabFocusInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1) { - m_prefabFocusInterface->FocusOnPathIndex(length - 2); + m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2); } } ); @@ -66,7 +65,7 @@ namespace AzToolsFramework::Prefab void PrefabViewportFocusPathHandler::OnPrefabFocusChanged() { // Push new Path - m_breadcrumbsWidget->pushPath(m_prefabFocusInterface->GetPrefabFocusPath().c_str()); + m_breadcrumbsWidget->pushPath(m_prefabFocusInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str()); } } // namespace AzToolsFramework::Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h index 5ce76d78ec..f1d09311da 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h @@ -8,6 +8,8 @@ #pragma once +#include + #include #include @@ -36,6 +38,8 @@ namespace AzToolsFramework::Prefab AzQtComponents::BreadCrumbs* m_breadcrumbsWidget = nullptr; QToolButton* m_backButton = nullptr; + AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + PrefabFocusInterface* m_prefabFocusInterface = nullptr; }; } // namespace AzToolsFramework::Prefab From 34def643a206bb38db0b351e3c246b8145d0160d Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 28 Sep 2021 21:30:06 -0700 Subject: [PATCH 11/24] In Slice mode, don't instantiate the handler, and also hide and disable the breadcrumbs widgets. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- Code/Editor/ViewportTitleDlg.cpp | 24 +++++++++++++++++++++++- Code/Editor/ViewportTitleDlg.h | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Code/Editor/ViewportTitleDlg.cpp b/Code/Editor/ViewportTitleDlg.cpp index 0c5c45e5c8..f5a8834656 100644 --- a/Code/Editor/ViewportTitleDlg.cpp +++ b/Code/Editor/ViewportTitleDlg.cpp @@ -15,6 +15,7 @@ #include "ViewportTitleDlg.h" // Qt +#include #include #include @@ -38,6 +39,7 @@ #include #include +#include #include #include @@ -145,6 +147,11 @@ CViewportTitleDlg::~CViewportTitleDlg() AZ::VR::VREventBus::Handler::BusDisconnect(); GetISystem()->GetISystemEventDispatcher()->RemoveListener(this); GetIEditor()->UnregisterNotifyListener(this); + + if (m_prefabViewportFocusPathHandler) + { + delete m_prefabViewportFocusPathHandler; + } } void CViewportTitleDlg::SetupCameraDropdownMenu() @@ -312,7 +319,22 @@ void CViewportTitleDlg::OnInitDialog() m_cameraSpeed->setFixedWidth(width); - m_prefabViewportFocusPathHandler.Initialize(m_ui->m_prefabFocusPath, m_ui->m_prefabFocusBackButton); + bool isPrefabSystemEnabled = false; + AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled); + + if (isPrefabSystemEnabled) + { + m_prefabViewportFocusPathHandler = new AzToolsFramework::Prefab::PrefabViewportFocusPathHandler(); + m_prefabViewportFocusPathHandler->Initialize(m_ui->m_prefabFocusPath, m_ui->m_prefabFocusBackButton); + } + else + { + m_ui->m_prefabFocusPath->setEnabled(false); + m_ui->m_prefabFocusBackButton->setEnabled(false); + m_ui->m_prefabFocusPath->hide(); + m_ui->m_prefabFocusBackButton->hide(); + } + } ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Editor/ViewportTitleDlg.h b/Code/Editor/ViewportTitleDlg.h index dfdf567a39..6996fe7750 100644 --- a/Code/Editor/ViewportTitleDlg.h +++ b/Code/Editor/ViewportTitleDlg.h @@ -177,7 +177,7 @@ protected: QWidgetAction* m_gridSizeActionWidget = nullptr; QWidgetAction* m_angleSizeActionWidget = nullptr; - AzToolsFramework::Prefab::PrefabViewportFocusPathHandler m_prefabViewportFocusPathHandler; + AzToolsFramework::Prefab::PrefabViewportFocusPathHandler* m_prefabViewportFocusPathHandler = nullptr; QScopedPointer m_ui; }; From b28f871b2007ef71baf71481270562c1198f5bcf Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 28 Sep 2021 23:32:15 -0700 Subject: [PATCH 12/24] Fix casing of BreadCrumbs include in PrefabViewportFocusPathHandler. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h index f1d09311da..ce7744fb1b 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h @@ -12,7 +12,7 @@ #include -#include +#include #include #include From 94bd95cee3340632bdf637742fe00ae1f1e308eb Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 29 Sep 2021 09:58:04 -0700 Subject: [PATCH 13/24] Solve merge issue Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- Code/Editor/ViewportTitleDlg.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Code/Editor/ViewportTitleDlg.cpp b/Code/Editor/ViewportTitleDlg.cpp index 20b6ac9483..75d16e9a40 100644 --- a/Code/Editor/ViewportTitleDlg.cpp +++ b/Code/Editor/ViewportTitleDlg.cpp @@ -39,11 +39,8 @@ #include #include -<<<<<<< HEAD -#include -======= #include ->>>>>>> a8559c4000297b8c2876b8213d83f1678dbc3d84 +#include #include #include From 96d833fc4e04482897f4185013ff68da8332b976 Mon Sep 17 00:00:00 2001 From: amzn-tommy Date: Wed, 29 Sep 2021 12:07:01 -0700 Subject: [PATCH 14/24] Removing a function that was originally added to convert model asset ids in levels that were created prior to December 2020, which pre-dates anything created for the public O3DE release. Signed-off-by: amzn-tommy --- .../Source/Mesh/MeshComponentController.cpp | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp index 2dc3b9134a..6cdb44a9e1 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp @@ -194,41 +194,13 @@ namespace AZ incompatible.push_back(AZ_CRC("MeshService", 0x71d8a455)); } - // [GFX TODO] [ATOM-13339] Remove the ModelAsset id fix up function in MeshComponentController - // Model id was changed due to fix for [ATOM-13312]. We can remove this code when all the levels are updated. - void FixUpModelAsset(Data::Asset& modelAsset) - { - Data::AssetId assetId; - Data::AssetCatalogRequestBus::BroadcastResult( - assetId, - &Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, - modelAsset.GetHint().c_str(), - AZ::RPI::ModelAsset::TYPEINFO_Uuid(), - false); - if (assetId != modelAsset.GetId()) - { - if (assetId.IsValid()) - { - modelAsset = Data::Asset{ assetId, AZ::RPI::ModelAsset::TYPEINFO_Uuid(), modelAsset.GetHint().c_str() }; - modelAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::QueueLoad); - } - else - { - AZ_Error("MeshComponentController", false, "Failed to find asset id for [%s] ", modelAsset.GetHint().c_str()); - } - } - } - MeshComponentController::MeshComponentController(const MeshComponentConfig& config) : m_configuration(config) { - FixUpModelAsset(m_configuration.m_modelAsset); } void MeshComponentController::Activate(const AZ::EntityComponentIdPair& entityComponentIdPair) { - FixUpModelAsset(m_configuration.m_modelAsset); - const AZ::EntityId entityId = entityComponentIdPair.GetEntityId(); m_entityComponentIdPair = entityComponentIdPair; From a7f6bfbb507137c2ca761b132ba0db4990751514 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 29 Sep 2021 14:21:19 -0700 Subject: [PATCH 15/24] Fix tests to work with new interface APIs using EditorEntityContextId Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Tests/FocusMode/EditorFocusModeTests.cpp | 20 +++++++++++----- .../Prefab/PrefabFocus/PrefabFocusTests.cpp | 24 +++++++++++-------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp index 5972da58a7..59fcf85662 100644 --- a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp @@ -75,11 +75,15 @@ namespace AzToolsFramework FocusModeInterface* focusModeInterface = AZ::Interface::Get(); EXPECT_TRUE(focusModeInterface != nullptr); - focusModeInterface->SetFocusRoot(m_entityMap["carId"]); - EXPECT_EQ(focusModeInterface->GetFocusRoot(), m_entityMap["carId"]); + AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); - focusModeInterface->ClearFocusRoot(); - EXPECT_EQ(focusModeInterface->GetFocusRoot(), AZ::EntityId()); + focusModeInterface->SetFocusRoot(m_entityMap["carId"]); + EXPECT_EQ(focusModeInterface->GetFocusRoot(editorEntityContextId), m_entityMap["carId"]); + + focusModeInterface->ClearFocusRoot(editorEntityContextId); + EXPECT_EQ(focusModeInterface->GetFocusRoot(editorEntityContextId), AZ::EntityId()); } TEST_F(EditorFocusModeTests, EditorFocusModeTests_IsInFocusSubTree) @@ -87,7 +91,11 @@ namespace AzToolsFramework FocusModeInterface* focusModeInterface = AZ::Interface::Get(); EXPECT_TRUE(focusModeInterface != nullptr); - focusModeInterface->ClearFocusRoot(); + AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); + + focusModeInterface->ClearFocusRoot(editorEntityContextId); EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), true); EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), true); @@ -123,6 +131,6 @@ namespace AzToolsFramework EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), false); EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true); - focusModeInterface->ClearFocusRoot(); + focusModeInterface->ClearFocusRoot(editorEntityContextId); } } diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp index ffd6cd2a44..92de47fa62 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp @@ -69,15 +69,19 @@ namespace UnitTest { GenerateTestHierarchy(); + AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull(); + AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult( + editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId); + PrefabFocusInterface* prefabFocusInterface = AZ::Interface::Get(); EXPECT_TRUE(prefabFocusInterface != nullptr); // Verify FocusOnOwningPrefab works when passing the container entity of the root prefab. { prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["city"]->GetContainerEntityId()); - EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["city"]->GetTemplateId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(editorEntityContextId), m_instanceMap["city"]->GetTemplateId()); - auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(editorEntityContextId); EXPECT_TRUE(instance.has_value()); EXPECT_EQ(&instance->get(), m_instanceMap["city"]); } @@ -85,9 +89,9 @@ namespace UnitTest // Verify FocusOnOwningPrefab works when passing a nested entity of the root prefab. { prefabFocusInterface->FocusOnOwningPrefab(m_entityMap["city"]->GetId()); - EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["city"]->GetTemplateId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(editorEntityContextId), m_instanceMap["city"]->GetTemplateId()); - auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(editorEntityContextId); EXPECT_TRUE(instance.has_value()); EXPECT_EQ(&instance->get(), m_instanceMap["city"]); } @@ -95,9 +99,9 @@ namespace UnitTest // Verify FocusOnOwningPrefab works when passing the container entity of a nested prefab. { prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["car"]->GetContainerEntityId()); - EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["car"]->GetTemplateId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(editorEntityContextId), m_instanceMap["car"]->GetTemplateId()); - auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(editorEntityContextId); EXPECT_TRUE(instance.has_value()); EXPECT_EQ(&instance->get(), m_instanceMap["car"]); } @@ -105,9 +109,9 @@ namespace UnitTest // Verify FocusOnOwningPrefab works when passing a nested entity of the a nested prefab. { prefabFocusInterface->FocusOnOwningPrefab(m_entityMap["passenger1"]->GetId()); - EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["car"]->GetTemplateId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(editorEntityContextId), m_instanceMap["car"]->GetTemplateId()); - auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(editorEntityContextId); EXPECT_TRUE(instance.has_value()); EXPECT_EQ(&instance->get(), m_instanceMap["car"]); } @@ -121,9 +125,9 @@ namespace UnitTest EXPECT_TRUE(rootPrefabInstance.has_value()); prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId()); - EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), rootPrefabInstance->get().GetTemplateId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(editorEntityContextId), rootPrefabInstance->get().GetTemplateId()); - auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(editorEntityContextId); EXPECT_TRUE(instance.has_value()); EXPECT_EQ(&instance->get(), &rootPrefabInstance->get()); } From 615b53df50785b5a96f80d18cd805282af233441 Mon Sep 17 00:00:00 2001 From: chcurran <82187351+carlitosan@users.noreply.github.com> Date: Wed, 29 Sep 2021 16:26:14 -0700 Subject: [PATCH 16/24] fix bad unused variable component property check Signed-off-by: chcurran <82187351+carlitosan@users.noreply.github.com> --- .../Code/Builder/ScriptCanvasBuilder.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilder.cpp b/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilder.cpp index 9c3cdca3e8..5e97bf4aec 100644 --- a/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilder.cpp +++ b/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilder.cpp @@ -181,12 +181,15 @@ namespace ScriptCanvasBuilder continue; } - // copy to override unused list for editor display - m_overridesUnused.push_back(*graphVariable); - auto& overrideValue = m_overridesUnused.back(); - overrideValue.DeepCopy(*graphVariable); - overrideValue.SetScriptInputControlVisibility(AZ::Edit::PropertyVisibility::Hide); - overrideValue.SetAllowSignalOnChange(false); + if (graphVariable->IsComponentProperty()) + { + // copy to override unused list for editor display + m_overridesUnused.push_back(*graphVariable); + auto& overrideValue = m_overridesUnused.back(); + overrideValue.DeepCopy(*graphVariable); + overrideValue.SetScriptInputControlVisibility(AZ::Edit::PropertyVisibility::Hide); + overrideValue.SetAllowSignalOnChange(false); + } } } From 27c7e2dcb0b3f823fee9a84cee75a630ff724130 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 29 Sep 2021 19:13:30 -0700 Subject: [PATCH 17/24] Add correct include in FocusModeInterface to fix non-Unity builds. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzToolsFramework/FocusMode/FocusModeInterface.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h index a1047d4207..75e3bab60f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h @@ -12,6 +12,8 @@ #include #include +#include + namespace AzToolsFramework { //! FocusModeInterface From 4d368fe3292e1842713dd991662c9e3e0facb3d1 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Thu, 30 Sep 2021 08:18:12 -0700 Subject: [PATCH 18/24] Fix to capitalization in include via the UI Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- Code/Editor/ViewportTitleDlg.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Editor/ViewportTitleDlg.ui b/Code/Editor/ViewportTitleDlg.ui index 9b02876054..7ba6361cf9 100644 --- a/Code/Editor/ViewportTitleDlg.ui +++ b/Code/Editor/ViewportTitleDlg.ui @@ -142,7 +142,7 @@ AzQtComponents::BreadCrumbs QWidget -
AzQtComponents/Components/Widgets/Breadcrumbs.h
+
AzQtComponents/Components/Widgets/BreadCrumbs.h
1
From 225d9d3b3c297101f359be5f3ddde0784675b0e5 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Thu, 30 Sep 2021 10:33:32 -0700 Subject: [PATCH 19/24] Fixes to unit tests after merge Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp | 2 +- .../Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp index 56b8190719..22bc7a494c 100644 --- a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp @@ -27,7 +27,7 @@ namespace AzToolsFramework // Calling ClearFocusRoot restores the default focus root (which is an invalid EntityId). m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId); - EXPECT_EQ(m_focusModeInterface->GetFocusRoot(editorEntityContextId), AZ::EntityId()); + EXPECT_EQ(m_focusModeInterface->GetFocusRoot(m_editorEntityContextId), AZ::EntityId()); } TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_AncestorsDescendants) diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp index fa431e79ec..14eee84b7d 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp @@ -173,7 +173,7 @@ namespace UnitTest EXPECT_EQ( m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), rootPrefabInstance->get().GetTemplateId()); - auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance(); + auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance(m_editorEntityContextId); EXPECT_TRUE(instance.has_value()); EXPECT_EQ(&instance->get(), &rootPrefabInstance->get()); } From aa8594b49101360f922642ec723394fb3afaa488 Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Thu, 30 Sep 2021 11:19:23 -0700 Subject: [PATCH 20/24] Add a unit test for `XcbInputDeviceKeyboard` (#4393) * Use XcbUniquePtr to manage xcb resources Signed-off-by: Chris Burel * Add a unit test for XcbInputDeviceKeyboard Signed-off-by: Chris Burel --- Code/Framework/AzFramework/CMakeLists.txt | 3 + .../Common/Xcb/AzFramework/XcbApplication.cpp | 18 +-- Code/Framework/AzFramework/Tests/Main.cpp | 9 ++ .../platform_specific_test_targets.cmake | 7 + .../Tests/Platform/Common/Xcb/Actions.h | 27 ++++ .../Tests/Platform/Common/Xcb/Main.cpp | 9 ++ .../Platform/Common/Xcb/MockXcbInterface.cpp | 81 ++++++++++ .../Platform/Common/Xcb/MockXcbInterface.h | 81 ++++++++++ .../Tests/Platform/Common/Xcb/Printers.cpp | 33 ++++ .../Tests/Platform/Common/Xcb/Printers.h | 17 ++ .../Xcb/XcbInputDeviceKeyboardTests.cpp | 145 ++++++++++++++++++ .../Xcb/azframework_xcb_tests_files.cmake | 17 ++ .../platform_specific_test_targets.cmake | 33 ++++ .../Mac/platform_specific_test_targets.cmake | 7 + .../platform_specific_test_targets.cmake | 7 + .../iOS/platform_specific_test_targets.cmake | 7 + .../Tests/frameworktests_files.cmake | 2 +- 17 files changed, 492 insertions(+), 11 deletions(-) create mode 100644 Code/Framework/AzFramework/Tests/Main.cpp create mode 100644 Code/Framework/AzFramework/Tests/Platform/Android/platform_specific_test_targets.cmake create mode 100644 Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h create mode 100644 Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Main.cpp create mode 100644 Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp create mode 100644 Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h create mode 100644 Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.cpp create mode 100644 Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.h create mode 100644 Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp create mode 100644 Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake create mode 100644 Code/Framework/AzFramework/Tests/Platform/Linux/platform_specific_test_targets.cmake create mode 100644 Code/Framework/AzFramework/Tests/Platform/Mac/platform_specific_test_targets.cmake create mode 100644 Code/Framework/AzFramework/Tests/Platform/Windows/platform_specific_test_targets.cmake create mode 100644 Code/Framework/AzFramework/Tests/Platform/iOS/platform_specific_test_targets.cmake diff --git a/Code/Framework/AzFramework/CMakeLists.txt b/Code/Framework/AzFramework/CMakeLists.txt index f6851f0ab8..59393d90a1 100644 --- a/Code/Framework/AzFramework/CMakeLists.txt +++ b/Code/Framework/AzFramework/CMakeLists.txt @@ -76,6 +76,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAMESPACE AZ FILES_CMAKE Tests/frameworktests_files.cmake + ${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake INCLUDE_DIRECTORIES PRIVATE Tests @@ -93,6 +94,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAME AZ::AzFramework.Tests ) + include(${pal_dir}/platform_specific_test_targets.cmake) + endif() endif() diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp index 7cbd9ae0e2..f57f4a89ac 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp @@ -8,6 +8,7 @@ #include #include +#include namespace AzFramework { @@ -17,8 +18,8 @@ namespace AzFramework { public: XcbConnectionManagerImpl() + : m_xcbConnection(xcb_connect(nullptr, nullptr)) { - m_xcbConnection = xcb_connect(nullptr, nullptr); AZ_Error("Application", m_xcbConnection != nullptr, "Unable to connect to X11 Server."); XcbConnectionManagerBus::Handler::BusConnect(); } @@ -26,16 +27,15 @@ namespace AzFramework ~XcbConnectionManagerImpl() override { XcbConnectionManagerBus::Handler::BusDisconnect(); - xcb_disconnect(m_xcbConnection); } xcb_connection_t* GetXcbConnection() const override { - return m_xcbConnection; + return m_xcbConnection.get(); } private: - xcb_connection_t* m_xcbConnection = nullptr; + XcbUniquePtr m_xcbConnection = nullptr; }; //////////////////////////////////////////////////////////////////////////////////////////////// @@ -65,10 +65,9 @@ namespace AzFramework { if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection()) { - if (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection)) + if (auto event = XcbStdFreePtr{xcb_poll_for_event(xcbConnection)}) { - XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event); - free(event); + XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event.get()); } } } @@ -78,10 +77,9 @@ namespace AzFramework { if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection()) { - while (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection)) + while (auto event = XcbStdFreePtr{xcb_poll_for_event(xcbConnection)}) { - XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event); - free(event); + XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event.get()); } } } diff --git a/Code/Framework/AzFramework/Tests/Main.cpp b/Code/Framework/AzFramework/Tests/Main.cpp new file mode 100644 index 0000000000..c88648458a --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Main.cpp @@ -0,0 +1,9 @@ +/* + * 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 +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Code/Framework/AzFramework/Tests/Platform/Android/platform_specific_test_targets.cmake b/Code/Framework/AzFramework/Tests/Platform/Android/platform_specific_test_targets.cmake new file mode 100644 index 0000000000..7a325ca97e --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Android/platform_specific_test_targets.cmake @@ -0,0 +1,7 @@ +# +# 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 +# +# diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h new file mode 100644 index 0000000000..1650ff4f8d --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h @@ -0,0 +1,27 @@ +/* + * 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 + +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_1_VALUE_PARAMS(p0)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0 }; + return value; +} +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_2_VALUE_PARAMS(p0, p1)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0, p1 }; + return value; +} diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Main.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Main.cpp new file mode 100644 index 0000000000..c88648458a --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Main.cpp @@ -0,0 +1,9 @@ +/* + * 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 +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp new file mode 100644 index 0000000000..64c3967fdc --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp @@ -0,0 +1,81 @@ +/* + * 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 "MockXcbInterface.h" + +// The functions defined in this file will take precedence over those defined +// in the real libxcb.so, allowing the test code to use a mock implementation + +extern "C" +{ + +// ---------------------------------------------------------------------------- +// xcb +xcb_connection_t* xcb_connect(const char* displayname, int* screenp) +{ + return MockXcbInterface::Instance()->xcb_connect(displayname, screenp); +} +void xcb_disconnect(xcb_connection_t* c) +{ + return MockXcbInterface::Instance()->xcb_disconnect(c); +} +xcb_generic_event_t* xcb_poll_for_event(xcb_connection_t* c) +{ + return MockXcbInterface::Instance()->xcb_poll_for_event(c); +} + +// ---------------------------------------------------------------------------- +// xcb-xkb +xcb_xkb_use_extension_cookie_t xcb_xkb_use_extension(xcb_connection_t* c, uint16_t wantedMajor, uint16_t wantedMinor) +{ + return MockXcbInterface::Instance()->xcb_xkb_use_extension(c, wantedMajor, wantedMinor); +} +xcb_xkb_use_extension_reply_t* xcb_xkb_use_extension_reply(xcb_connection_t* c, xcb_xkb_use_extension_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_xkb_use_extension_reply(c, cookie, e); +} + +// ---------------------------------------------------------------------------- +// xkb-x11 +int32_t xkb_x11_get_core_keyboard_device_id(xcb_connection_t* connection) +{ + return MockXcbInterface::Instance()->xkb_x11_get_core_keyboard_device_id(connection); +} +struct xkb_keymap* xkb_x11_keymap_new_from_device(struct xkb_context* context, xcb_connection_t* connection, int32_t device_id, enum xkb_keymap_compile_flags flags) +{ + return MockXcbInterface::Instance()->xkb_x11_keymap_new_from_device(context, connection, device_id, flags); +} +xkb_state* xkb_x11_state_new_from_device(xkb_keymap* keymap, xcb_connection_t* connection, int32_t device_id) +{ + return MockXcbInterface::Instance()->xkb_x11_state_new_from_device(keymap, connection, device_id); +} + +// ---------------------------------------------------------------------------- +// xkbcommon +xkb_context* xkb_context_new(enum xkb_context_flags flags) +{ + return MockXcbInterface::Instance()->xkb_context_new(flags); +} +void xkb_context_unref(xkb_context *context) +{ + return MockXcbInterface::Instance()->xkb_context_unref(context); +} +void xkb_keymap_unref(xkb_keymap *keymap) +{ + return MockXcbInterface::Instance()->xkb_keymap_unref(keymap); +} +void xkb_state_unref(xkb_state *state) +{ + return MockXcbInterface::Instance()->xkb_state_unref(state); +} +xkb_keysym_t xkb_state_key_get_one_sym(xkb_state *state, xkb_keycode_t key) +{ + return MockXcbInterface::Instance()->xkb_state_key_get_one_sym(state, key); +} + +} diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h new file mode 100644 index 0000000000..ecda005830 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h @@ -0,0 +1,81 @@ +/* + * 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 + +#define explicit ExplicitIsACXXKeyword +#include +#undef explicit +#include + +#include "Printers.h" + +// xcb / xkb do not provide definitions of these structs, but the tests need some concrete value for them. +struct xcb_connection_t +{ +}; + +struct xkb_context +{ +}; + +struct xkb_keymap +{ +}; + +struct xkb_state +{ +}; + +class MockXcbInterface +{ +public: + MockXcbInterface() + { + self = this; + } + MockXcbInterface(const MockXcbInterface&) = delete; + MockXcbInterface(MockXcbInterface&&) = delete; + MockXcbInterface& operator=(const MockXcbInterface&) = delete; + MockXcbInterface& operator=(MockXcbInterface&&) = delete; + ~MockXcbInterface() + { + self = nullptr; + } + + static MockXcbInterface* Instance() { return self; } + + // xcb + MOCK_CONST_METHOD2(xcb_connect, xcb_connection_t*(const char* displayname, int* screenp)); + 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)); + + // 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)); + MOCK_CONST_METHOD3(xcb_xkb_use_extension_reply, xcb_xkb_use_extension_reply_t*(xcb_connection_t* c, xcb_xkb_use_extension_cookie_t cookie, xcb_generic_error_t** e)); + + // xkb-x11 + MOCK_CONST_METHOD1(xkb_x11_get_core_keyboard_device_id, int32_t(xcb_connection_t* connection)); + MOCK_CONST_METHOD4(xkb_x11_keymap_new_from_device, xkb_keymap*(xkb_context* context, xcb_connection_t* connection, int32_t device_id, xkb_keymap_compile_flags flags)); + MOCK_CONST_METHOD3(xkb_x11_state_new_from_device, xkb_state*(xkb_keymap* keymap, xcb_connection_t* connection, int32_t device_id)); + + // xkbcommon + MOCK_CONST_METHOD1(xkb_context_new, xkb_context*(xkb_context_flags flags)); + MOCK_CONST_METHOD1(xkb_context_unref, void(xkb_context* context)); + MOCK_CONST_METHOD1(xkb_keymap_unref, void(xkb_keymap* keymap)); + MOCK_CONST_METHOD1(xkb_state_unref, void(xkb_state* state)); + MOCK_CONST_METHOD2(xkb_state_key_get_one_sym, xkb_keysym_t(xkb_state *state, xkb_keycode_t key)); + +private: + static inline MockXcbInterface* self = nullptr; +}; diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.cpp new file mode 100644 index 0000000000..9de17bd776 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.cpp @@ -0,0 +1,33 @@ +/* + * 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 "Printers.h" +#include +#include + +namespace AzFramework +{ + void PrintTo(const InputChannel::State& state, std::ostream* os) + { + switch(state) + { + case InputChannel::State::Began: + *os << "Began"; + break; + case InputChannel::State::Ended: + *os << "Ended"; + break; + case InputChannel::State::Idle: + *os << "Idle"; + break; + case InputChannel::State::Updated: + *os << "Updated"; + break; + } + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.h new file mode 100644 index 0000000000..0659ec6eb1 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Printers.h @@ -0,0 +1,17 @@ +/* + * 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 AzFramework +{ + void PrintTo(const InputChannel::State& state, std::ostream* os); +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp new file mode 100644 index 0000000000..1b494c9525 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp @@ -0,0 +1,145 @@ +/* + * 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 "MockXcbInterface.h" +#include "Actions.h" + +template +xcb_generic_event_t MakeEvent(T event) +{ + return *reinterpret_cast(&event); +} + +namespace AzFramework +{ + TEST(XcbInputDeviceKeyboard, InputChannelsUpdateStateFromXcbEvents) + { + using testing::Return; + using testing::Eq; + using testing::_; + MockXcbInterface interface; + + xcb_connection_t connection{}; + xkb_context xkbContext{}; + xkb_keymap xkbKeymap{}; + xkb_state xkbState{}; + const int32_t coreDeviceId{1}; + + constexpr xcb_keycode_t keycodeForAKey = 38; + + const AZStd::array events + { + MakeEvent(xcb_key_press_event_t{ + /*.response_type = */ XCB_KEY_PRESS, + /*.detail = */ keycodeForAKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + MakeEvent(xcb_key_release_event_t{ + /*.response_type = */ XCB_KEY_RELEASE, + /*.detail = */ keycodeForAKey, + /*.sequence = */ 0, + /*.time = */ 0, + /*.root = */ 0, + /*.event = */ 0, + /*.child = */ 0, + /*.root_x = */ 0, + /*.root_y = */ 0, + /*.event_x = */ 0, + /*.event_y = */ 0, + /*.state = */ 0, + /*.same_screen = */ 0, + /*.pad0 = */ 0 + }), + }; + + EXPECT_CALL(interface, xcb_connect(_, _)) + .WillOnce(Return(&connection)); + EXPECT_CALL(interface, xcb_disconnect(&connection)) + .Times(1); + + EXPECT_CALL(interface, xkb_context_new(XKB_CONTEXT_NO_FLAGS)) + .WillOnce(Return(&xkbContext)); + EXPECT_CALL(interface, xkb_context_unref(&xkbContext)) + .Times(1); + + EXPECT_CALL(interface, xkb_x11_keymap_new_from_device(&xkbContext, &connection, coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS)) + .WillOnce(Return(&xkbKeymap)); + EXPECT_CALL(interface, xkb_keymap_unref(&xkbKeymap)) + .Times(1); + + EXPECT_CALL(interface, xkb_x11_state_new_from_device(&xkbKeymap, &connection, coreDeviceId)) + .WillOnce(Return(&xkbState)); + EXPECT_CALL(interface, xkb_state_unref(&xkbState)) + .Times(1); + + EXPECT_CALL(interface, xcb_xkb_use_extension(&connection, 1, 0)); + EXPECT_CALL(interface, xcb_xkb_use_extension_reply(&connection, _, _)) + .WillOnce(ReturnMalloc( + /* .response_type =*/static_cast(XCB_XKB_USE_EXTENSION), + /* .supported =*/ static_cast(1)) + ); + EXPECT_CALL(interface, xkb_x11_get_core_keyboard_device_id(&connection)) + .WillRepeatedly(Return(coreDeviceId)); + + // 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 we malloc new copies + // here + EXPECT_CALL(interface, xcb_poll_for_event(&connection)) + .WillOnce(ReturnMalloc(events[0])) + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(events[1])) + .WillOnce(Return(nullptr)) + ; + + EXPECT_CALL(interface, xkb_state_key_get_one_sym(&xkbState, keycodeForAKey)) + .WillOnce(Return(XKB_KEY_a)) + .WillOnce(Return(XKB_KEY_a)) + ; + + Application application; + application.Start({}, {}); + + const InputChannel* inputChannel = InputChannelRequests::FindInputChannel(InputDeviceKeyboard::Key::AlphanumericA); + ASSERT_TRUE(inputChannel); + EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Idle)); + + application.PumpSystemEventLoopUntilEmpty(); + application.TickSystem(); + application.Tick(); + + EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Began)); + + application.PumpSystemEventLoopUntilEmpty(); + application.TickSystem(); + application.Tick(); + + EXPECT_THAT(inputChannel->GetState(), Eq(InputChannel::State::Ended)); + + application.Stop(); + } +} // 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 new file mode 100644 index 0000000000..aebd28222b --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake @@ -0,0 +1,17 @@ +# +# 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 + Actions.h + Main.cpp + MockXcbInterface.cpp + MockXcbInterface.h + Printers.cpp + Printers.h + XcbInputDeviceKeyboardTests.cpp +) diff --git a/Code/Framework/AzFramework/Tests/Platform/Linux/platform_specific_test_targets.cmake b/Code/Framework/AzFramework/Tests/Platform/Linux/platform_specific_test_targets.cmake new file mode 100644 index 0000000000..e443831462 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Linux/platform_specific_test_targets.cmake @@ -0,0 +1,33 @@ +# +# 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 (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb") + # This library defines local implementations of all the used xcb functions, + # in order to allow tests to run in the absence of a running X server. + # These have to be in a separate library, so that the normal AzFramework + # tests do not need to set up a mock Xcb interface. + ly_add_target( + NAME AzFramework.Xcb.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE AZ + FILES_CMAKE + Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + ${pal_dir} + BUILD_DEPENDENCIES + PRIVATE + AZ::AzFramework + AZ::AzTest + AZ::AzTestShared + AZ::AzFrameworkTestShared + ) + ly_add_googletest( + NAME AZ::AzFramework.Xcb.Tests + ) +endif() diff --git a/Code/Framework/AzFramework/Tests/Platform/Mac/platform_specific_test_targets.cmake b/Code/Framework/AzFramework/Tests/Platform/Mac/platform_specific_test_targets.cmake new file mode 100644 index 0000000000..7a325ca97e --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Mac/platform_specific_test_targets.cmake @@ -0,0 +1,7 @@ +# +# 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 +# +# diff --git a/Code/Framework/AzFramework/Tests/Platform/Windows/platform_specific_test_targets.cmake b/Code/Framework/AzFramework/Tests/Platform/Windows/platform_specific_test_targets.cmake new file mode 100644 index 0000000000..7a325ca97e --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Windows/platform_specific_test_targets.cmake @@ -0,0 +1,7 @@ +# +# 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 +# +# diff --git a/Code/Framework/AzFramework/Tests/Platform/iOS/platform_specific_test_targets.cmake b/Code/Framework/AzFramework/Tests/Platform/iOS/platform_specific_test_targets.cmake new file mode 100644 index 0000000000..7a325ca97e --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/iOS/platform_specific_test_targets.cmake @@ -0,0 +1,7 @@ +# +# 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 +# +# diff --git a/Code/Framework/AzFramework/Tests/frameworktests_files.cmake b/Code/Framework/AzFramework/Tests/frameworktests_files.cmake index 680155d49d..6c4f611352 100644 --- a/Code/Framework/AzFramework/Tests/frameworktests_files.cmake +++ b/Code/Framework/AzFramework/Tests/frameworktests_files.cmake @@ -7,7 +7,7 @@ # set(FILES - ../../AzCore/Tests/Main.cpp + Main.cpp Spawnable/SpawnableEntitiesInterfaceTests.cpp Spawnable/SpawnableEntitiesManagerTests.cpp ArchiveCompressionTests.cpp From 5fe4d8043d70a23f55d081dcfd4097d45ebcec95 Mon Sep 17 00:00:00 2001 From: chcurran <82187351+carlitosan@users.noreply.github.com> Date: Thu, 30 Sep 2021 12:55:41 -0700 Subject: [PATCH 21/24] set ed_useNewAssetBrowserTableView to false by default Signed-off-by: chcurran <82187351+carlitosan@users.noreply.github.com> --- .../AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp index c6772ea2d7..acf935e6dc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.cpp @@ -20,7 +20,7 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") AZ_POP_DISABLE_WARNING AZ_CVAR( - bool, ed_useNewAssetBrowserTableView, true, nullptr, AZ::ConsoleFunctorFlags::Null, + bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Use the new AssetBrowser TableView for searching assets."); namespace AzToolsFramework { From 60a0d2ba01d3e04d090ae2ce8add30b13df2cc36 Mon Sep 17 00:00:00 2001 From: mrieggeramzn <61609885+mrieggeramzn@users.noreply.github.com> Date: Thu, 30 Sep 2021 15:34:12 -0700 Subject: [PATCH 22/24] Atom/mriegger/decalnormalmaps (#4302) * Adding normal maps to decals initial commit Signed-off-by: mrieggeramzn * Adding checks for normal maps not present Signed-off-by: mrieggeramzn * Readding the proper switch statement Signed-off-by: mrieggeramzn * Addressing PR feedback, adding brackets Signed-off-by: mrieggeramzn * Adding comments Signed-off-by: mrieggeramzn * fixing compile issue Signed-off-by: mrieggeramzn --- .../ShaderLib/Atom/Features/PBR/Decals.azsli | 31 ++-- .../ShaderResourceGroups/Decals/ViewSrg.azsli | 18 ++- .../Code/Source/Decals/DecalTextureArray.cpp | 137 ++++++++++++------ .../Code/Source/Decals/DecalTextureArray.h | 37 +++-- .../DecalTextureArrayFeatureProcessor.cpp | 36 ++++- .../DecalTextureArrayFeatureProcessor.h | 3 +- .../Tests/Decals/DecalTextureArrayTests.cpp | 2 +- 7 files changed, 181 insertions(+), 83 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli index 1bd8e0b9a5..5e7088617b 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli @@ -16,6 +16,7 @@ #include #include #include +#include void ApplyDecal(uint currDecalIndex, inout Surface surface); @@ -47,9 +48,10 @@ void ApplyDecal(uint currDecalIndex, inout Surface surface) ViewSrg::Decal decal = ViewSrg::m_decals[currDecalIndex]; float3x3 decalRot = MatrixFromQuaternion(decal.m_quaternion); - + decalRot = transpose(decalRot); + float3 localPos = surface.position - decal.m_position; - localPos = mul(localPos, decalRot); + localPos = mul(decalRot, localPos); float3 decalUVW = localPos * rcp(decal.m_halfSize); if(decalUVW.x >= -1.0f && decalUVW.x <= 1.0f && @@ -63,30 +65,39 @@ void ApplyDecal(uint currDecalIndex, inout Surface surface) decalUVW.y *= -1; float3 decalUV = float3(decalUVW.xy * 0.5f + 0.5f, textureIndex); - + float3 decalSample; float4 baseMap = 0; + float2 normalMap = 0; switch(textureArrayIndex) { case 0: - baseMap = ViewSrg::m_decalTextureArray0.Sample(PassSrg::LinearSampler, decalUV); + baseMap = ViewSrg::m_decalTextureArrayDiffuse0.Sample(PassSrg::LinearSampler, decalUV); + normalMap = ViewSrg::m_decalTextureArrayNormalMaps0.Sample(PassSrg::LinearSampler, decalUV); break; case 1: - baseMap = ViewSrg::m_decalTextureArray1.Sample(PassSrg::LinearSampler, decalUV); + baseMap = ViewSrg::m_decalTextureArrayDiffuse1.Sample(PassSrg::LinearSampler, decalUV); + normalMap = ViewSrg::m_decalTextureArrayNormalMaps1.Sample(PassSrg::LinearSampler, decalUV); break; case 2: - baseMap = ViewSrg::m_decalTextureArray2.Sample(PassSrg::LinearSampler, decalUV); + baseMap = ViewSrg::m_decalTextureArrayDiffuse2.Sample(PassSrg::LinearSampler, decalUV); + normalMap = ViewSrg::m_decalTextureArrayNormalMaps2.Sample(PassSrg::LinearSampler, decalUV); break; case 3: - baseMap = ViewSrg::m_decalTextureArray3.Sample(PassSrg::LinearSampler, decalUV); + baseMap = ViewSrg::m_decalTextureArrayDiffuse3.Sample(PassSrg::LinearSampler, decalUV); + normalMap = ViewSrg::m_decalTextureArrayNormalMaps3.Sample(PassSrg::LinearSampler, decalUV); break; case 4: - baseMap = ViewSrg::m_decalTextureArray4.Sample(PassSrg::LinearSampler, decalUV); - break; + baseMap = ViewSrg::m_decalTextureArrayDiffuse4.Sample(PassSrg::LinearSampler, decalUV); + normalMap = ViewSrg::m_decalTextureArrayNormalMaps4.Sample(PassSrg::LinearSampler, decalUV); + break; } float opacity = baseMap.a * decal.m_opacity * GetDecalAttenuation(surface.normal, decalRot[2], decal.m_angleAttenuation); - surface.albedo = lerp(surface.albedo, baseMap.rgb, opacity); + surface.albedo = lerp(surface.albedo, baseMap.rgb, opacity); + + float3 normalMapWS = GetWorldSpaceNormal(normalMap, decalRot[2], decalRot[0], decalRot[1], 1.0f); + surface.normal = normalize(lerp(surface.normal, normalMapWS, opacity)); } } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/Decals/ViewSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/Decals/ViewSrg.azsli index 16b23432e1..260614b0f7 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/Decals/ViewSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/Decals/ViewSrg.azsli @@ -31,12 +31,18 @@ partial ShaderResourceGroup ViewSrg // e.g. m_decalTextureArray0 might store 24 textures @128x128, // m_decalTextureArray1 might store 16 * 256x256 // and m_decalTextureArray2 might store 4 @ 512x512 - - Texture2DArray m_decalTextureArray0; - Texture2DArray m_decalTextureArray1; - Texture2DArray m_decalTextureArray2; - Texture2DArray m_decalTextureArray3; - Texture2DArray m_decalTextureArray4; + // This must match the variable NumTextureArrays in DecalTextureArrayFeatureProcessor.h + Texture2DArray m_decalTextureArrayDiffuse0; + Texture2DArray m_decalTextureArrayDiffuse1; + Texture2DArray m_decalTextureArrayDiffuse2; + Texture2DArray m_decalTextureArrayDiffuse3; + Texture2DArray m_decalTextureArrayDiffuse4; + + Texture2DArray m_decalTextureArrayNormalMaps0; + Texture2DArray m_decalTextureArrayNormalMaps1; + Texture2DArray m_decalTextureArrayNormalMaps2; + Texture2DArray m_decalTextureArrayNormalMaps3; + Texture2DArray m_decalTextureArrayNormalMaps4; uint m_decalCount; } diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.cpp index ebdc884ecf..87ac0a9679 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.cpp @@ -5,7 +5,6 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ - #include "DecalTextureArray.h" #include #include @@ -24,7 +23,16 @@ namespace AZ { namespace { - static const char* BaseColorTextureMapName = "baseColor.textureMap"; + static AZ::Name GetMapName(const DecalMapType mapType) + { + // Using local static to avoid cost of creating AZ::Name. Also so that this can be called from other static functions + static AZStd::array mapNames = + { + AZ::Name("baseColor.textureMap"), + AZ::Name("normal.textureMap") + }; + return mapNames[mapType]; + } static AZ::Data::AssetId GetImagePoolId() { @@ -40,6 +48,7 @@ namespace AZ return asset; } + // Extract exactly which texture asset we need to load from the given material and map type (diffuse, normal, etc). static AZ::Data::Asset GetStreamingImageAsset(const AZ::RPI::MaterialAsset& materialAsset, const AZ::Name& propertyName) { if (!materialAsset.IsReady()) @@ -78,11 +87,6 @@ namespace AZ const AZ::RPI::MaterialAsset* materialAsset = materialAssetData.GetAs(); return GetStreamingImageAsset(*materialAsset, propertyName); } - - AZ::Data::Asset GetBaseColorImageAsset(const AZ::Data::Asset materialAssetData) - { - return GetStreamingImageAsset(materialAssetData, AZ::Name(BaseColorTextureMapName)); - } } int DecalTextureArray::FindMaterial(const AZ::Data::AssetId materialAssetId) const @@ -103,7 +107,7 @@ namespace AZ { AZ_Error("DecalTextureArray", FindMaterial(materialAssetId) == -1, "Adding material when it already exists in the array"); // Invalidate the existing texture array, as we need to repack it taking into account the new material. - m_textureArrayPacked = nullptr; + AZStd::fill(m_textureArrayPacked.begin(), m_textureArrayPacked.end(), nullptr); MaterialData materialData; materialData.m_materialAssetId = materialAssetId; @@ -122,42 +126,42 @@ namespace AZ return m_materials[index].m_materialAssetId; } - RHI::Size DecalTextureArray::GetImageDimensions() const + RHI::Size DecalTextureArray::GetImageDimensions(const DecalMapType mapType) const { AZ_Assert(m_materials.size() > 0, "GetImageDimensions() cannot be called until at least one material has been added"); const int iter = m_materials.begin(); // All textures in a texture array must have the same size, so just pick the first const MaterialData& firstMaterial = m_materials[iter]; - const auto& baseColorAsset = GetBaseColorImageAsset(firstMaterial.m_materialAssetData); + const auto& baseColorAsset = GetStreamingImageAsset(firstMaterial.m_materialAssetData, GetMapName(mapType)); return baseColorAsset->GetImageDescriptor().m_size; } - const AZ::Data::Instance& DecalTextureArray::GetPackedTexture() const + const AZ::Data::Instance& DecalTextureArray::GetPackedTexture(const DecalMapType mapType) const { - return m_textureArrayPacked; + return m_textureArrayPacked[mapType]; } bool DecalTextureArray::IsValidDecalMaterial(const AZ::RPI::MaterialAsset& materialAsset) { - return GetStreamingImageAsset(materialAsset, AZ::Name(BaseColorTextureMapName)).IsReady(); + return GetStreamingImageAsset(materialAsset, GetMapName(DecalMapType_Diffuse)).IsReady(); } - AZ::Data::Asset DecalTextureArray::BuildPackedMipChainAsset(const size_t numTexturesToCreate) + AZ::Data::Asset DecalTextureArray::BuildPackedMipChainAsset(const DecalMapType mapType, const size_t numTexturesToCreate) { RPI::ImageMipChainAssetCreator assetCreator; - const uint32_t mipLevels = GetNumMipLevels(); + const uint32_t mipLevels = GetNumMipLevels(mapType); - assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), static_cast(mipLevels), aznumeric_cast(numTexturesToCreate)); + assetCreator.Begin(Data::AssetId(AZ::Uuid::CreateRandom()), aznumeric_cast(mipLevels), aznumeric_cast(numTexturesToCreate)); for (uint32_t mipLevel = 0; mipLevel < mipLevels; ++mipLevel) { - const auto& layout = GetLayout(mipLevel); + const auto& layout = GetLayout(mapType, mipLevel); assetCreator.BeginMip(layout); for (int i = 0; i < m_materials.array_size(); ++i) { - const auto rawData = GetRawImageData(i, mipLevel); - assetCreator.AddSubImage(rawData.data(), rawData.size()); + const auto imageData = GetRawImageData(GetMapName(mapType), i, mipLevel); + assetCreator.AddSubImage(imageData.data(), imageData.size()); } assetCreator.EndMip(); @@ -169,10 +173,12 @@ namespace AZ return AZStd::move(asset); } - RHI::ImageDescriptor DecalTextureArray::CreatePackedImageDescriptor(const uint16_t arraySize, const uint16_t mipLevels) const + RHI::ImageDescriptor DecalTextureArray::CreatePackedImageDescriptor( + const DecalMapType mapType, const uint16_t arraySize, const uint16_t mipLevels) const { - const RHI::Size imageDimensions = GetImageDimensions(); - RHI::ImageDescriptor imageDescriptor = RHI::ImageDescriptor::Create2DArray(RHI::ImageBindFlags::ShaderRead, imageDimensions.m_width, imageDimensions.m_height, arraySize, GetFormat()); + const RHI::Size imageDimensions = GetImageDimensions(mapType); + RHI::ImageDescriptor imageDescriptor = RHI::ImageDescriptor::Create2DArray( + RHI::ImageBindFlags::ShaderRead, imageDimensions.m_width, imageDimensions.m_height, arraySize, GetFormat(mapType)); imageDescriptor.m_mipLevels = mipLevels; return imageDescriptor; } @@ -189,21 +195,34 @@ namespace AZ } const size_t numTexturesToCreate = m_materials.array_size(); - const auto mipChainAsset = BuildPackedMipChainAsset(numTexturesToCreate); - RHI::ImageViewDescriptor imageViewDescriptor; - imageViewDescriptor.m_isArray = true; + for (int i = 0; i < DecalMapType_Num; ++i) + { + const DecalMapType mapType = aznumeric_cast(i); + if (!AreAllTextureMapsPresent(mapType)) + { + AZ_Warning("DecalTextureArray", true, "Missing decal texture maps for %s. Please make sure all maps of this type are present.\n", GetMapName(mapType).GetCStr()); + m_textureArrayPacked[i] = nullptr; + continue; + } - RPI::StreamingImageAssetCreator assetCreator; - assetCreator.Begin(Data::AssetId(Uuid::CreateRandom())); - assetCreator.SetPoolAssetId(GetImagePoolId()); - assetCreator.SetFlags(RPI::StreamingImageFlags::None); - assetCreator.SetImageDescriptor(CreatePackedImageDescriptor(aznumeric_cast(numTexturesToCreate), GetNumMipLevels())); - assetCreator.SetImageViewDescriptor(imageViewDescriptor); - assetCreator.AddMipChainAsset(*mipChainAsset); - Data::Asset packedAsset; - const bool createdOk = assetCreator.End(packedAsset); - AZ_Error("TextureArrayData", createdOk, "Pack() call failed."); - m_textureArrayPacked = createdOk ? RPI::StreamingImage::FindOrCreate(packedAsset) : nullptr; + const auto mipChainAsset = BuildPackedMipChainAsset(mapType, numTexturesToCreate); + RHI::ImageViewDescriptor imageViewDescriptor; + imageViewDescriptor.m_isArray = true; + + RPI::StreamingImageAssetCreator assetCreator; + assetCreator.Begin(Data::AssetId(Uuid::CreateRandom())); + assetCreator.SetPoolAssetId(GetImagePoolId()); + assetCreator.SetFlags(RPI::StreamingImageFlags::None); + assetCreator.SetImageDescriptor( + CreatePackedImageDescriptor(mapType, aznumeric_cast(numTexturesToCreate), GetNumMipLevels(mapType))); + assetCreator.SetImageViewDescriptor(imageViewDescriptor); + assetCreator.AddMipChainAsset(*mipChainAsset); + Data::Asset packedAsset; + const bool createdOk = assetCreator.End(packedAsset); + AZ_Error("TextureArrayData", createdOk, "Pack() call failed."); + m_textureArrayPacked[i] = createdOk ? RPI::StreamingImage::FindOrCreate(packedAsset) : nullptr; + + } // Free unused memory ClearAssets(); @@ -225,29 +244,30 @@ namespace AZ } } - uint16_t DecalTextureArray::GetNumMipLevels() const + uint16_t DecalTextureArray::GetNumMipLevels(const DecalMapType mapType) const { AZ_Assert(m_materials.size() > 0, "GetNumMipLevels() cannot be called until at least one material has been added"); // All decals in a texture array must have the same number of mips, so just pick the first const int iter = m_materials.begin(); const MaterialData& firstMaterial = m_materials[iter]; - const auto& baseColorAsset = GetBaseColorImageAsset(firstMaterial.m_materialAssetData); - return baseColorAsset->GetImageDescriptor().m_mipLevels; + const auto& imageAsset = GetStreamingImageAsset(firstMaterial.m_materialAssetData, GetMapName(mapType)); + return imageAsset->GetImageDescriptor().m_mipLevels; } - RHI::ImageSubresourceLayout DecalTextureArray::GetLayout(int mip) const + RHI::ImageSubresourceLayout DecalTextureArray::GetLayout(const DecalMapType mapType, int mip) const { AZ_Assert(m_materials.size() > 0, "GetLayout() cannot be called unless at least one material has been added"); const int iter = m_materials.begin(); - const auto& descriptor = GetBaseColorImageAsset(m_materials[iter].m_materialAssetData)->GetImageDescriptor(); + const auto& descriptor = + GetStreamingImageAsset(m_materials[iter].m_materialAssetData, GetMapName(mapType))->GetImageDescriptor(); RHI::Size mipSize = descriptor.m_size; mipSize.m_width >>= mip; mipSize.m_height >>= mip; return AZ::RHI::GetImageSubresourceLayout(mipSize, descriptor.m_format); } - AZStd::array_view DecalTextureArray::GetRawImageData(int arrayLevel, const int mip) const + AZStd::array_view DecalTextureArray::GetRawImageData(const AZ::Name& mapName, int arrayLevel, const int mip) const { // We always want to provide valid data to the AssetCreator for each texture. // If this spot in the array is empty, just provide some random image as filler. @@ -257,17 +277,20 @@ namespace AZ { arrayLevel = m_materials.begin(); } - - const auto image = GetBaseColorImageAsset(m_materials[arrayLevel].m_materialAssetData); + const auto image = GetStreamingImageAsset(m_materials[arrayLevel].m_materialAssetData, mapName); + if (!image) + { + return {}; + } const auto srcData = image->GetSubImageData(mip, 0); return srcData; } - AZ::RHI::Format DecalTextureArray::GetFormat() const + AZ::RHI::Format DecalTextureArray::GetFormat(const DecalMapType mapType) const { AZ_Assert(m_materials.size() > 0, "GetFormat() can only be called after at least one material has been added."); const int iter = m_materials.begin(); - const auto& baseColorAsset = GetBaseColorImageAsset(m_materials[iter].m_materialAssetData); + const auto& baseColorAsset = GetStreamingImageAsset(m_materials[iter].m_materialAssetData, GetMapName(mapType)); return baseColorAsset->GetImageDescriptor().m_format; } @@ -290,6 +313,25 @@ namespace AZ return id.IsValid() && materialData.m_materialAssetData.IsReady(); } + bool DecalTextureArray::AreAllTextureMapsPresent(const DecalMapType mapType) const + { + int iter = m_materials.begin(); + while (iter != -1) + { + if (!IsTextureMapPresentInMaterial(m_materials[iter], mapType)) + { + return false; + } + iter = m_materials.next(iter); + } + return true; + } + + bool DecalTextureArray::IsTextureMapPresentInMaterial(const MaterialData& materialData, const DecalMapType mapType) const + { + return GetStreamingImageAsset(materialData.m_materialAssetData, GetMapName(mapType)).IsReady(); + } + void DecalTextureArray::ClearAssets() { int iter = m_materials.begin(); @@ -330,7 +372,8 @@ namespace AZ if (m_materials.size() == 0) return false; - return m_textureArrayPacked == nullptr; + // We pack all diffuse/normal/etc in one go, so just check to see if the diffusemaps need packing + return m_textureArrayPacked[DecalMapType_Diffuse] == nullptr; } } diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.h b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.h index eb0f825e08..97bd8b9cbe 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.h +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArray.h @@ -28,8 +28,18 @@ namespace AZ namespace Render { + enum DecalMapType : uint32_t + { + DecalMapType_Diffuse, + DecalMapType_Normal, + DecalMapType_Num + }; + //! Helper class used by DecalTextureArrayFeatureProcessor. //! Given a set of images (all with the same dimensions and format), it can pack them together into a single textureArray that can be sent to the GPU. + //! Note that once textures are packed, this class will release any material references + //! This might free memory if nothing else is holding onto them + //! The class DOES keep note of which material asset ids were added, so it can load them again if necessary if the whole thing needs to be repacked class DecalTextureArray : public Data::AssetBus::MultiHandler { public: @@ -40,8 +50,12 @@ namespace AZ AZ::Data::AssetId GetMaterialAssetId(const int index) const; + // Packs all the added materials into one texture array per DecalMapType. void Pack(); - const Data::Instance& GetPackedTexture() const; + + // Note that we pack each type into a separate texture array. This is because formats are + // often different (BC5 for normals, BC7 for diffuse, etc) + const Data::Instance& GetPackedTexture(const DecalMapType mapType) const; static bool IsValidDecalMaterial(const RPI::MaterialAsset& materialAsset); @@ -56,22 +70,25 @@ namespace AZ void OnAssetReady(Data::Asset asset) override; + // Returns the index of the material in the m_materials container. -1 if not present. int FindMaterial(const AZ::Data::AssetId materialAssetId) const; // packs the contents of the source images into a texture array readable by the GPU and returns it - AZ::Data::Asset BuildPackedMipChainAsset(const size_t numTexturesToCreate); + AZ::Data::Asset BuildPackedMipChainAsset(const DecalMapType mapType, const size_t numTexturesToCreate); + RHI::ImageDescriptor CreatePackedImageDescriptor(const DecalMapType mapType, const uint16_t arraySize, const uint16_t mipLevels) const; - RHI::ImageDescriptor CreatePackedImageDescriptor(const uint16_t arraySize, const uint16_t mipLevels) const; - - uint16_t GetNumMipLevels() const; - RHI::Size GetImageDimensions() const; - RHI::Format GetFormat() const; - RHI::ImageSubresourceLayout GetLayout(int mip) const; - AZStd::array_view GetRawImageData(int arrayLevel, int mip) const; + uint16_t GetNumMipLevels(const DecalMapType mapType) const; + RHI::Size GetImageDimensions(const DecalMapType mapType) const; + RHI::Format GetFormat(const DecalMapType mapType) const; + RHI::ImageSubresourceLayout GetLayout(const DecalMapType mapType, int mip) const; + AZStd::array_view GetRawImageData(const AZ::Name& mapName, int arrayLevel, int mip) const; bool AreAllAssetsReady() const; bool IsAssetReady(const MaterialData& materialData) const; + bool AreAllTextureMapsPresent(const DecalMapType mapType) const; + bool IsTextureMapPresentInMaterial(const MaterialData& materialData, const DecalMapType mapType) const; + void ClearAssets(); void ClearAsset(MaterialData& materialData); @@ -81,7 +98,7 @@ namespace AZ bool NeedsPacking() const; IndexableList m_materials; - Data::Instance m_textureArrayPacked; + AZStd::array, DecalMapType_Num> m_textureArrayPacked; AZStd::unordered_set m_assetsCurrentlyLoading; }; diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp index c0b8f3315a..e5bf0bd9fa 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp @@ -322,13 +322,30 @@ namespace AZ void DecalTextureArrayFeatureProcessor::CacheShaderIndices() { - for (int i = 0; i < NumTextureArrays; ++i) - { - const RHI::ShaderResourceGroupLayout* viewSrgLayout = RPI::RPISystemInterface::Get()->GetViewSrgLayout().get(); - const AZStd::string baseName = "m_decalTextureArray" + AZStd::to_string(i); + // The azsl shader should define several texture arrays such as: + // Texture2DArray m_decalTextureArrayDiffuse0; + // Texture2DArray m_decalTextureArrayDiffuse1; + // Texture2DArray m_decalTextureArrayDiffuse2; + // and + // Texture2DArray m_decalTextureArrayNormalMaps0; + // Texture2DArray m_decalTextureArrayNormalMaps1; + // Texture2DArray m_decalTextureArrayNormalMaps2; + static const AZStd::array ShaderNames = { "m_decalTextureArrayDiffuse", + "m_decalTextureArrayNormalMaps" }; - m_decalTextureArrayIndices[i] = viewSrgLayout->FindShaderInputImageIndex(Name(baseName.c_str())); - AZ_Warning("DecalTextureArrayFeatureProcessor", m_decalTextureArrayIndices[i].IsValid(), "Unable to find %s in decal shader.", baseName.c_str()); + for (int mapType = 0; mapType < DecalMapType_Num; ++mapType) + { + for (int texArrayIdx = 0; texArrayIdx < NumTextureArrays; ++texArrayIdx) + { + const RHI::ShaderResourceGroupLayout* viewSrgLayout = RPI::RPISystemInterface::Get()->GetViewSrgLayout().get(); + const AZStd::string baseName = ShaderNames[mapType] + AZStd::to_string(texArrayIdx); + + m_decalTextureArrayIndices[texArrayIdx][mapType] = viewSrgLayout->FindShaderInputImageIndex(Name(baseName.c_str())); + AZ_Warning( + "DecalTextureArrayFeatureProcessor", m_decalTextureArrayIndices[texArrayIdx][mapType].IsValid(), + "Unable to find %s in decal shader.", + baseName.c_str()); + } } } @@ -411,8 +428,11 @@ namespace AZ int iter = m_textureArrayList.begin(); while (iter != -1) { - const auto& packedTexture = m_textureArrayList[iter].second.GetPackedTexture(); - view->GetShaderResourceGroup()->SetImage(m_decalTextureArrayIndices[iter], packedTexture); + for (int mapType = 0 ; mapType < DecalMapType_Num ; ++mapType) + { + const auto& packedTexture = m_textureArrayList[iter].second.GetPackedTexture(aznumeric_cast(mapType)); + view->GetShaderResourceGroup()->SetImage(m_decalTextureArrayIndices[iter][mapType], packedTexture); + } iter = m_textureArrayList.next(iter); } } diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h index 57c5c69e0d..13a6682629 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h @@ -89,6 +89,7 @@ namespace AZ private: // Number of size and format permutations + // This number should match the number of texture arrays in Decals/ViewSrg.azsli static constexpr int NumTextureArrays = 5; static constexpr const char* FeatureProcessorName = "DecalTextureArrayFeatureProcessor"; @@ -128,7 +129,7 @@ namespace AZ // 4 textures @ 512x512 IndexableList < AZStd::pair < AZ::RHI::Size, DecalTextureArray>> m_textureArrayList; - AZStd::array m_decalTextureArrayIndices; + AZStd::array, NumTextureArrays> m_decalTextureArrayIndices; GpuBufferHandler m_decalBufferHandler; AsyncLoadTracker m_materialLoadTracker; diff --git a/Gems/Atom/Feature/Common/Code/Tests/Decals/DecalTextureArrayTests.cpp b/Gems/Atom/Feature/Common/Code/Tests/Decals/DecalTextureArrayTests.cpp index fcbcec0256..84c2b88507 100644 --- a/Gems/Atom/Feature/Common/Code/Tests/Decals/DecalTextureArrayTests.cpp +++ b/Gems/Atom/Feature/Common/Code/Tests/Decals/DecalTextureArrayTests.cpp @@ -42,7 +42,7 @@ namespace UnitTest { AZ::Render::DecalTextureArray decalTextureArray; decalTextureArray.Pack(); - auto nothing = decalTextureArray.GetPackedTexture(); + auto nothing = decalTextureArray.GetPackedTexture(AZ::Render::DecalMapType_Diffuse); EXPECT_EQ(nothing, nullptr); } From e1c49e436dab7e8ee58bd4da885736b426e201aa Mon Sep 17 00:00:00 2001 From: "rgba16f [Amazon]" <82187279+rgba16f@users.noreply.github.com> Date: Thu, 30 Sep 2021 17:45:33 -0500 Subject: [PATCH 23/24] convert atom to task graph (#4230) * Intial attempt to convert the Atom/RHI/FrameScheduler to use the new TaskGraph api Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * Avoid enqueuing work on the active task thread if the submitted task graph is waitable When submitting a task graph, supplying a wait event implies that dependent jobs must occur on threads that do not wait on the event (in the absence of work stealing). This change prevents this by adding a notion of a task thread enable/disable state, and prohibiting dependent jobs from being enqueued on waiting threads. Signed-off-by: Jeremy Ong * Convert RPI/Scene to use TaskGraph pass 1, Culling jobs remain on the old system Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * RemoveTask Graph changes from the FrameScheduler::ExecuteGroups, use old job system instead Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * Per review, removing commented out code Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * Cleanup debug code, & build fix Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * Add a cvar & interface to query whether to use jobs or task graph Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * Make TaskGraph assert if you try to wait inside a job Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * Fix TaskTest SpawnSubgraph to account for the new TaskGraphEvent assert on wait in a running task Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * 3 minor cleanups. 1) Events always store a ptr to their executor 2) Fix clang compile error 3) remove an early out. Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * Fix double group end that was causing assert/crash plus misc minor diff's with development Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * Fix deallocation failure on deactivation of the TaskGraphSystemComponent. Also make the system component account for multiple creation in Unit Tests. Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * Update with PR feedback 1) Rename UseTaskGraph to IsTaskGraphActive & update related code 2) prefer TaskExecutor::SetInstance 3) add comments and remove commented out code Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * Fix incorrect RTTI name for TaskGraphActiveInterface Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> * Move TaskGraphSystemComponent CRC calculation to a shared variable Signed-off-by: rgba16f <82187279+rgba16f@users.noreply.github.com> Co-authored-by: Jeremy Ong --- Code/Framework/AzCore/AzCore/AzCoreModule.cpp | 3 + .../AzCore/AzCore/Task/TaskExecutor.cpp | 85 ++++-- .../AzCore/AzCore/Task/TaskExecutor.h | 7 +- .../AzCore/AzCore/Task/TaskGraph.cpp | 8 +- Code/Framework/AzCore/AzCore/Task/TaskGraph.h | 15 + .../AzCore/AzCore/Task/TaskGraph.inl | 10 +- .../AzCore/Task/TaskGraphSystemComponent.cpp | 88 ++++++ .../AzCore/Task/TaskGraphSystemComponent.h | 47 +++ .../AzCore/AzCore/azcore_files.cmake | 2 + Code/Framework/AzCore/Tests/TaskTests.cpp | 84 +++++- .../AzFramework/Application/Application.cpp | 2 + .../Code/Include/Atom/RHI/FrameScheduler.h | 3 + .../RHI/Code/Source/RHI/FrameScheduler.cpp | 119 ++++++-- .../Code/Source/RHI/AsyncUploadQueue.cpp | 1 + .../RPI/Code/Include/Atom/RPI.Public/Scene.h | 19 ++ .../Atom/RPI/Code/Source/RPI.Public/Scene.cpp | 281 ++++++++++++++---- .../001_DefaultWhite.material | 4 +- .../002_BaseColorLerp.material | 4 +- 18 files changed, 652 insertions(+), 130 deletions(-) create mode 100644 Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp create mode 100644 Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.h diff --git a/Code/Framework/AzCore/AzCore/AzCoreModule.cpp b/Code/Framework/AzCore/AzCore/AzCoreModule.cpp index c60ea1bd72..ec45335f95 100644 --- a/Code/Framework/AzCore/AzCore/AzCoreModule.cpp +++ b/Code/Framework/AzCore/AzCore/AzCoreModule.cpp @@ -22,6 +22,7 @@ #include #include #include +#include namespace AZ { @@ -41,6 +42,7 @@ namespace AZ TimeSystemComponent::CreateDescriptor(), LoggerSystemComponent::CreateDescriptor(), EventSchedulerSystemComponent::CreateDescriptor(), + TaskGraphSystemComponent::CreateDescriptor(), #if !defined(AZCORE_EXCLUDE_LUA) ScriptSystemComponent::CreateDescriptor(), @@ -55,6 +57,7 @@ namespace AZ azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), + azrtti_typeid(), }; } } diff --git a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp index 2bb88fbfa2..d2d19d1359 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp +++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.cpp @@ -190,11 +190,13 @@ namespace AZ class TaskWorker { public: - void Spawn(::AZ::TaskExecutor& executor, size_t id, AZStd::semaphore& initSemaphore, bool affinitize) + static thread_local TaskWorker* t_worker; + + void Spawn(::AZ::TaskExecutor& executor, uint32_t id, AZStd::semaphore& initSemaphore, bool affinitize) { m_executor = &executor; - AZStd::string threadName = AZStd::string::format("TaskWorker %zu", id); + AZStd::string threadName = AZStd::string::format("TaskWorker %u", id); AZStd::thread_desc desc = {}; desc.m_name = threadName.c_str(); if (affinitize) @@ -205,12 +207,29 @@ namespace AZ m_thread = AZStd::thread{ [this, &initSemaphore] { + t_worker = this; initSemaphore.release(); Run(); }, &desc }; } + // Threads that wait on a graph to complete are disqualified from receiving tasks until the wait finishes + void Disable() + { + m_enabled = false; + } + + void Enable() + { + m_enabled = true; + } + + bool Enabled() const + { + return m_enabled; + } + void Join() { m_active.store(false, AZStd::memory_order_release); @@ -222,11 +241,7 @@ namespace AZ { m_queue.Enqueue(task); - if (!m_busy.exchange(true)) - { - // The worker was idle prior to enqueueing the task, release the semaphore - m_semaphore.release(); - } + m_semaphore.release(); } private: @@ -234,7 +249,6 @@ namespace AZ { while (m_active) { - m_busy = false; m_semaphore.acquire(); if (!m_active) @@ -242,8 +256,6 @@ namespace AZ return; } - m_busy = true; - Task* task = m_queue.TryDequeue(); while (task) { @@ -271,12 +283,15 @@ namespace AZ AZStd::thread m_thread; AZStd::atomic m_active; - AZStd::atomic m_busy; + AZStd::atomic m_enabled = true; AZStd::binary_semaphore m_semaphore; ::AZ::TaskExecutor* m_executor; TaskQueue m_queue; + friend class ::AZ::TaskExecutor; }; + + thread_local TaskWorker* TaskWorker::t_worker = nullptr; } // namespace Internal static EnvironmentVariable s_executor; @@ -291,13 +306,16 @@ namespace AZ return **s_executor; } - // TODO: Create the default executor as part of a component (as in TaskManagerComponent) void TaskExecutor::SetInstance(TaskExecutor* executor) { - AZ_Assert(!s_executor, "Attempting to set the global task executor more than once"); - - s_executor = AZ::Environment::CreateVariable("GlobalTaskExecutor"); - s_executor.Set(executor); + if (!executor) + { + s_executor.Reset(); + } + else if (!s_executor) // ignore any calls to set after the first (this happens in unit tests that create new system entities) + { + s_executor = AZ::Environment::CreateVariable(s_executorName, executor); + } } TaskExecutor::TaskExecutor(uint32_t threadCount) @@ -307,14 +325,12 @@ namespace AZ m_workers = reinterpret_cast(azmalloc(m_threadCount * sizeof(Internal::TaskWorker))); - bool affinitize = m_threadCount == AZStd::thread::hardware_concurrency(); - AZStd::semaphore initSemaphore; - for (size_t i = 0; i != m_threadCount; ++i) + for (uint32_t i = 0; i != m_threadCount; ++i) { new (m_workers + i) Internal::TaskWorker{}; - m_workers[i].Spawn(*this, i, initSemaphore, affinitize); + m_workers[i].Spawn(*this, i, initSemaphore, false); } for (size_t i = 0; i != m_threadCount; ++i) @@ -334,9 +350,21 @@ namespace AZ azfree(m_workers); } - void TaskExecutor::Submit(Internal::CompiledTaskGraph& graph) + Internal::TaskWorker* TaskExecutor::GetTaskWorker() + { + if (Internal::TaskWorker::t_worker && Internal::TaskWorker::t_worker->m_executor == this) + { + return Internal::TaskWorker::t_worker; + } + return nullptr; + } + + void TaskExecutor::Submit(Internal::CompiledTaskGraph& graph, TaskGraphEvent* event) { ++m_graphsRemaining; + + event->m_executor = this; // Used to validate event is not waited for inside a job + // Submit all tasks that have no inbound edges for (Internal::Task& task : graph.Tasks()) { @@ -352,11 +380,24 @@ namespace AZ // TODO: Something more sophisticated is likely needed here. // First, we are completely ignoring affinity. // Second, some heuristics on core availability will help distribute work more effectively - m_workers[++m_lastSubmission % m_threadCount].Enqueue(&task); + uint32_t nextWorker = ++m_lastSubmission % m_threadCount; + while (!m_workers[nextWorker].Enabled()) + { + // Graphs that are waiting for the completion of a task graph cannot enqueue tasks onto + // the thread issuing the wait. + nextWorker = ++m_lastSubmission % m_threadCount; + } + + m_workers[nextWorker].Enqueue(&task); } void TaskExecutor::ReleaseGraph() { --m_graphsRemaining; } + + void TaskExecutor::ReactivateTaskWorker() + { + GetTaskWorker()->Enable(); + } } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h index dc2fa5a4c8..7e1ff80902 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h +++ b/Code/Framework/AzCore/AzCore/Task/TaskExecutor.h @@ -72,14 +72,19 @@ namespace AZ explicit TaskExecutor(uint32_t threadCount = 0); ~TaskExecutor(); - void Submit(Internal::CompiledTaskGraph& graph); + // Submit a task graph for execution. Waitable task graphs cannot enqueue work on the task thread + // that is currently active + void Submit(Internal::CompiledTaskGraph& graph, TaskGraphEvent* event); void Submit(Internal::Task& task); private: friend class Internal::TaskWorker; + friend class TaskGraphEvent; + Internal::TaskWorker* GetTaskWorker(); void ReleaseGraph(); + void ReactivateTaskWorker(); Internal::TaskWorker* m_workers; uint32_t m_threadCount = 0; diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp b/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp index 3fb93903c9..f57b06890a 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.cpp @@ -14,6 +14,12 @@ namespace AZ { using Internal::CompiledTaskGraph; + void TaskGraphEvent::Wait() + { + AZ_Assert(m_executor->GetTaskWorker() == nullptr, "Waiting in a task is unsupported"); + m_semaphore.acquire(); + } + void TaskToken::PrecedesInternal(TaskToken& comesAfter) { AZ_Assert(!m_parent.m_submitted, "Cannot mutate a TaskGraph that was previously submitted."); @@ -71,7 +77,7 @@ namespace AZ m_compiledTaskGraph->m_tasks[i].Init(); } - executor.Submit(*m_compiledTaskGraph); + executor.Submit(*m_compiledTaskGraph, waitEvent); if (m_retained) { diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.h b/Code/Framework/AzCore/AzCore/Task/TaskGraph.h index 4b454c63de..9553013a4b 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.h +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.h @@ -22,10 +22,19 @@ namespace AZ namespace Internal { class CompiledTaskGraph; + class TaskWorker; } class TaskExecutor; class TaskGraph; + class TaskGraphActiveInterface + { + public: + AZ_RTTI(TaskGraphActiveInterface, "{08118074-B139-4EF9-B8FD-29F1D6DC9233}"); + + virtual bool IsTaskGraphActive() const = 0; + }; + // A TaskToken is returned each time a Task is added to the TaskGraph. TaskTokens are used to // express dependencies between tasks within the graph, and have no purpose after the graph // is submitted (simply let them go out of scope) @@ -70,9 +79,12 @@ namespace AZ private: friend class ::AZ::Internal::CompiledTaskGraph; friend class TaskGraph; + friend class TaskExecutor; + void Signal(); AZStd::binary_semaphore m_semaphore; + TaskExecutor* m_executor = nullptr; }; // The TaskGraph encapsulates a set of tasks and their interdependencies. After adding @@ -89,6 +101,9 @@ namespace AZ // Reset the state of the task graph to begin recording tasks and edges again // NOTE: Graph must be in a "settled" state (cannot be in-flight) void Reset(); + + // Returns false if 1 or more tasks have been added to the graph + bool IsEmpty(); // Add a task to the graph, retrieiving a token that can be used to express dependencies // between tasks. The first argument specifies the TaskKind, used for tracking the task. diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl b/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl index e0ac74ba9d..7b2f0cefdc 100644 --- a/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraph.inl @@ -33,11 +33,6 @@ namespace AZ return m_semaphore.try_acquire_for(AZStd::chrono::milliseconds{ 0 }); } - inline void TaskGraphEvent::Wait() - { - m_semaphore.acquire(); - } - inline void TaskGraphEvent::Signal() { m_semaphore.release(); @@ -59,6 +54,11 @@ namespace AZ return { AddTask(descriptor, AZStd::forward(lambdas))... }; } + inline bool TaskGraph::IsEmpty() + { + return m_tasks.empty(); + } + inline void TaskGraph::Detach() { m_retained = false; diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp new file mode 100644 index 0000000000..eed461ecb4 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.cpp @@ -0,0 +1,88 @@ +/* + * 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 + +// Create a cvar as a central location for experimentation with switching from the Job system to TaskGraph system. +AZ_CVAR(bool, cl_activateTaskGraph, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Flag clients of TaskGraph to switch between jobs/taskgraph (Note does not disable task graph system)"); +static constexpr uint32_t TaskExecutorServiceCrc = AZ_CRC_CE("TaskExecutorService"); + +namespace AZ +{ + void TaskGraphSystemComponent::Activate() + { + AZ_Assert(m_taskExecutor == nullptr, "Error multiple activation of the TaskGraphSystemComponent"); + + if (Interface::Get() == nullptr) + { + Interface::Register(this); + m_taskExecutor = aznew TaskExecutor(); + TaskExecutor::SetInstance(m_taskExecutor); + } + } + + void TaskGraphSystemComponent::Deactivate() + { + if (&TaskExecutor::Instance() == m_taskExecutor) // check that our instance is the global instance (not always true in unit tests) + { + m_taskExecutor->SetInstance(nullptr); + } + if (m_taskExecutor) + { + azdestroy(m_taskExecutor); + m_taskExecutor = nullptr; + } + if (Interface::Get() == this) + { + Interface::Unregister(this); + } + } + + void TaskGraphSystemComponent::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(TaskExecutorServiceCrc); + } + + void TaskGraphSystemComponent::GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(TaskExecutorServiceCrc); + } + + void TaskGraphSystemComponent::GetDependentServices([[maybe_unused]] ComponentDescriptor::DependencyArrayType& dependent) + { + } + + void TaskGraphSystemComponent::Reflect(ReflectContext* context) + { + if (SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ; + + if (AZ::EditContext* ec = serializeContext->GetEditContext()) + { + ec->Class + ("TaskGraph", "System component to create the default executor") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Category, "Engine") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System")) + ; + } + } + } + + bool TaskGraphSystemComponent::IsTaskGraphActive() const + { + return cl_activateTaskGraph; + } +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.h b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.h new file mode 100644 index 0000000000..a4c6da9539 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Task/TaskGraphSystemComponent.h @@ -0,0 +1,47 @@ +/* + * 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 +#include + +namespace AZ +{ + class TaskGraphSystemComponent + : public Component + , public TaskGraphActiveInterface + { + public: + AZ_COMPONENT(AZ::TaskGraphSystemComponent, "{5D56B829-1FEB-43D5-A0BD-E33C0497EFE2}") + + TaskGraphSystemComponent() = default; + + // Implement TaskGraphActiveInterface + bool IsTaskGraphActive() const override; + + private: + ////////////////////////////////////////////////////////////////////////// + // Component base + void Activate() override; + void Deactivate() override; + ////////////////////////////////////////////////////////////////////////// + + /// \ref ComponentDescriptor::GetProvidedServices + static void GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided); + /// \ref ComponentDescriptor::GetIncompatibleServices + static void GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible); + /// \ref ComponentDescriptor::GetDependentServices + static void GetDependentServices(ComponentDescriptor::DependencyArrayType& dependent); + /// \red ComponentDescriptor::Reflect + static void Reflect(ReflectContext* reflection); + + AZ::TaskExecutor* m_taskExecutor = nullptr; + }; +} diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index aa07959997..14579cbf33 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -633,6 +633,8 @@ set(FILES Task/TaskGraph.cpp Task/TaskGraph.h Task/TaskGraph.inl + Task/TaskGraphSystemComponent.h + Task/TaskGraphSystemComponent.cpp Threading/ThreadSafeDeque.h Threading/ThreadSafeDeque.inl Threading/ThreadSafeObject.h diff --git a/Code/Framework/AzCore/Tests/TaskTests.cpp b/Code/Framework/AzCore/Tests/TaskTests.cpp index e743ab6643..9e839f60ee 100644 --- a/Code/Framework/AzCore/Tests/TaskTests.cpp +++ b/Code/Framework/AzCore/Tests/TaskTests.cpp @@ -34,7 +34,7 @@ namespace UnitTest AZ::AllocatorInstance::Create(); AZ::AllocatorInstance::Create(); - m_executor = aznew TaskExecutor(4); + m_executor = aznew TaskExecutor(); } void TearDown() override @@ -236,6 +236,82 @@ namespace UnitTest EXPECT_EQ(x, 1); } + TEST_F(TaskGraphTestFixture, SingleTask) + { + AZStd::atomic_int32_t x = 0; + + TaskGraph graph; + graph.AddTask( + defaultTD, + [&x] + { + x = 1; + }); + + TaskGraphEvent ev; + graph.SubmitOnExecutor(*m_executor, &ev); + ev.Wait(); + + EXPECT_EQ(1, x); + } + + + TEST_F(TaskGraphTestFixture, SingleTaskChain) + { + AZStd::atomic_int32_t x = 0; + + TaskGraph graph; + auto a = graph.AddTask( + defaultTD, + [&x] + { + x += 1; + }); + auto b = graph.AddTask( + defaultTD, + [&x] + { + x += 1; + }); + b.Precedes(a); + + TaskGraphEvent ev; + graph.SubmitOnExecutor(*m_executor, &ev); + ev.Wait(); + + EXPECT_EQ(2, x); + } + + TEST_F(TaskGraphTestFixture, MultipleIndependentTaskChains) + { + AZStd::atomic_int32_t x = 0; + constexpr int numChains = 5; + + TaskGraph graph; + for( int i = 0; i < numChains; ++i) + { + auto a = graph.AddTask( + defaultTD, + [&x] + { + x += 1; + }); + auto b = graph.AddTask( + defaultTD, + [&x] + { + x += 1; + }); + b.Precedes(a); + } + + TaskGraphEvent ev; + graph.SubmitOnExecutor(*m_executor, &ev); + ev.Wait(); + + EXPECT_EQ(2*numChains, x); + } + TEST_F(TaskGraphTestFixture, VariadicInterface) { int x = 0; @@ -388,6 +464,7 @@ namespace UnitTest EXPECT_EQ(3, x); } + // Waiting inside a task is disallowed , test that it fails correctly TEST_F(TaskGraphTestFixture, SpawnSubgraph) { AZStd::atomic x = 0; @@ -434,7 +511,10 @@ namespace UnitTest f.Precedes(g); TaskGraphEvent ev; subgraph.SubmitOnExecutor(*m_executor, &ev); + // TaskGraphEvent::Wait asserts if called on a worker thread, suppress & validate assert + AZ_TEST_START_TRACE_SUPPRESSION; ev.Wait(); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); }); auto d = graph.AddTask( defaultTD, @@ -464,8 +544,6 @@ namespace UnitTest TaskGraphEvent ev; graph.SubmitOnExecutor(*m_executor, &ev); ev.Wait(); - - EXPECT_EQ(3 | 0b100000, x); } TEST_F(TaskGraphTestFixture, RetainedGraph) diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp index 12974d03cf..5c7398a95c 100644 --- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp +++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -295,6 +296,7 @@ namespace AzFramework azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), + azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameScheduler.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameScheduler.h index ddcee53e69..48e7e0f339 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameScheduler.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/FrameScheduler.h @@ -23,6 +23,7 @@ namespace AZ { class Job; + class TaskGraphActiveInterface; namespace RHI { @@ -228,6 +229,8 @@ namespace AZ // list of RayTracingShaderTables that should be built this frame AZStd::vector> m_rayTracingShaderTablesToBuild; + + AZ::TaskGraphActiveInterface* m_taskGraphActive = nullptr; }; } } diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp index 5d2feb1e34..3363675d0e 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp @@ -25,9 +25,11 @@ #include #include +#include #include #include #include +#include namespace AZ { @@ -77,6 +79,8 @@ namespace AZ m_rootScope = m_rootScopeProducer->GetScope(); m_device = &device; + m_taskGraphActive = AZ::Interface::Get(); + m_lastFrameEndTime = AZStd::GetTimeNowTicks(); return ResultCode::Success; @@ -85,6 +89,7 @@ namespace AZ void FrameScheduler::Shutdown() { m_device = nullptr; + m_taskGraphActive = nullptr; m_rootScopeProducer = nullptr; m_rootScope = nullptr; m_frameGraphExecuter = nullptr; @@ -258,50 +263,98 @@ namespace AZ if (m_compileRequest.m_jobPolicy == JobPolicy::Parallel) { - const auto compileGroupsBeginFunction = [](ShaderResourceGroupPool* srgPool) - { - srgPool->CompileGroupsBegin(); - }; - - resourcePoolDatabase.ForEachShaderResourceGroupPool(compileGroupsBeginFunction); - // Iterate over each SRG pool and fork jobs to compile SRGs. const uint32_t compilesPerJob = m_compileRequest.m_shaderResourceGroupCompilesPerJob; - AZ::JobCompletion jobCompletion; - - const auto compileIntervalsFunction = [compilesPerJob, &jobCompletion](ShaderResourceGroupPool* srgPool) + if (m_taskGraphActive && m_taskGraphActive->IsTaskGraphActive()) { - const uint32_t compilesInPool = srgPool->GetGroupsToCompileCount(); - const uint32_t jobCount = DivideByMultiple(compilesInPool, compilesPerJob); + AZ::TaskGraph taskGraph; - for (uint32_t i = 0; i < jobCount; ++i) + const auto compileIntervalsFunction = [compilesPerJob, &taskGraph](ShaderResourceGroupPool* srgPool) { - Interval interval; - interval.m_min = i * compilesPerJob; - interval.m_max = AZStd::min(interval.m_min + compilesPerJob, compilesInPool); + srgPool->CompileGroupsBegin(); + const uint32_t compilesInPool = srgPool->GetGroupsToCompileCount(); + const uint32_t jobCount = DivideByMultiple(compilesInPool, compilesPerJob); + AZ::TaskDescriptor srgCompileDesc{"SrgCompile", "Graphics"}; + AZ::TaskDescriptor srgCompileEndDesc{"SrgCompileEnd", "Graphics"}; - const auto compileGroupsForIntervalLambda = [srgPool, interval]() + auto srgCompileEndTask = taskGraph.AddTask( + srgCompileEndDesc, + [srgPool]() + { + srgPool->CompileGroupsEnd(); + }); + + for (uint32_t i = 0; i < jobCount; ++i) { - AZ_PROFILE_SCOPE(RHI, "FrameScheduler : compileGroupsForIntervalLambda"); - srgPool->CompileGroupsForInterval(interval); - }; + Interval interval; + interval.m_min = i * compilesPerJob; + interval.m_max = AZStd::min(interval.m_min + compilesPerJob, compilesInPool); - AZ::Job* executeGroupJob = AZ::CreateJobFunction(AZStd::move(compileGroupsForIntervalLambda), true, nullptr); - executeGroupJob->SetDependent(&jobCompletion); - executeGroupJob->Start(); + auto compileTask = taskGraph.AddTask( + srgCompileDesc, + [srgPool, interval]() + { + AZ_PROFILE_SCOPE(RHI, "FrameScheduler : compileGroupsForIntervalLambda"); + srgPool->CompileGroupsForInterval(interval); + }); + compileTask.Precedes(srgCompileEndTask); + } + }; + + resourcePoolDatabase.ForEachShaderResourceGroupPool(AZStd::move(compileIntervalsFunction)); + if (!taskGraph.IsEmpty()) + { + AZ::TaskGraphEvent finishedEvent; + taskGraph.Submit(&finishedEvent); + finishedEvent.Wait(); } - }; - - resourcePoolDatabase.ForEachShaderResourceGroupPool(AZStd::move(compileIntervalsFunction)); - - jobCompletion.StartAndWaitForCompletion(); - - const auto compileGroupsEndFunction = [](ShaderResourceGroupPool* srgPool) + } + else // use Job system { - srgPool->CompileGroupsEnd(); - }; + const auto compileGroupsBeginFunction = [](ShaderResourceGroupPool* srgPool) + { + srgPool->CompileGroupsBegin(); + }; - resourcePoolDatabase.ForEachShaderResourceGroupPool(compileGroupsEndFunction); + resourcePoolDatabase.ForEachShaderResourceGroupPool(compileGroupsBeginFunction); + + // Iterate over each SRG pool and fork jobs to compile SRGs. + AZ::JobCompletion jobCompletion; + + const auto compileIntervalsFunction = [compilesPerJob, &jobCompletion](ShaderResourceGroupPool* srgPool) + { + const uint32_t compilesInPool = srgPool->GetGroupsToCompileCount(); + const uint32_t jobCount = DivideByMultiple(compilesInPool, compilesPerJob); + + for (uint32_t i = 0; i < jobCount; ++i) + { + Interval interval; + interval.m_min = i * compilesPerJob; + interval.m_max = AZStd::min(interval.m_min + compilesPerJob, compilesInPool); + + const auto compileGroupsForIntervalLambda = [srgPool, interval]() + { + AZ_PROFILE_SCOPE(RHI, "FrameScheduler : compileGroupsForIntervalLambda"); + srgPool->CompileGroupsForInterval(interval); + }; + + AZ::Job* executeGroupJob = AZ::CreateJobFunction(AZStd::move(compileGroupsForIntervalLambda), true, nullptr); + executeGroupJob->SetDependent(&jobCompletion); + executeGroupJob->Start(); + } + }; + + resourcePoolDatabase.ForEachShaderResourceGroupPool(AZStd::move(compileIntervalsFunction)); + + jobCompletion.StartAndWaitForCompletion(); + + const auto compileGroupsEndFunction = [](ShaderResourceGroupPool* srgPool) + { + srgPool->CompileGroupsEnd(); + }; + + resourcePoolDatabase.ForEachShaderResourceGroupPool(compileGroupsEndFunction); + } } else { diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp index bd163f1419..eadd571452 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/AsyncUploadQueue.cpp @@ -33,6 +33,7 @@ namespace AZ // Use separate work submission queue from the hw copy queue to avoid the per frame sync. m_copyQueue = CommandQueue::Create(); + m_copyQueue->SetName(AZ::Name("AsyncUpload Queue")); RHI::CommandQueueDescriptor commandQueueDescriptor; commandQueueDescriptor.m_hardwareQueueClass = RHI::HardwareQueueClass::Copy; 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..fc81368331 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -194,6 +195,9 @@ namespace AZ // This function is called every time scene's render pipelines change. void RebuildPipelineStatesLookup(); + // Helper function to wait for end of TaskGraph + void WaitTGEvent(AZ::TaskGraphEvent& completionTGEvent, AZStd::atomic_bool* workToWaitOn = nullptr); + // Helper function for wait and clean up a completion job void WaitAndCleanCompletionJob(AZ::JobCompletion*& completionJob); @@ -204,12 +208,26 @@ namespace AZ // This happens in UpdateSrgs() void PrepareSceneSrg(); + // Implementation functions that allow scene to switch between using Jobs or TaskGraphs + void SimulateTaskGraph(); + void SimulateJobs(); + + void CollectDrawPacketsTaskGraph(); + void CollectDrawPacketsJobs(); + + void FinalizeDrawListsTaskGraph(); + void FinalizeDrawListsJobs(); + // List of feature processors that are active for this scene AZStd::vector m_featureProcessors; // List of pipelines of this scene. Each pipeline has an unique pipeline Id. AZStd::vector m_pipelines; + // CPU simulation TaskGraphEvent to wait for completion of all the simulation tasks + AZ::TaskGraphEvent m_simulationFinishedTGEvent; + AZStd::atomic_bool m_simulationFinishedWorkActive = false; + // CPU simulation job completion for track all feature processors' simulation jobs AZ::JobCompletion* m_simulationCompletion = nullptr; @@ -228,6 +246,7 @@ namespace AZ SceneId m_id; bool m_activated = false; + bool m_taskGraphActive = false; // update during tick, to ensure it only changes on frame boundaries RenderPipelinePtr m_defaultPipeline; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 646cba1999..c5d82c6f29 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -23,6 +23,8 @@ #include #include +#include + #include namespace AZ @@ -92,7 +94,14 @@ namespace AZ Scene::~Scene() { - WaitAndCleanCompletionJob(m_simulationCompletion); + if (m_taskGraphActive) + { + WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive); + } + else + { + WaitAndCleanCompletionJob(m_simulationCompletion); + } SceneRequestBus::Handler::BusDisconnect(); // Remove all the render pipelines. Need to process queued changes with pass system before and after remove render pipelines @@ -346,6 +355,47 @@ namespace AZ return nullptr; } + void Scene::SimulateTaskGraph() + { + static const AZ::TaskDescriptor simulationTGDesc{"RPI::Scene::Simulate", "Graphics"}; + AZ::TaskGraph simulationTG; + + for (FeatureProcessorPtr& fp : m_featureProcessors) + { + FeatureProcessor* featureProcessor = fp.get(); + simulationTG.AddTask( + simulationTGDesc, + [this, featureProcessor]() + { + featureProcessor->Simulate(m_simulatePacket); + }); + } + simulationTG.Detach(); + m_simulationFinishedWorkActive = true; + simulationTG.Submit(&m_simulationFinishedTGEvent); + } + + void Scene::SimulateJobs() + { + // Create a new job to track completion. + m_simulationCompletion = aznew AZ::JobCompletion(); + + for (FeatureProcessorPtr& fp : m_featureProcessors) + { + FeatureProcessor* featureProcessor = fp.get(); + const auto jobLambda = [this, featureProcessor]() + { + + featureProcessor->Simulate(m_simulatePacket); + }; + + AZ::Job* simulationJob = AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr); //auto-deletes + simulationJob->SetDependent(m_simulationCompletion); + simulationJob->Start(); + } + //[GFX TODO]: the completion job should start here + } + void Scene::Simulate([[maybe_unused]] const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) { AZ_PROFILE_SCOPE(RPI, "Scene: Simulate"); @@ -353,7 +403,17 @@ namespace AZ m_simulationTime = tickInfo.m_currentGameTime; // If previous simulation job wasn't done, wait for it to finish. - WaitAndCleanCompletionJob(m_simulationCompletion); + if (m_taskGraphActive) + { + WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive); + } + else + { + WaitAndCleanCompletionJob(m_simulationCompletion); + } + + auto taskGraphActiveInterface = AZ::Interface::Get(); + m_taskGraphActive = taskGraphActiveInterface && taskGraphActiveInterface->IsTaskGraphActive(); if (jobPolicy == RHI::JobPolicy::Serial) { @@ -364,22 +424,27 @@ namespace AZ } else { - // Create a new job to track completion. - m_simulationCompletion = aznew AZ::JobCompletion(); - - for (FeatureProcessorPtr& fp : m_featureProcessors) + if (m_taskGraphActive) { - FeatureProcessor* featureProcessor = fp.get(); - const auto jobLambda = [this, featureProcessor]() - { - featureProcessor->Simulate(m_simulatePacket); - }; - - AZ::Job* simulationJob = AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr); //auto-deletes - simulationJob->SetDependent(m_simulationCompletion); - simulationJob->Start(); + SimulateTaskGraph(); } - //[GFX TODO]: the completion job should start here + else + { + SimulateJobs(); + } + } + } + + void Scene::WaitTGEvent(AZ::TaskGraphEvent& completionTGEvent, AZStd::atomic_bool* workToWaitOn ) + { + AZ_PROFILE_SCOPE(RPI, "Scene: WaitAndCleanCompletionJob"); + if (!workToWaitOn || workToWaitOn->load()) + { + completionTGEvent.Wait(); + } + if (workToWaitOn) + { + workToWaitOn->store(false); } } @@ -394,7 +459,7 @@ namespace AZ completionJob = nullptr; } } - + void Scene::ConnectEvent(PrepareSceneSrgEvent::Handler& handler) { handler.Connect(m_prepareSrgEvent); @@ -418,12 +483,139 @@ namespace AZ } } + void Scene::CollectDrawPacketsTaskGraph() + { + AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets"); + AZ::TaskGraphEvent collectDrawPacketsTGEvent; + static const AZ::TaskDescriptor collectDrawPacketsTGDesc{"RPI_Scene_PrepareRender_CollectDrawPackets", "Graphics"}; + + AZ::TaskGraph collectDrawPacketsTG; + // Launch FeatureProcessor::Render() jobs + for (auto& fp : m_featureProcessors) + { + collectDrawPacketsTG.AddTask( + collectDrawPacketsTGDesc, + [this, &fp]() + { + fp->Render(m_renderPacket); + }); + + } + collectDrawPacketsTG.Submit(&collectDrawPacketsTGEvent); + + // Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs if m_parallelOctreeTraversal) + bool parallelOctreeTraversal = m_cullingScene->GetDebugContext().m_parallelOctreeTraversal; + m_cullingScene->BeginCulling(m_renderPacket.m_views); + AZ::JobCompletion processCullablesCompletion; + for (ViewPtr& viewPtr : m_renderPacket.m_views) + { + AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob) + { + m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job + }, + true, nullptr); //auto-deletes + if (parallelOctreeTraversal) + { + processCullablesJob->SetDependent(&processCullablesCompletion); + processCullablesJob->Start(); + } + else + { + processCullablesJob->StartAndWaitForCompletion(); + } + } + + WaitTGEvent(collectDrawPacketsTGEvent); + processCullablesCompletion.StartAndWaitForCompletion(); + } + + void Scene::CollectDrawPacketsJobs() + { + AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets"); + AZ::JobCompletion* collectDrawPacketsCompletion = aznew AZ::JobCompletion(); + + // Launch FeatureProcessor::Render() jobs + for (auto& fp : m_featureProcessors) + { + const auto renderLambda = [this, &fp]() + { + fp->Render(m_renderPacket); + }; + + AZ::Job* renderJob = AZ::CreateJobFunction(AZStd::move(renderLambda), true, nullptr); //auto-deletes + renderJob->SetDependent(collectDrawPacketsCompletion); + renderJob->Start(); + } + + // Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs) + m_cullingScene->BeginCulling(m_renderPacket.m_views); + for (ViewPtr& viewPtr : m_renderPacket.m_views) + { + AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob) + { + m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job + }, + true, nullptr); //auto-deletes + if (m_cullingScene->GetDebugContext().m_parallelOctreeTraversal) + { + processCullablesJob->SetDependent(collectDrawPacketsCompletion); + processCullablesJob->Start(); + } + else + { + processCullablesJob->StartAndWaitForCompletion(); + } + } + + WaitAndCleanCompletionJob(collectDrawPacketsCompletion); + } + + void Scene::FinalizeDrawListsTaskGraph() + { + AZ::TaskGraphEvent finalizeDrawListsTGEvent; + static const AZ::TaskDescriptor finalizeDrawListsTGDesc{"RPI_Scene_PrepareRender_FinalizeDrawLists", "Graphics"}; + + AZ::TaskGraph finalizeDrawListsTG; + for (auto& view : m_renderPacket.m_views) + { + finalizeDrawListsTG.AddTask( + finalizeDrawListsTGDesc, + [view]() + { + view->FinalizeDrawLists(); + }); + } + finalizeDrawListsTG.Submit(&finalizeDrawListsTGEvent); + WaitTGEvent(finalizeDrawListsTGEvent); + } + + void Scene::FinalizeDrawListsJobs() + { + AZ::JobCompletion* finalizeDrawListsCompletion = aznew AZ::JobCompletion(); + for (auto& view : m_renderPacket.m_views) + { + const auto finalizeDrawListsLambda = [view]() + { + view->FinalizeDrawLists(); + }; + + AZ::Job* finalizeDrawListsJob = AZ::CreateJobFunction(AZStd::move(finalizeDrawListsLambda), true, nullptr); //auto-deletes + finalizeDrawListsJob->SetDependent(finalizeDrawListsCompletion); + finalizeDrawListsJob->Start(); + } + WaitAndCleanCompletionJob(finalizeDrawListsCompletion); + } + void Scene::PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) { AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender"); + if (m_taskGraphActive) + { + WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive); + } + else { - AZ_PROFILE_SCOPE(RPI, "WaitForSimulationCompletion"); WaitAndCleanCompletionJob(m_simulationCompletion); } @@ -496,44 +688,16 @@ namespace AZ } { - AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets"); - AZ::JobCompletion* collectDrawPacketsCompletion = aznew AZ::JobCompletion(); - // Launch FeatureProcessor::Render() jobs - for (auto& fp : m_featureProcessors) + if (m_taskGraphActive) { - const auto renderLambda = [this, &fp]() - { - fp->Render(m_renderPacket); - }; - - AZ::Job* renderJob = AZ::CreateJobFunction(AZStd::move(renderLambda), true, nullptr); //auto-deletes - renderJob->SetDependent(collectDrawPacketsCompletion); - renderJob->Start(); + CollectDrawPacketsTaskGraph(); } - - // Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs) - m_cullingScene->BeginCulling(m_renderPacket.m_views); - for (ViewPtr& viewPtr : m_renderPacket.m_views) + else { - AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob) - { - m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob); - }, - true, nullptr); //auto-deletes - if (m_cullingScene->GetDebugContext().m_parallelOctreeTraversal) - { - processCullablesJob->SetDependent(collectDrawPacketsCompletion); - processCullablesJob->Start(); - } - else - { - processCullablesJob->StartAndWaitForCompletion(); - } + CollectDrawPacketsJobs(); } - WaitAndCleanCompletionJob(collectDrawPacketsCompletion); - m_cullingScene->EndCulling(); // Add dynamic draw data for all the views @@ -556,20 +720,15 @@ namespace AZ } else { - AZ::JobCompletion* finalizeDrawListsCompletion = aznew AZ::JobCompletion(); - for (auto& view : m_renderPacket.m_views) + if (m_taskGraphActive) { - const auto finalizeDrawListsLambda = [view]() - { - view->FinalizeDrawLists(); - }; - - AZ::Job* finalizeDrawListsJob = AZ::CreateJobFunction(AZStd::move(finalizeDrawListsLambda), true, nullptr); //auto-deletes - finalizeDrawListsJob->SetDependent(finalizeDrawListsCompletion); - finalizeDrawListsJob->Start(); + FinalizeDrawListsTaskGraph(); + } + else + { + FinalizeDrawListsJobs(); } AZ_PROFILE_END(RPI); - WaitAndCleanCompletionJob(finalizeDrawListsCompletion); } } diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/001_DefaultWhite.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/001_DefaultWhite.material index 8fe732cb09..2e4eee7f8e 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/001_DefaultWhite.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/001_DefaultWhite.material @@ -1,6 +1,6 @@ { "description": "", - "materialType": "Materials\\Types\\StandardPBR.materialtype", + "materialType": "Materials/Types/StandardPBR.materialtype", "parentMaterial": "", "propertyLayoutVersion": 3 -} +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/002_BaseColorLerp.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/002_BaseColorLerp.material index 8509e08d78..f8214e1b2e 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/002_BaseColorLerp.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/002_BaseColorLerp.material @@ -1,6 +1,6 @@ { "description": "", - "materialType": "Materials\\Types\\StandardPBR.materialtype", + "materialType": "Materials/Types/StandardPBR.materialtype", "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { @@ -16,4 +16,4 @@ "textureMap": "TestData/Textures/TextureHaven/4k_castle_brick_02_red/4k_castle_brick_02_red_hp_bc.png" } } -} +} \ No newline at end of file From b3d7de1e78c447d44f3cb3702a1b084e6c319aaf Mon Sep 17 00:00:00 2001 From: mrieggeramzn Date: Fri, 1 Oct 2021 13:03:52 -0700 Subject: [PATCH 24/24] Improvements. You can now call SetDecalMaterial() with any permutation and it works as expected Signed-off-by: mrieggeramzn --- .../ShaderLib/Atom/Features/PBR/Decals.azsli | 2 + .../DecalTextureArrayFeatureProcessor.cpp | 45 ++++++++++++++----- .../DecalTextureArrayFeatureProcessor.h | 1 + .../Decals/DecalComponentController.cpp | 2 +- .../Source/Decals/EditorDecalComponent.cpp | 2 +- 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli index 5e7088617b..f9ead72ce4 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Decals.azsli @@ -70,6 +70,8 @@ void ApplyDecal(uint currDecalIndex, inout Surface surface) float4 baseMap = 0; float2 normalMap = 0; + // Each texture array handles a size permutation. + // e.g. it could be that tex array 0 handles 256x256 and tex array 1 handles 512x64, etc. switch(textureArrayIndex) { case 0: diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp index e5bf0bd9fa..0323282f5c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp @@ -301,23 +301,44 @@ namespace AZ return; } - if (material.IsValid()) + if (GetMaterialUsedByDecal(handle) == material) + return; + + const auto decalIndex = handle.GetIndex(); + + const bool isValidMaterialBeingUsedCurrently = m_decalData.GetData(decalIndex).m_textureArrayIndex != DecalData::UnusedIndex; + if (isValidMaterialBeingUsedCurrently) { - AZ_Assert(m_decalData.GetData(handle.GetIndex()).m_textureArrayIndex == DecalData::UnusedIndex, "Setting Material on a decal more than once is not currently supported."); + RemoveMaterialFromDecal(decalIndex); + } - const auto iter = m_materialToTextureArrayLookupTable.find(material); - if (iter != m_materialToTextureArrayLookupTable.end()) - { - // This material is already loaded and registered with this feature processor - iter->second.m_useCount++; - SetDecalTextureLocation(handle, iter->second.m_location); - return; - } + if (!material.IsValid()) + return; - // Material not loaded so queue it up for loading. - QueueMaterialLoadForDecal(material, handle); + const auto iter = m_materialToTextureArrayLookupTable.find(material); + if (iter != m_materialToTextureArrayLookupTable.end()) + { + // This material is already loaded and registered with this feature processor + iter->second.m_useCount++; + SetDecalTextureLocation(handle, iter->second.m_location); return; } + + // Material not loaded so queue it up for loading. + QueueMaterialLoadForDecal(material, handle); + } + + void DecalTextureArrayFeatureProcessor::RemoveMaterialFromDecal(const uint16_t decalIndex) + { + DecalLocation decalLocation; + decalLocation.textureArrayIndex = m_decalData.GetData(decalIndex).m_textureArrayIndex; + decalLocation.textureIndex = m_decalData.GetData(decalIndex).m_textureIndex; + RemoveDecalFromTextureArrays(decalLocation); + + m_decalData.GetData(decalIndex).m_textureArrayIndex = DecalData::UnusedIndex; + m_decalData.GetData(decalIndex).m_textureIndex = DecalData::UnusedIndex; + + m_deviceBufferNeedsUpdate = true; } void DecalTextureArrayFeatureProcessor::CacheShaderIndices() diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h index 13a6682629..fd535bbe64 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.h @@ -114,6 +114,7 @@ namespace AZ AZStd::optional AddMaterialToTextureArrays(const AZ::RPI::MaterialAsset* materialAsset); int FindTextureArrayWithSize(const RHI::Size& size) const; + void RemoveMaterialFromDecal(const uint16_t decalIndex); void SetDecalTextureLocation(const DecalHandle& handle, const DecalLocation location); void QueueMaterialLoadForDecal(const AZ::Data::AssetId material, const DecalHandle handle); bool RemoveDecalFromTextureArrays(const DecalLocation decalLocation); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.cpp index 5d16164d92..dd8266c1ca 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/DecalComponentController.cpp @@ -230,7 +230,7 @@ namespace AZ { DecalNotificationBus::Event(m_entityId, &DecalNotifications::OnMaterialChanged, m_configuration.m_materialAsset); - if (m_featureProcessor && m_configuration.m_materialAsset.GetId().IsValid()) + if (m_featureProcessor) { m_featureProcessor->SetDecalMaterial(m_handle, m_configuration.m_materialAsset.GetId()); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.cpp index c5d61dd54a..f8865e8d34 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Decals/EditorDecalComponent.cpp @@ -192,7 +192,7 @@ namespace AZ u32 EditorDecalComponent::OnConfigurationChanged() { - BaseClass::OnConfigurationChanged(); + m_controller.ConfigurationChanged(); return Edit::PropertyRefreshLevels::AttributesAndValues; }