[development] Migrate Atom CPU timing stats tracking to use global stats profiler (#4549)

This change is a preparation for moving the CPU profiler/visualization system from Atom into its own Gem by removing the dependency on local time tracking object AZ::RHI::CpuTimingStatistics

Full changes include:
- Removed all usage of AZ::RHI::CpuTimingStatistics
-- Replaced with pushing to AZ::Statistics::StatisticalProfilerProxy global instance
- Promoted VariableTimer from AZ::RHI to AZ::Debug
- Removed now unused CpuTimingStatistics.h

Signed-off-by: AMZN-ScottR 24445312+AMZN-ScottR@users.noreply.github.com
This commit is contained in:
Scott Romero
2021-10-11 14:00:42 -07:00
committed by GitHub
parent d3c2e288e9
commit a95c609bd8
36 changed files with 213 additions and 221 deletions
@@ -46,5 +46,23 @@ namespace AZ
private:
AZStd::sys_time_t m_timeStamp;
};
//! Utility type that updates the given variable with the lifetime of the object in cycles.
//! Useful for quick scope based timing.
struct ScopedTimer
{
explicit ScopedTimer(AZStd::sys_time_t& variable)
: m_variable(variable)
{
m_timer.Stamp();
}
~ScopedTimer()
{
m_variable = m_timer.GetDeltaTimeInTicks();
}
AZStd::sys_time_t& m_variable;
Timer m_timer;
};
}
}
@@ -11,7 +11,6 @@
#include <Atom/RHI/CpuProfilerImpl.h>
#include <Atom/RHI/RHIUtils.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RHI.Reflect/CpuTimingStatistics.h>
#include <AzCore/Statistics/RunningStatistic.h>
#include <Atom/RPI.Public/GpuQuery/GpuQueryTypes.h>
@@ -457,17 +456,8 @@ namespace AZ
JsonSerializerSettings serializationSettings;
serializationSettings.m_keepDefaults = true;
double frameTime = 0.0;
const AZ::RHI::CpuTimingStatistics* stats = AZ::RHI::RHISystemInterface::Get()->GetCpuTimingStatistics();
if (stats)
{
frameTime = stats->GetFrameToFrameTimeMilliseconds();
}
else
{
AZStd::string warning = AZStd::string::format("Failed to get Cpu frame time");
AZ_Warning("ProfilingCaptureSystemComponent", false, warning.c_str());
}
double frameTime = AZ::RHI::RHISystemInterface::Get()->GetCpuFrameTime();
AZ_Warning("ProfilingCaptureSystemComponent", frameTime > 0, "Failed to get Cpu frame time");
CpuFrameTimeSerializer serializer(frameTime);
const auto saveResult = JsonSerializationUtils::SaveObjectToFile(&serializer,
@@ -16,6 +16,7 @@
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
AZ_DECLARE_BUDGET(RHI);
inline static constexpr AZ::Crc32 rhiMetricsId = AZ_CRC_CE("RHI");
namespace UnitTest
{
@@ -1,77 +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 <Atom/RHI.Reflect/AttachmentEnums.h>
#include <AzCore/Debug/Timer.h>
#include <AzCore/Name/Name.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/Casting/numeric_cast.h>
namespace AZ
{
namespace RHI
{
//! Container and helper type for storing per frame CPU timing data.
//! Users can queue up generic timings in Scopes or add to specific timing data.
struct CpuTimingStatistics
{
struct QueueStatistics
{
//! The display name of the queue the statistics are for.
Name m_queueName;
//! Time spent executing queued work.
AZStd::sys_time_t m_executeDuration{};
};
//! Statistics for each command queue.
AZStd::vector<QueueStatistics> m_queueStatistics;
//! The amount of time spent between two calls to EndFrame.
AZStd::sys_time_t m_frameToFrameTime{};
//! The amount of time spent presenting (vsync can affect this).
AZStd::sys_time_t m_presentDuration{};
void Reset()
{
m_queueStatistics.clear();
}
double GetFrameToFrameTimeMilliseconds() const
{
return (m_frameToFrameTime * 1000) / aznumeric_cast<double>(AZStd::GetTimeTicksPerSecond());
}
};
//! Utility type that updates the given variable with the lifetime of the object in cycles.
//! Useful for quick scope based timing.
struct VariableTimer
{
VariableTimer() = delete;
VariableTimer(AZStd::sys_time_t& variable)
: m_variable(variable)
{
m_timer.Stamp();
}
~VariableTimer()
{
m_variable = m_timer.GetDeltaTimeInTicks();
}
AZStd::sys_time_t& m_variable;
AZ::Debug::Timer m_timer;
};
}
}
//! Utility for timing a section of code and writing the timing (in cycles) to the given variable.
#define AZ_PROFILE_RHI_VARIABLE(variable) \
AZ::RHI::VariableTimer AZ_JOIN(variableTimer, __LINE__)(variable);
+5 -8
View File
@@ -27,9 +27,6 @@ namespace AZ
{
namespace RHI
{
struct CpuTimingStatistics;
//! The Device is a context for managing GPU state and memory on a physical device. The user creates
//! a device instance from a PhysicalDevice. Each device has its own capabilities and limits, and can
//! be configured to buffer a specific number of frames.
@@ -91,10 +88,10 @@ namespace AZ
//! scope. Otherwise, an error code is returned.
ResultCode CompileMemoryStatistics(MemoryStatistics& memoryStatistics, MemoryStatisticsReportFlags reportFlags);
//! Fills the provided data structure with cpu timing statistics specific to this device. This
//! method can only be called on an initialized device, and outside of the BeginFrame / EndFrame
//! scope. Otherwise, an error code is returned.
ResultCode UpdateCpuTimingStatistics(CpuTimingStatistics& cpuTimingStatistics) const;
//! Pushes internally recorded timing statistics upwards into the global stats profiler, under the RHI section.
//! This method can only be called on an initialized device, and outside of the BeginFrame / EndFrame scope.
//! Otherwise, an error code is returned.
ResultCode UpdateCpuTimingStatistics() const;
//! Returns the physical device associated with this device.
const PhysicalDevice& GetPhysicalDevice() const;
@@ -186,7 +183,7 @@ namespace AZ
virtual void CompileMemoryStatisticsInternal(MemoryStatisticsBuilder& builder) = 0;
//! Called when the device is reporting cpu timing statistics.
virtual void UpdateCpuTimingStatisticsInternal(CpuTimingStatistics& cpuTimingStatistics) const = 0;
virtual void UpdateCpuTimingStatisticsInternal() const = 0;
//! Fills the capabilities for each format.
virtual void FillFormatsCapabilitiesInternal(FormatCapabilitiesList& formatsCapabilities) = 0;
@@ -7,7 +7,6 @@
*/
#pragma once
#include <Atom/RHI.Reflect/CpuTimingStatistics.h>
#include <Atom/RHI.Reflect/FrameSchedulerEnums.h>
#include <Atom/RHI.Reflect/MemoryStatistics.h>
#include <Atom/RHI/FrameGraphBuilder.h>
@@ -168,8 +167,8 @@ namespace AZ
/// Returns the timing statistics for the previous frame.
const TransientAttachmentStatistics* GetTransientAttachmentStatistics() const;
/// Returns cpu timing statistics for the previous frame.
const CpuTimingStatistics* GetCpuTimingStatistics() const;
/// Returns current CPU frame to frame time in milliseconds.
double GetCpuFrameTime() const;
/// Returns memory statistics for the previous frame.
const MemoryStatistics* GetMemoryStatistics() const;
@@ -216,7 +215,6 @@ namespace AZ
Ptr<TransientAttachmentPool> m_transientAttachmentPool;
CpuTimingStatistics m_cpuTimingStatistics;
AZStd::sys_time_t m_lastFrameEndTime{};
MemoryStatistics m_memoryStatistics;
@@ -48,7 +48,7 @@ namespace AZ
RHI::PipelineStateCache* GetPipelineStateCache() override;
const RHI::FrameSchedulerCompileRequest& GetFrameSchedulerCompileRequest() const override;
void ModifyFrameSchedulerStatisticsFlags(RHI::FrameSchedulerStatisticsFlags statisticsFlags, bool enableFlags) override;
const RHI::CpuTimingStatistics* GetCpuTimingStatistics() const override;
double GetCpuFrameTime() const override;
const RHI::TransientAttachmentStatistics* GetTransientAttachmentStatistics() const override;
const RHI::MemoryStatistics* GetMemoryStatistics() const override;
const RHI::TransientAttachmentPoolDescriptor* GetTransientAttachmentPoolDescriptor() const override;
@@ -27,7 +27,6 @@ namespace AZ
class PipelineStateCache;
class PlatformLimitsDescriptor;
class RayTracingShaderTable;
struct CpuTimingStatistics;
struct FrameSchedulerCompileRequest;
struct TransientAttachmentStatistics;
struct TransientAttachmentPoolDescriptor;
@@ -55,7 +54,7 @@ namespace AZ
virtual void ModifyFrameSchedulerStatisticsFlags(RHI::FrameSchedulerStatisticsFlags statisticsFlags, bool enableFlags) = 0;
virtual const RHI::CpuTimingStatistics* GetCpuTimingStatistics() const = 0;
virtual double GetCpuFrameTime() const = 0;
virtual const RHI::TransientAttachmentStatistics* GetTransientAttachmentStatistics() const = 0;
@@ -32,6 +32,23 @@ namespace AZ
return ResultCode::InvalidOperation;
}
#endif
if (auto statsProfiler = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get(); statsProfiler)
{
auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId);
static constexpr AZStd::string_view presentStatName("Present");
static constexpr AZ::Crc32 presentStatId(presentStatName);
rhiMetrics.GetStatsManager().AddStatistic(presentStatId, presentStatName, /*units=*/"clocks", /*failIfExist=*/false);
if (!GetName().IsEmpty())
{
const AZStd::string commandQueueName(GetName().GetCStr());
const AZ::Crc32 commandQueueId(GetName().GetHash());
rhiMetrics.GetStatsManager().AddStatistic(commandQueueId, commandQueueName, /*units=*/"clocks", /*failIfExist=*/false);
}
}
const ResultCode resultCode = InitInternal(device, descriptor);
if (resultCode == ResultCode::Success)
@@ -12,6 +12,7 @@
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/Debug/Timer.h>
#include <AzCore/Statistics/StatisticalProfilerProxy.h>
#include <Atom/RHI/RHIUtils.h>
namespace AZ
@@ -73,6 +74,11 @@ namespace AZ
m_initialized = true;
SystemTickBus::Handler::BusConnect();
m_continuousCaptureData.set_capacity(10);
if (auto statsProfiler = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get(); statsProfiler)
{
statsProfiler->ActivateProfiler(AZ_CRC_CE("RHI"), true);
}
}
void CpuProfilerImpl::Shutdown()
+2 -2
View File
@@ -160,11 +160,11 @@ namespace AZ
return ResultCode::InvalidOperation;
}
ResultCode Device::UpdateCpuTimingStatistics(CpuTimingStatistics& cpuTimingStatistics) const
ResultCode Device::UpdateCpuTimingStatistics() const
{
if (ValidateIsNotInFrame())
{
UpdateCpuTimingStatisticsInternal(cpuTimingStatistics);
UpdateCpuTimingStatisticsInternal();
return ResultCode::Success;
}
return ResultCode::InvalidOperation;
@@ -35,6 +35,9 @@ namespace AZ
{
namespace RHI
{
static constexpr const char* frameTimeMetricName = "Frame to Frame Time";
static constexpr AZ::Crc32 frameTimeMetricId = AZ_CRC_CE(frameTimeMetricName);
ResultCode FrameScheduler::Init(Device& device, const FrameSchedulerDescriptor& descriptor)
{
ResultCode resultCode = ResultCode::Success;
@@ -81,6 +84,12 @@ namespace AZ
m_taskGraphActive = AZ::Interface<AZ::TaskGraphActiveInterface>::Get();
if (auto statsProfiler = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get(); statsProfiler)
{
auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId);
rhiMetrics.GetStatsManager().AddStatistic(frameTimeMetricId, frameTimeMetricName, /*units=*/"clocks", /*failIfExist=*/false);
}
m_lastFrameEndTime = AZStd::GetTimeNowTicks();
return ResultCode::Success;
@@ -278,7 +287,7 @@ namespace AZ
AZ::TaskDescriptor srgCompileEndDesc{"SrgCompileEnd", "Graphics"};
auto srgCompileEndTask = taskGraph.AddTask(
srgCompileEndDesc,
srgCompileEndDesc,
[srgPool]()
{
srgPool->CompileGroupsEnd();
@@ -449,7 +458,7 @@ namespace AZ
m_device->CompileMemoryStatistics(m_memoryStatistics, MemoryStatisticsReportFlags::Detail);
}
m_device->UpdateCpuTimingStatistics(m_cpuTimingStatistics);
m_device->UpdateCpuTimingStatistics();
m_scopeProducers.clear();
m_scopeProducerLookup.clear();
@@ -460,7 +469,10 @@ namespace AZ
}
const AZStd::sys_time_t timeNowTicks = AZStd::GetTimeNowTicks();
m_cpuTimingStatistics.m_frameToFrameTime = timeNowTicks - m_lastFrameEndTime;
if (auto statsProfiler = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get(); statsProfiler)
{
statsProfiler->PushSample(rhiMetricsId, frameTimeMetricId, static_cast<double>(timeNowTicks - m_lastFrameEndTime));
}
m_lastFrameEndTime = timeNowTicks;
return ResultCode::Success;
@@ -588,12 +600,18 @@ namespace AZ
: nullptr;
}
const CpuTimingStatistics* FrameScheduler::GetCpuTimingStatistics() const
double FrameScheduler::GetCpuFrameTime() const
{
return
CheckBitsAny(m_compileRequest.m_statisticsFlags, FrameSchedulerStatisticsFlags::GatherCpuTimingStatistics)
? &m_cpuTimingStatistics
: nullptr;
if (CheckBitsAny(m_compileRequest.m_statisticsFlags, FrameSchedulerStatisticsFlags::GatherCpuTimingStatistics))
{
if (auto statsProfiler = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get(); statsProfiler)
{
auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId);
const auto* frameTimeStat = rhiMetrics.GetStatistic(frameTimeMetricId);
return (frameTimeStat->GetMostRecentSample() * 1000) / aznumeric_cast<double>(AZStd::GetTimeTicksPerSecond());
}
}
return 0;
}
ScopeId FrameScheduler::GetRootScopeId() const
+2 -2
View File
@@ -254,9 +254,9 @@ namespace AZ
: RHI::ResetBits(m_compileRequest.m_statisticsFlags, statisticsFlags);
}
const RHI::CpuTimingStatistics* RHISystem::GetCpuTimingStatistics() const
double RHISystem::GetCpuFrameTime() const
{
return m_frameScheduler.GetCpuTimingStatistics();
return m_frameScheduler.GetCpuFrameTime();
}
const RHI::TransientAttachmentStatistics* RHISystem::GetTransientAttachmentStatistics() const
+1 -1
View File
@@ -47,7 +47,7 @@ namespace UnitTest
void CompileMemoryStatisticsInternal(AZ::RHI::MemoryStatisticsBuilder&) override {}
void UpdateCpuTimingStatisticsInternal([[maybe_unused]] AZ::RHI::CpuTimingStatistics& cpuTimingStatistics) const override {}
void UpdateCpuTimingStatisticsInternal() const override {}
AZStd::chrono::microseconds GpuTimestampToMicroseconds([[maybe_unused]] uint64_t gpuTimestamp, [[maybe_unused]] AZ::RHI::HardwareQueueClass queueClass) const override
{
@@ -112,7 +112,6 @@ set(FILES
Source/RHI.Reflect/ShaderResourceGroupLayout.cpp
Source/RHI.Reflect/ShaderResourceGroupLayoutDescriptor.cpp
Source/RHI.Reflect/ShaderResourceGroupPoolDescriptor.cpp
Include/Atom/RHI.Reflect/CpuTimingStatistics.h
Include/Atom/RHI.Reflect/MemoryStatistics.h
Include/Atom/RHI.Reflect/TransientAttachmentStatistics.h
Include/Atom/RHI.Reflect/SwapChainDescriptor.h
@@ -10,8 +10,8 @@
#include <RHI/Fence.h>
#include <RHI/SwapChain.h>
#include <RHI/Conversions.h>
#include <AzCore/Debug/EventTraceDrillerBus.h>
#include <Atom/RHI.Reflect/CpuTimingStatistics.h>
#include <AzCore/Debug/Timer.h>
namespace AZ
{
@@ -139,7 +139,7 @@ namespace AZ
QueueCommand([=](void* commandQueue)
{
AZ_PROFILE_SCOPE(RHI, "ExecuteWork");
AZ_PROFILE_RHI_VARIABLE(m_lastExecuteDuration);
AZ::Debug::ScopedTimer executionTimer(m_lastExecuteDuration);
static const uint32_t CommandListCountMax = 128;
ID3D12CommandQueue* dx12CommandQueue = static_cast<ID3D12CommandQueue*>(commandQueue);
@@ -185,7 +185,7 @@ namespace AZ
dx12CommandQueue->Signal(fence->Get(), fence->GetPendingValue());
}
AZ_PROFILE_RHI_VARIABLE(m_lastPresentDuration);
AZ::Debug::ScopedTimer presentTimer(m_lastPresentDuration);
for (RHI::SwapChain* swapChain : request.m_swapChainsToPresent)
{
swapChain->Present();
@@ -7,7 +7,6 @@
*/
#include <Atom/RHI/Device.h>
#include <Atom/RHI.Reflect/CpuTimingStatistics.h>
#include <AzCore/Debug/EventTraceDrillerBus.h>
#include <RHI/CommandQueueContext.h>
#include <RHI/Device.h>
@@ -183,17 +182,22 @@ namespace AZ
return *m_commandQueues[static_cast<uint32_t>(hardwareQueueClass)];
}
void CommandQueueContext::UpdateCpuTimingStatistics(RHI::CpuTimingStatistics& cpuTimingStatistics) const
void CommandQueueContext::UpdateCpuTimingStatistics() const
{
cpuTimingStatistics.Reset();
AZStd::sys_time_t presentDuration = 0;
for (const RHI::Ptr<CommandQueue>& commandQueue : m_commandQueues)
if (auto statsProfiler = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get(); statsProfiler)
{
cpuTimingStatistics.m_queueStatistics.push_back({ commandQueue->GetName(), commandQueue->GetLastExecuteDuration() });
presentDuration += commandQueue->GetLastPresentDuration();
auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId);
AZStd::sys_time_t presentDuration = 0;
for (const RHI::Ptr<CommandQueue>& commandQueue : m_commandQueues)
{
const AZ::Crc32 commandQueueId(commandQueue->GetName().GetHash());
rhiMetrics.PushSample(commandQueueId, static_cast<double>(commandQueue->GetLastExecuteDuration()));
presentDuration += commandQueue->GetLastPresentDuration();
}
rhiMetrics.PushSample(AZ_CRC_CE("Present"), static_cast<double>(presentDuration));
}
cpuTimingStatistics.m_presentDuration = presentDuration;
}
const FenceSet& CommandQueueContext::GetCompiledFences()
@@ -14,11 +14,6 @@
namespace AZ
{
namespace RHI
{
struct CpuTimingStatistics;
}
namespace DX12
{
class CommandQueueContext
@@ -49,7 +44,7 @@ namespace AZ
RHI::HardwareQueueClass hardwareQueueClass,
const ExecuteWorkRequest& request);
void UpdateCpuTimingStatistics(RHI::CpuTimingStatistics& cpuTimingStatistics) const;
void UpdateCpuTimingStatistics() const;
// Fences across all queues that are compiled by the frame graph compilation phase
const FenceSet& GetCompiledFences();
@@ -184,9 +184,9 @@ namespace AZ
m_stagingMemoryAllocator.ReportMemoryUsage(builder);
}
void Device::UpdateCpuTimingStatisticsInternal(RHI::CpuTimingStatistics& cpuTimingStatistics) const
void Device::UpdateCpuTimingStatisticsInternal() const
{
m_commandQueueContext.UpdateCpuTimingStatistics(cpuTimingStatistics);
m_commandQueueContext.UpdateCpuTimingStatistics();
}
void Device::EndFrameInternal()
+1 -1
View File
@@ -147,7 +147,7 @@ namespace AZ
void ShutdownInternal() override;
void CompileMemoryStatisticsInternal(RHI::MemoryStatisticsBuilder& builder) override;
void UpdateCpuTimingStatisticsInternal(RHI::CpuTimingStatistics& cpuTimingStatistics) const override;
void UpdateCpuTimingStatisticsInternal() const override;
void BeginFrameInternal() override;
void EndFrameInternal() override;
void WaitForIdleInternal() override;
@@ -5,14 +5,15 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Atom/RHI.Reflect/CpuTimingStatistics.h>
#include <AzCore/Debug/EventTrace.h>
#include <RHI/CommandQueue.h>
#include <RHI/Conversions.h>
#include <RHI/Device.h>
#include <RHI/Fence.h>
#include <RHI/SwapChain.h>
#include <AzCore/Debug/Timer.h>
namespace AZ
{
namespace Metal
@@ -115,7 +116,7 @@ namespace AZ
@autoreleasepool
{
AZ_PROFILE_SCOPE(RHI, "ExecuteWork");
AZ_PROFILE_RHI_VARIABLE(m_lastExecuteDuration);
AZ::Debug::ScopedTimer executionTimer(m_lastExecuteDuration);
if (request.m_signalFenceValue > 0)
{
@@ -128,7 +129,7 @@ namespace AZ
}
{
AZ_PROFILE_RHI_VARIABLE(m_lastPresentDuration);
AZ::Debug::ScopedTimer presentTimer(m_lastPresentDuration);
for (RHI::SwapChain* swapChain : request.m_swapChainsToPresent)
{
@@ -8,7 +8,6 @@
#include <AzCore/Debug/EventTrace.h>
#include <Atom/RHI/CommandQueue.h>
#include <Atom/RHI.Reflect/CpuTimingStatistics.h>
#include <RHI/Device.h>
#include <RHI/CommandQueue.h>
#include <RHI/SwapChain.h>
@@ -135,18 +134,23 @@ namespace AZ
m_commandQueues[hardwareQueueIdx]->QueueGpuSignal(fence);
}
}
void CommandQueueContext::UpdateCpuTimingStatistics(RHI::CpuTimingStatistics& cpuTimingStatistics) const
{
cpuTimingStatistics.Reset();
AZStd::sys_time_t presentDuration = 0;
for (const RHI::Ptr<CommandQueue>& commandQueue : m_commandQueues)
void CommandQueueContext::UpdateCpuTimingStatistics() const
{
if (auto statsProfiler = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get(); statsProfiler)
{
cpuTimingStatistics.m_queueStatistics.push_back({ commandQueue->GetName(), commandQueue->GetLastExecuteDuration() });
presentDuration += commandQueue->GetLastPresentDuration();
auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId);
AZStd::sys_time_t presentDuration = 0;
for (const RHI::Ptr<CommandQueue>& commandQueue : m_commandQueues)
{
const AZ::Crc32 commandQueueId(commandQueue->GetName().GetHash());
rhiMetrics.PushSample(commandQueueId, static_cast<double>(commandQueue->GetLastExecuteDuration()));
presentDuration += commandQueue->GetLastPresentDuration();
}
rhiMetrics.PushSample(AZ_CRC_CE("Present"), static_cast<double>(presentDuration));
}
cpuTimingStatistics.m_presentDuration = presentDuration;
}
}
}
@@ -40,7 +40,7 @@ namespace AZ
/// Fences across all queues that are compiled by the frame graph compilation phase
const FenceSet& GetCompiledFences();
void UpdateCpuTimingStatistics(RHI::CpuTimingStatistics& cpuTimingStatistics) const;
void UpdateCpuTimingStatistics() const;
private:
AZStd::array<RHI::Ptr<CommandQueue>, RHI::HardwareQueueClassCount> m_commandQueues;
FenceSet m_compiledFences;
@@ -245,9 +245,9 @@ namespace AZ
{
}
void Device::UpdateCpuTimingStatisticsInternal(RHI::CpuTimingStatistics& cpuTimingStatistics) const
void Device::UpdateCpuTimingStatisticsInternal() const
{
m_commandQueueContext.UpdateCpuTimingStatistics(cpuTimingStatistics);
m_commandQueueContext.UpdateCpuTimingStatistics();
}
void Device::FillFormatsCapabilitiesInternal(FormatCapabilitiesList& formatsCapabilities)
+1 -1
View File
@@ -161,7 +161,7 @@ namespace AZ
RHI::ResultCode InitInternal(RHI::PhysicalDevice& physicalDevice) override;
void ShutdownInternal() override;
void CompileMemoryStatisticsInternal(RHI::MemoryStatisticsBuilder& builder) override;
void UpdateCpuTimingStatisticsInternal(RHI::CpuTimingStatistics& cpuTimingStatistics) const override;
void UpdateCpuTimingStatisticsInternal() const override;
void BeginFrameInternal() override;
void EndFrameInternal() override;
void WaitForIdleInternal() override;
+1 -1
View File
@@ -32,7 +32,7 @@ namespace AZ
RHI::ResultCode InitInternal([[maybe_unused]] RHI::PhysicalDevice& physicalDevice) override { return RHI::ResultCode::Success; }
void ShutdownInternal() override {}
void CompileMemoryStatisticsInternal([[maybe_unused]] RHI::MemoryStatisticsBuilder& builder) override {}
void UpdateCpuTimingStatisticsInternal([[maybe_unused]] RHI::CpuTimingStatistics& cpuTimingStatistics) const override {}
void UpdateCpuTimingStatisticsInternal() const override {}
void BeginFrameInternal() override {}
void EndFrameInternal() override {}
void WaitForIdleInternal() override {}
@@ -5,13 +5,14 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Debug/EventTraceDrillerBus.h>
#include <RHI/CommandList.h>
#include <RHI/CommandQueue.h>
#include <RHI/Conversion.h>
#include <RHI/Device.h>
#include <RHI/SwapChain.h>
#include <Atom/RHI.Reflect/CpuTimingStatistics.h>
#include <AzCore/Debug/Timer.h>
namespace AZ
{
@@ -46,7 +47,7 @@ namespace AZ
QueueCommand([=](void* queue)
{
AZ_PROFILE_SCOPE(RHI, "ExecuteWork");
AZ_PROFILE_RHI_VARIABLE(m_lastExecuteDuration);
AZ::Debug::ScopedTimer executionTimer(m_lastExecuteDuration);
Queue* vulkanQueue = static_cast<Queue*>(queue);
@@ -80,7 +81,7 @@ namespace AZ
}
{
AZ_PROFILE_RHI_VARIABLE(m_lastPresentDuration);
AZ::Debug::ScopedTimer presentTimer(m_lastPresentDuration);
// present the image of the current frame.
for (RHI::SwapChain* swapChain : request.m_swapChainsToPresent)
@@ -13,7 +13,6 @@
#include <RHI/Semaphore.h>
#include <RHI/Conversion.h>
#include <RHI/SwapChain.h>
#include <Atom/RHI.Reflect/CpuTimingStatistics.h>
namespace AZ
{
@@ -363,17 +362,22 @@ namespace AZ
return queueSelection.m_familyIndex != InvalidFamilyIndex;
}
void CommandQueueContext::UpdateCpuTimingStatistics(RHI::CpuTimingStatistics& cpuTimingStatistics) const
void CommandQueueContext::UpdateCpuTimingStatistics() const
{
cpuTimingStatistics.Reset();
AZStd::sys_time_t presentDuration = 0;
for (const RHI::Ptr<CommandQueue>& commandQueue : m_commandQueues)
if (auto statsProfiler = AZ::Interface<AZ::Statistics::StatisticalProfilerProxy>::Get(); statsProfiler)
{
cpuTimingStatistics.m_queueStatistics.push_back({ commandQueue->GetName(), commandQueue->GetLastExecuteDuration() });
presentDuration += commandQueue->GetLastPresentDuration();
auto& rhiMetrics = statsProfiler->GetProfiler(rhiMetricsId);
AZStd::sys_time_t presentDuration = 0;
for (const RHI::Ptr<CommandQueue>& commandQueue : m_commandQueues)
{
const AZ::Crc32 commandQueueId(commandQueue->GetName().GetHash());
rhiMetrics.PushSample(commandQueueId, static_cast<double>(commandQueue->GetLastExecuteDuration()));
presentDuration += commandQueue->GetLastPresentDuration();
}
rhiMetrics.PushSample(AZ_CRC_CE("Present"), static_cast<double>(presentDuration));
}
cpuTimingStatistics.m_presentDuration = presentDuration;
}
}
}
@@ -16,11 +16,6 @@
namespace AZ
{
namespace RHI
{
struct CpuTimingStatistics;
}
namespace Vulkan
{
class Device;
@@ -62,7 +57,7 @@ namespace AZ
AZStd::vector<uint32_t> GetQueueFamilyIndices(const RHI::HardwareQueueClassMask hardwareQueueClassMask) const;
VkPipelineStageFlags GetSupportedPipelineStages(uint32_t queueFamilyIndex) const;
void UpdateCpuTimingStatistics(RHI::CpuTimingStatistics& cpuTimingStatistics) const;
void UpdateCpuTimingStatistics() const;
private:
Descriptor m_descriptor;
@@ -547,9 +547,9 @@ namespace AZ
physicalDevice.CompileMemoryStatistics(builder);
}
void Device::UpdateCpuTimingStatisticsInternal(RHI::CpuTimingStatistics& cpuTimingStatistics) const
void Device::UpdateCpuTimingStatisticsInternal() const
{
m_commandQueueContext.UpdateCpuTimingStatistics(cpuTimingStatistics);
m_commandQueueContext.UpdateCpuTimingStatistics();
}
AZStd::vector<RHI::Format> Device::GetValidSwapChainImageFormats(const RHI::WindowHandle& windowHandle) const
@@ -126,7 +126,7 @@ namespace AZ
void EndFrameInternal() override;
void WaitForIdleInternal() override;
void CompileMemoryStatisticsInternal(RHI::MemoryStatisticsBuilder& builder) override;
void UpdateCpuTimingStatisticsInternal(RHI::CpuTimingStatistics& cpuTimingStatistics) const override;
void UpdateCpuTimingStatisticsInternal() const override;
AZStd::vector<RHI::Format> GetValidSwapChainImageFormats(const RHI::WindowHandle& windowHandle) const override;
AZStd::chrono::microseconds GpuTimestampToMicroseconds(uint64_t gpuTimestamp, RHI::HardwareQueueClass queueClass) const override;
void FillFormatsCapabilitiesInternal(FormatCapabilitiesList& formatsCapabilities) override;
+1 -1
View File
@@ -61,7 +61,7 @@ namespace UnitTest
void EndFrameInternal() override {}
void WaitForIdleInternal() override {}
void CompileMemoryStatisticsInternal(AZ::RHI::MemoryStatisticsBuilder&) override {}
void UpdateCpuTimingStatisticsInternal([[maybe_unused]] AZ::RHI::CpuTimingStatistics& cpuTimingStatistics) const override {}
void UpdateCpuTimingStatisticsInternal() const override {}
AZStd::chrono::microseconds GpuTimestampToMicroseconds([[maybe_unused]] uint64_t gpuTimestamp, [[maybe_unused]] AZ::RHI::HardwareQueueClass queueClass) const override
{
return AZStd::chrono::microseconds();
@@ -9,7 +9,6 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RHI.Reflect/CpuTimingStatistics.h>
#include <Atom/RHI/CpuProfiler.h>
#include <Atom/RPI.Public/Pass/ParentPass.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
@@ -96,10 +95,10 @@ namespace MaterialEditor
ResetStats();
}
const AZ::RHI::CpuTimingStatistics* stats = AZ::RHI::RHISystemInterface::Get()->GetCpuTimingStatistics();
if (stats)
double frameTime = AZ::RHI::RHISystemInterface::Get()->GetCpuFrameTime();
if (frameTime > 0)
{
m_cpuFrameTimeMs.PushSample(stats->GetFrameToFrameTimeMilliseconds());
m_cpuFrameTimeMs.PushSample(frameTime);
}
AZ::RHI::Ptr<AZ::RPI::ParentPass> rootPass = AZ::RPI::PassSystemInterface::Get()->GetRootPass();
@@ -12,17 +12,11 @@
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Math/Random.h>
#include <Atom/RHI.Reflect/CpuTimingStatistics.h>
#include <Atom/RHI/CpuProfiler.h>
namespace AZ
{
namespace RHI
{
struct CpuTimingStatistics;
}
namespace Render
{
//! Stores all the data associated with a row in the table.
@@ -88,11 +82,17 @@ namespace AZ
using GroupRegionName = AZ::RHI::CachedTimeRegion::GroupRegionName;
public:
struct CpuTimingEntry
{
const AZStd::string& m_name;
double m_executeDuration;
};
ImGuiCpuProfiler() = default;
~ImGuiCpuProfiler() = default;
//! Draws the overall CPU profiling window, defaults to the statistical view
void Draw(bool& keepDrawing, const AZ::RHI::CpuTimingStatistics& cpuTimingStatistics);
void Draw(bool& keepDrawing);
private:
static constexpr float RowHeight = 35.0;
@@ -121,11 +121,14 @@ namespace AZ
// Sort the table by a given column, rearranges the pointers in m_tableData.
void SortTable(ImGuiTableSortSpecs* sortSpecs);
// gather the latest timing statistics
void CacheCpuTimingStatistics();
// Get the profiling data from the last frame, only called when the profiler is not paused.
void CollectFrameData();
// Cull old data from internal storage, only called when profiler is not paused.
void CullFrameData(const AZ::RHI::CpuTimingStatistics& currentCpuTimingStatistics);
void CullFrameData();
// Draws a single block onto the timeline into the specified row
void DrawBlock(const TimeRegion& block, u64 targetRow);
@@ -204,7 +207,8 @@ namespace AZ
bool m_enableVisualizer = false;
// Last captured CPU timing statistics
AZ::RHI::CpuTimingStatistics m_cpuTimingStatisticsWhenPause;
AZStd::vector<CpuTimingEntry> m_cpuTimingStatisticsWhenPause;
AZStd::sys_time_t m_frameToFrameTime{};
AZStd::string m_lastCapturedFilePath;
@@ -7,7 +7,6 @@
*/
#include <Atom/Feature/Utils/ProfilingCaptureBus.h>
#include <Atom/RHI.Reflect/CpuTimingStatistics.h>
#include <Atom/RHI/CpuProfiler.h>
#include <Atom/RHI/CpuProfilerImpl.h>
#include <Atom/RPI.Edit/Common/JsonUtils.h>
@@ -16,6 +15,7 @@
#include <AzCore/Casting/lossy_cast.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/JSON/filereadstream.h>
#include <AzCore/Statistics/StatisticalProfilerProxy.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/JsonSerializationResult.h>
#include <AzCore/std/containers/map.h>
@@ -31,7 +31,7 @@ namespace AZ
{
namespace CpuProfilerImGuiHelper
{
inline float TicksToMs(AZStd::sys_time_t ticks)
inline float TicksToMs(double ticks)
{
// Note: converting to microseconds integer before converting to milliseconds float
const AZStd::sys_time_t ticksPerSecond = AZStd::GetTimeTicksPerSecond();
@@ -39,6 +39,11 @@ namespace AZ
return static_cast<float>((ticks * 1000) / (ticksPerSecond / 1000)) / 1000.0f;
}
inline float TicksToMs(AZStd::sys_time_t ticks)
{
return TicksToMs(static_cast<double>(ticks));
}
using DeserializedCpuData = AZStd::vector<RHI::CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializerEntry>;
inline Outcome<DeserializedCpuData, AZStd::string> LoadSavedCpuProfilingStatistics(const AZStd::string& capturePath)
{
@@ -108,7 +113,9 @@ namespace AZ
}
} // namespace CpuProfilerImGuiHelper
inline void ImGuiCpuProfiler::Draw(bool& keepDrawing, const AZ::RHI::CpuTimingStatistics& currentCpuTimingStatistics)
inline void ImGuiCpuProfiler::Draw(bool& keepDrawing)
{
// Cache the value to detect if it was changed by ImGui(user pressed 'x')
const bool cachedShowCpuProfiler = keepDrawing;
@@ -121,10 +128,10 @@ namespace AZ
if (!m_paused)
{
// Update region map and cache the input cpu timing statistics when the profiling is not paused
m_cpuTimingStatisticsWhenPause = currentCpuTimingStatistics;
CacheCpuTimingStatistics();
CollectFrameData();
CullFrameData(currentCpuTimingStatistics);
CullFrameData();
// Only listen to system ticks when the profiler is active
if (!SystemTickBus::Handler::BusIsConnected())
@@ -354,19 +361,12 @@ namespace AZ
{
DrawCommonHeader();
const AZ::RHI::CpuTimingStatistics& cpuTimingStatistics = m_cpuTimingStatisticsWhenPause;
const auto ShowTimeInMs = [](AZStd::sys_time_t duration)
{
ImGui::Text("%.2f ms", CpuProfilerImGuiHelper::TicksToMs(duration));
};
const auto ShowRow = [&ShowTimeInMs](const char* regionLabel, AZStd::sys_time_t duration)
const auto ShowRow = [](const char* regionLabel, double duration)
{
ImGui::Text("%s", regionLabel);
ImGui::NextColumn();
ShowTimeInMs(duration);
ImGui::Text("%.2f ms", CpuProfilerImGuiHelper::TicksToMs(duration));
ImGui::NextColumn();
};
@@ -377,11 +377,9 @@ namespace AZ
ImGui::SetColumnWidth(0, 660.0f);
ImGui::SetColumnWidth(1, 100.0f);
ShowRow("Frame to Frame Time", cpuTimingStatistics.m_frameToFrameTime);
ShowRow("Present Time", cpuTimingStatistics.m_presentDuration);
for (const auto& queueStatistics : cpuTimingStatistics.m_queueStatistics)
for (const auto& queueStatistics : m_cpuTimingStatisticsWhenPause)
{
ShowRow(queueStatistics.m_queueName.GetCStr(), queueStatistics.m_executeDuration);
ShowRow(queueStatistics.m_name.c_str(), queueStatistics.m_executeDuration);
}
ImGui::Separator();
@@ -653,6 +651,32 @@ namespace AZ
ImGui::EndChild();
}
inline void ImGuiCpuProfiler::CacheCpuTimingStatistics()
{
using namespace AZ::Statistics;
m_cpuTimingStatisticsWhenPause.clear();
if (auto statsProfiler = AZ::Interface<StatisticalProfilerProxy>::Get(); statsProfiler)
{
auto& rhiMetrics = statsProfiler->GetProfiler(AZ_CRC_CE("RHI"));
const NamedRunningStatistic* frameTimeMetric = rhiMetrics.GetStatistic(AZ_CRC_CE("Frame to Frame Time"));
if (frameTimeMetric)
{
m_frameToFrameTime = static_cast<AZStd::sys_time_t>(frameTimeMetric->GetMostRecentSample());
}
AZStd::vector<NamedRunningStatistic*> statistics;
rhiMetrics.GetStatsManager().GetAllStatistics(statistics);
for (NamedRunningStatistic* stat : statistics)
{
m_cpuTimingStatisticsWhenPause.push_back({ stat->GetName(), stat->GetMostRecentSample() });
stat->Reset();
}
}
}
inline void ImGuiCpuProfiler::CollectFrameData()
{
// We maintain separate datastores for the visualizer and the statistical view because they require different
@@ -721,10 +745,9 @@ namespace AZ
}
}
inline void ImGuiCpuProfiler::CullFrameData(const AZ::RHI::CpuTimingStatistics& currentCpuTimingStatistics)
inline void ImGuiCpuProfiler::CullFrameData()
{
const AZStd::sys_time_t frameToFrameTime = currentCpuTimingStatistics.m_frameToFrameTime;
const AZStd::sys_time_t deleteBeforeTick = AZStd::GetTimeNowTicks() - frameToFrameTime * m_framesToCollect;
const AZStd::sys_time_t deleteBeforeTick = AZStd::GetTimeNowTicks() - m_frameToFrameTime * m_framesToCollect;
// Remove old frame boundary data
auto firstBoundaryToKeepItr = AZStd::upper_bound(m_frameEndTicks.begin(), m_frameEndTicks.end(), deleteBeforeTick);
@@ -86,11 +86,7 @@ namespace AtomImGuiTools
}
if (m_showCpuProfiler)
{
const AZ::RHI::CpuTimingStatistics* stats = AZ::RHI::RHISystemInterface::Get()->GetCpuTimingStatistics();
if (stats)
{
m_imguiCpuProfiler.Draw(m_showCpuProfiler, *stats);
}
m_imguiCpuProfiler.Draw(m_showCpuProfiler);
}
if (m_showTransientAttachmentProfiler)
{