[development] added option to control update frequency in profiler visualizer (#7705)
resolves #4365 Increased the initial profiler ImGui window size Updated thread render order so the main thread is always on top Signed-off-by: AMZN-ScottR 24445312+AMZN-ScottR@users.noreply.github.com
This commit is contained in:
@@ -24,13 +24,17 @@
|
||||
#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;
|
||||
|
||||
constexpr int MinSavableFrameCount = 30; // 1 second @ 30 fps
|
||||
constexpr int MaxSavableFrameCount = 2000;
|
||||
|
||||
constexpr int MaxUpdateFrequencyMs = 2000; // 2 seconds
|
||||
|
||||
namespace CpuProfilerImGuiHelper
|
||||
{
|
||||
float TicksToMs(double ticks)
|
||||
@@ -116,12 +120,18 @@ namespace Profiler
|
||||
}
|
||||
} // namespace CpuProfilerImGuiHelper
|
||||
|
||||
ImGuiCpuProfiler::ImGuiCpuProfiler()
|
||||
{
|
||||
// thread IDs are hashed internally to unify display across platforms
|
||||
m_mainThreadId = AZStd::hash<AZStd::thread_id>{}(AZStd::this_thread::get_id());
|
||||
}
|
||||
|
||||
void ImGuiCpuProfiler::Draw(bool& keepDrawing)
|
||||
{
|
||||
// Cache the value to detect if it was changed by ImGui(user pressed 'x')
|
||||
const bool cachedShowCpuProfiler = keepDrawing;
|
||||
|
||||
const ImVec2 windowSize(900.0f, 600.0f);
|
||||
const ImVec2 windowSize(1280.0f, 720.0f);
|
||||
ImGui::SetNextWindowSize(windowSize, ImGuiCond_Once);
|
||||
if (ImGui::Begin("CPU Profiler", &keepDrawing, ImGuiWindowFlags_None))
|
||||
{
|
||||
@@ -491,6 +501,7 @@ namespace Profiler
|
||||
}
|
||||
|
||||
// -- CPU Visualizer --
|
||||
|
||||
void ImGuiCpuProfiler::DrawVisualizer()
|
||||
{
|
||||
DrawCommonHeader();
|
||||
@@ -499,9 +510,19 @@ namespace Profiler
|
||||
if (ImGui::BeginChild("Options and Statistics", { 0, 0 }, true))
|
||||
{
|
||||
ImGui::Columns(3, "Options", true);
|
||||
ImGui::SliderInt("Saved Frames", &m_framesToCollect, 10, 20000, "%d", ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_Logarithmic);
|
||||
|
||||
ImGui::SliderInt("Update Freq. (ms)", &m_updateFrequencyMs, 0, MaxUpdateFrequencyMs, "%d", ImGuiSliderFlags_AlwaysClamp);
|
||||
ImGui::SliderInt("Saved Frames", &m_framesToCollect, MinSavableFrameCount, MaxSavableFrameCount, "%d", ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_Logarithmic);
|
||||
m_visualizerHighlightFilter.Draw("Find Region");
|
||||
|
||||
// estimate the number of frames required to fulfill the update frequency
|
||||
const AZ::TimeMs deltaMs = AZ::TimeUsToMs(AZ::GetRealTickDeltaTimeUs());
|
||||
const int estimatedFrameCountPadding = 5; // padding is necessary to prevent flashes of blank frames
|
||||
const int estimatedFrameCount = aznumeric_cast<int >(AZ::TimeMs{ m_updateFrequencyMs } / deltaMs) + estimatedFrameCountPadding;
|
||||
|
||||
// bump the number of saved frames to the update frequency estimate to prevent periods of empty data
|
||||
m_framesToCollect = AZStd::max(m_framesToCollect, estimatedFrameCount);
|
||||
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::Text("Viewport width: %.3f ms", CpuProfilerImGuiHelper::TicksToMs(GetViewportTickWidth()));
|
||||
@@ -555,24 +576,25 @@ namespace Profiler
|
||||
|
||||
// Main draw loop
|
||||
AZ::u64 baseRow = 0;
|
||||
for (const auto& [currentThreadId, singleThreadData] : m_savedData)
|
||||
|
||||
auto drawThreadDataFunc = [&](size_t threadId, const AZStd::vector<TimeRegion>& threadData)
|
||||
{
|
||||
// Find the first TimeRegion that we should draw
|
||||
auto regionItr = AZStd::lower_bound(
|
||||
singleThreadData.begin(), singleThreadData.end(), *startTickItr,
|
||||
threadData.begin(), threadData.end(), *startTickItr,
|
||||
[](const TimeRegion& wrapper, AZStd::sys_time_t target)
|
||||
{
|
||||
return wrapper.m_startTick < target;
|
||||
});
|
||||
|
||||
if (regionItr == singleThreadData.end())
|
||||
if (regionItr == threadData.end())
|
||||
{
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw all of the blocks for a given thread/row
|
||||
AZ::u64 maxDepth = 0;
|
||||
while (regionItr != singleThreadData.end())
|
||||
while (regionItr != threadData.end())
|
||||
{
|
||||
const TimeRegion& region = *regionItr;
|
||||
|
||||
@@ -590,10 +612,20 @@ namespace Profiler
|
||||
}
|
||||
|
||||
// Draw UI details
|
||||
DrawThreadLabel(baseRow, currentThreadId);
|
||||
DrawThreadLabel(baseRow, threadId);
|
||||
DrawThreadSeparator(baseRow, maxDepth);
|
||||
|
||||
baseRow += maxDepth + 1; // Next draw loop should start one row down
|
||||
};
|
||||
|
||||
// keep the main thread at the top
|
||||
drawThreadDataFunc(m_mainThreadId, m_savedData[m_mainThreadId]);
|
||||
for (const auto& [threadId, threadData] : m_savedData)
|
||||
{
|
||||
if (threadId != m_mainThreadId)
|
||||
{
|
||||
drawThreadDataFunc(threadId, threadData);
|
||||
}
|
||||
}
|
||||
|
||||
DrawFrameBoundaries();
|
||||
@@ -678,8 +710,8 @@ namespace Profiler
|
||||
// Get the latest TimeRegionMap
|
||||
const CpuProfiler::TimeRegionMap& timeRegionMap = CpuProfiler::Get()->GetTimeRegionMap();
|
||||
|
||||
m_viewportStartTick = AZStd::numeric_limits<AZ::s64>::max();
|
||||
m_viewportEndTick = AZStd::numeric_limits<AZ::s64>::lowest();
|
||||
AZ::s64 viewportStartTick = AZStd::numeric_limits<AZ::s64>::max();
|
||||
AZ::s64 viewportEndTick = AZStd::numeric_limits<AZ::s64>::lowest();
|
||||
|
||||
// Iterate through the entire TimeRegionMap and copy the data since it will get deleted on the next frame
|
||||
for (const auto& [threadId, singleThreadRegionMap] : timeRegionMap)
|
||||
@@ -724,8 +756,8 @@ namespace Profiler
|
||||
});
|
||||
|
||||
// Use the latest frame's data as the new bounds of the viewport
|
||||
m_viewportStartTick = AZStd::min(newVisualizerData.front().m_startTick, m_viewportStartTick);
|
||||
m_viewportEndTick = AZStd::max(newVisualizerData.back().m_endTick, m_viewportEndTick);
|
||||
viewportStartTick = AZStd::min(newVisualizerData.front().m_startTick, viewportStartTick);
|
||||
viewportEndTick = AZStd::max(newVisualizerData.back().m_endTick, viewportEndTick);
|
||||
|
||||
m_savedRegionCount += newVisualizerData.size();
|
||||
|
||||
@@ -734,6 +766,16 @@ namespace Profiler
|
||||
savedDataVec.insert(
|
||||
savedDataVec.end(), AZStd::make_move_iterator(newVisualizerData.begin()), AZStd::make_move_iterator(newVisualizerData.end()));
|
||||
}
|
||||
|
||||
// only update the viewport bounds at the specified frequency
|
||||
m_currentUpdateTimeMs += AZ::TimeUsToMs(AZ::GetRealTickDeltaTimeUs());
|
||||
if (m_currentUpdateTimeMs >= static_cast<AZ::TimeMs>(m_updateFrequencyMs))
|
||||
{
|
||||
m_currentUpdateTimeMs = AZ::TimeMs{ 0 };
|
||||
|
||||
m_viewportStartTick = viewportStartTick;
|
||||
m_viewportEndTick = viewportEndTick;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiCpuProfiler::CullFrameData()
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <AzCore/std/containers/map.h>
|
||||
#include <AzCore/std/containers/set.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
#include <AzCore/Time/ITime.h>
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
@@ -92,7 +93,7 @@ namespace Profiler
|
||||
double m_executeDuration = 0;
|
||||
};
|
||||
|
||||
ImGuiCpuProfiler() = default;
|
||||
ImGuiCpuProfiler();
|
||||
~ImGuiCpuProfiler() = default;
|
||||
|
||||
//! Draws the overall CPU profiling window, defaults to the statistical view
|
||||
@@ -100,7 +101,8 @@ namespace Profiler
|
||||
|
||||
private:
|
||||
static constexpr float RowHeight = 35.0f;
|
||||
static constexpr int DefaultFramesToCollect = 50;
|
||||
static constexpr int DefaultFramesToCollect = 60; // 1 second @ 60 fps
|
||||
static constexpr int DefaultUpdateFrequencyMs = 1000; // 1 second
|
||||
static constexpr float MediumFrameTimeLimit = 16.6f; // 60 fps
|
||||
static constexpr float HighFrameTimeLimit = 33.3f; // 30 fps
|
||||
|
||||
@@ -169,6 +171,9 @@ namespace Profiler
|
||||
|
||||
// --- Visualizer Members ---
|
||||
|
||||
int m_updateFrequencyMs = DefaultUpdateFrequencyMs;
|
||||
AZ::TimeMs m_currentUpdateTimeMs = AZ::TimeMs{ 0 };
|
||||
|
||||
int m_framesToCollect = DefaultFramesToCollect;
|
||||
|
||||
// Tally of the number of saved profiling events so far
|
||||
@@ -182,6 +187,7 @@ namespace Profiler
|
||||
// note: we use size_t as a proxy for thread_id because native_thread_id_type differs differs from
|
||||
// platform to platform, which causes problems when deserializing saved captures.
|
||||
AZStd::unordered_map<size_t, AZStd::vector<TimeRegion>> m_savedData;
|
||||
size_t m_mainThreadId = 0;
|
||||
|
||||
// Region color cache
|
||||
AZStd::unordered_map<GroupRegionName, ImVec4, CachedTimeRegion::GroupRegionName::Hash> m_regionColorMap;
|
||||
|
||||
Reference in New Issue
Block a user