diff --git a/examples/ExampleApps/CubesExampleApp.cpp b/examples/ExampleApps/CubesExampleApp.cpp index b167569..0952192 100644 --- a/examples/ExampleApps/CubesExampleApp.cpp +++ b/examples/ExampleApps/CubesExampleApp.cpp @@ -12,6 +12,7 @@ #include "Scene/Material.hpp" #include "Scene/Vertex.hpp" #include "Scene/SimpleDrawable.hpp" +#include "Scene/UI/PerformanceInfo.hpp" #include "Input/InputManager.hpp" #include "Host/GraphicsAppManager.hpp" #include "Math/Math.hpp" @@ -42,6 +43,9 @@ namespace OpenVulkano std::vector nodesPool; Vector3f_SIMD position = {0, 0, -10}; + OpenVulkano::Scene::UI::SimpleUi m_ui; + std::shared_ptr m_perfInfo; + public: void Init() override { @@ -78,6 +82,10 @@ namespace OpenVulkano camController.Init(&cam); camController.SetDefaultKeybindings(); + + std::shared_ptr m_perfInfo = std::make_shared(); + m_ui.AddElement(m_perfInfo); + GetGraphicsAppManager()->GetRenderer()->SetActiveUi(&m_ui); } void Tick() override diff --git a/examples/ExampleApps/MovingCubeApp.cpp b/examples/ExampleApps/MovingCubeApp.cpp index fe97ee3..24874fd 100644 --- a/examples/ExampleApps/MovingCubeApp.cpp +++ b/examples/ExampleApps/MovingCubeApp.cpp @@ -16,6 +16,7 @@ #include "Scene/Camera.hpp" #include "Scene/SimpleAnimationController.hpp" #include "Scene/SequenceAnimationController.hpp" +#include "Scene/UI/PerformanceInfo.hpp" #include "Input/InputManager.hpp" #include "Host/GraphicsAppManager.hpp" #include "Base/EngineConfiguration.hpp" @@ -44,6 +45,9 @@ namespace OpenVulkano Scene::SimpleAnimationController m_simpleAnimationController; Scene::SequenceAnimationController m_sequenceAnimationController; + Scene::UI::SimpleUi m_ui; + std::shared_ptr m_perfInfo; + SceneElement m_whiteBox; SceneElement m_redBox; @@ -161,6 +165,10 @@ namespace OpenVulkano CreatePyramid(&m_pyramid, Math::Vector4f(0.9, 0.9, 0.6, 1.0)); m_pyramid.m_node.worldMat = Math::Utils::translate(Math::Vector3f(13, 0, 0)); + + std::shared_ptr m_perfInfo = std::make_shared(); + m_ui.AddElement(m_perfInfo); + GetGraphicsAppManager()->GetRenderer()->SetActiveUi(&m_ui); } void OnSimpleAnimationCompleted(Scene::SimpleAnimationController *anim) diff --git a/openVulkanoCpp/Host/Windows/SystemInfo.cpp b/openVulkanoCpp/Host/Windows/SystemInfo.cpp index 49a5c70..6457544 100644 --- a/openVulkanoCpp/Host/Windows/SystemInfo.cpp +++ b/openVulkanoCpp/Host/Windows/SystemInfo.cpp @@ -48,13 +48,25 @@ namespace OpenVulkano { switch(type) { - case APP_MEM_TYPE::VM_MAX: return counters.PeakWorkingSetSize; + case APP_MEM_TYPE::VM_MAX: return counters.PeakPagefileUsage; case APP_MEM_TYPE::USED: return counters.PrivateUsage; } } Logger::PERF->error("Failed to get process memory info"); return 0; } + + size_t GetCommitLimit() + { + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + if(GlobalMemoryStatusEx(&status)) + { + return status.ullTotalPageFile; + } + Logger::PERF->error("Failed to get system commit limit"); + return 0; + } } size_t SystemInfo::GetSystemRam() @@ -69,12 +81,12 @@ namespace OpenVulkano size_t SystemInfo::GetAppRamMax() { - return std::min(GetSystemRam(), GetAppVirtualMemoryMax()); + return std::min(GetSystemRam(), GetCommitLimit()); } size_t SystemInfo::GetAppRamAvailable() { - return std::min(GetSystemRamAvailable(), GetAppVirtualMemoryMax() - GetAppRamUsed()); + return std::min(GetSystemRamAvailable(), GetCommitLimit() - GetAppRamUsed()); } size_t SystemInfo::GetAppRamUsed() diff --git a/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiPlotTwoLines.cpp b/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiPlotTwoLines.cpp new file mode 100644 index 0000000..5052545 --- /dev/null +++ b/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiPlotTwoLines.cpp @@ -0,0 +1,114 @@ +/* + * 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 +{ + namespace + { + void RenderLine(ImDrawList* drawList, float (*getter)(void* data, int idx), void* data, int valuesCount, float scaleMin, float scaleMax, ImRect innerBb, ImColor color, int resW) + { + const float tStep = 1.0f / (float)resW; + const float invScale = (scaleMin == scaleMax) ? 0.0f : (1.0f / (scaleMax - scaleMin)); + + float v0 = getter(data, 0); + float t0 = 0.0f; + ImVec2 tp0 = ImVec2(t0, 1.0f - ImSaturate((v0 - scaleMin) * invScale)); + + for (int n = 0; n < resW; n++) + { + const float t1 = t0 + tStep; + const int v1Idx = (int)(t0 * (valuesCount - 1) + 0.5f); + IM_ASSERT(v1Idx >= 0 && v1Idx < valuesCount); + const float v1 = getter(data, ImMin(v1Idx + 1, valuesCount - 1)); + const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scaleMin) * invScale) ); + + ImVec2 pos0 = ImLerp(innerBb.Min, innerBb.Max, tp0); + ImVec2 pos1 = ImLerp(innerBb.Min, innerBb.Max, tp1); + + drawList->AddLine(pos0, pos1, color); + + t0 = t1; + tp0 = tp1; + } + } + } + + void PlotTwoLines(const char* label, + float (*getter1)(void* data, int idx), void* data1, const char *tooltip1, + float (*getter2)(void* data, int idx), void* data2, const char *tooltip2, + int valuesCount, float scaleMin, float scaleMax, ImVec2 graphSize, ImColor color1, ImColor color2) + { + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + const ImGuiStyle& style = ImGui::GetCurrentContext()->Style; + const ImGuiID id = window->GetID(label); + + const ImVec2 labelSize = CalcTextSize(label, NULL, true); + const ImVec2 frameSize = CalcItemSize(graphSize, CalcItemWidth(), labelSize.y + style.FramePadding.y * 2.0f); + + const ImRect frameBb(window->DC.CursorPos, ImVec2(window->DC.CursorPos.x + frameSize.x, window->DC.CursorPos.y + frameSize.y)); + const ImRect innerBb(ImVec2(frameBb.Min.x + style.FramePadding.x, frameBb.Min.y + style.FramePadding.y), ImVec2(frameBb.Max.x - style.FramePadding.x, frameBb.Max.y - style.FramePadding.y)); + const ImRect totalBb(frameBb.Min, ImVec2(frameBb.Max.x + (labelSize.x > 0.0f ? (style.ItemInnerSpacing.x + labelSize.x) : 0.0f), frameBb.Max.y)); + + ItemSize(totalBb, style.FramePadding.y); + if (!ItemAdd(totalBb, 0, &frameBb)) + return; + const bool hovered = ItemHoverable(frameBb, id, ImGui::GetCurrentContext()->LastItemData.InFlags); + + // Determine scale from values if not specified + if(scaleMin == FLT_MAX || scaleMax == FLT_MAX) + { + float vMin = FLT_MAX; + float vMax = -FLT_MAX; + for(int i = 0; i < valuesCount; i++) + { + const float v1 = getter1(data1, i); + const float v2 = getter2(data2, i); + if(v1 != v1 || v2 != v2) // Ignore NaN values + continue; + vMin = ImMin(ImMin(vMin, v1), v2); + vMax = ImMax(ImMax(vMax, v1), v2); + } + if(scaleMin == FLT_MAX) + scaleMin = vMin; + if(scaleMax == FLT_MAX) + scaleMax = vMax; + } + + RenderFrame(frameBb.Min, frameBb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + + if(valuesCount >= 2) + { + int resW = ImMin((int)frameSize.x, valuesCount) - 1; + int itemCount = valuesCount - 1; + + // Tooltip on hover + if(hovered && innerBb.Contains(ImGui::GetCurrentContext()->IO.MousePos)) + { + const float t = ImClamp((ImGui::GetCurrentContext()->IO.MousePos.x - innerBb.Min.x) / (innerBb.Max.x - innerBb.Min.x), 0.0f, 0.9999f); + const int vIdx = (int)(t * itemCount); + IM_ASSERT(vIdx >= 0 && vIdx < valuesCount); + + const float v0 = getter1(data1, vIdx % valuesCount); + const float v1 = getter2(data2, (vIdx + 1) % valuesCount); + SetTooltip("%s: %8.4g\n%s: %8.4g", tooltip1, v0, tooltip2, v1); + } + + RenderLine(window->DrawList, getter1, data1, valuesCount, scaleMin, scaleMax, innerBb, color1, resW); + RenderLine(window->DrawList, getter2, data2, valuesCount, scaleMin, scaleMax, innerBb, color2, resW); + } + + if(labelSize.x > 0.0f) + RenderText(ImVec2(frameBb.Max.x + style.ItemInnerSpacing.x, innerBb.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..1ad54fe --- /dev/null +++ b/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiPlotTwoLines.hpp @@ -0,0 +1,18 @@ +/* + * 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), ImColor color1 = ImColor(255, 100, 100), ImColor color2 = ImColor(100, 255, 100)); +} \ No newline at end of file diff --git a/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiTextFmt.hpp b/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiTextFmt.hpp index a209ae8..b628ef4 100644 --- a/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiTextFmt.hpp +++ b/openVulkanoCpp/Scene/UI/ImGuiExtensions/ImGuiTextFmt.hpp @@ -22,6 +22,6 @@ namespace ImGui template IMGUI_API void TextFmt(T&& fmt, const Args &... args) { std::string str = fmt::format(std::forward(fmt), args...); - ImGui::TextUnformatted(&*str.begin(), &*str.end()); + ImGui::TextUnformatted(str.data(), str.data() + str.size()); } } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/UI/PerformanceInfo.cpp b/openVulkanoCpp/Scene/UI/PerformanceInfo.cpp new file mode 100644 index 0000000..10e1399 --- /dev/null +++ b/openVulkanoCpp/Scene/UI/PerformanceInfo.cpp @@ -0,0 +1,92 @@ +/* + * 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 "PerformanceInfo.hpp" +#include "Base/FrameMetadata.hpp" +#include "Base/Utils.hpp" +#include "Host/SystemInfo.hpp" +#include "Math/ByteSize.hpp" +#include "ImGuiExtensions/ImGuiTextFmt.hpp" +#include "ImGuiExtensions/ImGuiPlotTwoLines.hpp" + +#include + +namespace OpenVulkano::Scene::UI +{ + namespace + { + float FloatCollectionValuesGetter(void *data, int index) + { + auto collection = static_cast(data); + float value = collection->m_values[index]; + float result = value / collection->m_maxValue * 100; + return result; + } + } + + void PerformanceInfo::UpdateQueues() + { + float newFrameTime = static_cast(CURRENT_FRAME.frameTime); + if(newFrameTime > m_frames.m_maxValue) + m_frames.m_maxValue = newFrameTime; + + m_frames.m_values.push_back(newFrameTime); + + while(m_frames.m_values.size() > m_windowSize) + { + float poppedFrameTime = m_frames.m_values.front(); + m_frames.m_values.pop_front(); + if(m_frames.m_maxValue == poppedFrameTime) + { + m_frames.m_maxValue = FLT_MIN; + for(auto value : m_frames.m_values) + { + if(value > m_frames.m_maxValue) + m_frames.m_maxValue = value; + } + } + } + + m_ramUsed.m_values.push_back(static_cast(SystemInfo::GetAppRamUsed())); + while(m_ramUsed.m_values.size() > m_windowSize) + m_ramUsed.m_values.pop_front(); + + m_ramUsed.m_maxValue = SystemInfo::GetAppRamMax(); + + assert(m_ramUsed.m_values.size() <= m_windowSize); + } + + void PerformanceInfo::BeginDraw() + { + bool alwaysShow = true; + ImGui::Begin("Performance Info", &alwaysShow); + } + + void PerformanceInfo::Draw() + { + UpdateQueues(); + assert(m_frames.m_values.size() == m_ramUsed.m_values.size()); + + ImGui::Text("Last Frame Time: %f s, max: %f s", m_frames.m_values.back(), m_frames.m_maxValue); + ImGui::Text("Last FPS: %f", 1 / m_frames.m_values.back()); + ImGui::TextFmt("RAM: {} / {}, Free: {}", ByteSize(m_ramUsed.m_values.back()), ByteSize(SystemInfo::GetAppRamMax()), ByteSize(SystemInfo::GetAppRamAvailable())); + ImGui::SliderInt("Window Size", &m_windowSize, 10, 1000); + + ImVec2 availableSize = ImGui::GetContentRegionAvail(); + float scaleMin = 0.01; + float scaleMax = 100; + ImGui::PlotTwoLines("", + &FloatCollectionValuesGetter, &m_frames, "Frame Time(wrt max)", + &FloatCollectionValuesGetter, &m_ramUsed, "RAM", + (int)m_ramUsed.m_values.size(), + scaleMin, scaleMax, availableSize); + } + + void PerformanceInfo::EndDraw() + { + ImGui::End(); + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Scene/UI/PerformanceInfo.hpp b/openVulkanoCpp/Scene/UI/PerformanceInfo.hpp new file mode 100644 index 0000000..ed3342f --- /dev/null +++ b/openVulkanoCpp/Scene/UI/PerformanceInfo.hpp @@ -0,0 +1,34 @@ +/* + * 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 "UI.hpp" + +#include + +namespace OpenVulkano::Scene::UI +{ + struct FloatCollection + { + std::deque m_values; + float m_maxValue; + }; + + class PerformanceInfo : public UiElement + { + int m_windowSize = 200; + FloatCollection m_ramUsed; + FloatCollection m_frames; + + void UpdateQueues(); + + protected: + void BeginDraw() override; + void Draw() override; + void EndDraw() override; + }; +} diff --git a/openVulkanoCpp/Scene/UI/UI.hpp b/openVulkanoCpp/Scene/UI/UI.hpp index 09e8560..b09d64f 100644 --- a/openVulkanoCpp/Scene/UI/UI.hpp +++ b/openVulkanoCpp/Scene/UI/UI.hpp @@ -50,4 +50,15 @@ namespace OpenVulkano::Scene::UI void Draw() override {} }; + + class SimpleUi : public Ui + { + public: + void Init() override {} + + void AddElement(const std::shared_ptr& element) + { + children.push_back(element); + } + }; }