From 1f2eaface9a084a6fb183081443d1534c6f88b19 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Mon, 25 Oct 2021 11:45:12 -0700 Subject: [PATCH 01/16] [profiler_capture_api] merging overlapping profiler EBuses into AzCore Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- .../AzCore/AzCore/Debug/ProfilerBus.h | 34 +++++---- .../AzCore/Debug/ProfilerReflection.cpp | 70 +++++++++++++++++++ .../AzCore/AzCore/Debug/ProfilerReflection.h | 19 +++++ .../AzCore/Script/ScriptSystemComponent.cpp | 2 + .../AzCore/AzCore/azcore_files.cmake | 2 + .../Code/Include/Profiler/ProfilerBus.h | 59 ---------------- .../Profiler/Code/Source/ImGuiCpuProfiler.cpp | 47 +++++++------ Gems/Profiler/Code/Source/ImGuiCpuProfiler.h | 3 + .../Code/Source/ProfilerSystemComponent.cpp | 70 ++++--------------- .../Code/Source/ProfilerSystemComponent.h | 14 ++-- Gems/Profiler/Code/profiler_files.cmake | 1 - 11 files changed, 165 insertions(+), 156 deletions(-) create mode 100644 Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp create mode 100644 Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.h delete mode 100644 Gems/Profiler/Code/Include/Profiler/ProfilerBus.h diff --git a/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h b/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h index 1537be3b81..31eb0c9160 100644 --- a/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h +++ b/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h @@ -9,6 +9,8 @@ #pragma once #include +#include +#include namespace AZ { @@ -23,17 +25,13 @@ namespace AZ public: virtual ~ProfilerNotifications() = default; - virtual void OnProfileSystemInitialized() = 0; + //! Notify when the current profiler capture is finished + //! @param result Set to true if it's finished successfully + //! @param info The output file path or error information which depends on the return. + virtual void OnCaptureFinished(bool result, const AZStd::string& info) = 0; }; using ProfilerNotificationBus = AZ::EBus; - enum class ProfileFrameAdvanceType - { - Game, - Render, - Default = Game - }; - /** * ProfilerRequests provides an interface for making profiling system requests */ @@ -41,14 +39,26 @@ namespace AZ : public AZ::EBusTraits { public: + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + // Allow multiple threads to concurrently make requests using MutexType = AZStd::mutex; virtual ~ProfilerRequests() = default; - virtual bool IsActive() = 0; - virtual void FrameAdvance(ProfileFrameAdvanceType type) = 0; + //! Getter/setter for the profiler active state + virtual bool IsActive() const = 0; + virtual void SetActive(bool active) = 0; + + //! Capture a single frame of profiling data + virtual bool CaptureFrame(const AZStd::string& outputFilePath) = 0; + + //! Starting/ending a multi-frame capture of profiling data + virtual bool StartCapture(const AZStd::string& outputFilePath) = 0; + virtual bool EndCapture() = 0; }; using ProfilerRequestBus = AZ::EBus; - } -} + } // namespace Debug +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp new file mode 100644 index 0000000000..72ad4b735b --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp @@ -0,0 +1,70 @@ +/* + * 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 AZ::Debug +{ + class ProfilerNotificationBusHandler final + : public ProfilerNotificationBus::Handler + , public AZ::BehaviorEBusHandler + { + public: + AZ_EBUS_BEHAVIOR_BINDER(ProfilerNotificationBusHandler, "{44161459-B816-4876-95A4-BA16DEC767D6}", AZ::SystemAllocator, + OnCaptureFinished + ); + + void OnCaptureFinished(bool result, const AZStd::string& info) override + { + Call(FN_OnCaptureFinished, result, info); + } + + static void Reflect(AZ::ReflectContext* context) + { + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("ProfilerNotificationBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) + ->Attribute(AZ::Script::Attributes::Module, "debug") + ->Handler(); + } + } + }; + + void ProfilerReflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + if (AZ::EditContext* ec = serialize->GetEditContext()) + { + ProfilerNotificationBusHandler::Reflect(context); + } + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->EBus("ProfilerRequestBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) + ->Attribute(AZ::Script::Attributes::Module, "debug") + + ->Event("IsActive", &ProfilerRequestBus::Events::IsActive) + ->Event("SetActive", &ProfilerRequestBus::Events::SetActive) + + ->Event("CaptureFrame", &ProfilerRequestBus::Events::CaptureFrame) + + ->Event("StartCapture", &ProfilerRequestBus::Events::StartCapture) + ->Event("EndCapture", &ProfilerRequestBus::Events::EndCapture); + + ProfilerNotificationBusHandler::Reflect(context); + } + } +} // namespace AZ::Debug diff --git a/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.h b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.h new file mode 100644 index 0000000000..d6857defe5 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.h @@ -0,0 +1,19 @@ +/* + * 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 + +namespace AZ +{ + class ReflectContext; + + namespace Debug + { + //! Reflects the profiler bus script bindings + void ProfilerReflect(AZ::ReflectContext* context); + } // namespace Debug +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp index a83232c8cb..3fa20cb682 100644 --- a/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp +++ b/Code/Framework/AzCore/AzCore/Script/ScriptSystemComponent.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -925,6 +926,7 @@ void ScriptSystemComponent::Reflect(ReflectContext* reflection) // reflect default entity MathReflect(behaviorContext); ScriptDebug::Reflect(behaviorContext); + Debug::ProfilerReflect(behaviorContext); Debug::TraceReflect(behaviorContext); behaviorContext->Class("Platform") diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 41229429f2..bb824a6436 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -106,6 +106,8 @@ set(FILES Debug/Profiler.inl Debug/Profiler.h Debug/ProfilerBus.h + Debug/ProfilerReflection.cpp + Debug/ProfilerReflection.h Debug/StackTracer.h Debug/EventTrace.h Debug/EventTrace.cpp diff --git a/Gems/Profiler/Code/Include/Profiler/ProfilerBus.h b/Gems/Profiler/Code/Include/Profiler/ProfilerBus.h deleted file mode 100644 index 22352185d3..0000000000 --- a/Gems/Profiler/Code/Include/Profiler/ProfilerBus.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ -#pragma once - -#include -#include -#include - -namespace Profiler -{ - class ProfilerRequests - { - public: - AZ_RTTI(ProfilerRequests, "{3757c4e5-1941-457c-85ae-16305e17a4c6}"); - virtual ~ProfilerRequests() = default; - - //! Enable/Disable the CpuProfiler - virtual void SetProfilerEnabled(bool enabled) = 0; - - //! Dump a single frame of Cpu profiling data - virtual bool CaptureCpuProfilingStatistics(const AZStd::string& outputFilePath) = 0; - - //! Start a multiframe capture of CPU profiling data. - virtual bool BeginContinuousCpuProfilingCapture() = 0; - - //! End and dump an in-progress continuous capture. - virtual bool EndContinuousCpuProfilingCapture(const AZStd::string& outputFilePath) = 0; - }; - - class ProfilerBusTraits - : public AZ::EBusTraits - { - public: - // EBusTraits overrides - static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; - }; - - class ProfilerNotifications - : public AZ::EBusTraits - { - public: - virtual ~ProfilerNotifications() = default; - - //! Notify when the current CpuProfilingStatistics capture is finished - //! @param result Set to true if it's finished successfully - //! @param info The output file path or error information which depends on the return. - virtual void OnCaptureCpuProfilingStatisticsFinished(bool result, const AZStd::string& info) = 0; - }; - - using ProfilerInterface = AZ::Interface; - using ProfilerRequestBus = AZ::EBus; - using ProfilerNotificationBus = AZ::EBus; -} // namespace Profiler diff --git a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp index 3f364f99e0..0320020e86 100644 --- a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp +++ b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp @@ -10,9 +10,9 @@ #include -#include #include +#include #include #include #include @@ -156,16 +156,10 @@ namespace Profiler if (m_captureToFile) { - AZStd::string timeString; - AZStd::to_string(timeString, AZStd::GetTimeNowSecond()); - - const AZStd::string frameDataFilePath = AZStd::string::format("%s/cpu_single_%s.json", defaultSaveLocation, timeString.c_str()); - - char resolvedPath[AZ::IO::MaxPathLength]; - AZ::IO::FileIOBase::GetInstance()->ResolvePath(frameDataFilePath.c_str(), resolvedPath, AZ::IO::MaxPathLength); - m_lastCapturedFilePath = resolvedPath; - - ProfilerRequestBus::Broadcast(&ProfilerRequestBus::Events::CaptureCpuProfilingStatistics, frameDataFilePath); + AZ::Debug::ProfilerRequestBus::Broadcast( + &AZ::Debug::ProfilerRequestBus::Events::CaptureFrame, + GenerateOutputFile("single") + ); } m_captureToFile = false; @@ -208,22 +202,15 @@ namespace Profiler { if (isInProgress) { - AZStd::string timeString; - AZStd::to_string(timeString, AZStd::GetTimeNowSecond()); - - const AZStd::string frameDataFilePath = AZStd::string::format("%s/cpu_multi_%s.json", defaultSaveLocation, timeString.c_str()); - - char resolvedPath[AZ::IO::MaxPathLength]; - AZ::IO::FileIOBase::GetInstance()->ResolvePath(frameDataFilePath.c_str(), resolvedPath, AZ::IO::MaxPathLength); - m_lastCapturedFilePath = resolvedPath; - - ProfilerRequestBus::Broadcast(&ProfilerRequestBus::Events::EndContinuousCpuProfilingCapture, frameDataFilePath); - + AZ::Debug::ProfilerRequestBus::Broadcast(&AZ::Debug::ProfilerRequestBus::Events::EndCapture); m_paused = true; } else { - ProfilerRequestBus::Broadcast(&ProfilerRequestBus::Events::BeginContinuousCpuProfilingCapture); + AZ::Debug::ProfilerRequestBus::Broadcast( + &AZ::Debug::ProfilerRequestBus::Events::StartCapture, + GenerateOutputFile("multi") + ); } } @@ -418,6 +405,20 @@ namespace Profiler ImGui::End(); } + AZStd::string ImGuiCpuProfiler::GenerateOutputFile(const char* nameHint) + { + AZStd::string timeString; + AZStd::to_string(timeString, AZStd::GetTimeNowSecond()); + + const AZStd::string frameDataFilePath = AZStd::string::format("%s/cpu_%s_%s.json", defaultSaveLocation, nameHint, timeString.c_str()); + + char resolvedPath[AZ::IO::MaxPathLength]; + AZ::IO::FileIOBase::GetInstance()->ResolvePath(frameDataFilePath.c_str(), resolvedPath, AZ::IO::MaxPathLength); + m_lastCapturedFilePath = resolvedPath; + + return frameDataFilePath; + } + void ImGuiCpuProfiler::LoadFile() { const AZ::IO::Path& pathToLoad = m_cachedCapturePaths[m_currentFileIndex]; diff --git a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.h b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.h index 2c6a3e470a..f074a9974f 100644 --- a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.h +++ b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.h @@ -107,6 +107,9 @@ namespace Profiler //! Draws the statistical view of the CPU profiling data. void DrawStatisticsView(); + //! Generates the full output timestamped file path based on nameHint + AZStd::string GenerateOutputFile(const char* nameHint); + //! Callback invoked when the "Load File" button is pressed in the file picker. void LoadFile(); diff --git a/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp b/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp index bc51ffd0a7..f37aa702c1 100644 --- a/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp +++ b/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp @@ -51,32 +51,6 @@ namespace Profiler int m_framesLeft{ 0 }; }; - class ProfilerNotificationBusHandler final - : public ProfilerNotificationBus::Handler - , public AZ::BehaviorEBusHandler - { - public: - AZ_EBUS_BEHAVIOR_BINDER(ProfilerNotificationBusHandler, "{44161459-B816-4876-95A4-BA16DEC767D6}", AZ::SystemAllocator, - OnCaptureCpuProfilingStatisticsFinished - ); - - void OnCaptureCpuProfilingStatisticsFinished(bool result, const AZStd::string& info) override - { - Call(FN_OnCaptureCpuProfilingStatisticsFinished, result, info); - } - - static void Reflect(AZ::ReflectContext* context) - { - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->EBus("ProfilerNotificationBus") - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) - ->Attribute(AZ::Script::Attributes::Module, "profiler") - ->Handler(); - } - } - }; - bool SerializeCpuProfilingData(const AZStd::ring_buffer& data, AZStd::string outputFilePath, bool wasEnabled) { AZ_TracePrintf("ProfilerSystemComponent", "Beginning serialization of %zu frames of profiling data\n", data.size()); @@ -107,8 +81,8 @@ namespace Profiler CpuProfiler::Get()->SetProfilerEnabled(false); } - // Notify listeners that the pass' PipelineStatistics queries capture has finished. - ProfilerNotificationBus::Broadcast(&ProfilerNotificationBus::Events::OnCaptureCpuProfilingStatisticsFinished, + // Notify listeners that the profiler capture has finished. + AZ::Debug::ProfilerNotificationBus::Broadcast(&AZ::Debug::ProfilerNotificationBus::Events::OnCaptureFinished, saveResult.IsSuccess(), captureInfo); @@ -128,21 +102,9 @@ namespace Profiler ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System")) ->Attribute(AZ::Edit::Attributes::AutoExpand, true); - - ProfilerNotificationBusHandler::Reflect(context); } } - if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) - { - behaviorContext->EBus("ProfilerRequestBus") - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) - ->Attribute(AZ::Script::Attributes::Module, "profiler") - ->Event("CaptureCpuProfilingStatistics", &ProfilerRequestBus::Events::CaptureCpuProfilingStatistics); - - ProfilerNotificationBusHandler::Reflect(context); - } - CpuProfilingStatisticsSerializer::Reflect(context); } @@ -166,23 +128,15 @@ namespace Profiler ProfilerSystemComponent::ProfilerSystemComponent() { - if (ProfilerInterface::Get() == nullptr) - { - ProfilerInterface::Register(this); - } } ProfilerSystemComponent::~ProfilerSystemComponent() { - if (ProfilerInterface::Get() == this) - { - ProfilerInterface::Unregister(this); - } } void ProfilerSystemComponent::Activate() { - ProfilerRequestBus::Handler::BusConnect(); + AZ::Debug::ProfilerRequestBus::Handler::BusConnect(); m_cpuProfiler.Init(); } @@ -191,7 +145,7 @@ namespace Profiler { m_cpuProfiler.Shutdown(); - ProfilerRequestBus::Handler::BusDisconnect(); + AZ::Debug::ProfilerRequestBus::Handler::BusDisconnect(); // Block deactivation until the IO thread has finished serializing the CPU data if (m_cpuDataSerializationThread.joinable()) @@ -200,12 +154,17 @@ namespace Profiler } } - void ProfilerSystemComponent::SetProfilerEnabled(bool enabled) + bool ProfilerSystemComponent::IsActive() const + { + return m_cpuProfiler.IsProfilerEnabled(); + } + + void ProfilerSystemComponent::SetActive(bool enabled) { m_cpuProfiler.SetProfilerEnabled(enabled); } - bool ProfilerSystemComponent::CaptureCpuProfilingStatistics(const AZStd::string& outputFilePath) + bool ProfilerSystemComponent::CaptureFrame(const AZStd::string& outputFilePath) { bool expected = false; if (!m_cpuCaptureInProgress.compare_exchange_strong(expected, true)) @@ -236,12 +195,13 @@ namespace Profiler return true; } - bool ProfilerSystemComponent::BeginContinuousCpuProfilingCapture() + bool ProfilerSystemComponent::StartCapture(const AZStd::string& outputFilePath) { + m_captureFile = outputFilePath; return m_cpuProfiler.BeginContinuousCapture(); } - bool ProfilerSystemComponent::EndContinuousCpuProfilingCapture(const AZStd::string& outputFilePath) + bool ProfilerSystemComponent::EndCapture() { bool expected = false; if (!m_cpuDataSerializationInProgress.compare_exchange_strong(expected, true)) @@ -263,7 +223,7 @@ namespace Profiler // cpuProfilingData could be 1GB+ once saved, so use an IO thread to write it to disk. auto threadIoFunction = - [data = AZStd::move(captureResult), filePath = AZStd::string(outputFilePath), &flag = m_cpuDataSerializationInProgress]() + [data = AZStd::move(captureResult), filePath = m_captureFile, &flag = m_cpuDataSerializationInProgress]() { SerializeCpuProfilingData(data, filePath, true); flag.store(false); diff --git a/Gems/Profiler/Code/Source/ProfilerSystemComponent.h b/Gems/Profiler/Code/Source/ProfilerSystemComponent.h index 76121be04f..534e7d8386 100644 --- a/Gems/Profiler/Code/Source/ProfilerSystemComponent.h +++ b/Gems/Profiler/Code/Source/ProfilerSystemComponent.h @@ -8,17 +8,17 @@ #pragma once -#include #include #include +#include #include namespace Profiler { class ProfilerSystemComponent : public AZ::Component - , protected ProfilerRequestBus::Handler + , protected AZ::Debug::ProfilerRequestBus::Handler { public: AZ_COMPONENT(ProfilerSystemComponent, "{3f52c1d7-d920-4781-8ed7-88077ec4f305}"); @@ -39,10 +39,11 @@ namespace Profiler void Deactivate() override; // ProfilerRequestBus interface implementation - void SetProfilerEnabled(bool enabled) override; - bool CaptureCpuProfilingStatistics(const AZStd::string& outputFilePath) override; - bool BeginContinuousCpuProfilingCapture() override; - bool EndContinuousCpuProfilingCapture(const AZStd::string& outputFilePath) override; + bool IsActive() const override; + void SetActive(bool active) override; + bool CaptureFrame(const AZStd::string& outputFilePath) override; + bool StartCapture(const AZStd::string& outputFilePath) override; + bool EndCapture() override; AZStd::thread m_cpuDataSerializationThread; @@ -51,6 +52,7 @@ namespace Profiler AZStd::atomic_bool m_cpuCaptureInProgress{ false }; CpuProfilerImpl m_cpuProfiler; + AZStd::string m_captureFile; }; } // namespace Profiler diff --git a/Gems/Profiler/Code/profiler_files.cmake b/Gems/Profiler/Code/profiler_files.cmake index 51ceb00139..ebbca1cd78 100644 --- a/Gems/Profiler/Code/profiler_files.cmake +++ b/Gems/Profiler/Code/profiler_files.cmake @@ -7,7 +7,6 @@ # set(FILES - Include/Profiler/ProfilerBus.h Include/Profiler/ProfilerImGuiBus.h Source/CpuProfiler.h Source/CpuProfilerImpl.cpp From 6ddfb6500f0467a9887168d2b45382f8c0fda178 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Mon, 25 Oct 2021 11:45:58 -0700 Subject: [PATCH 02/16] [profiler_capture_api] fixed ambiguous LogLevel type in some unity file scenarios Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/Debug/Trace.cpp | 25 ++-- .../WinAPI/AzCore/Debug/Trace_WinAPI.cpp | 139 +++++++++--------- 2 files changed, 78 insertions(+), 86 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Debug/Trace.cpp b/Code/Framework/AzCore/AzCore/Debug/Trace.cpp index 357149d096..1dd9d0f0f9 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Trace.cpp +++ b/Code/Framework/AzCore/AzCore/Debug/Trace.cpp @@ -27,26 +27,21 @@ #include #include -namespace AZ +namespace AZ::Debug { - namespace Debug - { - struct StackFrame; + struct StackFrame; - namespace Platform - { + namespace Platform + { #if defined(AZ_ENABLE_DEBUG_TOOLS) - bool AttachDebugger(); - bool IsDebuggerPresent(); - void HandleExceptions(bool isEnabled); - void DebugBreak(); + bool AttachDebugger(); + bool IsDebuggerPresent(); + void HandleExceptions(bool isEnabled); + void DebugBreak(); #endif - void Terminate(int exitCode); - } + void Terminate(int exitCode); } - using namespace AZ::Debug; - namespace DebugInternal { // other threads can trigger fatals and errors, but the same thread should not, to avoid stack overflow. @@ -616,4 +611,4 @@ namespace AZ val.Set(level); } } -} // namspace AZ +} // namspace AZ::Debug diff --git a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Debug/Trace_WinAPI.cpp b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Debug/Trace_WinAPI.cpp index 28d3459ba3..9b78da222c 100644 --- a/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Debug/Trace_WinAPI.cpp +++ b/Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Debug/Trace_WinAPI.cpp @@ -17,7 +17,7 @@ #include -namespace AZ +namespace AZ::Debug { #if defined(AZ_ENABLE_DEBUG_TOOLS) LONG WINAPI ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo); @@ -26,94 +26,91 @@ namespace AZ constexpr int g_maxMessageLength = 4096; - namespace Debug + namespace Platform { - namespace Platform - { #if defined(AZ_ENABLE_DEBUG_TOOLS) - bool IsDebuggerPresent() + bool IsDebuggerPresent() + { + return ::IsDebuggerPresent() ? true : false; + } + + void HandleExceptions(bool isEnabled) + { + if (isEnabled) { - return ::IsDebuggerPresent() ? true : false; + g_previousExceptionHandler = ::SetUnhandledExceptionFilter(&ExceptionHandler); } - - void HandleExceptions(bool isEnabled) + else { - if (isEnabled) - { - g_previousExceptionHandler = ::SetUnhandledExceptionFilter(&ExceptionHandler); - } - else - { - ::SetUnhandledExceptionFilter(g_previousExceptionHandler); - g_previousExceptionHandler = NULL; - } + ::SetUnhandledExceptionFilter(g_previousExceptionHandler); + g_previousExceptionHandler = NULL; } + } - bool AttachDebugger() + bool AttachDebugger() + { + if (IsDebuggerPresent()) { - if (IsDebuggerPresent()) - { - return true; - } - - // Launch vsjitdebugger.exe, this app is always present in System32 folder - // with an installation of any version of visual studio. - // It will open a debugging dialog asking the user what debugger to use - - STARTUPINFOW startupInfo = {0}; - startupInfo.cb = sizeof(startupInfo); - PROCESS_INFORMATION processInfo = {0}; - - wchar_t cmdline[MAX_PATH]; - swprintf_s(cmdline, L"vsjitdebugger.exe -p %li", ::GetCurrentProcessId()); - bool success = ::CreateProcessW( - NULL, // No module name (use command line) - cmdline, // Command line - NULL, // Process handle not inheritable - NULL, // Thread handle not inheritable - FALSE, // No handle inheritance - 0, // No creation flags - NULL, // Use parent's environment block - NULL, // Use parent's starting directory - &startupInfo, // Pointer to STARTUPINFO structure - &processInfo); // Pointer to PROCESS_INFORMATION structure - - if (success) - { - ::WaitForSingleObject(processInfo.hProcess, INFINITE); - ::CloseHandle(processInfo.hProcess); - ::CloseHandle(processInfo.hThread); - return true; - } - return false; + return true; } - void DebugBreak() + // Launch vsjitdebugger.exe, this app is always present in System32 folder + // with an installation of any version of visual studio. + // It will open a debugging dialog asking the user what debugger to use + + STARTUPINFOW startupInfo = {0}; + startupInfo.cb = sizeof(startupInfo); + PROCESS_INFORMATION processInfo = {0}; + + wchar_t cmdline[MAX_PATH]; + swprintf_s(cmdline, L"vsjitdebugger.exe -p %li", ::GetCurrentProcessId()); + bool success = ::CreateProcessW( + NULL, // No module name (use command line) + cmdline, // Command line + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + FALSE, // No handle inheritance + 0, // No creation flags + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &startupInfo, // Pointer to STARTUPINFO structure + &processInfo); // Pointer to PROCESS_INFORMATION structure + + if (success) { - __debugbreak(); + ::WaitForSingleObject(processInfo.hProcess, INFINITE); + ::CloseHandle(processInfo.hProcess); + ::CloseHandle(processInfo.hThread); + return true; } + return false; + } + + void DebugBreak() + { + __debugbreak(); + } #endif // AZ_ENABLE_DEBUG_TOOLS - void Terminate(int exitCode) - { - TerminateProcess(GetCurrentProcess(), exitCode); - } + void Terminate(int exitCode) + { + TerminateProcess(GetCurrentProcess(), exitCode); + } - void OutputToDebugger([[maybe_unused]] const char* window, const char* message) + void OutputToDebugger([[maybe_unused]] const char* window, const char* message) + { + AZStd::fixed_wstring tmpW; + if(window) { - AZStd::fixed_wstring tmpW; - if(window) - { - AZStd::to_wstring(tmpW, window); - tmpW += L": "; - OutputDebugStringW(tmpW.c_str()); - tmpW.clear(); - } - AZStd::to_wstring(tmpW, message); + AZStd::to_wstring(tmpW, window); + tmpW += L": "; OutputDebugStringW(tmpW.c_str()); + tmpW.clear(); } + AZStd::to_wstring(tmpW, message); + OutputDebugStringW(tmpW.c_str()); } - } + } // namespace Platform #if defined(AZ_ENABLE_DEBUG_TOOLS) @@ -211,4 +208,4 @@ namespace AZ } #endif -} +} // namspace AZ::Debug From 471436b0fc731f185a4cc2f2b359f152bc432e6e Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Mon, 25 Oct 2021 11:48:00 -0700 Subject: [PATCH 03/16] [profiler_capture_api] added cvar/console access for profiler capture Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- .../AzCore/AzCore/Debug/Profiler.cpp | 40 +++++++++++++++++++ .../Profiler/Code/Source/ImGuiCpuProfiler.cpp | 16 ++++++-- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp b/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp index 0bb92b923d..4abda46214 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp +++ b/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp @@ -7,4 +7,44 @@ */ #include +#include +#include +#include +namespace AZ::Debug +{ + AZ_CVAR(AZ::CVarFixedString, bg_profilerCaptureLocation, "@user@/Profiler", nullptr, ConsoleFunctorFlags::Null, + "Specify where to save profiler capture data"); + + AZStd::string GenerateOutputFile(const char* nameHint) + { + AZStd::string timeString; + AZStd::to_string(timeString, AZStd::GetTimeNowSecond()); + + AZ::CVarFixedString captureOutput = static_cast(bg_profilerCaptureLocation); + + return AZStd::string::format("%s/capture_%s_%s.json", captureOutput.c_str(), nameHint, timeString.c_str()); + } + + void ProfilerCaptureFrame([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) + { + AZStd::string captureFile = GenerateOutputFile("single"); + AZLOG_INFO("Setting capture file to %s", captureFile.c_str()); + AZ::Debug::ProfilerRequestBus::Broadcast(&AZ::Debug::ProfilerRequestBus::Events::CaptureFrame, captureFile); + } + AZ_CONSOLEFREEFUNC(ProfilerCaptureFrame, AZ::ConsoleFunctorFlags::DontReplicate, "Capture a single frame of profiling data"); + + void ProfilerStartCapture([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) + { + AZStd::string captureFile = GenerateOutputFile("multi"); + AZLOG_INFO("Setting capture file to %s", captureFile.c_str()); + ProfilerRequestBus::Broadcast(&ProfilerRequestBus::Events::StartCapture, captureFile); + } + AZ_CONSOLEFREEFUNC(ProfilerStartCapture, AZ::ConsoleFunctorFlags::DontReplicate, "Start a multi-frame capture of profiling data"); + + void ProfilerEndCapture([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) + { + AZ::Debug::ProfilerRequestBus::Broadcast(&AZ::Debug::ProfilerRequestBus::Events::EndCapture); + } + AZ_CONSOLEFREEFUNC(ProfilerEndCapture, AZ::ConsoleFunctorFlags::DontReplicate, "End and dump an in-progress continuous capture"); +} // namespace AZ::Debug diff --git a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp index 0320020e86..52fe09f9e8 100644 --- a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp +++ b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp @@ -12,6 +12,7 @@ #include +#include #include #include #include @@ -24,10 +25,13 @@ #include #include -namespace Profiler +namespace AZ::Debug { - static constexpr const char* defaultSaveLocation = "@user@/Profiler"; + AZ_CVAR_EXTERNED(AZ::CVarFixedString, bg_profilerCaptureLocation); +} +namespace Profiler +{ namespace CpuProfilerImGuiHelper { float TicksToMs(double ticks) @@ -222,8 +226,10 @@ namespace Profiler // Only update the cached file list when opened so that we aren't making IO calls on every frame. m_cachedCapturePaths.clear(); + AZ::CVarFixedString captureOutput = static_cast(AZ::Debug::bg_profilerCaptureLocation); + auto* base = AZ::IO::FileIOBase::GetInstance(); - base->FindFiles(defaultSaveLocation, "*.json", + base->FindFiles(captureOutput.c_str(), "*.json", [&paths = m_cachedCapturePaths](const char* path) -> bool { auto foundPath = AZ::IO::Path(path); @@ -410,7 +416,9 @@ namespace Profiler AZStd::string timeString; AZStd::to_string(timeString, AZStd::GetTimeNowSecond()); - const AZStd::string frameDataFilePath = AZStd::string::format("%s/cpu_%s_%s.json", defaultSaveLocation, nameHint, timeString.c_str()); + AZ::CVarFixedString captureOutput = static_cast(AZ::Debug::bg_profilerCaptureLocation); + + const AZStd::string frameDataFilePath = AZStd::string::format("%s/cpu_%s_%s.json", captureOutput.c_str(), nameHint, timeString.c_str()); char resolvedPath[AZ::IO::MaxPathLength]; AZ::IO::FileIOBase::GetInstance()->ResolvePath(frameDataFilePath.c_str(), resolvedPath, AZ::IO::MaxPathLength); From c6d3e7900cdb4971d8927fcf182740c744da870b Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Mon, 25 Oct 2021 15:51:12 -0700 Subject: [PATCH 04/16] [profiler_capture_api] updated ProfilerRequests::StartCapture signature as per PR feedback Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h | 2 +- Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp | 4 ++-- Gems/Profiler/Code/Source/ProfilerSystemComponent.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h b/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h index 31eb0c9160..89c59f01b5 100644 --- a/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h +++ b/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h @@ -56,7 +56,7 @@ namespace AZ virtual bool CaptureFrame(const AZStd::string& outputFilePath) = 0; //! Starting/ending a multi-frame capture of profiling data - virtual bool StartCapture(const AZStd::string& outputFilePath) = 0; + virtual bool StartCapture(AZStd::string outputFilePath) = 0; virtual bool EndCapture() = 0; }; using ProfilerRequestBus = AZ::EBus; diff --git a/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp b/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp index f37aa702c1..09df0970b8 100644 --- a/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp +++ b/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp @@ -195,9 +195,9 @@ namespace Profiler return true; } - bool ProfilerSystemComponent::StartCapture(const AZStd::string& outputFilePath) + bool ProfilerSystemComponent::StartCapture(AZStd::string outputFilePath) { - m_captureFile = outputFilePath; + m_captureFile = AZStd::move(outputFilePath); return m_cpuProfiler.BeginContinuousCapture(); } diff --git a/Gems/Profiler/Code/Source/ProfilerSystemComponent.h b/Gems/Profiler/Code/Source/ProfilerSystemComponent.h index 534e7d8386..000b11cfdf 100644 --- a/Gems/Profiler/Code/Source/ProfilerSystemComponent.h +++ b/Gems/Profiler/Code/Source/ProfilerSystemComponent.h @@ -42,7 +42,7 @@ namespace Profiler bool IsActive() const override; void SetActive(bool active) override; bool CaptureFrame(const AZStd::string& outputFilePath) override; - bool StartCapture(const AZStd::string& outputFilePath) override; + bool StartCapture(AZStd::string outputFilePath) override; bool EndCapture() override; From e38a44f0614925c8cc0830a156495479ead412a8 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Mon, 25 Oct 2021 18:18:12 -0700 Subject: [PATCH 05/16] [profiler_capture_api] minor memory usage rework pointed out in PR feedback Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp | 9 ++++----- Gems/Profiler/Code/Source/ImGuiCpuProfiler.h | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp index 52fe09f9e8..88b200c733 100644 --- a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp +++ b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp @@ -418,13 +418,12 @@ namespace Profiler AZ::CVarFixedString captureOutput = static_cast(AZ::Debug::bg_profilerCaptureLocation); - const AZStd::string frameDataFilePath = AZStd::string::format("%s/cpu_%s_%s.json", captureOutput.c_str(), nameHint, timeString.c_str()); + const AZ::IO::FixedMaxPathString frameDataFilePath = + AZ::IO::FixedMaxPathString::format("%s/cpu_%s_%s.json", captureOutput.c_str(), nameHint, timeString.c_str()); - char resolvedPath[AZ::IO::MaxPathLength]; - AZ::IO::FileIOBase::GetInstance()->ResolvePath(frameDataFilePath.c_str(), resolvedPath, AZ::IO::MaxPathLength); - m_lastCapturedFilePath = resolvedPath; + AZ::IO::FileIOBase::GetInstance()->ResolvePath(m_lastCapturedFilePath, frameDataFilePath.c_str()); - return frameDataFilePath; + return m_lastCapturedFilePath.String(); } void ImGuiCpuProfiler::LoadFile() diff --git a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.h b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.h index f074a9974f..2e01b8fd6b 100644 --- a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.h +++ b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.h @@ -217,7 +217,7 @@ namespace Profiler AZStd::vector m_cpuTimingStatisticsWhenPause; AZStd::sys_time_t m_frameToFrameTime{}; - AZStd::string m_lastCapturedFilePath; + AZ::IO::FixedMaxPath m_lastCapturedFilePath; bool m_showFilePicker = false; From 57a69978b968dd40cc0062fabaf8141bfa9c6f2b Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Tue, 26 Oct 2021 12:15:30 -0700 Subject: [PATCH 06/16] [profiler_capture_api] replace profiler capture location cvar with setting registry entry Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- .../AzCore/AzCore/Debug/Profiler.cpp | 22 +++++++++++++++---- .../AzCore/AzCore/Debug/ProfilerBus.h | 11 ++++++++++ .../Profiler/Code/Source/ImGuiCpuProfiler.cpp | 10 ++------- Registry/profiler.setreg | 15 +++++++++++++ 4 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 Registry/profiler.setreg diff --git a/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp b/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp index 4abda46214..3b5033a07f 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp +++ b/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp @@ -10,18 +10,16 @@ #include #include #include +#include namespace AZ::Debug { - AZ_CVAR(AZ::CVarFixedString, bg_profilerCaptureLocation, "@user@/Profiler", nullptr, ConsoleFunctorFlags::Null, - "Specify where to save profiler capture data"); - AZStd::string GenerateOutputFile(const char* nameHint) { AZStd::string timeString; AZStd::to_string(timeString, AZStd::GetTimeNowSecond()); - AZ::CVarFixedString captureOutput = static_cast(bg_profilerCaptureLocation); + AZ::IO::FixedMaxPathString captureOutput = GetProfilerCaptureLocation(); return AZStd::string::format("%s/capture_%s_%s.json", captureOutput.c_str(), nameHint, timeString.c_str()); } @@ -47,4 +45,20 @@ namespace AZ::Debug AZ::Debug::ProfilerRequestBus::Broadcast(&AZ::Debug::ProfilerRequestBus::Events::EndCapture); } AZ_CONSOLEFREEFUNC(ProfilerEndCapture, AZ::ConsoleFunctorFlags::DontReplicate, "End and dump an in-progress continuous capture"); + + AZ::IO::FixedMaxPathString GetProfilerCaptureLocation() + { + AZ::IO::FixedMaxPathString captureOutput; + if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry) + { + settingsRegistry->Get(captureOutput, RegistryKey_ProfilerCaptureLocation); + } + + if (captureOutput.empty()) + { + captureOutput = ProfilerCaptureLocationFallback; + } + + return captureOutput; + } } // namespace AZ::Debug diff --git a/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h b/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h index 89c59f01b5..03a89b11f2 100644 --- a/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h +++ b/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h @@ -10,12 +10,19 @@ #include #include +#include #include namespace AZ { namespace Debug { + //! settings registry entry for specifying where to output profiler captures + static constexpr const char* RegistryKey_ProfilerCaptureLocation = "/O3DE/AzCore/Debug/Profiler/CaptureLocation"; + + //! fallback value in the event the settings registry isn't ready or doesn't contain the key + static constexpr const char* ProfilerCaptureLocationFallback = "@user@/Profiler"; + /** * ProfilerNotifications provides a profiler event interface that can be used to update listeners on profiler status */ @@ -60,5 +67,9 @@ namespace AZ virtual bool EndCapture() = 0; }; using ProfilerRequestBus = AZ::EBus; + + //! helper function for getting the profiler capture location from the settings registry that + //! includes fallback handing in the event the registry value can't be determined + AZ::IO::FixedMaxPathString GetProfilerCaptureLocation(); } // namespace Debug } // namespace AZ diff --git a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp index 88b200c733..fd19460153 100644 --- a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp +++ b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp @@ -12,7 +12,6 @@ #include -#include #include #include #include @@ -25,11 +24,6 @@ #include #include -namespace AZ::Debug -{ - AZ_CVAR_EXTERNED(AZ::CVarFixedString, bg_profilerCaptureLocation); -} - namespace Profiler { namespace CpuProfilerImGuiHelper @@ -226,7 +220,7 @@ namespace Profiler // Only update the cached file list when opened so that we aren't making IO calls on every frame. m_cachedCapturePaths.clear(); - AZ::CVarFixedString captureOutput = static_cast(AZ::Debug::bg_profilerCaptureLocation); + AZ::IO::FixedMaxPathString captureOutput = AZ::Debug::GetProfilerCaptureLocation(); auto* base = AZ::IO::FileIOBase::GetInstance(); base->FindFiles(captureOutput.c_str(), "*.json", @@ -416,7 +410,7 @@ namespace Profiler AZStd::string timeString; AZStd::to_string(timeString, AZStd::GetTimeNowSecond()); - AZ::CVarFixedString captureOutput = static_cast(AZ::Debug::bg_profilerCaptureLocation); + AZ::IO::FixedMaxPathString captureOutput = AZ::Debug::GetProfilerCaptureLocation(); const AZ::IO::FixedMaxPathString frameDataFilePath = AZ::IO::FixedMaxPathString::format("%s/cpu_%s_%s.json", captureOutput.c_str(), nameHint, timeString.c_str()); diff --git a/Registry/profiler.setreg b/Registry/profiler.setreg new file mode 100644 index 0000000000..a295b007e1 --- /dev/null +++ b/Registry/profiler.setreg @@ -0,0 +1,15 @@ +{ + "O3DE": + { + "AzCore": + { + "Debug": + { + "Profiler": + { + "CaptureLocation" : "@user@/Profiler" + } + } + } + } +} \ No newline at end of file From a2c42ab07281e1b7ef56496da12c70f86a59c782 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Tue, 26 Oct 2021 15:15:52 -0700 Subject: [PATCH 07/16] [profiler_capture_api] started migration of ProfilerRequests EBus to AZ::Interface Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- .../AzCore/AzCore/Debug/Profiler.cpp | 23 +++++++++++------ .../AzCore/AzCore/Debug/ProfilerBus.h | 25 ++++++++++++------- .../AzCore/IO/Streamer/StreamerComponent.cpp | 5 +++- Gems/PhysX/Code/Source/Scene/PhysXScene.cpp | 5 +++- .../Profiler/Code/Source/ImGuiCpuProfiler.cpp | 13 +++------- .../Code/Source/ProfilerSystemComponent.cpp | 8 ++++++ 6 files changed, 52 insertions(+), 27 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp b/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp index 3b5033a07f..3b6b3b7e7a 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp +++ b/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp @@ -26,23 +26,32 @@ namespace AZ::Debug void ProfilerCaptureFrame([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { - AZStd::string captureFile = GenerateOutputFile("single"); - AZLOG_INFO("Setting capture file to %s", captureFile.c_str()); - AZ::Debug::ProfilerRequestBus::Broadcast(&AZ::Debug::ProfilerRequestBus::Events::CaptureFrame, captureFile); + if (auto profilerSystem = ProfilerSystemInterface::Get(); profilerSystem) + { + AZStd::string captureFile = GenerateOutputFile("single"); + AZLOG_INFO("Setting capture file to %s", captureFile.c_str()); + profilerSystem->CaptureFrame(captureFile); + } } AZ_CONSOLEFREEFUNC(ProfilerCaptureFrame, AZ::ConsoleFunctorFlags::DontReplicate, "Capture a single frame of profiling data"); void ProfilerStartCapture([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { - AZStd::string captureFile = GenerateOutputFile("multi"); - AZLOG_INFO("Setting capture file to %s", captureFile.c_str()); - ProfilerRequestBus::Broadcast(&ProfilerRequestBus::Events::StartCapture, captureFile); + if (auto profilerSystem = ProfilerSystemInterface::Get(); profilerSystem) + { + AZStd::string captureFile = GenerateOutputFile("multi"); + AZLOG_INFO("Setting capture file to %s", captureFile.c_str()); + profilerSystem->StartCapture(captureFile); + } } AZ_CONSOLEFREEFUNC(ProfilerStartCapture, AZ::ConsoleFunctorFlags::DontReplicate, "Start a multi-frame capture of profiling data"); void ProfilerEndCapture([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { - AZ::Debug::ProfilerRequestBus::Broadcast(&AZ::Debug::ProfilerRequestBus::Events::EndCapture); + if (auto profilerSystem = ProfilerSystemInterface::Get(); profilerSystem) + { + profilerSystem->EndCapture(); + } } AZ_CONSOLEFREEFUNC(ProfilerEndCapture, AZ::ConsoleFunctorFlags::DontReplicate, "End and dump an in-progress continuous capture"); diff --git a/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h b/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h index 03a89b11f2..9194381e23 100644 --- a/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h +++ b/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h @@ -43,16 +43,9 @@ namespace AZ * ProfilerRequests provides an interface for making profiling system requests */ class ProfilerRequests - : public AZ::EBusTraits { public: - // EBusTraits overrides - static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; - - // Allow multiple threads to concurrently make requests - using MutexType = AZStd::mutex; - + AZ_RTTI(ProfilerRequests, "{90AEC117-14C1-4BAE-9704-F916E49EF13F}"); virtual ~ProfilerRequests() = default; //! Getter/setter for the profiler active state @@ -66,7 +59,21 @@ namespace AZ virtual bool StartCapture(AZStd::string outputFilePath) = 0; virtual bool EndCapture() = 0; }; - using ProfilerRequestBus = AZ::EBus; + + class ProfilerRequestsTraits + : public AZ::EBusTraits + { + public: + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + + // Allow multiple threads to concurrently make requests + using MutexType = AZStd::mutex; + }; + + using ProfilerSystemInterface = AZ::Interface; + using ProfilerRequestBus = AZ::EBus; //! helper function for getting the profiler capture location from the settings registry that //! includes fallback handing in the event the registry value can't be determined diff --git a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp index 9728866fb6..9a465021c2 100644 --- a/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp +++ b/Code/Framework/AzCore/AzCore/IO/Streamer/StreamerComponent.cpp @@ -156,7 +156,10 @@ namespace AZ void StreamerComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { bool isEnabled = false; - AZ::Debug::ProfilerRequestBus::BroadcastResult(isEnabled, &AZ::Debug::ProfilerRequests::IsActive); + if (auto profilerSystem = AZ::Debug::ProfilerSystemInterface::Get(); profilerSystem) + { + isEnabled = profilerSystem->IsActive(); + } if (isEnabled) { diff --git a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp index 86e3ceb98f..22fc665eec 100644 --- a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp +++ b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp @@ -1175,7 +1175,10 @@ namespace PhysX using physx::PxGeometryType; bool isProfilingActive = false; - AZ::Debug::ProfilerRequestBus::BroadcastResult(isProfilingActive, &AZ::Debug::ProfilerRequests::IsActive); + if (auto profilerSystem = AZ::Debug::ProfilerSystemInterface::Get(); profilerSystem) + { + isProfilingActive = profilerSystem->IsActive(); + } if (!isProfilingActive) { diff --git a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp index fd19460153..e1a734136b 100644 --- a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp +++ b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp @@ -154,10 +154,7 @@ namespace Profiler if (m_captureToFile) { - AZ::Debug::ProfilerRequestBus::Broadcast( - &AZ::Debug::ProfilerRequestBus::Events::CaptureFrame, - GenerateOutputFile("single") - ); + AZ::Debug::ProfilerSystemInterface::Get()->CaptureFrame(GenerateOutputFile("single")); } m_captureToFile = false; @@ -198,17 +195,15 @@ namespace Profiler bool isInProgress = CpuProfiler::Get()->IsContinuousCaptureInProgress(); if (ImGui::Button(isInProgress ? "End" : "Begin")) { + auto profilerSystem = AZ::Debug::ProfilerSystemInterface::Get(); if (isInProgress) { - AZ::Debug::ProfilerRequestBus::Broadcast(&AZ::Debug::ProfilerRequestBus::Events::EndCapture); + profilerSystem->EndCapture(); m_paused = true; } else { - AZ::Debug::ProfilerRequestBus::Broadcast( - &AZ::Debug::ProfilerRequestBus::Events::StartCapture, - GenerateOutputFile("multi") - ); + profilerSystem->StartCapture(GenerateOutputFile("multi")); } } diff --git a/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp b/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp index 09df0970b8..4deda64964 100644 --- a/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp +++ b/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp @@ -128,10 +128,18 @@ namespace Profiler 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() From f90074adb6e78addc635667c97b2c8bd7ca0a2a3 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Wed, 27 Oct 2021 15:54:03 -0700 Subject: [PATCH 08/16] [profiler_capture_api] added utility for reflecting AZ::Interfaces through the BehaviorContext Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- .../AzCore/RTTI/BehaviorInterfaceProxy.h | 122 ++++++++++++++++++ .../AzCore/AzCore/azcore_files.cmake | 1 + 2 files changed, 123 insertions(+) create mode 100644 Code/Framework/AzCore/AzCore/RTTI/BehaviorInterfaceProxy.h diff --git a/Code/Framework/AzCore/AzCore/RTTI/BehaviorInterfaceProxy.h b/Code/Framework/AzCore/AzCore/RTTI/BehaviorInterfaceProxy.h new file mode 100644 index 0000000000..430524d3a2 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/RTTI/BehaviorInterfaceProxy.h @@ -0,0 +1,122 @@ +/* + * 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 AZ +{ + /** + * Utility class for reflecting an AZ::Interface through the BehaviorContext + * + * Example: + * + * class MyInterface + * { + * public: + * AZ_RTTI(MyInterface, "{BADDF000D-CDCD-CDCD-CDCD-BAAAADF0000D}"); + * virtual ~MyInterface() = default; + * + * virtual AZStd::string Foo() = 0; + * virtual void Bar(float x, float y) = 0; + * }; + * + * class MySystemProxy + * : public BehaviorInterfaceProxy + * { + * public: + * AZ_RTTI(MySystemProxy, "{CDCDCDCD-BAAD-BADD-F00D-CDCDCDCDCDCD}", BehaviorInterfaceProxy); + * }; + * + * void Reflect(AZ::ReflectContext* context) + * { + * if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + * { + * behaviorContext->ConstantProperty("g_MySystem", MySystemProxy::GetInstance) + * ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + * ->Attribute(AZ::Script::Attributes::Module, "MyModule"); + * + * behaviorContext->Class("MySystemInterface") + * ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + * ->Attribute(AZ::Script::Attributes::Module, "MyModule") + * + * ->Method("Foo", MySystemProxy::WrapMethod<&MyInterface::Foo>()) + * ->Method("Bar", MySystemProxy::WrapMethod<&MyInterface::Bar>()); + * } + * } + */ + template + class BehaviorInterfaceProxy + { + public: + AZ_CLASS_ALLOCATOR(BehaviorInterfaceProxy, AZ::SystemAllocator, 0); + AZ_RTTI(BehaviorInterfaceProxy, "{E7CC8D27-4499-454E-A7DF-3F72FBECD30D}"); + + //! Accessor for use with ConstantProperty + static T* GetInstance() + { + T* interfacePtr = AZ::Interface::Get(); + AZ_Warning("BehaviorInterfaceProxy", interfacePtr, + "There is currently no global %s registered with an AZ Interface", + AzTypeInfo::Name() + ); + // Don't delete the global instance, it is not owned by the behavior context + return interfacePtr; + } + + //! Helper for attaching interface function via ClassBuilder::Method + template + static auto WrapMethod() + { + using FuncTraits = AZStd::function_traits>; + return FuncTraits::template expand_args::template WrapMethod(); + } + + BehaviorInterfaceProxy() = default; + virtual ~BehaviorInterfaceProxy() = default; + + //! Stores the instance which will use the provided shared_ptr deleter when the reference count hits zero + BehaviorInterfaceProxy(AZStd::shared_ptr sharedInstance) + : m_instance(AZStd::move(sharedInstance)) + { + } + + //! Stores the instance which will perform a no-op deleter when the reference count hits zero + BehaviorInterfaceProxy(T* rawIntance) + : m_instance(rawIntance, [](T*) {}) + { + } + + //! Returns if the m_instance shared pointer is non-nullptr + bool IsValid() const { return m_instance; } + + private: + template + struct MethodWrapper + { + template + static auto WrapMethod() + { + using return_type = AZStd::function_traits_get_result_t>; + return [](BehaviorInterfaceProxy* proxy, Args... params) -> return_type + { + if (proxy && proxy->IsValid()) + { + return AZStd::invoke(Method, proxy->m_instance, AZStd::forward(params)...); + } + return return_type(); + }; + } + }; + + AZStd::shared_ptr m_instance; + }; +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index d4a26256bf..3cd702e588 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -456,6 +456,7 @@ set(FILES RTTI/BehaviorContext.h RTTI/BehaviorContextUtilities.h RTTI/BehaviorContextUtilities.cpp + RTTI/BehaviorInterfaceProxy.h RTTI/BehaviorObjectSignals.h RTTI/TypeSafeIntegral.h Script/ScriptAsset.cpp From e7ed2c2a83f22c02886e2826ba9c733e87145f5a Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Wed, 27 Oct 2021 15:55:02 -0700 Subject: [PATCH 09/16] [profiler_capture_api] updated ProfilerRequests reflection to be as AZ::Interface Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- .../AzCore/Debug/ProfilerReflection.cpp | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp index 72ad4b735b..430e320c54 100644 --- a/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp +++ b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp @@ -9,11 +9,16 @@ #include #include +#include #include #include namespace AZ::Debug { + static constexpr const char* ProfilerScriptCategory = "Profiler"; + static constexpr const char* ProfilerScriptModule = "debug"; + static constexpr AZ::Script::Attributes::ScopeFlags ProfilerScriptScope = AZ::Script::Attributes::ScopeFlags::Automation; + class ProfilerNotificationBusHandler final : public ProfilerNotificationBus::Handler , public AZ::BehaviorEBusHandler @@ -33,38 +38,46 @@ namespace AZ::Debug if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->EBus("ProfilerNotificationBus") - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) - ->Attribute(AZ::Script::Attributes::Module, "debug") + ->Attribute(AZ::Script::Attributes::Category, ProfilerScriptCategory) + ->Attribute(AZ::Script::Attributes::Module, ProfilerScriptModule) + ->Attribute(AZ::Script::Attributes::Scope, ProfilerScriptScope) ->Handler(); } } }; - void ProfilerReflect(AZ::ReflectContext* context) + class ProfilerSystemScriptProxy + : public BehaviorInterfaceProxy { - if (AZ::SerializeContext* serialize = azrtti_cast(context)) - { - if (AZ::EditContext* ec = serialize->GetEditContext()) - { - ProfilerNotificationBusHandler::Reflect(context); - } - } + public: + AZ_RTTI(ProfilerSystemScriptProxy, "{D671FB70-8B09-4C3A-96CD-06A339F3138E}", BehaviorInterfaceProxy); + }; + void ProfilerReflect(AZ::ReflectContext* context) + { if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { - behaviorContext->EBus("ProfilerRequestBus") - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) - ->Attribute(AZ::Script::Attributes::Module, "debug") + behaviorContext->ConstantProperty("g_ProfilerSystem", ProfilerSystemScriptProxy::GetInstance) + ->Attribute(AZ::Script::Attributes::Category, ProfilerScriptCategory) + ->Attribute(AZ::Script::Attributes::Module, ProfilerScriptModule) + ->Attribute(AZ::Script::Attributes::Scope, ProfilerScriptScope); + + behaviorContext->Class("ProfilerSystemInterface") + ->Attribute(AZ::Script::Attributes::Category, ProfilerScriptCategory) + ->Attribute(AZ::Script::Attributes::Module, ProfilerScriptModule) + ->Attribute(AZ::Script::Attributes::Scope, ProfilerScriptScope) - ->Event("IsActive", &ProfilerRequestBus::Events::IsActive) - ->Event("SetActive", &ProfilerRequestBus::Events::SetActive) + ->Method("IsValid", &ProfilerSystemScriptProxy::IsValid) - ->Event("CaptureFrame", &ProfilerRequestBus::Events::CaptureFrame) + ->Method("IsActive", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::IsActive>()) + ->Method("SetActive", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::SetActive>()) - ->Event("StartCapture", &ProfilerRequestBus::Events::StartCapture) - ->Event("EndCapture", &ProfilerRequestBus::Events::EndCapture); + ->Method("CaptureFrame", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::CaptureFrame>()) - ProfilerNotificationBusHandler::Reflect(context); + ->Method("StartCapture", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::StartCapture>()) + ->Method("EndCapture", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::EndCapture>()); } + + ProfilerNotificationBusHandler::Reflect(context); } } // namespace AZ::Debug From fc94ede4398b9d8b6f96ecdc63ad0f158cf7f2c4 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Thu, 28 Oct 2021 11:55:49 -0700 Subject: [PATCH 10/16] [profiler_capture_api] fixed runtime issues with BehaviorInterfaceProxy reflection Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- .../AzCore/Debug/ProfilerReflection.cpp | 10 +++- .../AzCore/RTTI/BehaviorInterfaceProxy.h | 58 ++++++++++--------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp index 430e320c54..3455400cfe 100644 --- a/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp +++ b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp @@ -51,13 +51,15 @@ namespace AZ::Debug { public: AZ_RTTI(ProfilerSystemScriptProxy, "{D671FB70-8B09-4C3A-96CD-06A339F3138E}", BehaviorInterfaceProxy); + + AZ_BEHAVIOR_INTERFACE(ProfilerSystemScriptProxy, ProfilerRequests); }; void ProfilerReflect(AZ::ReflectContext* context) { if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { - behaviorContext->ConstantProperty("g_ProfilerSystem", ProfilerSystemScriptProxy::GetInstance) + behaviorContext->ConstantProperty("g_ProfilerSystem", ProfilerSystemScriptProxy::GetProxy) ->Attribute(AZ::Script::Attributes::Category, ProfilerScriptCategory) ->Attribute(AZ::Script::Attributes::Module, ProfilerScriptModule) ->Attribute(AZ::Script::Attributes::Scope, ProfilerScriptScope); @@ -69,6 +71,12 @@ namespace AZ::Debug ->Method("IsValid", &ProfilerSystemScriptProxy::IsValid) + ->Method("GetCaptureLocation", + [](ProfilerSystemScriptProxy*) -> AZStd::string + { + return AZStd::string(GetProfilerCaptureLocation().c_str()); + }) + ->Method("IsActive", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::IsActive>()) ->Method("SetActive", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::SetActive>()) diff --git a/Code/Framework/AzCore/AzCore/RTTI/BehaviorInterfaceProxy.h b/Code/Framework/AzCore/AzCore/RTTI/BehaviorInterfaceProxy.h index 430524d3a2..0e7a4ac356 100644 --- a/Code/Framework/AzCore/AzCore/RTTI/BehaviorInterfaceProxy.h +++ b/Code/Framework/AzCore/AzCore/RTTI/BehaviorInterfaceProxy.h @@ -34,13 +34,14 @@ namespace AZ * { * public: * AZ_RTTI(MySystemProxy, "{CDCDCDCD-BAAD-BADD-F00D-CDCDCDCDCDCD}", BehaviorInterfaceProxy); + * AZ_BEHAVIOR_INTERFACE(MySystemProxy, MyInterface); * }; * * void Reflect(AZ::ReflectContext* context) * { * if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) * { - * behaviorContext->ConstantProperty("g_MySystem", MySystemProxy::GetInstance) + * behaviorContext->ConstantProperty("g_MySystem", MySystemProxy::GetProxy) * ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) * ->Attribute(AZ::Script::Attributes::Module, "MyModule"); * @@ -60,26 +61,6 @@ namespace AZ AZ_CLASS_ALLOCATOR(BehaviorInterfaceProxy, AZ::SystemAllocator, 0); AZ_RTTI(BehaviorInterfaceProxy, "{E7CC8D27-4499-454E-A7DF-3F72FBECD30D}"); - //! Accessor for use with ConstantProperty - static T* GetInstance() - { - T* interfacePtr = AZ::Interface::Get(); - AZ_Warning("BehaviorInterfaceProxy", interfacePtr, - "There is currently no global %s registered with an AZ Interface", - AzTypeInfo::Name() - ); - // Don't delete the global instance, it is not owned by the behavior context - return interfacePtr; - } - - //! Helper for attaching interface function via ClassBuilder::Method - template - static auto WrapMethod() - { - using FuncTraits = AZStd::function_traits>; - return FuncTraits::template expand_args::template WrapMethod(); - } - BehaviorInterfaceProxy() = default; virtual ~BehaviorInterfaceProxy() = default; @@ -98,25 +79,48 @@ namespace AZ //! Returns if the m_instance shared pointer is non-nullptr bool IsValid() const { return m_instance; } - private: - template + protected: + //! Internal access for use in the derived GetProxy function + static T* GetInstance() + { + T* interfacePtr = AZ::Interface::Get(); + AZ_Warning("BehaviorInterfaceProxy", interfacePtr, + "There is currently no global %s registered with an AZ Interface", + AzTypeInfo::Name() + ); + // Don't delete the global instance, it is not owned by the behavior context + return interfacePtr; + } + + template struct MethodWrapper { - template + template static auto WrapMethod() { - using return_type = AZStd::function_traits_get_result_t>; - return [](BehaviorInterfaceProxy* proxy, Args... params) -> return_type + using ReturnType = AZStd::function_traits_get_result_t>; + return [](Proxy* proxy, Args... params) -> ReturnType { if (proxy && proxy->IsValid()) { return AZStd::invoke(Method, proxy->m_instance, AZStd::forward(params)...); } - return return_type(); + return ReturnType(); }; } }; AZStd::shared_ptr m_instance; }; + + #define AZ_BEHAVIOR_INTERFACE(ProxyType, InterfaceType) \ + static ProxyType GetProxy() { return GetInstance(); } \ + template \ + static auto WrapMethod() { \ + using FuncTraits = AZStd::function_traits>; \ + return FuncTraits::template expand_args::template WrapMethod(); \ + } \ + ProxyType() = default; \ + ProxyType(AZStd::shared_ptr sharedInstance) : BehaviorInterfaceProxy(sharedInstance) {} \ + ProxyType(InterfaceType* rawIntance) : BehaviorInterfaceProxy(rawIntance) {} } // namespace AZ From 90509c7fa20068f7dbe271aa08b8864a59ff3aa5 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Thu, 28 Oct 2021 14:18:04 -0700 Subject: [PATCH 11/16] [profiler_capture_api] removed now unnecessary ProfilerRequests EBus alias after AZ::Interface conversion Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h | 13 ------------- .../Code/Source/ProfilerSystemComponent.cpp | 4 ---- Gems/Profiler/Code/Source/ProfilerSystemComponent.h | 4 ++-- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h b/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h index 9194381e23..322bfb60c2 100644 --- a/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h +++ b/Code/Framework/AzCore/AzCore/Debug/ProfilerBus.h @@ -60,20 +60,7 @@ namespace AZ virtual bool EndCapture() = 0; }; - class ProfilerRequestsTraits - : public AZ::EBusTraits - { - public: - // EBusTraits overrides - static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; - - // Allow multiple threads to concurrently make requests - using MutexType = AZStd::mutex; - }; - using ProfilerSystemInterface = AZ::Interface; - using ProfilerRequestBus = AZ::EBus; //! helper function for getting the profiler capture location from the settings registry that //! includes fallback handing in the event the registry value can't be determined diff --git a/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp b/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp index 4deda64964..24da9e050e 100644 --- a/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp +++ b/Gems/Profiler/Code/Source/ProfilerSystemComponent.cpp @@ -144,8 +144,6 @@ namespace Profiler void ProfilerSystemComponent::Activate() { - AZ::Debug::ProfilerRequestBus::Handler::BusConnect(); - m_cpuProfiler.Init(); } @@ -153,8 +151,6 @@ namespace Profiler { m_cpuProfiler.Shutdown(); - AZ::Debug::ProfilerRequestBus::Handler::BusDisconnect(); - // Block deactivation until the IO thread has finished serializing the CPU data if (m_cpuDataSerializationThread.joinable()) { diff --git a/Gems/Profiler/Code/Source/ProfilerSystemComponent.h b/Gems/Profiler/Code/Source/ProfilerSystemComponent.h index 000b11cfdf..a1c8c47479 100644 --- a/Gems/Profiler/Code/Source/ProfilerSystemComponent.h +++ b/Gems/Profiler/Code/Source/ProfilerSystemComponent.h @@ -18,7 +18,7 @@ namespace Profiler { class ProfilerSystemComponent : public AZ::Component - , protected AZ::Debug::ProfilerRequestBus::Handler + , protected AZ::Debug::ProfilerRequests { public: AZ_COMPONENT(ProfilerSystemComponent, "{3f52c1d7-d920-4781-8ed7-88077ec4f305}"); @@ -38,7 +38,7 @@ namespace Profiler void Activate() override; void Deactivate() override; - // ProfilerRequestBus interface implementation + // ProfilerRequests interface implementation bool IsActive() const override; void SetActive(bool active) override; bool CaptureFrame(const AZStd::string& outputFilePath) override; From e67db19a630d08b6302528a943f0033a6c1c32fe Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Thu, 28 Oct 2021 14:26:22 -0700 Subject: [PATCH 12/16] [profiler_capture_api] add sample python script to test Profiler System interface bindings Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- .../Editor/Scripts/Profiler/__init__.py | 7 +++++ .../Profiler/profiler_system_example.py | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 AutomatedTesting/Editor/Scripts/Profiler/__init__.py create mode 100644 AutomatedTesting/Editor/Scripts/Profiler/profiler_system_example.py diff --git a/AutomatedTesting/Editor/Scripts/Profiler/__init__.py b/AutomatedTesting/Editor/Scripts/Profiler/__init__.py new file mode 100644 index 0000000000..a26f3b57be --- /dev/null +++ b/AutomatedTesting/Editor/Scripts/Profiler/__init__.py @@ -0,0 +1,7 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# \ No newline at end of file diff --git a/AutomatedTesting/Editor/Scripts/Profiler/profiler_system_example.py b/AutomatedTesting/Editor/Scripts/Profiler/profiler_system_example.py new file mode 100644 index 0000000000..16f161c50e --- /dev/null +++ b/AutomatedTesting/Editor/Scripts/Profiler/profiler_system_example.py @@ -0,0 +1,30 @@ +# +# 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 +# +# + +import azlmbr.debug as debug + +import pathlib + +def test_profiler_system(): + if not debug.g_ProfilerSystem.IsValid(): + print('g_ProfilerSystem is INVALID') + return + + state = 'ACTIVE' if debug.g_ProfilerSystem.IsActive() else 'INACTIVE' + print(f'Profiler system is currently {state}') + + capture_location = pathlib.Path(debug.g_ProfilerSystem.GetCaptureLocation()) + print(f'Capture location set to {capture_location}') + + print('Capturing single frame...' ) + capture_file = str(capture_location / 'script_capture_frame.json') + debug.g_ProfilerSystem.CaptureFrame(capture_file) + +# Invoke main function +if __name__ == '__main__': + test_profiler_system() From 9bc0422f0b0fcf6a23361642f8b0a8166f4fad5c Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Thu, 28 Oct 2021 14:39:17 -0700 Subject: [PATCH 13/16] [profiler_capture_api] added trailing newlines to a couple new files Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- AutomatedTesting/Editor/Scripts/Profiler/__init__.py | 2 +- Registry/profiler.setreg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AutomatedTesting/Editor/Scripts/Profiler/__init__.py b/AutomatedTesting/Editor/Scripts/Profiler/__init__.py index a26f3b57be..7a325ca97e 100644 --- a/AutomatedTesting/Editor/Scripts/Profiler/__init__.py +++ b/AutomatedTesting/Editor/Scripts/Profiler/__init__.py @@ -4,4 +4,4 @@ # # SPDX-License-Identifier: Apache-2.0 OR MIT # -# \ No newline at end of file +# diff --git a/Registry/profiler.setreg b/Registry/profiler.setreg index a295b007e1..b46bfd8beb 100644 --- a/Registry/profiler.setreg +++ b/Registry/profiler.setreg @@ -12,4 +12,4 @@ } } } -} \ No newline at end of file +} From b2e6711196295a81f47224d1578aa939afb47133 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Fri, 29 Oct 2021 14:45:25 -0700 Subject: [PATCH 14/16] [profiler_capture_api] fixed clang compile error Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/Debug/Trace.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/Debug/Trace.cpp b/Code/Framework/AzCore/AzCore/Debug/Trace.cpp index 1dd9d0f0f9..b9e4003500 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Trace.cpp +++ b/Code/Framework/AzCore/AzCore/Debug/Trace.cpp @@ -55,7 +55,7 @@ namespace AZ::Debug // Globals const int g_maxMessageLength = 4096; static const char* g_dbgSystemWnd = "System"; - Trace Debug::g_tracer; + Trace g_tracer; void* g_exceptionInfo = nullptr; // Environment var needed to track ignored asserts across systems and disable native UI under certain conditions From 4721f44b928365e930124d3cefa49cecacfe325c Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Fri, 29 Oct 2021 17:19:38 -0700 Subject: [PATCH 15/16] [profiler_capture_api] more changes based on PR feedback Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- Code/Framework/AzCore/AzCore/Debug/Profiler.cpp | 8 ++------ Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp | 3 ++- Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp | 5 +---- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp b/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp index 3b6b3b7e7a..0cb77b2d6e 100644 --- a/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp +++ b/Code/Framework/AzCore/AzCore/Debug/Profiler.cpp @@ -16,12 +16,8 @@ namespace AZ::Debug { AZStd::string GenerateOutputFile(const char* nameHint) { - AZStd::string timeString; - AZStd::to_string(timeString, AZStd::GetTimeNowSecond()); - AZ::IO::FixedMaxPathString captureOutput = GetProfilerCaptureLocation(); - - return AZStd::string::format("%s/capture_%s_%s.json", captureOutput.c_str(), nameHint, timeString.c_str()); + return AZStd::string::format("%s/capture_%s_%lld.json", captureOutput.c_str(), nameHint, AZStd::GetTimeNowSecond()); } void ProfilerCaptureFrame([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) @@ -41,7 +37,7 @@ namespace AZ::Debug { AZStd::string captureFile = GenerateOutputFile("multi"); AZLOG_INFO("Setting capture file to %s", captureFile.c_str()); - profilerSystem->StartCapture(captureFile); + profilerSystem->StartCapture(AZStd::move(captureFile)); } } AZ_CONSOLEFREEFUNC(ProfilerStartCapture, AZ::ConsoleFunctorFlags::DontReplicate, "Start a multi-frame capture of profiling data"); diff --git a/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp index 3455400cfe..42151a6996 100644 --- a/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp +++ b/Code/Framework/AzCore/AzCore/Debug/ProfilerReflection.cpp @@ -74,7 +74,8 @@ namespace AZ::Debug ->Method("GetCaptureLocation", [](ProfilerSystemScriptProxy*) -> AZStd::string { - return AZStd::string(GetProfilerCaptureLocation().c_str()); + AZ::IO::FixedMaxPathString captureOutput = GetProfilerCaptureLocation(); + return AZStd::string(captureOutput.c_str(), captureOutput.length()); }) ->Method("IsActive", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::IsActive>()) diff --git a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp index e1a734136b..26c2f9f974 100644 --- a/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp +++ b/Gems/Profiler/Code/Source/ImGuiCpuProfiler.cpp @@ -402,13 +402,10 @@ namespace Profiler AZStd::string ImGuiCpuProfiler::GenerateOutputFile(const char* nameHint) { - AZStd::string timeString; - AZStd::to_string(timeString, AZStd::GetTimeNowSecond()); - AZ::IO::FixedMaxPathString captureOutput = AZ::Debug::GetProfilerCaptureLocation(); const AZ::IO::FixedMaxPathString frameDataFilePath = - AZ::IO::FixedMaxPathString::format("%s/cpu_%s_%s.json", captureOutput.c_str(), nameHint, timeString.c_str()); + AZ::IO::FixedMaxPathString::format("%s/cpu_%s_%lld.json", captureOutput.c_str(), nameHint, AZStd::GetTimeNowSecond()); AZ::IO::FileIOBase::GetInstance()->ResolvePath(m_lastCapturedFilePath, frameDataFilePath.c_str()); From 9057cfbcbf6045752ee21443e5e71f78b71237d7 Mon Sep 17 00:00:00 2001 From: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> Date: Mon, 1 Nov 2021 11:41:44 -0700 Subject: [PATCH 16/16] [profiler_capture_api] fixed release compile error Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com> --- Code/Legacy/CrySystem/AZCoreLogSink.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Legacy/CrySystem/AZCoreLogSink.h b/Code/Legacy/CrySystem/AZCoreLogSink.h index 8c88752e9a..1b09c3198d 100644 --- a/Code/Legacy/CrySystem/AZCoreLogSink.h +++ b/Code/Legacy/CrySystem/AZCoreLogSink.h @@ -16,7 +16,7 @@ #include -namespace AZ +namespace AZ::Debug { AZ_CVAR_EXTERNED(int, bg_traceLogLevel); } @@ -64,7 +64,7 @@ public: if(!hasSetCVar && ready) { // AZ logging only has a concept of 3 levels (error, warning, info) but cry logging has 4 levels (..., messaging). If info level is set, we'll turn on messaging as well - int logLevel = AZ::bg_traceLogLevel == AZ::Debug::LogLevel::Info ? 4 : AZ::bg_traceLogLevel; + int logLevel = AZ::Debug::bg_traceLogLevel == AZ::Debug::LogLevel::Info ? 4 : AZ::Debug::bg_traceLogLevel; gEnv->pConsole->GetCVar("log_WriteToFileVerbosity")->Set(logLevel); hasSetCVar = true;