[development] minor Profiler gem fixes (#5473)

Fixed incorrect frame boundary guess when loading a profile capture
Added some missing default initializers
Removed implicit dependency on Atom timing marker constants
Fixed issue with small visualizer viewport bounds when loading a saved capture

Signed-off-by: AMZN-ScottR 24445312+AMZN-ScottR@users.noreply.github.com
This commit is contained in:
Scott Romero
2021-11-30 08:27:27 -08:00
committed by GitHub
parent 4dd9e94bc3
commit 12efc84632
5 changed files with 57 additions and 23 deletions
@@ -157,6 +157,22 @@ namespace AZ::Statistics
}
}
void GetAllStatistics(AZStd::vector<NamedRunningStatistic*>& stats)
{
for (auto& iter : m_profilers)
{
iter.second.m_profiler.GetStatsManager().GetAllStatistics(stats);
}
}
void GetAllStatisticsOfUnits(AZStd::vector<NamedRunningStatistic*>& stats, const char* units)
{
for (auto& iter : m_profilers)
{
iter.second.m_profiler.GetStatsManager().GetAllStatisticsOfUnits(stats, units);
}
}
private:
struct ProfilerInfo
{
@@ -56,13 +56,25 @@ namespace AZ
void GetAllStatistics(AZStd::vector<NamedRunningStatistic*>& vector)
{
for (auto const& it : m_statistics)
for (const auto& it : m_statistics)
{
NamedRunningStatistic* stat = it.second;
vector.push_back(stat);
}
}
void GetAllStatisticsOfUnits(AZStd::vector<NamedRunningStatistic*>& vector, const char* units)
{
for (const auto& it : m_statistics)
{
NamedRunningStatistic* stat = it.second;
if (stat->GetUnits() == units)
{
vector.push_back(stat);
}
}
}
//! Helper method to apply units to statistics with empty units string.
AZ::u32 ApplyUnits(const AZStd::string& units)
{
+1 -1
View File
@@ -150,7 +150,7 @@ namespace Profiler
AZStd::mutex m_continuousCaptureEndingMutex;
AZStd::atomic_bool m_continuousCaptureInProgress;
AZStd::atomic_bool m_continuousCaptureInProgress = false;
// Stores multiple frames of profiling data, size is controlled by MaxFramesToSave. Flushed when EndContinuousCapture is called.
// Ring buffer so that we can have fast append of new data + removal of old profiling data with good cache locality.
+24 -17
View File
@@ -23,9 +23,13 @@
#include <AzCore/std/sort.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/time.h>
#include <AzCore/Time/ITime.h>
namespace Profiler
{
constexpr AZStd::sys_time_t ProfilerViewEdgePadding = 5000;
constexpr size_t InitialCpuTimingStatsAllocation = 8;
namespace CpuProfilerImGuiHelper
{
float TicksToMs(double ticks)
@@ -435,6 +439,11 @@ namespace Profiler
m_tableData.clear();
m_groupRegionMap.clear();
// Since we don't serialize the frame boundaries, we will use "Component application simulation tick" from
// ComponentApplication::Tick as a heuristic.
static const AZ::Name::Hash frameBoundaryHash = AZ::Name("Component application simulation tick").GetHash();
AZStd::sys_time_t frameTime = 0;
for (const auto& entry : deserializedData)
{
const auto [groupNameItr, wasGroupNameInserted] = m_deserializedStringPool.emplace(entry.m_groupName.GetCStr());
@@ -445,10 +454,12 @@ namespace Profiler
const CachedTimeRegion newRegion(*groupRegionNameItr, entry.m_stackDepth, entry.m_startTick, entry.m_endTick);
m_savedData[entry.m_threadId].push_back(newRegion);
// Since we don't serialize the frame boundaries, we need to use the RPI's OnSystemTick event as a heuristic.
const static AZ::Name frameBoundaryName = AZ::Name("RPISystem: OnSystemTick");
if (entry.m_regionName == frameBoundaryName)
if (entry.m_regionName.GetHash() == frameBoundaryHash)
{
if (!m_frameEndTicks.empty())
{
frameTime = entry.m_endTick - m_frameEndTicks.back();
}
m_frameEndTicks.push_back(entry.m_endTick);
}
@@ -462,9 +473,9 @@ namespace Profiler
m_groupRegionMap[*groupNameItr][*regionNameItr].RecordRegion(newRegion, entry.m_threadId);
}
// Update viewport bounds with some added UX fudge factor
m_viewportStartTick = deserializedData.back().m_startTick - 1000;
m_viewportEndTick = deserializedData.back().m_endTick + 1000;
// Update viewport bounds to the estimated final frame time with some padding
m_viewportStartTick = m_frameEndTicks.back() - frameTime - ProfilerViewEdgePadding;
m_viewportEndTick = m_frameEndTicks.back() + ProfilerViewEdgePadding;
// Invariant: each vector in m_savedData must be sorted so that we can efficiently cull region data.
for (auto& [threadId, singleThreadData] : m_savedData)
@@ -642,21 +653,13 @@ namespace Profiler
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);
statistics.reserve(InitialCpuTimingStatsAllocation);
statsProfiler->GetAllStatisticsOfUnits(statistics, "clocks");
for (NamedRunningStatistic* stat : statistics)
{
m_cpuTimingStatisticsWhenPause.push_back({ stat->GetName(), stat->GetMostRecentSample() });
stat->Reset();
}
}
}
@@ -731,7 +734,11 @@ namespace Profiler
void ImGuiCpuProfiler::CullFrameData()
{
const AZStd::sys_time_t deleteBeforeTick = AZStd::GetTimeNowTicks() - m_frameToFrameTime * m_framesToCollect;
const AZ::TimeUs delta = AZ::GetRealTickDeltaTimeUs();
const float deltaTimeInSeconds = AZ::TimeUsToSeconds(delta);
const AZStd::sys_time_t frameToFrameTime = static_cast<AZStd::sys_time_t>(deltaTimeInSeconds * AZStd::GetTimeTicksPerSecond());
const AZStd::sys_time_t deleteBeforeTick = AZStd::GetTimeNowTicks() - frameToFrameTime * m_framesToCollect;
// Remove old frame boundary data
auto firstBoundaryToKeepItr = AZStd::upper_bound(m_frameEndTicks.begin(), m_frameEndTicks.end(), deleteBeforeTick);
+3 -4
View File
@@ -89,7 +89,7 @@ namespace Profiler
struct CpuTimingEntry
{
const AZStd::string& m_name;
double m_executeDuration;
double m_executeDuration = 0;
};
ImGuiCpuProfiler() = default;
@@ -175,8 +175,8 @@ namespace Profiler
AZ::u64 m_savedRegionCount = 0;
// Viewport tick bounds, these are used to convert tick space -> screen space and cull so we only draw onscreen objects
AZStd::sys_time_t m_viewportStartTick;
AZStd::sys_time_t m_viewportEndTick;
AZStd::sys_time_t m_viewportStartTick = 0;
AZStd::sys_time_t m_viewportEndTick = 0;
// Map to store each thread's TimeRegions, individual vectors are sorted by start tick
// note: we use size_t as a proxy for thread_id because native_thread_id_type differs differs from
@@ -215,7 +215,6 @@ namespace Profiler
// Last captured CPU timing statistics
AZStd::vector<CpuTimingEntry> m_cpuTimingStatisticsWhenPause;
AZStd::sys_time_t m_frameToFrameTime{};
AZ::IO::FixedMaxPath m_lastCapturedFilePath;