From bd00867fe624303c7f823e0c5551c6762efae858 Mon Sep 17 00:00:00 2001 From: Jacob Hilliard Date: Wed, 21 Jul 2021 11:57:23 -0700 Subject: [PATCH] Visualizer: Implement thread hovering tooltip Signed-off-by: Jacob Hilliard --- .../Include/Atom/Utils/ImGuiCpuProfiler.h | 12 ++++-- .../Include/Atom/Utils/ImGuiCpuProfiler.inl | 41 +++++++++++++++---- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h index 695ad7e332..d21783a801 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -33,16 +34,17 @@ namespace AZ struct TableRow { - void RecordRegion(const AZ::RHI::CachedTimeRegion& region); + void RecordRegion(const AZ::RHI::CachedTimeRegion& region, AZStd::thread_id threadId); double GetAverageInvocationsPerFrame() const; - - static u64 ms_frames; + AZStd::string TableRow::GetExecutingThreadsLabel() const; AZStd::string m_groupName; AZStd::string m_regionName; AZStd::sys_time_t m_maxTicks; AZStd::sys_time_t m_runningAverageTicks; u64 m_invocations; + + AZStd::set m_executingThreads; }; //! Visual profiler for Cpu statistics. @@ -52,6 +54,8 @@ namespace AZ class ImGuiCpuProfiler : SystemTickBus::Handler { + friend struct TableRow; + // Region Name -> Array of ThreadRegion entries using RegionEntryMap = AZStd::map; // Group Name -> RegionEntryMap @@ -79,6 +83,8 @@ namespace AZ static constexpr float MediumFrameTimeLimit = 16.6; // 60 fps static constexpr float HighFrameTimeLimit = 33.3; // 30 fps + static u64 ms_framesActive; + // Draw the shared header between the two windows void DrawCommonHeader(); diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl index bb470f39e8..124e48af13 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiCpuProfiler.inl @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -25,7 +26,7 @@ namespace AZ { namespace Render { - inline u64 TableRow::ms_frames = 0; + inline u64 ImGuiCpuProfiler::ms_framesActive = 0; namespace CpuProfilerImGuiHelper { @@ -41,6 +42,7 @@ namespace AZ { return AZStd::string::format("Thread: %zu", static_cast(threadId)); } + inline float TicksToMs(AZStd::sys_time_t ticks) { // Note: converting to microseconds integer before converting to milliseconds float @@ -170,6 +172,7 @@ namespace AZ } ImGui::Text(statistics->m_groupName.c_str()); + const ImVec2 topLeftBound = ImGui::GetItemRectMin(); ImGui::TableNextColumn(); ImGui::Text(statistics->m_regionName.c_str()); @@ -182,7 +185,17 @@ namespace AZ ImGui::TableNextColumn(); ImGui::Text("%.1f", statistics->GetAverageInvocationsPerFrame()); + const ImVec2 botRightBound = ImGui::GetItemRectMax(); ImGui::TableNextColumn(); + + // NOTE: we are manually checking the bounds rather than using ImGui::IsItemHovered + Begin/EndGroup because + // ImGui reports incorrect bounds when using Begin/End group in the Tables API. + if (ImGui::IsMouseHoveringRect(topLeftBound, botRightBound, false)) + { + ImGui::BeginTooltip(); + ImGui::Text(statistics->GetExecutingThreadsLabel().c_str()); + ImGui::EndTooltip(); + } } } ImGui::EndTable(); @@ -275,7 +288,7 @@ namespace AZ { m_tableData.clear(); m_groupRegionMap.clear(); - TableRow::ms_frames = 0; + ImGuiCpuProfiler::ms_framesActive = 0; } DrawTable(); @@ -446,8 +459,8 @@ namespace AZ // Get the latest TimeRegionMap const RHI::CpuProfiler::TimeRegionMap& timeRegionMap = RHI::CpuProfiler::Get()->GetTimeRegionMap(); - m_viewportStartTick = INT64_MAX; - m_viewportEndTick = INT64_MIN; + m_viewportStartTick = AZStd::numeric_limits::max(); + m_viewportEndTick = AZStd::numeric_limits::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) @@ -477,7 +490,7 @@ namespace AZ m_tableData.push_back(&m_groupRegionMap[groupName][regionName]); } - m_groupRegionMap[groupName][regionName].RecordRegion(region); + m_groupRegionMap[groupName][regionName].RecordRegion(region, threadId); } } @@ -842,13 +855,13 @@ namespace AZ else { m_frameEndTicks.push_back(AZStd::GetTimeNowTicks()); - TableRow::ms_frames++; + ImGuiCpuProfiler::ms_framesActive++; } } // ---- TableRow impl ---- - inline void TableRow::RecordRegion(const AZ::RHI::CachedTimeRegion& region) + inline void TableRow::RecordRegion(const AZ::RHI::CachedTimeRegion& region, AZStd::thread_id threadId) { m_invocations++; const AZStd::sys_time_t deltaTime = AZStd::abs(region.m_endTick - region.m_startTick); @@ -857,11 +870,23 @@ namespace AZ // Standard running average algorithm const auto newMean = m_runningAverageTicks + aznumeric_cast((deltaTime - m_runningAverageTicks) * 1.0 / m_invocations); m_runningAverageTicks = newMean; + + m_executingThreads.insert(threadId); } inline double TableRow::GetAverageInvocationsPerFrame() const { - return 1.0 * m_invocations / ms_frames; + return 1.0 * m_invocations / ImGuiCpuProfiler::ms_framesActive; + } + + inline AZStd::string TableRow::GetExecutingThreadsLabel() const + { + AZStd::string threadString; + for (const auto& threadId : m_executingThreads) + { + threadString.append(CpuProfilerImGuiHelper::TextThreadId(threadId.m_id) + ", "); + } + return threadString; } } // namespace Render } // namespace AZ