aboutsummaryrefslogtreecommitdiff
path: root/vendor/zgui/libs/imgui_test_engine/imgui_te_perftool.cpp
diff options
context:
space:
mode:
authorNic Gaffney <gaffney_nic@protonmail.com>2025-10-13 01:19:27 -0500
committerNic Gaffney <gaffney_nic@protonmail.com>2025-10-13 01:19:27 -0500
commit1f9b827badb2de4c4eaae11c0d02242ec90af7f6 (patch)
tree27acb295833e6eee730dd71f98db280d54c6a4ad /vendor/zgui/libs/imgui_test_engine/imgui_te_perftool.cpp
parentb5d0c1dcd751f4735d9f6b45c805300000c9d171 (diff)
downloadparticle-sim-1f9b827badb2de4c4eaae11c0d02242ec90af7f6.tar.gz
Updating to zig 0.15.1
Diffstat (limited to 'vendor/zgui/libs/imgui_test_engine/imgui_te_perftool.cpp')
-rw-r--r--vendor/zgui/libs/imgui_test_engine/imgui_te_perftool.cpp1949
1 files changed, 0 insertions, 1949 deletions
diff --git a/vendor/zgui/libs/imgui_test_engine/imgui_te_perftool.cpp b/vendor/zgui/libs/imgui_test_engine/imgui_te_perftool.cpp
deleted file mode 100644
index 65b853a..0000000
--- a/vendor/zgui/libs/imgui_test_engine/imgui_te_perftool.cpp
+++ /dev/null
@@ -1,1949 +0,0 @@
-// dear imgui test engine
-// (performance tool)
-// Browse and visualize samples recorded by ctx->PerfCapture() calls.
-// User access via 'Test Engine UI -> Tools -> Perf Tool'
-
-/*
-
-Index of this file:
-// [SECTION] Header mess
-// [SECTION] ImGuiPerflogEntry
-// [SECTION] Types & everything else
-// [SECTION] USER INTERFACE
-// [SECTION] SETTINGS
-// [SECTION] TESTS
-
-*/
-
-// Terminology:
-// * Entry: information about execution of a single perf test. This corresponds to one line in CSV file.
-// * Batch: a group of entries that were created together during a single execution. A new batch is created each time
-// one or more perf tests are executed. All entries in a single batch will have a matching ImGuiPerflogEntry::Timestamp.
-// * Build: A group of batches that have matching BuildType, OS, Cpu, Compiler, GitBranchName.
-// * Baseline: A batch that we are comparing against. Baselines are identified by batch timestamp and build id.
-
-//-------------------------------------------------------------------------
-// [SECTION] Header mess
-//-------------------------------------------------------------------------
-
-#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
-#define _CRT_SECURE_NO_WARNINGS
-#endif
-
-#define IMGUI_DEFINE_MATH_OPERATORS
-#include "imgui_te_perftool.h"
-#include "imgui.h"
-#include "imgui_internal.h"
-#include "imgui_te_utils.h"
-#include "thirdparty/Str/Str.h"
-#include <time.h> // time(), localtime()
-#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
-#include "implot.h"
-#include "implot_internal.h"
-#endif
-
-// For tests
-#include "imgui_te_engine.h"
-#include "imgui_te_context.h"
-#include "imgui_te_internal.h" // ImGuiTestEngine_GetPerfTool()
-#include "imgui_capture_tool.h"
-
-//-------------------------------------------------------------------------
-// [SECTION] ImGuiPerflogEntry
-//-------------------------------------------------------------------------
-
-void ImGuiPerfToolEntry::Set(const ImGuiPerfToolEntry& other)
-{
- Timestamp = other.Timestamp;
- Category = other.Category;
- TestName = other.TestName;
- DtDeltaMs = other.DtDeltaMs;
- DtDeltaMsMin = other.DtDeltaMsMin;
- DtDeltaMsMax = other.DtDeltaMsMax;
- NumSamples = other.NumSamples;
- PerfStressAmount = other.PerfStressAmount;
- GitBranchName = other.GitBranchName;
- BuildType = other.BuildType;
- Cpu = other.Cpu;
- OS = other.OS;
- Compiler = other.Compiler;
- Date = other.Date;
- //DateMax = ...
- VsBaseline = other.VsBaseline;
- LabelIndex = other.LabelIndex;
-}
-
-//-------------------------------------------------------------------------
-// [SECTION] Types & everything else
-//-------------------------------------------------------------------------
-
-typedef ImGuiID(*HashEntryFn)(ImGuiPerfToolEntry* entry);
-typedef void(*FormatEntryLabelFn)(ImGuiPerfTool* perftool, Str* result, ImGuiPerfToolEntry* entry);
-
-struct ImGuiPerfToolColumnInfo
-{
- const char* Title;
- int Offset;
- ImGuiDataType Type;
- bool ShowAlways;
- ImGuiTableFlags Flags;
-
- template<typename T>
- T GetValue(const ImGuiPerfToolEntry* entry) const { return *(T*)((const char*)entry + Offset); }
-};
-
-// Update _ShowEntriesTable() and SaveHtmlReport() when adding new entries.
-static const ImGuiPerfToolColumnInfo PerfToolColumnInfo[] =
-{
- { /* 00 */ "Date", offsetof(ImGuiPerfToolEntry, Timestamp), ImGuiDataType_U64, true, ImGuiTableColumnFlags_DefaultHide },
- { /* 01 */ "Test Name", offsetof(ImGuiPerfToolEntry, TestName), ImGuiDataType_COUNT, true, 0 },
- { /* 02 */ "Branch", offsetof(ImGuiPerfToolEntry, GitBranchName), ImGuiDataType_COUNT, true, 0 },
- { /* 03 */ "Compiler", offsetof(ImGuiPerfToolEntry, Compiler), ImGuiDataType_COUNT, true, 0 },
- { /* 04 */ "OS", offsetof(ImGuiPerfToolEntry, OS), ImGuiDataType_COUNT, true, 0 },
- { /* 05 */ "CPU", offsetof(ImGuiPerfToolEntry, Cpu), ImGuiDataType_COUNT, true, 0 },
- { /* 06 */ "Build", offsetof(ImGuiPerfToolEntry, BuildType), ImGuiDataType_COUNT, true, 0 },
- { /* 07 */ "Stress", offsetof(ImGuiPerfToolEntry, PerfStressAmount), ImGuiDataType_S32, true, 0 },
- { /* 08 */ "Avg ms", offsetof(ImGuiPerfToolEntry, DtDeltaMs), ImGuiDataType_Double, true, 0 },
- { /* 09 */ "Min ms", offsetof(ImGuiPerfToolEntry, DtDeltaMsMin), ImGuiDataType_Double, false, 0 },
- { /* 00 */ "Max ms", offsetof(ImGuiPerfToolEntry, DtDeltaMsMax), ImGuiDataType_Double, false, 0 },
- { /* 11 */ "Samples", offsetof(ImGuiPerfToolEntry, NumSamples), ImGuiDataType_S32, false, 0 },
- { /* 12 */ "VS Baseline", offsetof(ImGuiPerfToolEntry, VsBaseline), ImGuiDataType_Float, true, 0 },
-};
-
-static const char* PerfToolReportDefaultOutputPath = "./output/capture_perf_report.html";
-
-// This is declared as a standalone function in order to run without a PerfTool instance
-void ImGuiTestEngine_PerfToolAppendToCSV(ImGuiPerfTool* perf_log, ImGuiPerfToolEntry* entry, const char* filename)
-{
- if (filename == NULL)
- filename = IMGUI_PERFLOG_DEFAULT_FILENAME;
-
- if (!ImFileCreateDirectoryChain(filename, ImPathFindFilename(filename)))
- {
- fprintf(stderr, "Unable to create missing directory '%*s', perftool entry was not saved.\n", (int)(ImPathFindFilename(filename) - filename), filename);
- return;
- }
-
- // Appends to .csv
- FILE* f = fopen(filename, "a+b");
- if (f == NULL)
- {
- fprintf(stderr, "Unable to open '%s', perftool entry was not saved.\n", filename);
- return;
- }
- fprintf(f, "%llu,%s,%s,%.3f,x%d,%s,%s,%s,%s,%s,%s\n", entry->Timestamp, entry->Category, entry->TestName,
- entry->DtDeltaMs, entry->PerfStressAmount, entry->GitBranchName, entry->BuildType, entry->Cpu, entry->OS,
- entry->Compiler, entry->Date);
- fflush(f);
- fclose(f);
-
- // Register to runtime perf tool if any
- if (perf_log != NULL)
- perf_log->AddEntry(entry);
-}
-
-// Tri-state button. Copied and modified ButtonEx().
-static bool Button3(const char* label, int* value)
-{
- ImGuiWindow* window = ImGui::GetCurrentWindow();
- if (window->SkipItems)
- return false;
-
- ImGuiContext& g = *GImGui;
- const ImGuiStyle& style = g.Style;
- const ImGuiID id = window->GetID(label);
- const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
- float dot_radius2 = g.FontSize;
- ImVec2 btn_size(dot_radius2 * 2, dot_radius2);
-
- ImVec2 pos = window->DC.CursorPos;
- ImVec2 size = ImGui::CalcItemSize(ImVec2(), btn_size.x + label_size.x + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x, label_size.y + style.FramePadding.y * 2.0f);
-
- const ImRect bb(pos, pos + size);
- ImGui::ItemSize(size, style.FramePadding.y);
- if (!ImGui::ItemAdd(bb, id))
- return false;
-
- bool hovered, held;
- bool pressed = ImGui::ButtonBehavior(ImRect(pos, pos + style.FramePadding + btn_size), id, &hovered, &held, 0);
-
- // Render
- const ImU32 col = ImGui::GetColorU32(ImGuiCol_FrameBg);
- ImGui::RenderNavHighlight(bb, id);
- ImGui::RenderFrame(bb.Min + style.FramePadding, bb.Min + style.FramePadding + btn_size, col, true, /*style.FrameRounding*/ 5.0f);
-
- ImColor btn_col;
- if (held)
- btn_col = style.Colors[ImGuiCol_SliderGrabActive];
- else if (hovered)
- btn_col = style.Colors[ImGuiCol_ButtonHovered];
- else
- btn_col = style.Colors[ImGuiCol_SliderGrab];
- ImVec2 center = bb.Min + ImVec2(dot_radius2 + (dot_radius2 * (float)*value), dot_radius2) * 0.5f + style.FramePadding;
- window->DrawList->AddCircleFilled(center, dot_radius2 * 0.5f, btn_col);
-
- ImRect text_bb;
- text_bb.Min = bb.Min + style.FramePadding + ImVec2(btn_size.x + style.ItemInnerSpacing.x, 0);
- text_bb.Max = text_bb.Min + label_size;
- ImGui::RenderTextClipped(text_bb.Min, text_bb.Max, label, NULL, &label_size, style.ButtonTextAlign, &bb);
-
- *value = (*value + pressed) % 3;
- return pressed;
-}
-
-static ImGuiID GetBuildID(const ImGuiPerfToolEntry* entry)
-{
- IM_ASSERT(entry != NULL);
- ImGuiID build_id = ImHashStr(entry->BuildType);
- build_id = ImHashStr(entry->OS, 0, build_id);
- build_id = ImHashStr(entry->Cpu, 0, build_id);
- build_id = ImHashStr(entry->Compiler, 0, build_id);
- build_id = ImHashStr(entry->GitBranchName, 0, build_id);
- return build_id;
-}
-
-static ImGuiID GetBuildID(const ImGuiPerfToolBatch* batch)
-{
- IM_ASSERT(batch != NULL);
- IM_ASSERT(!batch->Entries.empty());
- return GetBuildID(&batch->Entries.Data[0]);
-}
-
-// Batch ID depends on display type. It is either a build ID (when combinding by build type) or batch timestamp otherwise.
-static ImGuiID GetBatchID(const ImGuiPerfTool* perftool, const ImGuiPerfToolEntry* entry)
-{
- IM_ASSERT(perftool != NULL);
- IM_ASSERT(entry != NULL);
- if (perftool->_DisplayType == ImGuiPerfToolDisplayType_CombineByBuildInfo)
- return GetBuildID(entry);
- else
- return (ImU32)entry->Timestamp;
-}
-
-static int PerfToolComparerStr(const void* a, const void* b)
-{
- return strcmp(*(const char**)b, *(const char**)a);
-}
-
-static int IMGUI_CDECL PerfToolComparerByEntryInfo(const void* lhs, const void* rhs)
-{
- const ImGuiPerfToolEntry* a = (const ImGuiPerfToolEntry*)lhs;
- const ImGuiPerfToolEntry* b = (const ImGuiPerfToolEntry*)rhs;
-
- // While build ID does include git branch it wont ensure branches are grouped together, therefore we do branch
- // sorting manually.
- int result = strcmp(a->GitBranchName, b->GitBranchName);
-
- // Now that we have groups of branches - sort individual builds within those groups.
- if (result == 0)
- result = ImClamp<int>((int)((ImS64)GetBuildID(a) - (ImS64)GetBuildID(b)), -1, +1);
-
- // Group individual runs together within build groups.
- if (result == 0)
- result = (int)ImClamp<ImS64>((ImS64)b->Timestamp - (ImS64)a->Timestamp, -1, +1);
-
- // And finally sort individual runs by perf name so we can have a predictable order (used to optimize in _Rebuild()).
- if (result == 0)
- result = (int)strcmp(a->TestName, b->TestName);
-
- return result;
-}
-
-static ImGuiPerfTool* PerfToolInstance = NULL;
-static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)
-{
- IM_ASSERT(PerfToolInstance != NULL);
- ImGuiPerfTool* tool = PerfToolInstance;
- const ImGuiTableSortSpecs* sort_specs = PerfToolInstance->_InfoTableSortSpecs;
- int batch_index_a, entry_index_a, mono_index_a, batch_index_b, entry_index_b, mono_index_b;
- tool->_UnpackSortedKey(*(ImU64*)lhs, &batch_index_a, &entry_index_a, &mono_index_a);
- tool->_UnpackSortedKey(*(ImU64*)rhs, &batch_index_b, &entry_index_b, &mono_index_b);
- for (int i = 0; i < sort_specs->SpecsCount; i++)
- {
- const ImGuiTableColumnSortSpecs* specs = &sort_specs->Specs[i];
- const ImGuiPerfToolColumnInfo& col_info = PerfToolColumnInfo[specs->ColumnIndex];
- const ImGuiPerfToolBatch* batch_a = &tool->_Batches[batch_index_a];
- const ImGuiPerfToolBatch* batch_b = &tool->_Batches[batch_index_b];
- ImGuiPerfToolEntry* a = &batch_a->Entries.Data[entry_index_a];
- ImGuiPerfToolEntry* b = &batch_b->Entries.Data[entry_index_b];
- if (specs->SortDirection == ImGuiSortDirection_Ascending)
- ImSwap(a, b);
-
- int result = 0;
- switch (col_info.Type)
- {
- case ImGuiDataType_S32:
- result = col_info.GetValue<int>(a) - col_info.GetValue<int>(b);
- break;
- case ImGuiDataType_U64:
- result = (int)(col_info.GetValue<ImU64>(a) - col_info.GetValue<ImU64>(b));
- break;
- case ImGuiDataType_Float:
- result = (int)((col_info.GetValue<float>(a) - col_info.GetValue<float>(b)) * 1000.0f);
- break;
- case ImGuiDataType_Double:
- result = (int)((col_info.GetValue<double>(a) - col_info.GetValue<double>(b)) * 1000.0);
- break;
- case ImGuiDataType_COUNT:
- result = strcmp(col_info.GetValue<const char*>(a), col_info.GetValue<const char*>(b));
- break;
- default:
- IM_ASSERT(false);
- }
- if (result != 0)
- return result;
- }
- return mono_index_a - mono_index_b;
-}
-
-// Dates are in format "YYYY-MM-DD"
-static bool IsDateValid(const char* date)
-{
- if (date[4] != '-' || date[7] != '-')
- return false;
- for (int i = 0; i < 10; i++)
- {
- if (i == 4 || i == 7)
- continue;
- if (date[i] < '0' || date[i] > '9')
- return false;
- }
- return true;
-}
-
-static float FormatVsBaseline(ImGuiPerfToolEntry* entry, ImGuiPerfToolEntry* baseline_entry, Str& out_label)
-{
- if (baseline_entry == NULL)
- {
- out_label.appendf("--");
- return FLT_MAX;
- }
-
- if (entry == baseline_entry)
- {
- out_label.append("baseline");
- return FLT_MAX;
- }
-
- double percent_vs_first = 100.0 / baseline_entry->DtDeltaMs * entry->DtDeltaMs;
- double dt_change = -(100.0 - percent_vs_first);
- if (dt_change == INFINITY)
- out_label.appendf("--");
- else if (ImAbs(dt_change) > 0.001f)
- out_label.appendf("%+.2lf%% (%s)", dt_change, dt_change < 0.0f ? "faster" : "slower");
- else
- out_label.appendf("==");
- return (float)dt_change;
-}
-
-#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
-static void PerfToolFormatBuildInfo(ImGuiPerfTool* perftool, Str* result, ImGuiPerfToolBatch* batch)
-{
- IM_ASSERT(perftool != NULL);
- IM_ASSERT(result != NULL);
- IM_ASSERT(batch != NULL);
- IM_ASSERT(batch->Entries.Size > 0);
- ImGuiPerfToolEntry* entry = &batch->Entries.Data[0];
- Str64f legend_format("x%%-%dd %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds %%s%%s%%s%%s(%%-%dd sample%%s)%%s",
- perftool->_AlignStress, perftool->_AlignType, perftool->_AlignCpu, perftool->_AlignOs, perftool->_AlignCompiler,
- perftool->_AlignBranch, perftool->_AlignSamples);
- result->appendf(legend_format.c_str(), entry->PerfStressAmount, entry->BuildType, entry->Cpu, entry->OS,
- entry->Compiler, entry->GitBranchName, entry->Date,
-#if 0
- // Show min-max dates.
- perftool->_CombineByBuildInfo ? " - " : "",
- entry->DateMax ? entry->DateMax : "",
-#else
- "", "",
-#endif
- *entry->Date ? " " : "",
- batch->NumSamples,
- batch->NumSamples > 1 ? "s" : "", // Singular/plural form of "sample(s)"
- batch->NumSamples > 1 || perftool->_AlignSamples == 1 ? "" : " " // Space after legend entry to separate * marking baseline
- );
-}
-#endif
-
-static int PerfToolCountBuilds(ImGuiPerfTool* perftool, bool only_visible)
-{
- int num_builds = 0;
- ImU64 build_id = 0;
- for (ImGuiPerfToolEntry& entry : perftool->_SrcData)
- {
- if (build_id != GetBuildID(&entry))
- {
- if (!only_visible || perftool->_IsVisibleBuild(&entry))
- num_builds++;
- build_id = GetBuildID(&entry);
- }
- }
- return num_builds;
-}
-
-static bool InputDate(const char* label, char* date, int date_len, bool valid)
-{
- ImGui::SetNextItemWidth(ImGui::CalcTextSize("YYYY-MM-DD").x + ImGui::GetStyle().FramePadding.x * 2.0f);
- const bool date_valid = date[0] == 0 || (IsDateValid(date) && valid);
- if (!date_valid)
- {
- ImGui::PushStyleColor(ImGuiCol_Border, IM_COL32(255, 0, 0, 255));
- ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);
- }
- bool date_changed = ImGui::InputTextWithHint(label, "YYYY-MM-DD", date, date_len);
- if (!date_valid)
- {
- ImGui::PopStyleVar();
- ImGui::PopStyleColor();
- }
- return date_changed;
-}
-
-static void FormatDate(ImU64 microseconds, char* buf, size_t buf_size)
-{
- time_t timestamp = (time_t)(microseconds / 1000000);
- tm* time = localtime(&timestamp);
- ImFormatString(buf, buf_size, "%04d-%02d-%02d", time->tm_year + 1900, time->tm_mon + 1, time->tm_mday);
-}
-
-static void FormatDateAndTime(ImU64 microseconds, char* buf, size_t buf_size)
-{
- time_t timestamp = (time_t)(microseconds / 1000000);
- tm* time = localtime(&timestamp);
- ImFormatString(buf, buf_size, "%04d-%02d-%02d %02d:%02d:%02d", time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec);
-}
-
-static void RenderFilterInput(ImGuiPerfTool* perf, const char* hint, float width = -FLT_MIN)
-{
- if (ImGui::IsWindowAppearing())
- strcpy(perf->_Filter, "");
- ImGui::SetNextItemWidth(width);
- ImGui::InputTextWithHint("##filter", hint, perf->_Filter, IM_ARRAYSIZE(perf->_Filter));
- if (ImGui::IsWindowAppearing())
- ImGui::SetKeyboardFocusHere();
-}
-
-static bool RenderMultiSelectFilter(ImGuiPerfTool* perf, const char* filter_hint, ImVector<const char*>* labels)
-{
- ImGuiContext& g = *ImGui::GetCurrentContext();
- ImGuiIO& io = ImGui::GetIO();
- ImGuiStorage& visibility = perf->_Visibility;
- bool modified = false;
- RenderFilterInput(perf, filter_hint, -(ImGui::CalcTextSize("(?)").x + g.Style.ItemSpacing.x));
- ImGui::SameLine();
- ImGui::TextDisabled("(?)");
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Hold CTRL to invert other items.\nHold SHIFT to close popup instantly.");
-
- // Keep popup open for multiple actions if SHIFT is pressed.
- if (!io.KeyShift)
- ImGui::PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);
-
- if (ImGui::MenuItem("Show All"))
- {
- for (const char* label : *labels)
- if (strstr(label, perf->_Filter) != NULL)
- visibility.SetBool(ImHashStr(label), true);
- modified = true;
- }
-
- if (ImGui::MenuItem("Hide All"))
- {
- for (const char* label : *labels)
- if (strstr(label, perf->_Filter) != NULL)
- visibility.SetBool(ImHashStr(label), false);
- modified = true;
- }
-
- // Render perf labels in reversed order. Labels are sorted, but stored in reversed order to render them on the plot
- // from top down (ImPlot renders stuff from bottom up).
- int filtered_entries = 0;
- for (int i = labels->Size - 1; i >= 0; i--)
- {
- const char* label = (*labels)[i];
- if (strstr(label, perf->_Filter) == NULL) // Filter out entries not matching a filter query
- continue;
-
- if (filtered_entries == 0)
- ImGui::Separator();
-
- ImGuiID build_id = ImHashStr(label);
- bool visible = visibility.GetBool(build_id, true);
- if (ImGui::MenuItem(label, NULL, &visible))
- {
- modified = true;
- if (io.KeyCtrl)
- {
- for (const char* label2 : *labels)
- {
- ImGuiID build_id2 = ImHashStr(label2);
- visibility.SetBool(build_id2, !visibility.GetBool(build_id2, true));
- }
- }
- else
- {
- visibility.SetBool(build_id, !visibility.GetBool(build_id, true));
- }
- }
- filtered_entries++;
- }
-
- if (!io.KeyShift)
- ImGui::PopItemFlag();
-
- return modified;
-}
-
-// Based on ImPlot::SetupFinish().
-#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
-static ImRect ImPlotGetYTickRect(int t, int y = 0)
-{
- ImPlotContext& gp = *GImPlot;
- ImPlotPlot& plot = *gp.CurrentPlot;
- ImPlotAxis& ax = plot.YAxis(y);
- const ImPlotTicker& tkc = ax.Ticker;
- const bool opp = ax.IsOpposite();
- ImRect result(1.0f, 1.0f, -1.0f, -1.0f);
- if (ax.HasTickLabels())
- {
- const ImPlotTick& tk = tkc.Ticks[t];
- const float datum = ax.Datum1 + (opp ? gp.Style.LabelPadding.x : (-gp.Style.LabelPadding.x - tk.LabelSize.x));
- if (tk.ShowLabel && tk.PixelPos >= plot.PlotRect.Min.y - 1 && tk.PixelPos <= plot.PlotRect.Max.y + 1)
- {
- ImVec2 start(datum, tk.PixelPos - 0.5f * tk.LabelSize.y);
- result.Min = start;
- result.Max = start + tk.LabelSize;
- }
- }
- return result;
-}
-#endif // #if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
-
-ImGuiPerfTool::ImGuiPerfTool()
-{
- _CsvParser = IM_NEW(ImGuiCsvParser)();
- Clear();
-}
-
-ImGuiPerfTool::~ImGuiPerfTool()
-{
- _SrcData.clear_destruct();
- _Batches.clear_destruct();
- IM_DELETE(_CsvParser);
-}
-
-void ImGuiPerfTool::AddEntry(ImGuiPerfToolEntry* entry)
-{
- if (strcmp(_FilterDateFrom, entry->Date) > 0)
- ImStrncpy(_FilterDateFrom, entry->Date, IM_ARRAYSIZE(_FilterDateFrom));
- if (strcmp(_FilterDateTo, entry->Date) < 0)
- ImStrncpy(_FilterDateTo, entry->Date, IM_ARRAYSIZE(_FilterDateTo));
-
- _SrcData.push_back(*entry);
- _Batches.clear_destruct();
-}
-
-void ImGuiPerfTool::_Rebuild()
-{
- if (_SrcData.empty())
- return;
-
- ImGuiStorage& temp_set = _TempSet;
- _Labels.resize(0);
- _LabelsVisible.resize(0);
- _InfoTableSort.resize(0);
- _Batches.clear_destruct();
- _InfoTableSortDirty = true;
-
- // Gather all visible labels. Legend batches will store data in this order.
- temp_set.Data.resize(0); // name_id:IsLabelSeen
- for (ImGuiPerfToolEntry& entry : _SrcData)
- {
- ImGuiID name_id = ImHashStr(entry.TestName);
- if (!temp_set.GetBool(name_id))
- {
- temp_set.SetBool(name_id, true);
- _Labels.push_back(entry.TestName);
- if (_IsVisibleTest(entry.TestName))
- _LabelsVisible.push_front(entry.TestName);
- }
- }
- int num_visible_labels = _LabelsVisible.Size;
-
- // Labels are sorted in reverse order so they appear to be oredered from top down.
- ImQsort(_Labels.Data, _Labels.Size, sizeof(const char*), &PerfToolComparerStr);
- ImQsort(_LabelsVisible.Data, num_visible_labels, sizeof(const char*), &PerfToolComparerStr);
-
- // _SrcData vector stores sorted raw entries of imgui_perflog.csv. Sorting is very important,
- // algorithm depends on data being correctly sorted. Sorting _SrcData is OK, because it is only
- // ever appended to and never written out to disk. Entries are sorted by multiple criteria,
- // in specified order:
- // 1. By branch name
- // 2. By build ID
- // 3. By run timestamp
- // 4. By test name
- // This results in a neatly partitioned dataset where similar data is grouped together and where perf test order
- // is consistent in all batches. Sorting by build ID _before_ timestamp is also important as we will be aggregating
- // entries by build ID instead of timestamp, when appropriate display mode is enabled.
- ImQsort(_SrcData.Data, _SrcData.Size, sizeof(ImGuiPerfToolEntry), &PerfToolComparerByEntryInfo);
-
- // Sort groups of entries into batches.
- const bool combine_by_build_info = _DisplayType == ImGuiPerfToolDisplayType_CombineByBuildInfo;
- _LabelBarCounts.Data.resize(0);
-
- // Process all batches. `entry` is always a first batch element (guaranteed by _SrcData being sorted by timestamp).
- // At the end of this loop we fast-forward until next batch (first entry having different batch id (which is a
- // timestamp or build info)).
- for (ImGuiPerfToolEntry* entry = _SrcData.begin(); entry < _SrcData.end();)
- {
- // Filtered out entries can be safely ignored. Note that entry++ does not follow logic of fast-forwarding to the
- // next batch, as found at the end of this loop. This is OK, because all entries belonging to a same batch will
- // also have same date.
- if ((_FilterDateFrom[0] && strcmp(entry->Date, _FilterDateFrom) < 0) || (_FilterDateTo[0] && strcmp(entry->Date, _FilterDateTo) > 0))
- {
- entry++;
- continue;
- }
-
- _Batches.push_back(ImGuiPerfToolBatch());
- ImGuiPerfToolBatch& batch = _Batches.back();
- batch.BatchID = GetBatchID(this, entry);
- batch.Entries.resize(num_visible_labels);
-
- // Fill in defaults. Done once before data aggregation loop, because same entry may be touched multiple times in
- // the following loop when entries are being combined by build info.
- for (int i = 0; i < num_visible_labels; i++)
- {
- ImGuiPerfToolEntry* e = &batch.Entries.Data[i];
- *e = *entry;
- e->DtDeltaMs = 0;
- e->NumSamples = 0;
- e->LabelIndex = i;
- e->TestName = _LabelsVisible.Data[i];
- }
-
- // Find perf test runs for this particular batch and accumulate them.
- for (int i = 0; i < num_visible_labels; i++)
- {
- // This inner loop walks all entries that belong to current batch. Due to sorting we are sure that batch
- // always starts with `entry`, and all entries that belong to a batch (whether we combine by build info or not)
- // will be grouped in _SrcData.
- ImGuiPerfToolEntry* aggregate = &batch.Entries.Data[i];
- for (ImGuiPerfToolEntry* e = entry; e < _SrcData.end() && GetBatchID(this, e) == batch.BatchID; e++)
- {
- if (strcmp(e->TestName, aggregate->TestName) != 0)
- continue;
- aggregate->DtDeltaMs += e->DtDeltaMs;
- aggregate->NumSamples++;
- aggregate->DtDeltaMsMin = ImMin(aggregate->DtDeltaMsMin, e->DtDeltaMs);
- aggregate->DtDeltaMsMax = ImMax(aggregate->DtDeltaMsMax, e->DtDeltaMs);
- }
- }
-
- // In case data is combined by build info, DtDeltaMs will be a sum of all combined entries. Average it out.
- if (combine_by_build_info)
- for (int i = 0; i < num_visible_labels; i++)
- {
- ImGuiPerfToolEntry* aggregate = &batch.Entries.Data[i];
- if (aggregate->NumSamples > 0)
- aggregate->DtDeltaMs /= aggregate->NumSamples;
- }
-
- // Advance to the next batch.
- batch.NumSamples = 1;
- if (combine_by_build_info)
- {
- ImU64 last_timestamp = entry->Timestamp;
- for (ImGuiID build_id = GetBuildID(entry); entry < _SrcData.end() && build_id == GetBuildID(entry);)
- {
- // Also count how many unique batches participate in this aggregated batch.
- if (entry->Timestamp != last_timestamp)
- {
- batch.NumSamples++;
- last_timestamp = entry->Timestamp;
- }
- entry++;
- }
- }
- else
- {
- for (ImU64 timestamp = entry->Timestamp; entry < _SrcData.end() && timestamp == entry->Timestamp;)
- entry++;
- }
- }
-
- // Create man entries for every batch.
- // Pushed after sorting so they are always at the start of the chart.
- const char* mean_labels[] = { "harmonic mean", "arithmetic mean", "geometric mean" };
- int num_visible_mean_labels = 0;
- for (const char* label : mean_labels)
- {
- _Labels.push_back(label);
- if (_IsVisibleTest(label))
- {
- _LabelsVisible.push_back(label);
- num_visible_mean_labels++;
- }
- }
- for (ImGuiPerfToolBatch& batch : _Batches)
- {
- double delta_sum = 0.0;
- double delta_prd = 1.0;
- double delta_rec = 0.0;
- for (int i = 0; i < batch.Entries.Size; i++)
- {
- ImGuiPerfToolEntry* entry = &batch.Entries.Data[i];
- delta_sum += entry->DtDeltaMs;
- delta_prd *= entry->DtDeltaMs;
- delta_rec += 1 / entry->DtDeltaMs;
- }
-
- int visible_label_i = 0;
- for (int i = 0; i < IM_ARRAYSIZE(mean_labels); i++)
- {
- if (!_IsVisibleTest(mean_labels[i]))
- continue;
-
- batch.Entries.push_back(ImGuiPerfToolEntry());
- ImGuiPerfToolEntry* mean_entry = &batch.Entries.back();
- *mean_entry = batch.Entries.Data[0];
- mean_entry->LabelIndex = _LabelsVisible.Size - num_visible_mean_labels + visible_label_i;
- mean_entry->TestName = _LabelsVisible.Data[mean_entry->LabelIndex];
- mean_entry->GitBranchName = "";
- mean_entry->BuildType = "";
- mean_entry->Compiler = "";
- mean_entry->OS = "";
- mean_entry->Cpu = "";
- mean_entry->Date = "";
- visible_label_i++;
- if (i == 0)
- mean_entry->DtDeltaMs = num_visible_labels / delta_rec;
- else if (i == 1)
- mean_entry->DtDeltaMs = delta_sum / num_visible_labels;
- else if (i == 2)
- mean_entry->DtDeltaMs = pow(delta_prd, 1.0 / num_visible_labels);
- else
- IM_ASSERT(0);
- }
- IM_ASSERT(batch.Entries.Size == _LabelsVisible.Size);
- }
-
- // Find number of bars (batches) each label will render.
- for (ImGuiPerfToolBatch& batch : _Batches)
- {
- if (!_IsVisibleBuild(&batch))
- continue;
-
- for (ImGuiPerfToolEntry& entry : batch.Entries)
- {
- ImGuiID label_id = ImHashStr(entry.TestName);
- int num_bars = _LabelBarCounts.GetInt(label_id) + 1;
- _LabelBarCounts.SetInt(label_id, num_bars);
- }
- }
-
- // Index branches, used for per-branch colors.
- temp_set.Data.resize(0); // ImHashStr(branch_name):linear_index
- int branch_index_last = 0;
- _BaselineBatchIndex = -1;
- for (ImGuiPerfToolBatch& batch : _Batches)
- {
- if (batch.Entries.empty())
- continue;
- ImGuiPerfToolEntry* entry = &batch.Entries.Data[0];
- ImGuiID branch_hash = ImHashStr(entry->GitBranchName);
- batch.BranchIndex = temp_set.GetInt(branch_hash, -1);
- if (batch.BranchIndex < 0)
- {
- batch.BranchIndex = branch_index_last++;
- temp_set.SetInt(branch_hash, batch.BranchIndex);
- }
-
- if (_BaselineBatchIndex < 0)
- if ((combine_by_build_info && GetBuildID(entry) == _BaselineBuildId) || _BaselineTimestamp == entry->Timestamp)
- _BaselineBatchIndex = _Batches.index_from_ptr(&batch);
- }
-
- // When per-branch colors are enabled we aggregate sample counts and set them to all batches with identical build info.
- temp_set.Data.resize(0); // build_id:TotalSamples
- if (_DisplayType == ImGuiPerfToolDisplayType_PerBranchColors)
- {
- // Aggregate totals to temp_set.
- for (ImGuiPerfToolBatch& batch : _Batches)
- {
- ImGuiID build_id = GetBuildID(&batch);
- temp_set.SetInt(build_id, temp_set.GetInt(build_id, 0) + batch.NumSamples);
- }
-
- // Fill in batch sample counts.
- for (ImGuiPerfToolBatch& batch : _Batches)
- {
- ImGuiID build_id = GetBuildID(&batch);
- batch.NumSamples = temp_set.GetInt(build_id, 1);
- }
- }
-
- _NumVisibleBuilds = PerfToolCountBuilds(this, true);
- _NumUniqueBuilds = PerfToolCountBuilds(this, false);
-
- _CalculateLegendAlignment();
- temp_set.Data.resize(0);
-}
-
-void ImGuiPerfTool::Clear()
-{
- _Labels.clear();
- _LabelsVisible.clear();
- _Batches.clear_destruct();
- _Visibility.Clear();
- _SrcData.clear_destruct();
- _CsvParser->Clear();
-
- ImStrncpy(_FilterDateFrom, "9999-99-99", IM_ARRAYSIZE(_FilterDateFrom));
- ImStrncpy(_FilterDateTo, "0000-00-00", IM_ARRAYSIZE(_FilterDateFrom));
-}
-
-bool ImGuiPerfTool::LoadCSV(const char* filename)
-{
- if (filename == NULL)
- filename = IMGUI_PERFLOG_DEFAULT_FILENAME;
-
- Clear();
-
- ImGuiCsvParser* parser = _CsvParser;
- parser->Columns = 11;
- if (!parser->Load(filename))
- return false;
-
- // Read perf test entries from CSV
- for (int row = 0; row < parser->Rows; row++)
- {
- ImGuiPerfToolEntry entry;
- int col = 0;
- sscanf(parser->GetCell(row, col++), "%llu", &entry.Timestamp);
- entry.Category = parser->GetCell(row, col++);
- entry.TestName = parser->GetCell(row, col++);
- sscanf(parser->GetCell(row, col++), "%lf", &entry.DtDeltaMs);
- sscanf(parser->GetCell(row, col++), "x%d", &entry.PerfStressAmount);
- entry.GitBranchName = parser->GetCell(row, col++);
- entry.BuildType = parser->GetCell(row, col++);
- entry.Cpu = parser->GetCell(row, col++);
- entry.OS = parser->GetCell(row, col++);
- entry.Compiler = parser->GetCell(row, col++);
- entry.Date = parser->GetCell(row, col++);
- AddEntry(&entry);
- }
-
- return true;
-}
-
-void ImGuiPerfTool::ViewOnly(const char** perf_names)
-{
- // Data would not be built if we tried to view perftool of a particular test without first opening perftool via button. We need data to be built to hide perf tests.
- if (_Batches.empty())
- _Rebuild();
-
- // Hide other perf tests.
- for (const char* label : _Labels)
- {
- bool visible = false;
- for (const char** p_name = perf_names; !visible && *p_name; p_name++)
- visible |= strcmp(label, *p_name) == 0;
- _Visibility.SetBool(ImHashStr(label), visible);
- }
-}
-
-void ImGuiPerfTool::ViewOnly(const char* perf_name)
-{
- const char* names[] = { perf_name, NULL };
- ViewOnly(names);
-}
-
-ImGuiPerfToolEntry* ImGuiPerfTool::GetEntryByBatchIdx(int idx, const char* perf_name)
-{
- if (idx < 0)
- return NULL;
- IM_ASSERT(idx < _Batches.Size);
- ImGuiPerfToolBatch& batch = _Batches.Data[idx];
- for (int i = 0; i < batch.Entries.Size; i++)
- if (ImGuiPerfToolEntry* entry = &batch.Entries.Data[i])
- if (strcmp(entry->TestName, perf_name) == 0)
- return entry;
- return NULL;
-}
-
-bool ImGuiPerfTool::_IsVisibleBuild(ImGuiPerfToolBatch* batch)
-{
- IM_ASSERT(batch != NULL);
- if (batch->Entries.empty())
- return false; // All entries are hidden.
- return _IsVisibleBuild(&batch->Entries.Data[0]);
-}
-
-bool ImGuiPerfTool::_IsVisibleBuild(ImGuiPerfToolEntry* entry)
-{
- return _Visibility.GetBool(ImHashStr(entry->GitBranchName), true) &&
- _Visibility.GetBool(ImHashStr(entry->Compiler), true) &&
- _Visibility.GetBool(ImHashStr(entry->Cpu), true) &&
- _Visibility.GetBool(ImHashStr(entry->OS), true) &&
- _Visibility.GetBool(ImHashStr(entry->BuildType), true);
-}
-
-bool ImGuiPerfTool::_IsVisibleTest(const char* test_name)
-{
- return _Visibility.GetBool(ImHashStr(test_name), true);
-}
-
-void ImGuiPerfTool::_CalculateLegendAlignment()
-{
- // Estimate paddings for legend format so it looks nice and aligned
- // FIXME: Rely on font being monospace. May need to recalculate every frame on a per-need basis based on font?
- _AlignStress = _AlignType = _AlignCpu = _AlignOs = _AlignCompiler = _AlignBranch = _AlignSamples = 0;
- for (ImGuiPerfToolBatch& batch : _Batches)
- {
- if (batch.Entries.empty())
- continue;
- ImGuiPerfToolEntry* entry = &batch.Entries.Data[0];
- if (!_IsVisibleBuild(entry))
- continue;
- _AlignStress = ImMax(_AlignStress, (int)ceil(log10(entry->PerfStressAmount)));
- _AlignType = ImMax(_AlignType, (int)strlen(entry->BuildType));
- _AlignCpu = ImMax(_AlignCpu, (int)strlen(entry->Cpu));
- _AlignOs = ImMax(_AlignOs, (int)strlen(entry->OS));
- _AlignCompiler = ImMax(_AlignCompiler, (int)strlen(entry->Compiler));
- _AlignBranch = ImMax(_AlignBranch, (int)strlen(entry->GitBranchName));
- _AlignSamples = ImMax(_AlignSamples, (int)Str16f("%d", entry->NumSamples).length());
- }
-}
-
-bool ImGuiPerfTool::SaveHtmlReport(const char* file_name, const char* image_file)
-{
- if (!ImFileCreateDirectoryChain(file_name, ImPathFindFilename(file_name)))
- return false;
-
- FILE* fp = fopen(file_name, "w+");
- if (fp == NULL)
- return false;
-
- fprintf(fp, "<!doctype html>\n"
- "<html>\n"
- "<head>\n"
- " <meta charset=\"utf-8\"/>\n"
- " <title>Dear ImGui perf report</title>\n"
- "</head>\n"
- "<body>\n"
- " <pre id=\"content\">\n");
-
- // Embed performance chart.
- fprintf(fp, "## Dear ImGui perf report\n\n");
-
- if (image_file != NULL)
- {
- FILE* fp_img = fopen(image_file, "rb");
- if (fp_img != NULL)
- {
- ImVector<char> image_buffer;
- ImVector<char> base64_buffer;
- fseek(fp_img, 0, SEEK_END);
- image_buffer.resize((int)ftell(fp_img));
- base64_buffer.resize(((image_buffer.Size / 3) + 1) * 4 + 1);
- rewind(fp_img);
- fread(image_buffer.Data, 1, image_buffer.Size, fp_img);
- fclose(fp_img);
- int len = ImStrBase64Encode((unsigned char*)image_buffer.Data, base64_buffer.Data, image_buffer.Size);
- base64_buffer.Data[len] = 0;
- fprintf(fp, "![](data:image/png;base64,%s)\n\n", base64_buffer.Data);
- }
- }
-
- // Print info table.
- const bool combine_by_build_info = _DisplayType == ImGuiPerfToolDisplayType_CombineByBuildInfo;
- for (const auto& column_info : PerfToolColumnInfo)
- if (column_info.ShowAlways || combine_by_build_info)
- fprintf(fp, "| %s ", column_info.Title);
- fprintf(fp, "|\n");
- for (const auto& column_info : PerfToolColumnInfo)
- if (column_info.ShowAlways || combine_by_build_info)
- fprintf(fp, "| -- ");
- fprintf(fp, "|\n");
-
- for (int row_index = _InfoTableSort.Size - 1; row_index >= 0; row_index--)
- {
- int batch_index_sorted, entry_index_sorted;
- _UnpackSortedKey(_InfoTableSort[row_index], &batch_index_sorted, &entry_index_sorted);
- ImGuiPerfToolBatch* batch = &_Batches[batch_index_sorted];
- ImGuiPerfToolEntry* entry = &batch->Entries[entry_index_sorted];
- const char* test_name = entry->TestName;
- if (!_IsVisibleBuild(entry) || entry->NumSamples == 0)
- continue;
-
- ImGuiPerfToolEntry* baseline_entry = GetEntryByBatchIdx(_BaselineBatchIndex, test_name);
- for (int i = 0; i < IM_ARRAYSIZE(PerfToolColumnInfo); i++)
- {
- Str30f label("");
- const ImGuiPerfToolColumnInfo& column_info = PerfToolColumnInfo[i];
- if (column_info.ShowAlways || combine_by_build_info)
- {
- switch (i)
- {
- case 0:
- {
- char date[64];
- FormatDateAndTime(entry->Timestamp, date, IM_ARRAYSIZE(date));
- fprintf(fp, "| %s ", date);
- break;
- }
- case 1: fprintf(fp, "| %s ", entry->TestName); break;
- case 2: fprintf(fp, "| %s ", entry->GitBranchName); break;
- case 3: fprintf(fp, "| %s ", entry->Compiler); break;
- case 4: fprintf(fp, "| %s ", entry->OS); break;
- case 5: fprintf(fp, "| %s ", entry->Cpu); break;
- case 6: fprintf(fp, "| %s ", entry->BuildType); break;
- case 7: fprintf(fp, "| x%d ", entry->PerfStressAmount); break;
- case 8: fprintf(fp, "| %.2f ", entry->DtDeltaMs); break;
- case 9: fprintf(fp, "| %.2f ", entry->DtDeltaMsMin); break;
- case 10: fprintf(fp, "| %.2f ", entry->DtDeltaMsMax); break;
- case 11: fprintf(fp, "| %d ", entry->NumSamples); break;
- case 12: FormatVsBaseline(entry, baseline_entry, label); fprintf(fp, "| %s ", label.c_str()); break;
- default: IM_ASSERT(0); break;
- }
- }
- }
- fprintf(fp, "|\n");
- }
-
- fprintf(fp, "</pre>\n"
- " <script src=\"https://cdn.jsdelivr.net/npm/marked@4.0.0/marked.min.js\"></script>\n"
- " <script>\n"
- " var content = document.getElementById('content');\n"
- " content.innerHTML = marked.parse(content.innerText);\n"
- " </script>\n"
- "</body>\n"
- "</html>\n");
-
- fclose(fp);
- return true;
-}
-
-void ImGuiPerfTool::_SetBaseline(int batch_index)
-{
- IM_ASSERT(batch_index < _Batches.Size);
- _BaselineBatchIndex = batch_index;
- if (batch_index >= 0)
- {
- _BaselineTimestamp = _Batches.Data[batch_index].Entries.Data[0].Timestamp;
- _BaselineBuildId = GetBuildID(&_Batches.Data[batch_index]);
- }
-}
-
-//-------------------------------------------------------------------------
-// [SECTION] USER INTERFACE
-//-------------------------------------------------------------------------
-
-void ImGuiPerfTool::ShowPerfToolWindow(ImGuiTestEngine* engine, bool* p_open)
-{
- if (!ImGui::Begin("Dear ImGui Perf Tool", p_open))
- {
- ImGui::End();
- return;
- }
-
- if (ImGui::IsWindowAppearing() && Empty())
- LoadCSV();
-
- ImGuiStyle& style = ImGui::GetStyle();
-
- // -----------------------------------------------------------------------------------------------------------------
- // Render utility buttons
- // -----------------------------------------------------------------------------------------------------------------
-
- // Date filter
- ImGui::AlignTextToFramePadding();
- ImGui::TextUnformatted("Date Range:");
- ImGui::SameLine();
-
- bool dirty = _Batches.empty();
- bool date_changed = InputDate("##date-from", _FilterDateFrom, IM_ARRAYSIZE(_FilterDateFrom),
- (strcmp(_FilterDateFrom, _FilterDateTo) <= 0 || !*_FilterDateTo));
- if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right))
- ImGui::OpenPopup("InputDate From Menu");
- ImGui::SameLine(0, 0.0f);
- ImGui::TextUnformatted("..");
- ImGui::SameLine(0, 0.0f);
- date_changed |= InputDate("##date-to", _FilterDateTo, IM_ARRAYSIZE(_FilterDateTo),
- (strcmp(_FilterDateFrom, _FilterDateTo) <= 0 || !*_FilterDateFrom));
- if (date_changed)
- {
- dirty = (!_FilterDateFrom[0] || IsDateValid(_FilterDateFrom)) && (!_FilterDateTo[0] || IsDateValid(_FilterDateTo));
- if (_FilterDateFrom[0] && _FilterDateTo[0])
- dirty &= strcmp(_FilterDateFrom, _FilterDateTo) <= 0;
- }
- if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right))
- ImGui::OpenPopup("InputDate To Menu");
- ImGui::SameLine();
-
- for (int i = 0; i < 2; i++)
- {
- if (ImGui::BeginPopup(i == 0 ? "InputDate From Menu" : "InputDate To Menu"))
- {
- char* date = i == 0 ? _FilterDateFrom : _FilterDateTo;
- int date_size = i == 0 ? IM_ARRAYSIZE(_FilterDateFrom) : IM_ARRAYSIZE(_FilterDateTo);
- if (i == 0 && ImGui::MenuItem("Set Min"))
- {
- for (ImGuiPerfToolEntry& entry : _SrcData)
- if (strcmp(date, entry.Date) > 0)
- {
- ImStrncpy(date, entry.Date, date_size);
- dirty = true;
- }
- }
- if (ImGui::MenuItem("Set Max"))
- {
- for (ImGuiPerfToolEntry& entry : _SrcData)
- if (strcmp(date, entry.Date) < 0)
- {
- ImStrncpy(date, entry.Date, date_size);
- dirty = true;
- }
- }
- if (ImGui::MenuItem("Set Today"))
- {
- time_t now = time(NULL);
- FormatDate((ImU64)now * 1000000, date, date_size);
- dirty = true;
- }
- ImGui::EndPopup();
- }
- }
-
- if (ImGui::Button(Str64f("Filter builds (%d/%d)###Filter builds", _NumVisibleBuilds, _NumUniqueBuilds).c_str()))
- ImGui::OpenPopup("Filter builds");
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Hide or show individual builds.");
- ImGui::SameLine();
- if (ImGui::Button(Str64f("Filter tests (%d/%d)###Filter tests", _LabelsVisible.Size, _Labels.Size).c_str()))
- ImGui::OpenPopup("Filter perfs");
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Hide or show individual tests.");
- ImGui::SameLine();
-
- dirty |= Button3("Combine", (int*)&_DisplayType);
- if (ImGui::IsItemHovered())
- {
- ImGui::BeginTooltip();
- ImGui::RadioButton("Display each run separately", _DisplayType == ImGuiPerfToolDisplayType_Simple);
- ImGui::RadioButton("Use one color per branch. Disables baseline comparisons!", _DisplayType == ImGuiPerfToolDisplayType_PerBranchColors);
- ImGui::RadioButton("Combine multiple runs with same build info into one averaged build entry.", _DisplayType == ImGuiPerfToolDisplayType_CombineByBuildInfo);
- ImGui::EndTooltip();
- }
-
- ImGui::SameLine();
- if (_ReportGenerating && ImGuiTestEngine_IsTestQueueEmpty(engine))
- {
- _ReportGenerating = false;
- ImOsOpenInShell(PerfToolReportDefaultOutputPath);
- }
- if (_Batches.empty())
- ImGui::BeginDisabled();
- if (ImGui::Button("Html Export"))
- {
- // In order to capture a screenshot Report is saved by executing a "capture_perf_report" test.
- _ReportGenerating = true;
- ImGuiTestEngine_QueueTests(engine, ImGuiTestGroup_Tests, "capture_perf_report");
- }
- if (_Batches.empty())
- ImGui::EndDisabled();
- ImGui::SameLine();
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Generate a report and open it in the browser.");
-
- // Align help button to the right.
- float help_pos = ImGui::GetWindowContentRegionMax().x - style.FramePadding.x * 2 - ImGui::CalcTextSize("(?)").x;
- if (help_pos > ImGui::GetCursorPosX())
- ImGui::SetCursorPosX(help_pos);
-
- ImGui::TextDisabled("(?)");
- if (ImGui::IsItemHovered())
- {
- ImGui::BeginTooltip();
- ImGui::BulletText("To change baseline build, double-click desired build in the legend.");
- ImGui::BulletText("Extra information is displayed when hovering bars of a particular perf test and holding SHIFT.");
- ImGui::BulletText("Double-click plot to fit plot into available area.");
- ImGui::EndTooltip();
- }
-
- if (ImGui::BeginPopup("Filter builds"))
- {
- ImGuiStorage& temp_set = _TempSet;
- temp_set.Data.resize(0); // ImHashStr(BuildProperty):seen
-
- static const char* columns[] = { "Branch", "Build", "CPU", "OS", "Compiler" };
- bool show_all = ImGui::Button("Show All");
- ImGui::SameLine();
- bool hide_all = ImGui::Button("Hide All");
- if (ImGui::BeginTable("Builds", IM_ARRAYSIZE(columns), ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit))
- {
- for (int i = 0; i < IM_ARRAYSIZE(columns); i++)
- ImGui::TableSetupColumn(columns[i]);
- ImGui::TableHeadersRow();
-
- // Find columns with nothing checked.
- bool checked_any[] = { false, false, false, false, false };
- for (ImGuiPerfToolEntry& entry : _SrcData)
- {
- const char* properties[] = { entry.GitBranchName, entry.BuildType, entry.Cpu, entry.OS, entry.Compiler };
- for (int i = 0; i < IM_ARRAYSIZE(properties); i++)
- {
- ImGuiID hash = ImHashStr(properties[i]);
- checked_any[i] |= _Visibility.GetBool(hash, true);
- }
- }
-
- int property_offsets[] =
- {
- offsetof(ImGuiPerfToolEntry, GitBranchName),
- offsetof(ImGuiPerfToolEntry, BuildType),
- offsetof(ImGuiPerfToolEntry, Cpu),
- offsetof(ImGuiPerfToolEntry, OS),
- offsetof(ImGuiPerfToolEntry, Compiler),
- };
-
- ImGui::TableNextRow();
- for (int i = 0; i < IM_ARRAYSIZE(property_offsets); i++)
- {
- ImGui::TableSetColumnIndex(i);
- for (ImGuiPerfToolEntry& entry : _SrcData)
- {
- const char* property = *(const char**)((const char*)&entry + property_offsets[i]);
- ImGuiID hash = ImHashStr(property);
- if (temp_set.GetBool(hash))
- continue;
- temp_set.SetBool(hash, true);
-
- bool visible = _Visibility.GetBool(hash, true) || show_all;
- if (hide_all)
- visible = false;
- bool modified = ImGui::Checkbox(property, &visible) || show_all || hide_all;
- _Visibility.SetBool(hash, visible);
- if (modified)
- {
- _CalculateLegendAlignment();
- _NumVisibleBuilds = PerfToolCountBuilds(this, true);
- dirty = true;
- }
- if (!checked_any[i])
- {
- ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImColor(1.0f, 0.0f, 0.0f, 0.2f));
- if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered)
- ImGui::SetTooltip("Check at least one item in each column to see any data.");
- }
- }
- }
- ImGui::EndTable();
- }
- ImGui::EndPopup();
- }
-
- if (ImGui::BeginPopup("Filter perfs"))
- {
- dirty |= RenderMultiSelectFilter(this, "Filter by perf test", &_Labels);
- if (ImGui::IsKeyPressed(ImGuiKey_Escape))
- ImGui::CloseCurrentPopup();
- ImGui::EndPopup();
- }
-
- if (dirty)
- _Rebuild();
-
- // Rendering a plot of empty dataset is not possible.
- if (_Batches.empty() || _LabelsVisible.Size == 0 || _NumVisibleBuilds == 0)
- {
- ImGui::TextUnformatted("No data is available. Run some perf tests or adjust filter settings.");
- }
- else
- {
-#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
- // Splitter between two following child windows is rendered first.
- float plot_height = 0.0f;
- float& table_height = _InfoTableHeight;
- ImGui::Splitter("splitter", &plot_height, &table_height, ImGuiAxis_Y, +1);
-
- // Double-click to move splitter to bottom
- if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
- {
- table_height = 0;
- plot_height = ImGui::GetContentRegionAvail().y - style.ItemSpacing.y;
- ImGui::ClearActiveID();
- }
-
- // Render entries plot
- if (ImGui::BeginChild(ImGui::GetID("plot"), ImVec2(0, plot_height)))
- _ShowEntriesPlot();
- ImGui::EndChild();
-
- // Render entries tables
- if (table_height > 0.0f)
- {
- if (ImGui::BeginChild(ImGui::GetID("info-table"), ImVec2(0, table_height)))
- _ShowEntriesTable();
- ImGui::EndChild();
- }
-#else
- _ShowEntriesTable();
-#endif
- }
- ImGui::End();
-}
-
-#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
-static double GetLabelVerticalOffset(double occupy_h, int max_visible_builds, int now_visible_builds)
-{
- const double h = occupy_h / (float)max_visible_builds;
- double offset = -h * ((max_visible_builds - 1) * 0.5);
- return (double)now_visible_builds * h + offset;
-}
-#endif
-
-void ImGuiPerfTool::_ShowEntriesPlot()
-{
-#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
- ImGuiIO& io = ImGui::GetIO();
- ImGuiStyle& style = ImGui::GetStyle();
- Str256 label;
- Str256 display_label;
-
- ImPlot::PushStyleColor(ImPlotCol_AxisBgHovered, IM_COL32(0, 0, 0, 0));
- ImPlot::PushStyleColor(ImPlotCol_AxisBgActive, IM_COL32(0, 0, 0, 0));
- if (!ImPlot::BeginPlot("PerfTool", ImVec2(-1, -1), ImPlotFlags_NoTitle))
- return;
-
- ImPlot::SetupAxis(ImAxis_X1, NULL, ImPlotAxisFlags_NoTickLabels);
- if (_LabelsVisible.Size > 1)
- {
- ImPlot::SetupAxisTicks(ImAxis_Y1, 0, _LabelsVisible.Size, _LabelsVisible.Size, _LabelsVisible.Data);
- }
- else if (_LabelsVisible.Size == 1)
- {
- const char* labels[] = { _LabelsVisible[0], "" };
- ImPlot::SetupAxisTicks(ImAxis_Y1, 0, _LabelsVisible.Size, 2, labels);
- }
- ImPlot::SetupLegend(ImPlotLocation_NorthEast);
-
- // Amount of vertical space bars of one label will occupy. 1.0 would leave no space between bars of adjacent labels.
- const float occupy_h = 0.8f;
-
- // Plot bars
- bool legend_hovered = false;
- ImGuiStorage& temp_set = _TempSet;
- temp_set.Data.resize(0); // ImHashStr(TestName):now_visible_builds_i
- int current_baseline_batch_index = _BaselineBatchIndex; // Cache this value before loop, so toggling it does not create flicker.
- for (int batch_index = 0; batch_index < _Batches.Size; batch_index++)
- {
- ImGuiPerfToolBatch& batch = _Batches[batch_index];
- if (!_IsVisibleBuild(&batch.Entries.Data[0]))
- continue;
-
- // Plot bars.
- label.clear();
- display_label.clear();
- PerfToolFormatBuildInfo(this, &label, &batch);
- display_label.append(label.c_str());
- ImGuiID batch_label_id;
- bool baseline_match = false;
- if (_DisplayType == ImGuiPerfToolDisplayType_PerBranchColors)
- {
- // No "vs baseline" comparison for per-branch colors, because runs are combined in the legend, but not in the info table.
- batch_label_id = GetBuildID(&batch);
- }
- else
- {
- batch_label_id = ImHashData(&batch.BatchID, sizeof(batch.BatchID));
- baseline_match = current_baseline_batch_index == batch_index;
- }
- display_label.appendf("%s###%08X", baseline_match ? " *" : "", batch_label_id);
-
- // Plot all bars one by one, so batches with varying number of bars would not contain empty holes.
- for (ImGuiPerfToolEntry& entry : batch.Entries)
- {
- if (entry.NumSamples == 0)
- continue; // Dummy entry, perf did not run for this test in this batch.
- ImGuiID label_id = ImHashStr(entry.TestName);
- const int max_visible_builds = _LabelBarCounts.GetInt(label_id);
- const int now_visible_builds = temp_set.GetInt(label_id);
- temp_set.SetInt(label_id, now_visible_builds + 1);
- double y_pos = (double)entry.LabelIndex + GetLabelVerticalOffset(occupy_h, max_visible_builds, now_visible_builds);
- ImPlot::SetNextFillStyle(ImPlot::GetColormapColor(_DisplayType == ImGuiPerfToolDisplayType_PerBranchColors ? batch.BranchIndex : batch_index));
- ImPlot::PlotBars<double>(display_label.c_str(), &entry.DtDeltaMs, &y_pos, 1, occupy_h / (double)max_visible_builds, ImPlotBarsFlags_Horizontal);
- }
- legend_hovered |= ImPlot::IsLegendEntryHovered(display_label.c_str());
-
- // Set baseline.
- if (ImPlot::IsLegendEntryHovered(display_label.c_str()))
- {
- if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
- _SetBaseline(batch_index);
- }
- }
-
- // Plot highlights.
- ImPlotContext& gp = *GImPlot;
- ImPlotPlot& plot = *gp.CurrentPlot;
- _PlotHoverTest = -1;
- _PlotHoverBatch = -1;
- _PlotHoverTestLabel = false;
- bool can_highlight = !legend_hovered && (ImPlot::IsPlotHovered() || ImPlot::IsAxisHovered(ImAxis_Y1));
- ImDrawList* plot_draw_list = ImPlot::GetPlotDrawList();
-
- // Highlight bars when hovering a label.
- int hovered_label_index = -1;
- for (int i = 0; i < _LabelsVisible.Size && can_highlight; i++)
- {
- ImRect label_rect_loose = ImPlotGetYTickRect(i); // Rect around test label
- ImRect label_rect_tight; // Rect around test label, covering bar height and label area width
- label_rect_tight.Min.y = ImPlot::PlotToPixels(0, (float)i + 0.5f).y;
- label_rect_tight.Max.y = ImPlot::PlotToPixels(0, (float)i - 0.5f).y;
- label_rect_tight.Min.x = plot.CanvasRect.Min.x;
- label_rect_tight.Max.x = plot.PlotRect.Min.x;
-
- ImRect rect_bars; // Rect around bars only
- rect_bars.Min.x = plot.PlotRect.Min.x;
- rect_bars.Max.x = plot.PlotRect.Max.x;
- rect_bars.Min.y = ImPlot::PlotToPixels(0, (float)i + 0.5f).y;
- rect_bars.Max.y = ImPlot::PlotToPixels(0, (float)i - 0.5f).y;
-
- // Render underline signaling it is clickable. Clicks are handled when rendering info table.
- if (label_rect_loose.Contains(io.MousePos))
- {
- ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
- plot_draw_list->AddLine(ImFloor(label_rect_loose.GetBL()), ImFloor(label_rect_loose.GetBR()),
- ImColor(style.Colors[ImGuiCol_Text]));
- }
-
- // Highlight bars belonging to hovered label.
- if (label_rect_tight.Contains(io.MousePos))
- {
- plot_draw_list->AddRectFilled(rect_bars.Min, rect_bars.Max, ImColor(style.Colors[ImGuiCol_TextSelectedBg]));
- _PlotHoverTestLabel = true;
- _PlotHoverTest = i;
- }
-
- if (rect_bars.Contains(io.MousePos))
- hovered_label_index = i;
- }
-
- // Highlight individual bars when hovering them on the plot or info table.
- temp_set.Data.resize(0); // ImHashStr(hovered_label):now_visible_builds_i
- if (hovered_label_index < 0)
- hovered_label_index = _TableHoveredTest;
- if (hovered_label_index >= 0)
- {
- const char* hovered_label = _LabelsVisible.Data[hovered_label_index];
- ImGuiID label_id = ImHashStr(hovered_label);
- for (ImGuiPerfToolBatch& batch : _Batches)
- {
- int batch_index = _Batches.index_from_ptr(&batch);
- if (!_IsVisibleBuild(&batch))
- continue;
-
- ImGuiPerfToolEntry* entry = &batch.Entries.Data[hovered_label_index];
- if (entry->NumSamples == 0)
- continue; // Dummy entry, perf did not run for this test in this batch.
-
- int max_visible_builds = _LabelBarCounts.GetInt(label_id);
- const int now_visible_builds = temp_set.GetInt(label_id);
- temp_set.SetInt(label_id, now_visible_builds + 1);
- float h = occupy_h / (float)max_visible_builds;
- float y_pos = (float)entry->LabelIndex;
- y_pos += (float)GetLabelVerticalOffset(occupy_h, max_visible_builds, now_visible_builds);
- ImRect rect_bar; // Rect around hovered bar only
- rect_bar.Min.x = plot.PlotRect.Min.x;
- rect_bar.Max.x = plot.PlotRect.Max.x;
- rect_bar.Min.y = ImPlot::PlotToPixels(0, y_pos - h * 0.5f + h).y; // ImPlot y_pos is for bar center, therefore we adjust positions by half-height to get a bounding box.
- rect_bar.Max.y = ImPlot::PlotToPixels(0, y_pos - h * 0.5f).y;
-
- // Mouse is hovering label or bars of a perf test - highlight them in info table.
- if (_PlotHoverTest < 0 && rect_bar.Min.y <= io.MousePos.y && io.MousePos.y < rect_bar.Max.y && io.MousePos.x > plot.PlotRect.Min.x)
- {
- // _LabelsVisible is inverted to make perf test order match info table order. Revert it back.
- _PlotHoverTest = hovered_label_index;
- _PlotHoverBatch = batch_index;
- plot_draw_list->AddRectFilled(rect_bar.Min, rect_bar.Max, ImColor(style.Colors[ImGuiCol_TextSelectedBg]));
- }
-
- // Mouse is hovering a row in info table - highlight relevant bars on the plot.
- if (_TableHoveredBatch == batch_index && _TableHoveredTest == hovered_label_index)
- plot_draw_list->AddRectFilled(rect_bar.Min, rect_bar.Max, ImColor(style.Colors[ImGuiCol_TextSelectedBg]));
- }
- }
-
- if (io.KeyShift && _PlotHoverTest >= 0)
- {
- // Info tooltip with delta times of each batch for a hovered test.
- const char* test_name = _LabelsVisible.Data[_PlotHoverTest];
- ImGui::BeginTooltip();
- float w = ImGui::CalcTextSize(test_name).x;
- float total_w = ImGui::GetContentRegionAvail().x;
- if (total_w > w)
- ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (total_w - w) * 0.5f);
- ImGui::TextUnformatted(test_name);
-
- for (int i = 0; i < _Batches.Size; i++)
- {
- if (ImGuiPerfToolEntry* hovered_entry = GetEntryByBatchIdx(i, test_name))
- ImGui::Text("%s %.3fms", label.c_str(), hovered_entry->DtDeltaMs);
- else
- ImGui::Text("%s --", label.c_str());
- }
- ImGui::EndTooltip();
- }
-
- ImPlot::EndPlot();
- ImPlot::PopStyleColor(2);
-#else
- ImGui::TextUnformatted("Not enabled because ImPlot is not available (IMGUI_TEST_ENGINE_ENABLE_IMPLOT=0).");
-#endif
-}
-
-void ImGuiPerfTool::_ShowEntriesTable()
-{
- ImGuiTableFlags table_flags = ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_Sortable |
- ImGuiTableFlags_SortMulti | ImGuiTableFlags_SortTristate | ImGuiTableFlags_Resizable |
- ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY;
- if (!ImGui::BeginTable("PerfInfo", IM_ARRAYSIZE(PerfToolColumnInfo), table_flags))
- return;
-
- ImGuiStyle& style = ImGui::GetStyle();
- int num_visible_labels = _LabelsVisible.Size;
-
- // Test name column is not sorted because we do sorting only within perf runs of a particular tests,
- // so as far as sorting function is concerned all items in first column are identical.
- for (int i = 0; i < IM_ARRAYSIZE(PerfToolColumnInfo); i++)
- {
- const ImGuiPerfToolColumnInfo& info = PerfToolColumnInfo[i];
- ImGuiTableColumnFlags column_flags = info.Flags;
- if (i == 0 && _DisplayType != ImGuiPerfToolDisplayType_Simple)
- column_flags |= ImGuiTableColumnFlags_Disabled; // Date only visible in non-combining mode.
- if (!info.ShowAlways && _DisplayType != ImGuiPerfToolDisplayType_CombineByBuildInfo)
- column_flags |= ImGuiTableColumnFlags_Disabled;
- ImGui::TableSetupColumn(info.Title, column_flags);
- }
- ImGui::TableSetupScrollFreeze(0, 1);
-
- if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs())
- if (sorts_specs->SpecsDirty || _InfoTableSortDirty)
- {
- // Fill sort table with unsorted indices.
- sorts_specs->SpecsDirty = _InfoTableSortDirty = false;
-
- // Reinitialize sorting table to unsorted state.
- _InfoTableSort.resize(num_visible_labels * _Batches.Size);
- for (int entry_index = 0, i = 0; entry_index < num_visible_labels; entry_index++)
- for (int batch_index = 0; batch_index < _Batches.Size; batch_index++, i++)
- _InfoTableSort.Data[i] = (((ImU64)batch_index * num_visible_labels + entry_index) << 24) | i;
-
- // Sort batches of each label.
- if (sorts_specs->SpecsCount > 0)
- {
- _InfoTableSortSpecs = sorts_specs;
- PerfToolInstance = this;
- ImQsort(_InfoTableSort.Data, (size_t)_InfoTableSort.Size, sizeof(_InfoTableSort.Data[0]), CompareWithSortSpecs);
- _InfoTableSortSpecs = NULL;
- PerfToolInstance = NULL;
- }
- }
-
- ImGui::TableHeadersRow();
-
- // ImPlot renders bars from bottom to the top. We want bars to render from top to the bottom, therefore we loop
- // labels and batches in reverse order.
- _TableHoveredTest = -1;
- _TableHoveredBatch = -1;
- const bool scroll_into_view = _PlotHoverTestLabel && ImGui::IsMouseClicked(ImGuiMouseButton_Left);
- const float header_row_height = ImGui::TableGetCellBgRect(ImGui::GetCurrentTable(), 0).GetHeight();
- ImRect scroll_into_view_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
-
- for (int row_index = _InfoTableSort.Size - 1; row_index >= 0; row_index--)
- {
- int batch_index_sorted, entry_index_sorted;
- _UnpackSortedKey(_InfoTableSort[row_index], &batch_index_sorted, &entry_index_sorted);
- ImGuiPerfToolBatch* batch = &_Batches[batch_index_sorted];
- ImGuiPerfToolEntry* entry = &batch->Entries[entry_index_sorted];
- const char* test_name = entry->TestName;
-
- if (!_IsVisibleBuild(entry) || !_IsVisibleTest(entry->TestName) || entry->NumSamples == 0)
- continue;
-
- ImGui::PushID(entry);
- ImGui::TableNextRow();
- if (row_index & 1)
- ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImGui::GetColorU32(ImGuiCol_TableRowBgAlt, 0.5f));
- else
- ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImGui::GetColorU32(ImGuiCol_TableRowBg, 0.5f));
-
- if (_PlotHoverTest == entry_index_sorted)
- {
- // Highlight a row that corresponds to hovered bar, or all rows that correspond to hovered perf test label.
- if (_PlotHoverBatch == batch_index_sorted || _PlotHoverTestLabel)
- ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImColor(style.Colors[ImGuiCol_TextSelectedBg]));
- }
-
- ImGuiPerfToolEntry* baseline_entry = GetEntryByBatchIdx(_BaselineBatchIndex, test_name);
-
- // Date
- if (ImGui::TableNextColumn())
- {
- char date[64];
- FormatDateAndTime(entry->Timestamp, date, IM_ARRAYSIZE(date));
- ImGui::TextUnformatted(date);
- }
-
- // Build info
- if (ImGui::TableNextColumn())
- {
- // ImGuiSelectableFlags_Disabled + changing ImGuiCol_TextDisabled color prevents selectable from overriding table highlight behavior.
- ImGui::PushStyleColor(ImGuiCol_Header, style.Colors[ImGuiCol_Text]);
- ImGui::PushStyleColor(ImGuiCol_HeaderHovered, style.Colors[ImGuiCol_TextSelectedBg]);
- ImGui::PushStyleColor(ImGuiCol_HeaderActive, style.Colors[ImGuiCol_TextSelectedBg]);
- ImGui::Selectable(entry->TestName, false, ImGuiSelectableFlags_SpanAllColumns);
- ImGui::PopStyleColor(3);
- if (ImGui::IsItemHovered())
- {
- _TableHoveredTest = entry_index_sorted;
- _TableHoveredBatch = batch_index_sorted;
- }
-
- if (ImGui::BeginPopupContextItem())
- {
- if (entry == baseline_entry)
- ImGui::BeginDisabled();
- if (ImGui::MenuItem("Set as baseline"))
- _SetBaseline(batch_index_sorted);
- if (entry == baseline_entry)
- ImGui::EndDisabled();
- ImGui::EndPopup();
- }
- }
- if (ImGui::TableNextColumn())
- ImGui::TextUnformatted(entry->GitBranchName);
- if (ImGui::TableNextColumn())
- ImGui::TextUnformatted(entry->Compiler);
- if (ImGui::TableNextColumn())
- ImGui::TextUnformatted(entry->OS);
- if (ImGui::TableNextColumn())
- ImGui::TextUnformatted(entry->Cpu);
- if (ImGui::TableNextColumn())
- ImGui::TextUnformatted(entry->BuildType);
- if (ImGui::TableNextColumn())
- ImGui::Text("x%d", entry->PerfStressAmount);
-
- // Avg ms
- if (ImGui::TableNextColumn())
- ImGui::Text("%.3lf", entry->DtDeltaMs);
-
- // Min ms
- if (ImGui::TableNextColumn())
- ImGui::Text("%.3lf", entry->DtDeltaMsMin);
-
- // Max ms
- if (ImGui::TableNextColumn())
- ImGui::Text("%.3lf", entry->DtDeltaMsMax);
-
- // Num samples
- if (ImGui::TableNextColumn())
- ImGui::Text("%d", entry->NumSamples);
-
- // VS Baseline
- if (ImGui::TableNextColumn())
- {
- float dt_change = (float)entry->VsBaseline;
- if (_DisplayType == ImGuiPerfToolDisplayType_PerBranchColors)
- {
- ImGui::TextUnformatted("--");
- }
- else
- {
- Str30 label;
- dt_change = FormatVsBaseline(entry, baseline_entry, label);
- ImGui::TextUnformatted(label.c_str());
- if (dt_change != entry->VsBaseline)
- {
- entry->VsBaseline = dt_change;
- _InfoTableSortDirty = true; // Force re-sorting.
- }
- }
- }
-
- if (_PlotHoverTest == entry_index_sorted && scroll_into_view)
- {
- ImGuiTable* table = ImGui::GetCurrentTable();
- scroll_into_view_rect.Add(ImGui::TableGetCellBgRect(table, 0));
- }
-
- ImGui::PopID();
- }
-
- if (scroll_into_view)
- {
- scroll_into_view_rect.Min.y -= header_row_height; // FIXME-TABLE: Compensate for frozen header row covering a first content row scrolled into view.
- ImGui::ScrollToRect(ImGui::GetCurrentWindow(), scroll_into_view_rect, ImGuiScrollFlags_NoScrollParent);
- }
-
- ImGui::EndTable();
-}
-
-//-------------------------------------------------------------------------
-// [SECTION] SETTINGS
-//-------------------------------------------------------------------------
-
-static void PerflogSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler* ini_handler)
-{
- ImGuiPerfTool* perftool = (ImGuiPerfTool*)ini_handler->UserData;
- perftool->_Visibility.Clear();
-}
-
-static void* PerflogSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char*)
-{
- return (void*)1;
-}
-
-static void PerflogSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler* ini_handler, void*, const char* line)
-{
- ImGuiPerfTool* perftool = (ImGuiPerfTool*)ini_handler->UserData;
- char buf[128];
- int visible = -1, display_type = -1;
- /**/ if (sscanf(line, "DateFrom=%10s", perftool->_FilterDateFrom)) {}
- else if (sscanf(line, "DateTo=%10s", perftool->_FilterDateTo)) {}
- else if (sscanf(line, "DisplayType=%d", &display_type)) { perftool->_DisplayType = (ImGuiPerfToolDisplayType)display_type; }
- else if (sscanf(line, "BaselineBuildId=%llu", &perftool->_BaselineBuildId)) {}
- else if (sscanf(line, "BaselineTimestamp=%llu", &perftool->_BaselineTimestamp)) {}
- else if (sscanf(line, "TestVisibility=%[^,],%d", buf, &visible) == 2) { perftool->_Visibility.SetBool(ImHashStr(buf), !!visible); }
- else if (sscanf(line, "BuildVisibility=%[^,],%d", buf, &visible) == 2) { perftool->_Visibility.SetBool(ImHashStr(buf), !!visible); }
-}
-
-static void PerflogSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler* ini_handler)
-{
- ImGuiPerfTool* perftool = (ImGuiPerfTool*)ini_handler->UserData;
- perftool->_Batches.clear_destruct();
- perftool->_SetBaseline(-1);
-}
-
-static void PerflogSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler* ini_handler, ImGuiTextBuffer* buf)
-{
- ImGuiPerfTool* perftool = (ImGuiPerfTool*)ini_handler->UserData;
- if (perftool->_Batches.empty())
- return;
- buf->appendf("[%s][Data]\n", ini_handler->TypeName);
- buf->appendf("DateFrom=%s\n", perftool->_FilterDateFrom);
- buf->appendf("DateTo=%s\n", perftool->_FilterDateTo);
- buf->appendf("DisplayType=%d\n", perftool->_DisplayType);
- buf->appendf("BaselineBuildId=%llu\n", perftool->_BaselineBuildId);
- buf->appendf("BaselineTimestamp=%llu\n", perftool->_BaselineTimestamp);
- for (const char* label : perftool->_Labels)
- buf->appendf("TestVisibility=%s,%d\n", label, perftool->_Visibility.GetBool(ImHashStr(label), true));
-
- ImGuiStorage& temp_set = perftool->_TempSet;
- temp_set.Data.clear();
- for (ImGuiPerfToolEntry& entry : perftool->_SrcData)
- {
- const char* properties[] = { entry.GitBranchName, entry.BuildType, entry.Cpu, entry.OS, entry.Compiler };
- for (int i = 0; i < IM_ARRAYSIZE(properties); i++)
- {
- ImGuiID hash = ImHashStr(properties[i]);
- if (!temp_set.GetBool(hash))
- {
- temp_set.SetBool(hash, true);
- buf->appendf("BuildVisibility=%s,%d\n", properties[i], perftool->_Visibility.GetBool(hash, true));
- }
- }
- }
- buf->append("\n");
-}
-
-void ImGuiPerfTool::_AddSettingsHandler()
-{
- ImGuiSettingsHandler ini_handler;
- ini_handler.TypeName = "TestEnginePerfTool";
- ini_handler.TypeHash = ImHashStr("TestEnginePerfTool");
- ini_handler.ClearAllFn = PerflogSettingsHandler_ClearAll;
- ini_handler.ReadOpenFn = PerflogSettingsHandler_ReadOpen;
- ini_handler.ReadLineFn = PerflogSettingsHandler_ReadLine;
- ini_handler.ApplyAllFn = PerflogSettingsHandler_ApplyAll;
- ini_handler.WriteAllFn = PerflogSettingsHandler_WriteAll;
- ini_handler.UserData = this;
- ImGui::AddSettingsHandler(&ini_handler);
-}
-
-void ImGuiPerfTool::_UnpackSortedKey(ImU64 key, int* batch_index, int* entry_index, int* monotonic_index)
-{
- IM_ASSERT(batch_index != NULL);
- IM_ASSERT(entry_index != NULL);
- const int num_visible_labels = _LabelsVisible.Size;
- *batch_index = (int)((key >> 24) / num_visible_labels);
- *entry_index = (int)((key >> 24) % num_visible_labels);
- if (monotonic_index)
- *monotonic_index = (int)(key & 0xFFFFFF);
-}
-
-//-------------------------------------------------------------------------
-// [SECTION] TESTS
-//-------------------------------------------------------------------------
-
-static bool SetPerfToolWindowOpen(ImGuiTestContext* ctx, bool is_open)
-{
- ctx->MenuClick("//Dear ImGui Test Engine/Tools");
- bool was_open = ctx->ItemIsChecked("//##Menu_00/Perf Tool");
- ctx->MenuAction(is_open ? ImGuiTestAction_Check : ImGuiTestAction_Uncheck, "//Dear ImGui Test Engine/Tools/Perf Tool");
- return was_open;
-}
-
-void RegisterTests_TestEnginePerfTool(ImGuiTestEngine* e)
-{
- ImGuiTest* t = NULL;
-
- // ## Flex perf tool code.
- t = IM_REGISTER_TEST(e, "testengine", "testengine_cov_perftool");
- t->GuiFunc = [](ImGuiTestContext* ctx)
- {
- IM_UNUSED(ctx);
- ImGui::Begin("Test Func", NULL, ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
- int loop_count = 1000;
- bool v1 = false, v2 = true;
- for (int n = 0; n < loop_count / 2; n++)
- {
- ImGui::PushID(n);
- ImGui::Checkbox("Hello, world", &v1);
- ImGui::Checkbox("Hello, world", &v2);
- ImGui::PopID();
- }
- ImGui::End();
- };
- t->TestFunc = [](ImGuiTestContext* ctx)
- {
- ImGuiPerfTool* perftool = ImGuiTestEngine_GetPerfTool(ctx->Engine);
- const char* temp_perf_csv = "output/misc_cov_perf_tool.csv";
-
- Str16f min_date_bkp = perftool->_FilterDateFrom;
- Str16f max_date_bkp = perftool->_FilterDateTo;
-
- // Execute few perf tests, serialize them to temporary csv file.
- ctx->PerfIterations = 50; // Make faster
- ctx->PerfCapture("perf", "misc_cov_perf_tool_1", temp_perf_csv);
- ctx->PerfCapture("perf", "misc_cov_perf_tool_2", temp_perf_csv);
-
- // Load perf data from csv file and open perf tool.
- perftool->Clear();
- perftool->LoadCSV(temp_perf_csv);
- bool perf_was_open = SetPerfToolWindowOpen(ctx, true);
- ctx->Yield();
-
- ImGuiWindow* window = ctx->GetWindowByRef("Dear ImGui Perf Tool");
- IM_CHECK(window != NULL);
- ImVec2 pos_bkp = window->Pos;
- ImVec2 size_bkp = window->Size;
- ctx->SetRef(window);
- ctx->WindowMove("", ImVec2(50, 50));
- ctx->WindowResize("", ImVec2(1400, 900));
-#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
- ImGuiWindow* plot_child = ctx->WindowInfo("plot")->Window; // "plot/PerfTool" prior to implot 2023/08/21
- IM_CHECK(plot_child != NULL);
-
- // Move legend to right side.
- ctx->MouseMoveToPos(plot_child->Rect().GetCenter());
- ctx->MouseDoubleClick(ImGuiMouseButton_Left); // Auto-size plots while at it
- ctx->MouseClick(ImGuiMouseButton_Right);
- ctx->MenuClick("//$FOCUSED/Legend/NE");
-
- // Click some stuff for more coverage.
- ctx->MouseMoveToPos(plot_child->Rect().GetCenter());
- ctx->KeyPress(ImGuiMod_Shift);
-#endif
- ctx->ItemClick("##date-from", ImGuiMouseButton_Right);
- ctx->ItemClick(ctx->GetID("//$FOCUSED/Set Min"));
- ctx->ItemClick("##date-to", ImGuiMouseButton_Right);
- ctx->ItemClick(ctx->GetID("//$FOCUSED/Set Max"));
- ctx->ItemClick("###Filter builds");
- ctx->ItemClick("###Filter tests");
- ctx->ItemClick("Combine", 0, ImGuiTestOpFlags_MoveToEdgeL); // Toggle thrice to leave state unchanged
- ctx->ItemClick("Combine", 0, ImGuiTestOpFlags_MoveToEdgeL);
- ctx->ItemClick("Combine", 0, ImGuiTestOpFlags_MoveToEdgeL);
-
- // Restore original state.
- perftool->Clear(); // Clear test data and load original data
- ImFileDelete(temp_perf_csv);
- perftool->LoadCSV();
- ctx->Yield();
-#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
- ctx->MouseMoveToPos(plot_child->Rect().GetCenter());
- ctx->MouseDoubleClick(ImGuiMouseButton_Left); // Fit plot to original data
-#endif
- ImStrncpy(perftool->_FilterDateFrom, min_date_bkp.c_str(), IM_ARRAYSIZE(perftool->_FilterDateFrom));
- ImStrncpy(perftool->_FilterDateTo, max_date_bkp.c_str(), IM_ARRAYSIZE(perftool->_FilterDateTo));
- ImGui::SetWindowPos(window, pos_bkp);
- ImGui::SetWindowSize(window, size_bkp);
- SetPerfToolWindowOpen(ctx, perf_was_open); // Restore window visibility
- };
-
- // ## Capture perf tool graph.
- t = IM_REGISTER_TEST(e, "capture", "capture_perf_report");
- t->TestFunc = [](ImGuiTestContext* ctx)
- {
- ImGuiPerfTool* perftool = ImGuiTestEngine_GetPerfTool(ctx->Engine);
- const char* perf_report_image = NULL;
- if (!ImFileExist(IMGUI_PERFLOG_DEFAULT_FILENAME))
- {
- ctx->LogWarning("Perf tool has no data. Perf report generation was aborted.");
- return;
- }
-
- char min_date_bkp[sizeof(perftool->_FilterDateFrom)], max_date_bkp[sizeof(perftool->_FilterDateTo)];
- ImStrncpy(min_date_bkp, perftool->_FilterDateFrom, IM_ARRAYSIZE(min_date_bkp));
- ImStrncpy(max_date_bkp, perftool->_FilterDateTo, IM_ARRAYSIZE(max_date_bkp));
- bool perf_was_open = SetPerfToolWindowOpen(ctx, true);
- ctx->Yield();
-
- ImGuiWindow* window = ctx->GetWindowByRef("Dear ImGui Perf Tool");
- IM_CHECK_SILENT(window != NULL);
- ImVec2 pos_bkp = window->Pos;
- ImVec2 size_bkp = window->Size;
- ctx->SetRef(window);
- ctx->WindowMove("", ImVec2(50, 50));
- ctx->WindowResize("", ImVec2(1400, 900));
-#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
- ctx->ItemDoubleClick("splitter"); // Hide info table
-
- ImGuiWindow* plot_child = ctx->WindowInfo("plot")->Window; // "plot/PerfTool" prior to implot 2023/08/21
- IM_CHECK(plot_child != NULL);
-
- // Move legend to right side.
- ctx->MouseMoveToPos(plot_child->Rect().GetCenter());
- ctx->MouseDoubleClick(ImGuiMouseButton_Left); // Auto-size plots while at it
- ctx->MouseClick(ImGuiMouseButton_Right);
- ctx->MenuClick("//$FOCUSED/Legend/NE");
-#endif
- // Click some stuff for more coverage.
- ctx->ItemClick("##date-from", ImGuiMouseButton_Right);
- ctx->ItemClick(ctx->GetID("//$FOCUSED/Set Min"));
- ctx->ItemClick("##date-to", ImGuiMouseButton_Right);
- ctx->ItemClick(ctx->GetID("//$FOCUSED/Set Max"));
-#if IMGUI_TEST_ENGINE_ENABLE_IMPLOT
- // Take a screenshot.
- ImGuiCaptureArgs* args = ctx->CaptureArgs;
- args->InCaptureRect = plot_child->Rect();
- ctx->CaptureAddWindow(window->Name);
- ctx->CaptureScreenshot(ImGuiCaptureFlags_HideMouseCursor);
- ctx->ItemDragWithDelta("splitter", ImVec2(0, -180)); // Show info table
- perf_report_image = args->InOutputFile;
-#endif
- ImStrncpy(perftool->_FilterDateFrom, min_date_bkp, IM_ARRAYSIZE(min_date_bkp));
- ImStrncpy(perftool->_FilterDateTo, max_date_bkp, IM_ARRAYSIZE(max_date_bkp));
- ImGui::SetWindowPos(window, pos_bkp);
- ImGui::SetWindowSize(window, size_bkp);
- SetPerfToolWindowOpen(ctx, perf_was_open); // Restore window visibility
-
- const char* perf_report_output = getenv("CAPTURE_PERF_REPORT_OUTPUT");
- if (perf_report_output == NULL)
- perf_report_output = PerfToolReportDefaultOutputPath;
- perftool->SaveHtmlReport(perf_report_output, perf_report_image);
- };
-}
-
-//-------------------------------------------------------------------------