From de3e842425b23e7166a62dc59f59a5b1a36861bd Mon Sep 17 00:00:00 2001 From: JackCarterSmith Date: Mon, 16 Sep 2024 23:14:06 +0200 Subject: [PATCH] Performance monitoring --- Engine/Graphics/DebugUI.cpp | 23 ++++++++++- Engine/Graphics/DebugUI.hpp | 14 ++++++- Engine/Utils/Perfs.cpp | 81 +++++++++++++++++++++++++++++++++++++ Engine/Utils/Perfs.hpp | 6 +++ Engine/Utils/Timers.hpp | 5 ++- Game.cpp | 62 +++++++++++++++++++++++----- Game.hpp | 9 ++--- ProtoTank.cpp | 13 ++---- srcs.list | 2 + 9 files changed, 185 insertions(+), 30 deletions(-) create mode 100644 Engine/Utils/Perfs.cpp create mode 100644 Engine/Utils/Perfs.hpp diff --git a/Engine/Graphics/DebugUI.cpp b/Engine/Graphics/DebugUI.cpp index 8d4cab4..bf70a0d 100644 --- a/Engine/Graphics/DebugUI.cpp +++ b/Engine/Graphics/DebugUI.cpp @@ -1,4 +1,8 @@ #include "DebugUI.hpp" + +#include +#include + #include "../Misc/Fonts.hpp" @@ -10,7 +14,22 @@ DebugUI::DebugUI() { gDbgText.setFillColor(sf::Color::White); } -void DebugUI::UpdateDebugData(std::shared_ptr context) { - gDbgText.setString("CPU Usage: XXX.X % (ticks: XXXX.X ms)\nFPS: XXX\nRAM Usage: XXXX.X MB"); +void DebugUI::UpdateDebugData(const double cpu_usage, const double loop_time, const int fps, const std::size_t ram_virt_usage, const std::size_t ram_phys_usage) { + gDbgStats.cpu_usage = cpu_usage; + gDbgStats.loop_time = loop_time; + gDbgStats.fps = fps; + gDbgStats.ram_virt_usage = ram_virt_usage; + gDbgStats.ram_phys_usage = ram_phys_usage; +} + +void DebugUI::DrawDebugData(std::shared_ptr context) { + std::ostringstream outStrStream; + outStrStream << std::fixed; + + outStrStream << std::setprecision(0) << "CPU Usage: " << gDbgStats.cpu_usage << " % (cycle: " << std::setprecision(1) << gDbgStats.loop_time << " ms)" << std::endl; + outStrStream << std::setprecision(0) << "FPS: " << gDbgStats.fps << std::endl; + outStrStream << std::setprecision(1) << "RAM Usage: " << gDbgStats.ram_virt_usage << "MB(V) / " << gDbgStats.ram_phys_usage << "MB(P)"; + + gDbgText.setString(outStrStream.str()); context->draw(gDbgText); } \ No newline at end of file diff --git a/Engine/Graphics/DebugUI.hpp b/Engine/Graphics/DebugUI.hpp index 9a8057e..cecd2f2 100644 --- a/Engine/Graphics/DebugUI.hpp +++ b/Engine/Graphics/DebugUI.hpp @@ -1,21 +1,33 @@ #pragma once #include +#include #include #include #include +struct sDbgStats { + double cpu_usage; + double loop_time; + int fps; + std::size_t ram_virt_usage; + std::size_t ram_phys_usage; +}; + class DebugUI { public: DebugUI(); ~DebugUI() {} - void UpdateDebugData(std::shared_ptr context); + void UpdateDebugData(const double cpu_usage, const double loop_time, const int fps, const std::size_t ram_virt_usage, const std::size_t ram_phys_usage); + void DrawDebugData(std::shared_ptr context); private: sf::Font gDbgFont; sf::Text gDbgText; + struct sDbgStats gDbgStats = {0}; + }; \ No newline at end of file diff --git a/Engine/Utils/Perfs.cpp b/Engine/Utils/Perfs.cpp new file mode 100644 index 0000000..dfb2aee --- /dev/null +++ b/Engine/Utils/Perfs.cpp @@ -0,0 +1,81 @@ +#include "Perfs.hpp" + +#ifdef __MINGW32__ + #include + #include +#else + #include "stdlib.h" + #include "stdio.h" + #include "string.h" +#endif + +#ifndef __MINGW32__ +static int parseLine(char* line) { + // This assumes that a digit will be found and the line ends in " Kb". + int i = strlen(line); + const char* p = line; + while (*p <'0' || *p > '9') p++; + line[i-3] = '\0'; + i = atoi(p); + return i; +} + +// Returned value in KB! +static int getLinuxVirtualMemUsage() { + FILE* file = fopen("/proc/self/status", "r"); + int result = -1; + char line[128]; + + while (fgets(line, 128, file) != NULL){ + if (strncmp(line, "VmSize:", 7) == 0){ + result = parseLine(line); + break; + } + } + fclose(file); + return result; +} + +// Returned value in KB! +static int getLinuxPhysicalMemUsage() { + FILE* file = fopen("/proc/self/status", "r"); + int result = -1; + char line[128]; + + while (fgets(line, 128, file) != NULL){ + if (strncmp(line, "VmRSS:", 7) == 0){ + result = parseLine(line); + break; + } + } + fclose(file); + return result; +} +#endif + + +// Returned value in KB! +const size_t PerfsGetVirtMem(void) { + size_t out; +#ifdef __MINGW32__ + PROCESS_MEMORY_COUNTERS_EX memCounter; + GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&memCounter, sizeof(memCounter)); + out = memCounter.PrivateUsage / 1000; +#else + out = getLinuxVirtualMemUsage(); +#endif + return out; +} + +// Returned value in KB! +const size_t PerfsGetPhysMem(void) { + size_t out; +#ifdef __MINGW32__ + PROCESS_MEMORY_COUNTERS_EX memCounter; + GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&memCounter, sizeof(memCounter)); + out = memCounter.WorkingSetSize / 1000; +#else + out = getLinuxPhysicalMemUsage(); +#endif + return out; +} \ No newline at end of file diff --git a/Engine/Utils/Perfs.hpp b/Engine/Utils/Perfs.hpp new file mode 100644 index 0000000..2436b59 --- /dev/null +++ b/Engine/Utils/Perfs.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include + +const size_t PerfsGetVirtMem(void); +const size_t PerfsGetPhysMem(void); diff --git a/Engine/Utils/Timers.hpp b/Engine/Utils/Timers.hpp index 725691b..ec74d33 100644 --- a/Engine/Utils/Timers.hpp +++ b/Engine/Utils/Timers.hpp @@ -11,18 +11,21 @@ public: } ~SysTimer() {} + void Reset() { this->t_previousDelta = std::chrono::high_resolution_clock::now(); } + const double GetElapsedTime() const { return (double)(std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - t_start).count()); } const double GetElapsedTimeS() const { return GetElapsedTime() / 1000000; } + const double GetDeltaTime() { return GetDeltaTime(false); } const double GetDeltaTime(bool reset) { auto curr_time = std::chrono::high_resolution_clock::now(); - double ret = std::chrono::duration_cast(curr_time - t_previousDelta).count(); + double ret = std::chrono::duration_cast(curr_time - t_previousDelta).count(); if (reset) this->t_previousDelta = curr_time; return ret; diff --git a/Game.cpp b/Game.cpp index f44c9a9..72f37ce 100644 --- a/Game.cpp +++ b/Game.cpp @@ -1,16 +1,29 @@ #include "Game.hpp" +#include + +#include #include #include +#include "Engine/Utils/Perfs.hpp" +#include "Engine/Utils/Timers.hpp" + using std::make_shared; using std::make_unique; +#define TARGET_FPS (1000/60) + + +std::unique_ptr mPerfsTimer; Game::Game(bool dbg) : gbDbgModeEnabled(dbg) { InitWindows(); - if (gbDbgModeEnabled) + if (gbDbgModeEnabled) { gDbgUI = make_unique(); + mPerfsTimer = make_unique(); + mPerfsTimer->Reset(); + } } void Game::InitWindows() { @@ -32,20 +45,43 @@ void Game::InitWindows() { gMainWindow->setMouseCursorVisible(true); } -GAME_STATUS Game::Update(SysTimer& time) { - gLoopTimer.restart(); +GAME_STATUS Game::Tick(SysTimer& time) { + static std::vector msTicksMonitoring; + static double mPrevdelta = 0; + static int mFrameCnt = 0; + double mDelta = time.GetDeltaTime(); + + if (gDbgUI != nullptr && mPerfsTimer != nullptr) { + msTicksMonitoring.push_back((mDelta - mPrevdelta) / TARGET_FPS); + if (mPerfsTimer->GetDeltaTime() >= 1000) { // Re-evaluate CPU usage every seconds (for FP'S') + double cpuUsage = std::accumulate(msTicksMonitoring.begin(), msTicksMonitoring.end(), 0) / msTicksMonitoring.size(); + + gDbgUI->UpdateDebugData(cpuUsage, (mDelta - mPrevdelta), mFrameCnt, (PerfsGetVirtMem() / 1000), (PerfsGetPhysMem() / 1000)); + + mFrameCnt = -1; // Minus one to compensate the direct next frame + msTicksMonitoring.clear(); + mPerfsTimer->Reset(); + } + } sf::Event event; - while (gMainWindow->pollEvent(event)) { + while (gMainWindow->pollEvent(event)) { // Wait for next frame (fixed FPS) if (event.type == sf::Event::Closed) gMainWindow->close(); } - /* - auto ticks = static_cast(mLoopTimer.getElapsedTime().asMilliseconds()); - gTicksMonitoring.push_back(ticks); - double cpuUsage = std::accumulate(gTicksMonitoring.begin(), gTicksMonitoring.end(), 0) / gTicksMonitoring.size(); - */ + if (mDelta >= TARGET_FPS) { //TODO: Replace with global var and use dynamic window loader + Update(); + Render(); + + mFrameCnt++; + time.Reset(); + mPrevdelta = -mDelta; + } else { + mPrevdelta = mDelta; + } + + sf::sleep(sf::milliseconds(TARGET_FPS - (mDelta - mPrevdelta))); if (gMainWindow->isOpen()) return GAME_RUNNING; @@ -53,7 +89,11 @@ GAME_STATUS Game::Update(SysTimer& time) { return GAME_QUIT; } -void Game::Render(SysTimer& time) { +void Game::Update() { + +} + +void Game::Render() { // Clear the draw buffer gMainWindow->clear(); @@ -62,7 +102,7 @@ void Game::Render(SysTimer& time) { // Draw the UI above //UI::draw() // Draw the debug informations if enabled - if (gDbgUI != nullptr) gDbgUI->UpdateDebugData(gMainWindow); + if (gDbgUI != nullptr) gDbgUI->DrawDebugData(gMainWindow); // Present the draw buffer gMainWindow->display(); diff --git a/Game.hpp b/Game.hpp index 1846af6..cb67f39 100644 --- a/Game.hpp +++ b/Game.hpp @@ -2,7 +2,6 @@ #include #include -#include #include #include "Engine/Graphics/DebugUI.hpp" @@ -25,18 +24,16 @@ public: Game(Game const&) = delete; Game& operator= (Game const&) = delete; - GAME_STATUS Update(SysTimer& time); - void Render(SysTimer& time); + GAME_STATUS Tick(SysTimer& time); private: void InitWindows(); + void Update(); + void Render(); bool gbDbgModeEnabled; std::shared_ptr gMainWindow = nullptr; std::unique_ptr gDbgUI = nullptr; - sf::Clock gLoopTimer; - std::vector gTicksMonitoring; - }; \ No newline at end of file diff --git a/ProtoTank.cpp b/ProtoTank.cpp index efe48d4..a5686f6 100644 --- a/ProtoTank.cpp +++ b/ProtoTank.cpp @@ -5,29 +5,24 @@ #include #include "Game.hpp" -#include "Engine/Utils/Timers.hpp" - -using std::cout, std::endl, std::fixed, std::setprecision; int main(int argc, char** argv) { SysTimer mSysTimer; - cout << fixed << setprecision(6); + std::cout << std::fixed << std::setprecision(6); GAME_STATUS status = GAME_INIT; - cout << "[" << mSysTimer.GetElapsedTimeS() << "] Init game instance" << endl; + std::cout << "[" << mSysTimer.GetElapsedTimeS() << "] Init game instance" << std::endl; auto arcadeGame = std::make_unique(true); for ( ;; ) { - status = arcadeGame->Update(mSysTimer); + status = arcadeGame->Tick(mSysTimer); if (status == GAME_QUIT) break; - - arcadeGame->Render(mSysTimer); } - cout << "[" << mSysTimer.GetElapsedTimeS() << "] Bye bye!" << endl; + std::cout << "[" << mSysTimer.GetElapsedTimeS() << "] Bye bye!" << std::endl; return EXIT_SUCCESS; } diff --git a/srcs.list b/srcs.list index 99c2e63..a3f265b 100644 --- a/srcs.list +++ b/srcs.list @@ -7,6 +7,8 @@ set(UTILS_SCRS Engine/Utils/3DMaths.cpp Engine/Utils/3DMaths.hpp Engine/Utils/Timers.hpp + Engine/Utils/Perfs.cpp + Engine/Utils/Perfs.hpp ) set(MISC_SCRS Engine/Misc/Console.cpp