diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_LayerBlender.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_LayerBlender.py index 13f2a569c1..438a7df732 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_LayerBlender.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/test_LayerBlender.py @@ -86,6 +86,7 @@ class TestLayerBlender(object): @pytest.mark.SUITE_periodic @pytest.mark.dynveg_area @pytest.mark.parametrize("launcher_platform", ['windows']) + @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/4170") def test_LayerBlender_E2E_Launcher(self, workspace, project, launcher, level, remote_console_instance, launcher_platform): diff --git a/CMakeLists.txt b/CMakeLists.txt index 43b0dd240e..e659270f84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,11 +29,11 @@ include(cmake/PAL.cmake) include(cmake/PALTools.cmake) include(cmake/RuntimeDependencies.cmake) include(cmake/Configurations.cmake) # Requires to be after PAL so we get platform variable definitions -include(cmake/Install.cmake) include(cmake/Dependencies.cmake) include(cmake/Deployment.cmake) include(cmake/3rdParty.cmake) include(cmake/LYPython.cmake) +include(cmake/Install.cmake) include(cmake/LYWrappers.cmake) include(cmake/Gems.cmake) include(cmake/UnitTest.cmake) diff --git a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui index df7474d9d4..a345438aed 100644 --- a/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui +++ b/Code/Editor/AzAssetBrowser/AzAssetBrowserWindow.ui @@ -140,9 +140,6 @@ QAbstractItemView::ScrollPerPixel - - false - true @@ -201,6 +198,11 @@
AzToolsFramework/AssetBrowser/Search/SearchWidget.h
1 + + AzQtComponents::TableView + QTreeView +
AzQtComponents/Components/Widgets/TableView.h
+
AzToolsFramework::AssetBrowser::AssetBrowserTreeView QTreeView @@ -214,7 +216,7 @@ AzToolsFramework::AssetBrowser::AssetBrowserTableView - QTableView + AzQtComponents::TableView
AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h
diff --git a/Code/Editor/Core/QtEditorApplication_linux.cpp b/Code/Editor/Core/QtEditorApplication_linux.cpp index fa39609308..175bef0238 100644 --- a/Code/Editor/Core/QtEditorApplication_linux.cpp +++ b/Code/Editor/Core/QtEditorApplication_linux.cpp @@ -8,11 +8,21 @@ #include "QtEditorApplication.h" +#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB +#include +#endif + namespace Editor { - bool EditorQtApplication::nativeEventFilter(const QByteArray& , void* , long* ) + bool EditorQtApplication::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long*) { - // TODO_KDAB_LINUX + if (GetIEditor()->IsInGameMode()) + { +#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + AzFramework::LinuxXcbEventHandlerBus::Broadcast(&AzFramework::LinuxXcbEventHandler::HandleXcbEvent, static_cast(message)); +#endif + return true; + } return false; } } diff --git a/Code/Editor/EditorToolsApplication.cpp b/Code/Editor/EditorToolsApplication.cpp index 1e5d747e4a..26e608f657 100644 --- a/Code/Editor/EditorToolsApplication.cpp +++ b/Code/Editor/EditorToolsApplication.cpp @@ -34,10 +34,14 @@ namespace EditorInternal : ToolsApplication(argc, argv) { EditorToolsApplicationRequests::Bus::Handler::BusConnect(); + AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusConnect(); + AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusConnect(); } EditorToolsApplication::~EditorToolsApplication() { + AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler::BusDisconnect(); + AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusDisconnect(); EditorToolsApplicationRequests::Bus::Handler::BusDisconnect(); Stop(); } @@ -48,7 +52,6 @@ namespace EditorInternal return m_StartupAborted; } - void EditorToolsApplication::RegisterCoreComponents() { AzToolsFramework::ToolsApplication::RegisterCoreComponents(); @@ -274,5 +277,14 @@ namespace EditorInternal Exit(); } -} + AzToolsFramework::ViewportInteraction::KeyboardModifiers EditorToolsApplication::QueryKeyboardModifiers() + { + return AzToolsFramework::ViewportInteraction::BuildKeyboardModifiers(QGuiApplication::queryKeyboardModifiers()); + } + AZStd::chrono::milliseconds EditorToolsApplication::EditorViewportInputTimeNow() + { + const auto now = AZStd::chrono::high_resolution_clock::now(); + return AZStd::chrono::time_point_cast(now).time_since_epoch(); + } +} // namespace EditorInternal diff --git a/Code/Editor/EditorToolsApplication.h b/Code/Editor/EditorToolsApplication.h index 422772cbc4..d4e6223445 100644 --- a/Code/Editor/EditorToolsApplication.h +++ b/Code/Editor/EditorToolsApplication.h @@ -7,7 +7,9 @@ */ #pragma once + #include +#include #include "Core/EditorMetricsPlainTextNameRegistration.h" #include "EditorToolsApplicationAPI.h" @@ -19,6 +21,8 @@ namespace EditorInternal class EditorToolsApplication : public AzToolsFramework::ToolsApplication , public EditorToolsApplicationRequests::Bus::Handler + , public AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler + , public AzToolsFramework::ViewportInteraction::EditorViewportInputTimeNowRequestBus::Handler { public: EditorToolsApplication(int* argc, char*** argv); @@ -44,6 +48,12 @@ namespace EditorInternal void CreateReflectionManager() override; void Reflect(AZ::ReflectContext* context) override; + // EditorModifierKeyRequestBus overrides ... + AzToolsFramework::ViewportInteraction::KeyboardModifiers QueryKeyboardModifiers() override; + + // EditorViewportInputTimeNowRequestBus overrides ... + AZStd::chrono::milliseconds EditorViewportInputTimeNow() override; + protected: // From EditorToolsApplicationRequests bool OpenLevel(AZStd::string_view levelName) override; diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp index 1ed901e514..5b5b52d28f 100644 --- a/Code/Editor/EditorViewportWidget.cpp +++ b/Code/Editor/EditorViewportWidget.cpp @@ -744,11 +744,15 @@ void EditorViewportWidget::RenderAll() { namespace AztfVi = AzToolsFramework::ViewportInteraction; + AztfVi::KeyboardModifiers keyboardModifiers; + AztfVi::EditorModifierKeyRequestBus::BroadcastResult( + keyboardModifiers, &AztfVi::EditorModifierKeyRequestBus::Events::QueryKeyboardModifiers); + m_debugDisplay->DepthTestOff(); m_manipulatorManager->DrawManipulators( *m_debugDisplay, GetCameraState(), BuildMouseInteractionInternal( - AztfVi::MouseButtons(AztfVi::TranslateMouseButtons(QGuiApplication::mouseButtons())), QueryKeyboardModifiers(), + AztfVi::MouseButtons(AztfVi::TranslateMouseButtons(QGuiApplication::mouseButtons())), keyboardModifiers, BuildMousePick(WidgetToViewport(mapFromGlobal(QCursor::pos()))))); m_debugDisplay->DepthTestOn(); } @@ -959,12 +963,13 @@ QWidget* EditorViewportWidget::GetWidgetForViewportContextMenu() bool EditorViewportWidget::ShowingWorldSpace() { - return QueryKeyboardModifiers().Shift(); -} + namespace AztfVi = AzToolsFramework::ViewportInteraction; -AzToolsFramework::ViewportInteraction::KeyboardModifiers EditorViewportWidget::QueryKeyboardModifiers() -{ - return AzToolsFramework::ViewportInteraction::BuildKeyboardModifiers(QGuiApplication::queryKeyboardModifiers()); + AztfVi::KeyboardModifiers keyboardModifiers; + AztfVi::EditorModifierKeyRequestBus::BroadcastResult( + keyboardModifiers, &AztfVi::EditorModifierKeyRequestBus::Events::QueryKeyboardModifiers); + + return keyboardModifiers.Shift(); } void EditorViewportWidget::SetViewportId(int id) @@ -1039,7 +1044,6 @@ void EditorViewportWidget::ConnectViewportInteractionRequestBus() { AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusConnect(GetViewportId()); AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusConnect(GetViewportId()); - AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusConnect(); m_viewportUi.ConnectViewportUiBus(GetViewportId()); AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusConnect(); @@ -1050,7 +1054,6 @@ void EditorViewportWidget::DisconnectViewportInteractionRequestBus() AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusDisconnect(); m_viewportUi.DisconnectViewportUiBus(); - AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler::BusDisconnect(); AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusDisconnect(); AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusDisconnect(); } diff --git a/Code/Editor/EditorViewportWidget.h b/Code/Editor/EditorViewportWidget.h index 6b46524394..49930a2a13 100644 --- a/Code/Editor/EditorViewportWidget.h +++ b/Code/Editor/EditorViewportWidget.h @@ -92,7 +92,6 @@ class SANDBOX_API EditorViewportWidget final , private AzFramework::InputSystemCursorConstraintRequestBus::Handler , private AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler , private AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler - , private AzToolsFramework::ViewportInteraction::EditorModifierKeyRequestBus::Handler , private AzFramework::AssetCatalogEventBus::Handler , private AZ::RPI::SceneNotificationBus::Handler { @@ -212,9 +211,6 @@ private: // EditorEntityViewportInteractionRequestBus overrides ... void FindVisibleEntities(AZStd::vector& visibleEntities) override; - // EditorModifierKeyRequestBus overrides ... - AzToolsFramework::ViewportInteraction::KeyboardModifiers QueryKeyboardModifiers() override; - // Camera::EditorCameraRequestBus overrides ... void SetViewFromEntityPerspective(const AZ::EntityId& entityId) override; void SetViewAndMovementLockFromEntityPerspective(const AZ::EntityId& entityId, bool lockCameraMovement) override; diff --git a/Code/Editor/Include/IEditorClassFactory.h b/Code/Editor/Include/IEditorClassFactory.h index 0827dc96dc..dd47f803e2 100644 --- a/Code/Editor/Include/IEditorClassFactory.h +++ b/Code/Editor/Include/IEditorClassFactory.h @@ -14,8 +14,10 @@ #define CRYINCLUDE_EDITOR_INCLUDE_IEDITORCLASSFACTORY_H #pragma once +#include #include #include +#include #define DEFINE_UUID(l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ static const GUID uuid() { return { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }; } @@ -34,7 +36,7 @@ struct IUnknown #endif #define __uuidof(T) T::uuid() -#if defined(AZ_PLATFORM_LINUX) +#if defined(AZ_PLATFORM_LINUX) || defined(AZ_PLATFORM_MAC) # ifndef _REFGUID_DEFINED # define _REFGUID_DEFINED @@ -65,7 +67,7 @@ enum }; #endif -#endif // defined(AZ_PLATFORM_LINUX) +#endif // defined(AZ_PLATFORM_LINUX) || defined(AZ_PLATFORM_MAC) #include "SandboxAPI.h" diff --git a/Code/Editor/Platform/Mac/EditorEntitlements.plist b/Code/Editor/Platform/Mac/EditorEntitlements.plist new file mode 100644 index 0000000000..cefa2bf93b --- /dev/null +++ b/Code/Editor/Platform/Mac/EditorEntitlements.plist @@ -0,0 +1,10 @@ + + + + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.disable-library-validation + + + diff --git a/Code/Editor/Platform/Mac/editor_mac.cmake b/Code/Editor/Platform/Mac/editor_mac.cmake index fdccfafb46..eed955f2e4 100644 --- a/Code/Editor/Platform/Mac/editor_mac.cmake +++ b/Code/Editor/Platform/Mac/editor_mac.cmake @@ -12,28 +12,5 @@ set_target_properties(Editor PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_LIST_DIR}/gui_info.plist RESOURCE ${CMAKE_CURRENT_LIST_DIR}/Images.xcassets XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME EditorAppIcon + ENTITLEMENT_FILE_PATH ${CMAKE_CURRENT_LIST_DIR}/EditorEntitlements.plist ) - -# We cannot use ly_add_target here because we're already including this file from inside ly_add_target -# So we need to setup target, dependencies and install logic manually. -add_executable(EditorDummy Platform/Mac/main_dummy.cpp) -add_executable(AZ::EditorDummy ALIAS EditorDummy) - -ly_target_link_libraries(EditorDummy - PRIVATE - AZ::AzCore - AZ::AzFramework) - -ly_add_dependencies(Editor EditorDummy) - -# Store the aliased target into a DIRECTORY property -set_property(DIRECTORY APPEND PROPERTY LY_DIRECTORY_TARGETS AZ::EditorDummy) - -# Store the directory path in a GLOBAL property so that it can be accessed -# in the layout install logic. Skip if the directory has already been added -get_property(ly_all_target_directories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES) -if(NOT CMAKE_CURRENT_SOURCE_DIR IN_LIST ly_all_target_directories) - set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGET_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}) -endif() - -ly_install_add_install_path_setreg(Editor) \ No newline at end of file diff --git a/Code/Editor/Platform/Mac/gui_info.plist b/Code/Editor/Platform/Mac/gui_info.plist index cc87cbbb47..5b5f94e977 100644 --- a/Code/Editor/Platform/Mac/gui_info.plist +++ b/Code/Editor/Platform/Mac/gui_info.plist @@ -3,7 +3,7 @@ CFBundleExecutable - EditorDummy + Editor CFBundleIdentifier org.O3DE.Editor CFBundlePackageType diff --git a/Code/Editor/Platform/Mac/main_dummy.cpp b/Code/Editor/Platform/Mac/main_dummy.cpp deleted file mode 100644 index 348a32ab47..0000000000 --- a/Code/Editor/Platform/Mac/main_dummy.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include -#include -#include - -#include - -int main(int argc, char* argv[]) -{ - // Create a ComponentApplication to initialize the AZ::SystemAllocator and initialize the SettingsRegistry - AZ::ComponentApplication::Descriptor desc; - AZ::ComponentApplication application; - application.Create(desc); - - AZStd::vector envVars; - - const char* homePath = std::getenv("HOME"); - envVars.push_back(AZStd::string::format("HOME=%s", homePath)); - - if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) - { - const char* dyldLibPathOrig = std::getenv("DYLD_LIBRARY_PATH"); - AZStd::string dyldSearchPath = AZStd::string::format("DYLD_LIBRARY_PATH=%s", dyldLibPathOrig); - if (AZ::IO::FixedMaxPath projectModulePath; - settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath)) - { - dyldSearchPath.append(":"); - dyldSearchPath.append(projectModulePath.c_str()); - } - - if (AZ::IO::FixedMaxPath installedBinariesFolder; - settingsRegistry->Get(installedBinariesFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) - { - if (AZ::IO::FixedMaxPath engineRootFolder; - settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder)) - { - installedBinariesFolder = engineRootFolder / installedBinariesFolder; - dyldSearchPath.append(":"); - dyldSearchPath.append(installedBinariesFolder.c_str()); - } - } - envVars.push_back(dyldSearchPath); - } - - AZStd::string commandArgs; - for (int i = 1; i < argc; i++) - { - commandArgs.append(argv[i]); - commandArgs.append(" "); - } - - AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - AZ::IO::Path processPath{ AZ::IO::PathView(AZ::Utils::GetExecutableDirectory()) }; - processPath /= "Editor"; - processLaunchInfo.m_processExecutableString = AZStd::move(processPath.Native()); - processLaunchInfo.m_commandlineParameters = commandArgs; - processLaunchInfo.m_environmentVariables = &envVars; - processLaunchInfo.m_showWindow = true; - - AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); - - application.Destroy(); - - return 0; -} - diff --git a/Code/Editor/Plugins/EditorCommon/CMakeLists.txt b/Code/Editor/Plugins/EditorCommon/CMakeLists.txt index 2aff57cfb1..cd9f2e79c7 100644 --- a/Code/Editor/Plugins/EditorCommon/CMakeLists.txt +++ b/Code/Editor/Plugins/EditorCommon/CMakeLists.txt @@ -51,4 +51,5 @@ ly_add_target( AZ::AzCore AZ::AzToolsFramework AZ::AzQtComponents + Legacy::EditorCore ) diff --git a/Code/Framework/AzCore/AzCore/Debug/Profiler.h b/Code/Framework/AzCore/AzCore/Debug/Profiler.h index 8af48e47f6..56103e8314 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Profiler.h +++ b/Code/Framework/AzCore/AzCore/Debug/Profiler.h @@ -59,6 +59,20 @@ namespace AZStd namespace AZ::Debug { + // interface for externally defined profiler systems + class Profiler + { + public: + AZ_RTTI(Profiler, "{3E5D6329-72D1-41BA-9158-68A349D1A4D5}"); + + Profiler() = default; + virtual ~Profiler() = default; + + // support for the extra macro args (e.g. format strings) will come in a later PR + virtual void BeginRegion(const Budget* budget, const char* eventName) = 0; + virtual void EndRegion(const Budget* budget) = 0; + }; + class ProfileScope { public: diff --git a/Code/Framework/AzCore/AzCore/Debug/Profiler.inl b/Code/Framework/AzCore/AzCore/Debug/Profiler.inl index 8ca8368ce1..74c0f553c4 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Profiler.inl +++ b/Code/Framework/AzCore/AzCore/Debug/Profiler.inl @@ -6,6 +6,8 @@ * */ +#include + namespace AZ::Debug { template @@ -22,9 +24,11 @@ namespace AZ::Debug PIXBeginEvent(PIX_COLOR_INDEX(budget->Crc() & 0xff), eventName, args...); #endif budget->BeginProfileRegion(); -// TODO: injecting instrumentation for other profilers -// NOTE: external profiler registration won't occur inline in a header necessarily in this manner, but the exact mechanism -// will be introduced in a future PR + + if (auto profiler = AZ::Interface::Get(); profiler) + { + profiler->BeginRegion(budget, eventName); + } #endif } @@ -39,6 +43,10 @@ namespace AZ::Debug #if defined(USE_PIX) PIXEndEvent(); #endif + if (auto profiler = AZ::Interface::Get(); profiler) + { + profiler->EndRegion(budget); + } #endif } diff --git a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Module/DynamicModuleHandle_UnixLike.cpp b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Module/DynamicModuleHandle_UnixLike.cpp index 5cf854d49f..1ca6a7c4c7 100644 --- a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Module/DynamicModuleHandle_UnixLike.cpp +++ b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Module/DynamicModuleHandle_UnixLike.cpp @@ -72,6 +72,7 @@ namespace AZ { if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { + bool fileFound = false; if (AZ::IO::FixedMaxPath projectModulePath; settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath)) { @@ -79,6 +80,23 @@ namespace AZ if (AZ::IO::SystemFile::Exists(projectModulePath.c_str())) { m_fileName.assign(projectModulePath.c_str(), projectModulePath.Native().size()); + fileFound = true; + } + } + if (!fileFound) + { + if (AZ::IO::FixedMaxPath installedBinariesPath; + settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) + { + if (AZ::IO::FixedMaxPath engineRootFolder; + settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder)) + { + installedBinariesPath = engineRootFolder / installedBinariesPath / fullFilePath; + if (AZ::IO::SystemFile::Exists(installedBinariesPath.c_str())) + { + m_fileName.assign(installedBinariesPath.c_str(), installedBinariesPath.Native().size()); + } + } } } } diff --git a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp index 9aff67a00f..2e31936057 100644 --- a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp +++ b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp @@ -9,6 +9,7 @@ #include #include +#include namespace AZ { @@ -39,6 +40,14 @@ namespace AZ AZ::IO::FixedMaxPath path{homePath}; return path.Native(); } + + struct passwd* pass = getpwuid(getuid()); + if (pass) + { + AZ::IO::FixedMaxPath path{pass->pw_dir}; + return path.Native(); + } + return {}; } diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp index c94d588a90..d2a81102dc 100644 --- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp +++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp @@ -181,7 +181,7 @@ namespace AZ::IO::ArchiveInternal return 0; } - nTotal = (AZStd::min)(nTotal, GetFileSize() - m_nCurSeek); + nTotal = AZStd::min(nTotal, GetFileSize() - m_nCurSeek); int64_t nReadBytes = GetFile()->ReadData(pDest, m_nCurSeek, nTotal); if (nReadBytes == -1) diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp index b909689c22..1b3645630e 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp @@ -9,8 +9,19 @@ #include #include +#include + namespace AzFramework { + ClickDetector::ClickDetector() + { + m_timeNowFn = [] + { + const auto now = AZStd::chrono::high_resolution_clock::now(); + return AZStd::chrono::time_point_cast(now).time_since_epoch(); + }; + } + ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta) { const auto previousDetectionState = m_detectionState; @@ -26,11 +37,13 @@ namespace AzFramework if (clickEvent == ClickEvent::Down) { - const auto now = std::chrono::steady_clock::now(); + const auto now = m_timeNowFn(); if (m_tryBeginTime) { - const std::chrono::duration diff = now - m_tryBeginTime.value(); - if (diff.count() < m_doubleClickInterval) + using FloatingPointSeconds = AZStd::chrono::duration; + + const auto diff = now - m_tryBeginTime.value(); + if (FloatingPointSeconds(diff).count() < m_doubleClickInterval) { return ClickOutcome::Nil; } @@ -43,7 +56,8 @@ namespace AzFramework } else if (clickEvent == ClickEvent::Up) { - const auto clickOutcome = [detectionState = m_detectionState] { + const auto clickOutcome = [detectionState = m_detectionState] + { if (detectionState == DetectionState::WaitingForMove) { return ClickOutcome::Click; @@ -66,4 +80,9 @@ namespace AzFramework return ClickOutcome::Nil; } + + void ClickDetector::OverrideTimeNowFn(AZStd::function timeNowFn) + { + m_timeNowFn = AZStd::move(timeNowFn); + } } // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.h b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.h index f95924550a..70bdeb4619 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include @@ -21,10 +22,9 @@ namespace AzFramework //! (mouse down with movement and then mouse up). class ClickDetector { - //! Alias for recording time of mouse down events - using Time = std::chrono::time_point; - public: + ClickDetector(); + //! Internal representation of click event (map from external event for this when //! calling DetectClick). enum class ClickEvent @@ -51,6 +51,10 @@ namespace AzFramework void SetDoubleClickInterval(float doubleClickInterval); //! Override the dead zone before a 'move' outcome will be triggered. void SetDeadZone(float deadZone); + //! Override how the current time is retrieved. + //! This is helpful to override when it comes to simulating different passages of + //! time to avoid double click issues in tests for example. + void OverrideTimeNowFn(AZStd::function timeNowFn); private: //! Internal state of ClickDetector based on incoming events. @@ -65,7 +69,9 @@ namespace AzFramework float m_deadZone = 2.0f; //!< How far to move before a click is cancelled (when Move will fire). float m_doubleClickInterval = 0.4f; //!< Default double click interval, can be overridden. DetectionState m_detectionState; //!< Internal state of ClickDetector. - AZStd::optional diff --git a/Code/Tools/AssetProcessor/Platform/Mac/main_dummy.cpp b/Code/Tools/AssetProcessor/Platform/Mac/main_dummy.cpp deleted file mode 100644 index 3eed37e555..0000000000 --- a/Code/Tools/AssetProcessor/Platform/Mac/main_dummy.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include -#include -#include -#include - -#include - -int main(int argc, char* argv[]) -{ - // Create a ComponentApplication to initialize the AZ::SystemAllocator and initialize the SettingsRegistry - AZ::ComponentApplication::Descriptor desc; - AZ::ComponentApplication application; - application.Create(desc); - - AZStd::vector envVars; - - const char* homePath = std::getenv("HOME"); - envVars.push_back(AZStd::string::format("HOME=%s", homePath)); - - if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) - { - const char* dyldLibPathOrig = std::getenv("DYLD_LIBRARY_PATH"); - AZStd::string dyldSearchPath = AZStd::string::format("DYLD_LIBRARY_PATH=%s", dyldLibPathOrig); - if (AZ::IO::FixedMaxPath projectModulePath; - settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath)) - { - dyldSearchPath.append(":"); - dyldSearchPath.append(projectModulePath.c_str()); - } - - if (AZ::IO::FixedMaxPath installedBinariesFolder; - settingsRegistry->Get(installedBinariesFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) - { - if (AZ::IO::FixedMaxPath engineRootFolder; - settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder)) - { - installedBinariesFolder = engineRootFolder / installedBinariesFolder; - dyldSearchPath.append(":"); - dyldSearchPath.append(installedBinariesFolder.c_str()); - } - } - envVars.push_back(dyldSearchPath); - } - - AZStd::string commandArgs; - for (int i = 1; i < argc; i++) - { - commandArgs.append(argv[i]); - commandArgs.append(" "); - } - - AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - AZ::IO::Path processPath{ AZ::IO::PathView(AZ::Utils::GetExecutableDirectory()) }; - processPath /= "AssetProcessor"; - processLaunchInfo.m_processExecutableString = AZStd::move(processPath.Native()); - processLaunchInfo.m_commandlineParameters = commandArgs; - processLaunchInfo.m_environmentVariables = &envVars; - processLaunchInfo.m_showWindow = true; - - AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); - - application.Destroy(); - - return 0; -} - diff --git a/Code/Tools/BundleLauncher/CMakeLists.txt b/Code/Tools/BundleLauncher/CMakeLists.txt new file mode 100644 index 0000000000..812cb21099 --- /dev/null +++ b/Code/Tools/BundleLauncher/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# 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 +# +# + +# This is the launcher that will be used by the O3DE_SDK.app bundle +# generated by the cmake install process for Mac. +if(NOT ${PAL_PLATFORM_NAME} STREQUAL Mac) + return() +endif() + +ly_add_target( + NAME O3DE_SDK EXECUTABLE + NAMESPACE AZ + FILES_CMAKE + O3DE_SDK_files.cmake + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzFramework +) diff --git a/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp b/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp new file mode 100644 index 0000000000..0c362ac829 --- /dev/null +++ b/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +int main(int argc, char* argv[]) +{ + // We need to pass in the engine path since we won't be able to find it by searching upwards. + // We can't use any containers that use our custom allocator till after the call to ComponentApplication::Create() + AZ::IO::FixedMaxPath processPath = AZ::Utils::GetExecutableDirectory(); + AZ::IO::FixedMaxPath enginePath = (processPath / "../Engine").LexicallyNormal(); + auto enginePathParam = AZ::SettingsRegistryInterface::FixedValueString::format(R"(--engine-path="%s")", enginePath.c_str()); + // Uses the fixed_vector deduction guide to determine the type is AZStd::fixed_vector + AZStd::fixed_vector commandLineParams{ processPath.Native().data(), enginePathParam.data() }; + + + // Create a ComponentApplication to initialize the AZ::SystemAllocator and initialize the SettingsRegistry + AZ::ComponentApplication application(static_cast(commandLineParams.size()), commandLineParams.data()); + application.Create(AZ::ComponentApplication::Descriptor()); + + AZ::IO::FixedMaxPath installedBinariesFolder; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + if (settingsRegistry->Get(installedBinariesFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) + { + installedBinariesFolder = enginePath / installedBinariesFolder; + } + } + + AZ::IO::FixedMaxPath shellPath = "/bin/sh"; + AZStd::string parameters = AZStd::string::format("-c \"export LY_CMAKE_PATH=/usr/local/bin && \"%s/python/get_python.sh\"\"", enginePath.c_str()); + AzFramework::ProcessLauncher::ProcessLaunchInfo shellProcessLaunch; + shellProcessLaunch.m_processExecutableString = AZStd::move(shellPath.Native()); + shellProcessLaunch.m_commandlineParameters = parameters; + shellProcessLaunch.m_showWindow = true; + shellProcessLaunch.m_workingDirectory = enginePath.String(); + AZStd::unique_ptr shellProcess(AzFramework::ProcessWatcher::LaunchProcess(shellProcessLaunch, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE)); + shellProcess->WaitForProcessToExit(120); + shellProcess.reset(); + + AZ::IO::FixedMaxPath projectManagerPath = installedBinariesFolder/"o3de.app"/"Contents"/"MacOS"/"o3de"; + AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; + processLaunchInfo.m_processExecutableString = AZStd::move(projectManagerPath.Native()); + processLaunchInfo.m_showWindow = true; + AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); + + application.Destroy(); + + return 0; +} + diff --git a/Code/Tools/BundleLauncher/O3DE_SDK_files.cmake b/Code/Tools/BundleLauncher/O3DE_SDK_files.cmake new file mode 100644 index 0000000000..04989c9df2 --- /dev/null +++ b/Code/Tools/BundleLauncher/O3DE_SDK_files.cmake @@ -0,0 +1,11 @@ +# +# 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 + O3DE_SDK_Launcher.cpp +) diff --git a/Code/Tools/BundleLauncher/info.plist b/Code/Tools/BundleLauncher/info.plist new file mode 100644 index 0000000000..5b3dd43b37 --- /dev/null +++ b/Code/Tools/BundleLauncher/info.plist @@ -0,0 +1,18 @@ + + + + + CFBundleExecutable + O3DE_SDK + CFBundleIdentifier + org.O3DE.O3DE_SDK + CFBundlePackageType + APPL + CFBundleSignature + ???? + NSHumanReadableCopyright + Copyright (c) Contributors to the Open 3D Engine Project. + NSPrincipalClass + NSApplication + + diff --git a/Code/Tools/CMakeLists.txt b/Code/Tools/CMakeLists.txt index 66c43e53e5..8107089433 100644 --- a/Code/Tools/CMakeLists.txt +++ b/Code/Tools/CMakeLists.txt @@ -20,3 +20,4 @@ add_subdirectory(GridHub) add_subdirectory(Standalone) add_subdirectory(TestImpactFramework) add_subdirectory(ProjectManager) +add_subdirectory(BundleLauncher) diff --git a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac.cmake b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac.cmake index 7a325ca97e..5cd1fb5a22 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac.cmake +++ b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac.cmake @@ -5,3 +5,4 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT # # + diff --git a/Code/Tools/ProjectManager/Resources/Delete.svg b/Code/Tools/ProjectManager/Resources/Delete.svg new file mode 100644 index 0000000000..f932c71544 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Delete.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Code/Tools/ProjectManager/Resources/Edit.svg b/Code/Tools/ProjectManager/Resources/Edit.svg new file mode 100644 index 0000000000..3ee9bbbfae --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Edit.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc index 2e93e9eca9..30bcc1ace5 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qrc +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qrc @@ -35,5 +35,8 @@ Backgrounds/DefaultBackground.jpg Backgrounds/FtueBackground.jpg FeatureTagClose.svg + Refresh.svg + Edit.svg + Delete.svg diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 17c5077d83..30117d6636 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -518,3 +518,82 @@ QProgressBar::chunk { font-size: 12px; font-weight: 600; } + +/************** Engine **************/ + +#engineTab::tab-bar { + left: 60px; +} + +#engineTabBar::tab { + height: 50px; + background-color: transparent; + font-weight: 400; + font-size: 18px; + min-width: 160px; +} + +#engineTabBar::tab:selected { + border-bottom: 3px solid #94D2FF; + color: #94D2FF; + font-weight: 600; +} +#engineTabBar::tab:hover { + color: #94D2FF; + font-weight: 600; +} +#engineTabBar::tab:pressed { + color: #66bcfa; +} + +#engineTopFrame { + background-color:#1E252F; +} + +/************** Gem Repo **************/ + +#gemRepoHeaderLabel { + font-size: 12px; +} + +#gemRepoHeaderRefreshButton { + background-color: transparent; + qproperty-flat: true; + qproperty-iconSize: 14px; +} + +#gemRepoHeaderAddButton { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #888888, stop: 1.0 #555555); + qproperty-flat: true; + margin-right:30px; + min-width:120px; + max-width:120px; + min-height:24px; + max-height:24px; + border-radius: 3px; + text-align:center; + font-size:12px; + font-weight:600; +} +#gemRepoHeaderAddButton:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #999999, stop: 1.0 #666666); +} +#gemRepoHeaderAddButton:pressed { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #555555, stop: 1.0 #777777); +} + +#gemRepoHeaderTable { + background-color: transparent; + max-height: 30px; +} + +#gemRepoListHeader { + background-color: transparent; +} + +#gemRepoInspector { + background: #444444; +} diff --git a/Code/Tools/ProjectManager/Resources/Refresh.svg b/Code/Tools/ProjectManager/Resources/Refresh.svg new file mode 100644 index 0000000000..80cc892c68 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Refresh.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp new file mode 100644 index 0000000000..c78a9426db --- /dev/null +++ b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +#include +#include +#include + +namespace O3DE::ProjectManager +{ + EngineScreenCtrl::EngineScreenCtrl(QWidget* parent) + : ScreenWidget(parent) + { + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setContentsMargins(0, 0, 0, 0); + + QFrame* topBarFrameWidget = new QFrame(this); + topBarFrameWidget->setObjectName("engineTopFrame"); + QHBoxLayout* topBarHLayout = new QHBoxLayout(); + topBarHLayout->setContentsMargins(0, 0, 0, 0); + + topBarFrameWidget->setLayout(topBarHLayout); + + QTabWidget* tabWidget = new QTabWidget(); + tabWidget->setObjectName("engineTab"); + tabWidget->tabBar()->setObjectName("engineTabBar"); + tabWidget->tabBar()->setFocusPolicy(Qt::TabFocus); + + m_engineSettingsScreen = new EngineSettingsScreen(); + m_gemRepoScreen = new GemRepoScreen(); + + tabWidget->addTab(m_engineSettingsScreen, tr("General")); + tabWidget->addTab(m_gemRepoScreen, tr("Gem Repositories")); + topBarHLayout->addWidget(tabWidget); + + vLayout->addWidget(topBarFrameWidget); + + setLayout(vLayout); + } + + ProjectManagerScreen EngineScreenCtrl::GetScreenEnum() + { + return ProjectManagerScreen::UpdateProject; + } + + QString EngineScreenCtrl::GetTabText() + { + return tr("Engine"); + } + + bool EngineScreenCtrl::IsTab() + { + return true; + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h new file mode 100644 index 0000000000..9e799f13e7 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/EngineScreenCtrl.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#endif + +namespace O3DE::ProjectManager +{ + QT_FORWARD_DECLARE_CLASS(EngineSettingsScreen) + QT_FORWARD_DECLARE_CLASS(GemRepoScreen) + + class EngineScreenCtrl + : public ScreenWidget + { + public: + explicit EngineScreenCtrl(QWidget* parent = nullptr); + ~EngineScreenCtrl() = default; + ProjectManagerScreen GetScreenEnum() override; + + QString GetTabText() override; + bool IsTab() override; + + EngineSettingsScreen* m_engineSettingsScreen = nullptr; + GemRepoScreen* m_gemRepoScreen = nullptr; + }; + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp index 0c24aac9f1..dec1c9a257 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.cpp @@ -7,15 +7,16 @@ */ #include -#include -#include -#include -#include #include #include #include #include +#include +#include +#include +#include + namespace O3DE::ProjectManager { EngineSettingsScreen::EngineSettingsScreen(QWidget* parent) @@ -78,16 +79,6 @@ namespace O3DE::ProjectManager return ProjectManagerScreen::EngineSettings; } - QString EngineSettingsScreen::GetTabText() - { - return tr("Engine"); - } - - bool EngineSettingsScreen::IsTab() - { - return true; - } - void EngineSettingsScreen::OnTextChanged() { // save engine settings diff --git a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h index 9d212c44b1..2f16400405 100644 --- a/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/EngineSettingsScreen.h @@ -24,8 +24,6 @@ namespace O3DE::ProjectManager ~EngineSettingsScreen() = default; ProjectManagerScreen GetScreenEnum() override; - QString GetTabText() override; - bool IsTab() override; protected slots: void OnTextChanged(); diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.cpp new file mode 100644 index 0000000000..3e524d8ec8 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.cpp @@ -0,0 +1,32 @@ +/* + * 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 + +namespace O3DE::ProjectManager +{ + GemRepoInfo::GemRepoInfo( + const QString& name, const QString& creator, const QString& summary, const QDateTime& lastUpdated, bool isEnabled = true) + : m_name(name) + , m_creator(creator) + , m_summary(summary) + , m_lastUpdated(lastUpdated) + , m_isEnabled(isEnabled) + { + } + + bool GemRepoInfo::IsValid() const + { + return !m_name.isEmpty(); + } + + bool GemRepoInfo::operator<(const GemRepoInfo& gemRepoInfo) const + { + return (m_lastUpdated < gemRepoInfo.m_lastUpdated); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h new file mode 100644 index 0000000000..6f4f828951 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInfo.h @@ -0,0 +1,37 @@ +/* + * 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 + +#if !defined(Q_MOC_RUN) +#include +#include +#endif + +namespace O3DE::ProjectManager +{ + class GemRepoInfo + { + public: + GemRepoInfo() = default; + GemRepoInfo(const QString& name, const QString& creator, const QString& summary, const QDateTime& lastUpdated, bool isEnabled); + + bool IsValid() const; + + bool operator<(const GemRepoInfo& gemRepoInfo) const; + + QString m_path; + QString m_name = "Unknown Gem Repo Name"; + QString m_creator = "Unknown Creator"; + bool m_isEnabled = false; //! Is the repo currently enabled for this engine? + QString m_summary = "No summary provided."; + QString m_directoryLink; + QString m_repoLink; + QDateTime m_lastUpdated; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp new file mode 100644 index 0000000000..88ccee2636 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp @@ -0,0 +1,222 @@ +/* + * 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 + +namespace O3DE::ProjectManager +{ + GemRepoItemDelegate::GemRepoItemDelegate(QAbstractItemModel* model, QObject* parent) + : QStyledItemDelegate(parent) + , m_model(model) + { + m_refreshIcon = QIcon(":/Refresh.svg").pixmap(s_refreshIconSize, s_refreshIconSize); + m_editIcon = QIcon(":/Edit.svg").pixmap(s_iconSize, s_iconSize); + m_deleteIcon = QIcon(":/Delete.svg").pixmap(s_iconSize, s_iconSize); + } + + void GemRepoItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const + { + if (!modelIndex.isValid()) + { + return; + } + + QStyleOptionViewItem options(option); + initStyleOption(&options, modelIndex); + + painter->setRenderHint(QPainter::Antialiasing); + + QRect fullRect, itemRect, contentRect; + CalcRects(options, fullRect, itemRect, contentRect); + QRect buttonRect = CalcButtonRect(contentRect); + + QFont standardFont(options.font); + standardFont.setPixelSize(static_cast(s_fontSize)); + QFontMetrics standardFontMetrics(standardFont); + + painter->save(); + painter->setClipping(true); + painter->setClipRect(fullRect); + painter->setFont(standardFont); + painter->setPen(m_textColor); + + // Draw background + painter->fillRect(fullRect, m_backgroundColor); + + // Draw item background + const QColor itemBackgroundColor = options.state & QStyle::State_MouseOver ? m_itemBackgroundColor.lighter(120) : m_itemBackgroundColor; + painter->fillRect(itemRect, itemBackgroundColor); + + // Draw border + if (options.state & QStyle::State_Selected) + { + painter->save(); + QPen borderPen(m_borderColor); + borderPen.setWidth(s_borderWidth); + painter->setPen(borderPen); + painter->drawRect(itemRect); + + painter->restore(); + } + + // Repo enabled + DrawButton(painter, buttonRect, modelIndex); + + // Repo name + QString repoName = GemRepoModel::GetName(modelIndex); + repoName = QFontMetrics(standardFont).elidedText(repoName, Qt::TextElideMode::ElideRight, s_nameMaxWidth); + + QRect repoNameRect = GetTextRect(standardFont, repoName, s_fontSize); + int currentHorizontalOffset = buttonRect.left() + s_buttonWidth + s_buttonSpacing; + repoNameRect.moveTo(currentHorizontalOffset, contentRect.center().y() - repoNameRect.height() / 2); + repoNameRect = painter->boundingRect(repoNameRect, Qt::TextSingleLine, repoName); + + painter->drawText(repoNameRect, Qt::TextSingleLine, repoName); + + // Rem repo creator + QString repoCreator = GemRepoModel::GetCreator(modelIndex); + repoCreator = standardFontMetrics.elidedText(repoCreator, Qt::TextElideMode::ElideRight, s_creatorMaxWidth); + + QRect repoCreatorRect = GetTextRect(standardFont, repoCreator, s_fontSize); + currentHorizontalOffset += s_nameMaxWidth + s_contentSpacing; + repoCreatorRect.moveTo(currentHorizontalOffset, contentRect.center().y() - repoCreatorRect.height() / 2); + repoCreatorRect = painter->boundingRect(repoCreatorRect, Qt::TextSingleLine, repoCreator); + + painter->drawText(repoCreatorRect, Qt::TextSingleLine, repoCreator); + + // Repo update + QString repoUpdatedDate = GemRepoModel::GetLastUpdated(modelIndex).toString("dd/MM/yyyy hh:mmap"); + repoUpdatedDate = standardFontMetrics.elidedText(repoUpdatedDate, Qt::TextElideMode::ElideRight, s_updatedMaxWidth); + + QRect repoUpdatedDateRect = GetTextRect(standardFont, repoUpdatedDate, s_fontSize); + currentHorizontalOffset += s_creatorMaxWidth + s_contentSpacing; + repoUpdatedDateRect.moveTo(currentHorizontalOffset, contentRect.center().y() - repoUpdatedDateRect.height() / 2); + repoUpdatedDateRect = painter->boundingRect(repoUpdatedDateRect, Qt::TextSingleLine, repoUpdatedDate); + + painter->drawText(repoUpdatedDateRect, Qt::TextSingleLine, repoUpdatedDate); + + // Draw refresh button + painter->drawPixmap( + repoUpdatedDateRect.left() + repoUpdatedDateRect.width() + s_refreshIconSpacing, + contentRect.center().y() - s_refreshIconSize / 3, // Dividing size by 3 centers much better + m_refreshIcon); + + if (options.state & QStyle::State_MouseOver) + { + DrawEditButtons(painter, contentRect); + } + + painter->restore(); + } + + QSize GemRepoItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const + { + QStyleOptionViewItem options(option); + initStyleOption(&options, modelIndex); + + int marginsHorizontal = s_itemMargins.left() + s_itemMargins.right() + s_contentMargins.left() + s_contentMargins.right(); + return QSize(marginsHorizontal + s_buttonWidth + s_buttonSpacing + s_nameMaxWidth + s_creatorMaxWidth + s_updatedMaxWidth + s_contentSpacing * 3, s_height); + } + + bool GemRepoItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) + { + if (!modelIndex.isValid()) + { + return false; + } + + if (event->type() == QEvent::KeyPress) + { + auto keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Space) + { + const bool isAdded = GemRepoModel::IsEnabled(modelIndex); + GemRepoModel::SetEnabled(*model, modelIndex, !isAdded); + return true; + } + } + + if (event->type() == QEvent::MouseButtonPress) + { + QMouseEvent* mouseEvent = static_cast(event); + + QRect fullRect, itemRect, contentRect; + CalcRects(option, fullRect, itemRect, contentRect); + const QRect buttonRect = CalcButtonRect(contentRect); + + if (buttonRect.contains(mouseEvent->pos())) + { + const bool isAdded = GemRepoModel::IsEnabled(modelIndex); + GemRepoModel::SetEnabled(*model, modelIndex, !isAdded); + return true; + } + } + + return QStyledItemDelegate::editorEvent(event, model, option, modelIndex); + } + + void GemRepoItemDelegate::CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const + { + outFullRect = QRect(option.rect); + outItemRect = QRect(outFullRect.adjusted(s_itemMargins.left(), s_itemMargins.top(), -s_itemMargins.right(), -s_itemMargins.bottom())); + outContentRect = QRect(outItemRect.adjusted(s_contentMargins.left(), s_contentMargins.top(), -s_contentMargins.right(), -s_contentMargins.bottom())); + } + + QRect GemRepoItemDelegate::GetTextRect(QFont& font, const QString& text, qreal fontSize) const + { + font.setPixelSize(static_cast(fontSize)); + return QFontMetrics(font).boundingRect(text); + } + + QRect GemRepoItemDelegate::CalcButtonRect(const QRect& contentRect) const + { + const QPoint topLeft = QPoint(contentRect.left(), contentRect.top() + contentRect.height() / 2 - s_buttonHeight / 2); + const QSize size = QSize(s_buttonWidth, s_buttonHeight); + return QRect(topLeft, size); + } + + void GemRepoItemDelegate::DrawButton(QPainter* painter, const QRect& buttonRect, const QModelIndex& modelIndex) const + { + painter->save(); + QPoint circleCenter; + + const bool isEnabled = GemRepoModel::IsEnabled(modelIndex); + if (isEnabled) + { + painter->setBrush(m_buttonEnabledColor); + painter->setPen(m_buttonEnabledColor); + + circleCenter = buttonRect.center() + QPoint(buttonRect.width() / 2 - s_buttonBorderRadius + 1, 1); + } + else + { + circleCenter = buttonRect.center() + QPoint(-buttonRect.width() / 2 + s_buttonBorderRadius + 1, 1); + } + + // Rounded rect + painter->drawRoundedRect(buttonRect, s_buttonBorderRadius, s_buttonBorderRadius); + + // Circle + painter->setBrush(m_textColor); + painter->drawEllipse(circleCenter, s_buttonCircleRadius, s_buttonCircleRadius); + + painter->restore(); + } + + void GemRepoItemDelegate::DrawEditButtons(QPainter* painter, const QRect& contentRect) const + { + painter->drawPixmap(contentRect.right() - s_iconSize * 2 - s_iconSpacing, contentRect.center().y() - s_iconSize / 2, m_editIcon); + painter->drawPixmap(contentRect.right() - s_iconSize, contentRect.center().y() - s_iconSize / 2, m_deleteIcon); + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.h new file mode 100644 index 0000000000..08d1fdffae --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.h @@ -0,0 +1,82 @@ +/* + * 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 + +#if !defined(Q_MOC_RUN) +#include +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QAbstractItemModel) +QT_FORWARD_DECLARE_CLASS(QEvent) + +namespace O3DE::ProjectManager +{ + class GemRepoItemDelegate + : public QStyledItemDelegate + { + Q_OBJECT // AUTOMOC + + public: + explicit GemRepoItemDelegate(QAbstractItemModel* model, QObject* parent = nullptr); + ~GemRepoItemDelegate() = default; + + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override; + bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override; + + // Colors + const QColor m_textColor = QColor("#FFFFFF"); + const QColor m_backgroundColor = QColor("#333333"); // Outside of the actual repo item + const QColor m_itemBackgroundColor = QColor("#404040"); // Background color of the repo item + const QColor m_borderColor = QColor("#1E70EB"); + const QColor m_buttonEnabledColor = QColor("#1E70EB"); + + // Item + inline constexpr static int s_height = 72; // Repo item total height + inline constexpr static qreal s_fontSize = 12.0; + + // Margin and borders + inline constexpr static QMargins s_itemMargins = QMargins(/*left=*/0, /*top=*/8, /*right=*/60, /*bottom=*/8); // Item border distances + inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/20, /*right=*/20, /*bottom=*/20); // Distances of the elements within an item to the item borders + inline constexpr static int s_borderWidth = 4; + + // Content + inline constexpr static int s_contentSpacing = 5; + inline constexpr static int s_nameMaxWidth = 145; + inline constexpr static int s_creatorMaxWidth = 115; + inline constexpr static int s_updatedMaxWidth = 125; + + // Button + inline constexpr static int s_buttonWidth = 32; + inline constexpr static int s_buttonHeight = 16; + inline constexpr static int s_buttonBorderRadius = 8; + inline constexpr static int s_buttonCircleRadius = s_buttonBorderRadius - 2; + inline constexpr static int s_buttonSpacing = 20; + + // Icon + inline constexpr static int s_iconSize = 24; + inline constexpr static int s_iconSpacing = 16; + inline constexpr static int s_refreshIconSize = 14; + inline constexpr static int s_refreshIconSpacing = 10; + + protected: + void CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const; + QRect GetTextRect(QFont& font, const QString& text, qreal fontSize) const; + QRect CalcButtonRect(const QRect& contentRect) const; + void DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const; + void DrawEditButtons(QPainter* painter, const QRect& contentRect) const; + + QAbstractItemModel* m_model = nullptr; + + QPixmap m_refreshIcon; + QPixmap m_editIcon; + QPixmap m_deleteIcon; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp new file mode 100644 index 0000000000..519d52cb35 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +namespace O3DE::ProjectManager +{ + GemRepoListView::GemRepoListView(QAbstractItemModel* model, QWidget* parent) + : QListView(parent) + { + setObjectName("gemRepoListView"); + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + + setModel(model); + setItemDelegate(new GemRepoItemDelegate(model, this)); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h new file mode 100644 index 0000000000..0fd5d5c180 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h @@ -0,0 +1,28 @@ +/* + * 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 + +#if !defined(Q_MOC_RUN) +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QAbstractItemModel) + +namespace O3DE::ProjectManager +{ + class GemRepoListView + : public QListView + { + Q_OBJECT // AUTOMOC + + public: + explicit GemRepoListView(QAbstractItemModel* model, QWidget* parent = nullptr); + ~GemRepoListView() = default; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp new file mode 100644 index 0000000000..7a42c135e9 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.cpp @@ -0,0 +1,94 @@ +/* + * 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 O3DE::ProjectManager +{ + GemRepoModel::GemRepoModel(QObject* parent) + : QStandardItemModel(parent) + { + m_selectionModel = new QItemSelectionModel(this, parent); + } + + QItemSelectionModel* GemRepoModel::GetSelectionModel() const + { + return m_selectionModel; + } + + void GemRepoModel::AddGemRepo(const GemRepoInfo& gemRepoInfo) + { + QStandardItem* item = new QStandardItem(); + + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + + item->setData(gemRepoInfo.m_name, RoleName); + item->setData(gemRepoInfo.m_creator, RoleCreator); + item->setData(gemRepoInfo.m_summary, RoleSummary); + item->setData(gemRepoInfo.m_isEnabled, RoleIsEnabled); + item->setData(gemRepoInfo.m_directoryLink, RoleDirectoryLink); + item->setData(gemRepoInfo.m_repoLink, RoleRepoLink); + item->setData(gemRepoInfo.m_lastUpdated, RoleLastUpdated); + item->setData(gemRepoInfo.m_path, RolePath); + + appendRow(item); + } + + void GemRepoModel::Clear() + { + clear(); + } + + QString GemRepoModel::GetName(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleName).toString(); + } + + QString GemRepoModel::GetCreator(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleCreator).toString(); + } + + QString GemRepoModel::GetSummary(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleSummary).toString(); + } + + QString GemRepoModel::GetDirectoryLink(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleDirectoryLink).toString(); + } + + QString GemRepoModel::GetRepoLink(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleRepoLink).toString(); + } + + QDateTime GemRepoModel::GetLastUpdated(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleLastUpdated).toDateTime(); + } + + QString GemRepoModel::GetPath(const QModelIndex& modelIndex) + { + return modelIndex.data(RolePath).toString(); + } + + bool GemRepoModel::IsEnabled(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleIsEnabled).toBool(); + } + + void GemRepoModel::SetEnabled(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isEnabled) + { + model.setData(modelIndex, isEnabled, RoleIsEnabled); + } + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h new file mode 100644 index 0000000000..2f1537d339 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h @@ -0,0 +1,58 @@ +/* + * 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 + +#if !defined(Q_MOC_RUN) +#include +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QItemSelectionModel) + +namespace O3DE::ProjectManager +{ + class GemRepoModel + : public QStandardItemModel + { + Q_OBJECT // AUTOMOC + + public: + explicit GemRepoModel(QObject* parent = nullptr); + QItemSelectionModel* GetSelectionModel() const; + + void AddGemRepo(const GemRepoInfo& gemInfo); + void Clear(); + + static QString GetName(const QModelIndex& modelIndex); + static QString GetCreator(const QModelIndex& modelIndex); + static QString GetSummary(const QModelIndex& modelIndex); + static QString GetDirectoryLink(const QModelIndex& modelIndex); + static QString GetRepoLink(const QModelIndex& modelIndex); + static QDateTime GetLastUpdated(const QModelIndex& modelIndex); + static QString GetPath(const QModelIndex& modelIndex); + + static bool IsEnabled(const QModelIndex& modelIndex); + static void SetEnabled(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isEnabled); + + private: + enum UserRole + { + RoleName = Qt::UserRole, + RoleCreator, + RoleSummary, + RoleIsEnabled, + RoleDirectoryLink, + RoleRepoLink, + RoleLastUpdated, + RolePath + }; + + QItemSelectionModel* m_selectionModel = nullptr; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp new file mode 100644 index 0000000000..82de53a0d0 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.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 +#include +#include +#include +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + GemRepoScreen::GemRepoScreen(QWidget* parent) + : ScreenWidget(parent) + { + m_gemRepoModel = new GemRepoModel(this); + + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setMargin(0); + vLayout->setSpacing(0); + setLayout(vLayout); + + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setMargin(0); + hLayout->setSpacing(0); + vLayout->addLayout(hLayout); + + hLayout->addSpacing(60); + + m_gemRepoInspector = new QFrame(this); + m_gemRepoInspector->setObjectName(tr("gemRepoInspector")); + m_gemRepoInspector->setFixedWidth(240); + + QVBoxLayout* middleVLayout = new QVBoxLayout(); + middleVLayout->setMargin(0); + middleVLayout->setSpacing(0); + + middleVLayout->addSpacing(30); + + QHBoxLayout* topMiddleHLayout = new QHBoxLayout(); + topMiddleHLayout->setMargin(0); + topMiddleHLayout->setSpacing(0); + + m_lastAllUpdateLabel = new QLabel(tr("Last Updated: Never"), this); + m_lastAllUpdateLabel->setObjectName("gemRepoHeaderLabel"); + topMiddleHLayout->addWidget(m_lastAllUpdateLabel); + + topMiddleHLayout->addSpacing(20); + + m_AllUpdateButton = new QPushButton(QIcon(":/Refresh.svg"), tr("Update All"), this); + m_AllUpdateButton->setObjectName("gemRepoHeaderRefreshButton"); + topMiddleHLayout->addWidget(m_AllUpdateButton); + + topMiddleHLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + m_AddRepoButton = new QPushButton(tr("Add Repository"), this); + m_AddRepoButton->setObjectName("gemRepoHeaderAddButton"); + topMiddleHLayout->addWidget(m_AddRepoButton); + + middleVLayout->addLayout(topMiddleHLayout); + + middleVLayout->addSpacing(30); + + // Create a QTableWidget just for its header + // Using a seperate model allows the setup of a header exactly as needed + m_gemRepoHeaderTable = new QTableWidget(this); + m_gemRepoHeaderTable->setObjectName("gemRepoHeaderTable"); + m_gemRepoListHeader = m_gemRepoHeaderTable->horizontalHeader(); + m_gemRepoListHeader->setObjectName("gemRepoListHeader"); + m_gemRepoListHeader->setSectionResizeMode(QHeaderView::ResizeMode::Fixed); + + // Insert columns so the header labels will show up + m_gemRepoHeaderTable->insertColumn(0); + m_gemRepoHeaderTable->insertColumn(1); + m_gemRepoHeaderTable->insertColumn(2); + m_gemRepoHeaderTable->insertColumn(3); + m_gemRepoHeaderTable->setHorizontalHeaderLabels({ tr("Enabled"), tr("Repository Name"), tr("Creator"), tr("Updated") }); + + const int headerExtraMargin = 10; + m_gemRepoListHeader->resizeSection(0, GemRepoItemDelegate::s_buttonWidth + GemRepoItemDelegate::s_buttonSpacing - 3); + m_gemRepoListHeader->resizeSection(1, GemRepoItemDelegate::s_nameMaxWidth + GemRepoItemDelegate::s_contentSpacing - headerExtraMargin); + m_gemRepoListHeader->resizeSection(2, GemRepoItemDelegate::s_creatorMaxWidth + GemRepoItemDelegate::s_contentSpacing - headerExtraMargin); + m_gemRepoListHeader->resizeSection(3, GemRepoItemDelegate::s_updatedMaxWidth + GemRepoItemDelegate::s_contentSpacing - headerExtraMargin); + + // Required to set stylesheet in code as it will not be respected if set in qss + m_gemRepoHeaderTable->horizontalHeader()->setStyleSheet("QHeaderView::section { background-color:transparent; color:white; font-size:12px; text-align:left; border-style:none; }"); + middleVLayout->addWidget(m_gemRepoHeaderTable); + + m_gemRepoListView = new GemRepoListView(m_gemRepoModel, this); + middleVLayout->addWidget(m_gemRepoListView); + + hLayout->addLayout(middleVLayout); + hLayout->addWidget(m_gemRepoInspector); + + Reinit(); + } + + void GemRepoScreen::Reinit() + { + m_gemRepoModel->clear(); + FillModel(); + + // Select the first entry after everything got correctly sized + QTimer::singleShot(200, [=]{ + QModelIndex firstModelIndex = m_gemRepoListView->model()->index(0,0); + m_gemRepoListView->selectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect); + }); + } + + void GemRepoScreen::FillModel() + { + AZ::Outcome, AZStd::string> allGemRepoInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoInfos(); + if (allGemRepoInfosResult.IsSuccess()) + { + // Add all available repos to the model + const QVector allGemRepoInfos = allGemRepoInfosResult.GetValue(); + for (const GemRepoInfo& gemRepoInfo : allGemRepoInfos) + { + m_gemRepoModel->AddGemRepo(gemRepoInfo); + } + } + else + { + QMessageBox::critical(this, tr("Operation failed"), QString("Cannot retrieve gem repos for engine.\n\nError:\n%2").arg(allGemRepoInfosResult.GetError().c_str())); + } + } + + ProjectManagerScreen GemRepoScreen::GetScreenEnum() + { + return ProjectManagerScreen::GemRepos; + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h new file mode 100644 index 0000000000..b5316db84f --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h @@ -0,0 +1,50 @@ +/* + * 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 + +#if !defined(Q_MOC_RUN) +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QLabel) +QT_FORWARD_DECLARE_CLASS(QPushButton) +QT_FORWARD_DECLARE_CLASS(QHeaderView) +QT_FORWARD_DECLARE_CLASS(QTableWidget) + +namespace O3DE::ProjectManager +{ + QT_FORWARD_DECLARE_CLASS(GemRepoListView) + QT_FORWARD_DECLARE_CLASS(GemRepoModel) + + class GemRepoScreen + : public ScreenWidget + { + public: + explicit GemRepoScreen(QWidget* parent = nullptr); + ~GemRepoScreen() = default; + ProjectManagerScreen GetScreenEnum() override; + + void Reinit(); + + GemRepoModel* GetGemRepoModel() const { return m_gemRepoModel; } + + private: + void FillModel(); + + QTableWidget* m_gemRepoHeaderTable = nullptr; + QHeaderView* m_gemRepoListHeader = nullptr; + GemRepoListView* m_gemRepoListView = nullptr; + QFrame* m_gemRepoInspector = nullptr; + GemRepoModel* m_gemRepoModel = nullptr; + + QLabel* m_lastAllUpdateLabel; + QPushButton* m_AllUpdateButton; + QPushButton* m_AddRepoButton; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp index a723ccb917..d2f2d969f1 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp @@ -22,7 +22,7 @@ namespace O3DE::ProjectManager QVector screenEnums = { ProjectManagerScreen::Projects, - ProjectManagerScreen::EngineSettings, + ProjectManagerScreen::Engine, ProjectManagerScreen::CreateProject, ProjectManagerScreen::UpdateProject }; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 18901946f3..6f8ff9abce 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -912,4 +912,45 @@ namespace O3DE::ProjectManager return AZ::Success(AZStd::move(templates)); } } + + GemRepoInfo PythonBindings::GemRepoInfoFromPath(pybind11::handle path, pybind11::handle pyEnginePath) + { + /* Placeholder Logic */ + (void)path; + (void)pyEnginePath; + + return GemRepoInfo(); + } + +//#define MOCK_GEM_REPO_INFO true + + AZ::Outcome, AZStd::string> PythonBindings::GetAllGemRepoInfos() + { + QVector gemRepos; + +#ifndef MOCK_GEM_REPO_INFO + auto result = ExecuteWithLockErrorHandling( + [&] + { + /* Placeholder Logic, o3de scripts need method added + * + for (auto path : m_manifest.attr("get_gem_repos")()) + { + gemRepos.push_back(GemRepoInfoFromPath(path, pybind11::none())); + } + * + */ + }); + if (!result.IsSuccess()) + { + return AZ::Failure(result.GetError().c_str()); + } +#else + gemRepos.push_back(GemRepoInfo("JohnCreates", "John Smith", "", QDateTime(QDate(2021, 8, 31), QTime(11, 57)), true)); + gemRepos.push_back(GemRepoInfo("JanesGems", "Jane Doe", "", QDateTime(QDate(2021, 9, 10), QTime(18, 23)), false)); +#endif // MOCK_GEM_REPO_INFO + + std::sort(gemRepos.begin(), gemRepos.end()); + return AZ::Success(AZStd::move(gemRepos)); + } } diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.h b/Code/Tools/ProjectManager/Source/PythonBindings.h index 8482ac56e7..3b766c3797 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.h +++ b/Code/Tools/ProjectManager/Source/PythonBindings.h @@ -56,12 +56,16 @@ namespace O3DE::ProjectManager // ProjectTemplate AZ::Outcome> GetProjectTemplates(const QString& projectPath = {}) override; + // Gem Repos + AZ::Outcome, AZStd::string> GetAllGemRepoInfos() override; + private: AZ_DISABLE_COPY_MOVE(PythonBindings); AZ::Outcome ExecuteWithLockErrorHandling(AZStd::function executionCallback); bool ExecuteWithLock(AZStd::function executionCallback); GemInfo GemInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath); + GemRepoInfo GemRepoInfoFromPath(pybind11::handle path, pybind11::handle pyEnginePath); ProjectInfo ProjectInfoFromPath(pybind11::handle path); ProjectTemplateInfo ProjectTemplateInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath); bool RegisterThisEngine(); diff --git a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h index ca14a54630..9fd3002f93 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h +++ b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h @@ -17,6 +17,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -56,14 +57,14 @@ namespace O3DE::ProjectManager /** * Get info about a Gem - * @param path the absolute path to the Gem + * @param projectPath the absolute path to the Gem * @return an outcome with GemInfo on success */ virtual AZ::Outcome GetGemInfo(const QString& path, const QString& projectPath = {}) = 0; /** * Get all available gem infos. This concatenates gems registered by the engine and the project. - * @param path The absolute path to the project. + * @param projectPath The absolute path to the project. * @return A list of gem infos. */ virtual AZ::Outcome, AZStd::string> GetAllGemInfos(const QString& projectPath) = 0; @@ -155,6 +156,14 @@ namespace O3DE::ProjectManager * @return an outcome with ProjectTemplateInfos on success */ virtual AZ::Outcome> GetProjectTemplates(const QString& projectPath = {}) = 0; + + // Gem Repos + + /** + * Get all available gem repo infos. Gathers all repos registered with the engine. + * @return A list of gem repo infos. + */ + virtual AZ::Outcome, AZStd::string> GetAllGemRepoInfos() = 0; }; using PythonBindingsInterface = AZ::Interface; diff --git a/Code/Tools/ProjectManager/Source/ScreenDefs.h b/Code/Tools/ProjectManager/Source/ScreenDefs.h index 97ebe19751..2ced8c08c9 100644 --- a/Code/Tools/ProjectManager/Source/ScreenDefs.h +++ b/Code/Tools/ProjectManager/Source/ScreenDefs.h @@ -23,7 +23,9 @@ namespace O3DE::ProjectManager Projects, UpdateProject, UpdateProjectSettings, - EngineSettings + Engine, + EngineSettings, + GemRepos }; static QHash s_ProjectManagerStringNames = { @@ -34,7 +36,9 @@ namespace O3DE::ProjectManager { "Projects", ProjectManagerScreen::Projects}, { "UpdateProject", ProjectManagerScreen::UpdateProject}, { "UpdateProjectSettings", ProjectManagerScreen::UpdateProjectSettings}, - { "EngineSettings", ProjectManagerScreen::EngineSettings} + { "Engine", ProjectManagerScreen::Engine}, + { "EngineSettings", ProjectManagerScreen::EngineSettings}, + { "GemRepos", ProjectManagerScreen::GemRepos} }; // need to define qHash for ProjectManagerScreen when using scoped enums diff --git a/Code/Tools/ProjectManager/Source/ScreenFactory.cpp b/Code/Tools/ProjectManager/Source/ScreenFactory.cpp index f3bddfdd27..44aa713e6a 100644 --- a/Code/Tools/ProjectManager/Source/ScreenFactory.cpp +++ b/Code/Tools/ProjectManager/Source/ScreenFactory.cpp @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include namespace O3DE::ProjectManager { @@ -41,9 +43,15 @@ namespace O3DE::ProjectManager case (ProjectManagerScreen::UpdateProjectSettings): newScreen = new UpdateProjectSettingsScreen(parent); break; + case (ProjectManagerScreen::Engine): + newScreen = new EngineScreenCtrl(parent); + break; case (ProjectManagerScreen::EngineSettings): newScreen = new EngineSettingsScreen(parent); break; + case (ProjectManagerScreen::GemRepos): + newScreen = new GemRepoScreen(parent); + break; case (ProjectManagerScreen::Empty): default: newScreen = new ScreenWidget(parent); diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index 6d0f392e52..7a336972e0 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -56,6 +56,8 @@ set(FILES Source/ProjectsScreen.cpp Source/ProjectSettingsScreen.h Source/ProjectSettingsScreen.cpp + Source/EngineScreenCtrl.h + Source/EngineScreenCtrl.cpp Source/EngineSettingsScreen.h Source/EngineSettingsScreen.cpp Source/ProjectButtonWidget.h @@ -98,4 +100,14 @@ set(FILES Source/GemCatalog/GemRequirementListView.cpp Source/GemCatalog/GemSortFilterProxyModel.h Source/GemCatalog/GemSortFilterProxyModel.cpp + Source/GemRepo/GemRepoScreen.h + Source/GemRepo/GemRepoScreen.cpp + Source/GemRepo/GemRepoInfo.h + Source/GemRepo/GemRepoInfo.cpp + Source/GemRepo/GemRepoItemDelegate.h + Source/GemRepo/GemRepoItemDelegate.cpp + Source/GemRepo/GemRepoListView.h + Source/GemRepo/GemRepoListView.cpp + Source/GemRepo/GemRepoModel.h + Source/GemRepo/GemRepoModel.cpp ) diff --git a/Gems/Atom/Feature/Common/Code/CMakeLists.txt b/Gems/Atom/Feature/Common/Code/CMakeLists.txt index b8c414dcc6..9c4ffe5b5e 100644 --- a/Gems/Atom/Feature/Common/Code/CMakeLists.txt +++ b/Gems/Atom/Feature/Common/Code/CMakeLists.txt @@ -40,6 +40,8 @@ ly_add_target( Gem::Atom_Feature_Common.Public Gem::ImGui.imguilib #3rdParty::lux_core # AZ_TRAIT_LUXCORE_SUPPORTED is disabled in every platform, Issue #3915 will remove + RUNTIME_DEPENDENCIES + Gem::ImGui.imguilib ) ly_add_target( diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp index 214643505a..2057c596e5 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp @@ -565,7 +565,7 @@ namespace AZ AuxGeomBufferData* AuxGeomDrawQueue::Commit() { - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "AuxGeomDrawQueue: Commit"); + AZ_PROFILE_SCOPE(AzRender, "AuxGeomDrawQueue: Commit"); // get a mutually exclusive lock and then switch to the next buffer, returning a pointer to the current buffer (before the switch) // grab the lock @@ -585,7 +585,7 @@ namespace AZ void AuxGeomDrawQueue::ClearCurrentBufferData() { - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "AuxGeomDrawQueue: ClearCurrentBufferData"); + AZ_PROFILE_SCOPE(AzRender, "AuxGeomDrawQueue: ClearCurrentBufferData"); // no need for mutex here, this function is only called from a function holding a lock AuxGeomBufferData& data = m_buffers[m_currentBufferIndex]; @@ -649,7 +649,7 @@ namespace AZ AZ::u8 width, int32_t viewProjOverrideIndex) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "AuxGeomDrawQueue: DrawPrimitiveWithSharedVerticesCommon"); // grab a mutex lock for the rest of this function so that a commit cannot happen during it and // other threads can't add geometry during it @@ -720,8 +720,7 @@ namespace AZ AZ::u8 width, int32_t viewProjOverrideIndex) { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "AuxGeomDrawQueue: DrawPrimitiveWithSharedVerticesCommon"); + AZ_PROFILE_SCOPE(AzRender, "AuxGeomDrawQueue: DrawPrimitiveWithSharedVerticesCommon"); AZ_Assert(indexCount >= verticesPerPrimitiveType && (indexCount % verticesPerPrimitiveType == 0), "Index count must be at least %d and must be a multiple of %d", diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomFeatureProcessor.cpp index feeb1dedcc..ee6d3ba4a3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomFeatureProcessor.cpp @@ -80,7 +80,7 @@ namespace AZ void AuxGeomFeatureProcessor::Render(const FeatureProcessor::RenderPacket& fpPacket) { - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "AuxGeomFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(AzRender, "AuxGeomFeatureProcessor: Render"); // Get the scene data and switch buffers so that other threads can continue to queue requests AuxGeomBufferData* bufferData = static_cast(m_sceneDrawQueue.get())->Commit(); diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/DynamicPrimitiveProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/DynamicPrimitiveProcessor.cpp index f35809f148..9af6493413 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/DynamicPrimitiveProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/DynamicPrimitiveProcessor.cpp @@ -70,7 +70,7 @@ namespace AZ void DynamicPrimitiveProcessor::PrepareFrame() { - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "DynamicPrimitiveProcessor: PrepareFrame"); + AZ_PROFILE_SCOPE(AzRender, "DynamicPrimitiveProcessor: PrepareFrame"); m_drawPackets.clear(); m_processSrgs.clear(); @@ -88,7 +88,7 @@ namespace AZ void DynamicPrimitiveProcessor::ProcessDynamicPrimitives(const AuxGeomBufferData* bufferData, const RPI::FeatureProcessor::RenderPacket& fpPacket) { - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "DynamicPrimitiveProcessor: ProcessDynamicPrimitives"); + AZ_PROFILE_SCOPE(AzRender, "DynamicPrimitiveProcessor: ProcessDynamicPrimitives"); RHI::DrawPacketBuilder drawPacketBuilder; const DynamicPrimitiveData& srcPrimitives = bufferData->m_primitiveData; diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/FixedShapeProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/FixedShapeProcessor.cpp index 0045311bef..8b5e439ca0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/FixedShapeProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/FixedShapeProcessor.cpp @@ -108,8 +108,8 @@ namespace AZ } void FixedShapeProcessor::PrepareFrame() - { - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "FixedShapeProcessor: PrepareFrame"); + { + AZ_PROFILE_SCOPE(AzRender, "FixedShapeProcessor: PrepareFrame"); m_processSrgs.clear(); m_drawPackets.clear(); @@ -127,8 +127,7 @@ namespace AZ void FixedShapeProcessor::ProcessObjects(const AuxGeomBufferData* bufferData, const RPI::FeatureProcessor::RenderPacket& fpPacket) { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("AuxGeom", "FixedShapeProcessor: ProcessObjects"); + AZ_PROFILE_SCOPE(AzRender, "FixedShapeProcessor: ProcessObjects"); RHI::DrawPacketBuilder drawPacketBuilder; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/CapsuleLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/CapsuleLightFeatureProcessor.cpp index 683295cf5b..849c930afd 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/CapsuleLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/CapsuleLightFeatureProcessor.cpp @@ -102,7 +102,7 @@ namespace AZ void CapsuleLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "CapsuleLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "CapsuleLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -114,7 +114,7 @@ namespace AZ void CapsuleLightFeatureProcessor::Render(const CapsuleLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "CapsuleLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "CapsuleLightFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp index c235f78595..702818dcd9 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp @@ -196,7 +196,7 @@ namespace AZ void DirectionalLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket&) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "DirectionalLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "DirectionalLightFeatureProcessor: Simulate"); if (m_shadowingLightHandle.IsValid()) { @@ -293,7 +293,7 @@ namespace AZ void DirectionalLightFeatureProcessor::Render(const FeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "DirectionalLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "DirectionalLightFeatureProcessor: Render"); if (m_shadowingLightHandle.IsValid()) { @@ -1232,7 +1232,7 @@ namespace AZ void DirectionalLightFeatureProcessor::SetFilterParameterToPass(LightHandle handle, const RPI::View* cameraView) { - AZ_ATOM_PROFILE_FUNCTION("DirectionalLightFeatureProcessor", "DirectionalLightFeatureProcessor::SetFilterParameterToPass"); + AZ_PROFILE_SCOPE(RPI, "DirectionalLightFeatureProcessor::SetFilterParameterToPass"); if (handle != m_shadowingLightHandle) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp index dfbeea0ffe..ca2c038e46 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp @@ -123,7 +123,7 @@ namespace AZ void DiskLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "DiskLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "DiskLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -135,7 +135,7 @@ namespace AZ void DiskLightFeatureProcessor::Render(const DiskLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "DiskLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "DiskLightFeatureProcessor: Simulate"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp index af440e5040..bfb0b1252b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp @@ -119,7 +119,7 @@ namespace AZ void PointLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "PointLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "PointLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -131,7 +131,7 @@ namespace AZ void PointLightFeatureProcessor::Render(const PointLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "PointLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "PointLightFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PolygonLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PolygonLightFeatureProcessor.cpp index 089adf4621..9dbf01ae26 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PolygonLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PolygonLightFeatureProcessor.cpp @@ -132,7 +132,7 @@ namespace AZ::Render void PolygonLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "PolygonLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "PolygonLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -153,7 +153,7 @@ namespace AZ::Render void PolygonLightFeatureProcessor::Render(const PolygonLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "PolygonLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "PolygonLightFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/QuadLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/QuadLightFeatureProcessor.cpp index e22174225d..aadc8c2020 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/QuadLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/QuadLightFeatureProcessor.cpp @@ -107,7 +107,7 @@ namespace AZ void QuadLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "QuadLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "QuadLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -119,7 +119,7 @@ namespace AZ void QuadLightFeatureProcessor::Render(const QuadLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "QuadLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "QuadLightFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimplePointLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimplePointLightFeatureProcessor.cpp index b8bbb03335..f49a5e94fc 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimplePointLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimplePointLightFeatureProcessor.cpp @@ -102,7 +102,7 @@ namespace AZ void SimplePointLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "SimplePointLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "SimplePointLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -114,7 +114,7 @@ namespace AZ void SimplePointLightFeatureProcessor::Render(const SimplePointLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "SimplePointLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "SimplePointLightFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimpleSpotLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimpleSpotLightFeatureProcessor.cpp index d4776faec6..277b5026d7 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimpleSpotLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/SimpleSpotLightFeatureProcessor.cpp @@ -102,7 +102,7 @@ namespace AZ void SimpleSpotLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "SimpleSpotLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "SimpleSpotLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -114,7 +114,7 @@ namespace AZ void SimpleSpotLightFeatureProcessor::Render(const SimpleSpotLightFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "SimpleSpotLightFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "SimpleSpotLightFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp index 4954ffc01c..4ff718b5ad 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalFeatureProcessor.cpp @@ -107,7 +107,7 @@ namespace AZ void DecalFeatureProcessor::Simulate(const RPI::FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "DecalFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "DecalFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -131,7 +131,7 @@ namespace AZ void DecalFeatureProcessor::Render(const RPI::FeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "DecalFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "DecalFeatureProcessor: Render"); AZStd::array_view> baseMaps = GetImagesFromDecalData<1>(); AZStd::array_view> opacityMaps = GetImagesFromDecalData<2>(); diff --git a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp index 4ecfd7fc09..c0b8f3315a 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Decals/DecalTextureArrayFeatureProcessor.cpp @@ -145,7 +145,7 @@ namespace AZ void DecalTextureArrayFeatureProcessor::Simulate(const RPI::FeatureProcessor::SimulatePacket& packet) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "DecalTextureArrayFeatureProcessor: Simulate"); AZ_UNUSED(packet); if (m_deviceBufferNeedsUpdate) @@ -158,7 +158,7 @@ namespace AZ void DecalTextureArrayFeatureProcessor::Render(const RPI::FeatureProcessor::RenderPacket& packet) { // Note that decals are rendered as part of the forward shading pipeline. We only need to bind the decal buffers/textures in here. - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "DecalTextureArrayFeatureProcessor: Render"); for (const RPI::ViewPtr& view : packet.m_views) { @@ -294,7 +294,7 @@ namespace AZ void DecalTextureArrayFeatureProcessor::SetDecalMaterial(const DecalHandle handle, const AZ::Data::AssetId material) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "DecalTextureArrayFeatureProcessor: SetDecalMaterial"); if (handle.IsNull()) { AZ_Warning("DecalTextureArrayFeatureProcessor", false, "Invalid handle passed to DecalTextureArrayFeatureProcessor::SetDecalMaterial()."); @@ -364,7 +364,7 @@ namespace AZ void DecalTextureArrayFeatureProcessor::OnAssetReady(const Data::Asset asset) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "DecalTextureArrayFeatureProcessor: OnAssetReady"); const Data::AssetId& assetId = asset->GetId(); const RPI::MaterialAsset* materialAsset = asset.GetAs(); diff --git a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp index 543851da0f..d79240d4f1 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp @@ -111,7 +111,7 @@ namespace AZ void DiffuseProbeGridFeatureProcessor::Simulate([[maybe_unused]] const FeatureProcessor::SimulatePacket& packet) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "DiffuseProbeGridFeatureProcessor: Simulate"); // update pipeline states if (m_needUpdatePipelineStates) diff --git a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp index b6ca1191dd..1a68317532 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiPass.cpp @@ -577,8 +577,7 @@ namespace AZ void ImGuiPass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("Pass", "ImGuiPass: Execute"); + AZ_PROFILE_SCOPE(AzRender, "ImGuiPass: BuildCommandListInternal"); context.GetCommandList()->SetViewport(m_viewportState); @@ -607,8 +606,7 @@ namespace AZ uint32_t ImGuiPass::UpdateImGuiResources() { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("Pass", "ImGuiPass: UpdateImGuiResources"); + AZ_PROFILE_SCOPE(AzRender, "ImGuiPass: UpdateImGuiResources"); auto imguiContextScope = ImguiContextScope(m_imguiContext); diff --git a/Gems/Atom/Feature/Common/Code/Source/ImageBasedLights/ImageBasedLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ImageBasedLights/ImageBasedLightFeatureProcessor.cpp index 7c41379a14..9cdad34f17 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ImageBasedLights/ImageBasedLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ImageBasedLights/ImageBasedLightFeatureProcessor.cpp @@ -50,7 +50,7 @@ namespace AZ void ImageBasedLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "ImageBasedLightFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "ImageBasedLightFeatureProcessor: Simulate"); AZ_UNUSED(packet); m_sceneSrg->SetImage(m_specularEnvMapIndex, m_specular); diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 8ab324cdf7..52019f8d2f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -75,8 +75,7 @@ namespace AZ void MeshFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("RPI", "MeshFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "MeshFeatureProcessor: Simulate"); AZ_UNUSED(packet); AZStd::concurrency_check_scope scopeCheck(m_meshDataChecker); @@ -149,7 +148,7 @@ namespace AZ const MeshHandleDescriptor& descriptor, const MaterialAssignmentMap& materials) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "MeshFeatureProcessor: AcquireMesh"); // don't need to check the concurrency during emplace() because the StableDynamicArray won't move the other elements during insertion MeshHandle meshDataHandle = m_meshData.emplace(); @@ -984,7 +983,7 @@ namespace AZ void MeshDataInstance::UpdateDrawPackets(bool forceUpdate /*= false*/) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "MeshDataInstance:: UpdateDrawPackets"); for (auto& drawPacketList : m_drawPacketListsByLod) { for (auto& drawPacket : drawPacketList) @@ -999,7 +998,7 @@ namespace AZ void MeshDataInstance::BuildCullable() { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "MeshDataInstance: BuildCullable"); AZ_Assert(m_cullableNeedsRebuild, "This function only needs to be called if the cullable to be rebuilt"); AZ_Assert(m_model, "The model has not finished loading yet"); @@ -1076,7 +1075,7 @@ namespace AZ void MeshDataInstance::UpdateCullBounds(const TransformServiceFeatureProcessor* transformService) { - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "MeshDataInstance: UpdateCullBounds"); AZ_Assert(m_cullBoundsNeedsUpdate, "This function only needs to be called if the culling bounds need to be rebuilt"); AZ_Assert(m_model, "The model has not finished loading yet"); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessFeatureProcessor.cpp index 35294d7403..d5b3ebd63b 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcess/PostProcessFeatureProcessor.cpp @@ -49,7 +49,7 @@ namespace AZ void PostProcessFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "PostProcessFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "PostProcessFeatureProcessor: Simulate"); AZ_UNUSED(packet); UpdateTime(); diff --git a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp index 56d413e84f..a795d88212 100644 --- a/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp @@ -161,7 +161,7 @@ namespace AZ void SMAAFeatureProcessor::Render([[maybe_unused]] const SMAAFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "SMAAFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "SMAAFeatureProcessor: Render"); UpdateConvertToPerceptualPass(); UpdateEdgeDetectionPass(); diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp index 341d1a0274..91c72013dd 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp @@ -154,8 +154,7 @@ namespace AZ void ReflectionProbeFeatureProcessor::Simulate([[maybe_unused]] const FeatureProcessor::SimulatePacket& packet) { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("ReflectionProbe", "ReflectionProbeFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(AzRender, "ReflectionProbeFeatureProcessor: Simulate"); // update pipeline states if (m_needUpdatePipelineStates) @@ -194,7 +193,6 @@ namespace AZ if (m_probeSortRequired) { AZ_PROFILE_SCOPE(AzRender, "Sort reflection probes"); - AZ_ATOM_PROFILE_FUNCTION("ReflectionProbe", "ReflectionProbeFeatureProcessor: Sort reflection probes"); // sort the probes by descending inner volume size, so the smallest volumes are rendered last auto sortFn = [](AZStd::shared_ptr const& probe1, AZStd::shared_ptr const& probe2) -> bool diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp index 68c31bd859..8fb971f8f5 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -486,7 +486,7 @@ namespace AZ::Render void ProjectedShadowFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& /*packet*/) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "ProjectedShadowFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "ProjectedShadowFeatureProcessor: Simulate"); if (m_shadowmapPassNeedsUpdate) { @@ -581,7 +581,7 @@ namespace AZ::Render void ProjectedShadowFeatureProcessor::Render(const ProjectedShadowFeatureProcessor::RenderPacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "ProjectedShadowFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(RPI, "ProjectedShadowFeatureProcessor: Render"); if (!m_projectedShadowmapsPasses.empty()) { diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp index 0aa72bf2ca..500eadf110 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp @@ -69,8 +69,7 @@ namespace AZ void SkinnedMeshFeatureProcessor::Render(const FeatureProcessor::RenderPacket& packet) { - AZ_PROFILE_FUNCTION(AzRender); - AZ_ATOM_PROFILE_FUNCTION("SkinnedMesh", "SkinnedMeshFeatureProcessor: Render"); + AZ_PROFILE_SCOPE(AzRender, "SkinnedMeshFeatureProcessor: Render"); #if 0 //[GFX_TODO][ATOM-13564] Temporarily disable skinning culling until we figure out how to hook up visibility & lod selection with skinning: //Setup the culling workgroup (it will be re-used for each view) diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp index b84d4347a7..4146a669fe 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp @@ -534,7 +534,7 @@ namespace AZ // lod0 Positions[^ ^] lod0Normals[^ ^] lod1Positions[^ ^] lod1Normals[^ ^] // lod0 subMesh0+1 Positions[^ ^^ ^] lod0 subMesh0+1 Normals[^ ^^ ^] lod1 sm0+1 pos[^ ^^ ^] lod1 sm0+1 norm[^ ^^ ^] - AZ_PROFILE_FUNCTION(AzRender); + AZ_PROFILE_SCOPE(AzRender, "SkinnedMeshInputBuffers: CreateSkinnedMeshInstance"); AZStd::intrusive_ptr instance = aznew SkinnedMeshInstance; // Each model gets a unique, random ID, so if the same source model is used for multiple instances, multiple target models will be created. diff --git a/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp index d0150fc0c2..d4fa3f52be 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkyBox/SkyBoxFeatureProcessor.cpp @@ -105,7 +105,7 @@ namespace AZ void SkyBoxFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "SkyBoxFeatureProcessor: Simulate"); + AZ_PROFILE_SCOPE(RPI, "SkyBoxFeatureProcessor: Simulate"); AZ_UNUSED(packet); m_sceneSrg->SetConstant(m_skyboxEnableIndex, m_enable); diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfiler.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfiler.h index 3fedc99566..70c5771b57 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfiler.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfiler.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -21,15 +22,15 @@ namespace AZ //! Structure that is used to cache a timed region into the thread's local storage. struct CachedTimeRegion { - //! Structure that the profiling macro utilizes to create statically initialized instance to create string - //! literals in static memory + //! Structure used internally for caching assumed global string pointers (ideally literals) to the marker group/region + //! NOTE: When used in a separate shared library, the library mustn't be unloaded before the CpuProfiler is shutdown. struct GroupRegionName { GroupRegionName() = delete; GroupRegionName(const char* const group, const char* const region); - - const char* const m_groupName = nullptr; - const char* const m_regionName = nullptr; + + const char* m_groupName = nullptr; + const char* m_regionName = nullptr; struct Hash { @@ -39,31 +40,16 @@ namespace AZ }; CachedTimeRegion() = default; - CachedTimeRegion(const GroupRegionName* groupRegionName); - CachedTimeRegion(const GroupRegionName* groupRegionName, uint16_t stackDepth, uint64_t startTick, uint64_t endTick); + CachedTimeRegion(const GroupRegionName& groupRegionName); + CachedTimeRegion(const GroupRegionName& groupRegionName, uint16_t stackDepth, uint64_t startTick, uint64_t endTick); - //! Pointer to the GroupRegionName static instance. - //! NOTE: When used in a separate shared library, the library mustn't be unloaded before - //! the CpuProfiler is shutdown. - const GroupRegionName* m_groupRegionName = nullptr; + GroupRegionName m_groupRegionName{nullptr, nullptr}; uint16_t m_stackDepth = 0u; AZStd::sys_time_t m_startTick = 0; AZStd::sys_time_t m_endTick = 0; }; - //! Helper class used as a RAII-style mechanism for the macros to begin and end a region. - class TimeRegion : public CachedTimeRegion - { - public: - TimeRegion() = delete; - TimeRegion(const GroupRegionName* groupRegionName); - ~TimeRegion(); - - //! End region - void EndRegion(); - }; - //! Interface class of the CpuProfiler class CpuProfiler { @@ -80,12 +66,6 @@ namespace AZ static CpuProfiler* Get(); - //! Add a new time region - virtual void BeginTimeRegion(TimeRegion& timeRegion) = 0; - - //! Ends a time region - virtual void EndTimeRegion() = 0; - //! Get the last frame's TimeRegionMap virtual const TimeRegionMap& GetTimeRegionMap() const = 0; @@ -101,38 +81,7 @@ namespace AZ virtual void SetProfilerEnabled(bool enabled) = 0; virtual bool IsProfilerEnabled() const = 0 ; - - //! Used by AZ_ATOM_PROFILE_DYNAMIC to create GroupRegionNames with known lifetimes. - virtual const CachedTimeRegion::GroupRegionName& InsertDynamicName(const char* groupName, const AZStd::string& regionName) = 0; }; } // namespace RPI } // namespace AZ - -//! Utility functions for timing a section of code and writing the timing (in cycles) to a new, named time region inside the -//! provided statistics data. - -//! Supply a group and region to the time region -#define AZ_ATOM_PROFILE_TIME_GROUP_REGION(groupName, regionName) \ - static const AZ::RHI::CachedTimeRegion::GroupRegionName AZ_JOIN(groupRegionName, __LINE__)(groupName, regionName); \ - AZ::RHI::TimeRegion AZ_JOIN(timeRegion, __LINE__)(&AZ_JOIN(groupRegionName, __LINE__)); - -//! Supply a region to the time region; "Default" will be used for the group -#define AZ_ATOM_PROFILE_TIME_REGION(regionName) \ - AZ_ATOM_PROFILE_TIME_GROUP_REGION("Default", regionName) - -//! Used to create a time region; "Default" will be used for the group, and __FUNCTION__ macro for the region -#define AZ_ATOM_PROFILE_TIME_FUNCTION() \ - AZ_ATOM_PROFILE_TIME_GROUP_REGION("Default", AZ_FUNCTION_SIGNATURE) - -//! Macro that combines the AZ_TRACE_METHOD with time profiling macro -#define AZ_ATOM_PROFILE_FUNCTION(groupName, regionName) \ - AZ_TRACE_METHOD(); \ - AZ_ATOM_PROFILE_TIME_GROUP_REGION(groupName, regionName) \ - -//! Macro that allows for region names to be submitted at runtime. Use sparingly - this acquires a lock and allocates new objects within a map. -#define AZ_ATOM_PROFILE_DYNAMIC(groupName, regionName) \ - static_assert(AZStd::is_convertible_v, "Runtime group names are not allowed, use a static string literal instead."); \ - const AZ::RHI::CachedTimeRegion::GroupRegionName& AZ_JOIN(groupRegionName, __LINE__) = \ - AZ::RHI::CpuProfiler::Get()->InsertDynamicName(groupName, regionName); \ - AZ::RHI::TimeRegion AZ_JOIN(timeRegion, __LINE__)(&AZ_JOIN(groupRegionName, __LINE__)); diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfilerImpl.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfilerImpl.h index 92b56880b7..29886625ea 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfilerImpl.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/CpuProfilerImpl.h @@ -43,13 +43,13 @@ namespace AZ static constexpr uint32_t TimeRegionStackSize = 2048u; // Adds a region to the stack, gets called each time a region begins - void RegionStackPushBack(TimeRegion& timeRegion); + void RegionStackPushBack(CachedTimeRegion& timeRegion); // Pops a region from the stack, gets called each time a region ends void RegionStackPopBack(); // Add a new cached time region. If the stack is empty, flush all entries to the cached map - void AddCachedRegion(CachedTimeRegion&& timeRegionCached); + void AddCachedRegion(const CachedTimeRegion& timeRegionCached); // Tries to flush the map to the passed parameter, only if the thread's mutex is unlocked void TryFlushCachedMap(CpuProfiler::ThreadTimeRegionMap& cachedRegionMap); @@ -63,7 +63,7 @@ namespace AZ // Use fixed vectors to avoid re-allocating new elements // Keeps track of the regions that added and removed using the macro - AZStd::fixed_vector m_timeRegionStack; + AZStd::fixed_vector m_timeRegionStack; // Keeps track of regions that completed (i.e regions that was pushed and popped from the stack) // Intermediate storage point for the CachedTimeRegions, when the stack is empty, all entries will be @@ -85,7 +85,8 @@ namespace AZ //! forwards the request to profile a region to the appropriate thread. The user is able to request all //! cached regions, which are stored on a per thread frequency. class CpuProfilerImpl final - : public CpuProfiler + : public AZ::Debug::Profiler + , public CpuProfiler , public SystemTickBus::Handler { friend class CpuTimingLocalStorage; @@ -107,16 +108,17 @@ namespace AZ // m_timeRegionMap so that the next frame has up-to-date profiling data. void OnSystemTick() final override; + //! AZ::Debug::Profiler overrides... + void BeginRegion(const AZ::Debug::Budget* budget, const char* eventName) final override; + void EndRegion(const AZ::Debug::Budget* budget) final override; + //! CpuProfiler overrides... - void BeginTimeRegion(TimeRegion& timeRegion) final override; - void EndTimeRegion() final override; const TimeRegionMap& GetTimeRegionMap() const final override; bool BeginContinuousCapture() final override; bool EndContinuousCapture(AZStd::ring_buffer& flushTarget) final override; bool IsContinuousCaptureInProgress() const final override; void SetProfilerEnabled(bool enabled) final override; bool IsProfilerEnabled() const final override; - const CachedTimeRegion::GroupRegionName& InsertDynamicName(const char* groupName, const AZStd::string& regionName) final override; private: static constexpr AZStd::size_t MaxFramesToSave = 2 * 60 * 120; // 2 minutes of 120fps @@ -133,15 +135,6 @@ namespace AZ AZStd::vector, AZ::OSStdAllocator> m_registeredThreads; AZStd::mutex m_threadRegisterMutex; - // Pool for GroupRegionNames that are generated at runtime through AZ_ATOM_PROFILE_DYNAMIC. Each unique - // combination of group name and region name submitted will be stored in this pool to emulate static lifetime. - AZStd::unordered_set m_dynamicGroupRegionNamePool; - - // String pool for storing region names submitted at runtime. Each call to AZ_ATOM_PROFILE_DYNAMIC will either construct - // a string in this pool or use an already-existing entry. - AZStd::unordered_set m_regionNameStringPool; - AZStd::mutex m_dynamicNameMutex; - // Thread local storage, gets lazily allocated when a thread is created static thread_local CpuTimingLocalStorage* ms_threadLocalStorage; diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/MemorySubAllocator.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/MemorySubAllocator.h index a10afd880f..bdeb252174 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/MemorySubAllocator.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/MemorySubAllocator.h @@ -157,7 +157,7 @@ namespace AZ template void MemorySubAllocator::GarbageCollect() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "MemorySubAllocator: GarbageCollect"); + AZ_PROFILE_SCOPE(RHI, "MemorySubAllocator: GarbageCollect"); for (PageContext& pageContext : m_pageContexts) { pageContext.m_allocator.GarbageCollect(); diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h index cd8a85a385..988416326e 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/ObjectCollector.h @@ -173,7 +173,7 @@ namespace AZ template void ObjectCollector::Collect(bool forceFlush) { - AZ_ATOM_PROFILE_FUNCTION("DX12", "ObjectCollector: Collect"); + AZ_PROFILE_SCOPE(RHI, "ObjectCollector: Collect"); m_mutex.lock(); if (m_pendingObjects.size()) { diff --git a/Gems/Atom/RHI/Code/Source/RHI/AsyncWorkQueue.cpp b/Gems/Atom/RHI/Code/Source/RHI/AsyncWorkQueue.cpp index 52021fa736..a076bf3e58 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/AsyncWorkQueue.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/AsyncWorkQueue.cpp @@ -128,7 +128,7 @@ namespace AZ return; } - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncWorkQueue: WaitToFinish"); AZStd::unique_lock lock(m_waitWorkItemMutex); m_waitWorkItemCondition.wait(lock, [&]() {return HasFinishedWork(workHandle); }); diff --git a/Gems/Atom/RHI/Code/Source/RHI/BufferPool.cpp b/Gems/Atom/RHI/Code/Source/RHI/BufferPool.cpp index 9849db254e..5d06d02668 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/BufferPool.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/BufferPool.cpp @@ -163,7 +163,7 @@ namespace AZ return ResultCode::InvalidArgument; } - AZ_ATOM_PROFILE_FUNCTION("RHI", "BufferPool::OrphanBuffer"); + AZ_PROFILE_SCOPE(RHI, "BufferPool::OrphanBuffer"); return OrphanBufferInternal(buffer); } diff --git a/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp b/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp index 15621c7f2b..0983d4db22 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/CommandQueue.cpp @@ -22,7 +22,7 @@ namespace AZ ResultCode CommandQueue::Init(Device& device, const CommandQueueDescriptor& descriptor) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueue: Init"); #if defined (AZ_RHI_ENABLE_VALIDATION) if (IsInitialized()) @@ -83,7 +83,7 @@ namespace AZ void CommandQueue::FlushCommands() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "CommandQueue: FlushCommands"); + AZ_PROFILE_SCOPE(RHI, "CommandQueue: FlushCommands"); while (!m_isWorkQueueEmpty && !m_isQuitting) { AZStd::this_thread::yield(); diff --git a/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp b/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp index e16b89b5cd..1bc17adb22 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/CpuProfilerImpl.cpp @@ -27,38 +27,14 @@ namespace AZ return Interface::Get(); } - // --- TimeRegion --- - - TimeRegion::TimeRegion(const GroupRegionName* groupRegionName) : - CachedTimeRegion(groupRegionName) - { - if (CpuProfiler::Get()) - { - CpuProfiler::Get()->BeginTimeRegion(*this); - } - } - - TimeRegion::~TimeRegion() - { - EndRegion(); - } - - void TimeRegion::EndRegion() - { - if (CpuProfiler::Get()) - { - CpuProfiler::Get()->EndTimeRegion(); - } - } - // --- CachedTimeRegion --- - CachedTimeRegion::CachedTimeRegion(const GroupRegionName* groupRegionName) + CachedTimeRegion::CachedTimeRegion(const GroupRegionName& groupRegionName) { m_groupRegionName = groupRegionName; } - CachedTimeRegion::CachedTimeRegion(const GroupRegionName* groupRegionName, uint16_t stackDepth, uint64_t startTick, uint64_t endTick) + CachedTimeRegion::CachedTimeRegion(const GroupRegionName& groupRegionName, uint16_t stackDepth, uint64_t startTick, uint64_t endTick) { m_groupRegionName = groupRegionName; m_stackDepth = stackDepth; @@ -92,6 +68,7 @@ namespace AZ void CpuProfilerImpl::Init() { + Interface::Register(this); Interface::Register(this); m_initialized = true; SystemTickBus::Handler::BusConnect(); @@ -106,6 +83,7 @@ namespace AZ } // When this call is made, no more thread profiling calls can be performed anymore Interface::Unregister(this); + Interface::Unregister(this); // Wait for the remaining threads that might still be processing its profiling calls AZStd::unique_lock shutdownLock(m_shutdownMutex); @@ -121,7 +99,7 @@ namespace AZ SystemTickBus::Handler::BusDisconnect(); } - void CpuProfilerImpl::BeginTimeRegion(TimeRegion& timeRegion) + void CpuProfilerImpl::BeginRegion(const AZ::Debug::Budget* budget, const char* eventName) { // Try to lock here, the shutdownMutex will only be contested when the CpuProfiler is shutting down. if (m_shutdownMutex.try_lock_shared()) @@ -132,6 +110,7 @@ namespace AZ RegisterThreadStorage(); // Push it to the stack + CachedTimeRegion timeRegion({budget->Name(), eventName}); ms_threadLocalStorage->RegionStackPushBack(timeRegion); } @@ -139,11 +118,12 @@ namespace AZ } } - void CpuProfilerImpl::EndTimeRegion() + void CpuProfilerImpl::EndRegion([[maybe_unused]] const AZ::Debug::Budget* budget) { // Try to lock here, the shutdownMutex will only be contested when the CpuProfiler is shutting down. if (m_shutdownMutex.try_lock_shared()) { + // guard against enabling mid-marker if (m_enabled && ms_threadLocalStorage != nullptr) { ms_threadLocalStorage->RegionStackPopBack(); @@ -232,19 +212,6 @@ namespace AZ return m_enabled; } - const CachedTimeRegion::GroupRegionName& CpuProfilerImpl::InsertDynamicName(const char* groupName, const AZStd::string& regionName) - { - AZStd::scoped_lock lock(m_dynamicNameMutex); - AZ_Warning("CpuProfiler", m_regionNameStringPool.size() < MaxRegionStringPoolSize, - "Stored dynamic region names are accumulating. Consider removing a AZ_ATOM_PROFILE_DYNAMIC invocation."); - auto [regionNameItr, wasRegionInserted] = m_regionNameStringPool.insert(regionName); - - CachedTimeRegion::GroupRegionName newGroupRegionName(groupName, regionNameItr->c_str()); - auto [groupRegionNameItr, wasGroupRegionInserted] = m_dynamicGroupRegionNamePool.insert(newGroupRegionName); - - return *groupRegionNameItr; - } - void CpuProfilerImpl::OnSystemTick() { if (!m_enabled) @@ -307,7 +274,7 @@ namespace AZ m_deleteFlag = true; } - void CpuTimingLocalStorage::RegionStackPushBack(TimeRegion& timeRegion) + void CpuTimingLocalStorage::RegionStackPushBack(CachedTimeRegion& timeRegion) { // If it was (re)enabled, clear the lists first if (m_clearContainers) @@ -323,13 +290,13 @@ namespace AZ timeRegion.m_stackDepth = static_cast(m_stackLevel); AZ_Assert(m_timeRegionStack.size() < TimeRegionStackSize, "Adding too many time regions to the stack. Increase the size of TimeRegionStackSize."); - m_timeRegionStack.push_back(&timeRegion); + m_timeRegionStack.push_back(timeRegion); // Increment the stack m_stackLevel++; // Set the starting time at the end, to avoid recording the minor overhead - timeRegion.m_startTick = AZStd::GetTimeNowTicks(); + m_timeRegionStack.back().m_startTick = AZStd::GetTimeNowTicks(); } void CpuTimingLocalStorage::RegionStackPopBack() @@ -344,23 +311,23 @@ namespace AZ const AZStd::sys_time_t endRegionTime = AZStd::GetTimeNowTicks(); AZ_Assert(!m_timeRegionStack.empty(), "Trying to pop an element in the stack, but it's empty."); - TimeRegion* back = m_timeRegionStack.back(); + CachedTimeRegion back = m_timeRegionStack.back(); m_timeRegionStack.pop_back(); // Set the ending time - back->m_endTick = endRegionTime; + back.m_endTick = endRegionTime; // Decrement the stack m_stackLevel--; // Add an entry to the cached region - AddCachedRegion(CachedTimeRegion(back->m_groupRegionName, back->m_stackDepth, back->m_startTick, back->m_endTick)); + AddCachedRegion(back); } // Gets called when region ends and all data is set - void CpuTimingLocalStorage::AddCachedRegion(CachedTimeRegion&& timeRegionCached) + void CpuTimingLocalStorage::AddCachedRegion(const CachedTimeRegion& timeRegionCached) { - if (m_hitSizeLimitMap[timeRegionCached.m_groupRegionName->m_regionName]) + if (m_hitSizeLimitMap[timeRegionCached.m_groupRegionName.m_regionName]) { return; } @@ -379,12 +346,12 @@ namespace AZ // Add the cached regions to the map for (auto& cachedTimeRegion : m_cachedTimeRegions) { - const AZStd::string regionName = cachedTimeRegion.m_groupRegionName->m_regionName; + const AZStd::string regionName = cachedTimeRegion.m_groupRegionName.m_regionName; AZStd::vector& regionVec = m_cachedTimeRegionMap[regionName]; regionVec.push_back(cachedTimeRegion); if (regionVec.size() >= TimeRegionStackSize) { - m_hitSizeLimitMap[cachedTimeRegion.m_groupRegionName->m_regionName] = true; + m_hitSizeLimitMap.insert_or_assign(AZStd::move(regionName), true); } } @@ -448,8 +415,8 @@ namespace AZ CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializerEntry::CpuProfilingStatisticsSerializerEntry( const RHI::CachedTimeRegion& cachedTimeRegion, AZStd::thread_id threadId) { - m_groupName = cachedTimeRegion.m_groupRegionName->m_groupName; - m_regionName = cachedTimeRegion.m_groupRegionName->m_regionName; + m_groupName = cachedTimeRegion.m_groupRegionName.m_groupName; + m_regionName = cachedTimeRegion.m_groupRegionName.m_regionName; m_stackDepth = cachedTimeRegion.m_stackDepth; m_startTick = cachedTimeRegion.m_startTick; m_endTick = cachedTimeRegion.m_endTick; diff --git a/Gems/Atom/RHI/Code/Source/RHI/Device.cpp b/Gems/Atom/RHI/Code/Source/RHI/Device.cpp index 3af09717df..9453ff79ee 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/Device.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/Device.cpp @@ -128,7 +128,7 @@ namespace AZ { if (ValidateIsInitialized() && ValidateIsInFrame()) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "Device: EndFrame"); + AZ_PROFILE_SCOPE(RHI, "Device: EndFrame"); EndFrameInternal(); m_isInFrame = false; return ResultCode::Success; @@ -150,7 +150,7 @@ namespace AZ { if (ValidateIsInitialized() && ValidateIsNotInFrame()) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "Device: CompileMemoryStatistics"); + AZ_PROFILE_SCOPE(RHI, "Device: CompileMemoryStatistics"); MemoryStatisticsBuilder builder; builder.Begin(memoryStatistics, reportFlags); CompileMemoryStatisticsInternal(builder); diff --git a/Gems/Atom/RHI/Code/Source/RHI/Fence.cpp b/Gems/Atom/RHI/Code/Source/RHI/Fence.cpp index b35260caca..ca0493a52c 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/Fence.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/Fence.cpp @@ -81,7 +81,7 @@ namespace AZ return ResultCode::InvalidOperation; } - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "Fence: WaitOnCpu"); WaitOnCpuInternal(); return ResultCode::Success; } diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp index ff6ecb4df5..b85fa88f34 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameGraph.cpp @@ -73,7 +73,7 @@ namespace AZ void FrameGraph::Clear() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraph: Clear"); + AZ_PROFILE_SCOPE(RHI, "FrameGraph: Clear"); for (Scope* scope : m_scopes) { scope->Deactivate(); @@ -126,7 +126,7 @@ namespace AZ ResultCode FrameGraph::End() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraph: End"); + AZ_PROFILE_SCOPE(RHI, "FrameGraph: End"); ResultCode resultCode = ValidateEnd(); if (resultCode != ResultCode::Success) { diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphCompiler.cpp index c4204a4a90..5c5691d975 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphCompiler.cpp @@ -121,7 +121,7 @@ namespace AZ */ MessageOutcome FrameGraphCompiler::Compile(const FrameGraphCompileRequest& request) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: Compile"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: Compile"); MessageOutcome outcome = ValidateCompileRequest(request); if (!outcome) @@ -146,7 +146,7 @@ namespace AZ /// [Phase 4] Compile platform-specific scope data after all attachments and views have been compiled. { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: Scope Compile"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: Scope Compile"); for (Scope* scope : frameGraph.GetScopes()) { @@ -162,7 +162,7 @@ namespace AZ FrameGraph& frameGraph, FrameSchedulerCompileFlags compileFlags) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileQueueCentricScopeGraph"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileQueueCentricScopeGraph"); const bool disableAsyncQueues = CheckBitsAll(compileFlags, FrameSchedulerCompileFlags::DisableAsyncQueues); if (disableAsyncQueues) @@ -480,7 +480,7 @@ namespace AZ return; } - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileTransientAttachments"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileTransientAttachments"); ExtendTransientAttachmentAsyncQueueLifetimes(frameGraph, compileFlags); @@ -769,7 +769,7 @@ namespace AZ void FrameGraphCompiler::CompileResourceViews(const FrameGraphAttachmentDatabase& attachmentDatabase) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileResourceViews"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileResourceViews"); for (ImageFrameAttachment* imageAttachment : attachmentDatabase.GetImageAttachments()) { diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphExecuter.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphExecuter.cpp index 342e537993..429081cd2c 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameGraphExecuter.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameGraphExecuter.cpp @@ -71,14 +71,13 @@ namespace AZ void FrameGraphExecuter::Begin(const FrameGraph& frameGraph) { - AZ_TRACE_METHOD(); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphExecuter: Begin"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphExecuter: Begin"); BeginInternal(frameGraph); } void FrameGraphExecuter::End() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphExecuter: End"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphExecuter: End"); AZ_Assert(m_pendingGroups.empty(), "Pending contexts in queue."); m_groups.clear(); EndInternal(); diff --git a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp index fe69f56856..90218709fa 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/FrameScheduler.cpp @@ -137,7 +137,7 @@ namespace AZ ResultCode FrameScheduler::ImportScopeProducer(ScopeProducer& scopeProducer) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: ImportScopeProducer"); if (!ValidateIsProcessing()) { @@ -171,14 +171,14 @@ namespace AZ MessageOutcome FrameScheduler::Compile(const FrameSchedulerCompileRequest& compileRequest) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: Compile"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: Compile"); PrepareProducers(); m_compileRequest = compileRequest; { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RHI", "FrameScheduler: Compile: OnFrameCompile"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: Compile: OnFrameCompile"); FrameEventBus::Broadcast(&FrameEventBus::Events::OnFrameCompile); } @@ -193,7 +193,7 @@ namespace AZ if (outcome.IsSuccess()) { { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RHI", "FrameScheduler: Compile: OnFrameCompileEnd"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: Compile: OnFrameCompileEnd"); FrameEventBus::Broadcast(&FrameEventBus::Events::OnFrameCompileEnd, *m_frameGraph); } @@ -216,8 +216,7 @@ namespace AZ void FrameScheduler::PrepareProducers() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: PrepareProducers"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: PrepareProducers"); for (ScopeProducer* scopeProducer : m_scopeProducers) { @@ -237,8 +236,7 @@ namespace AZ void FrameScheduler::CompileProducers() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: CompileProducers"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: CompileProducers"); for (ScopeProducer* scopeProducer : m_scopeProducers) { @@ -249,8 +247,7 @@ namespace AZ void FrameScheduler::CompileShaderResourceGroups() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: CompileShaderResourceGroups"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: CompileShaderResourceGroups"); // Execute all queued resource invalidations, which will mark SRG's for compilation. { @@ -286,7 +283,7 @@ namespace AZ const auto compileGroupsForIntervalLambda = [srgPool, interval]() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler : compileGroupsForIntervalLambda"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler : compileGroupsForIntervalLambda"); srgPool->CompileGroupsForInterval(interval); }; @@ -322,8 +319,7 @@ namespace AZ void FrameScheduler::BuildRayTracingShaderTables() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: BuildRayTracingShaderTables"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: BuildRayTracingShaderTables"); for (auto rayTracingShaderTable : m_rayTracingShaderTablesToBuild) { @@ -341,8 +337,7 @@ namespace AZ ResultCode FrameScheduler::BeginFrame() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: BeginFrame"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: BeginFrame"); if (!ValidateIsInitialized()) { @@ -376,8 +371,7 @@ namespace AZ ResultCode FrameScheduler::EndFrame() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: EndFrame"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: EndFrame"); if (Validation::IsEnabled()) { @@ -404,7 +398,7 @@ namespace AZ m_scopeProducerLookup.clear(); { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RHI", "FrameScheduler: EndFrame: OnFrameEnd"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: EndFrame: OnFrameEnd"); FrameEventBus::Event(m_device, &FrameEventBus::Events::OnFrameEnd); } @@ -431,8 +425,7 @@ namespace AZ void FrameScheduler::ExecuteGroupInternal(AZ::Job* parentJob, uint32_t groupIndex) { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: ExecuteGroupInternal"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: ExecuteGroupInternal"); FrameGraphExecuteGroup* executeGroup = m_frameGraphExecuter->BeginGroup(groupIndex); const uint32_t contextCount = executeGroup->GetContextCount(); @@ -474,8 +467,7 @@ namespace AZ void FrameScheduler::Execute(JobPolicy overrideJobPolicy) { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameScheduler: Execute"); + AZ_PROFILE_SCOPE(RHI, "FrameScheduler: Execute"); const uint32_t groupCount = m_frameGraphExecuter->GetGroupCount(); const JobPolicy platformJobPolicy = m_frameGraphExecuter->GetJobPolicy(); diff --git a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp index 6e98456933..868580f0c7 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/PipelineStateCache.cpp @@ -212,7 +212,7 @@ namespace AZ void PipelineStateCache::Compact() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "PipelineStateCache: Compact"); + AZ_PROFILE_SCOPE(RHI, "PipelineStateCache: Compact"); AZStd::unique_lock lock(m_mutex); // Merge the pending cache into the read-only cache. diff --git a/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp b/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp index a381209d2b..69f7002435 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/RHISystem.cpp @@ -187,8 +187,7 @@ namespace AZ void RHISystem::FrameUpdate(FrameGraphCallback frameGraphCallback) { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("RHI", "RHISystem: FrameUpdate"); + AZ_PROFILE_SCOPE(RHI, "RHISystem: FrameUpdate"); { AZ_PROFILE_SCOPE(RHI, "main per-frame work"); @@ -201,7 +200,7 @@ namespace AZ * own RHI scopes to the frame scheduler. This happens prior to the RPI pass graph registration. */ { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RHI", "RHISystem: FrameUpdate: OnFramePrepare"); + AZ_PROFILE_SCOPE(RHI, "RHISystem: FrameUpdate: OnFramePrepare"); RHISystemNotificationBus::Broadcast(&RHISystemNotificationBus::Events::OnFramePrepare, m_frameScheduler); } diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/AsyncUploadQueue.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/AsyncUploadQueue.cpp index 29b210e17e..cb9dac3864 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/AsyncUploadQueue.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/AsyncUploadQueue.cpp @@ -196,7 +196,7 @@ namespace AZ AsyncUploadQueue::FramePacket* AsyncUploadQueue::BeginFramePacket() { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: BeginFramePacket"); AZ_Assert(!m_recordingFrame, "The previous frame packet isn't ended"); FramePacket* framePacket = &m_framePackets[m_frameIndex]; @@ -212,7 +212,7 @@ namespace AZ void AsyncUploadQueue::EndFramePacket(ID3D12CommandQueue* commandQueue) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: EndFramePacket"); AZ_Assert(m_recordingFrame, "The frame packet wasn't started. You need to call StartFramePacket first."); AssertSuccess(m_commandList->Close()); @@ -229,7 +229,7 @@ namespace AZ // [GFX TODO][ATOM-4205] Stage/Upload 3D streaming images more efficiently. uint64_t AsyncUploadQueue::QueueUpload(const RHI::StreamingImageExpandRequest& request, uint32_t residentMip) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: QueueUpload"); uint64_t fenceValue = m_uploadFence.Increment(); @@ -475,7 +475,7 @@ namespace AZ void AsyncUploadQueue::WaitForUpload(uint64_t fenceValue) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: WaitForUpload"); if (!IsUploadFinished(fenceValue)) { @@ -489,7 +489,7 @@ namespace AZ void AsyncUploadQueue::ProcessCallbacks(uint64_t fenceValue) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: ProcessCallbacks"); AZStd::lock_guard lock(m_callbackMutex); while (m_callbacks.size() > 0 && m_callbacks.front().second <= fenceValue) { diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp index c5c71a9f43..efa9518ee1 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListBase.cpp @@ -33,7 +33,7 @@ namespace AZ void CommandListBase::Reset(ID3D12CommandAllocator* commandAllocator) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandListBase: Reset"); AZ_Assert(m_queuedBarriers.empty(), "Unflushed barriers in command list."); m_commandList->Reset(commandAllocator, nullptr); @@ -95,7 +95,7 @@ namespace AZ { if (m_queuedBarriers.size()) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandListBase: FlushBarriers"); m_commandList->ResourceBarrier((UINT)m_queuedBarriers.size(), m_queuedBarriers.data()); m_queuedBarriers.clear(); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListPool.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListPool.cpp index c31fe8ada8..94262064da 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListPool.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandListPool.cpp @@ -175,7 +175,7 @@ namespace AZ void CommandListAllocator::Collect() { - AZ_ATOM_PROFILE_FUNCTION("DX12", "CommandListAllocator: Collect"); + AZ_PROFILE_SCOPE(RHI, "CommandListAllocator: Collect(DX12)"); for (uint32_t queueIdx = 0; queueIdx < RHI::HardwareQueueClassCount; ++queueIdx) { m_commandListSubAllocators[queueIdx].ForEach([](Internal::CommandListSubAllocator& commandListSubAllocator) diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueue.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueue.cpp index 9d58217f01..0cde6aeb09 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueue.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueue.cpp @@ -195,7 +195,7 @@ namespace AZ void CommandQueue::UpdateTileMappings(CommandList& commandList) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueue: UpdateTileMappings"); for (const CommandList::TileMapRequest& request : commandList.GetTileMapRequests()) { const uint32_t tileCount = request.m_sourceRegionSize.NumTiles; @@ -229,7 +229,7 @@ namespace AZ void CommandQueue::WaitForIdle() { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueue: WaitForIdle"); Fence fence; fence.Init(m_device.get(), RHI::FenceState::Reset); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp index 96d621df59..eee137026d 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/CommandQueueContext.cpp @@ -101,7 +101,7 @@ namespace AZ void CommandQueueContext::WaitForIdle() { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueueContext: WaitForIdle"); for (uint32_t hardwareQueueIdx = 0; hardwareQueueIdx < RHI::HardwareQueueClassCount; ++hardwareQueueIdx) { if (m_commandQueues[hardwareQueueIdx]) @@ -113,7 +113,7 @@ namespace AZ void CommandQueueContext::Begin() { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueueContext: Begin"); { AZ_PROFILE_SCOPE(RHI, "Clearing Command Queue Timers"); @@ -131,8 +131,7 @@ namespace AZ void CommandQueueContext::End() { - AZ_PROFILE_FUNCTION(RHI); - AZ_ATOM_PROFILE_FUNCTION("DX12", "CommandQueueContext: End"); + AZ_PROFILE_SCOPE(RHI, "CommandQueueContext: End"); QueueGpuSignals(m_frameFences[m_currentFrameIndex]); @@ -146,7 +145,6 @@ namespace AZ { AZ_PROFILE_SCOPE(RHI, "Wait and Reset Fence"); - AZ_ATOM_PROFILE_TIME_GROUP_REGION("DX12", "CommandQueueContext: Wait on Fences"); FenceEvent event("FrameFence"); m_frameFences[m_currentFrameIndex].Wait(event); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp index 942b514b97..816b904e16 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/DescriptorContext.cpp @@ -341,7 +341,7 @@ namespace AZ void DescriptorContext::GarbageCollect() { - AZ_ATOM_PROFILE_FUNCTION("DX12", "DescriptorContext: GarbageCollect"); + AZ_PROFILE_SCOPE(RHI, "DescriptorContext: GarbageCollect(DX12)"); for (const auto& itr : m_platformLimitsDescriptor->m_descriptorHeapLimits) { for (uint32_t shaderVisibleIdx = 0; shaderVisibleIdx < PlatformLimitsDescriptor::NumHeapFlags; ++shaderVisibleIdx) diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp index 7eaafc3705..722d535cb1 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp @@ -204,7 +204,7 @@ namespace AZ RHI::MessageOutcome FrameGraphCompiler::CompileInternal(const RHI::FrameGraphCompileRequest& request) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileInternal(DX12)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileInternal(DX12)"); RHI::FrameGraph& frameGraph = *request.m_frameGraph; @@ -373,7 +373,7 @@ namespace AZ void FrameGraphCompiler::CompileResourceBarriers(Scope* rootScope, const RHI::FrameGraphAttachmentDatabase& attachmentDatabase) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileResourceBarriers(DX12)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileResourceBarriers(DX12)"); for (RHI::BufferFrameAttachment* bufferFrameAttachment : attachmentDatabase.GetBufferAttachments()) { @@ -394,7 +394,7 @@ namespace AZ ResourceTransitionLoggerNull logger(bufferFrameAttachment.GetId()); #endif - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileBufferBarriers(DX12)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileBufferBarriers(DX12)"); Buffer& buffer = static_cast(*bufferFrameAttachment.GetBuffer()); RHI::BufferScopeAttachment* scopeAttachment = bufferFrameAttachment.GetFirstScopeAttachment(); @@ -469,7 +469,7 @@ namespace AZ ResourceTransitionLoggerNull logger(imageFrameAttachment.GetId()); #endif - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileImageBarriers (DX12)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileImageBarriers (DX12)"); Image& image = static_cast(*imageFrameAttachment.GetImage()); RHI::ImageScopeAttachment* scopeAttachment = imageFrameAttachment.GetFirstScopeAttachment(); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/StagingMemoryAllocator.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/StagingMemoryAllocator.cpp index 1a32f06fa2..337b25a793 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/StagingMemoryAllocator.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/StagingMemoryAllocator.cpp @@ -55,7 +55,7 @@ namespace AZ void StagingMemoryAllocator::GarbageCollect() { - AZ_ATOM_PROFILE_FUNCTION("DX12", "StagingMemoryAllocator: GarbageCollect"); + AZ_PROFILE_SCOPE(RHI, "StagingMemoryAllocator: GarbageCollect(DX12)"); m_mediumBlockAllocators.ForEach([](MemoryLinearSubAllocator& subAllocator) { subAllocator.GarbageCollect(); diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp index 7f0c535383..fb11700e04 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/CommandQueueContext.cpp @@ -79,8 +79,8 @@ namespace AZ void CommandQueueContext::End() { - AZ_PROFILE_FUNCTION(RHI); - + AZ_PROFILE_SCOPE(RHI, "CommandQueueContext: End"); + QueueGpuSignals(m_frameFences[m_currentFrameIndex]); for (uint32_t hardwareQueueIdx = 0; hardwareQueueIdx < RHI::HardwareQueueClassCount; ++hardwareQueueIdx) { @@ -92,7 +92,6 @@ namespace AZ { AZ_PROFILE_SCOPE(RHI, "Wait and Reset Fence"); - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RHI", "CommandQueueContext: Wait on Fences"); //Synchronize the CPU with the GPU by waiting on the fence until signalled by the GPU. CPU can only go upto //RHI::Limits::Device::FrameCountMax frames ahead of the GPU diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/FrameGraphCompiler.cpp index 520e417f6b..a347ed9b12 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/FrameGraphCompiler.cpp @@ -33,7 +33,7 @@ namespace AZ RHI::MessageOutcome FrameGraphCompiler::CompileInternal(const RHI::FrameGraphCompileRequest& request) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileInternal(Metal)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileInternal(Metal)"); RHI::FrameGraph& frameGraph = *request.m_frameGraph; if (!RHI::CheckBitsAny(request.m_compileFlags, RHI::FrameSchedulerCompileFlags::DisableAsyncQueues)) { diff --git a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp index 90ba04c3db..567f89b6e0 100644 --- a/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp +++ b/Gems/Atom/RHI/Metal/Code/Source/RHI/SwapChain.cpp @@ -192,7 +192,7 @@ namespace AZ id SwapChain::RequestDrawable(bool isFrameCaptureEnabled) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "SwapChain::RequestDrawable"); + AZ_PROFILE_SCOPE(RHI, "SwapChain::RequestDrawable"); m_metalView.metalLayer.framebufferOnly = !isFrameCaptureEnabled; const uint32_t currentImageIndex = GetCurrentImageIndex(); if(m_drawables[currentImageIndex]) diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/AsyncUploadQueue.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/AsyncUploadQueue.cpp index 7a8499ab9d..2c3958cb65 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/AsyncUploadQueue.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/AsyncUploadQueue.cpp @@ -451,7 +451,7 @@ namespace AZ AsyncUploadQueue::FramePacket* AsyncUploadQueue::BeginFramePacket(Queue* queue) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: BeginFramePacket"); AZ_Assert(!m_recordingFrame, "The previous frame packet isn't ended."); auto& device = static_cast(GetDevice()); @@ -471,7 +471,7 @@ namespace AZ void AsyncUploadQueue::EndFramePacket(Queue* queue, Semaphore* semaphoreToSignal /*=nullptr*/) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: EndFramePacket"); AZ_Assert(m_recordingFrame, "The frame packet wasn't started. You need to call StartFramePacket first."); m_commandList->EndCommandBuffer(); @@ -636,7 +636,7 @@ namespace AZ void AsyncUploadQueue::ProcessCallback(const RHI::AsyncWorkHandle& handle) { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "AsyncUploadQueue: ProcessCallback"); AZStd::unique_lock lock(m_callbackListMutex); auto findIter = m_callbackList.find(handle); if (findIter != m_callbackList.end()) diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp index 39f0ac9d58..c0d34e2bf9 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/CommandQueueContext.cpp @@ -42,7 +42,7 @@ namespace AZ void CommandQueueContext::End() { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueueContext: End"); for (auto& commandQueue : m_commandQueues) { @@ -55,7 +55,6 @@ namespace AZ { AZ_PROFILE_SCOPE(RHI, "Wait on Fences"); - AZ_ATOM_PROFILE_FUNCTION("RHI", "CommandQueueContext: Wait on Fences"); FencesPerQueue& nextFences = m_frameFences[m_currentFrameIndex]; for (auto& fence : nextFences) @@ -79,7 +78,7 @@ namespace AZ void CommandQueueContext::WaitForIdle() { - AZ_PROFILE_FUNCTION(RHI); + AZ_PROFILE_SCOPE(RHI, "CommandQueueContext: WaitForIdle"); for (auto& commandQueue : m_commandQueues) { commandQueue->WaitForIdle(); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphCompiler.cpp index 372ebc273d..2a30e36e04 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/FrameGraphCompiler.cpp @@ -45,7 +45,7 @@ namespace AZ RHI::MessageOutcome FrameGraphCompiler::CompileInternal(const RHI::FrameGraphCompileRequest& request) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileInternal(Vulkan)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileInternal(Vulkan)"); AZ_Assert(request.m_frameGraph, "FrameGraph is null."); RHI::FrameGraph& frameGraph = *request.m_frameGraph; @@ -89,7 +89,7 @@ namespace AZ void FrameGraphCompiler::CompileResourceBarriers(const RHI::FrameGraphAttachmentDatabase& attachmentDatabase) { - AZ_ATOM_PROFILE_FUNCTION("RHI", "FrameGraphCompiler: CompileResourceBarriers(Vulkan)"); + AZ_PROFILE_SCOPE(RHI, "FrameGraphCompiler: CompileResourceBarriers(Vulkan)"); for (RHI::BufferFrameAttachment* bufferFrameAttachment : attachmentDatabase.GetBufferAttachments()) { diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h index 52af56ba0e..6ca3bca652 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h @@ -19,6 +19,11 @@ #include +namespace UnitTest +{ + class MaterialTests; +} + namespace AZ { class ReflectContext; @@ -40,6 +45,7 @@ namespace AZ friend class MaterialAssetCreator; friend class MaterialAssetHandler; friend class MaterialAssetCreatorCommon; + friend class UnitTest::MaterialTests; public: AZ_RTTI(MaterialAsset, "{522C7BE0-501D-463E-92C6-15184A2B7AD8}", AZ::Data::AssetData); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index bedf4fe989..f67343faa1 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -299,7 +299,7 @@ namespace AZ //work function void Process() override { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "AddObjectsToViewJob: Process"); const View::UsageFlags viewFlags = m_jobData->m_view->GetUsageFlags(); const RHI::DrawListMask drawListMask = m_jobData->m_view->GetDrawListMask(); @@ -645,7 +645,7 @@ namespace AZ uint32_t AddLodDataToView(const Vector3& pos, const Cullable::LodData& lodData, RPI::View& view) { #ifdef AZ_CULL_PROFILE_DETAILED - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "AddLodDataToView"); #endif const Matrix4x4& viewToClip = view.GetViewToClipMatrix(); @@ -725,7 +725,7 @@ namespace AZ void CullingScene::BeginCulling(const AZStd::vector& views) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "CullingScene: BeginCulling"); + AZ_PROFILE_SCOPE(RPI, "CullingScene: BeginCulling"); m_cullDataConcurrencyCheck.soft_lock(); m_debugCtx.ResetCullStats(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQuerySystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQuerySystem.cpp index 257bb689ee..8077038ea7 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQuerySystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQuerySystem.cpp @@ -75,7 +75,7 @@ namespace AZ void GpuQuerySystem::Update() { - AZ_ATOM_PROFILE_FUNCTION("RPI", "GpuQuerySystem: Update"); + AZ_PROFILE_SCOPE(RPI, "GpuQuerySystem: Update"); for (auto& queryPool : m_queryPoolArray) { if (queryPool) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp index d4a55d1c29..71e2eaa2f5 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Image/ImageSystem.cpp @@ -34,6 +34,8 @@ #include #include +AZ_DECLARE_BUDGET(RPI); + namespace AZ { namespace RPI @@ -171,7 +173,7 @@ namespace AZ void ImageSystem::Update() { - AZ_ATOM_PROFILE_FUNCTION("RPI", "ImageSystem: Update"); + AZ_PROFILE_SCOPE(RPI, "ImageSystem: Update"); AZStd::lock_guard lock(m_activeStreamingPoolMutex); for (StreamingImagePool* imagePool : m_activeStreamingPools) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp index 2dcaa70deb..d44ed40c7b 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp @@ -100,9 +100,15 @@ namespace AZ ShaderReloadNotificationBus::MultiHandler::BusConnect(shaderItem.GetShaderAsset().GetId()); } + // If this Init() is actually a re-initialize, we need to re-apply any overridden property values + // after loading the property values from the asset, so we save that data here. MaterialPropertyFlags prevOverrideFlags = m_propertyOverrideFlags; AZStd::vector prevPropertyValues = m_propertyValues; + // The property values are cleared to their default state to ensure that SetPropertyValue() does not early-return + // when called below. This is important when Init() is actually a re-initialize. + m_propertyValues.clear(); + // Initialize the shader runtime data like shader constant buffers and shader variants by applying the // material's property values. This will feed through the normal runtime material value-change data flow, which may // include custom property change handlers provided by the material type. @@ -504,7 +510,7 @@ namespace AZ MaterialPropertyValue& savedPropertyValue = m_propertyValues[index.GetIndex()]; - // If the property value didn't actually change, don't waste time running functors and compiling the changes + // If the property value didn't actually change, don't waste time running functors and compiling the changes. if (savedPropertyValue == value) { return false; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp index 6fd313e27f..50da470ec4 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp @@ -42,7 +42,7 @@ namespace AZ Data::Instance Model::CreateInternal(const Data::Asset& modelAsset) { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "Model: CreateInternal"); Data::Instance model = aznew Model(); const RHI::ResultCode resultCode = model->Init(modelAsset); @@ -56,7 +56,7 @@ namespace AZ RHI::ResultCode Model::Init(const Data::Asset& modelAsset) { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "Model: Init"); m_lods.resize(modelAsset->GetLodAssets().size()); @@ -128,7 +128,7 @@ namespace AZ bool Model::LocalRayIntersection(const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, float& distanceNormalized, AZ::Vector3& normal) const { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "Model: LocalRayIntersection"); if (!GetModelAsset()) { @@ -171,7 +171,7 @@ namespace AZ float& distanceNormalized, AZ::Vector3& normal) const { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "Model: RayIntersection"); const AZ::Vector3 clampedScale = nonUniformScale.GetMax(AZ::Vector3(AZ::MinTransformScale)); const AZ::Transform inverseTM = modelTransform.GetInverse(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp index ef9521d23c..a1651defbc 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLodUtils.cpp @@ -27,7 +27,7 @@ namespace AZ ModelLodIndex SelectLod(const View* view, const Vector3& position, const Model& model, ModelLodIndex lodOverride) { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "ModelLodUtils: SelectLod"); ModelLodIndex lodIndex; if (model.GetLodCount() == 1) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp index a8feeb1047..34d72338dc 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp @@ -169,7 +169,7 @@ namespace AZ void PassSystem::RemovePasses() { m_state = PassSystemState::RemovingPasses; - AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: RemovePasses"); + AZ_PROFILE_SCOPE(RPI, "PassSystem: RemovePasses"); if (!m_removePassList.empty()) { @@ -189,8 +189,7 @@ namespace AZ void PassSystem::BuildPasses() { m_state = PassSystemState::BuildingPasses; - AZ_PROFILE_FUNCTION(RPI); - AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: BuildPassAttachments"); + AZ_PROFILE_SCOPE(RPI, "PassSystem: BuildPasses"); m_passHierarchyChanged = m_passHierarchyChanged || !m_buildPassList.empty(); @@ -239,8 +238,7 @@ namespace AZ void PassSystem::InitializePasses() { m_state = PassSystemState::InitializingPasses; - AZ_PROFILE_FUNCTION(RPI); - AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: BuildPassAttachments"); + AZ_PROFILE_SCOPE(RPI, "PassSystem: InitializePasses"); m_passHierarchyChanged = m_passHierarchyChanged || !m_initializePassList.empty(); @@ -277,7 +275,6 @@ namespace AZ void PassSystem::Validate() { m_state = PassSystemState::ValidatingPasses; - AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: Validate"); if (PassValidation::IsEnabled()) { @@ -286,7 +283,7 @@ namespace AZ return; } - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "PassSystem: Validate"); PassValidationResults validationResults; m_rootPass->Validate(validationResults); @@ -298,7 +295,7 @@ namespace AZ void PassSystem::ProcessQueuedChanges() { - AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: ProcessQueuedChanges"); + AZ_PROFILE_SCOPE(RPI, "PassSystem: ProcessQueuedChanges"); RemovePasses(); BuildPasses(); InitializePasses(); @@ -307,8 +304,7 @@ namespace AZ void PassSystem::FrameUpdate(RHI::FrameGraphBuilder& frameGraphBuilder) { - AZ_PROFILE_FUNCTION(RPI); - AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: FrameUpdate"); + AZ_PROFILE_SCOPE(RPI, "PassSystem: FrameUpdate"); ResetFrameStatistics(); ProcessQueuedChanges(); @@ -317,14 +313,14 @@ namespace AZ Pass::FramePrepareParams params{ &frameGraphBuilder }; { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Pass: FrameBegin"); + AZ_PROFILE_SCOPE(RPI, "Pass: FrameBegin"); m_rootPass->FrameBegin(params); } } void PassSystem::FrameEnd() { - AZ_ATOM_PROFILE_FUNCTION("RHI", "PassSystem: FrameEnd"); + AZ_PROFILE_SCOPE(RHI, "PassSystem: FrameEnd"); m_state = PassSystemState::FrameEnd; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp index 4c923d4a1a..d9f98c11d3 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RasterPass.cpp @@ -216,7 +216,7 @@ namespace AZ void RasterPass::CompileResources(const RHI::FrameGraphCompileContext& context) { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "RasterPass: CompileResources"); if (m_shaderResourceGroup == nullptr) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp index 500bf21628..5df1c655d6 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RPISystem.cpp @@ -233,7 +233,7 @@ namespace AZ void RPISystem::OnSystemTick() { - AZ_ATOM_PROFILE_FUNCTION("RPI", "RPISystem: OnSystemTick"); + AZ_PROFILE_SCOPE(RPI, "RPISystem: OnSystemTick"); // Image system update is using system tick but not game tick so it can stream images in background even game is pausing m_imageSystem.Update(); @@ -245,7 +245,7 @@ namespace AZ { return; } - AZ_ATOM_PROFILE_FUNCTION("RPI", "RPISystem: SimulationTick"); + AZ_PROFILE_SCOPE(RPI, "RPISystem: SimulationTick"); AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit); @@ -273,8 +273,7 @@ namespace AZ return; } - AZ_PROFILE_FUNCTION(RPI); - AZ_ATOM_PROFILE_FUNCTION("RPI", "RPISystem: RenderTick"); + AZ_PROFILE_SCOPE(RPI, "RPISystem: RenderTick"); // Query system update is to increment the frame count m_querySystem.Update(); @@ -301,7 +300,7 @@ namespace AZ }); { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "RPISystem: FrameEnd"); + AZ_PROFILE_SCOPE(RPI, "RPISystem: FrameEnd"); m_dynamicDraw.FrameEnd(); m_passSystem.FrameEnd(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp index 7f0ab9c8aa..409a084e4f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp @@ -397,7 +397,7 @@ namespace AZ void RenderPipeline::OnStartFrame() { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame"); m_lastRenderStartTime = m_lastRenderRequestTime; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp index 9fa49f7f2a..778548f14c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp @@ -350,7 +350,7 @@ namespace AZ void Scene::Simulate([[maybe_unused]] const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: Simulate"); + AZ_PROFILE_SCOPE(RPI, "Scene: Simulate"); m_simulationTime = tickInfo.m_currentGameTime; @@ -389,7 +389,7 @@ namespace AZ { if (completionJob) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: WaitAndCleanCompletionJob"); + AZ_PROFILE_SCOPE(RPI, "Scene: WaitAndCleanCompletionJob"); //[GFX TODO]: the completion job should start earlier and wait for completion here completionJob->StartAndWaitForCompletion(); delete completionJob; @@ -422,11 +422,10 @@ namespace AZ void Scene::PrepareRender([[maybe_unused]]const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) { - AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: PrepareRender"); + AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender"); { AZ_PROFILE_SCOPE(RPI, "WaitForSimulationCompletion"); - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "WaitForSimulationCompletion"); WaitAndCleanCompletionJob(m_simulationCompletion); } @@ -435,7 +434,7 @@ namespace AZ // Get active pipelines which need to be rendered and notify them of an impending frame. AZStd::vector activePipelines; { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Scene: OnPrepareFrame"); + AZ_PROFILE_SCOPE(RPI, "Scene: OnPrepareFrame"); for (auto& pipeline : m_pipelines) { pipeline->OnPrepareFrame(); @@ -449,7 +448,7 @@ namespace AZ // Get active pipelines which need to be rendered and notify them frame started for (const auto& pipeline : activePipelines) { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Scene: OnStartFrame"); + AZ_PROFILE_SCOPE(RPI, "Scene: OnStartFrame"); pipeline->OnStartFrame(); } @@ -468,7 +467,7 @@ namespace AZ { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Setup Views"); + AZ_PROFILE_SCOPE(RPI, "Setup Views"); // Collect persistent views from all pipelines to be rendered AZStd::map persistentViews; @@ -506,8 +505,7 @@ namespace AZ } { - AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets"); - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "CollectDrawPackets"); + AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets"); AZ::JobCompletion* collectDrawPacketsCompletion = aznew AZ::JobCompletion(); // Launch FeatureProcessor::Render() jobs @@ -550,14 +548,13 @@ namespace AZ // Add dynamic draw data for all the views if (m_dynamicDrawSystem) { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "DynamicDraw SubmitDrawData"); + AZ_PROFILE_SCOPE(RPI, "DynamicDraw SubmitDrawData"); m_dynamicDrawSystem->SubmitDrawData(this, m_renderPacket.m_views); } } { AZ_PROFILE_BEGIN(RPI, "FinalizeDrawLists"); - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "FinalizeDrawLists"); if (jobPolicy == RHI::JobPolicy::Serial) { for (auto& view : m_renderPacket.m_views) @@ -586,14 +583,14 @@ namespace AZ } { - AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Scene OnEndPrepareRender"); + AZ_PROFILE_SCOPE(RPI, "Scene OnEndPrepareRender"); SceneNotificationBus::Event(GetId(), &SceneNotification::OnEndPrepareRender); } } void Scene::OnFrameEnd() { - AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: OnFrameEnd"); + AZ_PROFILE_SCOPE(RPI, "Scene: OnFrameEnd"); bool didRender = false; for (auto& pipeline : m_pipelines) { @@ -730,7 +727,7 @@ namespace AZ void Scene::RebuildPipelineStatesLookup() { - AZ_ATOM_PROFILE_FUNCTION("RPI", "Scene: RebuildPipelineStatesLookup"); + AZ_PROFILE_SCOPE(RPI, "Scene: RebuildPipelineStatesLookup"); m_pipelineStatesLookup.clear(); AZStd::queue parents; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp index 9e633077e7..528d32e217 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Shader/Metrics/ShaderMetricsSystem.cpp @@ -113,7 +113,7 @@ namespace AZ return; } - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "ShaderMetricsSystem: RequestShaderVariant"); AZStd::lock_guard lock(m_metricsMutex); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp index bdb3ea8899..c2356cea45 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/View.cpp @@ -239,7 +239,7 @@ namespace AZ void View::FinalizeDrawLists() { - AZ_PROFILE_FUNCTION(RPI); + AZ_PROFILE_SCOPE(RPI, "View: FinalizeDrawLists"); m_drawListContext.FinalizeLists(); if (m_passesByDrawList) { diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialTests.cpp index d82a17e1f3..f202747bf6 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialTests.cpp @@ -182,6 +182,13 @@ namespace UnitTest EXPECT_EQ(srgData.GetImageView(srgData.FindShaderInputImageIndex(Name{ "m_image" }), 0), m_testImage->GetImageView()); EXPECT_EQ(srgData.GetConstant(srgData.FindShaderInputConstantIndex(Name{ "m_enum" })), 2u); } + + //! Provides write access to private material asset property values, primarily for simulating + //! MaterialAsset hot reload. + MaterialPropertyValue& AccessMaterialAssetPropertyValue(Data::Asset materialAsset, Name propertyName) + { + return materialAsset->m_propertyValues[materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyName).GetIndex()]; + } }; TEST_F(MaterialTests, TestCreateVsFindOrCreate) @@ -313,6 +320,30 @@ namespace UnitTest EXPECT_EQ(srgData.GetConstant(srgData.FindShaderInputConstantIndex(Name{ "m_uint" })), 42u); } + TEST_F(MaterialTests, TestSetPropertyValueWhenValueIsUnchanged) + { + Data::Instance material = Material::FindOrCreate(m_testMaterialAsset); + + EXPECT_TRUE(material->SetPropertyValue(material->FindPropertyIndex(Name{ "MyFloat" }), 2.5f)); + + ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName()); + EXPECT_TRUE(material->Compile()); + + // Taint the SRG so we can check whether it was set by the SetPropertyValue() calls below. + const RHI::ShaderResourceGroup* srg = material->GetRHIShaderResourceGroup(); + const RHI::ShaderResourceGroupData& srgData = srg->GetData(); + const_cast(&srgData)->SetConstant(m_testMaterialSrgLayout->FindShaderInputConstantIndex(Name{"m_float"}), 0.0f); + + // Set the properties to the same values as before + EXPECT_FALSE(material->SetPropertyValue(material->FindPropertyIndex(Name{ "MyFloat" }), 2.5f)); + + ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName()); + EXPECT_FALSE(material->Compile()); + + // Make sure the SRG is still tainted, because the SetPropertyValue() functions weren't processed + EXPECT_EQ(srgData.GetConstant(srgData.FindShaderInputConstantIndex(Name{ "m_float" })), 0.0f); + } + TEST_F(MaterialTests, TestImageNotProvided) { Data::Asset materialAssetWithEmptyImage; @@ -785,4 +816,45 @@ namespace UnitTest EXPECT_EQ((float)inputColor.GetElement(i), (float)colorFromMaterial.GetElement(i)); } } + + TEST_F(MaterialTests, TestReinitializeForHotReload) + { + Data::Instance material = Material::FindOrCreate(m_testMaterialAsset); + const RHI::ShaderResourceGroupData* srgData = &material->GetRHIShaderResourceGroup()->GetData(); + ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName()); + + // Check the default property value + EXPECT_EQ(material->GetPropertyValue(material->FindPropertyIndex(Name{ "MyFloat" })), 1.5f); + EXPECT_EQ(srgData->GetConstant(srgData->FindShaderInputConstantIndex(Name{ "m_float" })), 1.5f); + EXPECT_EQ(material->GetPropertyValue(material->FindPropertyIndex(Name{ "MyInt" })), -2); + EXPECT_EQ(srgData->GetConstant(srgData->FindShaderInputConstantIndex(Name{ "m_int" })), -2); + + // Override a property value + EXPECT_TRUE(material->SetPropertyValue(material->FindPropertyIndex(Name{ "MyFloat" }), 5.5f)); + + // Apply the changes + EXPECT_TRUE(material->Compile()); + ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName()); + + // Check the updated values with one overridden + EXPECT_EQ(material->GetPropertyValue(material->FindPropertyIndex(Name{ "MyFloat" })), 5.5f); + EXPECT_EQ(srgData->GetConstant(srgData->FindShaderInputConstantIndex(Name{ "m_float" })), 5.5f); + EXPECT_EQ(material->GetPropertyValue(material->FindPropertyIndex(Name{ "MyInt" })), -2); + EXPECT_EQ(srgData->GetConstant(srgData->FindShaderInputConstantIndex(Name{ "m_int" })), -2); + + // Pretend there was a hot-reload with new default values + AccessMaterialAssetPropertyValue(m_testMaterialAsset, Name{"MyFloat"}) = 0.5f; + AccessMaterialAssetPropertyValue(m_testMaterialAsset, Name{"MyInt"}) = -7; + AZ::Data::AssetBus::Event(m_testMaterialAsset.GetId(), &AZ::Data::AssetBus::Handler::OnAssetReloaded, m_testMaterialAsset); + srgData = &material->GetRHIShaderResourceGroup()->GetData(); + ProcessQueuedSrgCompilations(m_testMaterialShaderAsset, m_testMaterialSrgLayout->GetName()); + + // Make sure the override values are still there + EXPECT_EQ(srgData->GetConstant(srgData->FindShaderInputConstantIndex(Name{ "m_float" })), 5.5f); + EXPECT_EQ(material->GetPropertyValue(material->FindPropertyIndex(Name{ "MyFloat" })), 5.5f); + + // Make sure the new default value is applied where it was not overridden + EXPECT_EQ(material->GetPropertyValue(material->FindPropertyIndex(Name{ "MyInt" })), -7); + EXPECT_EQ(srgData->GetConstant(srgData->FindShaderInputConstantIndex(Name{ "m_int" })), -7); + } } diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h index ea4dd20250..a649fcf0f7 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h @@ -174,7 +174,7 @@ namespace AZ AZStd::unordered_map> m_savedData; // Region color cache - AZStd::unordered_map m_regionColorMap; + AZStd::unordered_map m_regionColorMap; // Tracks the frame boundaries AZStd::vector m_frameEndTicks = { INT64_MIN }; diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl index 61f68197b6..a573e3019a 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl @@ -124,7 +124,7 @@ namespace AZ m_cpuTimingStatisticsWhenPause = currentCpuTimingStatistics; CollectFrameData(); - CullFrameData(currentCpuTimingStatistics); + CullFrameData(currentCpuTimingStatistics); // Only listen to system ticks when the profiler is active if (!SystemTickBus::Handler::BusIsConnected()) @@ -148,7 +148,7 @@ namespace AZ } } ImGui::End(); - + if (m_captureToFile) { AZStd::sys_time_t timeNow = AZStd::GetTimeNowSecond(); @@ -325,7 +325,7 @@ namespace AZ { const bool ascending = sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending; const ImS16 columnToSort = sortSpecs->Specs->ColumnIndex; - + switch (columnToSort) { case (0): // Sort by group name @@ -343,7 +343,7 @@ namespace AZ case (4): // Sort by invocations AZStd::sort(m_tableData.begin(), m_tableData.end(), TableRow::TableRowCompareFunctor(&TableRow::m_invocationsLastFrame, ascending)); break; - case (5): // Sort by total time + case (5): // Sort by total time AZStd::sort(m_tableData.begin(), m_tableData.end(), TableRow::TableRowCompareFunctor(&TableRow::m_lastFrameTotalTicks, ascending)); break; } @@ -401,7 +401,7 @@ namespace AZ } DrawTable(); - } + } } inline void ImGuiCpuProfiler::DrawFilePicker() @@ -460,17 +460,17 @@ namespace AZ const auto [groupRegionNameItr, wasGroupRegionNameInserted] = m_deserializedGroupRegionNamePool.emplace(groupNameItr->c_str(), regionNameItr->c_str()); - const RHI::CachedTimeRegion newRegion(&(*groupRegionNameItr), entry.m_stackDepth, entry.m_startTick, entry.m_endTick); + const RHI::CachedTimeRegion newRegion(*groupRegionNameItr, entry.m_stackDepth, entry.m_startTick, entry.m_endTick); m_savedData[entry.m_threadId].push_back(newRegion); - // Since we don't serialize the frame boundaries, we need to use the RPI's OnSystemTick event as a heuristic. + // Since we don't serialize the frame boundaries, we need to use the RPI's OnSystemTick event as a heuristic. const static Name frameBoundaryName = Name("RPISystem: OnSystemTick"); if (entry.m_regionName == frameBoundaryName) { m_frameEndTicks.push_back(entry.m_endTick); - } + } - // Update running statistics + // Update running statistics if (!m_groupRegionMap[*groupNameItr].contains(*regionNameItr)) { m_groupRegionMap[*groupNameItr][*regionNameItr].m_groupName = *groupNameItr; @@ -487,7 +487,7 @@ namespace AZ // Invariant: each vector in m_savedData must be sorted so that we can efficiently cull region data. for (auto& [threadId, singleThreadData] : m_savedData) { - AZStd::sort(singleThreadData.begin(), singleThreadData.end(), + AZStd::sort(singleThreadData.begin(), singleThreadData.end(), [](const TimeRegion& lhs, const TimeRegion& rhs) { return lhs.m_startTick < rhs.m_startTick; @@ -669,7 +669,7 @@ namespace AZ // Iterate through the entire TimeRegionMap and copy the data since it will get deleted on the next frame for (const auto& [threadId, singleThreadRegionMap] : timeRegionMap) { - const size_t threadIdHashed = AZStd::hash{}(threadId); + const size_t threadIdHashed = AZStd::hash{}(threadId); // The profiler can sometime return threads without any profiling events when dropping threads, FIXME(ATOM-15949) if (singleThreadRegionMap.size() == 0) { @@ -686,7 +686,7 @@ namespace AZ newVisualizerData.push_back(region); // Copies // Also update the statistical view's data - const AZStd::string& groupName = region.m_groupRegionName->m_groupName; + const AZStd::string& groupName = region.m_groupRegionName.m_groupName; if (!m_groupRegionMap[groupName].contains(regionName)) { @@ -765,7 +765,7 @@ namespace AZ inline void ImGuiCpuProfiler::DrawBlock(const TimeRegion& block, u64 targetRow) { // Don't draw anything if the user is searching for regions and this block doesn't pass the filter - if (!m_visualizerHighlightFilter.PassFilter(block.m_groupRegionName->m_regionName)) + if (!m_visualizerHighlightFilter.PassFilter(block.m_groupRegionName.m_regionName)) { return; } @@ -798,7 +798,7 @@ namespace AZ if (regionPixelWidth > maxCharWidth) // We can draw at least one character { const AZStd::string label = - AZStd::string::format("%s/ %s", block.m_groupRegionName->m_groupName, block.m_groupRegionName->m_regionName); + AZStd::string::format("%s/ %s", block.m_groupRegionName.m_groupName, block.m_groupRegionName.m_regionName); const float textWidth = ImGui::CalcTextSize(label.c_str()).x; if (regionPixelWidth < textWidth) // Not enough space in the block to draw the whole name, draw clipped text. @@ -809,7 +809,7 @@ namespace AZ // so we must adjust for the scale manually. const float scaleFactor = ImGui::GetIO().FontGlobalScale; const float fontSize = ImGui::GetFont()->FontSize * scaleFactor; - + ImGui::GetFont()->RenderText(drawList, fontSize, startPoint, IM_COL32_WHITE, clipRect, label.c_str(), 0); } else // We have enough space to draw the entire label, draw and center text. @@ -828,7 +828,7 @@ namespace AZ if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { m_enableVisualizer = false; - const auto newFilter = AZStd::string(block.m_groupRegionName->m_regionName); + const auto newFilter = AZStd::string(block.m_groupRegionName.m_regionName); m_timedRegionFilter = ImGuiTextFilter(newFilter.c_str()); m_timedRegionFilter.Build(); } @@ -836,7 +836,7 @@ namespace AZ drawList->AddRect(startPoint, endPoint, ImGui::GetColorU32({ 1, 1, 1, 1 }), 0.0, 0, 1.5); ImGui::BeginTooltip(); - ImGui::Text("%s::%s", block.m_groupRegionName->m_groupName, block.m_groupRegionName->m_regionName); + ImGui::Text("%s::%s", block.m_groupRegionName.m_groupName, block.m_groupRegionName.m_regionName); ImGui::Text("Execution time: %.3f ms", CpuProfilerImGuiHelper::TicksToMs(block.m_endTick - block.m_startTick)); ImGui::Text("Ticks %lld => %lld", block.m_startTick, block.m_endTick); ImGui::EndTooltip(); @@ -846,10 +846,10 @@ namespace AZ inline ImU32 ImGuiCpuProfiler::GetBlockColor(const TimeRegion& block) { // Use the GroupRegionName pointer a key into the cache, equal regions will have equal pointers - const GroupRegionName* key = block.m_groupRegionName; - if (m_regionColorMap.contains(key)) // Cache hit + const GroupRegionName& key = block.m_groupRegionName; + if (auto iter = m_regionColorMap.find(key); iter != m_regionColorMap.end()) // Cache hit { - return ImGui::GetColorU32(m_regionColorMap[key]); + return ImGui::GetColorU32(iter->second); } // Cache miss, generate a new random color diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt index bf28ed3f14..e68681315e 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/CMakeLists.txt @@ -109,6 +109,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) RUNTIME_DEPENDENCIES Gem::Atom_RPI.Editor Gem::Atom_Feature_Common.Editor + Legacy::EditorCommon ) # The AtomLyIntegration_CommonFeatures.Editor module is used for Builders and Tools diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp index cbb30e6cd8..d8cae4564d 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp @@ -231,7 +231,7 @@ namespace SurfaceData void SurfaceDataMeshComponent::UpdateMeshData() { - AZ_PROFILE_FUNCTION(Entity); + AZ_PROFILE_SCOPE(Entity, "SurfaceDataMeshComponent: UpdateMeshData"); bool meshValidBeforeUpdate = false; bool meshValidAfterUpdate = false; diff --git a/Gems/Camera/Code/CMakeLists.txt b/Gems/Camera/Code/CMakeLists.txt index 3fbeec0908..138f0fda85 100644 --- a/Gems/Camera/Code/CMakeLists.txt +++ b/Gems/Camera/Code/CMakeLists.txt @@ -61,6 +61,8 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) Legacy::EditorCommon AZ::AzToolsFramework Gem::Camera.Static + RUNTIME_DEPENDENCIES + Legacy::EditorCommon ) # tools and builders use the above module. diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp index dbc25d7a60..bd5a14f772 100644 --- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp @@ -715,12 +715,15 @@ namespace CommandSystem // restore the workspace dirty flag GetCommandManager()->SetWorkspaceDirtyFlag(m_oldWorkspaceDirtyFlag); - AZStd::string resultString; - GetCommandManager()->ExecuteCommandInsideCommand("Unselect -animGraphIndex SELECT_ALL", resultString); + MCore::CommandGroup commandGroup; + commandGroup.AddCommandString("RecorderClear"); + commandGroup.AddCommandString("Unselect -animGraphIndex SELECT_ALL"); if (animGraph) { - GetCommandManager()->ExecuteCommandInsideCommand(AZStd::string::format("Select -animGraphID %d", animGraph->GetID()), resultString); + commandGroup.AddCommandString(AZStd::string::format("Select -animGraphID %d", animGraph->GetID())); } + AZStd::string resultString; + GetCommandManager()->ExecuteCommandGroupInsideCommand(commandGroup, resultString); return true; } diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h index 65bac09726..d09de1ad0d 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h @@ -35,7 +35,7 @@ namespace Multiplayer using EntityMigrationStartEvent = AZ::Event; using EntityMigrationEndEvent = AZ::Event<>; using EntityServerMigrationEvent = AZ::Event; - using EntityPreRenderEvent = AZ::Event; + using EntityPreRenderEvent = AZ::Event; using EntityCorrectionEvent = AZ::Event<>; //! @class NetBindComponent @@ -118,7 +118,7 @@ namespace Multiplayer void NotifyMigrationStart(ClientInputId migratedInputId); void NotifyMigrationEnd(); void NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId); - void NotifyPreRender(float deltaTime, float blendFactor); + void NotifyPreRender(float deltaTime); void NotifyCorrection(); void AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler); diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h index 914aeaadd3..a018279a79 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h @@ -29,24 +29,9 @@ namespace Multiplayer void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; private: - void OnPreRender(float deltaTime, float blendFactor); + void OnPreRender(float deltaTime); void OnCorrection(); - void OnRotationChangedEvent(const AZ::Quaternion& rotation); - void OnTranslationChangedEvent(const AZ::Vector3& translation); - void OnScaleChangedEvent(float scale); - void OnResetCountChangedEvent(); - - void UpdateTargetHostFrameId(); - - AZ::Transform m_previousTransform = AZ::Transform::CreateIdentity(); - AZ::Transform m_targetTransform = AZ::Transform::CreateIdentity(); - - AZ::Event::Handler m_rotationEventHandler; - AZ::Event::Handler m_translationEventHandler; - AZ::Event::Handler m_scaleEventHandler; - AZ::Event::Handler m_resetCountEventHandler; - EntityPreRenderEvent::Handler m_entityPreRenderEventHandler; EntityCorrectionEvent::Handler m_entityCorrectionEventHandler; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h index 4f714068db..e32b6188eb 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h @@ -193,15 +193,13 @@ namespace Multiplayer m_previousHostFrameId = time->GetHostFrameId(); m_previousHostTimeMs = time->GetHostTimeMs(); m_previousRewindConnectionId = time->GetRewindingConnectionId(); - time->AlterTime(frameId, timeMs, connectionId); m_previousBlendFactor = time->GetHostBlendFactor(); - time->AlterBlendFactor(blendFactor); + time->AlterTime(frameId, timeMs, blendFactor, connectionId); } inline ~ScopedAlterTime() { INetworkTime* time = GetNetworkTime(); - time->AlterTime(m_previousHostFrameId, m_previousHostTimeMs, m_previousRewindConnectionId); - time->AlterBlendFactor(m_previousBlendFactor); + time->AlterTime(m_previousHostFrameId, m_previousHostTimeMs, m_previousBlendFactor, m_previousRewindConnectionId); } private: HostFrameId m_previousHostFrameId = InvalidHostFrameId; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h index ae47aa8373..c12cbb660b 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h @@ -52,12 +52,6 @@ namespace Multiplayer //! @return the ConnectionId of the connection requesting the rewind operation virtual AzNetworking::ConnectionId GetRewindingConnectionId() const = 0; - //! Get the controlling connection that may be currently altering global game time. - //! Note this abstraction is required at a relatively high level to allow for 'don't rewind the shooter' semantics - //! @param rewindConnectionId if this parameter matches the current rewindConnectionId, it will return the unaltered hostFrameId - //! @return the HostFrameId taking into account the provided rewinding connectionId - virtual HostFrameId GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const = 0; - //! Forcibly sets the current network time to the provided frameId and game time in milliseconds. //! @param frameId the new HostFrameId to use //! @param timeMs the new HostTimeMs to use @@ -66,12 +60,9 @@ namespace Multiplayer //! Alters the current HostFrameId and binds that alteration to the provided ConnectionId. //! @param frameId the new HostFrameId to use //! @param timeMs the new HostTimeMs to use + //! @param blendFactor the factor used to blend between values at the current and previous HostFrameId //! @param rewindConnectionId the rewinding ConnectionId - virtual void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) = 0; - - //! Alters the current Host blend factor. Used to drive interpolation in rewound states. - //! @param blendFactor the blend factor to use - virtual void AlterBlendFactor(float blendFactor) = 0; + virtual void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, float blendFactor, AzNetworking::ConnectionId rewindConnectionId) = 0; //! Syncs all entities contained within a volume to the current rewind state. //! @param rewindVolume the volume to rewind entities within (needed for physics entities) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h index a8af564c15..152f6f47a7 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h @@ -60,7 +60,7 @@ namespace Multiplayer //! @return value in const base type form const BASE_TYPE& Get() const; - //! Const base type retriever for one host frame behind Get(). Only intended for use in SyncRewind contexts. + //! Const base type retriever for one host frame behind Get() when contextually appropriate, otherwise identical to Get(). //! @return value in const base type form const BASE_TYPE& GetPrevious() const; @@ -86,9 +86,13 @@ namespace Multiplayer private: //! Returns what the appropriate current time is for this rewindable property. - //! @return the appropriate current time is for this rewindable property + //! @return the appropriate current time for this rewindable property HostFrameId GetCurrentTimeForProperty() const; + //! Returns what the appropriate previous time is for this rewindable property. + //! @return the appropriate previous time for this rewindable property + HostFrameId GetPreviousTimeForProperty() const; + //! Updates the latest value for this object instance, if frameTime represents a current or future time. //! Any attempts to set old values on the object will fail //! @param value the new value to set in the object history diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.inl b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.inl index b0c9bc0c46..9183a1e9da 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.inl +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.inl @@ -69,7 +69,7 @@ namespace Multiplayer template inline const BASE_TYPE& RewindableObject::GetPrevious() const { - return GetValueForTime(GetCurrentTimeForProperty() - HostFrameId(1)); + return GetValueForTime(GetPreviousTimeForProperty()); } template @@ -118,7 +118,22 @@ namespace Multiplayer inline HostFrameId RewindableObject::GetCurrentTimeForProperty() const { INetworkTime* networkTime = Multiplayer::GetNetworkTime(); - return networkTime->GetHostFrameIdForRewindingConnection(m_owningConnectionId); + if (networkTime->IsTimeRewound() && (m_owningConnectionId == networkTime->GetRewindingConnectionId())) + { + return networkTime->GetUnalteredHostFrameId(); + } + return networkTime->GetHostFrameId(); + } + + template + inline HostFrameId RewindableObject::GetPreviousTimeForProperty() const + { + INetworkTime* networkTime = Multiplayer::GetNetworkTime(); + if (networkTime->IsTimeRewound() && (m_owningConnectionId == networkTime->GetRewindingConnectionId())) + { + return networkTime->GetUnalteredHostFrameId(); + } + return networkTime->GetHostFrameId() - HostFrameId(1); } template diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja index 0f62da11f3..41f0fa1b02 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja @@ -7,21 +7,21 @@ {% macro DeclareNetworkPropertyGetter(Property) %} {% set PropertyName = UpperFirst(Property.attrib['Name']) %} {% if Property.attrib['Container'] == 'Array' %} -{% if Property.attrib['IsRewindable']|booleanTrue %} +{% if Property.attrib['IsRewindable']|booleanTrue %} const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& Get{{ PropertyName }}Array() const; -{% else %} +{% else %} const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& Get{{ PropertyName }}Array() const; -{% endif %} +{% endif %} const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}(int32_t index) const; {% if Property.attrib['GenerateEventBindings']|booleanTrue %} void {{ PropertyName }}AddEvent(AZ::Event::Handler& handler); {% endif %} {% elif Property.attrib['Container'] == 'Vector' %} -{% if Property.attrib['IsRewindable']|booleanTrue %} +{% if Property.attrib['IsRewindable']|booleanTrue %} const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& Get{{ PropertyName }}Vector() const; -{% else %} +{% else %} const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& Get{{ PropertyName }}Vector() const; -{% endif %} +{% endif %} const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}(int32_t index) const; const {{ Property.attrib['Type'] }}& {{ PropertyName }}GetBack() const; uint32_t {{ PropertyName }}GetSize() const; @@ -31,6 +31,9 @@ void {{ PropertyName }}SizeChangedAddEvent(AZ::Event::Handler& handler {% endif %} {% else %} const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}() const; +{% if Property.attrib['IsRewindable']|booleanTrue %} +const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}Previous() const; +{% endif %} {% if Property.attrib['GenerateEventBindings']|booleanTrue %} void {{ PropertyName }}AddEvent(AZ::Event<{{ Property.attrib['Type'] }}>::Handler& handler); {% endif %} diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index e57e8dad2f..eb4b81ea96 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -3,11 +3,11 @@ {% macro LowerFirst(text) %}{{ text[0] | lower}}{{ text[1:] }}{% endmacro %} {% macro DefineNetworkPropertyGet(ClassName, Property, Prefix = '') %} {% if Property.attrib['Container'] == 'Array' %} -{% if Property.attrib['IsRewindable']|booleanTrue %} +{% if Property.attrib['IsRewindable']|booleanTrue %} const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const -{% else %} +{% else %} const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const -{% endif %} +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -25,11 +25,11 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}AddEvent(AZ::Even {% endif %} {% elif Property.attrib['Container'] == 'Vector' %} -{% if Property.attrib['IsRewindable']|booleanTrue %} +{% if Property.attrib['IsRewindable']|booleanTrue %} const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const -{% else %} +{% else %} const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const -{% endif %} +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -68,7 +68,12 @@ const {{ Property.attrib['Type'] }}& {{ ClassName }}::Get{{ UpperFirst(Property. { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } - +{% if Property.attrib['IsRewindable']|booleanTrue %} +const {{ Property.attrib['Type'] }}& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Previous() const +{ + return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}.GetPrevious(); +} +{% endif %} {% if Property.attrib['GenerateEventBindings']|booleanTrue %} void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}AddEvent(AZ::Event<{{ Property.attrib['Type'] }}>::Handler& handler) { diff --git a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml index 203750d761..c553bc5351 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml @@ -32,11 +32,11 @@ - + - + diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml index cec005cc26..8ab2e61e5e 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkTransformComponent.AutoComponent.xml @@ -12,9 +12,9 @@ - + - + diff --git a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp index 418bea79dd..c5f9b265f9 100644 --- a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp @@ -185,12 +185,9 @@ namespace Multiplayer // Discard move input events, client may be speed hacking if (m_clientBankedTime < sv_MaxBankTimeWindowSec) { - // Client blends from previous frame to target so here we subtract blend factor to get to that state - const float blendFactor = AZStd::min(AZStd::max(0.f, input.GetHostBlendFactor()), 1.0f); - const AZ::TimeMs blendMs = AZ::TimeMs(static_cast(static_cast(cl_InputRateMs)) * (1.0f - blendFactor)); m_clientBankedTime = AZStd::min(m_clientBankedTime + clientInputRateSec, (double)sv_MaxBankTimeWindowSec); // clamp to boundary { - ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs() - blendMs, input.GetHostBlendFactor(), invokingConnection->GetConnectionId()); + ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), input.GetHostBlendFactor(), invokingConnection->GetConnectionId()); GetNetBindComponent()->ProcessInput(input, static_cast(clientInputRateSec)); } @@ -436,10 +433,13 @@ namespace Multiplayer NetworkInputArray inputArray(GetEntityHandle()); NetworkInput& input = inputArray[0]; + const float blendFactor = AZStd::min(AZStd::max(0.f, multiplayer->GetCurrentBlendFactor()), 1.0f); + const AZ::TimeMs blendMs = AZ::TimeMs(static_cast(static_cast(cl_InputRateMs)) * (1.0f - blendFactor)); input.SetClientInputId(m_clientInputId); input.SetHostFrameId(networkTime->GetHostFrameId()); - input.SetHostTimeMs(multiplayer->GetCurrentHostTimeMs()); + // Account for the client blending from previous frame to current + input.SetHostTimeMs(multiplayer->GetCurrentHostTimeMs() - blendMs); input.SetHostBlendFactor(multiplayer->GetCurrentBlendFactor()); // Allow components to form the input for this frame diff --git a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp index 43577525c0..634cac74b2 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp @@ -405,9 +405,9 @@ namespace Multiplayer m_entityServerMigrationEvent.Signal(m_netEntityHandle, hostId, connectionId); } - void NetBindComponent::NotifyPreRender(float deltaTime, float blendFactor) + void NetBindComponent::NotifyPreRender(float deltaTime) { - m_entityPreRenderEvent.Signal(deltaTime, blendFactor); + m_entityPreRenderEvent.Signal(deltaTime); } void NetBindComponent::NotifyCorrection() diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp index fa08794c4d..1d9fee16e0 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp @@ -26,11 +26,7 @@ namespace Multiplayer } NetworkTransformComponent::NetworkTransformComponent() - : m_rotationEventHandler([this](const AZ::Quaternion& rotation) { OnRotationChangedEvent(rotation); }) - , m_translationEventHandler([this](const AZ::Vector3& translation) { OnTranslationChangedEvent(translation); }) - , m_scaleEventHandler([this](float scale) { OnScaleChangedEvent(scale); }) - , m_resetCountEventHandler([this](const uint8_t&) { OnResetCountChangedEvent(); }) - , m_entityPreRenderEventHandler([this](float deltaTime, float blendFactor) { OnPreRender(deltaTime, blendFactor); }) + : m_entityPreRenderEventHandler([this](float deltaTime) { OnPreRender(deltaTime); }) , m_entityCorrectionEventHandler([this]() { OnCorrection(); }) { ; @@ -43,15 +39,8 @@ namespace Multiplayer void NetworkTransformComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) { - RotationAddEvent(m_rotationEventHandler); - TranslationAddEvent(m_translationEventHandler); - ScaleAddEvent(m_scaleEventHandler); - ResetCountAddEvent(m_resetCountEventHandler); GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler); GetNetBindComponent()->AddEntityCorrectionEventHandler(m_entityCorrectionEventHandler); - - // When coming into relevance, reset all blending factors so we don't interpolate to our start position - OnResetCountChangedEvent(); } void NetworkTransformComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) @@ -59,59 +48,31 @@ namespace Multiplayer ; } - void NetworkTransformComponent::OnRotationChangedEvent(const AZ::Quaternion& rotation) - { - m_previousTransform.SetRotation(m_targetTransform.GetRotation()); - m_targetTransform.SetRotation(rotation); - UpdateTargetHostFrameId(); - } - - void NetworkTransformComponent::OnTranslationChangedEvent(const AZ::Vector3& translation) - { - m_previousTransform.SetTranslation(m_targetTransform.GetTranslation()); - m_targetTransform.SetTranslation(translation); - UpdateTargetHostFrameId(); - } - - void NetworkTransformComponent::OnScaleChangedEvent(float scale) - { - m_previousTransform.SetUniformScale(m_targetTransform.GetUniformScale()); - m_targetTransform.SetUniformScale(scale); - UpdateTargetHostFrameId(); - } - - void NetworkTransformComponent::OnResetCountChangedEvent() - { - m_targetTransform.SetRotation(GetRotation()); - m_targetTransform.SetTranslation(GetTranslation()); - m_targetTransform.SetUniformScale(GetScale()); - m_previousTransform = m_targetTransform; - } - - void NetworkTransformComponent::UpdateTargetHostFrameId() - { - HostFrameId currentHostFrameId = Multiplayer::GetNetworkTime()->GetHostFrameId(); - if (currentHostFrameId > m_targetHostFrameId) - { - m_targetHostFrameId = currentHostFrameId; - } - } - - void NetworkTransformComponent::OnPreRender([[maybe_unused]] float deltaTime, float blendFactor) + void NetworkTransformComponent::OnPreRender([[maybe_unused]] float deltaTime) { if (!HasController()) { AZ::Transform blendTransform; - if (Multiplayer::GetNetworkTime() && Multiplayer::GetNetworkTime()->GetHostFrameId() > m_targetHostFrameId) + blendTransform.SetRotation(GetRotation()); + blendTransform.SetTranslation(GetTranslation()); + blendTransform.SetUniformScale(GetScale()); + + const float blendFactor = GetMultiplayer()->GetCurrentBlendFactor(); + if (!AZ::IsClose(blendFactor, 1.0f)) { - m_previousTransform = m_targetTransform; - blendTransform = m_targetTransform; - } - else - { - blendTransform.SetRotation(m_previousTransform.GetRotation().Slerp(m_targetTransform.GetRotation(), blendFactor)); - blendTransform.SetTranslation(m_previousTransform.GetTranslation().Lerp(m_targetTransform.GetTranslation(), blendFactor)); - blendTransform.SetUniformScale(AZ::Lerp(m_previousTransform.GetUniformScale(), m_targetTransform.GetUniformScale(), blendFactor)); + AZ::Transform blendTransformPrevious; + blendTransformPrevious.SetRotation(GetRotationPrevious()); + blendTransformPrevious.SetTranslation(GetTranslationPrevious()); + blendTransformPrevious.SetUniformScale(GetScalePrevious()); + + if (!blendTransform.IsClose(blendTransformPrevious)) + { + blendTransform.SetRotation(blendTransformPrevious.GetRotation().Slerp(blendTransform.GetRotation(), blendFactor)); + blendTransform.SetTranslation( + blendTransformPrevious.GetTranslation().Lerp(blendTransform.GetTranslation(), blendFactor)); + blendTransform.SetUniformScale( + AZ::Lerp(blendTransformPrevious.GetUniformScale(), blendTransform.GetUniformScale(), blendFactor)); + } } if (!GetTransformComponent()->GetWorldTM().IsClose(blendTransform)) @@ -124,12 +85,15 @@ namespace Multiplayer void NetworkTransformComponent::OnCorrection() { // Snap to latest - OnResetCountChangedEvent(); + AZ::Transform targetTransform; + targetTransform.SetRotation(GetRotation()); + targetTransform.SetTranslation(GetTranslation()); + targetTransform.SetUniformScale(GetScale()); // Hard set the entities transform - if (!GetTransformComponent()->GetWorldTM().IsClose(m_targetTransform)) + if (!GetTransformComponent()->GetWorldTM().IsClose(targetTransform)) { - GetTransformComponent()->SetWorldTM(m_targetTransform); + GetTransformComponent()->SetWorldTM(targetTransform); } } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 24f865194f..e66eb8d3a3 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -922,7 +922,7 @@ namespace Multiplayer for (NetBindComponent* netBindComponent : gatheredEntities) { - netBindComponent->NotifyPreRender(deltaTime, m_renderBlendFactor); + netBindComponent->NotifyPreRender(deltaTime); } } else @@ -934,7 +934,7 @@ namespace Multiplayer NetBindComponent* netBindComponent = entity->FindComponent(); if (netBindComponent != nullptr) { - netBindComponent->NotifyPreRender(deltaTime, m_renderBlendFactor); + netBindComponent->NotifyPreRender(deltaTime); } } } diff --git a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp index cd9c1737d6..b37f485ff4 100644 --- a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp @@ -65,11 +65,6 @@ namespace Multiplayer return m_rewindingConnectionId; } - HostFrameId NetworkTime::GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const - { - return (IsTimeRewound() && (rewindConnectionId == m_rewindingConnectionId)) ? m_unalteredFrameId : m_hostFrameId; - } - void NetworkTime::ForceSetTime(HostFrameId frameId, AZ::TimeMs timeMs) { AZ_Assert(!IsTimeRewound(), "Forcibly setting network time is unsupported under a rewound time scope"); @@ -79,16 +74,12 @@ namespace Multiplayer m_rewindingConnectionId = AzNetworking::InvalidConnectionId; } - void NetworkTime::AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) + void NetworkTime::AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, float blendFactor, AzNetworking::ConnectionId rewindConnectionId) { m_hostFrameId = frameId; m_hostTimeMs = timeMs; - m_rewindingConnectionId = rewindConnectionId; - } - - void NetworkTime::AlterBlendFactor(float blendFactor) - { m_hostBlendFactor = blendFactor; + m_rewindingConnectionId = rewindConnectionId; } void NetworkTime::SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) @@ -121,8 +112,15 @@ namespace Multiplayer if (networkTransform != nullptr) { - // We're not presently factoring in interpolated position here - const AZ::Vector3 rewindCenter = networkTransform->GetTranslation(); // Get the rewound position + // Get the rewound position for target host frame ID plus the one preceding it for potential lerp + AZ::Vector3 rewindCenter = networkTransform->GetTranslation(); + const AZ::Vector3 rewindCenterPrevious = networkTransform->GetTranslationPrevious(); + const float blendFactor = GetNetworkTime()->GetHostBlendFactor(); + if (!AZ::IsClose(blendFactor, 1.0f) && !rewindCenter.IsClose(rewindCenterPrevious)) + { + // If we have a blend factor, lerp the translation for accuracy + rewindCenter = rewindCenterPrevious.Lerp(rewindCenter, blendFactor); + } const AZ::Vector3 rewindOffset = rewindCenter - currentCenter; // Compute offset between rewound and current positions const AZ::Aabb rewoundAabb = currentBounds.GetTranslated(rewindOffset); // Apply offset to the entity aabb diff --git a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h index 0278ddd2b0..2bcf019623 100644 --- a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h +++ b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h @@ -32,10 +32,8 @@ namespace Multiplayer AZ::TimeMs GetHostTimeMs() const override; float GetHostBlendFactor() const override; AzNetworking::ConnectionId GetRewindingConnectionId() const override; - HostFrameId GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const override; void ForceSetTime(HostFrameId frameId, AZ::TimeMs timeMs) override; - void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) override; - void AlterBlendFactor(float blendFactor) override; + void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, float blendFactor, AzNetworking::ConnectionId rewindConnectionId) override; void SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) override; void ClearRewoundEntities() override; //! @} diff --git a/Gems/Multiplayer/Code/Tests/RewindableObjectTests.cpp b/Gems/Multiplayer/Code/Tests/RewindableObjectTests.cpp index b8d60a94e8..04de971f0d 100644 --- a/Gems/Multiplayer/Code/Tests/RewindableObjectTests.cpp +++ b/Gems/Multiplayer/Code/Tests/RewindableObjectTests.cpp @@ -57,6 +57,35 @@ namespace UnitTest } } + TEST_F(RewindableObjectTests, CurrentPreviousTests) + { + Multiplayer::RewindableObject test(0); + + for (uint32_t i = 0; i < RewindableBufferFrames; ++i) + { + test = i; + EXPECT_EQ(i, test); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + } + + { + // Test that Get/GetPrevious return different value when not on the owning connection + Multiplayer::ScopedAlterTime time(static_cast(RewindableBufferFrames - 1), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); + EXPECT_EQ(RewindableBufferFrames - 1, test.Get()); + EXPECT_EQ(RewindableBufferFrames - 2, test.GetPrevious()); + } + + // Test that Get/GetPrevious return the unaltered frame on the owning conection + Multiplayer::GetNetworkTime()->AlterTime(static_cast(RewindableBufferFrames - 1), AZ::TimeMs{ 0 }, 1.f, AzNetworking::ConnectionId(0)); + { + Multiplayer::ScopedAlterTime time(static_cast(RewindableBufferFrames - 1), AZ::TimeMs{ 0 }, 1.f, AzNetworking::ConnectionId(0)); + test.SetOwningConnectionId(AzNetworking::ConnectionId(0)); + EXPECT_EQ(RewindableBufferFrames - 1, test.Get()); + EXPECT_EQ(RewindableBufferFrames - 1, test.GetPrevious()); + } + Multiplayer::GetNetworkTime()->AlterTime(static_cast(RewindableBufferFrames), AZ::TimeMs(0), 1.f, AzNetworking::InvalidConnectionId); + } + TEST_F(RewindableObjectTests, OverflowTests) { Multiplayer::RewindableObject test(0); diff --git a/cmake/3rdParty.cmake b/cmake/3rdParty.cmake index f70ed1aa02..4b7f929279 100644 --- a/cmake/3rdParty.cmake +++ b/cmake/3rdParty.cmake @@ -6,6 +6,17 @@ # # +define_property(TARGET PROPERTY LY_SYSTEM_LIBRARY + BRIEF_DOCS "Defines a 3rdParty library as a system library" + FULL_DOCS [[ + Property which is set on third party targets that should be considered + as provided by the system. Such targets are excluded from the runtime + dependencies considerations, and are not distributed as part of the + O3DE SDK package. Instead, users of the SDK are expected to install + such a third party library themselves. + ]] +) + # Do not overcomplicate searching for the 3rdParty path, if it is not easy to find, # the user should define it. @@ -79,9 +90,10 @@ endfunction() # "fileA\nMy/Output/Subfolder/lib" # "fileB\nMy/Output/Subfolder/bin" # +# \arg:SYSTEM If specified, the library is considered a system library, and is not copied to the build output directory function(ly_add_external_target) - set(options) + set(options SYSTEM) set(oneValueArgs NAME VERSION 3RDPARTY_DIRECTORY PACKAGE 3RDPARTY_ROOT_DIRECTORY OUTPUT_SUBDIRECTORY) set(multiValueArgs HEADER_CHECK COMPILE_DEFINITIONS INCLUDE_DIRECTORIES BUILD_DEPENDENCIES RUNTIME_DEPENDENCIES) @@ -300,6 +312,10 @@ function(ly_add_external_target) ) endif() + if(ly_add_external_target_SYSTEM) + set_target_properties(3rdParty::${NAME_WITH_NAMESPACE} PROPERTIES LY_SYSTEM_LIBRARY TRUE) + endif() + endif() endfunction() @@ -327,4 +343,4 @@ if(NOT INSTALLED_ENGINE) ly_include_cmake_file_list(cmake/3rdParty/cmake_files.cmake) ly_get_absolute_pal_filename(pal_3rdparty_dir ${CMAKE_CURRENT_SOURCE_DIR}/cmake/3rdParty/Platform/${PAL_PLATFORM_NAME}) ly_include_cmake_file_list(${pal_3rdparty_dir}/cmake_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake) -endif() \ No newline at end of file +endif() diff --git a/cmake/Install.cmake b/cmake/Install.cmake index 6f50c78fd7..adcd28fa37 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -152,3 +152,20 @@ function(ly_install_run_code CODE) ) endfunction() + +#! ly_install_run_script: specifies path to script to be added to the install process (will run at install time) +# +# \notes: +# - refer to cmake's install(SCRIPT documentation for more information +# +function(ly_install_run_script SCRIPT) + + if(NOT LY_INSTALL_ENABLED) + return() + endif() + + install(SCRIPT ${SCRIPT} + COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} # use the default for the time being + ) + +endfunction() \ No newline at end of file diff --git a/cmake/LYPython.cmake b/cmake/LYPython.cmake index eeb9f97a55..a8095fbc95 100644 --- a/cmake/LYPython.cmake +++ b/cmake/LYPython.cmake @@ -21,14 +21,17 @@ include(cmake/LySet.cmake) # CMAKE_HOST_SYSTEM_NAME is "Windows", "Darwin", or "Linux" in our cases.. if (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux" ) ly_set(LY_PYTHON_VERSION 3.7.10) + ly_set(LY_PYTHON_VERSION_MAJOR_MINOR 3.7) ly_set(LY_PYTHON_PACKAGE_NAME python-3.7.10-rev2-linux) ly_set(LY_PYTHON_PACKAGE_HASH 6b9cf455e6190ec38836194f4454bb9db6bfc6890b4baff185cc5520aa822f05) elseif (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin" ) ly_set(LY_PYTHON_VERSION 3.7.10) + ly_set(LY_PYTHON_VERSION_MAJOR_MINOR 3.7) ly_set(LY_PYTHON_PACKAGE_NAME python-3.7.10-rev1-darwin) ly_set(LY_PYTHON_PACKAGE_HASH 3f65801894e4e44b5faa84dd85ef80ecd772dcf728cdd2d668a6e75978a32695) elseif (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows" ) ly_set(LY_PYTHON_VERSION 3.7.10) + ly_set(LY_PYTHON_VERSION_MAJOR_MINOR 3.7) ly_set(LY_PYTHON_PACKAGE_NAME python-3.7.10-rev2-windows) ly_set(LY_PYTHON_PACKAGE_HASH 06d97488a2dbabe832ecfa832a42d3e8a7163ba95e975f032727331b0f49d280) endif() diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index fdd3256d78..8fb2effe29 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -21,15 +21,21 @@ define_property(TARGET PROPERTY LY_INSTALL_GENERATE_RUN_TARGET ly_set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core) -cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE runtime_output_directory) -cmake_path(RELATIVE_PATH CMAKE_LIBRARY_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE library_output_directory) - if(LY_MONOLITHIC_GAME) set(LY_BUILD_PERMUTATION Monolithic) else() set(LY_BUILD_PERMUTATION Default) endif() +cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE runtime_output_directory) +cmake_path(RELATIVE_PATH CMAKE_LIBRARY_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE library_output_directory) +# Get the output folders, archive is always the same, but runtime/library can be in subfolders defined per target +cmake_path(RELATIVE_PATH CMAKE_ARCHIVE_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE archive_output_directory) + +cmake_path(APPEND archive_output_directory "${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") +cmake_path(APPEND library_output_directory "${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") +cmake_path(APPEND runtime_output_directory "${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") + #! ly_setup_target: Setup the data needed to re-create the cmake target commands for a single target function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_target_source_dir) # De-alias target name @@ -78,9 +84,6 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar endforeach() endif() - # Get the output folders, archive is always the same, but runtime/library can be in subfolders defined per target - cmake_path(RELATIVE_PATH CMAKE_ARCHIVE_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE archive_output_directory) - get_target_property(target_runtime_output_directory ${TARGET_NAME} RUNTIME_OUTPUT_DIRECTORY) if(target_runtime_output_directory) cmake_path(RELATIVE_PATH target_runtime_output_directory BASE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} OUTPUT_VARIABLE target_runtime_output_subdirectory) @@ -91,10 +94,6 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_tar cmake_path(RELATIVE_PATH target_library_output_directory BASE_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} OUTPUT_VARIABLE target_library_output_subdirectory) endif() - cmake_path(APPEND archive_output_directory "${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") - cmake_path(APPEND library_output_directory "${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") - cmake_path(APPEND runtime_output_directory "${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") - if(COMMAND ly_install_target_override) # Mac needs special handling because of a cmake issue ly_install_target_override(TARGET ${TARGET_NAME} @@ -372,6 +371,10 @@ function(ly_setup_o3de_install) COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + if(COMMAND ly_post_install_steps) + ly_post_install_steps() + endif() + endfunction() #! ly_setup_cmake_install: install the "cmake" folder @@ -528,7 +531,7 @@ endfunction()" # of baking the path. This is needed so `cmake --install --prefix ` works regardless of the CMAKE_INSTALL_PREFIX # used to generate the solution. # CMAKE_INSTALL_PREFIX is still used when building the INSTALL target - set(install_output_folder "\${CMAKE_INSTALL_PREFIX}/${runtime_output_directory}/${PAL_PLATFORM_NAME}/$/${LY_BUILD_PERMUTATION}") + set(install_output_folder "\${CMAKE_INSTALL_PREFIX}/${runtime_output_directory}") set(target_file_dir "${install_output_folder}/${target_runtime_output_subdirectory}") ly_get_runtime_dependencies(runtime_dependencies ${target}) foreach(runtime_dependency ${runtime_dependencies}) diff --git a/cmake/Platform/Common/RuntimeDependencies_common.cmake b/cmake/Platform/Common/RuntimeDependencies_common.cmake index 95d91da7f7..60e55453f8 100644 --- a/cmake/Platform/Common/RuntimeDependencies_common.cmake +++ b/cmake/Platform/Common/RuntimeDependencies_common.cmake @@ -70,12 +70,23 @@ function(ly_get_runtime_dependencies ly_RUNTIME_DEPENDENCIES ly_TARGET) # link dependencies are not runtime dependencies (we dont have anything to copy) however, we need to traverse # them since them or some dependency downstream could have something to copy over - foreach(link_dependency ${link_dependencies}) - if(NOT ${link_dependency} MATCHES "^::@") # Skip wraping produced when targets are not created in the same directory (https://cmake.org/cmake/help/latest/prop_tgt/LINK_LIBRARIES.html) - unset(dependencies) - ly_get_runtime_dependencies(dependencies ${link_dependency}) - list(APPEND all_runtime_dependencies ${dependencies}) + foreach(link_dependency IN LISTS link_dependencies) + if(${link_dependency} MATCHES "^::@") + # Skip wraping produced when targets are not created in the same directory + # (https://cmake.org/cmake/help/latest/prop_tgt/LINK_LIBRARIES.html) + continue() endif() + + if(TARGET ${link_dependency} AND link_dependency MATCHES "^3rdParty::") + get_target_property(is_system_library ${link_dependency} LY_SYSTEM_LIBRARY) + if(is_system_library) + continue() + endif() + endif() + + unset(dependencies) + ly_get_runtime_dependencies(dependencies ${link_dependency}) + list(APPEND all_runtime_dependencies ${dependencies}) endforeach() # For manual dependencies, we want to copy over the dependency and traverse them diff --git a/cmake/Platform/Mac/Configurations_mac.cmake b/cmake/Platform/Mac/Configurations_mac.cmake index 33a58dbbda..b1c50b751d 100644 --- a/cmake/Platform/Mac/Configurations_mac.cmake +++ b/cmake/Platform/Mac/Configurations_mac.cmake @@ -29,9 +29,7 @@ else() endif() # Signing -# The "-o linker-signed" flag is required as a work-around for the following CMake issue: -# https://gitlab.kitware.com/cmake/cmake/-/issues/21854 -ly_set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--deep -o linker-signed") +ly_set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--deep") # Generate scheme files for Xcode ly_set(CMAKE_XCODE_GENERATE_SCHEME TRUE) diff --git a/cmake/Platform/Mac/InstallUtils_mac.cmake.in b/cmake/Platform/Mac/InstallUtils_mac.cmake.in new file mode 100644 index 0000000000..de6d9ddf65 --- /dev/null +++ b/cmake/Platform/Mac/InstallUtils_mac.cmake.in @@ -0,0 +1,168 @@ +# +# 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 +# +# + +function(fixup_qt_framework lib_name framework_path) + + file(REMOVE_RECURSE + ${framework_path}/Headers + ${framework_path}/Resources + ${framework_path}/${lib_name} + ${framework_path}/Versions/Current + ${framework_path}/Versions/5/Headers + ) + + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink 5 Current + WORKING_DIRECTORY ${framework_path}/Versions + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/${lib_name} ${lib_name} + WORKING_DIRECTORY ${framework_path} + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Resources Resources + WORKING_DIRECTORY ${framework_path} + ) + +endfunction() + +function(fixup_python_framework framework_path) + + file(REMOVE_RECURSE + ${framework_path}/Versions/Current + ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/Headers + ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/Python + ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/python@LY_PYTHON_VERSION_MAJOR_MINOR@/test + ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/python@LY_PYTHON_VERSION_MAJOR_MINOR@/site-packages/scipy/io/tests + ${framework_path}/Python + ${framework_path}/Resources + ${framework_path}/Headers + ) + + file(GLOB_RECURSE exe_file_list "${framework_path}/**/*.exe") + if(exe_file_list) + file(REMOVE_RECURSE ${exe_file_list}) + endif() + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink include/python@LY_PYTHON_VERSION_MAJOR_MINOR@m Headers + WORKING_DIRECTORY ${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@ + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink @LY_PYTHON_VERSION_MAJOR_MINOR@ Current + WORKING_DIRECTORY ${framework_path}/Versions/ + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Python Python + WORKING_DIRECTORY ${framework_path} + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Headers Headers + WORKING_DIRECTORY ${framework_path} + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink Versions/Current/Resources Resources + WORKING_DIRECTORY ${framework_path} + ) + file(CHMOD ${framework_path}/Versions/Current/Python + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + ) + +endfunction() + +function(codesign_file file entitlement_file) + + if (NOT @LY_ENABLE_HARDENED_RUNTIME@) + return() + endif() + + if(EXISTS ${entitlement_file}) + + execute_process(COMMAND "/usr/bin/codesign" "--force" "--sign" "@LY_CODE_SIGN_IDENTITY@" "--deep" "-o" "runtime" "--timestamp" "--entitlements" "${entitlement_file}" "${file}" + TIMEOUT 300 + OUTPUT_VARIABLE codesign_out + RESULT_VARIABLE codesign_ret + ) + else() + execute_process(COMMAND "/usr/bin/codesign" "--force" "--sign" "@LY_CODE_SIGN_IDENTITY@" "--deep" "-o" "runtime" "--timestamp" "${file}" + TIMEOUT 300 + OUTPUT_VARIABLE codesign_out + RESULT_VARIABLE codesign_ret + ) + endif() + + if(NOT ${codesign_ret} EQUAL "0") + message(FATAL_ERROR "Codesign operation for ${file_path} returned ${codesign_ret} with message ${codesign_out}") + endif() + +endfunction() + +function(codesign_python_framework_binaries framework_path) + + if (NOT @LY_ENABLE_HARDENED_RUNTIME@) + return() + endif() + + # The codesign "--deep" flag will only codesign binaries in folders with specific names. + # We need to codesign all the binaries that the "--deep" flag will miss. + file(GLOB_RECURSE files + LIST_DIRECTORIES false + "${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/bin/**" + "${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/lib/**" + "${framework_path}/Versions/@LY_PYTHON_VERSION_MAJOR_MINOR@/Resources/**") + + foreach(file ${files}) + if(NOT EXISTS ${file}) + file(REMOVE ${file}) + continue() + endif() + cmake_path(SET path_var "${file}") + cmake_path(GET path_var EXTENSION LAST_ONLY extension) + set(should_codesign FALSE) + set(extension_skip_list ".dylib" ".so" ".7m") + if (NOT extension) + set(should_codesign TRUE) + elseif(extension IN_LIST extension_skip_list) + set(should_codesign TRUE) + endif() + if(${should_codesign}) + codesign_file("${file}" "@LY_ROOT_FOLDER@/python/Platform/Mac/PythonEntitlements.plist") + endif() + endforeach() + +endfunction() + +function(ly_copy source_file target_directory) + + if("${source_file}" MATCHES "\\.[Ff]ramework[^\\.]") + + # fixup origin to copy the whole Framework folder + string(REGEX REPLACE "(.*\\.[Ff]ramework).*" "\\1" source_file "${source_file}") + + endif() + get_filename_component(target_filename "${source_file}" NAME) + file(COPY "${source_file}" DESTINATION "${target_directory}" FILE_PERMISSIONS @LY_COPY_PERMISSIONS@ FOLLOW_SYMLINK_CHAIN) + + # Our Qt and Python frameworks aren't in the correct bundle format to be codesigned. + if("${target_filename}" MATCHES "(Qt[^.]+)\\.[Ff]ramework") + fixup_qt_framework(${CMAKE_MATCH_1} "${target_directory}/${target_filename}") + # For some Qt frameworks(QtCore), signing the bundle doesn't work because of bundle + # format issues(despite the fixes above). But once we've patched the framework above, there's + # only one executable that we need to sign so we can do it directly. + set(target_filename "${target_filename}/Versions/5/${CMAKE_MATCH_1}") + elseif("${target_filename}" MATCHES "Python.framework") + fixup_python_framework("${target_directory}/${target_filename}") + codesign_python_framework_binaries("${target_directory}/${target_filename}") + endif() + codesign_file("${target_directory}/${target_filename}" "none") + +endfunction() + +function(ly_download_and_codesign_sdk_python) + execute_process(COMMAND ${CMAKE_COMMAND} -DPAL_PLATFORM_NAME=Mac -DLY_3RDPARTY_PATH=${CMAKE_INSTALL_PREFIX}/python -P ${CMAKE_INSTALL_PREFIX}/python/get_python.cmake) + fixup_python_framework(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework) + codesign_python_framework_binaries(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework) + codesign_file(${CMAKE_INSTALL_PREFIX}/python/runtime/@LY_PYTHON_PACKAGE_NAME@/Python.framework @LY_ROOT_FOLDER@/python/Platform/Mac/PythonEntitlements.plist) +endfunction() + +function(ly_codesign_sdk) + codesign_file(${LY_INSTALL_PATH_ORIGINAL}/O3DE_SDK.app "none") +endfunction() + + diff --git a/cmake/Platform/Mac/Install_mac.cmake b/cmake/Platform/Mac/Install_mac.cmake index 8f07b1bfde..bdc2300131 100644 --- a/cmake/Platform/Mac/Install_mac.cmake +++ b/cmake/Platform/Mac/Install_mac.cmake @@ -6,6 +6,8 @@ # # +include(cmake/Platform/Common/Install_common.cmake) + # This is used to generate a setreg file which will be placed inside the bundle # for targets that request it(eg. AssetProcessor/Editor). This is the relative path # to the bundle from the installed engine's root. This will be used to compute the @@ -16,7 +18,7 @@ set(installed_binaries_path_template [[ "AzCore": { "Runtime": { "FilePaths": { - "InstalledBinariesFolder": "bin/Mac/$" + "InstalledBinariesFolder": "@runtime_output_directory@" } } } @@ -24,15 +26,20 @@ set(installed_binaries_path_template [[ }]] ) -unset(target_conf_dir) -foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES) - string(TOUPPER ${conf} UCONF) - string(APPEND target_conf_dir $<$:${CMAKE_RUNTIME_OUTPUT_DIRECTORY_${UCONF}}>) -endforeach() +# This setreg file will be used by all of our installed app bundles to locate installed +# runtime dependencies. It contains the path to binary install directory relative to +# the installed engine root. +string(CONFIGURE "${installed_binaries_path_template}" configured_setreg_file) +file(GENERATE + OUTPUT ${CMAKE_BINARY_DIR}/runtime_install/$/BinariesInstallPath.setreg + CONTENT "${configured_setreg_file}" +) -set(installed_binaries_setreg_path ${target_conf_dir}/Registry/installed_binaries_path.setreg) - -file(GENERATE OUTPUT ${installed_binaries_setreg_path} CONTENT ${installed_binaries_path_template}) +# ly_install_run_script isn't defined yet so we use install(SCRIPT) directly. +# This needs to be done here because it needs to update the install prefix +# before cmake does anything else in the install process. +configure_file(${LY_ROOT_FOLDER}/cmake/Platform/Mac/PreInstallSteps_mac.cmake.in ${CMAKE_BINARY_DIR}/runtime_install/PreInstallSteps_mac.cmake @ONLY) +install(SCRIPT ${CMAKE_BINARY_DIR}/runtime_install/PreInstallSteps_mac.cmake COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) #! ly_install_target_override: Mac specific target installation function(ly_install_target_override) @@ -70,33 +77,62 @@ function(ly_install_target_override) COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} ) + set(install_relative_binaries_path "${ly_platform_install_target_RUNTIME_DIR}/${ly_platform_install_target_RUNTIME_SUBDIR}") + if (${is_bundle}) set_property(TARGET ${ly_platform_install_target_TARGET} PROPERTY RESOURCE ${cached_resources_dir}) + set(runtime_output_filename "$.app") + else() + set(runtime_output_filename "$") + endif() + + get_target_property(target_type ${ly_platform_install_target_TARGET} TYPE) + if(target_type IN_LIST LY_TARGET_TYPES_WITH_RUNTIME_OUTPUTS) + get_target_property(entitlement_file ${ly_platform_install_target_TARGET} ENTITLEMENT_FILE_PATH) + if (NOT entitlement_file) + set(entitlement_file "none") + endif() + + ly_file_read(${LY_ROOT_FOLDER}/cmake/Platform/Mac/runtime_install_mac.cmake.in template_file) + string(CONFIGURE "${template_file}" configured_template_file @ONLY) + file(GENERATE + OUTPUT ${CMAKE_BINARY_DIR}/runtime_install/$/${ly_platform_install_target_TARGET}.cmake + CONTENT "${configured_template_file}" + ) endif() -endfunction() - -#! ly_install_add_install_path_setreg: Adds the install path setreg file as a dependency -function(ly_install_add_install_path_setreg NAME) - set_property(TARGET ${NAME} APPEND PROPERTY INTERFACE_LY_TARGET_FILES "${installed_binaries_setreg_path}\nRegistry") endfunction() #! ly_install_code_function_override: Mac specific copy function to handle frameworks function(ly_install_code_function_override) - install(CODE -"function(ly_copy source_file target_directory) - if(\"\${source_file}\" MATCHES \"\\\\.[Ff]ramework[^\\\\.]\") - - # fixup origin to copy the whole Framework folder - string(REGEX REPLACE \"(.*\\\\.[Ff]ramework).*\" \"\\\\1\" source_file \"\${source_file}\") - get_filename_component(target_filename \"\${source_file}\" NAME) - - endif() - file(COPY \"\${source_file}\" DESTINATION \"\${target_directory}\" FILE_PERMISSIONS ${LY_COPY_PERMISSIONS}) -endfunction()" - COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME} - ) + configure_file(${LY_ROOT_FOLDER}/cmake/Platform/Mac/InstallUtils_mac.cmake.in ${CMAKE_BINARY_DIR}/runtime_install/InstallUtils_mac.cmake @ONLY) + ly_install_run_script(${CMAKE_BINARY_DIR}/runtime_install/InstallUtils_mac.cmake) + +endfunction() + +#! ly_post_install_steps: Any additional platform specific post install steps +function(ly_post_install_steps) + + # On Mac, after CMake is done installing, the code signatures on all our built binaries will be invalid. + # We need to now codesign each dynamic library, executable, and app bundle. It's specific to each target + # because there could potentially be different entitlements for different targets. + get_property(all_targets GLOBAL PROPERTY LY_ALL_TARGETS) + foreach(alias_target IN LISTS all_targets) + ly_de_alias_target(${alias_target} target) + # Exclude targets that dont produce runtime outputs + get_target_property(target_type ${target} TYPE) + if(NOT target_type IN_LIST LY_TARGET_TYPES_WITH_RUNTIME_OUTPUTS) + continue() + endif() + + ly_install_run_script(${CMAKE_BINARY_DIR}/runtime_install/$/${target}.cmake) + endforeach() + + ly_install_run_code(" + ly_download_and_codesign_sdk_python() + ly_codesign_sdk() + set(CMAKE_INSTALL_PREFIX ${LY_INSTALL_PATH_ORIGINAL}) + ") endfunction() -include(cmake/Platform/Common/Install_common.cmake) diff --git a/cmake/Platform/Mac/LYWrappers_mac.cmake b/cmake/Platform/Mac/LYWrappers_mac.cmake index 578f6fe041..245d3bb7c1 100644 --- a/cmake/Platform/Mac/LYWrappers_mac.cmake +++ b/cmake/Platform/Mac/LYWrappers_mac.cmake @@ -6,6 +6,16 @@ # # +set(LY_ENABLE_HARDENED_RUNTIME OFF CACHE BOOL "Enable hardened runtime capability for Mac builds. This should be ON when building the engine for notarization/distribution.") + +define_property(TARGET PROPERTY ENTITLEMENT_FILE_PATH + BRIEF_DOCS "Path to the entitlement file" + FULL_DOCS [[ + On MacOS, entitlements are used to grant certain privileges + to applications at runtime. Use this propery to specify the + path to a .plist file containing entitlements. + ]] +) function(ly_apply_platform_properties target) @@ -14,6 +24,18 @@ function(ly_apply_platform_properties target) INSTALL_RPATH "@executable_path/;@executable_path/../Frameworks" ) + get_property(is_imported TARGET ${target} PROPERTY IMPORTED) + if((NOT is_imported) AND (LY_ENABLE_HARDENED_RUNTIME)) + get_property(target_type TARGET ${target} PROPERTY TYPE) + set(runtime_types_list "MODULE_LIBRARY" "SHARED_LIBRARY" "EXECUTABLE") + if (target_type IN_LIST runtime_types_list) + set_target_properties(${target} PROPERTIES + XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES + XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS NO + ) + endif() + endif() + endfunction() diff --git a/cmake/Platform/Mac/PAL_mac.cmake b/cmake/Platform/Mac/PAL_mac.cmake index b415daf44a..065b35d69c 100644 --- a/cmake/Platform/Mac/PAL_mac.cmake +++ b/cmake/Platform/Mac/PAL_mac.cmake @@ -39,3 +39,6 @@ set(LY_ASSET_DEPLOY_ASSET_TYPE "mac" CACHE STRING "Set the asset type for deploy # Set the python cmd tool ly_set(LY_PYTHON_CMD ${CMAKE_CURRENT_SOURCE_DIR}/python/python.sh) + +# Only x86_64 is currently supported on Mac +ly_set(CMAKE_OSX_ARCHITECTURES "x86_64") diff --git a/cmake/Platform/Mac/PreInstallSteps_mac.cmake.in b/cmake/Platform/Mac/PreInstallSteps_mac.cmake.in new file mode 100644 index 0000000000..5033aadd6e --- /dev/null +++ b/cmake/Platform/Mac/PreInstallSteps_mac.cmake.in @@ -0,0 +1,39 @@ +# +# 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 +# +# + +cmake_minimum_required(VERSION 3.20) + +# The O3DE SDK will be shipped as an app bundle. So we create an O3DE_SDK.app directory +# and install SDK into the app's Contents/Engine directory. +set(LY_INSTALL_PATH_ORIGINAL ${CMAKE_INSTALL_PREFIX}) + +file(INSTALL @LY_ROOT_FOLDER@/Code/Tools/BundleLauncher/info.plist + DESTINATION ${CMAKE_INSTALL_PREFIX}/O3DE_SDK.app/Contents +) + +# This SDK launcher will install python site-packages and then launch the ProjectManager +# when a user double clicks on the SDK from Finder. We're only going to need one version +# of the SDK launcher regardless of what configs of the engine are installed. +if (EXISTS @CMAKE_BINARY_DIR@/bin/profile/O3DE_SDK) + set(sdk_launcher_config profile) +elseif (EXISTS @CMAKE_BINARY_DIR@/bin/debug/O3DE_SDK) + set(sdk_launcher_config debug) +elseif (EXISTS @CMAKE_BINARY_DIR@/bin/release/O3DE_SDK) + set(sdk_launcher_config release) +endif() +file(INSTALL @CMAKE_BINARY_DIR@/bin/${sdk_launcher_config}/O3DE_SDK + DESTINATION ${CMAKE_INSTALL_PREFIX}/O3DE_SDK.app/Contents/MacOS + USE_SOURCE_PERMISSIONS +) +file(INSTALL @CMAKE_BINARY_DIR@/runtime_install/${sdk_launcher_config}/BinariesInstallPath.setreg + DESTINATION ${CMAKE_INSTALL_PREFIX}/O3DE_SDK.app/Contents/MacOS/Registry +) + +# We need to update the CMAKE_INSTALL_PREFIX so that the engine is installed inside the app bundle. +file(MAKE_DIRECTORY ${CMAKE_INSTALL_PREFIX}/O3DE_SDK.app/Contents/Engine) +set(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/O3DE_SDK.app/Contents/Engine) \ No newline at end of file diff --git a/cmake/Platform/Mac/runtime_dependencies_mac.cmake.in b/cmake/Platform/Mac/runtime_dependencies_mac.cmake.in index d65578f82a..11551f608f 100644 --- a/cmake/Platform/Mac/runtime_dependencies_mac.cmake.in +++ b/cmake/Platform/Mac/runtime_dependencies_mac.cmake.in @@ -73,8 +73,8 @@ function(ly_copy source_file target_directory) return() endif() - # fixup the destination so it ends up in Contents/Plugins - string(REGEX REPLACE "(.*\\.app/Contents)/MacOS" "\\1/plugins" target_directory "${target_directory}") + # fixup the destination so it ends up in Contents/PlugIns + string(REGEX REPLACE "(.*\\.app/Contents)/MacOS" "\\1/PlugIns" target_directory "${target_directory}") set(local_plugin_dirs ${plugin_dirs}) list(APPEND local_plugin_dirs "${target_directory}") @@ -212,7 +212,6 @@ if(@target_file_dir@ MATCHES ".app/Contents/MacOS") file(REMOVE_RECURSE ${remove_file_list}) endif() - endif() else() # Non-bundle case diff --git a/cmake/Platform/Mac/runtime_install_mac.cmake.in b/cmake/Platform/Mac/runtime_install_mac.cmake.in new file mode 100644 index 0000000000..65b1ede77b --- /dev/null +++ b/cmake/Platform/Mac/runtime_install_mac.cmake.in @@ -0,0 +1,34 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +cmake_path(SET file_path "${CMAKE_INSTALL_PREFIX}/@install_relative_binaries_path@/@runtime_output_filename@") +cmake_path(GET file_path EXTENSION LAST_ONLY file_ext) + +if(file_ext STREQUAL .app) + + file(INSTALL @CMAKE_BINARY_DIR@/runtime_install/$/BinariesInstallPath.setreg + DESTINATION ${file_path}/Contents/MacOS/Registry + ) + + if(EXISTS "${file_path}/Contents/Frameworks/Python.framework") + codesign_python_framework_binaries("${file_path}/Contents/Frameworks/Python.framework") + endif() + +else() + + find_program(LY_INSTALL_NAME_TOOL install_name_tool) + if (NOT LY_INSTALL_NAME_TOOL) + message(FATAL_ERROR "Unable to locate 'install_name_tool'") + endif() + + execute_process(COMMAND + ${LY_INSTALL_NAME_TOOL} -add_rpath @loader_path ${file_path}) + +endif() + +codesign_file("${file_path}" "@entitlement_file@") diff --git a/python/Platform/Mac/PythonEntitlements.plist b/python/Platform/Mac/PythonEntitlements.plist new file mode 100644 index 0000000000..ed4892befa --- /dev/null +++ b/python/Platform/Mac/PythonEntitlements.plist @@ -0,0 +1,10 @@ + + + + + com.apple.security.cs.disable-library-validation + + com.apple.security.cs.allow-unsigned-executable-memory + + + diff --git a/python/get_python.sh b/python/get_python.sh index 248a0790fc..e8d35a311e 100755 --- a/python/get_python.sh +++ b/python/get_python.sh @@ -30,7 +30,8 @@ cd $DIR python_exitcode=$? if [ $python_exitcode == 0 ]; then echo get_python.sh: Python is already downloaded: $(./python.sh --version) - $DIR/pip.sh install -r $DIR/requirements.txt --quiet --disable-pip-version-check + $DIR/pip.sh install -r $DIR/requirements.txt --disable-pip-version-check --no-warn-script-location + $DIR/pip.sh install -e $DIR/../scripts/o3de --no-deps --disable-pip-version-check --no-warn-script-location exit 0 fi if [[ "$OSTYPE" = *"darwin"* ]]; @@ -73,5 +74,6 @@ if [ $retVal -ne 0 ]; then fi echo installing via pip... -$DIR/pip.sh install -r $DIR/requirements.txt --disable-pip-version-check +$DIR/pip.sh install -r $DIR/requirements.txt --disable-pip-version-check --no-warn-script-location +$DIR/pip.sh install -e $DIR/../scripts/o3de --no-deps --disable-pip-version-check --no-warn-script-location exit $? diff --git a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt index 21c4755a2e..2cbbe58b38 100644 --- a/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt +++ b/scripts/build/build_node/Platform/Linux/package-list.ubuntu-focal.txt @@ -13,6 +13,8 @@ libxcb-xinput0 # For Qt plugins at runtime libfontconfig1-dev # For Qt plugins at runtime libcurl4-openssl-dev # For HttpRequestor libsdl2-dev # for WWise/Audio -libxkbcommon-dev +libxcb-xkb-dev # For xcb keyboard input +libxkbcommon-x11-dev # For xcb keyboard input +libxkbcommon-dev # For xcb keyboard input zlib1g-dev mesa-common-dev