ImGuiCpuProfiler.h 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #pragma once
  9. #if defined(IMGUI_ENABLED)
  10. #include <CpuProfiler.h>
  11. #include <AzCore/Component/TickBus.h>
  12. #include <AzCore/IO/Path/Path.h>
  13. #include <AzCore/Math/Random.h>
  14. #include <AzCore/std/containers/map.h>
  15. #include <AzCore/std/containers/set.h>
  16. #include <AzCore/std/containers/unordered_set.h>
  17. #include <AzCore/Time/ITime.h>
  18. #include <imgui/imgui.h>
  19. namespace Profiler
  20. {
  21. //! Stores all the data associated with a row in the table.
  22. struct TableRow
  23. {
  24. template <typename T>
  25. struct TableRowCompareFunctor
  26. {
  27. TableRowCompareFunctor(T memberPointer, bool isAscending) : m_memberPointer(memberPointer), m_ascending(isAscending){};
  28. bool operator()(const TableRow* lhs, const TableRow* rhs)
  29. {
  30. return m_ascending ? lhs->*m_memberPointer < rhs->*m_memberPointer : lhs->*m_memberPointer > rhs->*m_memberPointer;
  31. }
  32. T m_memberPointer;
  33. bool m_ascending;
  34. };
  35. // Update running statistics with new region data
  36. void RecordRegion(const CachedTimeRegion& region, size_t threadId);
  37. void ResetPerFrameStatistics();
  38. // Get a string of all threads that this region executed in during the last frame
  39. AZStd::string GetExecutingThreadsLabel() const;
  40. AZStd::string m_groupName;
  41. AZStd::string m_regionName;
  42. // --- Per frame statistics ---
  43. AZ::u64 m_invocationsLastFrame = 0;
  44. // NOTE: set over unordered_set so the threads can be shown in increasing order in tooltip.
  45. AZStd::set<size_t> m_executingThreads;
  46. AZStd::sys_time_t m_lastFrameTotalTicks = 0;
  47. // Maximum execution time of a region in the last frame.
  48. AZStd::sys_time_t m_maxTicks = 0;
  49. // --- Aggregate statistics ---
  50. AZ::u64 m_invocationsTotal = 0;
  51. // Running average of Mean Time Per Call
  52. AZStd::sys_time_t m_runningAverageTicks = 0;
  53. };
  54. //! ImGui widget for examining CPU Profiling instrumentation.
  55. //! Offers both a statistical view (with sorting and searching capability) and a visualizer
  56. //! similar to other profiling tools.
  57. class ImGuiCpuProfiler
  58. : public AZ::SystemTickBus::Handler
  59. {
  60. // Region Name -> statistical view row data
  61. using RegionRowMap = AZStd::map<AZStd::string, TableRow>;
  62. // Group Name -> RegionRowMap
  63. using GroupRegionMap = AZStd::map<AZStd::string, RegionRowMap>;
  64. using TimeRegion = CachedTimeRegion;
  65. using GroupRegionName = CachedTimeRegion::GroupRegionName;
  66. public:
  67. struct CpuTimingEntry
  68. {
  69. const AZStd::string& m_name;
  70. double m_executeDuration = 0;
  71. double m_executeDurationAverage = 0;
  72. };
  73. ImGuiCpuProfiler();
  74. ~ImGuiCpuProfiler() = default;
  75. //! Draws the overall CPU profiling window, defaults to the statistical view
  76. void Draw(bool& keepDrawing);
  77. private:
  78. static constexpr float RowHeight = 35.0f;
  79. static constexpr int DefaultFramesToCollect = 60; // 1 second @ 60 fps
  80. static constexpr int DefaultUpdateFrequencyMs = 1000; // 1 second
  81. static constexpr float MediumFrameTimeLimit = 16.6f; // 60 fps
  82. static constexpr float HighFrameTimeLimit = 33.3f; // 30 fps
  83. //! Draws the statistical view of the CPU profiling data.
  84. void DrawStatisticsView();
  85. //! Generates the full output timestamped file path based on nameHint
  86. AZStd::string GenerateOutputFile(const char* nameHint);
  87. //! Callback invoked when the "Load File" button is pressed in the file picker.
  88. void LoadFile();
  89. //! Draws the file picker window.
  90. void DrawFilePicker();
  91. //! Draws the CPU profiling visualizer.
  92. void DrawVisualizer();
  93. // Draw the shared header between the two windows.
  94. void DrawCommonHeader();
  95. // Draw the region statistics table in the order specified by the pointers in m_tableData.
  96. void DrawTable();
  97. // Sort the table by a given column, rearranges the pointers in m_tableData.
  98. void SortTable(ImGuiTableSortSpecs* sortSpecs);
  99. // Clear the table, forcing it to rebuild
  100. void ResetTable();
  101. // gather the latest timing statistics
  102. void CacheCpuTimingStatistics();
  103. // Get the profiling data from the last frame, only called when the profiler is not paused.
  104. void CollectFrameData();
  105. // Cull old data from internal storage, only called when profiler is not paused.
  106. void CullFrameData();
  107. // Draws a single block onto the timeline into the specified row
  108. void DrawBlock(const TimeRegion& block, AZ::u64 targetRow);
  109. // Draw horizontal lines between threads in the timeline
  110. void DrawThreadSeparator(AZ::u64 threadBoundary, AZ::u64 maxDepth);
  111. // Draw the "Thread XXXXX" label onto the viewport
  112. void DrawThreadLabel(AZ::u64 baseRow, size_t threadId);
  113. // Draw the vertical lines separating frames in the timeline
  114. void DrawFrameBoundaries();
  115. // Draw the ruler with frame time labels
  116. void DrawRuler();
  117. // Draw the frame time histogram
  118. void DrawFrameTimeHistogram();
  119. // Converts raw ticks to a pixel value suitable to give to ImDrawList, handles window scrolling
  120. float ConvertTickToPixelSpace(AZStd::sys_time_t tick, AZStd::sys_time_t leftBound, AZStd::sys_time_t rightBound) const;
  121. AZStd::sys_time_t GetViewportTickWidth() const;
  122. // Gets the color for a block using the GroupRegionName as a key into the cache.
  123. // Generates a random ImU32 if the block does not yet have a color.
  124. ImU32 GetBlockColor(const TimeRegion& block);
  125. // System tick bus overrides
  126. void OnSystemTick() override;
  127. // Convert time ticks to milliseconds
  128. float TicksToMs(double ticks);
  129. // Convert time ticks to milliseconds
  130. float TicksToMs(AZStd::sys_time_t ticks);
  131. // --- Visualizer Members ---
  132. int m_updateFrequencyMs = DefaultUpdateFrequencyMs;
  133. AZ::TimeMs m_currentUpdateTimeMs = AZ::TimeMs{ 0 };
  134. int m_framesToCollect = DefaultFramesToCollect;
  135. // Tally of the number of saved profiling events so far
  136. AZ::u64 m_savedRegionCount = 0;
  137. // Viewport tick bounds, these are used to convert tick space -> screen space and cull so we only draw onscreen objects
  138. AZStd::sys_time_t m_viewportStartTick = 0;
  139. AZStd::sys_time_t m_viewportEndTick = 0;
  140. // Map to store each thread's TimeRegions, individual vectors are sorted by start tick
  141. // note: we use size_t as a proxy for thread_id because native_thread_id_type differs differs from
  142. // platform to platform, which causes problems when deserializing saved captures.
  143. AZStd::unordered_map<size_t, AZStd::vector<TimeRegion>> m_savedData;
  144. size_t m_mainThreadId = 0;
  145. // Region color cache
  146. AZStd::unordered_map<GroupRegionName, ImVec4, CachedTimeRegion::GroupRegionName::Hash> m_regionColorMap;
  147. // Tracks the frame boundaries
  148. AZStd::vector<AZStd::sys_time_t> m_frameEndTicks = { INT64_MIN };
  149. // Filter for highlighting regions on the visualizer
  150. ImGuiTextFilter m_visualizerHighlightFilter;
  151. // --- Tabular view members ---
  152. // ImGui filter used to filter TimedRegions.
  153. ImGuiTextFilter m_timedRegionFilter;
  154. // Saves statistical view data organized by group name -> region name -> row data
  155. GroupRegionMap m_groupRegionMap;
  156. // Saves pointers to objects in m_groupRegionMap, order reflects table ordering.
  157. // Non-owning, will be cleared when m_groupRegionMap is cleared.
  158. AZStd::vector<TableRow*> m_tableData;
  159. // Pause cpu profiling. The profiler will show the statistics of the last frame before pause.
  160. bool m_paused = false;
  161. // Export the profiling data from a single frame to a local file.
  162. bool m_captureToFile = false;
  163. // Toggle between the normal statistical view and the visual profiling view.
  164. bool m_enableVisualizer = false;
  165. // Last captured CPU timing statistics
  166. AZStd::vector<CpuTimingEntry> m_cpuTimingStatisticsWhenPause;
  167. AZ::IO::FixedMaxPath m_lastCapturedFilePath;
  168. bool m_showFilePicker = false;
  169. // Cached file paths to previous traces on disk, sorted with the most recent trace at the front.
  170. AZStd::vector<AZ::IO::Path> m_cachedCapturePaths;
  171. // Index into the file picker, used to determine which file to load when "Load File" is pressed.
  172. int m_currentFileIndex = 0;
  173. // Ticks per second used when the profiler data was recorded (when loading from a file).
  174. AZStd::sys_time_t m_ticksPerSecondFromFile = 0;
  175. // --- Loading capture state ---
  176. AZStd::unordered_set<AZStd::string> m_deserializedStringPool;
  177. AZStd::unordered_set<CachedTimeRegion::GroupRegionName, CachedTimeRegion::GroupRegionName::Hash> m_deserializedGroupRegionNamePool;
  178. };
  179. } // namespace Profiler
  180. #endif // defined(IMGUI_ENABLED)