You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/Profiler/Code/Source/ImGuiCpuProfiler.h

229 lines
8.3 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Component/TickBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Math/Random.h>
#include <Atom/RHI/CpuProfiler.h>
namespace Profiler
{
//! Stores all the data associated with a row in the table.
struct TableRow
{
template <typename T>
struct TableRowCompareFunctor
{
TableRowCompareFunctor(T memberPointer, bool isAscending) : m_memberPointer(memberPointer), m_ascending(isAscending){};
bool operator()(const TableRow* lhs, const TableRow* rhs)
{
return m_ascending ? lhs->*m_memberPointer < rhs->*m_memberPointer : lhs->*m_memberPointer > rhs->*m_memberPointer;
}
T m_memberPointer;
bool m_ascending;
};
// Update running statistics with new region data
void RecordRegion(const AZ::RHI::CachedTimeRegion& region, size_t threadId);
void ResetPerFrameStatistics();
// Get a string of all threads that this region executed in during the last frame
AZStd::string GetExecutingThreadsLabel() const;
AZStd::string m_groupName;
AZStd::string m_regionName;
// --- Per frame statistics ---
u64 m_invocationsLastFrame = 0;
// NOTE: set over unordered_set so the threads can be shown in increasing order in tooltip.
AZStd::set<size_t> m_executingThreads;
AZStd::sys_time_t m_lastFrameTotalTicks = 0;
// Maximum execution time of a region in the last frame.
AZStd::sys_time_t m_maxTicks = 0;
// --- Aggregate statistics ---
u64 m_invocationsTotal = 0;
// Running average of Mean Time Per Call
AZStd::sys_time_t m_runningAverageTicks = 0;
};
//! ImGui widget for examining Atom CPU Profiling instrumentation.
//! Offers both a statistical view (with sorting and searching capability) and a visualizer
//! similar to RAD and other profiling tools.
class ImGuiCpuProfiler
: SystemTickBus::Handler
{
// Region Name -> statistical view row data
using RegionRowMap = AZStd::map<AZStd::string, TableRow>;
// Group Name -> RegionRowMap
using GroupRegionMap = AZStd::map<AZStd::string, RegionRowMap>;
using TimeRegion = AZ::RHI::CachedTimeRegion;
using GroupRegionName = AZ::RHI::CachedTimeRegion::GroupRegionName;
public:
struct CpuTimingEntry
{
const AZStd::string& m_name;
double m_executeDuration;
};
ImGuiCpuProfiler() = default;
~ImGuiCpuProfiler() = default;
//! Draws the overall CPU profiling window, defaults to the statistical view
void Draw(bool& keepDrawing);
private:
static constexpr float RowHeight = 35.0;
static constexpr int DefaultFramesToCollect = 50;
static constexpr float MediumFrameTimeLimit = 16.6; // 60 fps
static constexpr float HighFrameTimeLimit = 33.3; // 30 fps
//! Draws the statistical view of the CPU profiling data.
void DrawStatisticsView();
//! Callback invoked when the "Load File" button is pressed in the file picker.
void LoadFile();
//! Draws the file picker window.
void DrawFilePicker();
//! Draws the CPU profiling visualizer.
void DrawVisualizer();
// Draw the shared header between the two windows.
void DrawCommonHeader();
// Draw the region statistics table in the order specified by the pointers in m_tableData.
void DrawTable();
// Sort the table by a given column, rearranges the pointers in m_tableData.
void SortTable(ImGuiTableSortSpecs* sortSpecs);
// gather the latest timing statistics
void CacheCpuTimingStatistics();
// Get the profiling data from the last frame, only called when the profiler is not paused.
void CollectFrameData();
// Cull old data from internal storage, only called when profiler is not paused.
void CullFrameData();
// Draws a single block onto the timeline into the specified row
void DrawBlock(const TimeRegion& block, u64 targetRow);
// Draw horizontal lines between threads in the timeline
void DrawThreadSeparator(u64 threadBoundary, u64 maxDepth);
// Draw the "Thread XXXXX" label onto the viewport
void DrawThreadLabel(u64 baseRow, size_t threadId);
// Draw the vertical lines separating frames in the timeline
void DrawFrameBoundaries();
// Draw the ruler with frame time labels
void DrawRuler();
// Draw the frame time histogram
void DrawFrameTimeHistogram();
// Converts raw ticks to a pixel value suitable to give to ImDrawList, handles window scrolling
float ConvertTickToPixelSpace(AZStd::sys_time_t tick, AZStd::sys_time_t leftBound, AZStd::sys_time_t rightBound) const;
AZStd::sys_time_t GetViewportTickWidth() const;
// Gets the color for a block using the GroupRegionName as a key into the cache.
// Generates a random ImU32 if the block does not yet have a color.
ImU32 GetBlockColor(const TimeRegion& block);
// System tick bus overrides
virtual void OnSystemTick() override;
// --- Visualizer Members ---
int m_framesToCollect = DefaultFramesToCollect;
// Tally of the number of saved profiling events so far
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;
// 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
// platform to platform, which causes problems when deserializing saved captures.
AZStd::unordered_map<size_t, AZStd::vector<TimeRegion>> m_savedData;
// Region color cache
AZStd::unordered_map<GroupRegionName, ImVec4, RHI::CachedTimeRegion::GroupRegionName::Hash> m_regionColorMap;
// Tracks the frame boundaries
AZStd::vector<AZStd::sys_time_t> m_frameEndTicks = { INT64_MIN };
// Filter for highlighting regions on the visualizer
ImGuiTextFilter m_visualizerHighlightFilter;
// --- Tabular view members ---
// ImGui filter used to filter TimedRegions.
ImGuiTextFilter m_timedRegionFilter;
// Saves statistical view data organized by group name -> region name -> row data
GroupRegionMap m_groupRegionMap;
// Saves pointers to objects in m_groupRegionMap, order reflects table ordering.
// Non-owning, will be cleared when m_groupRegionMap is cleared.
AZStd::vector<TableRow*> m_tableData;
// Pause cpu profiling. The profiler will show the statistics of the last frame before pause.
bool m_paused = false;
// Export the profiling data from a single frame to a local file.
bool m_captureToFile = false;
// Toggle between the normal statistical view and the visual profiling view.
bool m_enableVisualizer = false;
// Last captured CPU timing statistics
AZStd::vector<CpuTimingEntry> m_cpuTimingStatisticsWhenPause;
AZStd::sys_time_t m_frameToFrameTime{};
AZStd::string m_lastCapturedFilePath;
bool m_showFilePicker = false;
// Cached file paths to previous traces on disk, sorted with the most recent trace at the front.
AZStd::vector<IO::Path> m_cachedCapturePaths;
// Index into the file picker, used to determine which file to load when "Load File" is pressed.
int m_currentFileIndex = 0;
// --- Loading capture state ---
AZStd::unordered_set<AZStd::string> m_deserializedStringPool;
AZStd::unordered_set<RHI::CachedTimeRegion::GroupRegionName, RHI::CachedTimeRegion::GroupRegionName::Hash> m_deserializedGroupRegionNamePool;
};
} // namespace Profiler
#include "ImGuiCpuProfiler.inl"