From fc3f45c0828f8c326ddc3ec79b21070c3ac01f23 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 3 Apr 2018 14:49:13 +0200 Subject: [PATCH] [hud] Added stat counters to the HUD --- src/dxvk/dxvk_stats.cpp | 11 +- src/dxvk/dxvk_stats.h | 9 ++ src/dxvk/hud/dxvk_hud.cpp | 15 +-- src/dxvk/hud/dxvk_hud.h | 2 + src/dxvk/hud/dxvk_hud_config.cpp | 10 +- src/dxvk/hud/dxvk_hud_config.h | 10 +- src/dxvk/hud/dxvk_hud_fps.cpp | 2 +- src/dxvk/hud/dxvk_hud_stats.cpp | 168 +++++++++++++++++++++++++++++++ src/dxvk/hud/dxvk_hud_stats.h | 63 ++++++++++++ src/dxvk/meson.build | 1 + src/util/util_flags.h | 12 +++ 11 files changed, 288 insertions(+), 15 deletions(-) create mode 100644 src/dxvk/hud/dxvk_hud_stats.cpp create mode 100644 src/dxvk/hud/dxvk_hud_stats.h diff --git a/src/dxvk/dxvk_stats.cpp b/src/dxvk/dxvk_stats.cpp index 4441f537f..72e6f8a78 100644 --- a/src/dxvk/dxvk_stats.cpp +++ b/src/dxvk/dxvk_stats.cpp @@ -12,10 +12,17 @@ namespace dxvk { } + DxvkStatCounters DxvkStatCounters::diff(const DxvkStatCounters& other) const { + DxvkStatCounters result; + for (size_t i = 0; i < m_counters.size(); i++) + result.m_counters[i] = m_counters[i] - other.m_counters[i]; + return result; + } + + void DxvkStatCounters::merge(const DxvkStatCounters& other) { - for (size_t i = 0; i < m_counters.size(); i++) { + for (size_t i = 0; i < m_counters.size(); i++) m_counters[i] += other.m_counters[i]; - } } diff --git a/src/dxvk/dxvk_stats.h b/src/dxvk/dxvk_stats.h index 4a549131a..33df9fe99 100644 --- a/src/dxvk/dxvk_stats.h +++ b/src/dxvk/dxvk_stats.h @@ -77,6 +77,15 @@ namespace dxvk { m_counters[uint32_t(ctr)] = 0; } + /** + * \brief Computes difference + * + * Computes difference between counter values. + * \param [in] other Counters to subtract + * \returns Difference between counter sets + */ + DxvkStatCounters diff(const DxvkStatCounters& other) const; + /** * \brief Merges counters * diff --git a/src/dxvk/hud/dxvk_hud.cpp b/src/dxvk/hud/dxvk_hud.cpp index 0b4a58cf7..2d3c9ac30 100644 --- a/src/dxvk/hud/dxvk_hud.cpp +++ b/src/dxvk/hud/dxvk_hud.cpp @@ -12,7 +12,8 @@ namespace dxvk::hud { m_context (m_device->createContext()), m_textRenderer (m_device, m_context), m_uniformBuffer (createUniformBuffer()), - m_hudDeviceInfo (device) { + m_hudDeviceInfo (device), + m_hudStats (config.elements) { this->setupConstantState(); } @@ -31,6 +32,7 @@ namespace dxvk::hud { } m_hudFps.update(); + m_hudStats.update(m_device); this->beginRenderPass(recreateFbo); this->updateUniformBuffer(); @@ -42,11 +44,9 @@ namespace dxvk::hud { Rc Hud::createHud(const Rc& device) { HudConfig config(env::getEnvVar(L"DXVK_HUD")); - if (config.elements.isClear()) - return nullptr; - - // TODO implement configuration options for the HUD - return new Hud(device, config); + return !config.elements.isClear() + ? new Hud(device, config) + : nullptr; } @@ -78,6 +78,9 @@ namespace dxvk::hud { position = m_hudFps.renderText( m_context, m_textRenderer, position); } + + position = m_hudStats.renderText( + m_context, m_textRenderer, position); } diff --git a/src/dxvk/hud/dxvk_hud.h b/src/dxvk/hud/dxvk_hud.h index e8aaf7fc0..7d3613b84 100644 --- a/src/dxvk/hud/dxvk_hud.h +++ b/src/dxvk/hud/dxvk_hud.h @@ -8,6 +8,7 @@ #include "dxvk_hud_devinfo.h" #include "dxvk_hud_fps.h" #include "dxvk_hud_text.h" +#include "dxvk_hud_stats.h" namespace dxvk::hud { @@ -83,6 +84,7 @@ namespace dxvk::hud { HudDeviceInfo m_hudDeviceInfo; HudFps m_hudFps; + HudStats m_hudStats; void renderText(); diff --git a/src/dxvk/hud/dxvk_hud_config.cpp b/src/dxvk/hud/dxvk_hud_config.cpp index dcdb8065a..bdd0b9e18 100644 --- a/src/dxvk/hud/dxvk_hud_config.cpp +++ b/src/dxvk/hud/dxvk_hud_config.cpp @@ -1,10 +1,14 @@ #include "dxvk_hud_config.h" -namespace dxvk { +namespace dxvk::hud { const std::unordered_map g_hudElements = {{ - { "devinfo", HudElement::DeviceInfo }, - { "fps", HudElement::Framerate }, + { "devinfo", HudElement::DeviceInfo }, + { "fps", HudElement::Framerate }, + { "drawcalls", HudElement::StatDrawCalls }, + { "submissions", HudElement::StatSubmissions }, + { "pipelines", HudElement::StatPipelines }, + { "memory", HudElement::StatMemory }, }}; diff --git a/src/dxvk/hud/dxvk_hud_config.h b/src/dxvk/hud/dxvk_hud_config.h index ab7911c71..64de9f26a 100644 --- a/src/dxvk/hud/dxvk_hud_config.h +++ b/src/dxvk/hud/dxvk_hud_config.h @@ -2,7 +2,7 @@ #include "../dxvk_include.h" -namespace dxvk { +namespace dxvk::hud { /** * \brief HUD element @@ -11,8 +11,12 @@ namespace dxvk { * or disable HUD elements on demand. */ enum class HudElement { - DeviceInfo = 0, - Framerate = 1, + DeviceInfo = 0, + Framerate = 1, + StatDrawCalls = 2, + StatSubmissions = 3, + StatPipelines = 4, + StatMemory = 5, }; using HudElements = Flags; diff --git a/src/dxvk/hud/dxvk_hud_fps.cpp b/src/dxvk/hud/dxvk_hud_fps.cpp index ca840a786..cdc247028 100644 --- a/src/dxvk/hud/dxvk_hud_fps.cpp +++ b/src/dxvk/hud/dxvk_hud_fps.cpp @@ -41,7 +41,7 @@ namespace dxvk::hud { { 1.0f, 1.0f, 1.0f, 1.0f }, m_fpsString); - return HudPos { position.x, position.y + 20 }; + return HudPos { position.x, position.y + 24 }; } } \ No newline at end of file diff --git a/src/dxvk/hud/dxvk_hud_stats.cpp b/src/dxvk/hud/dxvk_hud_stats.cpp new file mode 100644 index 000000000..453a96fe9 --- /dev/null +++ b/src/dxvk/hud/dxvk_hud_stats.cpp @@ -0,0 +1,168 @@ +#include "dxvk_hud_stats.h" + +namespace dxvk::hud { + + HudStats::HudStats(HudElements elements) + : m_elements(filterElements(elements)) { } + + + HudStats::~HudStats() { + + } + + + void HudStats::update(const Rc& device) { + if (m_elements.isClear()) + return; + + // For some counters, we'll display the absolute value, + // for others, the average counter increment per frame. + DxvkStatCounters nextCounters = device->getStatCounters(); + m_diffCounters = nextCounters.diff(m_prevCounters); + m_prevCounters = nextCounters; + } + + + HudPos HudStats::renderText( + const Rc& context, + HudTextRenderer& renderer, + HudPos position) { + if (m_elements.test(HudElement::StatSubmissions)) + position = this->printSubmissionStats(context, renderer, position); + + if (m_elements.test(HudElement::StatDrawCalls)) + position = this->printDrawCallStats(context, renderer, position); + + if (m_elements.test(HudElement::StatPipelines)) + position = this->printPipelineStats(context, renderer, position); + + if (m_elements.test(HudElement::StatMemory)) + position = this->printMemoryStats(context, renderer, position); + + return position; + } + + + HudPos HudStats::printDrawCallStats( + const Rc& context, + HudTextRenderer& renderer, + HudPos position) { + const uint64_t frameCount = std::max(m_diffCounters.getCtr(DxvkStatCounter::QueuePresentCount), 1u); + + const uint64_t gpCalls = m_diffCounters.getCtr(DxvkStatCounter::CmdDrawCalls) / frameCount; + const uint64_t cpCalls = m_diffCounters.getCtr(DxvkStatCounter::CmdDispatchCalls) / frameCount; + const uint64_t rpCalls = m_diffCounters.getCtr(DxvkStatCounter::CmdRenderPassCount) / frameCount; + + const std::string strDrawCalls = str::format("Draw calls: ", gpCalls); + const std::string strDispatchCalls = str::format("Dispatch calls: ", cpCalls); + const std::string strRenderPasses = str::format("Render passes: ", rpCalls); + + renderer.drawText(context, 16.0f, + { position.x, position.y }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + strDrawCalls); + + renderer.drawText(context, 16.0f, + { position.x, position.y + 20.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + strDispatchCalls); + + renderer.drawText(context, 16.0f, + { position.x, position.y + 40.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + strRenderPasses); + + return { position.x, position.y + 64 }; + } + + + HudPos HudStats::printSubmissionStats( + const Rc& context, + HudTextRenderer& renderer, + HudPos position) { + const uint64_t frameCount = std::max(m_diffCounters.getCtr(DxvkStatCounter::QueuePresentCount), 1u); + const uint64_t numSubmits = m_diffCounters.getCtr(DxvkStatCounter::QueueSubmitCount) / frameCount; + + const std::string strSubmissions = str::format("Queue submissions: ", numSubmits); + + renderer.drawText(context, 16.0f, + { position.x, position.y }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + strSubmissions); + + return { position.x, position.y + 24.0f }; + } + + + HudPos HudStats::printPipelineStats( + const Rc& context, + HudTextRenderer& renderer, + HudPos position) { + constexpr uint64_t kib = 1024; + constexpr uint64_t mib = 1024 * 1024; + + const uint64_t gpCount = m_prevCounters.getCtr(DxvkStatCounter::PipeCountGraphics); + const uint64_t cpCount = m_prevCounters.getCtr(DxvkStatCounter::PipeCountCompute); + const uint64_t pcSize = m_prevCounters.getCtr(DxvkStatCounter::PipeCacheSize); + + const std::string strGpCount = str::format("Graphics pipelines: ", gpCount); + const std::string strCpCount = str::format("Compute pipelines: ", cpCount); + + const std::string strPcSize = str::format("Pipeline cache: ", pcSize >= mib + ? str::format(pcSize / mib, ".", ((10 * pcSize) / mib) % 10, " MB") + : str::format(pcSize / kib, " kB")); + + renderer.drawText(context, 16.0f, + { position.x, position.y }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + strGpCount); + + renderer.drawText(context, 16.0f, + { position.x, position.y + 20.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + strCpCount); + + renderer.drawText(context, 16.0f, + { position.x, position.y + 40.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + strPcSize); + + return { position.x, position.y + 64.0f }; + } + + + HudPos HudStats::printMemoryStats( + const Rc& context, + HudTextRenderer& renderer, + HudPos position) { + constexpr uint64_t mib = 1024 * 1024; + + const uint64_t memAllocated = m_prevCounters.getCtr(DxvkStatCounter::MemoryAllocated); + const uint64_t memUsed = m_prevCounters.getCtr(DxvkStatCounter::MemoryUsed); + + const std::string strMemAllocated = str::format("Memory allocated: ", memAllocated / mib, " MB"); + const std::string strMemUsed = str::format("Memory used: ", memUsed / mib, " MB"); + + renderer.drawText(context, 16.0f, + { position.x, position.y }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + strMemAllocated); + + renderer.drawText(context, 16.0f, + { position.x, position.y + 20.0f }, + { 1.0f, 1.0f, 1.0f, 1.0f }, + strMemUsed); + + return { position.x, position.y + 44.0f }; + } + + + HudElements HudStats::filterElements(HudElements elements) { + return elements & HudElements( + HudElement::StatDrawCalls, + HudElement::StatSubmissions, + HudElement::StatPipelines, + HudElement::StatMemory); + } + +} \ No newline at end of file diff --git a/src/dxvk/hud/dxvk_hud_stats.h b/src/dxvk/hud/dxvk_hud_stats.h new file mode 100644 index 000000000..1c9fe3fed --- /dev/null +++ b/src/dxvk/hud/dxvk_hud_stats.h @@ -0,0 +1,63 @@ +#pragma once + +#include "../dxvk_stats.h" + +#include "dxvk_hud_config.h" +#include "dxvk_hud_text.h" + +namespace dxvk::hud { + + /** + * \brief Statistics display for the HUD + * + * Displays some stat counters for the device + * if enabled. Certain groups of counters can + * be enabled inidividually. + */ + class HudStats { + + public: + + HudStats(HudElements elements); + ~HudStats(); + + void update( + const Rc& device); + + HudPos renderText( + const Rc& context, + HudTextRenderer& renderer, + HudPos position); + + private: + + const HudElements m_elements; + + DxvkStatCounters m_prevCounters; + DxvkStatCounters m_diffCounters; + + HudPos printDrawCallStats( + const Rc& context, + HudTextRenderer& renderer, + HudPos position); + + HudPos printSubmissionStats( + const Rc& context, + HudTextRenderer& renderer, + HudPos position); + + HudPos printPipelineStats( + const Rc& context, + HudTextRenderer& renderer, + HudPos position); + + HudPos printMemoryStats( + const Rc& context, + HudTextRenderer& renderer, + HudPos position); + + static HudElements filterElements(HudElements elements); + + }; + +} \ No newline at end of file diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index da1203bd4..5da6504ef 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -52,6 +52,7 @@ dxvk_src = files([ 'hud/dxvk_hud_devinfo.cpp', 'hud/dxvk_hud_font.cpp', 'hud/dxvk_hud_fps.cpp', + 'hud/dxvk_hud_stats.cpp', 'hud/dxvk_hud_text.cpp', 'vulkan/dxvk_vulkan_extensions.cpp', diff --git a/src/util/util_flags.h b/src/util/util_flags.h index 4dc07e46c..b73a0558a 100644 --- a/src/util/util_flags.h +++ b/src/util/util_flags.h @@ -68,6 +68,18 @@ namespace dxvk { return m_bits; } + Flags operator & (const Flags& other) const { + return Flags(m_bits & other.m_bits); + } + + Flags operator | (const Flags& other) const { + return Flags(m_bits | other.m_bits); + } + + Flags operator ^ (const Flags& other) const { + return Flags(m_bits ^ other.m_bits); + } + private: IntType m_bits = 0;