From 712f67166c4e7444c89e6f6d4fff1c14ab7a97cc Mon Sep 17 00:00:00 2001 From: guillaume-haerinck Date: Sun, 23 Feb 2025 13:41:11 +0100 Subject: [PATCH 1/5] Add superluminal profiler integration Signed-off-by: guillaume-haerinck --- .../SuperluminalProfiler/CMakeLists.txt | 11 ++ .../SuperluminalProfiler/Code/CMakeLists.txt | 60 ++++++++++ .../Code/Source/CpuProfiler.cpp | 64 +++++++++++ .../Code/Source/CpuProfiler.h | 55 ++++++++++ .../Code/Source/ProfilerModule.cpp | 50 +++++++++ .../Code/Source/ProfilerSystemComponent.cpp | 103 ++++++++++++++++++ .../Code/Source/ProfilerSystemComponent.h | 53 +++++++++ .../Code/superluminalprofiler_files.cmake | 14 +++ .../superluminalprofiler_shared_files.cmake | 11 ++ .../SuperluminalProfiler/gem.json | 23 ++++ .../SuperluminalProfiler/preview.png | 3 + 11 files changed, 447 insertions(+) create mode 100644 Gems/ExternalProfilers/SuperluminalProfiler/CMakeLists.txt create mode 100644 Gems/ExternalProfilers/SuperluminalProfiler/Code/CMakeLists.txt create mode 100644 Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.cpp create mode 100644 Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.h create mode 100644 Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerModule.cpp create mode 100644 Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.cpp create mode 100644 Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.h create mode 100644 Gems/ExternalProfilers/SuperluminalProfiler/Code/superluminalprofiler_files.cmake create mode 100644 Gems/ExternalProfilers/SuperluminalProfiler/Code/superluminalprofiler_shared_files.cmake create mode 100644 Gems/ExternalProfilers/SuperluminalProfiler/gem.json create mode 100644 Gems/ExternalProfilers/SuperluminalProfiler/preview.png diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/CMakeLists.txt b/Gems/ExternalProfilers/SuperluminalProfiler/CMakeLists.txt new file mode 100644 index 000000000..708b180e4 --- /dev/null +++ b/Gems/ExternalProfilers/SuperluminalProfiler/CMakeLists.txt @@ -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 +# +# + +o3de_gem_setup("SuperluminalProfiler") + +add_subdirectory(Code) diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/CMakeLists.txt b/Gems/ExternalProfilers/SuperluminalProfiler/Code/CMakeLists.txt new file mode 100644 index 000000000..25457d6df --- /dev/null +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/CMakeLists.txt @@ -0,0 +1,60 @@ +# +# 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(SUPERLUMINAL_API_PATH "C:/Program Files/Superluminal/Performance/API" CACHE PATH "Path to the Superluminal 3rd party profiler API folder.") +list(APPEND CMAKE_MODULE_PATH ${SUPERLUMINAL_API_PATH}) + +find_package(SuperluminalAPI REQUIRED) + +ly_add_target( + NAME ${gem_name}.Static STATIC + NAMESPACE Gem + FILES_CMAKE + superluminalprofiler_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + ${SuperluminalAPI_INCLUDE_DIRS} + BUILD_DEPENDENCIES + PUBLIC + AZ::AzCore + AZ::AzFramework + PRIVATE + ${SuperluminalAPI_LIBS_RELEASE} +) + +ly_add_target( + NAME ${gem_name} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAMESPACE Gem + FILES_CMAKE + superluminalprofiler_shared_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + Gem::${gem_name}.Static +) + +ly_add_source_properties( + SOURCES + Source/ProfilerModule.cpp + PROPERTY COMPILE_DEFINITIONS + VALUES + O3DE_GEM_NAME=${gem_name} + O3DE_GEM_VERSION=${gem_version}) + +ly_create_alias(NAME ${gem_name}.Servers NAMESPACE Gem TARGETS Gem::${gem_name}) +ly_create_alias(NAME ${gem_name}.Builders NAMESPACE Gem TARGETS Gem::${gem_name}) +ly_create_alias(NAME ${gem_name}.Clients NAMESPACE Gem TARGETS Gem::${gem_name}) +ly_create_alias(NAME ${gem_name}.Unified NAMESPACE Gem TARGETS Gem::${gem_name}) +ly_create_alias(NAME ${gem_name}.Tools NAMESPACE Gem TARGETS Gem::${gem_name}) diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.cpp b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.cpp new file mode 100644 index 000000000..3aa43811c --- /dev/null +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.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 + +namespace SuperluminalProfiler +{ + void CpuProfiler::Init() + { + AZ::Interface::Register(this); + m_initialized = true; + AZ::SystemTickBus::Handler::BusConnect(); + } + + void CpuProfiler::Shutdown() + { + if (!m_initialized) + { + return; + } + + // When this call is made, no more thread profiling calls can be performed anymore + AZ::Interface::Unregister(this); + + // Wait for the remaining threads that might still be processing its profiling calls + AZStd::unique_lock shutdownLock(m_shutdownMutex); + AZ::SystemTickBus::Handler::BusDisconnect(); + } + + void CpuProfiler::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()) + { + PerformanceAPI_BeginEvent(eventName, budget->Name(), budget->Crc()); + m_shutdownMutex.unlock_shared(); + } + } + + void CpuProfiler::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()) + { + PerformanceAPI_EndEvent(); + m_shutdownMutex.unlock_shared(); + } + } + + void CpuProfiler::OnSystemTick() + { + PerformanceAPI::InstrumentationScope("SystemTick", nullptr, PERFORMANCEAPI_MAKE_COLOR(0, 255, 0)); + } + +} diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.h b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.h new file mode 100644 index 000000000..19e83e740 --- /dev/null +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace SuperluminalProfiler +{ + class CpuProfiler final + : public AZ::Debug::Profiler + , public AZ::SystemTickBus::Handler + { + public: + AZ_RTTI(CpuProfiler, "{A05E7DC4-AB00-41BC-A739-8E58908CB84F}", AZ::Debug::Profiler); + AZ_CLASS_ALLOCATOR(CpuProfiler, AZ::SystemAllocator); + + CpuProfiler() = default; + ~CpuProfiler() = default; + + //! Registers/un-registers the AZ::Debug::Profiler instance to the interface + void Init(); + void Shutdown(); + + //! 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; + + //! Check to see if a programmatic capture is currently in progress, implies + //! that the profiler is active if returns True. + bool IsContinuousCaptureInProgress() const; + + //! AZ::SystemTickBus::Handler overrides + //! When fired, the profiler collects all profiling data from registered threads and updates + //! m_timeRegionMap so that the next frame has up-to-date profiling data. + void OnSystemTick() final override; + + private: + // This lock will only be contested when the CpuProfiler's Shutdown() method has been called + AZStd::shared_mutex m_shutdownMutex; + + bool m_initialized = false; + }; +} diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerModule.cpp b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerModule.cpp new file mode 100644 index 000000000..decde724a --- /dev/null +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerModule.cpp @@ -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 + * + */ + +#include + +#include +#include + +namespace SuperluminalProfiler +{ + class ProfilerModule + : public AZ::Module + { + public: + AZ_RTTI(ProfilerModule, "{7C666AFB-E699-42FB-8F32-DAEE5A62CD01}", AZ::Module); + AZ_CLASS_ALLOCATOR(ProfilerModule, AZ::SystemAllocator); + + ProfilerModule() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. + // This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert(m_descriptors.end(), { + ProfilerSystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + azrtti_typeid(), + }; + } + }; +} + +#if defined(O3DE_GEM_NAME) +AZ_DECLARE_MODULE_CLASS(AZ_JOIN(Gem_, O3DE_GEM_NAME), SuperluminalProfiler::ProfilerModule) +#else +AZ_DECLARE_MODULE_CLASS(Gem_Profiler, SuperluminalProfiler::ProfilerModule) +#endif diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.cpp b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.cpp new file mode 100644 index 000000000..1d885b810 --- /dev/null +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.cpp @@ -0,0 +1,103 @@ +/* + * 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 "ProfilerSystemComponent.h" + +#include +#include +#include +#include + +namespace SuperluminalProfiler +{ + static constexpr AZ::Crc32 profilerServiceCrc = AZ_CRC_CE("ProfilerService"); + + void ProfilerSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(0); + } + } + + void ProfilerSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(profilerServiceCrc); + } + + void ProfilerSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(profilerServiceCrc); + } + + void ProfilerSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + } + + void ProfilerSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + } + + ProfilerSystemComponent::ProfilerSystemComponent() + { + if (AZ::Debug::ProfilerSystemInterface::Get() == nullptr) + { + AZ::Debug::ProfilerSystemInterface::Register(this); + } + } + + ProfilerSystemComponent::~ProfilerSystemComponent() + { + if (AZ::Debug::ProfilerSystemInterface::Get() == this) + { + AZ::Debug::ProfilerSystemInterface::Unregister(this); + } + } + + void ProfilerSystemComponent::Activate() + { + m_cpuProfiler.Init(); + } + + void ProfilerSystemComponent::Deactivate() + { + m_cpuProfiler.Shutdown(); + } + + bool ProfilerSystemComponent::IsActive() const + { + return false; + } + + void ProfilerSystemComponent::SetActive([[maybe_unused]] bool enabled) + { + + } + + bool ProfilerSystemComponent::CaptureFrame([[maybe_unused]] const AZStd::string& outputFilePath) + { + return true; + } + + bool ProfilerSystemComponent::StartCapture([[maybe_unused]] AZStd::string outputFilePath) + { + return true; + } + + bool ProfilerSystemComponent::EndCapture() + { + return true; + } + + bool ProfilerSystemComponent::IsCaptureInProgress() const + { + return false; + } + +} diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.h b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.h new file mode 100644 index 000000000..068840eac --- /dev/null +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include "CpuProfiler.h" + +#include +#include + +namespace SuperluminalProfiler +{ + class ProfilerSystemComponent + : public AZ::Component + , protected AZ::Debug::ProfilerRequests + { + public: + AZ_COMPONENT(ProfilerSystemComponent, "{C920A0CC-A053-4A0E-8550-DC44FF03A2D1}"); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + ProfilerSystemComponent(); + ~ProfilerSystemComponent(); + + protected: + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + + // ProfilerRequests interface implementation + bool IsActive() const override; + void SetActive(bool active) override; + bool CaptureFrame(const AZStd::string& outputFilePath) override; + bool StartCapture(AZStd::string outputFilePath) override; + bool EndCapture() override; + bool IsCaptureInProgress() const override; + + private: + AZStd::atomic_bool m_cpuCaptureInProgress{ false }; + CpuProfiler m_cpuProfiler; + }; + +} diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/superluminalprofiler_files.cmake b/Gems/ExternalProfilers/SuperluminalProfiler/Code/superluminalprofiler_files.cmake new file mode 100644 index 000000000..0088f1baa --- /dev/null +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/superluminalprofiler_files.cmake @@ -0,0 +1,14 @@ +# +# 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 + Source/CpuProfiler.h + Source/CpuProfiler.cpp + Source/ProfilerSystemComponent.cpp + Source/ProfilerSystemComponent.h +) diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/superluminalprofiler_shared_files.cmake b/Gems/ExternalProfilers/SuperluminalProfiler/Code/superluminalprofiler_shared_files.cmake new file mode 100644 index 000000000..b8ee476e2 --- /dev/null +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/superluminalprofiler_shared_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 + Source/ProfilerModule.cpp +) diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/gem.json b/Gems/ExternalProfilers/SuperluminalProfiler/gem.json new file mode 100644 index 000000000..f3e261893 --- /dev/null +++ b/Gems/ExternalProfilers/SuperluminalProfiler/gem.json @@ -0,0 +1,23 @@ +{ + "gem_name": "SuperluminalProfiler", + "version": "0.0.0", + "display_name": "Superluminal Profiler", + "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", + "origin": "O3DE Extras", + "origin_url": "https://github.com/o3de/o3de-extras/development/Gems/ExternalProfilers/SuperluminalProfiler", + "type": "Code", + "summary": "Implements the O3DE profiler tags to be used by the superluminal third party profiler", + "canonical_tags": [ + "Gem", + "Tools" + ], + "user_tags": [ + "Profiler" + ], + "icon_path": "preview.png", + "requirements": "Users will need to download Superluminal from the Superluminal Web Site.", + "documentation_url": "https://www.superluminal.eu/docs/documentation.html", + "dependencies": [ + ] +} diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/preview.png b/Gems/ExternalProfilers/SuperluminalProfiler/preview.png new file mode 100644 index 000000000..da1e14790 --- /dev/null +++ b/Gems/ExternalProfilers/SuperluminalProfiler/preview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e94d306395c37690f4ff8cb71ebde477e7446e0b0c23e15f23c0a0d0c9ea221b +size 3286 From 7173ae8bb98fee1541976f5ddff3c35ad412211e Mon Sep 17 00:00:00 2001 From: guillaume-haerinck Date: Mon, 24 Feb 2025 02:26:42 +0100 Subject: [PATCH 2/5] Add support for tracy profiler Signed-off-by: guillaume-haerinck --- .../Code/Source/CpuProfiler.cpp | 8 -- .../Code/Source/CpuProfiler.h | 6 -- .../Code/Source/ProfilerModule.cpp | 2 +- .../Code/Source/ProfilerSystemComponent.cpp | 1 - .../TracyProfiler/CMakeLists.txt | 11 ++ .../TracyProfiler/Code/CMakeLists.txt | 81 ++++++++++++++ .../TracyProfiler/Code/Source/CpuProfiler.cpp | 67 ++++++++++++ .../TracyProfiler/Code/Source/CpuProfiler.h | 50 +++++++++ .../Code/Source/ProfilerModule.cpp | 50 +++++++++ .../Code/Source/ProfilerSystemComponent.cpp | 102 ++++++++++++++++++ .../Code/Source/ProfilerSystemComponent.h | 53 +++++++++ .../Code/tracyprofiler_files.cmake | 14 +++ .../Code/tracyprofiler_shared_files.cmake | 11 ++ Gems/ExternalProfilers/TracyProfiler/gem.json | 23 ++++ .../TracyProfiler/preview.png | 3 + 15 files changed, 466 insertions(+), 16 deletions(-) create mode 100644 Gems/ExternalProfilers/TracyProfiler/CMakeLists.txt create mode 100644 Gems/ExternalProfilers/TracyProfiler/Code/CMakeLists.txt create mode 100644 Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.cpp create mode 100644 Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.h create mode 100644 Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerModule.cpp create mode 100644 Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.cpp create mode 100644 Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.h create mode 100644 Gems/ExternalProfilers/TracyProfiler/Code/tracyprofiler_files.cmake create mode 100644 Gems/ExternalProfilers/TracyProfiler/Code/tracyprofiler_shared_files.cmake create mode 100644 Gems/ExternalProfilers/TracyProfiler/gem.json create mode 100644 Gems/ExternalProfilers/TracyProfiler/preview.png diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.cpp b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.cpp index 3aa43811c..a359b54e1 100644 --- a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.cpp +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.cpp @@ -18,7 +18,6 @@ namespace SuperluminalProfiler { AZ::Interface::Register(this); m_initialized = true; - AZ::SystemTickBus::Handler::BusConnect(); } void CpuProfiler::Shutdown() @@ -33,7 +32,6 @@ namespace SuperluminalProfiler // Wait for the remaining threads that might still be processing its profiling calls AZStd::unique_lock shutdownLock(m_shutdownMutex); - AZ::SystemTickBus::Handler::BusDisconnect(); } void CpuProfiler::BeginRegion(const AZ::Debug::Budget* budget, const char* eventName, ...) @@ -55,10 +53,4 @@ namespace SuperluminalProfiler m_shutdownMutex.unlock_shared(); } } - - void CpuProfiler::OnSystemTick() - { - PerformanceAPI::InstrumentationScope("SystemTick", nullptr, PERFORMANCEAPI_MAKE_COLOR(0, 255, 0)); - } - } diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.h b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.h index 19e83e740..670a5983d 100644 --- a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.h +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.h @@ -20,7 +20,6 @@ namespace SuperluminalProfiler { class CpuProfiler final : public AZ::Debug::Profiler - , public AZ::SystemTickBus::Handler { public: AZ_RTTI(CpuProfiler, "{A05E7DC4-AB00-41BC-A739-8E58908CB84F}", AZ::Debug::Profiler); @@ -41,11 +40,6 @@ namespace SuperluminalProfiler //! that the profiler is active if returns True. bool IsContinuousCaptureInProgress() const; - //! AZ::SystemTickBus::Handler overrides - //! When fired, the profiler collects all profiling data from registered threads and updates - //! m_timeRegionMap so that the next frame has up-to-date profiling data. - void OnSystemTick() final override; - private: // This lock will only be contested when the CpuProfiler's Shutdown() method has been called AZStd::shared_mutex m_shutdownMutex; diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerModule.cpp b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerModule.cpp index decde724a..8b3826f89 100644 --- a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerModule.cpp +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerModule.cpp @@ -46,5 +46,5 @@ namespace SuperluminalProfiler #if defined(O3DE_GEM_NAME) AZ_DECLARE_MODULE_CLASS(AZ_JOIN(Gem_, O3DE_GEM_NAME), SuperluminalProfiler::ProfilerModule) #else -AZ_DECLARE_MODULE_CLASS(Gem_Profiler, SuperluminalProfiler::ProfilerModule) +AZ_DECLARE_MODULE_CLASS(Gem_SuperluminalProfiler, SuperluminalProfiler::ProfilerModule) #endif diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.cpp b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.cpp index 1d885b810..b024ad9fb 100644 --- a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.cpp +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.cpp @@ -77,7 +77,6 @@ namespace SuperluminalProfiler void ProfilerSystemComponent::SetActive([[maybe_unused]] bool enabled) { - } bool ProfilerSystemComponent::CaptureFrame([[maybe_unused]] const AZStd::string& outputFilePath) diff --git a/Gems/ExternalProfilers/TracyProfiler/CMakeLists.txt b/Gems/ExternalProfilers/TracyProfiler/CMakeLists.txt new file mode 100644 index 000000000..8557a221e --- /dev/null +++ b/Gems/ExternalProfilers/TracyProfiler/CMakeLists.txt @@ -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 +# +# + +o3de_gem_setup("TracyProfiler") + +add_subdirectory(Code) diff --git a/Gems/ExternalProfilers/TracyProfiler/Code/CMakeLists.txt b/Gems/ExternalProfilers/TracyProfiler/Code/CMakeLists.txt new file mode 100644 index 000000000..ae98e3fcf --- /dev/null +++ b/Gems/ExternalProfilers/TracyProfiler/Code/CMakeLists.txt @@ -0,0 +1,81 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +# Add Tracy dependency using FetchContent +include(FetchContent) +FetchContent_Declare( + Tracy + GIT_REPOSITORY "https://github.com/wolfpld/tracy" + GIT_TAG "5d542dc09f3d9378d005092a4ad446bd405f819a" # version 0.11.1 +) + +set(TRACY_ENABLE ON) +set(TRACY_CALLSTACK ON) +set(TRACY_ON_DEMAND ON) +ly_append_configurations_options( + DEFINES + TRACY_ENABLE +) + +FetchContent_MakeAvailable(Tracy) + +# Let's not clutter the root of any IDE folder structure with 3rd party dependencies +# Setting the FOLDER makes it show up there in the solution build in VS and similarly +# any other IDEs that organize in folders. +set_target_properties( + TracyClient + PROPERTIES + FOLDER "3rdParty Dependencies" +) + +ly_add_target( + NAME ${gem_name}.Static STATIC + NAMESPACE Gem + FILES_CMAKE + tracyprofiler_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + ${Tracy_SOURCE_DIR}/public/tracy + BUILD_DEPENDENCIES + PUBLIC + AZ::AzCore + AZ::AzFramework + TracyClient +) + +ly_add_target( + NAME ${gem_name} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAMESPACE Gem + FILES_CMAKE + tracyprofiler_shared_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + Gem::${gem_name}.Static +) + +ly_add_source_properties( + SOURCES + Source/ProfilerModule.cpp + PROPERTY COMPILE_DEFINITIONS + VALUES + O3DE_GEM_NAME=${gem_name} + O3DE_GEM_VERSION=${gem_version}) + +ly_create_alias(NAME ${gem_name}.Servers NAMESPACE Gem TARGETS Gem::${gem_name}) +ly_create_alias(NAME ${gem_name}.Builders NAMESPACE Gem TARGETS Gem::${gem_name}) +ly_create_alias(NAME ${gem_name}.Clients NAMESPACE Gem TARGETS Gem::${gem_name}) +ly_create_alias(NAME ${gem_name}.Unified NAMESPACE Gem TARGETS Gem::${gem_name}) +ly_create_alias(NAME ${gem_name}.Tools NAMESPACE Gem TARGETS Gem::${gem_name}) diff --git a/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.cpp b/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.cpp new file mode 100644 index 000000000..1c0d7bc84 --- /dev/null +++ b/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include + +namespace TracyProfiler +{ + thread_local CpuProfiler::EventIdStack CpuProfiler::ms_threadLocalStorage; + + void CpuProfiler::Init() + { + AZ::Interface::Register(this); + m_initialized = true; + } + + void CpuProfiler::Shutdown() + { + if (!m_initialized) + { + return; + } + + // When this call is made, no more thread profiling calls can be performed anymore + AZ::Interface::Unregister(this); + + // Wait for the remaining threads that might still be processing its profiling calls + AZStd::unique_lock shutdownLock(m_shutdownMutex); + } + + void CpuProfiler::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()) + { + ms_threadLocalStorage.push_back({}); + TracyCZoneCtx& ctx = ms_threadLocalStorage.back(); + ctx = ___tracy_emit_zone_begin_alloc_callstack( + ___tracy_alloc_srcloc_name(__LINE__, __FILE__, strlen(__FILE__), __FUNCTION__, strlen(__FUNCTION__), eventName, strlen(eventName), budget->Crc()), + TRACY_CALLSTACK, + true + ); + TracyCZoneText(ctx, budget->Name(), 1); + m_shutdownMutex.unlock_shared(); + } + } + + void CpuProfiler::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() && !ms_threadLocalStorage.empty()) + { + auto& ctx = ms_threadLocalStorage.back(); + TracyCZoneEnd(ctx); + ms_threadLocalStorage.pop_back(); + + m_shutdownMutex.unlock_shared(); + } + } +} diff --git a/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.h b/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.h new file mode 100644 index 000000000..434157c9b --- /dev/null +++ b/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace TracyProfiler +{ + class CpuProfiler final + : public AZ::Debug::Profiler + { + public: + AZ_RTTI(CpuProfiler, "{9467E3F6-0581-4E46-A98A-F3C249FD7B24}", AZ::Debug::Profiler); + AZ_CLASS_ALLOCATOR(CpuProfiler, AZ::SystemAllocator); + + CpuProfiler() = default; + ~CpuProfiler() = default; + + //! Registers/un-registers the AZ::Debug::Profiler instance to the interface + void Init(); + void Shutdown(); + + //! 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; + + private: + using EventIdStack = AZStd::vector; + static thread_local EventIdStack ms_threadLocalStorage; + + // This lock will only be contested when the CpuProfiler's Shutdown() method has been called + AZStd::shared_mutex m_shutdownMutex; + + bool m_initialized = false; + }; +} diff --git a/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerModule.cpp b/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerModule.cpp new file mode 100644 index 000000000..5437ec59a --- /dev/null +++ b/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerModule.cpp @@ -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 + * + */ + +#include + +#include +#include + +namespace TracyProfiler +{ + class ProfilerModule + : public AZ::Module + { + public: + AZ_RTTI(ProfilerModule, "{BCE29245-571B-44C2-94E7-84D7E06F5A1F}", AZ::Module); + AZ_CLASS_ALLOCATOR(ProfilerModule, AZ::SystemAllocator); + + ProfilerModule() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. + // This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert(m_descriptors.end(), { + ProfilerSystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + azrtti_typeid(), + }; + } + }; +} + +#if defined(O3DE_GEM_NAME) +AZ_DECLARE_MODULE_CLASS(AZ_JOIN(Gem_, O3DE_GEM_NAME), TracyProfiler::ProfilerModule) +#else +AZ_DECLARE_MODULE_CLASS(Gem_TracyProfiler, TracyProfiler::ProfilerModule) +#endif diff --git a/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.cpp b/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.cpp new file mode 100644 index 000000000..8e09d5b76 --- /dev/null +++ b/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.cpp @@ -0,0 +1,102 @@ +/* + * 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 "ProfilerSystemComponent.h" + +#include +#include +#include +#include + +namespace TracyProfiler +{ + static constexpr AZ::Crc32 profilerServiceCrc = AZ_CRC_CE("ProfilerService"); + + void ProfilerSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(0); + } + } + + void ProfilerSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(profilerServiceCrc); + } + + void ProfilerSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(profilerServiceCrc); + } + + void ProfilerSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + } + + void ProfilerSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + } + + ProfilerSystemComponent::ProfilerSystemComponent() + { + if (AZ::Debug::ProfilerSystemInterface::Get() == nullptr) + { + AZ::Debug::ProfilerSystemInterface::Register(this); + } + } + + ProfilerSystemComponent::~ProfilerSystemComponent() + { + if (AZ::Debug::ProfilerSystemInterface::Get() == this) + { + AZ::Debug::ProfilerSystemInterface::Unregister(this); + } + } + + void ProfilerSystemComponent::Activate() + { + m_cpuProfiler.Init(); + } + + void ProfilerSystemComponent::Deactivate() + { + m_cpuProfiler.Shutdown(); + } + + bool ProfilerSystemComponent::IsActive() const + { + return false; + } + + void ProfilerSystemComponent::SetActive([[maybe_unused]] bool enabled) + { + } + + bool ProfilerSystemComponent::CaptureFrame([[maybe_unused]] const AZStd::string& outputFilePath) + { + return true; + } + + bool ProfilerSystemComponent::StartCapture([[maybe_unused]] AZStd::string outputFilePath) + { + return true; + } + + bool ProfilerSystemComponent::EndCapture() + { + return true; + } + + bool ProfilerSystemComponent::IsCaptureInProgress() const + { + return false; + } + +} diff --git a/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.h b/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.h new file mode 100644 index 000000000..c43cfbad8 --- /dev/null +++ b/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include "CpuProfiler.h" + +#include +#include + +namespace TracyProfiler +{ + class ProfilerSystemComponent + : public AZ::Component + , protected AZ::Debug::ProfilerRequests + { + public: + AZ_COMPONENT(ProfilerSystemComponent, "{839DAD12-4571-4484-B71F-2133A5BC1137}"); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + ProfilerSystemComponent(); + ~ProfilerSystemComponent(); + + protected: + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + + // ProfilerRequests interface implementation + bool IsActive() const override; + void SetActive(bool active) override; + bool CaptureFrame(const AZStd::string& outputFilePath) override; + bool StartCapture(AZStd::string outputFilePath) override; + bool EndCapture() override; + bool IsCaptureInProgress() const override; + + private: + AZStd::atomic_bool m_cpuCaptureInProgress{ false }; + CpuProfiler m_cpuProfiler; + }; + +} diff --git a/Gems/ExternalProfilers/TracyProfiler/Code/tracyprofiler_files.cmake b/Gems/ExternalProfilers/TracyProfiler/Code/tracyprofiler_files.cmake new file mode 100644 index 000000000..0088f1baa --- /dev/null +++ b/Gems/ExternalProfilers/TracyProfiler/Code/tracyprofiler_files.cmake @@ -0,0 +1,14 @@ +# +# 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 + Source/CpuProfiler.h + Source/CpuProfiler.cpp + Source/ProfilerSystemComponent.cpp + Source/ProfilerSystemComponent.h +) diff --git a/Gems/ExternalProfilers/TracyProfiler/Code/tracyprofiler_shared_files.cmake b/Gems/ExternalProfilers/TracyProfiler/Code/tracyprofiler_shared_files.cmake new file mode 100644 index 000000000..b8ee476e2 --- /dev/null +++ b/Gems/ExternalProfilers/TracyProfiler/Code/tracyprofiler_shared_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 + Source/ProfilerModule.cpp +) diff --git a/Gems/ExternalProfilers/TracyProfiler/gem.json b/Gems/ExternalProfilers/TracyProfiler/gem.json new file mode 100644 index 000000000..dbed91c43 --- /dev/null +++ b/Gems/ExternalProfilers/TracyProfiler/gem.json @@ -0,0 +1,23 @@ +{ + "gem_name": "TracyProfiler", + "version": "0.0.0", + "display_name": "Tracy Profiler", + "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", + "origin": "O3DE Extras", + "origin_url": "https://github.com/o3de/o3de-extras/development/Gems/ExternalProfilers/TracyProfiler", + "type": "Code", + "summary": "Implements the O3DE profiler tags to be used by the Tracy third party profiler", + "canonical_tags": [ + "Gem", + "Tools" + ], + "user_tags": [ + "Profiler" + ], + "icon_path": "preview.png", + "requirements": "Users will need to download Tracy from the Github release.", + "documentation_url": "https://github.com/wolfpld/tracy", + "dependencies": [ + ] +} diff --git a/Gems/ExternalProfilers/TracyProfiler/preview.png b/Gems/ExternalProfilers/TracyProfiler/preview.png new file mode 100644 index 000000000..33eefd7ff --- /dev/null +++ b/Gems/ExternalProfilers/TracyProfiler/preview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:577c064676b5b2b065697c98843dc2c4b8db56363548400d166aebaf14073c5f +size 2486 From a83ff677f4219757db7c97bc2b82a7485fa27817 Mon Sep 17 00:00:00 2001 From: guillaume-haerinck Date: Fri, 28 Feb 2025 23:24:37 +0100 Subject: [PATCH 3/5] add basic optick support and clang-format everything Signed-off-by: guillaume-haerinck --- .../OptickProfiler/CMakeLists.txt | 11 ++ .../OptickProfiler/Code/CMakeLists.txt | 66 ++++++++++ .../Code/Source/CpuProfiler.cpp | 86 +++++++++++++ .../OptickProfiler/Code/Source/CpuProfiler.h | 49 ++++++++ .../Code/Source/ProfilerModule.cpp | 51 ++++++++ .../Code/Source/ProfilerSystemComponent.cpp | 114 ++++++++++++++++++ .../Code/Source/ProfilerSystemComponent.h | 54 +++++++++ .../Code/optickprofiler_files.cmake | 14 +++ .../Code/optickprofiler_shared_files.cmake | 11 ++ .../ExternalProfilers/OptickProfiler/gem.json | 23 ++++ .../OptickProfiler/preview.png | 3 + .../Code/Source/CpuProfiler.cpp | 80 ++++++------ .../Code/Source/CpuProfiler.h | 57 +++++---- .../Code/Source/ProfilerModule.cpp | 55 ++++----- .../Code/Source/ProfilerSystemComponent.cpp | 5 +- .../Code/Source/ProfilerSystemComponent.h | 2 +- .../SuperluminalProfiler/gem.json | 3 +- .../TracyProfiler/Code/Source/CpuProfiler.cpp | 91 +++++++------- .../TracyProfiler/Code/Source/CpuProfiler.h | 55 +++++---- .../Code/Source/ProfilerModule.cpp | 55 ++++----- .../Code/Source/ProfilerSystemComponent.cpp | 5 +- .../Code/Source/ProfilerSystemComponent.h | 2 +- Gems/ExternalProfilers/TracyProfiler/gem.json | 3 +- 23 files changed, 689 insertions(+), 206 deletions(-) create mode 100644 Gems/ExternalProfilers/OptickProfiler/CMakeLists.txt create mode 100644 Gems/ExternalProfilers/OptickProfiler/Code/CMakeLists.txt create mode 100644 Gems/ExternalProfilers/OptickProfiler/Code/Source/CpuProfiler.cpp create mode 100644 Gems/ExternalProfilers/OptickProfiler/Code/Source/CpuProfiler.h create mode 100644 Gems/ExternalProfilers/OptickProfiler/Code/Source/ProfilerModule.cpp create mode 100644 Gems/ExternalProfilers/OptickProfiler/Code/Source/ProfilerSystemComponent.cpp create mode 100644 Gems/ExternalProfilers/OptickProfiler/Code/Source/ProfilerSystemComponent.h create mode 100644 Gems/ExternalProfilers/OptickProfiler/Code/optickprofiler_files.cmake create mode 100644 Gems/ExternalProfilers/OptickProfiler/Code/optickprofiler_shared_files.cmake create mode 100644 Gems/ExternalProfilers/OptickProfiler/gem.json create mode 100644 Gems/ExternalProfilers/OptickProfiler/preview.png diff --git a/Gems/ExternalProfilers/OptickProfiler/CMakeLists.txt b/Gems/ExternalProfilers/OptickProfiler/CMakeLists.txt new file mode 100644 index 000000000..11f6fc719 --- /dev/null +++ b/Gems/ExternalProfilers/OptickProfiler/CMakeLists.txt @@ -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 +# +# + +o3de_gem_setup("OptickProfiler") + +add_subdirectory(Code) diff --git a/Gems/ExternalProfilers/OptickProfiler/Code/CMakeLists.txt b/Gems/ExternalProfilers/OptickProfiler/Code/CMakeLists.txt new file mode 100644 index 000000000..2a2033492 --- /dev/null +++ b/Gems/ExternalProfilers/OptickProfiler/Code/CMakeLists.txt @@ -0,0 +1,66 @@ +# +# 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 +# +# + +# Add Optick dependency using FetchContent +include(FetchContent) +FetchContent_Declare( + Optick + GIT_REPOSITORY "https://github.com/bombomby/optick" + GIT_TAG "8abd28dee1a4034c973a3d32cd1777118e72df7e" # version 1.4.0+ +) + +set(OPTICK_ENABLED ON) +set(OPTICK_BUILD_GUI_APP OFF) +set(OPTICK_INSTALL_TARGETS OFF) + +FetchContent_MakeAvailable(Optick) +ly_add_target( + NAME ${gem_name}.Static STATIC + NAMESPACE Gem + FILES_CMAKE + optickprofiler_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + BUILD_DEPENDENCIES + PUBLIC + AZ::AzCore + AZ::AzFramework + OptickCore +) + +ly_add_target( + NAME ${gem_name} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAMESPACE Gem + FILES_CMAKE + optickprofiler_shared_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + Gem::${gem_name}.Static +) + +ly_add_source_properties( + SOURCES + Source/ProfilerModule.cpp + PROPERTY COMPILE_DEFINITIONS + VALUES + O3DE_GEM_NAME=${gem_name} + O3DE_GEM_VERSION=${gem_version}) + +ly_create_alias(NAME ${gem_name}.Servers NAMESPACE Gem TARGETS Gem::${gem_name}) +ly_create_alias(NAME ${gem_name}.Builders NAMESPACE Gem TARGETS Gem::${gem_name}) +ly_create_alias(NAME ${gem_name}.Clients NAMESPACE Gem TARGETS Gem::${gem_name}) +ly_create_alias(NAME ${gem_name}.Unified NAMESPACE Gem TARGETS Gem::${gem_name}) +ly_create_alias(NAME ${gem_name}.Tools NAMESPACE Gem TARGETS Gem::${gem_name}) diff --git a/Gems/ExternalProfilers/OptickProfiler/Code/Source/CpuProfiler.cpp b/Gems/ExternalProfilers/OptickProfiler/Code/Source/CpuProfiler.cpp new file mode 100644 index 000000000..51a535865 --- /dev/null +++ b/Gems/ExternalProfilers/OptickProfiler/Code/Source/CpuProfiler.cpp @@ -0,0 +1,86 @@ +/* + * 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 + +namespace OptickProfiler +{ + bool OnOptickStateChanged(Optick::State::Type state) + { + switch (state) + { + case Optick::State::DUMP_CAPTURE: + { + auto projectName = AZ::Utils::GetProjectName(); + Optick::AttachSummary("Project", projectName.c_str()); + } + break; + } + return true; + } + + void CpuProfiler::Init() + { + AZ::Interface::Register(this); + AZ::SystemTickBus::Handler::BusConnect(); + + Optick::SetStateChangedCallback(OnOptickStateChanged); + m_initialized = true; + } + + void CpuProfiler::Shutdown() + { + if (!m_initialized) + { + return; + } + + // When this call is made, no more thread profiling calls can be performed anymore + AZ::Interface::Unregister(this); + + // Wait for the remaining threads that might still be processing its profiling calls + AZStd::unique_lock shutdownLock(m_shutdownMutex); + + AZ::SystemTickBus::Handler::BusDisconnect(); + + Optick::StopCapture(); + Optick::Shutdown(); + } + + void CpuProfiler::BeginRegion([[maybe_unused]] const AZ::Debug::Budget* budget, [[maybe_unused]] 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()) + { + Optick::Event::Push(eventName); + m_shutdownMutex.unlock_shared(); + } + } + + void CpuProfiler::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()) + { + Optick::Event::Pop(); + m_shutdownMutex.unlock_shared(); + } + } + + void CpuProfiler::OnSystemTick() + { + Optick::Update(); + } +} // namespace OptickProfiler diff --git a/Gems/ExternalProfilers/OptickProfiler/Code/Source/CpuProfiler.h b/Gems/ExternalProfilers/OptickProfiler/Code/Source/CpuProfiler.h new file mode 100644 index 000000000..b2dc6289c --- /dev/null +++ b/Gems/ExternalProfilers/OptickProfiler/Code/Source/CpuProfiler.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace OptickProfiler +{ + class CpuProfiler final + : public AZ::Debug::Profiler + , public AZ::SystemTickBus::Handler + { + public: + AZ_RTTI(CpuProfiler, "{E4076EA4-EF44-499A-9750-37B623BBBF7C}", AZ::Debug::Profiler); + AZ_CLASS_ALLOCATOR(CpuProfiler, AZ::SystemAllocator); + + CpuProfiler() = default; + ~CpuProfiler() = default; + + //! Registers/un-registers the AZ::Debug::Profiler instance to the interface + void Init(); + void Shutdown(); + + //! 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; + + //! AZ::SystemTickBus::Handler overrides + void OnSystemTick() final override; + + private: + // This lock will only be contested when the CpuProfiler's Shutdown() method has been called + AZStd::shared_mutex m_shutdownMutex; + + bool m_initialized = false; + }; +} // namespace OptickProfiler diff --git a/Gems/ExternalProfilers/OptickProfiler/Code/Source/ProfilerModule.cpp b/Gems/ExternalProfilers/OptickProfiler/Code/Source/ProfilerModule.cpp new file mode 100644 index 000000000..0e92d8588 --- /dev/null +++ b/Gems/ExternalProfilers/OptickProfiler/Code/Source/ProfilerModule.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include +#include + +namespace OptickProfiler +{ + class ProfilerModule : public AZ::Module + { + public: + AZ_RTTI(ProfilerModule, "{189570C0-0E0E-4826-8AEC-DCE972CFC9B2}", AZ::Module); + AZ_CLASS_ALLOCATOR(ProfilerModule, AZ::SystemAllocator); + + ProfilerModule() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and + // EditContext. This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert( + m_descriptors.end(), + { + ProfilerSystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + azrtti_typeid(), + }; + } + }; +} // namespace OptickProfiler + +#if defined(O3DE_GEM_NAME) +AZ_DECLARE_MODULE_CLASS(AZ_JOIN(Gem_, O3DE_GEM_NAME), OptickProfiler::ProfilerModule) +#else +AZ_DECLARE_MODULE_CLASS(Gem_OptickProfiler, OptickProfiler::ProfilerModule) +#endif diff --git a/Gems/ExternalProfilers/OptickProfiler/Code/Source/ProfilerSystemComponent.cpp b/Gems/ExternalProfilers/OptickProfiler/Code/Source/ProfilerSystemComponent.cpp new file mode 100644 index 000000000..e4eef6b9d --- /dev/null +++ b/Gems/ExternalProfilers/OptickProfiler/Code/Source/ProfilerSystemComponent.cpp @@ -0,0 +1,114 @@ +/* + * 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 "ProfilerSystemComponent.h" + +#include +#include +#include +#include +#include +#include + +namespace OptickProfiler +{ + static constexpr AZ::Crc32 profilerServiceCrc = AZ_CRC_CE("ProfilerService"); + + void ProfilerSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class()->Version(0); + } + } + + void ProfilerSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(profilerServiceCrc); + } + + void ProfilerSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(profilerServiceCrc); + } + + void ProfilerSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) + { + } + + void ProfilerSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + } + + ProfilerSystemComponent::ProfilerSystemComponent() + { + if (AZ::Debug::ProfilerSystemInterface::Get() == nullptr) + { + AZ::Debug::ProfilerSystemInterface::Register(this); + } + } + + ProfilerSystemComponent::~ProfilerSystemComponent() + { + if (AZ::Debug::ProfilerSystemInterface::Get() == this) + { + AZ::Debug::ProfilerSystemInterface::Unregister(this); + } + } + + void ProfilerSystemComponent::Activate() + { + m_cpuProfiler.Init(); + } + + void ProfilerSystemComponent::Deactivate() + { + m_cpuProfiler.Shutdown(); + } + + bool ProfilerSystemComponent::IsActive() const + { + return false; + } + + void ProfilerSystemComponent::SetActive([[maybe_unused]] bool enabled) + { + } + + bool ProfilerSystemComponent::CaptureFrame([[maybe_unused]] const AZStd::string& outputFilePath) + { + return false; + } + + bool ProfilerSystemComponent::StartCapture(AZStd::string outputFilePath) + { + if (!m_cpuCaptureInProgress && Optick::StartCapture()) + { + m_captureFile = AZStd::move(outputFilePath); + m_cpuCaptureInProgress = true; + return true; + } + return false; + } + + bool ProfilerSystemComponent::EndCapture() + { + if (m_cpuCaptureInProgress && Optick::StopCapture()) + { + m_cpuCaptureInProgress = false; + return Optick::SaveCapture(m_captureFile.c_str()); + } + return false; + } + + bool ProfilerSystemComponent::IsCaptureInProgress() const + { + return m_cpuCaptureInProgress; + } + +} // namespace OptickProfiler diff --git a/Gems/ExternalProfilers/OptickProfiler/Code/Source/ProfilerSystemComponent.h b/Gems/ExternalProfilers/OptickProfiler/Code/Source/ProfilerSystemComponent.h new file mode 100644 index 000000000..bd34a68ba --- /dev/null +++ b/Gems/ExternalProfilers/OptickProfiler/Code/Source/ProfilerSystemComponent.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include "CpuProfiler.h" + +#include +#include + +namespace OptickProfiler +{ + class ProfilerSystemComponent + : public AZ::Component + , protected AZ::Debug::ProfilerRequests + { + public: + AZ_COMPONENT(ProfilerSystemComponent, "{E140D972-C1C0-44A7-A563-9F973944A8A1}"); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + ProfilerSystemComponent(); + ~ProfilerSystemComponent(); + + protected: + // AZ::Component interface implementation + void Activate() override; + void Deactivate() override; + + // ProfilerRequests interface implementation + bool IsActive() const override; + void SetActive(bool active) override; + bool CaptureFrame(const AZStd::string& outputFilePath) override; + bool StartCapture(AZStd::string outputFilePath) override; + bool EndCapture() override; + bool IsCaptureInProgress() const override; + + private: + AZStd::string m_captureFile; + AZStd::atomic_bool m_cpuCaptureInProgress{ false }; + CpuProfiler m_cpuProfiler; + }; + +} // namespace OptickProfiler diff --git a/Gems/ExternalProfilers/OptickProfiler/Code/optickprofiler_files.cmake b/Gems/ExternalProfilers/OptickProfiler/Code/optickprofiler_files.cmake new file mode 100644 index 000000000..0088f1baa --- /dev/null +++ b/Gems/ExternalProfilers/OptickProfiler/Code/optickprofiler_files.cmake @@ -0,0 +1,14 @@ +# +# 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 + Source/CpuProfiler.h + Source/CpuProfiler.cpp + Source/ProfilerSystemComponent.cpp + Source/ProfilerSystemComponent.h +) diff --git a/Gems/ExternalProfilers/OptickProfiler/Code/optickprofiler_shared_files.cmake b/Gems/ExternalProfilers/OptickProfiler/Code/optickprofiler_shared_files.cmake new file mode 100644 index 000000000..b8ee476e2 --- /dev/null +++ b/Gems/ExternalProfilers/OptickProfiler/Code/optickprofiler_shared_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 + Source/ProfilerModule.cpp +) diff --git a/Gems/ExternalProfilers/OptickProfiler/gem.json b/Gems/ExternalProfilers/OptickProfiler/gem.json new file mode 100644 index 000000000..0b8392756 --- /dev/null +++ b/Gems/ExternalProfilers/OptickProfiler/gem.json @@ -0,0 +1,23 @@ +{ + "gem_name": "OptickProfiler", + "version": "0.0.0", + "display_name": "Optick Profiler", + "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", + "origin": "O3DE Extras", + "origin_url": "https://github.com/o3de/o3de-extras/development/Gems/ExternalProfilers/OptickProfiler", + "type": "Code", + "summary": "Implements the O3DE profiler tags to be used by the Optick third party profiler", + "canonical_tags": [ + "Gem", + "Tools" + ], + "user_tags": [ + "Profiler" + ], + "icon_path": "preview.png", + "requirements": "Users will need to download Optick GUI from the Github release.", + "dependencies": [ + ], + "provided_unique_service": "Profiler" +} diff --git a/Gems/ExternalProfilers/OptickProfiler/preview.png b/Gems/ExternalProfilers/OptickProfiler/preview.png new file mode 100644 index 000000000..4203e1702 --- /dev/null +++ b/Gems/ExternalProfilers/OptickProfiler/preview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9aceaec5ac0ed223a91709a95b6679f498ea9ee91c47228dcd2af49bed805c2 +size 3343 diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.cpp b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.cpp index a359b54e1..1faae19e5 100644 --- a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.cpp +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.cpp @@ -14,43 +14,43 @@ namespace SuperluminalProfiler { - void CpuProfiler::Init() - { - AZ::Interface::Register(this); - m_initialized = true; - } - - void CpuProfiler::Shutdown() - { - if (!m_initialized) - { - return; - } - - // When this call is made, no more thread profiling calls can be performed anymore - AZ::Interface::Unregister(this); - - // Wait for the remaining threads that might still be processing its profiling calls - AZStd::unique_lock shutdownLock(m_shutdownMutex); - } - - void CpuProfiler::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()) - { - PerformanceAPI_BeginEvent(eventName, budget->Name(), budget->Crc()); - m_shutdownMutex.unlock_shared(); - } - } - - void CpuProfiler::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()) - { - PerformanceAPI_EndEvent(); - m_shutdownMutex.unlock_shared(); - } - } -} + void CpuProfiler::Init() + { + AZ::Interface::Register(this); + m_initialized = true; + } + + void CpuProfiler::Shutdown() + { + if (!m_initialized) + { + return; + } + + // When this call is made, no more thread profiling calls can be performed anymore + AZ::Interface::Unregister(this); + + // Wait for the remaining threads that might still be processing its profiling calls + AZStd::unique_lock shutdownLock(m_shutdownMutex); + } + + void CpuProfiler::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()) + { + PerformanceAPI_BeginEvent(eventName, budget->Name(), budget->Crc()); + m_shutdownMutex.unlock_shared(); + } + } + + void CpuProfiler::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()) + { + PerformanceAPI_EndEvent(); + m_shutdownMutex.unlock_shared(); + } + } +} // namespace SuperluminalProfiler diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.h b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.h index 670a5983d..db9461c49 100644 --- a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.h +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/CpuProfiler.h @@ -18,32 +18,31 @@ namespace SuperluminalProfiler { - class CpuProfiler final - : public AZ::Debug::Profiler - { - public: - AZ_RTTI(CpuProfiler, "{A05E7DC4-AB00-41BC-A739-8E58908CB84F}", AZ::Debug::Profiler); - AZ_CLASS_ALLOCATOR(CpuProfiler, AZ::SystemAllocator); - - CpuProfiler() = default; - ~CpuProfiler() = default; - - //! Registers/un-registers the AZ::Debug::Profiler instance to the interface - void Init(); - void Shutdown(); - - //! 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; - - //! Check to see if a programmatic capture is currently in progress, implies - //! that the profiler is active if returns True. - bool IsContinuousCaptureInProgress() const; - - private: - // This lock will only be contested when the CpuProfiler's Shutdown() method has been called - AZStd::shared_mutex m_shutdownMutex; - - bool m_initialized = false; - }; -} + class CpuProfiler final : public AZ::Debug::Profiler + { + public: + AZ_RTTI(CpuProfiler, "{A05E7DC4-AB00-41BC-A739-8E58908CB84F}", AZ::Debug::Profiler); + AZ_CLASS_ALLOCATOR(CpuProfiler, AZ::SystemAllocator); + + CpuProfiler() = default; + ~CpuProfiler() = default; + + //! Registers/un-registers the AZ::Debug::Profiler instance to the interface + void Init(); + void Shutdown(); + + //! 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; + + //! Check to see if a programmatic capture is currently in progress, implies + //! that the profiler is active if returns True. + bool IsContinuousCaptureInProgress() const; + + private: + // This lock will only be contested when the CpuProfiler's Shutdown() method has been called + AZStd::shared_mutex m_shutdownMutex; + + bool m_initialized = false; + }; +} // namespace SuperluminalProfiler diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerModule.cpp b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerModule.cpp index 8b3826f89..a2fc0b643 100644 --- a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerModule.cpp +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerModule.cpp @@ -13,35 +13,36 @@ namespace SuperluminalProfiler { - class ProfilerModule - : public AZ::Module - { - public: - AZ_RTTI(ProfilerModule, "{7C666AFB-E699-42FB-8F32-DAEE5A62CD01}", AZ::Module); - AZ_CLASS_ALLOCATOR(ProfilerModule, AZ::SystemAllocator); + class ProfilerModule : public AZ::Module + { + public: + AZ_RTTI(ProfilerModule, "{7C666AFB-E699-42FB-8F32-DAEE5A62CD01}", AZ::Module); + AZ_CLASS_ALLOCATOR(ProfilerModule, AZ::SystemAllocator); - ProfilerModule() - { - // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. - // Add ALL components descriptors associated with this gem to m_descriptors. - // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. - // This happens through the [MyComponent]::Reflect() function. - m_descriptors.insert(m_descriptors.end(), { - ProfilerSystemComponent::CreateDescriptor(), - }); - } + ProfilerModule() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and + // EditContext. This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert( + m_descriptors.end(), + { + ProfilerSystemComponent::CreateDescriptor(), + }); + } - /** - * Add required SystemComponents to the SystemEntity. - */ - AZ::ComponentTypeList GetRequiredSystemComponents() const override - { - return AZ::ComponentTypeList{ - azrtti_typeid(), - }; - } - }; -} + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + azrtti_typeid(), + }; + } + }; +} // namespace SuperluminalProfiler #if defined(O3DE_GEM_NAME) AZ_DECLARE_MODULE_CLASS(AZ_JOIN(Gem_, O3DE_GEM_NAME), SuperluminalProfiler::ProfilerModule) diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.cpp b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.cpp index b024ad9fb..906c3e778 100644 --- a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.cpp +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.cpp @@ -21,8 +21,7 @@ namespace SuperluminalProfiler { if (AZ::SerializeContext* serialize = azrtti_cast(context)) { - serialize->Class() - ->Version(0); + serialize->Class()->Version(0); } } @@ -99,4 +98,4 @@ namespace SuperluminalProfiler return false; } -} +} // namespace SuperluminalProfiler diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.h b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.h index 068840eac..1002ea480 100644 --- a/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.h +++ b/Gems/ExternalProfilers/SuperluminalProfiler/Code/Source/ProfilerSystemComponent.h @@ -50,4 +50,4 @@ namespace SuperluminalProfiler CpuProfiler m_cpuProfiler; }; -} +} // namespace SuperluminalProfiler diff --git a/Gems/ExternalProfilers/SuperluminalProfiler/gem.json b/Gems/ExternalProfilers/SuperluminalProfiler/gem.json index f3e261893..0bc3b505f 100644 --- a/Gems/ExternalProfilers/SuperluminalProfiler/gem.json +++ b/Gems/ExternalProfilers/SuperluminalProfiler/gem.json @@ -19,5 +19,6 @@ "requirements": "Users will need to download Superluminal from the Superluminal Web Site.", "documentation_url": "https://www.superluminal.eu/docs/documentation.html", "dependencies": [ - ] + ], + "provided_unique_service": "Profiler" } diff --git a/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.cpp b/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.cpp index 1c0d7bc84..d118921b0 100644 --- a/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.cpp +++ b/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.cpp @@ -13,55 +13,56 @@ namespace TracyProfiler { - thread_local CpuProfiler::EventIdStack CpuProfiler::ms_threadLocalStorage; + thread_local CpuProfiler::EventIdStack CpuProfiler::ms_threadLocalStorage; - void CpuProfiler::Init() - { - AZ::Interface::Register(this); - m_initialized = true; - } + void CpuProfiler::Init() + { + AZ::Interface::Register(this); + m_initialized = true; + } - void CpuProfiler::Shutdown() - { - if (!m_initialized) - { - return; - } + void CpuProfiler::Shutdown() + { + if (!m_initialized) + { + return; + } - // When this call is made, no more thread profiling calls can be performed anymore - AZ::Interface::Unregister(this); + // When this call is made, no more thread profiling calls can be performed anymore + AZ::Interface::Unregister(this); - // Wait for the remaining threads that might still be processing its profiling calls - AZStd::unique_lock shutdownLock(m_shutdownMutex); - } + // Wait for the remaining threads that might still be processing its profiling calls + AZStd::unique_lock shutdownLock(m_shutdownMutex); + } - void CpuProfiler::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()) - { - ms_threadLocalStorage.push_back({}); - TracyCZoneCtx& ctx = ms_threadLocalStorage.back(); - ctx = ___tracy_emit_zone_begin_alloc_callstack( - ___tracy_alloc_srcloc_name(__LINE__, __FILE__, strlen(__FILE__), __FUNCTION__, strlen(__FUNCTION__), eventName, strlen(eventName), budget->Crc()), - TRACY_CALLSTACK, - true - ); - TracyCZoneText(ctx, budget->Name(), 1); - m_shutdownMutex.unlock_shared(); - } - } + void CpuProfiler::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()) + { + // Do not use the macro as we don't want to use static storage (else events are wrong) + ms_threadLocalStorage.push_back({}); + TracyCZoneCtx& ctx = ms_threadLocalStorage.back(); + ctx = ___tracy_emit_zone_begin_alloc_callstack( + ___tracy_alloc_srcloc_name( + __LINE__, __FILE__, strlen(__FILE__), __FUNCTION__, strlen(__FUNCTION__), eventName, strlen(eventName), budget->Crc()), + TRACY_CALLSTACK, + true); + TracyCZoneText(ctx, budget->Name(), 1); + m_shutdownMutex.unlock_shared(); + } + } - void CpuProfiler::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() && !ms_threadLocalStorage.empty()) - { - auto& ctx = ms_threadLocalStorage.back(); - TracyCZoneEnd(ctx); - ms_threadLocalStorage.pop_back(); + void CpuProfiler::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() && !ms_threadLocalStorage.empty()) + { + auto& ctx = ms_threadLocalStorage.back(); + TracyCZoneEnd(ctx); + ms_threadLocalStorage.pop_back(); - m_shutdownMutex.unlock_shared(); - } - } -} + m_shutdownMutex.unlock_shared(); + } + } +} // namespace TracyProfiler diff --git a/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.h b/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.h index 434157c9b..0d6bdd500 100644 --- a/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.h +++ b/Gems/ExternalProfilers/TracyProfiler/Code/Source/CpuProfiler.h @@ -20,31 +20,30 @@ namespace TracyProfiler { - class CpuProfiler final - : public AZ::Debug::Profiler - { - public: - AZ_RTTI(CpuProfiler, "{9467E3F6-0581-4E46-A98A-F3C249FD7B24}", AZ::Debug::Profiler); - AZ_CLASS_ALLOCATOR(CpuProfiler, AZ::SystemAllocator); - - CpuProfiler() = default; - ~CpuProfiler() = default; - - //! Registers/un-registers the AZ::Debug::Profiler instance to the interface - void Init(); - void Shutdown(); - - //! 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; - - private: - using EventIdStack = AZStd::vector; - static thread_local EventIdStack ms_threadLocalStorage; - - // This lock will only be contested when the CpuProfiler's Shutdown() method has been called - AZStd::shared_mutex m_shutdownMutex; - - bool m_initialized = false; - }; -} + class CpuProfiler final : public AZ::Debug::Profiler + { + public: + AZ_RTTI(CpuProfiler, "{9467E3F6-0581-4E46-A98A-F3C249FD7B24}", AZ::Debug::Profiler); + AZ_CLASS_ALLOCATOR(CpuProfiler, AZ::SystemAllocator); + + CpuProfiler() = default; + ~CpuProfiler() = default; + + //! Registers/un-registers the AZ::Debug::Profiler instance to the interface + void Init(); + void Shutdown(); + + //! 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; + + private: + using EventIdStack = AZStd::vector; + static thread_local EventIdStack ms_threadLocalStorage; + + // This lock will only be contested when the CpuProfiler's Shutdown() method has been called + AZStd::shared_mutex m_shutdownMutex; + + bool m_initialized = false; + }; +} // namespace TracyProfiler diff --git a/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerModule.cpp b/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerModule.cpp index 5437ec59a..01542b5bb 100644 --- a/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerModule.cpp +++ b/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerModule.cpp @@ -13,35 +13,36 @@ namespace TracyProfiler { - class ProfilerModule - : public AZ::Module - { - public: - AZ_RTTI(ProfilerModule, "{BCE29245-571B-44C2-94E7-84D7E06F5A1F}", AZ::Module); - AZ_CLASS_ALLOCATOR(ProfilerModule, AZ::SystemAllocator); + class ProfilerModule : public AZ::Module + { + public: + AZ_RTTI(ProfilerModule, "{BCE29245-571B-44C2-94E7-84D7E06F5A1F}", AZ::Module); + AZ_CLASS_ALLOCATOR(ProfilerModule, AZ::SystemAllocator); - ProfilerModule() - { - // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. - // Add ALL components descriptors associated with this gem to m_descriptors. - // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext. - // This happens through the [MyComponent]::Reflect() function. - m_descriptors.insert(m_descriptors.end(), { - ProfilerSystemComponent::CreateDescriptor(), - }); - } + ProfilerModule() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + // Add ALL components descriptors associated with this gem to m_descriptors. + // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and + // EditContext. This happens through the [MyComponent]::Reflect() function. + m_descriptors.insert( + m_descriptors.end(), + { + ProfilerSystemComponent::CreateDescriptor(), + }); + } - /** - * Add required SystemComponents to the SystemEntity. - */ - AZ::ComponentTypeList GetRequiredSystemComponents() const override - { - return AZ::ComponentTypeList{ - azrtti_typeid(), - }; - } - }; -} + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + azrtti_typeid(), + }; + } + }; +} // namespace TracyProfiler #if defined(O3DE_GEM_NAME) AZ_DECLARE_MODULE_CLASS(AZ_JOIN(Gem_, O3DE_GEM_NAME), TracyProfiler::ProfilerModule) diff --git a/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.cpp b/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.cpp index 8e09d5b76..2ed21634f 100644 --- a/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.cpp +++ b/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.cpp @@ -21,8 +21,7 @@ namespace TracyProfiler { if (AZ::SerializeContext* serialize = azrtti_cast(context)) { - serialize->Class() - ->Version(0); + serialize->Class()->Version(0); } } @@ -99,4 +98,4 @@ namespace TracyProfiler return false; } -} +} // namespace TracyProfiler diff --git a/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.h b/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.h index c43cfbad8..6c487df8d 100644 --- a/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.h +++ b/Gems/ExternalProfilers/TracyProfiler/Code/Source/ProfilerSystemComponent.h @@ -50,4 +50,4 @@ namespace TracyProfiler CpuProfiler m_cpuProfiler; }; -} +} // namespace TracyProfiler diff --git a/Gems/ExternalProfilers/TracyProfiler/gem.json b/Gems/ExternalProfilers/TracyProfiler/gem.json index dbed91c43..92e7cf9db 100644 --- a/Gems/ExternalProfilers/TracyProfiler/gem.json +++ b/Gems/ExternalProfilers/TracyProfiler/gem.json @@ -19,5 +19,6 @@ "requirements": "Users will need to download Tracy from the Github release.", "documentation_url": "https://github.com/wolfpld/tracy", "dependencies": [ - ] + ], + "provided_unique_service": "Profiler" } From e370d46018c8052a4c1d60b9533cc1c3918e5167 Mon Sep 17 00:00:00 2001 From: guillaume-haerinck Date: Fri, 28 Feb 2025 23:42:44 +0100 Subject: [PATCH 4/5] Move optick into 3rd party folder Signed-off-by: guillaume-haerinck --- .../OptickProfiler/Code/CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Gems/ExternalProfilers/OptickProfiler/Code/CMakeLists.txt b/Gems/ExternalProfilers/OptickProfiler/Code/CMakeLists.txt index 2a2033492..38dd89434 100644 --- a/Gems/ExternalProfilers/OptickProfiler/Code/CMakeLists.txt +++ b/Gems/ExternalProfilers/OptickProfiler/Code/CMakeLists.txt @@ -19,6 +19,16 @@ set(OPTICK_BUILD_GUI_APP OFF) set(OPTICK_INSTALL_TARGETS OFF) FetchContent_MakeAvailable(Optick) + +# Let's not clutter the root of any IDE folder structure with 3rd party dependencies +# Setting the FOLDER makes it show up there in the solution build in VS and similarly +# any other IDEs that organize in folders. +set_target_properties( + Optick + PROPERTIES + FOLDER "3rdParty Dependencies" +) + ly_add_target( NAME ${gem_name}.Static STATIC NAMESPACE Gem From 6581368682b7a9b61ade46813bd1244b77afb18b Mon Sep 17 00:00:00 2001 From: guillaume-haerinck Date: Fri, 28 Feb 2025 23:44:06 +0100 Subject: [PATCH 5/5] Move optick into 3rd party folder Signed-off-by: guillaume-haerinck --- Gems/ExternalProfilers/OptickProfiler/Code/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/ExternalProfilers/OptickProfiler/Code/CMakeLists.txt b/Gems/ExternalProfilers/OptickProfiler/Code/CMakeLists.txt index 38dd89434..d7f261671 100644 --- a/Gems/ExternalProfilers/OptickProfiler/Code/CMakeLists.txt +++ b/Gems/ExternalProfilers/OptickProfiler/Code/CMakeLists.txt @@ -24,7 +24,7 @@ FetchContent_MakeAvailable(Optick) # Setting the FOLDER makes it show up there in the solution build in VS and similarly # any other IDEs that organize in folders. set_target_properties( - Optick + OptickCore PROPERTIES FOLDER "3rdParty Dependencies" )