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] [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