diff --git a/.gitignore b/.gitignore index 24af068c53..eb24d72701 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ _savebackup/ #Output folder for test results when running Automated Tests TestResults/** *.swatches +/imgui.ini diff --git a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp index 38c30d67a6..9cc98a15ec 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp @@ -186,7 +186,7 @@ namespace AZ { for (const RPI::Pass* pass : passes) { - m_timestampEntries.push_back({ pass->GetName(), pass->GetTimestampResult().GetTimestampInNanoseconds() }); + m_timestampEntries.push_back({pass->GetName(), pass->GetLatestTimestampResult().GetDurationInNanoseconds()}); } } @@ -223,7 +223,7 @@ namespace AZ { for (const RPI::Pass* pass : passes) { - m_pipelineStatisticsEntries.push_back({ pass->GetName(), pass->GetPipelineStatisticsResult() }); + m_pipelineStatisticsEntries.push_back({pass->GetName(), pass->GetLatestPipelineStatisticsResult()}); } } diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/GpuQuery/GpuQueryTypes.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/GpuQuery/GpuQueryTypes.h index 80a72aaf55..ae86557170 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/GpuQuery/GpuQueryTypes.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/GpuQuery/GpuQueryTypes.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include @@ -43,15 +44,19 @@ namespace AZ { public: TimestampResult() = default; - TimestampResult(uint64_t timestampInTicks); - TimestampResult(uint64_t timestampQueryResultLow, uint64_t timestampQueryResultHigh); - TimestampResult(AZStd::array_view&& timestampResultArray); + TimestampResult(uint64_t beginTick, uint64_t endTick, RHI::HardwareQueueClass hardwareQueueClass); - uint64_t GetTimestampInNanoseconds() const; - uint64_t GetTimestampInTicks() const; + uint64_t GetDurationInNanoseconds() const; + uint64_t GetDurationInTicks() const; + uint64_t GetTimestampBeginInTicks() const; + + void Add(const TimestampResult& extent); private: - uint64_t m_timestampInTicks = 0u; + // the timestamp of begin and duration in ticks. + uint64_t m_begin = 0; + uint64_t m_duration = 0; + RHI::HardwareQueueClass m_hardwareQueueClass = RHI::HardwareQueueClass::Graphics; }; //! The structure that is used to read back the results form the PipelineStatistics queries diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h index 9317158437..b4f63a7099 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h @@ -122,7 +122,6 @@ namespace AZ private: // RPI::Pass overrides... - TimestampResult GetTimestampResultInternal() const override; PipelineStatisticsResult GetPipelineStatisticsResultInternal() const override; // --- Hierarchy related functions --- diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h index ae249a5f45..b0d6bd4117 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h @@ -211,11 +211,11 @@ namespace AZ //! Prints the pass virtual void DebugPrint() const; - //! Return the Timestamp result of this pass - TimestampResult GetTimestampResult() const; + //! Return the latest Timestamp result of this pass + TimestampResult GetLatestTimestampResult() const; - //! Return the PipelineStatistic result of this pass - PipelineStatisticsResult GetPipelineStatisticsResult() const; + //! Return the latest PipelineStatistic result of this pass + PipelineStatisticsResult GetLatestPipelineStatisticsResult() const; //! Enables/Disables Timestamp queries for this pass virtual void SetTimestampQueryEnabled(bool enable); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQueryTypes.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQueryTypes.cpp index a1b2ece4dc..715964651f 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQueryTypes.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/GpuQuery/GpuQueryTypes.cpp @@ -21,41 +21,39 @@ namespace AZ namespace RPI { // --- TimestampResult --- - - TimestampResult::TimestampResult(uint64_t timestampInTicks) + TimestampResult::TimestampResult(uint64_t beginTick, uint64_t endTick, RHI::HardwareQueueClass hardwareQueueClass) { - m_timestampInTicks = timestampInTicks; + AZ_Assert(endTick >= beginTick, "TimestampResult: bad inputs"); + m_begin = beginTick; + m_duration = endTick - beginTick; + m_hardwareQueueClass = hardwareQueueClass; } - TimestampResult::TimestampResult(uint64_t timestampQueryResultLow, uint64_t timestampQueryResultHigh) - { - const uint64_t low = AZStd::min(timestampQueryResultLow, timestampQueryResultHigh); - const uint64_t high = AZStd::max(timestampQueryResultLow, timestampQueryResultHigh); - - m_timestampInTicks = high - low; - } - - TimestampResult::TimestampResult(AZStd::array_view&& timestampResultArray) - { - // Loop through all the child passes, and accumulate all the timestampTicks - for (const TimestampResult& timestampResult : timestampResultArray) - { - m_timestampInTicks += timestampResult.m_timestampInTicks; - } - } - - uint64_t TimestampResult::GetTimestampInNanoseconds() const + uint64_t TimestampResult::GetDurationInNanoseconds() const { const RHI::Ptr device = RHI::GetRHIDevice(); - const AZStd::chrono::microseconds timeInMicroseconds = device->GpuTimestampToMicroseconds(m_timestampInTicks, RHI::HardwareQueueClass::Graphics); + const AZStd::chrono::microseconds timeInMicroseconds = device->GpuTimestampToMicroseconds(m_duration, m_hardwareQueueClass); const auto timeInNanoseconds = AZStd::chrono::nanoseconds(timeInMicroseconds); return static_cast(timeInNanoseconds.count()); } - uint64_t TimestampResult::GetTimestampInTicks() const + uint64_t TimestampResult::GetDurationInTicks() const { - return m_timestampInTicks; + return m_duration; + } + + uint64_t TimestampResult::GetTimestampBeginInTicks() const + { + return m_begin; + } + + void TimestampResult::Add(const TimestampResult& extent) + { + uint64_t end1 = m_begin + m_duration; + uint64_t end2 = extent.m_begin + extent.m_duration; + m_begin = m_begin < extent.m_begin ? m_begin : extent.m_begin; + m_duration = (end1 > end2 ? end1 : end2) - m_begin; } // --- PipelineStatisticsResult --- diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp index 6314ae376e..106ef82bf1 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp @@ -393,19 +393,6 @@ namespace AZ } } - TimestampResult ParentPass::GetTimestampResultInternal() const - { - AZStd::vector timestampResultArray; - timestampResultArray.reserve(m_children.size()); - - // Calculate the Timestamp result by summing all of its child's TimestampResults - for (const Ptr& childPass : m_children) - { - timestampResultArray.emplace_back(childPass->GetTimestampResult()); - } - return TimestampResult(timestampResultArray); - } - PipelineStatisticsResult ParentPass::GetPipelineStatisticsResultInternal() const { AZStd::vector pipelineStatisticsResultArray; @@ -414,7 +401,7 @@ namespace AZ // Calculate the PipelineStatistics result by summing all of its child's PipelineStatistics for (const Ptr& childPass : m_children) { - pipelineStatisticsResultArray.emplace_back(childPass->GetPipelineStatisticsResult()); + pipelineStatisticsResultArray.emplace_back(childPass->GetLatestPipelineStatisticsResult()); } return PipelineStatisticsResult(pipelineStatisticsResultArray); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp index 5ecf293efe..9401d1a9e0 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -1273,24 +1273,14 @@ namespace AZ } } - TimestampResult Pass::GetTimestampResult() const + TimestampResult Pass::GetLatestTimestampResult() const { - if (IsEnabled() && IsTimestampQueryEnabled()) - { - return GetTimestampResultInternal(); - } - - return TimestampResult(); + return GetTimestampResultInternal(); } - PipelineStatisticsResult Pass::GetPipelineStatisticsResult() const + PipelineStatisticsResult Pass::GetLatestPipelineStatisticsResult() const { - if (IsEnabled() && IsPipelineStatisticsQueryEnabled()) - { - return GetPipelineStatisticsResultInternal(); - } - - return PipelineStatisticsResult(); + return GetPipelineStatisticsResultInternal(); } TimestampResult Pass::GetTimestampResultInternal() const diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp index 19a4f3d302..1fad385fa6 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp @@ -539,7 +539,7 @@ namespace AZ const uint32_t TimestampResultQueryCount = 2u; uint64_t timestampResult[TimestampResultQueryCount] = {0}; query->GetLatestResult(×tampResult, sizeof(uint64_t) * TimestampResultQueryCount); - m_timestampResult = TimestampResult(timestampResult[0], timestampResult[1]); + m_timestampResult = TimestampResult(timestampResult[0], timestampResult[1], RHI::HardwareQueueClass::Graphics); }); ExecuteOnPipelineStatisticsQuery([this](RHI::Ptr query) diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/PerformanceMonitorComponent.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/PerformanceMonitorComponent.cpp index afe3cc6fb6..513bedb55f 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/PerformanceMonitorComponent.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/PerformanceMonitorComponent.cpp @@ -109,8 +109,8 @@ namespace MaterialEditor AZ::RHI::Ptr rootPass = AZ::RPI::PassSystemInterface::Get()->GetRootPass(); if (rootPass) { - AZ::RPI::TimestampResult timestampResult = rootPass->GetTimestampResult(); - double gpuFrameTimeMs = aznumeric_cast(timestampResult.GetTimestampInNanoseconds()) / 1000000; + AZ::RPI::TimestampResult timestampResult = rootPass->GetLatestTimestampResult(); + double gpuFrameTimeMs = aznumeric_cast(timestampResult.GetDurationInNanoseconds()) / 1000000; m_gpuFrameTimeMs.PushSample(gpuFrameTimeMs); } } diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.h b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.h index 8936f15e55..6520844edd 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.h +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.h @@ -93,7 +93,9 @@ namespace AZ ImGuiPipelineStatisticsView(); //! Draw the PipelineStatistics window. - void DrawPipelineStatisticsWindow(bool& draw, const PassEntry* rootPassEntry, AZStd::unordered_map& m_timestampEntryDatabase); + void DrawPipelineStatisticsWindow(bool& draw, const PassEntry* rootPassEntry, + AZStd::unordered_map& m_timestampEntryDatabase, + AZ::RHI::Ptr rootPass); //! Total number of columns (Attribute columns + PassName column). static const uint32_t HeaderAttributeCount = PassEntry::PipelineStatisticsAttributeCount + 1u; @@ -139,6 +141,9 @@ namespace AZ // ImGui filter used to filter passes by the user's input. ImGuiTextFilter m_passFilter; + + // Pause and showing the pipeline statistics result when it's paused. + bool m_paused = false; }; class ImGuiTimestampView @@ -180,9 +185,19 @@ namespace AZ Count }; + // Timestamp refresh type . + enum class RefreshType : int32_t + { + Realtime = 0, + OncePerSecond, + Count + }; + public: //! Draw the Timestamp window. - void DrawTimestampWindow(bool& draw, const PassEntry* rootPassEntry, AZStd::unordered_map& m_timestampEntryDatabase); + void DrawTimestampWindow(bool& draw, const PassEntry* rootPassEntry, + AZStd::unordered_map& m_timestampEntryDatabase, + AZ::RHI::Ptr rootPass); private: // Draw option for the hierarchical view of the passes. @@ -223,6 +238,20 @@ namespace AZ // ImGui filter used to filter passes. ImGuiTextFilter m_passFilter; + + // Pause and showing the timestamp result when it's paused. + bool m_paused = false; + + // Hide non-parent passes which has 0 execution time. + bool m_hideZeroPasses = false; + + // Show pass execution timeline + bool m_showTimeline = false; + + // Controls how often the timestamp data is refreshed + RefreshType m_refreshType = RefreshType::OncePerSecond; + AZStd::sys_time_t m_lastUpdateTimeMicroSecond; + }; class ImGuiGpuProfiler diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl index a2b29f3fb7..c00533f740 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiGpuProfiler.inl @@ -105,9 +105,9 @@ namespace AZ // [GFX TODO][ATOM-4001] Cache the timestamp and PipelineStatistics results. // Get the query results from the passes. - m_timestampResult = pass->GetTimestampResult(); + m_timestampResult = pass->GetLatestTimestampResult(); - const RPI::PipelineStatisticsResult rps = pass->GetPipelineStatisticsResult(); + const RPI::PipelineStatisticsResult rps = pass->GetLatestPipelineStatisticsResult(); m_pipelineStatistics = { rps.m_vertexCount, rps.m_primitiveCount, rps.m_vertexShaderInvocationCount, rps.m_rasterizedPrimitiveCount, rps.m_renderedPrimitiveCount, rps.m_pixelShaderInvocationCount, rps.m_computeShaderInvocationCount }; @@ -153,7 +153,9 @@ namespace AZ } - inline void ImGuiPipelineStatisticsView::DrawPipelineStatisticsWindow(bool& draw, const PassEntry* rootPassEntry, AZStd::unordered_map& passEntryDatabase) + inline void ImGuiPipelineStatisticsView::DrawPipelineStatisticsWindow(bool& draw, + const PassEntry* rootPassEntry, AZStd::unordered_map& passEntryDatabase, + AZ::RHI::Ptr rootPass) { // Early out if nothing is supposed to be drawn if (!draw) @@ -188,12 +190,6 @@ namespace AZ continue; } - // Filter out disabled passes for the PipelineStatistics window if necessary. - if (!m_showDisabledPasses && !passEntry.IsPipelineStatisticsEnabled()) - { - continue; - } - // Filter out parent passes if necessary. if (!m_showParentPasses && passEntry.m_isParent) { @@ -230,6 +226,13 @@ namespace AZ // Start drawing the PipelineStatistics window. if (ImGui::Begin("PipelineStatistics Window", &draw, ImGuiWindowFlags_NoResize)) { + // Pause/unpause the profiling + if (ImGui::Button(m_paused ? "Resume" : "Pause")) + { + m_paused = !m_paused; + rootPass->SetPipelineStatisticsQueryEnabled(!m_paused); + } + ImGui::Columns(2, "HeaderColumns"); // Draw the statistics of the RootPass. @@ -426,23 +429,16 @@ namespace AZ } AZStd::string label; - if (passEntry->IsPipelineStatisticsEnabled()) + if (rootEntry && m_showAttributeContribution) { - if (rootEntry && m_showAttributeContribution) - { - label = AZStd::string::format("%llu (%u%%)", - static_cast(passEntry->m_pipelineStatistics[attributeIdx]), - static_cast(normalized * 100.0f)); - } - else - { - label = AZStd::string::format("%llu", - static_cast(passEntry->m_pipelineStatistics[attributeIdx])); - } + label = AZStd::string::format("%llu (%u%%)", + static_cast(passEntry->m_pipelineStatistics[attributeIdx]), + static_cast(normalized * 100.0f)); } else { - label = "-"; + label = AZStd::string::format("%llu", + static_cast(passEntry->m_pipelineStatistics[attributeIdx])); } if (rootEntry) @@ -523,7 +519,9 @@ namespace AZ // --- ImGuiTimestampView --- - inline void ImGuiTimestampView::DrawTimestampWindow(bool& draw, const PassEntry* rootPassEntry, AZStd::unordered_map& timestampEntryDatabase) + inline void ImGuiTimestampView::DrawTimestampWindow( + bool& draw, const PassEntry* rootPassEntry, AZStd::unordered_map& timestampEntryDatabase, + AZ::RHI::Ptr rootPass) { // Early out if nothing is supposed to be drawn if (!draw) @@ -534,10 +532,28 @@ namespace AZ // Clear the references from the previous frame. m_passEntryReferences.clear(); + // pass entry grid based on its timestamp + AZStd::vector sortedPassEntries; + AZStd::vector> sortedPassGrid; + // Set the child of the parent, only if it passes the filter. for (auto& passEntryIt : timestampEntryDatabase) { PassEntry* passEntry = &passEntryIt.second; + + // Collect all pass entries with non-zero durations + if (passEntry->m_timestampResult.GetDurationInTicks() > 0) + { + sortedPassEntries.push_back(passEntry); + } + + // Skip the pass if the pass' timestamp duration is 0 + if (m_hideZeroPasses && (!passEntry->m_isParent) && passEntry->m_timestampResult.GetDurationInTicks() == 0) + { + continue; + } + + // Only add pass if it pass the filter. if (m_passFilter.PassFilter(passEntry->m_name.GetCStr())) { if (passEntry->m_parent && !passEntry->m_linked) @@ -545,19 +561,94 @@ namespace AZ passEntry->m_parent->LinkChild(passEntry); } - AZ_Assert(m_passEntryReferences.size() < TimestampEntryCount, "Too many PassEntry references. Increase the size of the array."); + AZ_Assert( + m_passEntryReferences.size() < TimestampEntryCount, + "Too many PassEntry references. Increase the size of the array."); m_passEntryReferences.push_back(passEntry); } } + // Sort the pass entries based on their starting time and duration + AZStd::sort(sortedPassEntries.begin(), sortedPassEntries.end(), [](const PassEntry* passEntry1, const PassEntry* passEntry2) { + if (passEntry1->m_timestampResult.GetTimestampBeginInTicks() == passEntry2->m_timestampResult.GetTimestampBeginInTicks()) + { + return passEntry1->m_timestampResult.GetDurationInTicks() < passEntry2->m_timestampResult.GetDurationInTicks(); + } + return passEntry1->m_timestampResult.GetTimestampBeginInTicks() < passEntry2->m_timestampResult.GetTimestampBeginInTicks(); + }); + + // calculate the total GPU duration. + RPI::TimestampResult gpuTimestamp; + if (sortedPassEntries.size() > 0) + { + gpuTimestamp = sortedPassEntries.front()->m_timestampResult; + gpuTimestamp.Add(sortedPassEntries.back()->m_timestampResult); + } + + // Add a pass to the pass grid which none of the pass's timestamp range won't overlap each other. + // Search each row until the pass can be added to the end of row without overlap the previous one. + for (auto& passEntry : sortedPassEntries) + { + auto row = sortedPassGrid.begin(); + for (; row != sortedPassGrid.end(); row++) + { + if (row->empty()) + { + break; + } + auto last = (*row).back(); + if (passEntry->m_timestampResult.GetTimestampBeginInTicks() >= + last->m_timestampResult.GetTimestampBeginInTicks() + last->m_timestampResult.GetDurationInTicks()) + { + row->push_back(passEntry); + break; + } + } + if (row == sortedPassGrid.end()) + { + sortedPassGrid.push_back(); + sortedPassGrid.back().push_back(passEntry); + } + } + + // Refresh timestamp query + bool needEnable = false; + if (!m_paused) + { + if (m_refreshType == RefreshType::OncePerSecond) + { + auto now = AZStd::GetTimeNowMicroSecond(); + if (m_lastUpdateTimeMicroSecond == 0 || now - m_lastUpdateTimeMicroSecond > 1000000) + { + needEnable = true; + m_lastUpdateTimeMicroSecond = now; + } + } + else if (m_refreshType == RefreshType::Realtime) + { + needEnable = true; + } + } + + if (rootPass->IsTimestampQueryEnabled() != needEnable) + { + rootPass->SetTimestampQueryEnabled(needEnable); + } + const ImVec2 windowSize(680.0f, 620.0f); ImGui::SetNextWindowSize(windowSize, ImGuiCond_Always); if (ImGui::Begin("Timestamp View", &draw, ImGuiWindowFlags_NoResize)) { // Draw the header. { + // Pause/unpause the profiling + if (ImGui::Button(m_paused? "Resume":"Pause")) + { + m_paused = !m_paused; + } + // Draw the frame time (GPU). - const AZStd::string formattedTimestamp = FormatTimestampLabel(rootPassEntry->m_interpolatedTimestampInNanoseconds); + const AZStd::string formattedTimestamp = FormatTimestampLabel(gpuTimestamp.GetDurationInNanoseconds()); const AZStd::string headerFrameTime = AZStd::string::format("Total frame duration (GPU): %s", formattedTimestamp.c_str()); ImGui::Text(headerFrameTime.c_str()); @@ -566,6 +657,17 @@ namespace AZ ImGui::SameLine(); ImGui::RadioButton("Flat", reinterpret_cast(&m_viewType), static_cast(ProfilerViewType::Flat)); + // Draw the refresh option + ImGui::RadioButton("Realtime", reinterpret_cast(&m_refreshType), static_cast(RefreshType::Realtime)); + ImGui::SameLine(); + ImGui::RadioButton("Once Per Second", reinterpret_cast(&m_refreshType), static_cast(RefreshType::OncePerSecond)); + + // Show/hide non-parent passes which have zero execution time + ImGui::Checkbox("Hide Zero Cost Passes", &m_hideZeroPasses); + + // Show/hide the timeline bar of all the passes which has non-zero execution time + ImGui::Checkbox("Show Timeline", &m_showTimeline); + // Draw advanced options. const ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_None; GpuProfilerImGuiHelper::TreeNode("Advanced options", flags, [this](bool unrolled) @@ -587,6 +689,56 @@ namespace AZ ImGui::Separator(); + // Draw the pass entry grid + if (!sortedPassEntries.empty() && m_showTimeline) + { + const float passBarHeight = 20.f; + const float passBarSpace = 3.f; + float areaWidth = ImGui::GetContentRegionAvail().x - 20.f; + + if (ImGui::BeginChild("Timeline", ImVec2(areaWidth, (passBarHeight + passBarSpace) * sortedPassGrid.size()), false)) + { + // start tick and end tick for the area + uint64_t areaStartTick = sortedPassEntries.front()->m_timestampResult.GetTimestampBeginInTicks(); + uint64_t areaEndTick = sortedPassEntries.back()->m_timestampResult.GetTimestampBeginInTicks() + + sortedPassEntries.back()->m_timestampResult.GetDurationInTicks(); + uint64_t areaDurationInTicks = areaEndTick - areaStartTick; + + float rowStartY = 0.f; + for (auto& row : sortedPassGrid) + { + // row start y + for (auto passEntry : row) + { + // button start and end + float buttonStartX = (passEntry->m_timestampResult.GetTimestampBeginInTicks() - areaStartTick) * areaWidth / + areaDurationInTicks; + float buttonWidth = passEntry->m_timestampResult.GetDurationInTicks() * areaWidth / areaDurationInTicks; + ImGui::SetCursorPosX(buttonStartX); + ImGui::SetCursorPosY(rowStartY); + + // Adds a button and the hover colors. + ImGui::Button(passEntry->m_name.GetCStr(), ImVec2(buttonWidth, passBarHeight)); + + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("Name: %s", passEntry->m_name.GetCStr()); + ImGui::Text("Path: %s", passEntry->m_path.GetCStr()); + ImGui::Text("Duration in ticks: %u", passEntry->m_timestampResult.GetDurationInTicks()); + ImGui::Text("Duration in microsecond: %.3f us", passEntry->m_timestampResult.GetDurationInNanoseconds()/1000.f); + ImGui::EndTooltip(); + } + } + + rowStartY += passBarHeight + passBarSpace; + } + } + ImGui::EndChild(); + + ImGui::Separator(); + } + // Draw the timestamp view. { static const AZStd::array(TimestampMetricUnit::Count)> MetricUnitText = @@ -713,20 +865,18 @@ namespace AZ const auto drawWorkloadBar = [this](const AZStd::string& entryTime, const PassEntry* entry) { ImGui::NextColumn(); - ImGui::Text(entryTime.c_str()); - ImGui::NextColumn(); - - // Only draw the workload bar when the entry is enabled. - if (entry->IsTimestampEnabled()) + if (entry->m_isParent) { - DrawFrameWorkloadBar(NormalizeFrameWorkload(entry->m_interpolatedTimestampInNanoseconds)); + ImGui::NextColumn(); + ImGui::NextColumn(); } else { - ImGui::ProgressBar(0.0f, ImVec2(-1.0f, 0.0f), "Disabled"); + ImGui::Text(entryTime.c_str()); + ImGui::NextColumn(); + DrawFrameWorkloadBar(NormalizeFrameWorkload(entry->m_interpolatedTimestampInNanoseconds)); + ImGui::NextColumn(); } - - ImGui::NextColumn(); }; static const auto createHoverMarker = [](const char* text) @@ -800,23 +950,17 @@ namespace AZ // Draw the flat view. for (const PassEntry* entry : m_passEntryReferences) { + if (entry->m_isParent) + { + continue; + } const AZStd::string entryTime = FormatTimestampLabel(entry->m_interpolatedTimestampInNanoseconds); ImGui::Text(entry->m_name.GetCStr()); ImGui::NextColumn(); ImGui::Text(entryTime.c_str()); ImGui::NextColumn(); - - // Only draw the workload bar if the entry is enabled. - if (entry->IsTimestampEnabled()) - { - DrawFrameWorkloadBar(NormalizeFrameWorkload(entry->m_interpolatedTimestampInNanoseconds)); - } - else - { - ImGui::ProgressBar(0.0f, ImVec2(-1.0f, 0.0f), "Disabled"); - } - + DrawFrameWorkloadBar(NormalizeFrameWorkload(entry->m_interpolatedTimestampInNanoseconds)); ImGui::NextColumn(); } } @@ -890,23 +1034,33 @@ namespace AZ // Update the PassEntry database. const PassEntry* rootPassEntryRef = CreatePassEntries(rootPass); + bool wasDraw = draw; + GpuProfilerImGuiHelper::Begin("Gpu Profiler", &draw, ImGuiWindowFlags_NoResize, [this, &rootPass]() { - ImGui::Checkbox("Enable TimestampView", &m_drawTimestampView); + if (ImGui::Checkbox("Enable TimestampView", &m_drawTimestampView)) + { + rootPass->SetTimestampQueryEnabled(m_drawTimestampView); + } ImGui::Spacing(); - ImGui::Checkbox("Enable PipelineStatisticsView", &m_drawPipelineStatisticsView); + if(ImGui::Checkbox("Enable PipelineStatisticsView", &m_drawPipelineStatisticsView)) + { + rootPass->SetPipelineStatisticsQueryEnabled(m_drawPipelineStatisticsView); + } }); // Draw the PipelineStatistics window. - m_timestampView.DrawTimestampWindow(m_drawTimestampView, rootPassEntryRef, m_passEntryDatabase); + m_timestampView.DrawTimestampWindow(m_drawTimestampView, rootPassEntryRef, m_passEntryDatabase, rootPass); // Draw the PipelineStatistics window. - m_pipelineStatisticsView.DrawPipelineStatisticsWindow(m_drawPipelineStatisticsView, rootPassEntryRef, m_passEntryDatabase); + m_pipelineStatisticsView.DrawPipelineStatisticsWindow(m_drawPipelineStatisticsView, rootPassEntryRef, m_passEntryDatabase, rootPass); - // [GFX TODO][ATOM-13792] Optimization: ImGui GpuProfiler Pass hierarchy traversal. - // Enable/Disable the Timestamp and PipelineStatistics on the RootPass - rootPass->SetTimestampQueryEnabled(draw && m_drawTimestampView); - rootPass->SetPipelineStatisticsQueryEnabled(draw && m_drawPipelineStatisticsView); + //closing window + if (wasDraw && !draw) + { + rootPass->SetTimestampQueryEnabled(false); + rootPass->SetPipelineStatisticsQueryEnabled(false); + } } inline void ImGuiGpuProfiler::InterpolatePassEntries(AZStd::unordered_map& passEntryDatabase, float weight) const @@ -918,7 +1072,7 @@ namespace AZ { // Interpolate the timestamps. const double interpolated = Lerp(static_cast(oldEntryIt->second.m_interpolatedTimestampInNanoseconds), - static_cast(entry.second.m_timestampResult.GetTimestampInNanoseconds()), + static_cast(entry.second.m_timestampResult.GetDurationInNanoseconds()), static_cast(weight)); entry.second.m_interpolatedTimestampInNanoseconds = static_cast(interpolated); } diff --git a/Gems/ImGui/Code/Source/ImGuiManager.cpp b/Gems/ImGui/Code/Source/ImGuiManager.cpp index 697cc61f86..c446c98175 100644 --- a/Gems/ImGui/Code/Source/ImGuiManager.cpp +++ b/Gems/ImGui/Code/Source/ImGuiManager.cpp @@ -222,6 +222,10 @@ void ImGuiManager::Initialize() io.DisplaySize.x = 1920; io.DisplaySize.y = 1080; + // Create a default font + io.Fonts->AddFontDefault(); + io.Fonts->Build(); + // Broadcast ImGui Ready to Listeners ImGuiUpdateListenerBus::Broadcast(&IImGuiUpdateListener::OnImGuiInitialize); m_currentControllerIndex = -1;