From 93d5ea4291efe1de0c707793101077ba1ce77326 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Wed, 12 Jun 2024 18:36:55 +0300 Subject: [PATCH] ImGui::PlotTwoLines() extension, PerformanceInfo improvements --- .../UI/ImGuiExtensions/ImGuiPlotTwoLines.cpp | 142 ++++++++++++++++++ .../UI/ImGuiExtensions/ImGuiPlotTwoLines.hpp | 17 +++ openVulkanoCpp/Scene/UI/PerformanceInfo.cpp | 57 ++++--- openVulkanoCpp/Scene/UI/PerformanceInfo.hpp | 12 +- 4 files changed, 200 insertions(+), 28 deletions(-) create mode 100644 openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiPlotTwoLines.cpp create mode 100644 openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiPlotTwoLines.hpp diff --git a/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiPlotTwoLines.cpp b/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiPlotTwoLines.cpp new file mode 100644 index 0000000..bec93d1 --- /dev/null +++ b/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiPlotTwoLines.cpp @@ -0,0 +1,142 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "ImGuiPlotTwoLines.hpp" + +#include +#include + +namespace ImGui +{ + void PlotTwoLines(const char* label, + float (*getter1)(void* data, int idx), void* data1, const char *tooltipPrefix1, + float (*getter2)(void* data, int idx), void* data2, const char *tooltipPrefix2, + int values_count, float scale_min, float scale_max, ImVec2 graph_size) + { + ImGuiContext& g = *ImGui::GetCurrentContext(); + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return; + + const ImGuiStyle& style = g.Style; + + ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); + if (graph_size.x == 0.0f) + graph_size.x = ImGui::CalcItemWidth(); + if (graph_size.y == 0.0f) + graph_size.y = label_size.y + (style.FramePadding.y * 2); + + ImRect frame_bb(window->DC.CursorPos, ImVec2(window->DC.CursorPos.x + graph_size.x, window->DC.CursorPos.y + graph_size.y)); + ImRect inner_bb(ImVec2(frame_bb.Min.x + style.FramePadding.x, frame_bb.Min.y + style.FramePadding.y), ImVec2(frame_bb.Max.x - style.FramePadding.x, frame_bb.Max.y - style.FramePadding.y)); + ImRect total_bb(frame_bb.Min, ImVec2(frame_bb.Max.x + (label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f), frame_bb.Max.y)); + + ImGui::ItemSize(total_bb, style.FramePadding.y); + if (!ImGui::ItemAdd(total_bb, 0)) + return; + + // Determine scale from inputs if not specified + if (scale_min == FLT_MAX || scale_max == FLT_MAX) + { + float v1_min = FLT_MAX, v1_max = -FLT_MAX; + for (int i = 0; i < values_count; i++) + { + float v = getter1(data1, i); + if (v != v) // Ignore NaN values + continue; + v1_min = ImMin(v1_min, v); + v1_max = ImMax(v1_max, v); + } + float v2_min = FLT_MAX, v2_max = -FLT_MAX; + for (int i = 0; i < values_count; i++) + { + float v = getter2(data2, i); + if (v != v) // Ignore NaN values + continue; + v2_min = ImMin(v2_min, v); + v2_max = ImMax(v2_max, v); + } + if (scale_min == FLT_MAX) scale_min = ImMin(v1_min, v2_min); + if (scale_max == FLT_MAX) scale_max = ImMax(v1_max, v2_max); + } + + ImGui::RenderFrame(frame_bb.Min, frame_bb.Max, ImGui::GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + int res_w = ImMin((int)graph_size.x, values_count); // Number of samples + + if (res_w <= 0) + return; + + // Tooltip on hover + int v_hovered = -1; + if (ImGui::IsItemHovered()) + { + const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); + const int v_idx = (int)(t * values_count); + IM_ASSERT(v_idx >= 0 && v_idx < values_count); + + const float v0 = getter1(data1, v_idx); + const float v1 = getter2(data2, v_idx); + ImGui::SetTooltip("%d: (%s: %8.4g, %s: %8.4g)", v_idx, tooltipPrefix1, v0, tooltipPrefix2, v1); + v_hovered = v_idx; + } + + const float t_step = 1.0f / res_w; + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->PushClipRect(inner_bb.Min, inner_bb.Max, true); + + ImColor col1 = ImColor(255, 100, 100); + ImColor col2 = ImColor(100, 255, 100); + + // Plot first line + for (int n = 0; n < res_w; n++) + { + const float t0 = t_step * (n + 0); + const float t1 = t_step * (n + 1); + const int v_idx0 = (int)(t0 * values_count); + int v_idx1 = (int)(t1 * values_count); + if(v_idx1 >= values_count) + v_idx1 = values_count - 1; + + IM_ASSERT(v_idx0 >= 0 && v_idx0 < values_count); + IM_ASSERT(v_idx1 >= 0 && v_idx1 < values_count); + + const float v0 = getter1(data1, v_idx0); + const float v1 = getter1(data1, v_idx1); + const float x0 = ImLerp(inner_bb.Min.x, inner_bb.Max.x, t0); + const float x1 = ImLerp(inner_bb.Min.x, inner_bb.Max.x, t1); + const float y0 = ImLerp(inner_bb.Max.y, inner_bb.Min.y, (v0 - scale_min) / (scale_max - scale_min)); + const float y1 = ImLerp(inner_bb.Max.y, inner_bb.Min.y, (v1 - scale_min) / (scale_max - scale_min)); + draw_list->AddLine(ImVec2(x0, y0), ImVec2(x1, y1), col1); + } + + // Plot second line + for (int n = 0; n < res_w; n++) + { + const float t0 = t_step * (n + 0); + const float t1 = t_step * (n + 1); + const int v_idx0 = (int)(t0 * values_count); + int v_idx1 = (int)(t1 * values_count); + if(v_idx1 >= values_count) + v_idx1 = values_count - 1; + + IM_ASSERT(v_idx0 >= 0 && v_idx0 < values_count); + IM_ASSERT(v_idx1 >= 0 && v_idx1 < values_count); + + const float v0 = getter2(data2, v_idx0); + const float v1 = getter2(data2, v_idx1); + const float x0 = ImLerp(inner_bb.Min.x, inner_bb.Max.x, t0); + const float x1 = ImLerp(inner_bb.Min.x, inner_bb.Max.x, t1); + const float y0 = ImLerp(inner_bb.Max.y, inner_bb.Min.y, (v0 - scale_min) / (scale_max - scale_min)); + const float y1 = ImLerp(inner_bb.Max.y, inner_bb.Min.y, (v1 - scale_min) / (scale_max - scale_min)); + draw_list->AddLine(ImVec2(x0, y0), ImVec2(x1, y1), col2); + } + + draw_list->PopClipRect(); + + if (label_size.x > 0.0f) + ImGui::RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiPlotTwoLines.hpp b/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiPlotTwoLines.hpp new file mode 100644 index 0000000..a4bd497 --- /dev/null +++ b/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiPlotTwoLines.hpp @@ -0,0 +1,17 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include + +namespace ImGui +{ + void PlotTwoLines(const char* label, + float (*getter1)(void* data, int idx), void* data1, const char *tooltipPrefix1, + float (*getter2)(void* data, int idx), void* data2, const char *tooltipPrefix2, + int values_count, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0)); +} \ No newline at end of file diff --git a/openVulkanoCpp/Scene/UI/PerformanceInfo.cpp b/openVulkanoCpp/Scene/UI/PerformanceInfo.cpp index 37d8684..9a28ddf 100644 --- a/openVulkanoCpp/Scene/UI/PerformanceInfo.cpp +++ b/openVulkanoCpp/Scene/UI/PerformanceInfo.cpp @@ -10,17 +10,20 @@ #include "Host/SystemInfo.hpp" #include "Math/ByteSize.hpp" #include "ImGuiExtensions/ImGuiTextFmt.hpp" +#include "ImGuiExtensions/ImGuiPlotTwoLines.hpp" -#include +#include namespace OpenVulkano::Scene::UI { namespace { - float FloatValuesGetter(void *data, int index) + float FrameCollectionValuesGetter(void *data, int index) { - auto queue = reinterpret_cast *>(data); - return (*queue)[index]; + auto frameCollection = reinterpret_cast(data); + float value = frameCollection->m_frameTimes[index]; + float result = value / frameCollection->m_maxValue * 100; + return result; } float RamValuesGetter(void *data, int index) @@ -31,16 +34,25 @@ namespace OpenVulkano::Scene::UI float percentage = used / max * 100; return percentage; } + } void PerformanceInfo::UpdateQueues() { - m_frameTimes.push_back(static_cast(CURRENT_FRAME.frameTime)); - while(m_frameTimes.size() > m_frameCount) - m_frameTimes.pop_front(); + m_frameCollection.m_frameTimes.push_back(static_cast(CURRENT_FRAME.frameTime)); + while(m_frameCollection.m_frameTimes.size() > m_windowSize) + m_frameCollection.m_frameTimes.pop_front(); + + float maxFrameTime = FLT_MIN; + for(auto value : m_frameCollection.m_frameTimes) + { + if(value > maxFrameTime) + maxFrameTime = value; + } + m_frameCollection.m_maxValue = maxFrameTime; m_ramUsed.push_back(static_cast(SystemInfo::GetAppRamUsed())); - while(m_ramUsed.size() > m_ramUsageCount) + while(m_ramUsed.size() > m_windowSize) m_ramUsed.pop_front(); } @@ -53,24 +65,21 @@ namespace OpenVulkano::Scene::UI void PerformanceInfo::Draw() { UpdateQueues(); + assert(m_frameCollection.m_frameTimes.size() == m_ramUsed.size()); + + ImGui::Text("Last Frame Time(wrt max): %f s", m_frameCollection.m_frameTimes.back()); + ImGui::Text("Last FPS: %f", 1 / m_frameCollection.m_frameTimes.back()); + ImGui::SliderInt("Window Size", &m_windowSize, 10, 1000); + ImGui::TextFmt("RAM: {} / {}, Free: {}", ByteSize(m_ramUsed.back()), ByteSize(SystemInfo::GetAppRamMax()), ByteSize(SystemInfo::GetAppRamAvailable())); ImVec2 availableSize = ImGui::GetContentRegionAvail(); - availableSize.y *= 1/3.; - - ImGui::Text("Last Frame Time: %f s", m_frameTimes.back()); - ImGui::Text("Last FPS: %f", 1 / m_frameTimes.back()); - ImGui::SliderInt("Window Size", &m_frameCount, 10, 1000); - ImGui::PlotLines("", &FloatValuesGetter, &m_frameTimes, (int)m_frameTimes.size(), 0, nullptr, FLT_MAX, FLT_MAX, availableSize); - - if(ImGui::CollapsingHeader("RAM Usage")) - { - ImGui::TextFmt("RAM: {} / {}, Free: {}", ByteSize(m_ramUsed.back()), ByteSize(SystemInfo::GetAppRamMax()), ByteSize(SystemInfo::GetAppRamAvailable())); - - ImGui::SliderInt("Window Size ", &m_ramUsageCount, 10, 1000); - float scaleMin = 0.01; - float scaleMax = 100; - ImGui::PlotLines("", &RamValuesGetter, &m_ramUsed, (int)m_ramUsed.size(), 0, nullptr, scaleMin, scaleMax, availableSize); - } + float scaleMin = 0.01; + float scaleMax = 100; + ImGui::PlotTwoLines("", + &FrameCollectionValuesGetter, &m_frameCollection, "Frame Time", + &RamValuesGetter, &m_ramUsed, "RAM", + (int)m_ramUsed.size(), + scaleMin, scaleMax, availableSize); } void PerformanceInfo::EndDraw() diff --git a/openVulkanoCpp/Scene/UI/PerformanceInfo.hpp b/openVulkanoCpp/Scene/UI/PerformanceInfo.hpp index 3336a74..53afdd7 100644 --- a/openVulkanoCpp/Scene/UI/PerformanceInfo.hpp +++ b/openVulkanoCpp/Scene/UI/PerformanceInfo.hpp @@ -12,13 +12,17 @@ namespace OpenVulkano::Scene::UI { + struct FrameTimeCollection + { + std::deque m_frameTimes; + float m_maxValue; + }; + class PerformanceInfo : public UiElement { - int m_frameCount = 200; - std::deque m_frameTimes; - - int m_ramUsageCount = 200; + int m_windowSize = 200; std::deque m_ramUsed; + FrameTimeCollection m_frameCollection; void UpdateQueues();