From 77c8748c7387626a57c0950c0d1e5954024e726a Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Tue, 11 Mar 2025 13:03:29 +0100 Subject: [PATCH 1/4] DPL: make input slots display in DebugGUI scrollable --- .../src/FrameworkGUIDataRelayerUsage.cxx | 36 ++++++++++--------- .../src/FrameworkGUIDataRelayerUsage.h | 5 +-- .../src/FrameworkGUIDevicesGraph.cxx | 16 ++++++++- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx index 8e683d46131ea..fab0f0dbc4e36 100644 --- a/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx @@ -17,8 +17,7 @@ #include "Framework/DataProcessingStates.h" #include "InspectorHelpers.h" #include "PaletteHelpers.h" -#include "Framework/Logger.h" -#include +#include "FrameworkGUIDataRelayerUsage.h" #include #include @@ -27,11 +26,11 @@ static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return Im namespace o2::framework::gui { - // This is to display the information in the data relayer struct HeatMapHelper { template - static void draw(const char* name, + static void draw(const char* /*name*/, + int& v, ImVec2 const& sizeHint, std::function const& getNumInputs, std::function const& getNumRecords, @@ -49,14 +48,14 @@ struct HeatMapHelper { constexpr float MAX_BOX_Y_SIZE = 16.f; ImDrawList* drawList = ImGui::GetWindowDrawList(); ImVec2 winPos = ImGui::GetCursorScreenPos() + ImVec2{0, 7}; - auto records = getNumRecords(); - auto boxSizeX = std::min(size.x / records, MAX_BOX_X_SIZE); + size_t recordsWindow = v + WND; + auto boxSizeX = std::min(size.x / WND, MAX_BOX_X_SIZE); auto numInputs = getNumInputs(); ImGui::InvisibleButton("sensible area", ImVec2(size.x, size.y)); if (ImGui::IsItemHovered()) { auto pos = ImGui::GetMousePos() - winPos; - auto slot = std::lround(std::trunc(pos.x / size.x * records)); + auto slot = v + std::lround(std::trunc(pos.x / size.x * WND)); auto row = std::lround(std::trunc(pos.y / size.y * numInputs)); describeCell(row, slot); } @@ -72,18 +71,19 @@ struct HeatMapHelper { float padding = 1; size_t totalRects = 0; - for (size_t ri = 0, re = getNumRecords(); ri < re; ri++) { + for (size_t ri = v; ri < recordsWindow; ri++) { auto record = getRecord(ri); totalRects += getNumItems(record); } drawList->PrimReserve(totalRects * 6, totalRects * 4); - for (size_t ri = 0, re = getNumRecords(); ri < re; ri++) { + for (size_t ri = v; ri < recordsWindow; ri++) { auto record = getRecord(ri); - ImVec2 xOffset{(ri * boxSizeX) + padding, 0}; + ImVec2 xOffset{((ri - v) * boxSizeX) + padding, 0}; ImVec2 xSize{boxSizeX - 2 * padding, 0}; - auto boxSizeY = std::min(size.y / getNumItems(record), MAX_BOX_Y_SIZE); - for (size_t mi = 0, me = getNumItems(record); mi < me; mi++) { + auto me = getNumItems(record); + auto boxSizeY = std::min(size.y / me, MAX_BOX_Y_SIZE); + for (size_t mi = 0; mi < me; mi++) { ImVec2 yOffSet{0, (mi * boxSizeY) + padding}; ImVec2 ySize{0, boxSizeY - 2 * padding}; @@ -98,11 +98,12 @@ struct HeatMapHelper { } }; -void displayDataRelayer(DeviceMetricsInfo const& metrics, - DeviceInfo const& info, +void displayDataRelayer(DeviceMetricsInfo const& /*metrics*/, + DeviceInfo const& /*info*/, DeviceSpec const& spec, DataProcessingStates const& states, - ImVec2 const& size) + ImVec2 const& size, + int& v) { auto getNumInputs = [&states]() -> size_t { auto& inputsView = states.statesViews[(int)ProcessingStateId::DATA_QUERIES]; @@ -146,7 +147,7 @@ void displayDataRelayer(DeviceMetricsInfo const& metrics, } char const* const beginData = strchr(buffer + view.first, ' ') + 1; // Protect against buffer overflows - if (view.size <= beginData - buffer + i - view.first) { + if ((size_t)view.size <= beginData - buffer + i - view.first) { return &error; } return (int8_t const*)beginData + i; }; @@ -184,7 +185,7 @@ void displayDataRelayer(DeviceMetricsInfo const& metrics, if ((end - input) == 0) { continue; } - if (i == row) { + if (i == (size_t)row) { ImGui::Text("%d %.*s (%s)", row, int(end - input), input, InspectorHelpers::getLifeTimeStr(spec.inputs[i].matcher.lifetime).c_str()); break; } @@ -226,6 +227,7 @@ void displayDataRelayer(DeviceMetricsInfo const& metrics, if (getNumRecords()) { HeatMapHelper::draw("DataRelayer", + v, size, getNumInputs, getNumRecords, diff --git a/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.h b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.h index 8c4941474d8a7..8bea06829f0dc 100644 --- a/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.h +++ b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.h @@ -9,6 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "Framework/DeviceSpec.h" class ImVec2; namespace o2::framework @@ -19,9 +20,9 @@ class DataProcessingStates; namespace gui { - +static constexpr int WND = 16; /// View of the DataRelayer metrics for a given DeviceInfo -void displayDataRelayer(DeviceMetricsInfo const& metrics, DeviceInfo const& info, DeviceSpec const& spec, DataProcessingStates const&, ImVec2 const& size); +void displayDataRelayer(DeviceMetricsInfo const& metrics, DeviceInfo const& info, DeviceSpec const& spec, DataProcessingStates const&, ImVec2 const& size, int& v); } // namespace gui } // namespace o2::framework diff --git a/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx index 89126cf303a66..c15fd7c5a6e49 100644 --- a/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx @@ -713,7 +713,21 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, default: break; } - gui::displayDataRelayer(metricsInfos[node->ID], infos[node->ID], specs[node->ID], allStates[node->ID], ImVec2(140., 90.)); + static int v = 0; + auto records = [states = allStates[node->ID]]() -> size_t { + auto& view = states.statesViews[(int)ProcessingStateId::DATA_RELAYER_BASE]; + if (view.size == 0) { + return 0; + } + // The first number is the size of the pipeline + int numRecords = strtoul(states.statesBuffer.data() + view.first, nullptr, 10); + return numRecords; + }(); + if (records > 0) { + ImGui::PushItemWidth(140); + ImGui::SliderInt("##window", &v, 0, records - gui::WND, "start: %d", ImGuiSliderFlags_AlwaysClamp); + } + gui::displayDataRelayer(metricsInfos[node->ID], infos[node->ID], specs[node->ID], allStates[node->ID], ImVec2(140., 90.), v); ImGui::EndGroup(); // Save the size of what we have emitted and whether any of the widgets are being used From 177b89cbece32225f4aa0b67a19a37f50dd1c33d Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Tue, 11 Mar 2025 13:19:33 +0100 Subject: [PATCH 2/4] make start positions per-device --- Framework/Core/include/Framework/DeviceControl.h | 2 ++ Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Framework/Core/include/Framework/DeviceControl.h b/Framework/Core/include/Framework/DeviceControl.h index ce946e8e77fbf..2ec8f1bd19815 100644 --- a/Framework/Core/include/Framework/DeviceControl.h +++ b/Framework/Core/include/Framework/DeviceControl.h @@ -52,6 +52,8 @@ struct DeviceControl { int logStreams = 0; /// An incremental number to identify the device state int requestedState = 0; + /// The first slot in the records buffer to display in GUI + int firstSlot = 0; }; } // namespace o2::framework diff --git a/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx index c15fd7c5a6e49..3ec5a870dabe5 100644 --- a/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx @@ -725,9 +725,9 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, }(); if (records > 0) { ImGui::PushItemWidth(140); - ImGui::SliderInt("##window", &v, 0, records - gui::WND, "start: %d", ImGuiSliderFlags_AlwaysClamp); + ImGui::SliderInt("##window", &controls[node->ID].firstSlot, 0, records - gui::WND, "start: %d", ImGuiSliderFlags_AlwaysClamp); } - gui::displayDataRelayer(metricsInfos[node->ID], infos[node->ID], specs[node->ID], allStates[node->ID], ImVec2(140., 90.), v); + gui::displayDataRelayer(metricsInfos[node->ID], infos[node->ID], specs[node->ID], allStates[node->ID], ImVec2(140., 90.), controls[node->ID].firstSlot); ImGui::EndGroup(); // Save the size of what we have emitted and whether any of the widgets are being used From 8a4e79c235cd4d9a9f01f5cdae3bbf2c30819220 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Wed, 12 Mar 2025 12:17:47 +0100 Subject: [PATCH 3/4] add activity indicator on the slider --- .../Core/include/Framework/DeviceControl.h | 4 +- .../src/FrameworkGUIDataRelayerUsage.cxx | 69 ++++++++++++++++--- .../src/FrameworkGUIDevicesGraph.cxx | 18 +---- 3 files changed, 63 insertions(+), 28 deletions(-) diff --git a/Framework/Core/include/Framework/DeviceControl.h b/Framework/Core/include/Framework/DeviceControl.h index 2ec8f1bd19815..03889c00f6cf9 100644 --- a/Framework/Core/include/Framework/DeviceControl.h +++ b/Framework/Core/include/Framework/DeviceControl.h @@ -52,8 +52,8 @@ struct DeviceControl { int logStreams = 0; /// An incremental number to identify the device state int requestedState = 0; - /// The first slot in the records buffer to display in GUI - int firstSlot = 0; + /// The first window in the records buffer to display in GUI + int firstWnd = 1; }; } // namespace o2::framework diff --git a/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx index fab0f0dbc4e36..f91eabbc06dc5 100644 --- a/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx @@ -41,21 +41,69 @@ struct HeatMapHelper { std::function const& getColor, std::function const& describeCell) { - ImVec2 size = ImVec2(sizeHint.x, std::min(sizeHint.y, 16.f * getNumItems(0) + 2)); + float padding = 1; + // add slider to scroll between the grid display windows + size_t nw = getNumRecords() / WND; + ImGui::PushItemWidth(sizeHint.x); + ImGui::SliderInt("##window", &v, 1, nw, "wnd: %d", ImGuiSliderFlags_AlwaysClamp); + ImVec2 sliderMin = ImGui::GetItemRectMin(); + + constexpr float MAX_BOX_X_SIZE = 16.f; + constexpr float MAX_BOX_Y_SIZE = 32.f; + + ImVec2 size = ImVec2(sizeHint.x, std::min(sizeHint.y, MAX_BOX_Y_SIZE * getNumItems(0) + 2)); ImU32 BORDER_COLOR = ImColor(200, 200, 200, 255); ImU32 BACKGROUND_COLOR = ImColor(20, 20, 20, 255); - constexpr float MAX_BOX_X_SIZE = 16.f; - constexpr float MAX_BOX_Y_SIZE = 16.f; + ImU32 BORDER_COLOR_A = ImColor(200, 200, 200, 0); + ImU32 BACKGROUND_COLOR_A = ImColor(0, 0, 0, 0); + ImDrawList* drawList = ImGui::GetWindowDrawList(); - ImVec2 winPos = ImGui::GetCursorScreenPos() + ImVec2{0, 7}; - size_t recordsWindow = v + WND; + ImVec2 winPos = sliderMin; + + // overlay activity indicator on the slider + auto xsz = size.x / nw; + drawList->AddRectFilled( + ImVec2{0., 0.} + winPos, + ImVec2{size.x, size.y} + winPos, + BACKGROUND_COLOR_A); + drawList->AddRect( + ImVec2{0. - 1, -1} + winPos, + ImVec2{size.x + 1, size.y - 1} + winPos, + BORDER_COLOR_A); + + const static auto colorA = ImColor(ImVec4{0.945, 0.096, 0.278, 0.5}); + const static auto colorE = ImColor(ImVec4{0, 0, 0, 0}); + + drawList->PrimReserve(nw * 6, nw * 4); + for (size_t iw = 0; iw < nw; ++iw) { + ImVec2 xOffset{iw * xsz + 2 * padding, 0}; + ImVec2 xSize{xsz - 2 * padding, 0}; + ImVec2 yOffset{0, 2 * padding}; + ImVec2 ySize{0, 16 - 4 * padding}; + bool active = 0; + for (size_t ir = iw; ir < ((iw + WND > getNumRecords()) ? getNumRecords() : iw + WND); ++ir) { + for (size_t i = 0; i < getNumItems(ir); ++i) { + active = getValue(*getItem(ir, i)) > 0; + if (active) { + break; + } + } + } + drawList->PrimRect( + xOffset + yOffset + winPos, + xOffset + xSize + yOffset + ySize + winPos, + active ? colorA : colorE); + } + + // display the grid + size_t recordsWindow = v * WND; auto boxSizeX = std::min(size.x / WND, MAX_BOX_X_SIZE); auto numInputs = getNumInputs(); - + winPos = ImGui::GetCursorScreenPos() + ImVec2{0, 7}; ImGui::InvisibleButton("sensible area", ImVec2(size.x, size.y)); if (ImGui::IsItemHovered()) { auto pos = ImGui::GetMousePos() - winPos; - auto slot = v + std::lround(std::trunc(pos.x / size.x * WND)); + auto slot = (v - 1) * WND + std::lround(std::trunc(pos.x / size.x * WND)); auto row = std::lround(std::trunc(pos.y / size.y * numInputs)); describeCell(row, slot); } @@ -68,18 +116,17 @@ struct HeatMapHelper { ImVec2(0. - 1, -1) + winPos, ImVec2{size.x + 1, size.y - 1} + winPos, BORDER_COLOR); - float padding = 1; size_t totalRects = 0; - for (size_t ri = v; ri < recordsWindow; ri++) { + for (size_t ri = (v - 1) * WND; ri < recordsWindow; ri++) { auto record = getRecord(ri); totalRects += getNumItems(record); } drawList->PrimReserve(totalRects * 6, totalRects * 4); - for (size_t ri = v; ri < recordsWindow; ri++) { + for (size_t ri = (v - 1) * WND; ri < recordsWindow; ri++) { auto record = getRecord(ri); - ImVec2 xOffset{((ri - v) * boxSizeX) + padding, 0}; + ImVec2 xOffset{((ri - (v - 1) * WND) * boxSizeX) + padding, 0}; ImVec2 xSize{boxSizeX - 2 * padding, 0}; auto me = getNumItems(record); auto boxSizeY = std::min(size.y / me, MAX_BOX_Y_SIZE); diff --git a/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx index 3ec5a870dabe5..99c52947b2e8c 100644 --- a/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx @@ -692,6 +692,7 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, bool old_any_active = ImGui::IsAnyItemActive(); ImGui::SetCursorScreenPos(node_rect_min + NODE_WINDOW_PADDING); ImGui::BeginGroup(); // Lock horizontal position + ImGui::PushItemWidth(200.); ImGui::TextUnformatted(node->Name); switch (info.maxLogLevel) { case LogLevel::Critical: @@ -713,21 +714,8 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, default: break; } - static int v = 0; - auto records = [states = allStates[node->ID]]() -> size_t { - auto& view = states.statesViews[(int)ProcessingStateId::DATA_RELAYER_BASE]; - if (view.size == 0) { - return 0; - } - // The first number is the size of the pipeline - int numRecords = strtoul(states.statesBuffer.data() + view.first, nullptr, 10); - return numRecords; - }(); - if (records > 0) { - ImGui::PushItemWidth(140); - ImGui::SliderInt("##window", &controls[node->ID].firstSlot, 0, records - gui::WND, "start: %d", ImGuiSliderFlags_AlwaysClamp); - } - gui::displayDataRelayer(metricsInfos[node->ID], infos[node->ID], specs[node->ID], allStates[node->ID], ImVec2(140., 90.), controls[node->ID].firstSlot); + + gui::displayDataRelayer(metricsInfos[node->ID], infos[node->ID], specs[node->ID], allStates[node->ID], ImVec2(160., 90.), controls[node->ID].firstWnd); ImGui::EndGroup(); // Save the size of what we have emitted and whether any of the widgets are being used From f090122d33cc2b4aa834d0d981a849490159aa93 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Wed, 12 Mar 2025 12:21:44 +0100 Subject: [PATCH 4/4] adjust sizes --- Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx | 2 +- Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx index f91eabbc06dc5..c39e268fa90a7 100644 --- a/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx @@ -49,7 +49,7 @@ struct HeatMapHelper { ImVec2 sliderMin = ImGui::GetItemRectMin(); constexpr float MAX_BOX_X_SIZE = 16.f; - constexpr float MAX_BOX_Y_SIZE = 32.f; + constexpr float MAX_BOX_Y_SIZE = 16.f; ImVec2 size = ImVec2(sizeHint.x, std::min(sizeHint.y, MAX_BOX_Y_SIZE * getNumItems(0) + 2)); ImU32 BORDER_COLOR = ImColor(200, 200, 200, 255); diff --git a/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx index 99c52947b2e8c..1c4ddd7e6aabf 100644 --- a/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx @@ -692,7 +692,6 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, bool old_any_active = ImGui::IsAnyItemActive(); ImGui::SetCursorScreenPos(node_rect_min + NODE_WINDOW_PADDING); ImGui::BeginGroup(); // Lock horizontal position - ImGui::PushItemWidth(200.); ImGui::TextUnformatted(node->Name); switch (info.maxLogLevel) { case LogLevel::Critical: @@ -715,7 +714,7 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, break; } - gui::displayDataRelayer(metricsInfos[node->ID], infos[node->ID], specs[node->ID], allStates[node->ID], ImVec2(160., 90.), controls[node->ID].firstWnd); + gui::displayDataRelayer(metricsInfos[node->ID], infos[node->ID], specs[node->ID], allStates[node->ID], ImVec2(200., 160.), controls[node->ID].firstWnd); ImGui::EndGroup(); // Save the size of what we have emitted and whether any of the widgets are being used