diff --git a/README.md b/README.md index 25d03230..e8a53432 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,29 @@ +# MangoHud Specific + +- `DXVK_HUD_OFFSET_X` Set X offset of the DVXK Hud. +- `DXVK_HUD_OFFSET_Y` Set Y offset of the DVXK Hud. +- `DXVK_LOG_TO_FILE` Turn on logging and select path/filename (Fps,Cpu load,Gpu load) +- Logging Gpu load requires either mangogpuload or gpuload hud options + +# Hud options +- `mangogpuload` : Shows current gpu load. +- `mangocpuload` : Shows current cpu load. + +# Keybinds +- `F2` : Toggle Logging on/off +- `F12` : Toggle Hud on/off + +# MangoLog file + + When you press F2, a file is created with your chosen name + date/time stamp. + this file can be uploaded to https://flightlessmango.com/logs/new to create graphs automatically. + you can share the created page with others, just link it. + + #### Multiple log files + + It's possible to upload multiple files, you can rename them to your preferred names and upload them in a batch. + The graphs will then use those names in the data. + # DXVK A Vulkan-based translation layer for Direct3D 10/11 which allows running 3D applications on Linux using Wine. diff --git a/src/dxvk/dxvk_cpu.h b/src/dxvk/dxvk_cpu.h new file mode 100644 index 00000000..b2c8736a --- /dev/null +++ b/src/dxvk/dxvk_cpu.h @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +const int NUM_CPU_STATES = 10; + +struct Cpus{ + size_t num; + string name; + float value; + string output; +}; + +size_t numCpuCores = dxvk::thread::hardware_concurrency(); +size_t arraySize = numCpuCores + 1; +std::vector cpuArray; + +void coreCounting(){ + cpuArray.push_back({0, "CPU:"}); + for (size_t i = 0; i < arraySize; i++) { + size_t offset = i; + stringstream ss; + ss << "CPU" << offset << ":"; + string cpuNameString = ss.str(); + cpuArray.push_back({i+1 , cpuNameString}); + } +} + +std::string m_cpuUtilizationString; + +enum CPUStates +{ + S_USER = 0, + S_NICE, + S_SYSTEM, + S_IDLE, + S_IOWAIT, + S_IRQ, + S_SOFTIRQ, + S_STEAL, + S_GUEST, + S_GUEST_NICE +}; + +typedef struct CPUData +{ + std::string cpu; + size_t times[NUM_CPU_STATES]; +} CPUData; + +void ReadStatsCPU(std::vector & entries) +{ + std::ifstream fileStat("/proc/stat"); + + std::string line; + + const std::string STR_CPU("cpu"); + const std::size_t LEN_STR_CPU = STR_CPU.size(); + const std::string STR_TOT("tot"); + + while(std::getline(fileStat, line)) + { + // cpu stats line found + if(!line.compare(0, LEN_STR_CPU, STR_CPU)) + { + std::istringstream ss(line); + + // store entry + entries.emplace_back(CPUData()); + CPUData & entry = entries.back(); + + // read cpu label + ss >> entry.cpu; + + if(entry.cpu.size() > LEN_STR_CPU) + entry.cpu.erase(0, LEN_STR_CPU); + else + entry.cpu = STR_TOT; + + // read times + for(int i = 0; i < NUM_CPU_STATES; ++i) + ss >> entry.times[i]; + } + } +} + +size_t GetIdleTime(const CPUData & e) +{ + return e.times[S_IDLE] + + e.times[S_IOWAIT]; +} + +size_t GetActiveTime(const CPUData & e) +{ + return e.times[S_USER] + + e.times[S_NICE] + + e.times[S_SYSTEM] + + e.times[S_IRQ] + + e.times[S_SOFTIRQ] + + e.times[S_STEAL] + + e.times[S_GUEST] + + e.times[S_GUEST_NICE]; +} + +void PrintStats(const std::vector & entries1, const std::vector & entries2) +{ + const size_t NUM_ENTRIES = entries1.size(); + + for(size_t i = 0; i < NUM_ENTRIES; ++i) + { + const CPUData & e1 = entries1[i]; + const CPUData & e2 = entries2[i]; + + const float ACTIVE_TIME = static_cast(GetActiveTime(e2) - GetActiveTime(e1)); + const float IDLE_TIME = static_cast(GetIdleTime(e2) - GetIdleTime(e1)); + const float TOTAL_TIME = ACTIVE_TIME + IDLE_TIME; + + cpuArray[i].value = (truncf(100.f * ACTIVE_TIME / TOTAL_TIME) * 10 / 10); + } +} + +int getCpuUsage() +{ + std::vector entries1; + std::vector entries2; + + // snapshot 1 + ReadStatsCPU(entries1); + + // 100ms pause + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + // snapshot 2 + ReadStatsCPU(entries2); + + // print output + PrintStats(entries1, entries2); + + return 0; +} + + +void updateCpuStrings(){ + for (size_t i = 0; i < arraySize; i++) { + size_t spacing = 10; + string value = to_string(cpuArray[i].value); + value.erase( value.find_last_not_of('0') + 1, std::string::npos ); + size_t correctionValue = (spacing - cpuArray[i].name.length()) - value.length(); + string correction = ""; + for (size_t i = 0; i < correctionValue; i++) { + correction.append(" "); + } + stringstream ss; + if (i < 11) { + if (i == 0) { + ss << cpuArray[i].name << correction << cpuArray[i].value << "%"; + } else { + ss << cpuArray[i].name << correction << cpuArray[i].value << "%"; + } + } else { + ss << cpuArray[i].name << correction << cpuArray[i].value << "%"; + } + cpuArray[i].output = ss.str(); + } + } \ No newline at end of file diff --git a/src/dxvk/hud/dxvk_hud.cpp b/src/dxvk/hud/dxvk_hud.cpp index 4fcd3bd2..b6e23c4c 100644 --- a/src/dxvk/hud/dxvk_hud.cpp +++ b/src/dxvk/hud/dxvk_hud.cpp @@ -1,9 +1,13 @@ -#include #include - #include "dxvk_hud.h" +#include +#include namespace dxvk::hud { + float Hud::offset_x_float = 0; + float Hud::offset_y_float = 0; + bool show_hud = true; + int lastPress; Hud::Hud( const Rc& device, @@ -60,6 +64,14 @@ namespace dxvk::hud { Rc Hud::createHud(const Rc& device) { std::string hudElements = env::getEnvVar("DXVK_HUD"); + std::string offset_x = env::getEnvVar("DXVK_HUD_OFFSET_X"); + std::string offset_y = env::getEnvVar("DXVK_HUD_OFFSET_Y"); + + if (!offset_x.empty()) + offset_x_float = stof(offset_x); + + if (!offset_y.empty()) + offset_y_float = stof(offset_y); if (hudElements.empty()) hudElements = device->config().hud; @@ -82,33 +94,45 @@ namespace dxvk::hud { m_renderer.beginFrame(ctx, m_uniformData.surfaceSize); } - void Hud::renderHudElements(const Rc& ctx) { - HudPos position = { 8.0f, 24.0f }; + if(GetAsyncKeyState(VK_F12) & 0x8000) + { + if (GetTickCount() - lastPress > 500){ + lastPress = GetTickCount(); + if (show_hud){ + show_hud = false; + } else { + show_hud = true; + } + } + } - if (m_config.elements.test(HudElement::DxvkVersion)) { - m_renderer.drawText(ctx, 16.0f, - { position.x, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - "DXVK " DXVK_VERSION); - position.y += 24.0f; - } - - if (m_config.elements.test(HudElement::DxvkClientApi)) { - m_renderer.drawText(ctx, 16.0f, - { position.x, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_device->clientApi()); - position.y += 24.0f; - } - - if (m_config.elements.test(HudElement::DeviceInfo)) { - position = m_hudDeviceInfo.render( - ctx, m_renderer, position); - } + HudPos position = { offset_x_float + 8.0f, offset_y_float + 24.0f }; - position = m_hudFramerate.render(ctx, m_renderer, position); - position = m_hudStats .render(ctx, m_renderer, position); + if (show_hud){ + if (m_config.elements.test(HudElement::DxvkVersion)) { + m_renderer.drawText(ctx, 16.0f, + { position.x, position.y }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + "DXVK " DXVK_VERSION); + position.y += 24.0f; + } + + if (m_config.elements.test(HudElement::DxvkClientApi)) { + m_renderer.drawText(ctx, 16.0f, + { position.x, position.y }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + m_device->clientApi()); + position.y += 24.0f; + } + + if (m_config.elements.test(HudElement::DeviceInfo)) { + position = m_hudDeviceInfo.render( + ctx, m_renderer, position); + } + position = m_hudFramerate.render(ctx, m_renderer, position); + position = m_hudStats .render(ctx, m_renderer, position); + } } diff --git a/src/dxvk/hud/dxvk_hud.h b/src/dxvk/hud/dxvk_hud.h index 3b702c3b..71779977 100644 --- a/src/dxvk/hud/dxvk_hud.h +++ b/src/dxvk/hud/dxvk_hud.h @@ -29,6 +29,8 @@ namespace dxvk::hud { class Hud : public RcObject { public: + static float offset_x_float; + static float offset_y_float; Hud( const Rc& device, diff --git a/src/dxvk/hud/dxvk_hud_config.cpp b/src/dxvk/hud/dxvk_hud_config.cpp index fe1745bd..020a394b 100644 --- a/src/dxvk/hud/dxvk_hud_config.cpp +++ b/src/dxvk/hud/dxvk_hud_config.cpp @@ -16,6 +16,9 @@ namespace dxvk::hud { { "version", HudElement::DxvkVersion }, { "api", HudElement::DxvkClientApi }, { "compiler", HudElement::CompilerActivity }, + { "mangogpuload", HudElement::GpuLoad }, + { "mangocpuload", HudElement::CpuLoad }, + { "mangocpuload", HudElement::Logging }, }}; diff --git a/src/dxvk/hud/dxvk_hud_config.h b/src/dxvk/hud/dxvk_hud_config.h index 05a1e4b6..5d571663 100644 --- a/src/dxvk/hud/dxvk_hud_config.h +++ b/src/dxvk/hud/dxvk_hud_config.h @@ -22,6 +22,9 @@ namespace dxvk::hud { DxvkVersion = 8, DxvkClientApi = 9, CompilerActivity = 10, + GpuLoad = 11, + CpuLoad = 12, + Logging = 13, }; using HudElements = Flags; diff --git a/src/dxvk/hud/dxvk_hud_fps.cpp b/src/dxvk/hud/dxvk_hud_fps.cpp index f8cb6e7d..84eb2db6 100644 --- a/src/dxvk/hud/dxvk_hud_fps.cpp +++ b/src/dxvk/hud/dxvk_hud_fps.cpp @@ -1,7 +1,22 @@ #include "dxvk_hud_fps.h" +#include "dxvk_hud_stats.h" +#include "../dxvk_cpu.h" +#include #include #include +using namespace std; +time_t now_log = time(0); +tm *log_time = localtime(&now_log); +fstream f; + +struct logData{ + float fps; + float cpu; + uint64_t gpu; +}; + +std::vector logArray; namespace dxvk::hud { @@ -9,8 +24,8 @@ namespace dxvk::hud { : m_elements (elements), m_fpsString ("FPS: "), m_prevFpsUpdate(Clock::now()), - m_prevFtgUpdate(Clock::now()) { - + m_prevFtgUpdate(Clock::now()), + m_prevLogUpdate(Clock::now()) { } @@ -25,11 +40,48 @@ namespace dxvk::hud { TimePoint now = Clock::now(); TimeDiff elapsedFps = std::chrono::duration_cast(now - m_prevFpsUpdate); TimeDiff elapsedFtg = std::chrono::duration_cast(now - m_prevFtgUpdate); + TimeDiff elapsedLog = std::chrono::duration_cast(now - m_prevLogUpdate); m_prevFtgUpdate = now; + + if(GetAsyncKeyState(VK_F2) & 0x8000) + { + elapsedF2 = std::chrono::duration_cast(now - m_prevF2Press); + if (elapsedF2.count() > UpdateInterval || elapsedF2.count() == 0) { + if (mango_logging){ + m_prevF2Press = now; + mango_logging = false; + for (size_t i = 0; i < logArray.size(); i++) { + f << logArray[i].fps << "," << logArray[i].cpu << "," << logArray[i].gpu << endl; + } + f.close(); + logArray.clear(); + } else { + m_prevF2Press = now; + now_log = time(0); + log_time = localtime(&now_log); + mango_logging = true; + string date = to_string(log_time->tm_year + 1900) + "-" + to_string(1 + log_time->tm_mon) + "-" + to_string(log_time->tm_mday) + "_" + to_string(1 + log_time->tm_hour) + "-" + to_string(1 + log_time->tm_min) + "-" + to_string(1 + log_time->tm_sec); + f.open(logging + "_" + date, f.out | f.app); + } + } + } + + if (elapsedLog.count() >= LogUpdateInterval) { + fps = (10'000'000ll * m_frameCount) / elapsedFps.count(); + if (!logging.empty()){ + if (mango_logging){ + logArray.push_back({float(fps / 10 + (float(fps % 10) / 10)), cpuArray[0].value, gpuLoad}); + } + } + m_prevLogUpdate = now; + } - // Update FPS string if (elapsedFps.count() >= UpdateInterval) { - const int64_t fps = (10'000'000ll * m_frameCount) / elapsedFps.count(); + // Update FPS string + coreCounting(); + dxvk::thread([this] () { getCpuUsage();}); + updateCpuStrings(); + m_cpuUtilizationString = str::format(cpuArray[0].output); m_fpsString = str::format("FPS: ", fps / 10, ".", fps % 10); m_prevFpsUpdate = now; @@ -46,32 +98,81 @@ namespace dxvk::hud { const Rc& context, HudRenderer& renderer, HudPos position) { + if (m_elements.test(HudElement::GpuLoad)) { + position = this->renderGpuText( + context, renderer, position); + } + if (m_elements.test(HudElement::CpuLoad)) { + position = this->renderCpuText( + context, renderer, position); + } if (m_elements.test(HudElement::Framerate)) { position = this->renderFpsText( context, renderer, position); - } + } if (m_elements.test(HudElement::Frametimes)) { position = this->renderFrametimeGraph( context, renderer, position); } + if (mango_logging && !logging.empty()) { + this->renderLogging(context, renderer, + { float(renderer.surfaceSize().width) - 250.0f, float(renderer.surfaceSize().height) - 20.0f }); + } + return position; } - HudPos HudFps::renderFpsText( - const Rc& context, - HudRenderer& renderer, - HudPos position) { + + HudPos HudFps::renderGpuText( + const Rc& context, + HudRenderer& renderer, + HudPos position) { + renderer.drawText(context, 16.0f, + { position.x, position.y }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + m_gpuLoadString); + + return HudPos { position.x, position.y + 24 }; +} + +HudPos HudFps::renderCpuText( +const Rc& context, + HudRenderer& renderer, + HudPos position) { +renderer.drawText(context, 16.0f, + { position.x, position.y }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + m_cpuUtilizationString); + +return HudPos { position.x, position.y + 24 }; +} + +HudPos HudFps::renderFpsText( + const Rc& context, + HudRenderer& renderer, + HudPos position) { renderer.drawText(context, 16.0f, { position.x, position.y }, { 1.0f, 1.0f, 1.0f, 1.0f }, m_fpsString); + + return HudPos { position.x, position.y + 24 }; + } - return HudPos { position.x, position.y + 24 }; - } - + HudPos HudFps::renderLogging( + const Rc& context, + HudRenderer& renderer, + HudPos position) { + renderer.drawText(context, 16.0f, + { position.x, position.y }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + "Currently Logging..."); + + return HudPos { position.x, position.y}; + } HudPos HudFps::renderFrametimeGraph( const Rc& context, diff --git a/src/dxvk/hud/dxvk_hud_fps.h b/src/dxvk/hud/dxvk_hud_fps.h index c8c4b984..b1476889 100644 --- a/src/dxvk/hud/dxvk_hud_fps.h +++ b/src/dxvk/hud/dxvk_hud_fps.h @@ -17,8 +17,9 @@ namespace dxvk::hud { using TimeDiff = std::chrono::microseconds; using TimePoint = typename Clock::time_point; - constexpr static uint32_t NumDataPoints = 300; - constexpr static int64_t UpdateInterval = 500'000; + constexpr static uint32_t NumDataPoints = 300; + constexpr static int64_t UpdateInterval = 500'000; + constexpr static int64_t LogUpdateInterval = 100'000; public: HudFps(HudElements elements); @@ -36,23 +37,46 @@ namespace dxvk::hud { const HudElements m_elements; std::string m_fpsString; + bool mango_logging = false; + time_t lastPress = time(0); + std::string logging = env::getEnvVar("DXVK_LOG_TO_FILE"); + int64_t fps; TimePoint m_prevFpsUpdate; TimePoint m_prevFtgUpdate; + TimePoint m_prevLogUpdate; + TimePoint m_prevF2Press; + TimeDiff elapsedF2; int64_t m_frameCount = 0; std::array m_dataPoints = {}; uint32_t m_dataPointId = 0; + + HudPos renderGpuText( + const Rc& context, + HudRenderer& renderer, + HudPos position); + + HudPos renderCpuText( + const Rc& context, + HudRenderer& renderer, + HudPos position); HudPos renderFpsText( const Rc& context, HudRenderer& renderer, HudPos position); + HudPos renderFrametimeGraph( const Rc& context, HudRenderer& renderer, HudPos position); + + HudPos renderLogging( + const Rc& context, + HudRenderer& renderer, + HudPos position); }; diff --git a/src/dxvk/hud/dxvk_hud_stats.cpp b/src/dxvk/hud/dxvk_hud_stats.cpp index 995f186b..be8a5eb7 100644 --- a/src/dxvk/hud/dxvk_hud_stats.cpp +++ b/src/dxvk/hud/dxvk_hud_stats.cpp @@ -1,5 +1,7 @@ #include "dxvk_hud_stats.h" +std::string m_gpuLoadString = "GPU: "; +uint64_t gpuLoad; namespace dxvk::hud { HudStats::HudStats(HudElements elements) @@ -24,7 +26,7 @@ namespace dxvk::hud { // GPU load is a bit more complex than that since // we don't want to update this every frame - if (m_elements.test(HudElement::StatGpuLoad)) + if (m_elements.test(HudElement::GpuLoad) || m_elements.test(HudElement::StatGpuLoad)) this->updateGpuLoad(); } @@ -70,8 +72,8 @@ namespace dxvk::hud { uint64_t busyTicks = ticks > m_diffGpuIdleTicks ? uint64_t(ticks - m_diffGpuIdleTicks) : uint64_t(0); - - m_gpuLoadString = str::format("GPU: ", (100 * busyTicks) / ticks, "%"); + gpuLoad = 100 * busyTicks / ticks; + m_gpuLoadString = str::format("GPU: ", (100 * busyTicks) / ticks, "%"); } } @@ -224,7 +226,8 @@ namespace dxvk::hud { HudElement::StatPipelines, HudElement::StatMemory, HudElement::StatGpuLoad, - HudElement::CompilerActivity); + HudElement::CompilerActivity, + HudElement::GpuLoad); } } diff --git a/src/dxvk/hud/dxvk_hud_stats.h b/src/dxvk/hud/dxvk_hud_stats.h index 227f600c..b7d740f1 100644 --- a/src/dxvk/hud/dxvk_hud_stats.h +++ b/src/dxvk/hud/dxvk_hud_stats.h @@ -7,6 +7,8 @@ #include "dxvk_hud_config.h" #include "dxvk_hud_renderer.h" +extern std::string m_gpuLoadString; +extern uint64_t gpuLoad; namespace dxvk::hud { /** @@ -44,8 +46,6 @@ namespace dxvk::hud { uint64_t m_prevGpuIdleTicks = 0; uint64_t m_diffGpuIdleTicks = 0; - std::string m_gpuLoadString = "GPU: "; - void updateGpuLoad(); HudPos printDrawCallStats(