From 63b200f08d673307b79be04b52c665d5d6c5e223 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 30 Sep 2024 21:18:06 +0200 Subject: [PATCH] [dxvk] Reimplement HUD rendering to use Vulkan directly And change how rendering works in general so that we emit fewer draw calls. --- src/d3d11/d3d11_swapchain.cpp | 6 +- src/d3d9/d3d9_hud.cpp | 119 +- src/d3d9/d3d9_hud.h | 117 +- src/d3d9/d3d9_swapchain.cpp | 6 +- src/dxvk/hud/dxvk_hud.cpp | 105 +- src/dxvk/hud/dxvk_hud.h | 38 +- src/dxvk/hud/dxvk_hud_item.cpp | 1511 +++++++++++------ src/dxvk/hud/dxvk_hud_item.h | 317 +++- src/dxvk/hud/dxvk_hud_renderer.cpp | 850 ++++++---- src/dxvk/hud/dxvk_hud_renderer.h | 267 ++- .../shaders/hud_chunk_frag_background.frag | 17 +- .../hud/shaders/hud_chunk_frag_visualize.frag | 37 +- src/dxvk/hud/shaders/hud_chunk_vert.vert | 25 - .../shaders/hud_chunk_vert_background.vert | 47 + .../hud/shaders/hud_chunk_vert_visualize.vert | 58 + src/dxvk/hud/shaders/hud_frag_common.glsl | 79 +- src/dxvk/hud/shaders/hud_frame_time_eval.comp | 197 +++ src/dxvk/hud/shaders/hud_graph_frag.frag | 96 +- src/dxvk/hud/shaders/hud_graph_vert.vert | 28 +- src/dxvk/hud/shaders/hud_text_frag.frag | 13 +- src/dxvk/hud/shaders/hud_text_vert.vert | 39 +- src/dxvk/meson.build | 5 +- 22 files changed, 2580 insertions(+), 1397 deletions(-) delete mode 100644 src/dxvk/hud/shaders/hud_chunk_vert.vert create mode 100644 src/dxvk/hud/shaders/hud_chunk_vert_background.vert create mode 100644 src/dxvk/hud/shaders/hud_chunk_vert_visualize.vert create mode 100644 src/dxvk/hud/shaders/hud_frame_time_eval.comp diff --git a/src/d3d11/d3d11_swapchain.cpp b/src/d3d11/d3d11_swapchain.cpp index 639e166be..e23011574 100644 --- a/src/d3d11/d3d11_swapchain.cpp +++ b/src/d3d11/d3d11_swapchain.cpp @@ -402,8 +402,10 @@ namespace dxvk { m_imageViews.at(imageIndex), m_colorspace, VkRect2D(), m_swapImageView, m_colorspace, VkRect2D()); - // if (m_hud != nullptr) - // m_hud->render(m_context, info.format, info.imageExtent); + if (m_hud) { + m_hud->render(m_context->beginExternalRendering(), + m_imageViews.at(imageIndex), m_colorspace); + } m_blitter->endPresent(m_context->beginExternalRendering(), m_imageViews.at(imageIndex)); diff --git a/src/d3d9/d3d9_hud.cpp b/src/d3d9/d3d9_hud.cpp index d7acdc9fa..1412cd3ba 100644 --- a/src/d3d9/d3d9_hud.cpp +++ b/src/d3d9/d3d9_hud.cpp @@ -16,28 +16,23 @@ namespace dxvk::hud { HudPos HudSamplerCount::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; + renderer.drawText(16, position, 0xffc0ff00u, "Samplers:"); + renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_samplerCount); - renderer.drawText(16.0f, - { position.x, position.y }, - { 0.0f, 1.0f, 0.75f, 1.0f }, - "Samplers:"); - - renderer.drawText(16.0f, - { position.x + 120.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_samplerCount); - - position.y += 8.0f; + position.y += 8; return position; } HudTextureMemory::HudTextureMemory(D3D9DeviceEx* device) - : m_device (device) - , m_allocatedString ("") - , m_mappedString ("") {} + : m_device (device) + , m_allocatedString ("") + , m_mappedString ("") { } void HudTextureMemory::update(dxvk::high_resolution_clock::time_point time) { @@ -62,40 +57,26 @@ namespace dxvk::hud { HudPos HudTextureMemory::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; + renderer.drawText(16, position, 0xffc0ff00u, "Mappable:"); + renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_allocatedString); - renderer.drawText(16.0f, - { position.x, position.y }, - { 0.0f, 1.0f, 0.75f, 1.0f }, - "Mappable:"); - - renderer.drawText(16.0f, - { position.x + 120.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_allocatedString); - - position.y += 24.0f; - - renderer.drawText(16.0f, - { position.x, position.y }, - { 0.0f, 1.0f, 0.75f, 1.0f }, - "Mapped:"); - - renderer.drawText(16.0f, - { position.x + 120.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_mappedString); - - position.y += 8.0f; + position.y += 20; + renderer.drawText(16, position, 0xffc0ff00u, "Mapped:"); + renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_mappedString); + position.y += 8; return position; } HudFixedFunctionShaders::HudFixedFunctionShaders(D3D9DeviceEx* device) - : m_device (device) - , m_ffShaderCount ("") {} + : m_device (device) + , m_ffShaderCount ("") {} void HudFixedFunctionShaders::update(dxvk::high_resolution_clock::time_point time) { @@ -108,29 +89,26 @@ namespace dxvk::hud { HudPos HudFixedFunctionShaders::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; + renderer.drawText(16, position, 0xffc0ff00u, "FF Shaders:"); + renderer.drawText(16, { position.x + 155, position.y }, 0xffffffffu, m_ffShaderCount); - renderer.drawText(16.0f, - { position.x, position.y }, - { 0.0f, 1.0f, 0.75f, 1.0f }, - "FF Shaders:"); - - renderer.drawText(16.0f, - { position.x + 155.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_ffShaderCount); - - position.y += 8.0f; + position.y += 8; return position; } + HudSWVPState::HudSWVPState(D3D9DeviceEx* device) : m_device (device) , m_isSWVPText ("") {} + void HudSWVPState::update(dxvk::high_resolution_clock::time_point time) { if (m_device->IsSWVP()) { if (m_device->CanOnlySWVP()) { @@ -149,21 +127,16 @@ namespace dxvk::hud { HudPos HudSWVPState::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; + renderer.drawText(16, position, 0xffc0ff00u, "Vertex Processing:"); + renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, m_isSWVPText); - renderer.drawText(16.0f, - { position.x, position.y }, - { 0.0f, 1.0f, 0.75f, 1.0f }, - "Vertex Processing:"); - - renderer.drawText(16.0f, - { position.x + 240.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_isSWVPText); - - position.y += 8.0f; + position.y += 8; return position; } diff --git a/src/d3d9/d3d9_hud.h b/src/d3d9/d3d9_hud.h index 7c43ec0cd..4c84429e2 100644 --- a/src/d3d9/d3d9_hud.h +++ b/src/d3d9/d3d9_hud.h @@ -17,8 +17,11 @@ namespace dxvk::hud { void update(dxvk::high_resolution_clock::time_point time); HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); private: @@ -28,80 +31,92 @@ namespace dxvk::hud { }; - /** - * \brief HUD item to display unmappable memory - */ - class HudTextureMemory : public HudItem { - constexpr static int64_t UpdateInterval = 500'000; + /** + * \brief HUD item to display unmappable memory + */ + class HudTextureMemory : public HudItem { + constexpr static int64_t UpdateInterval = 500'000; + public: - public: + HudTextureMemory(D3D9DeviceEx* device); - HudTextureMemory(D3D9DeviceEx* device); + void update(dxvk::high_resolution_clock::time_point time); - void update(dxvk::high_resolution_clock::time_point time); + HudPos render( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); - HudPos render( - HudRenderer& renderer, - HudPos position); + private: - private: + D3D9DeviceEx* m_device; - D3D9DeviceEx* m_device; + uint32_t m_maxAllocated = 0; + uint32_t m_maxUsed = 0; + uint32_t m_maxMapped = 0; - uint32_t m_maxAllocated = 0; - uint32_t m_maxUsed = 0; - uint32_t m_maxMapped = 0; + dxvk::high_resolution_clock::time_point m_lastUpdate + = dxvk::high_resolution_clock::now(); - dxvk::high_resolution_clock::time_point m_lastUpdate - = dxvk::high_resolution_clock::now(); + std::string m_allocatedString; + std::string m_mappedString; - std::string m_allocatedString; - std::string m_mappedString; + }; - }; - /** - * \brief HUD item to display amount of generated fixed function shaders - */ - class HudFixedFunctionShaders : public HudItem { - public: + /** + * \brief HUD item to display amount of generated fixed function shaders + */ + class HudFixedFunctionShaders : public HudItem { - HudFixedFunctionShaders(D3D9DeviceEx* device); + public: - void update(dxvk::high_resolution_clock::time_point time); + HudFixedFunctionShaders(D3D9DeviceEx* device); - HudPos render( - HudRenderer& renderer, - HudPos position); + void update(dxvk::high_resolution_clock::time_point time); - private: + HudPos render( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); - D3D9DeviceEx* m_device; + private: - std::string m_ffShaderCount; + D3D9DeviceEx* m_device; - }; + std::string m_ffShaderCount; - /** - * \brief HUD item to whether or not we're in SWVP mode - */ - class HudSWVPState : public HudItem { - public: + }; - HudSWVPState(D3D9DeviceEx* device); - void update(dxvk::high_resolution_clock::time_point time); + /** + * \brief HUD item to whether or not we're in SWVP mode + */ + class HudSWVPState : public HudItem { - HudPos render( - HudRenderer& renderer, - HudPos position); + public: - private: + HudSWVPState(D3D9DeviceEx* device); - D3D9DeviceEx* m_device; + void update(dxvk::high_resolution_clock::time_point time); - std::string m_isSWVPText; + HudPos render( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); - }; + private: + + D3D9DeviceEx* m_device; + + std::string m_isSWVPText; + + }; } \ No newline at end of file diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index ed55d24d6..e39dcd702 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -851,8 +851,10 @@ namespace dxvk { m_wctx->imageViews.at(imageIndex), m_colorspace, dstRect, swapImageView, m_colorspace, srcRect); - // if (m_hud != nullptr) - // m_hud->render(m_context, info.format, info.imageExtent); + if (m_hud) { + m_hud->render(m_context->beginExternalRendering(), + m_wctx->imageViews.at(imageIndex), m_colorspace); + } m_blitter->endPresent(m_context->beginExternalRendering(), m_wctx->imageViews.at(imageIndex)); diff --git a/src/dxvk/hud/dxvk_hud.cpp b/src/dxvk/hud/dxvk_hud.cpp index 9b1d17688..16aab06aa 100644 --- a/src/dxvk/hud/dxvk_hud.cpp +++ b/src/dxvk/hud/dxvk_hud.cpp @@ -8,112 +8,51 @@ namespace dxvk::hud { const Rc& device) : m_device (device), m_renderer (device), - m_hudItems (device), - m_scale (m_hudItems.getOption("scale", 1.0f)), - m_opacity (m_hudItems.getOption("opacity", 1.0f)) { - // Sanitize scaling factor - if (m_scale < 0.01f) - m_scale = 1.0f; - - // Sanitize HUD opacity factor - if (m_opacity != 1.0f) - m_opacity = std::max(std::min(m_opacity, 1.0f), 0.1f); - - // Set up constant state - m_rsState.polygonMode = VK_POLYGON_MODE_FILL; - m_rsState.cullMode = VK_CULL_MODE_BACK_BIT; - m_rsState.frontFace = VK_FRONT_FACE_CLOCKWISE; - m_rsState.depthClipEnable = VK_FALSE; - m_rsState.depthBiasEnable = VK_FALSE; - m_rsState.conservativeMode = VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT; - m_rsState.sampleCount = VK_SAMPLE_COUNT_1_BIT; - m_rsState.flatShading = VK_FALSE; - m_rsState.lineMode = VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT; - - m_blendMode.enableBlending = VK_TRUE; - m_blendMode.colorSrcFactor = VK_BLEND_FACTOR_ONE; - m_blendMode.colorDstFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - m_blendMode.colorBlendOp = VK_BLEND_OP_ADD; - m_blendMode.alphaSrcFactor = VK_BLEND_FACTOR_ONE; - m_blendMode.alphaDstFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - m_blendMode.alphaBlendOp = VK_BLEND_OP_ADD; - m_blendMode.writeMask = VK_COLOR_COMPONENT_R_BIT - | VK_COLOR_COMPONENT_G_BIT - | VK_COLOR_COMPONENT_B_BIT - | VK_COLOR_COMPONENT_A_BIT; + m_hudItems (device) { + // Retrieve and sanitize options + m_options.scale = std::clamp(m_hudItems.getOption("scale", 1.0f), 0.25f, 4.0f); + m_options.opacity = std::clamp(m_hudItems.getOption("opacity", 1.0f), 0.1f, 1.0f); addItem("version", -1); addItem("devinfo", -1, m_device); addItem("fps", -1); - addItem("frametimes", -1); + addItem("frametimes", -1, device, &m_renderer); addItem("submissions", -1, device); addItem("drawcalls", -1, device); addItem("pipelines", -1, device); addItem("descriptors", -1, device); addItem("memory", -1, device); - addItem("allocations", -1, device); + addItem("allocations", -1, device, &m_renderer); addItem("cs", -1, device); addItem("gpuload", -1, device); addItem("compiler", -1, device); } - - + + Hud::~Hud() { } - - + + void Hud::update() { m_hudItems.update(); } - - + + void Hud::render( - const Rc& ctx, - VkSurfaceFormatKHR surfaceFormat, - VkExtent2D surfaceSize) { - this->setupRendererState(ctx, surfaceFormat, surfaceSize); - this->renderHudElements(ctx); + const DxvkContextObjects& ctx, + const Rc& dstView, + VkColorSpaceKHR dstColorSpace) { + auto key = m_renderer.getPipelineKey(dstView, dstColorSpace); + + m_renderer.beginFrame(ctx, dstView, dstColorSpace, m_options); + m_hudItems.render(ctx, key, m_options, m_renderer); + m_renderer.flushDraws(ctx, dstView, dstColorSpace, m_options); } - - + + Rc Hud::createHud(const Rc& device) { return new Hud(device); } - - - void Hud::setupRendererState( - const Rc& ctx, - VkSurfaceFormatKHR surfaceFormat, - VkExtent2D surfaceSize) { - VkColorSpaceKHR colorSpace = surfaceFormat.colorSpace; - - if (lookupFormatInfo(surfaceFormat.format)->flags.test(DxvkFormatFlag::ColorSpaceSrgb)) - colorSpace = VK_COLOR_SPACE_PASS_THROUGH_EXT; - - VkViewport viewport; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = float(surfaceSize.width); - viewport.height = float(surfaceSize.height); - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor; - scissor.offset = { 0, 0 }; - scissor.extent = surfaceSize; - - ctx->setViewports(1, &viewport, &scissor); - ctx->setRasterizerState(m_rsState); - ctx->setBlendMode(0, m_blendMode); - - ctx->setSpecConstant(VK_PIPELINE_BIND_POINT_GRAPHICS, 0, colorSpace); - m_renderer.beginFrame(ctx, surfaceSize, m_scale, m_opacity); - } - - - void Hud::renderHudElements(const Rc& ctx) { - m_hudItems.render(m_renderer); - } } diff --git a/src/dxvk/hud/dxvk_hud.h b/src/dxvk/hud/dxvk_hud.h index 7599dea8e..06668f2a1 100644 --- a/src/dxvk/hud/dxvk_hud.h +++ b/src/dxvk/hud/dxvk_hud.h @@ -6,15 +6,7 @@ #include "dxvk_hud_renderer.h" namespace dxvk::hud { - - /** - * \brief HUD uniform data - * Shader data for the HUD. - */ - struct HudUniformData { - VkExtent2D surfaceSize; - }; - + /** * \brief DXVK HUD * @@ -41,13 +33,14 @@ namespace dxvk::hud { * \brief Render HUD * * Renders the HUD to the given context. - * \param [in] ctx Device context - * \param [in] surfaceSize Image size, in pixels + * \param [in] ctx Context objects for rendering + * \param [in] dstView Swap chain image view + * \param [in] dstColorSpace Color space */ void render( - const Rc& ctx, - VkSurfaceFormatKHR surfaceFormat, - VkExtent2D surfaceSize); + const DxvkContextObjects& ctx, + const Rc& dstView, + VkColorSpaceKHR dstColorSpace); /** * \brief Adds a HUD item if enabled @@ -74,25 +67,12 @@ namespace dxvk::hud { private: - const Rc m_device; + Rc m_device; - DxvkRasterizerState m_rsState; - DxvkBlendMode m_blendMode; - - HudUniformData m_uniformData; HudRenderer m_renderer; HudItemSet m_hudItems; - float m_scale; - float m_opacity; - - void setupRendererState( - const Rc& ctx, - VkSurfaceFormatKHR surfaceFormat, - VkExtent2D surfaceSize); - - void renderHudElements( - const Rc& ctx); + HudOptions m_options; }; diff --git a/src/dxvk/hud/dxvk_hud_item.cpp b/src/dxvk/hud/dxvk_hud_item.cpp index d17001c4e..3e88d0bb6 100644 --- a/src/dxvk/hud/dxvk_hud_item.cpp +++ b/src/dxvk/hud/dxvk_hud_item.cpp @@ -2,7 +2,13 @@ #include #include -#include +#include +#include + +#include + +#include +#include #include #include @@ -70,11 +76,15 @@ namespace dxvk::hud { } - void HudItemSet::render(HudRenderer& renderer) { - HudPos position = { 8.0f, 8.0f }; + void HudItemSet::render( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer) { + HudPos position = { 8, 8 }; for (const auto& item : m_items) - position = item->render(renderer, position); + position = item->render(ctx, key, options, renderer, position); } @@ -88,16 +98,15 @@ namespace dxvk::hud { HudPos HudVersionItem::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; + renderer.drawText(16, position, 0xffffffffu, "DXVK " DXVK_VERSION); - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - "DXVK " DXVK_VERSION); - - position.y += 8.0f; + position.y += 8; return position; } @@ -114,16 +123,15 @@ namespace dxvk::hud { HudPos HudClientApiItem::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; + renderer.drawText(16, position, 0xffffffffu, m_api); - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_api); - - position.y += 8.0f; + position.y += 8; return position; } @@ -148,27 +156,21 @@ namespace dxvk::hud { HudPos HudDeviceInfoItem::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_deviceName); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; + renderer.drawText(16, position, 0xffffffffu, m_deviceName); - position.y += 24.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_driverName); + position.y += 24; + renderer.drawText(16, position, 0xffffffffu, m_driverName); - position.y += 20.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_driverVer); + position.y += 20; + renderer.drawText(16, position, 0xffffffffu, m_driverVer); - position.y += 8.0f; + position.y += 8; return position; } @@ -193,108 +195,536 @@ namespace dxvk::hud { HudPos HudFpsItem::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 0.25f, 0.25f, 1.0f }, - "FPS:"); + renderer.drawText(16, position, 0xff4040ffu, "FPS:"); + renderer.drawText(16, { position.x + 60, position.y }, + 0xffffffffu, m_frameRate); - renderer.drawText(16.0f, - { position.x + 60.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_frameRate); - - position.y += 8.0f; + position.y += 8; return position; } - HudFrameTimeItem::HudFrameTimeItem() { } - HudFrameTimeItem::~HudFrameTimeItem() { } + HudFrameTimeQueryPool::HudFrameTimeQueryPool( + const Rc& device) + : m_vkd(device->vkd()) { + VkQueryPoolCreateInfo info = { VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO }; + info.queryType = VK_QUERY_TYPE_TIMESTAMP; + info.queryCount = 1; + + VkResult vr = m_vkd->vkCreateQueryPool(m_vkd->device(), &info, nullptr, &m_pool); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create frame time query pool: ", vr)); + } - void HudFrameTimeItem::update(dxvk::high_resolution_clock::time_point time) { - auto elapsed = std::chrono::duration_cast(time - m_lastUpdate); + HudFrameTimeQueryPool::~HudFrameTimeQueryPool() { + m_vkd->vkDestroyQueryPool(m_vkd->device(), m_pool, nullptr); + } - m_dataPoints[m_dataPointId] = float(elapsed.count()); - m_dataPointId = (m_dataPointId + 1) % NumDataPoints; - m_lastUpdate = time; + HudFrameTimeItem::HudFrameTimeItem(const Rc& device, HudRenderer* renderer) + : m_device (device), + m_gfxSetLayout (createDescriptorSetLayout()), + m_gfxPipelineLayout (createPipelineLayout()) { + createComputePipeline(*renderer); + + renderer->createShaderModule(m_vs, VK_SHADER_STAGE_VERTEX_BIT, sizeof(hud_graph_vert), hud_graph_vert); + renderer->createShaderModule(m_fs, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(hud_graph_frag), hud_graph_frag); + } + + + HudFrameTimeItem::~HudFrameTimeItem() { + auto vk = m_device->vkd(); + + for (const auto& p : m_gfxPipelines) + vk->vkDestroyPipeline(vk->device(), p.second, nullptr); + + vk->vkDestroyShaderModule(vk->device(), m_vs.stageInfo.module, nullptr); + vk->vkDestroyShaderModule(vk->device(), m_fs.stageInfo.module, nullptr); + + vk->vkDestroyPipeline(vk->device(), m_computePipeline, nullptr); + vk->vkDestroyPipelineLayout(vk->device(), m_computePipelineLayout, nullptr); + vk->vkDestroyDescriptorSetLayout(vk->device(), m_computeSetLayout, nullptr); + + vk->vkDestroyPipelineLayout(vk->device(), m_gfxPipelineLayout, nullptr); + vk->vkDestroyDescriptorSetLayout(vk->device(), m_gfxSetLayout, nullptr); } HudPos HudFrameTimeItem::render( - HudRenderer& renderer, - HudPos position) { - std::array points; + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + if (!m_gpuBuffer) + createResources(ctx); - // 60 FPS = optimal, 10 FPS = worst - const float targetUs = 16'666.6f; - const float minUs = 5'000.0f; - const float maxUs = 100'000.0f; - - // Ten times the maximum/minimum number - // of milliseconds for a single frame - uint32_t minMs = 0xFFFFFFFFu; - uint32_t maxMs = 0x00000000u; - - // Paint the time points - for (uint32_t i = 0; i < NumDataPoints; i++) { - float us = m_dataPoints[(m_dataPointId + i) % NumDataPoints]; - - minMs = std::min(minMs, uint32_t(us / 100.0f)); - maxMs = std::max(maxMs, uint32_t(us / 100.0f)); - - float r = std::min(std::max(-1.0f + us / targetUs, 0.0f), 1.0f); - float g = std::min(std::max( 3.0f - us / targetUs, 0.0f), 1.0f); - float l = std::sqrt(r * r + g * g); - - HudNormColor color = { - uint8_t(255.0f * (r / l)), - uint8_t(255.0f * (g / l)), - uint8_t(0), uint8_t(255) }; - - float hVal = std::log2(std::max((us - minUs) / targetUs + 1.0f, 1.0f)) - / std::log2((maxUs - minUs) / targetUs); - - points[i].value = std::max(hVal, 1.0f / 40.0f); - points[i].color = color; - } - - renderer.drawGraph(position, - HudPos { float(NumDataPoints), 40.0f }, - points.size(), points.data()); - - position.y += 58.0f; + HudPos minPos = { 12, -128 }; + HudPos maxPos = { 162, -128 }; - renderer.drawText(12.0f, - { position.x, position.y }, - { 1.0f, 0.25f, 0.25f, 1.0f }, - "min:"); + HudPos graphPos = { 8, -120 }; + HudPos graphSize = { NumDataPoints, 80 }; + + uint32_t dataPoint = m_nextDataPoint++; + + processFrameTimes(ctx, key, renderer, + dataPoint, minPos, maxPos); + + drawFrameTimeGraph(ctx, key, renderer, + dataPoint, graphPos, graphSize); + + if (m_nextDataPoint >= NumDataPoints) + m_nextDataPoint = 0u; - renderer.drawText(12.0f, - { position.x + 45.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - str::format(minMs / 10, ".", minMs % 10)); - - renderer.drawText(12.0f, - { position.x + 150.0f, position.y }, - { 1.0f, 0.25f, 0.25f, 1.0f }, - "max:"); - - renderer.drawText(12.0f, - { position.x + 195.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - str::format(maxMs / 10, ".", maxMs % 10)); - - position.y += 4.0f; return position; } + void HudFrameTimeItem::processFrameTimes( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + HudRenderer& renderer, + uint32_t dataPoint, + HudPos minPos, + HudPos maxPos) { + // Write current time stamp to the buffer + DxvkBufferSliceHandle sliceHandle = m_gpuBuffer->getSliceHandle(); + + ctx.cmd->cmdResetQueryPool(DxvkCmdBuffer::InitBuffer, + m_queryPool->handle(), 0, 1); + + ctx.cmd->cmdWriteTimestamp(DxvkCmdBuffer::InitBuffer, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + m_queryPool->handle(), 0); + + ctx.cmd->cmdCopyQueryPoolResults(DxvkCmdBuffer::InitBuffer, + m_queryPool->handle(), 0, 1, sliceHandle.handle, + sliceHandle.offset + (dataPoint & 1u) * sizeof(uint64_t), sizeof(uint64_t), + VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); + + VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 }; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + + VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO }; + depInfo.memoryBarrierCount = 1u; + depInfo.pMemoryBarriers = &barrier; + + ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo); + + // Process contents of the buffer and write out text draws + VkDescriptorSet set = ctx.descriptorPool->alloc(m_computeSetLayout); + + auto bufferLayout = computeBufferLayout(); + + VkDescriptorBufferInfo frameTimeBuffer = m_gpuBuffer->getDescriptor( + 0, bufferLayout.timestampSize).buffer; + + VkDescriptorBufferInfo drawInfoBuffer = m_gpuBuffer->getDescriptor( + bufferLayout.drawInfoOffset, bufferLayout.drawInfoSize).buffer; + + VkDescriptorBufferInfo drawParamBuffer = m_gpuBuffer->getDescriptor( + bufferLayout.drawParamOffset, bufferLayout.drawParamSize).buffer; + + VkBufferView textBufferView = m_textView->handle(); + + std::array descriptorWrites = {{ + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, + set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &frameTimeBuffer }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, + set, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawParamBuffer }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, + set, 2, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawInfoBuffer }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, + set, 3, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, nullptr, nullptr, &textBufferView }, + }}; + + ctx.cmd->updateDescriptorSets( + descriptorWrites.size(), + descriptorWrites.data()); + + ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::InitBuffer, + VK_PIPELINE_BIND_POINT_COMPUTE, m_computePipeline); + + ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::InitBuffer, + VK_PIPELINE_BIND_POINT_COMPUTE, m_computePipelineLayout, + set, 0, nullptr); + + ComputePushConstants pushConstants = { }; + pushConstants.msPerTick = m_device->properties().core.properties.limits.timestampPeriod / 1000000.0f; + pushConstants.dataPoint = dataPoint; + pushConstants.textPosMinX = minPos.x + 48; + pushConstants.textPosMinY = minPos.y; + pushConstants.textPosMaxX = maxPos.x + 48; + pushConstants.textPosMaxY = maxPos.y; + + ctx.cmd->cmdPushConstants(DxvkCmdBuffer::InitBuffer, + m_computePipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, + 0, sizeof(pushConstants), &pushConstants); + + ctx.cmd->cmdDispatch(DxvkCmdBuffer::InitBuffer, 1, 1, 1); + + barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 }; + barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + barrier.dstAccessMask = m_gpuBuffer->info().access; + barrier.dstStageMask = m_gpuBuffer->info().stages; + + ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo); + + // Display the min/max numbers + renderer.drawText(12, minPos, 0xff4040ff, "min:"); + renderer.drawText(12, maxPos, 0xff4040ff, "max:"); + + renderer.drawTextIndirect(ctx, key, drawParamBuffer, + drawInfoBuffer, textBufferView, 2u); + + // Make sure GPU resources are being kept alive as necessary + ctx.cmd->trackResource(m_gpuBuffer->getAllocation()); + ctx.cmd->trackResource(m_queryPool); + } + + + void HudFrameTimeItem::drawFrameTimeGraph( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + HudRenderer& renderer, + uint32_t dataPoint, + HudPos graphPos, + HudPos graphSize) { + ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, getPipeline(renderer, key)); + + auto set = ctx.descriptorPool->alloc(m_gfxSetLayout); + + VkDescriptorBufferInfo bufferDescriptor = m_gpuBuffer->getDescriptor(0, + computeBufferLayout().timestampSize).buffer; + + VkWriteDescriptorSet descriptorWrite = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + descriptorWrite.dstSet = set; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = &bufferDescriptor; + + ctx.cmd->updateDescriptorSets(1, &descriptorWrite); + + ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, m_gfxPipelineLayout, + set, 0, nullptr); + + RenderPushConstants pushConstants = { }; + pushConstants.hud = renderer.getPushConstants(); + pushConstants.x = graphPos.x; + pushConstants.y = graphPos.y; + pushConstants.w = graphSize.x; + pushConstants.h = graphSize.y; + pushConstants.frameIndex = dataPoint; + + ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_gfxPipelineLayout, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + 0, sizeof(pushConstants), &pushConstants); + + ctx.cmd->cmdDraw(4, 1, 0, 0); + + ctx.cmd->trackResource(m_gpuBuffer->getAllocation()); + } + + + void HudFrameTimeItem::createResources( + const DxvkContextObjects& ctx) { + auto bufferLayout = computeBufferLayout(); + + DxvkBufferCreateInfo bufferInfo = { }; + bufferInfo.size = bufferLayout.totalSize; + bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_TRANSFER_SRC_BIT + | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT + | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT + | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; + bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT + | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT + | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT + | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT + | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + bufferInfo.access = VK_ACCESS_TRANSFER_READ_BIT + | VK_ACCESS_TRANSFER_WRITE_BIT + | VK_ACCESS_INDIRECT_COMMAND_READ_BIT + | VK_ACCESS_SHADER_READ_BIT + | VK_ACCESS_SHADER_WRITE_BIT; + + m_gpuBuffer = m_device->createBuffer(bufferInfo, VK_MEMORY_HEAP_DEVICE_LOCAL_BIT); + + DxvkBufferViewKey textViewInfo = { }; + textViewInfo.format = VK_FORMAT_R8_UINT; + textViewInfo.usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; + textViewInfo.offset = bufferLayout.textOffset; + textViewInfo.size = bufferLayout.textSize; + + m_textView = m_gpuBuffer->createView(textViewInfo); + + // Zero-init buffer so we don't display random garbage at the start + DxvkBufferSliceHandle bufferSlice = m_gpuBuffer->getSliceHandle(); + + ctx.cmd->cmdFillBuffer(DxvkCmdBuffer::InitBuffer, + bufferSlice.handle, bufferSlice.offset, bufferSlice.length, 0u); + + VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 }; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + barrier.dstAccessMask = m_gpuBuffer->info().access; + barrier.dstStageMask = m_gpuBuffer->info().stages; + + VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO }; + depInfo.memoryBarrierCount = 1u; + depInfo.pMemoryBarriers = &barrier; + + ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo); + ctx.cmd->trackResource(m_gpuBuffer->getAllocation()); + + // We'll use and initialize this later as necessary + m_queryPool = new HudFrameTimeQueryPool(m_device); + } + + + void HudFrameTimeItem::createComputePipeline( + HudRenderer& renderer) { + auto vk = m_device->vkd(); + + std::array bindings = {{ + { 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT }, + { 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT }, + { 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT }, + { 3, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT }, + }}; + + VkDescriptorSetLayoutCreateInfo setLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + setLayoutInfo.bindingCount = bindings.size(); + setLayoutInfo.pBindings = bindings.data(); + + VkResult vr = vk->vkCreateDescriptorSetLayout(vk->device(), + &setLayoutInfo, nullptr, &m_computeSetLayout); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create frame time compute set layout: ", vr)); + + VkPushConstantRange pushConstantRange = { }; + pushConstantRange.offset = 0u; + pushConstantRange.size = sizeof(ComputePushConstants); + pushConstantRange.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + pipelineLayoutInfo.setLayoutCount = 1u; + pipelineLayoutInfo.pSetLayouts = &m_computeSetLayout; + pipelineLayoutInfo.pushConstantRangeCount = 1; + pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange; + + vr = vk->vkCreatePipelineLayout(vk->device(), + &pipelineLayoutInfo, nullptr, &m_computePipelineLayout); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create frame time compute pipeline layout: ", vr)); + + HudShaderModule shader = { }; + renderer.createShaderModule(shader, VK_SHADER_STAGE_COMPUTE_BIT, + sizeof(hud_frame_time_eval), hud_frame_time_eval); + + VkComputePipelineCreateInfo info = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; + info.stage = shader.stageInfo; + info.layout = m_computePipelineLayout; + info.basePipelineIndex = -1; + + vr = vk->vkCreateComputePipelines(vk->device(), + VK_NULL_HANDLE, 1, &info, nullptr, &m_computePipeline); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create frame time compute pipeline: ", vr)); + + vk->vkDestroyShaderModule(vk->device(), shader.stageInfo.module, nullptr); + } + + + VkDescriptorSetLayout HudFrameTimeItem::createDescriptorSetLayout() { + auto vk = m_device->vkd(); + + std::array bindings = {{ + { 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT }, + }}; + + VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + info.bindingCount = bindings.size(); + info.pBindings = bindings.data(); + + VkDescriptorSetLayout layout = VK_NULL_HANDLE; + VkResult vr = vk->vkCreateDescriptorSetLayout( + vk->device(), &info, nullptr, &layout); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create frame time graphics pipeline descriptor set layout: ", vr)); + + return layout; + } + + + VkPipelineLayout HudFrameTimeItem::createPipelineLayout() { + auto vk = m_device->vkd(); + + VkPushConstantRange pushConstantRange = { }; + pushConstantRange.offset = 0u; + pushConstantRange.size = sizeof(RenderPushConstants); + pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + + VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + info.setLayoutCount = 1u; + info.pSetLayouts = &m_gfxSetLayout; + info.pushConstantRangeCount = 1; + info.pPushConstantRanges = &pushConstantRange; + + VkPipelineLayout layout = VK_NULL_HANDLE; + VkResult vr = vk->vkCreatePipelineLayout( + vk->device(), &info, nullptr, &layout); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create frame time graphics pipeline layout: ", vr)); + + return layout; + } + + + VkPipeline HudFrameTimeItem::getPipeline( + HudRenderer& renderer, + const HudPipelineKey& key) { + auto entry = m_gfxPipelines.find(key); + + if (entry != m_gfxPipelines.end()) + return entry->second; + + VkPipeline pipeline = createPipeline(renderer, key); + m_gfxPipelines.insert({ key, pipeline }); + return pipeline; + } + + + VkPipeline HudFrameTimeItem::createPipeline( + HudRenderer& renderer, + const HudPipelineKey& key) { + auto vk = m_device->vkd(); + + HudSpecConstants specConstants = renderer.getSpecConstants(key); + VkSpecializationInfo specInfo = renderer.getSpecInfo(&specConstants); + + std::array stages = { }; + stages[0] = m_vs.stageInfo; + stages[1] = m_fs.stageInfo; + stages[1].pSpecializationInfo = &specInfo; + + VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO }; + rtInfo.colorAttachmentCount = 1; + rtInfo.pColorAttachmentFormats = &key.format; + + VkPipelineVertexInputStateCreateInfo viState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; + + VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; + iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + + VkPipelineViewportStateCreateInfo vpState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; + + VkPipelineRasterizationStateCreateInfo rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; + rsState.cullMode = VK_CULL_MODE_NONE; + rsState.polygonMode = VK_POLYGON_MODE_FILL; + rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rsState.lineWidth = 1.0f; + + constexpr uint32_t sampleMask = 0x1; + + VkPipelineMultisampleStateCreateInfo msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; + msState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + msState.pSampleMask = &sampleMask; + + VkPipelineColorBlendAttachmentState cbAttachment = { }; + cbAttachment.blendEnable = VK_TRUE; + cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + cbAttachment.colorBlendOp = VK_BLEND_OP_ADD; + cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + cbAttachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineColorBlendStateCreateInfo cbOpaqueState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; + cbOpaqueState.attachmentCount = 1; + cbOpaqueState.pAttachments = &cbAttachment; + + static const std::array dynStates = { + VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT, + VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT, + }; + + VkPipelineDynamicStateCreateInfo dynState = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; + dynState.dynamicStateCount = dynStates.size(); + dynState.pDynamicStates = dynStates.data(); + + VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &rtInfo }; + info.stageCount = stages.size(); + info.pStages = stages.data(); + info.pVertexInputState = &viState; + info.pInputAssemblyState = &iaState; + info.pViewportState = &vpState; + info.pRasterizationState = &rsState; + info.pMultisampleState = &msState; + info.pColorBlendState = &cbOpaqueState; + info.pDynamicState = &dynState; + info.layout = m_gfxPipelineLayout; + info.basePipelineIndex = -1; + + VkPipeline pipeline = { }; + VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), + VK_NULL_HANDLE, 1, &info, nullptr, &pipeline); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create HUD memory detail pipeline 1: ", vr)); + + return pipeline; + } + + + HudFrameTimeItem::BufferLayout HudFrameTimeItem::computeBufferLayout() { + struct ComputeTimestampBuffer { + std::array timestamps; + std::array intervals; + float avgMs; + float minMs; + float maxMs; + }; + + BufferLayout result = { }; + result.timestampSize = align(sizeof(ComputeTimestampBuffer), 256u); + result.drawInfoOffset = result.timestampSize; + result.drawInfoSize = align(sizeof(HudTextDrawInfo) * NumTextDraws, 256u); + result.drawParamOffset = result.drawInfoOffset + result.drawInfoSize; + result.drawParamSize = align(sizeof(VkDrawIndirectCommand) * NumTextDraws, 256u); + result.textOffset = result.drawParamOffset + result.drawParamSize; + result.textSize = 256u; + result.totalSize = result.textOffset + result.textSize; + return result; + } + + + + HudSubmissionStatsItem::HudSubmissionStatsItem(const Rc& device) : m_device(device) { @@ -342,32 +772,20 @@ namespace dxvk::hud { HudPos HudSubmissionStatsItem::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; + renderer.drawText(16, position, 0xff4080ff, "Queue submissions:"); + renderer.drawText(16, { position.x + 228, position.y }, 0xffffffffu, m_submitString); - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 0.5f, 0.25f, 1.0f }, - "Queue submissions:"); + position.y += 20; + renderer.drawText(16, position, 0xff4080ff, "Queue syncs:"); + renderer.drawText(16, { position.x + 228, position.y }, 0xffffffffu, m_syncString); - renderer.drawText(16.0f, - { position.x + 228.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_submitString); - - position.y += 20.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 0.5f, 0.25f, 1.0f }, - "Queue syncs:"); - - renderer.drawText(16.0f, - { position.x + 228.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_syncString); - - position.y += 8.0f; + position.y += 8; return position; } @@ -403,53 +821,28 @@ namespace dxvk::hud { HudPos HudDrawCallStatsItem::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 0.25f, 0.5f, 1.0f, 1.0f }, - "Draw calls:"); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; + renderer.drawText(16, position, 0xffff8040, "Draw calls:"); + renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_gpCount)); - renderer.drawText(16.0f, - { position.x + 192.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - str::format(m_gpCount)); + position.y += 20; + renderer.drawText(16, position, 0xffff8040, "Dispatch calls:"); + renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_cpCount)); - position.y += 20.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 0.25f, 0.5f, 1.0f, 1.0f }, - "Dispatch calls:"); + position.y += 20; + renderer.drawText(16, position, 0xffff8040, "Render passes:"); + renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_rpCount)); - renderer.drawText(16.0f, - { position.x + 192.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - str::format(m_cpCount)); + position.y += 20; + renderer.drawText(16, position, 0xffff8040, "Barriers:"); + renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_pbCount)); - position.y += 20.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 0.25f, 0.5f, 1.0f, 1.0f }, - "Render passes:"); - - renderer.drawText(16.0f, - { position.x + 192.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - str::format(m_rpCount)); - - position.y += 20.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 0.25f, 0.5f, 1.0f, 1.0f }, - "Barriers:"); - - renderer.drawText(16.0f, - { position.x + 192.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - str::format(m_pbCount)); - - position.y += 8.0f; + position.y += 8; return position; } @@ -475,44 +868,26 @@ namespace dxvk::hud { HudPos HudPipelineStatsItem::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 0.25f, 1.0f, 1.0f }, - "Graphics pipelines:"); - - renderer.drawText(16.0f, - { position.x + 240.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - str::format(m_graphicsPipelines)); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; + renderer.drawText(16, position, 0xffff40ff, "Graphics pipelines:"); + renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_graphicsPipelines)); if (m_graphicsLibraries) { - position.y += 20.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 0.25f, 1.0f, 1.0f }, - "Graphics shaders:"); - - renderer.drawText(16.0f, - { position.x + 240.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - str::format(m_graphicsLibraries)); + position.y += 20; + renderer.drawText(16, position, 0xffff40ff, "Graphics shaders:"); + renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_graphicsLibraries)); } - position.y += 20.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 0.25f, 1.0f, 1.0f }, - "Compute pipelines:"); + position.y += 20; + renderer.drawText(16, position, 0xffff40ff, "Compute shaders:"); + renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_computePipelines)); - renderer.drawText(16.0f, - { position.x + 240.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - str::format(m_computePipelines)); - - position.y += 8.0f; + position.y += 8; return position; } @@ -537,31 +912,20 @@ namespace dxvk::hud { HudPos HudDescriptorStatsItem::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 0.25f, 0.5f, 1.0f }, - "Descriptor pools:"); - - renderer.drawText(16.0f, - { position.x + 216.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - str::format(m_descriptorPoolCount)); - - position.y += 20.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 0.25f, 0.5f, 1.0f }, - "Descriptor sets:"); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; + renderer.drawText(16, position, 0xff8040ff, "Descriptor pools:"); + renderer.drawText(16, { position.x + 216, position.y }, 0xffffffffu, str::format(m_descriptorPoolCount)); - renderer.drawText(16.0f, - { position.x + 216.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - str::format(m_descriptorSetCount)); + position.y += 20; + renderer.drawText(16, position, 0xff8040ff, "Descriptor sets:"); + renderer.drawText(16, { position.x + 216, position.y }, 0xffffffffu, str::format(m_descriptorSetCount)); - position.y += 8.0f; + position.y += 8; return position; } @@ -584,8 +948,11 @@ namespace dxvk::hud { HudPos HudMemoryStatsItem::render( - HudRenderer& renderer, - HudPos position) { + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { for (uint32_t i = 0; i < m_memory.memoryHeapCount; i++) { bool isDeviceLocal = m_memory.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT; @@ -599,57 +966,49 @@ namespace dxvk::hud { std::string text = str::format(std::setfill(' '), std::setw(5), memAllocatedMib, " MB (", percentage, "%) ", std::setw(5 + (percentage < 10 ? 1 : 0) + (percentage < 100 ? 1 : 0)), memUsedMib, " MB used"); - position.y += 16.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 1.0f, 1.0f, 0.25f, 1.0f }, - label); + position.y += 16; + renderer.drawText(16, position, 0xff40ffffu, label); + renderer.drawText(16, { position.x + 168, position.y }, 0xffffffffu, text); - renderer.drawText(16.0f, - { position.x + 168.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - text); - position.y += 4.0f; + position.y += 4; } - position.y += 4.0f; + position.y += 4; return position; } - HudMemoryDetailsItem::HudMemoryDetailsItem(const Rc& device) - : m_device(device) { - DxvkShaderCreateInfo shaderInfo; - shaderInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - shaderInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; - shaderInfo.pushConstSize = sizeof(ShaderArgs); - shaderInfo.outputMask = 0x1; - - m_vs = new DxvkShader(shaderInfo, SpirvCodeBuffer(hud_chunk_vert)); - - shaderInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - shaderInfo.outputMask = 0x1; - - m_fsBackground = new DxvkShader(shaderInfo, SpirvCodeBuffer(hud_chunk_frag_background)); - - DxvkBindingInfo pageMaskBinding = { - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM, - VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT }; - - shaderInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - shaderInfo.bindingCount = 1; - shaderInfo.bindings = &pageMaskBinding; - shaderInfo.inputMask = 0x1; - shaderInfo.outputMask = 0x1; - - m_fsVisualize = new DxvkShader(shaderInfo, SpirvCodeBuffer(hud_chunk_frag_visualize)); + HudMemoryDetailsItem::HudMemoryDetailsItem( + const Rc& device, + HudRenderer* renderer) + : m_device (device), + m_setLayout (createSetLayout()), + m_pipelineLayout (createPipelineLayout()) { + renderer->createShaderModule(m_fsBackground, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(hud_chunk_frag_background), hud_chunk_frag_background); + renderer->createShaderModule(m_vsBackground, VK_SHADER_STAGE_VERTEX_BIT, sizeof(hud_chunk_vert_background), hud_chunk_vert_background); + renderer->createShaderModule(m_fsVisualize, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(hud_chunk_frag_visualize), hud_chunk_frag_visualize); + renderer->createShaderModule(m_vsVisualize, VK_SHADER_STAGE_VERTEX_BIT, sizeof(hud_chunk_vert_visualize), hud_chunk_vert_visualize); } HudMemoryDetailsItem::~HudMemoryDetailsItem() { + auto vk = m_device->vkd(); + for (const auto& p : m_pipelines) { + vk->vkDestroyPipeline(vk->device(), p.second.background, nullptr); + vk->vkDestroyPipeline(vk->device(), p.second.visualize, nullptr); + } + + vk->vkDestroyShaderModule(vk->device(), m_vsBackground.stageInfo.module, nullptr); + vk->vkDestroyShaderModule(vk->device(), m_fsBackground.stageInfo.module, nullptr); + + vk->vkDestroyShaderModule(vk->device(), m_vsVisualize.stageInfo.module, nullptr); + vk->vkDestroyShaderModule(vk->device(), m_fsVisualize.stageInfo.module, nullptr); + + vk->vkDestroyPipelineLayout(vk->device(), m_pipelineLayout, nullptr); + vk->vkDestroyDescriptorSetLayout(vk->device(), m_setLayout, nullptr); } @@ -666,203 +1025,379 @@ namespace dxvk::hud { HudPos HudMemoryDetailsItem::render( - HudRenderer& renderer, - HudPos position) { - uploadChunkData(renderer); - - // Chunk memory per type, not including dedicated allocations - std::array chunkMemoryAllocated = { }; - std::array chunkMemoryUsed = { }; - - // Compute layout, align the entire element to the bottom right. - float maxWidth = 556.0f; - - HudPos pos = { - float(renderer.surfaceSize().width) / renderer.scale() - 8.0f - maxWidth, - float(renderer.surfaceSize().height) / renderer.scale() - 8.0f, - }; - - for (uint32_t i = 0; i < m_stats.memoryTypes.size(); i++) { - const auto& type = m_stats.memoryTypes.at(i); - - if (!type.allocated) - continue; - - // Reserve space for one line of text - pos.y -= 20.0f; - - float width = 0.0f; - - for (uint32_t j = 0; j < type.chunkCount; j++) { - const auto& chunk = m_stats.chunks.at(type.chunkIndex + j); - chunkMemoryAllocated.at(i) += chunk.capacity; - chunkMemoryUsed.at(i) += chunk.used; - - float pixels = float((chunk.pageCount + 15u) / 16u); - - if (width + pixels > maxWidth) { - pos.y -= 30.0f; - width = 0.0f; - } - - width += pixels + 6.0f; - } - - pos.y -= 30.0f + 4.0f; - } - - if (m_displayCacheStats) - pos.y -= 20.0f; - - // Actually render the thing - for (uint32_t i = 0; i < m_stats.memoryTypes.size(); i++) { - const auto& type = m_stats.memoryTypes.at(i); - - if (!type.allocated) - continue; - - VkDeviceSize dedicated = type.allocated - chunkMemoryAllocated.at(i); - VkDeviceSize allocated = chunkMemoryAllocated.at(i) + dedicated; - VkDeviceSize used = chunkMemoryUsed.at(i) + dedicated; - - std::string headline = str::format("Mem type ", i, " [", type.properties.heapIndex, "]: ", - type.chunkCount, " chunk", type.chunkCount != 1u ? "s" : "", " (", (allocated >> 20u), " MB, ", - ((used >= (1u << 20u)) ? used >> 20 : used >> 10), - (used >= (1u << 20u) ? " MB" : " kB"), " used)"); - - renderer.drawText(14.0f, - { pos.x, pos.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - headline); - - pos.y += 8.0f; - - float width = 0.0f; - - for (uint32_t j = 0; j < type.chunkCount; j++) { - const auto& chunk = m_stats.chunks.at(type.chunkIndex + j); - float pixels = float((chunk.pageCount + 15u) / 16u); - - if (width + pixels > maxWidth) { - pos.y += 30.0f; - width = 0.0f; - } - - drawChunk(renderer, - { pos.x + width, pos.y }, - { pixels, 24.0f }, - type.properties, chunk); - - width += pixels + 6.0f; - } - - pos.y += 46.0f; - } + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + // Layout, align the entire element to the bottom right. + int32_t x = -564; + int32_t y = -20; if (m_displayCacheStats) { uint32_t hitCount = m_cacheStats.requestCount - m_cacheStats.missCount; uint32_t hitRate = (100 * hitCount) / std::max(m_cacheStats.requestCount, 1u); std::string cacheStr = str::format("Cache: ", m_cacheStats.size >> 10, " kB (", hitRate, "% hit)"); + renderer.drawText(14, { x, y }, 0xffffffffu, cacheStr); - renderer.drawText(14.0f, - { pos.x, pos.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - cacheStr); + y -= 24; } + for (uint32_t i = m_stats.memoryTypes.size(); i; i--) { + const auto& type = m_stats.memoryTypes.at(i - 1); + + if (!type.allocated) + continue; + + // Compute layout and gather memory stats + DxvkMemoryStats stats = { }; + + int32_t w = 0; + int32_t h = 0; + + for (uint32_t j = 0; j < type.chunkCount; j++) { + const auto& chunk = m_stats.chunks.at(type.chunkIndex + j); + stats.memoryAllocated += chunk.capacity; + stats.memoryUsed += chunk.used; + + int32_t chunkWidth = (chunk.pageCount + 15u) / 16u + 2; + + if (x + w + chunkWidth > -8) { + w = 0; + h += 34; + } + + w += chunkWidth + 6; + } + + if (w) + h += 34; + + y -= h; + + w = 0; + h = 8; + + // Draw individual chunks + for (uint32_t j = 0; j < type.chunkCount; j++) { + const auto& chunk = m_stats.chunks.at(type.chunkIndex + j); + + // Default VRAM, blue + uint32_t color = 0xff804020u; + + if (type.properties.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { + // Cached memory, green + color = 0xff208020u; + } else if (!(type.properties.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { + if (!chunk.mapped) { + // Fallback allocation, grey + color = 0xff202020u; + } else { + // Uncached memory, red + color = 0xff202080u; + } + } else if (chunk.mapped) { + // Host-visible VRAM, yellow + color = 0xff208080u; + } + + int32_t chunkWidth = (chunk.pageCount + 15u) / 16u + 2; + + if (x + w + chunkWidth > -8) { + w = 0; + h += 34; + } + + drawChunk({ x + w, y + h }, { chunkWidth, 24 }, color, chunk); + + w += chunkWidth + 6; + } + + // Render descriptive text + std::string headline = str::format("Mem type ", i, " [", type.properties.heapIndex, "]: ", + type.chunkCount, " chunk", type.chunkCount != 1u ? "s" : "", " (", (stats.memoryAllocated >> 20u), " MB, ", + ((stats.memoryUsed >= (1u << 20u)) ? stats.memoryUsed >> 20 : stats.memoryUsed >> 10), + (stats.memoryUsed >= (1u << 20u) ? " MB" : " kB"), " used)"); + + renderer.drawText(14, { x, y }, 0xffffffffu, headline); + + y -= 24; + } + + flushDraws(ctx, key, options, renderer); return position; } - void HudMemoryDetailsItem::uploadChunkData(HudRenderer& renderer) { - DxvkContext* context = renderer.getContext(); - - VkDeviceSize size = sizeof(uint32_t) * m_stats.pageMasks.size(); - - if (m_pageMaskBuffer == nullptr || m_pageMaskBuffer->info().size < size) { - DxvkBufferCreateInfo info = { }; - info.size = std::max(VkDeviceSize(1u << 14), - (VkDeviceSize(-1) >> bit::lzcnt(size - 1u)) + 1u); - info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - info.access = VK_ACCESS_SHADER_READ_BIT; - info.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - - m_pageMaskBuffer = m_device->createBuffer(info, - VK_MEMORY_HEAP_DEVICE_LOCAL_BIT | - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - - DxvkBufferViewKey viewInfo = { }; - viewInfo.format = VK_FORMAT_UNDEFINED; - viewInfo.offset = 0; - viewInfo.size = info.size; - viewInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - - m_pageMaskView = m_pageMaskBuffer->createView(viewInfo); - } - - if (!m_stats.pageMasks.empty()) { - context->invalidateBuffer(m_pageMaskBuffer, m_pageMaskBuffer->allocateSlice()); - std::memcpy(m_pageMaskBuffer->mapPtr(0), &m_stats.pageMasks.at(0), size); - } + void HudMemoryDetailsItem::drawChunk( + HudPos pos, + HudPos size, + uint32_t color, + const DxvkMemoryChunkStats& chunk) { + auto& draw = m_drawInfos.emplace_back(); + draw.x = pos.x; + draw.y = pos.y; + draw.w = size.x; + draw.h = size.y; + draw.pageMask = chunk.pageMaskOffset; + draw.pageCount = chunk.pageCount; + draw.color = color; } - void HudMemoryDetailsItem::drawChunk( - HudRenderer& renderer, - HudPos pos, - HudPos size, - const VkMemoryType& memoryType, - const DxvkMemoryChunkStats& stats) const { - DxvkContext* context = renderer.getContext(); - VkExtent2D surfaceSize = renderer.surfaceSize(); + void HudMemoryDetailsItem::flushDraws( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer) { + if (m_drawInfos.empty()) + return; - static const DxvkInputAssemblyState iaState = { - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, - VK_FALSE, 0 }; + PipelinePair pipelines = getPipeline(renderer, key); - context->setInputAssemblyState(iaState); - context->bindResourceBufferView(VK_SHADER_STAGE_FRAGMENT_BIT, 0, Rc(m_pageMaskView)); + ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.background); - context->bindShader(Rc(m_vs)); - context->bindShader(Rc(m_fsBackground)); + // Bind resources + VkDescriptorSet set = ctx.descriptorPool->alloc(m_setLayout); - ShaderArgs args = { }; - args.pos.x = pos.x - 1.0f; - args.pos.y = pos.y - 1.0f; - args.size.x = size.x + 2.0f; - args.size.y = size.y + 2.0f; - args.scale.x = renderer.scale() / std::max(float(surfaceSize.width), 1.0f); - args.scale.y = renderer.scale() / std::max(float(surfaceSize.height), 1.0f); - args.opacity = renderer.opacity(); - args.color = 0xc0000000u; - args.maskIndex = stats.pageMaskOffset; - args.pageCount = stats.pageCount; + VkDescriptorBufferInfo drawDescriptor = { }; + VkDescriptorBufferInfo dataDescriptor = { }; - context->pushConstants(0, sizeof(args), &args); - context->draw(4, 1, 0, 0); + updateDataBuffer(drawDescriptor, dataDescriptor); - context->bindShader(Rc(m_fsVisualize)); + std::array descriptorWrites = {{ + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, + set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawDescriptor }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, + set, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &dataDescriptor }, + }}; - args.pos = pos; - args.size = size; + ctx.cmd->updateDescriptorSets( + descriptorWrites.size(), + descriptorWrites.data()); - if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { - args.color = 0xff208020u; - } else if (!(memoryType.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { - if (!stats.mapped) - args.color = 0xff202020u; - else - args.color = 0xff202080u; - } else if (stats.mapped) { - args.color = 0xff208080u; + ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, + set, 0, nullptr); + + HudPushConstants pushConstants = renderer.getPushConstants(); + + ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_pipelineLayout, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + 0, sizeof(pushConstants), &pushConstants); + + ctx.cmd->cmdDraw(4, m_drawInfos.size(), 0, 0); + + ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.visualize); + ctx.cmd->cmdDraw(4, m_drawInfos.size(), 0, 0); + + // Track data buffer lifetime + ctx.cmd->trackResource(m_dataBuffer->getAllocation()); + + m_drawInfos.clear(); + } + + + void HudMemoryDetailsItem::updateDataBuffer( + VkDescriptorBufferInfo& drawDescriptor, + VkDescriptorBufferInfo& dataDescriptor) { + size_t drawInfoSize = m_drawInfos.size() * sizeof(DrawInfo); + size_t drawInfoSizeAligned = align(drawInfoSize, 256u); + + size_t chunkDataSize = m_stats.pageMasks.size() * sizeof(uint32_t); + size_t chunkDataSizeAligned = align(chunkDataSize, 256u); + + size_t bufferSize = align(drawInfoSizeAligned + chunkDataSizeAligned, 2048u); + + if (!m_dataBuffer || m_dataBuffer->info().size < bufferSize) { + DxvkBufferCreateInfo bufferInfo; + bufferInfo.size = bufferSize; + bufferInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + bufferInfo.access = VK_ACCESS_SHADER_READ_BIT; + bufferInfo.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + + m_dataBuffer = m_device->createBuffer(bufferInfo, + VK_MEMORY_HEAP_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); } else { - args.color = 0xff804020u; + // Ensure we can update the buffer without overriding live data + m_dataBuffer->assignSlice(m_dataBuffer->allocateSlice()); } - context->pushConstants(0, sizeof(args), &args); - context->draw(4, 1, 0, 0); + // Update draw infos and pad unused area with zeroes + std::memcpy(m_dataBuffer->mapPtr(0), m_drawInfos.data(), drawInfoSize); + std::memset(m_dataBuffer->mapPtr(drawInfoSize), 0, drawInfoSizeAligned - drawInfoSize); + + // Update chunk data and pad with zeroes + std::memcpy(m_dataBuffer->mapPtr(drawInfoSizeAligned), m_stats.pageMasks.data(), chunkDataSize); + std::memset(m_dataBuffer->mapPtr(drawInfoSizeAligned + chunkDataSize), 0, chunkDataSizeAligned - chunkDataSize); + + // Write back descriptors + drawDescriptor = m_dataBuffer->getDescriptor(0, drawInfoSizeAligned).buffer; + dataDescriptor = m_dataBuffer->getDescriptor(drawInfoSizeAligned, chunkDataSizeAligned).buffer; + } + + + VkDescriptorSetLayout HudMemoryDetailsItem::createSetLayout() { + auto vk = m_device->vkd(); + + static const std::array bindings = {{ + { 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT }, + { 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT }, + }}; + + VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + info.bindingCount = bindings.size(); + info.pBindings = bindings.data(); + + VkDescriptorSetLayout layout = VK_NULL_HANDLE; + VkResult vr = vk->vkCreateDescriptorSetLayout(vk->device(), &info, nullptr, &layout); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create HUD descriptor set layout: ", vr)); + + return layout; + } + + + VkPipelineLayout HudMemoryDetailsItem::createPipelineLayout() { + auto vk = m_device->vkd(); + + VkPushConstantRange pushConstantRange = { }; + pushConstantRange.offset = 0u; + pushConstantRange.size = sizeof(HudPushConstants); + pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + + VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + info.setLayoutCount = 1; + info.pSetLayouts = &m_setLayout; + info.pushConstantRangeCount = 1; + info.pPushConstantRanges = &pushConstantRange; + + VkPipelineLayout layout = VK_NULL_HANDLE; + VkResult vr = vk->vkCreatePipelineLayout(vk->device(), &info, nullptr, &layout); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create HUD descriptor set layout: ", vr)); + + return layout; + } + + + HudMemoryDetailsItem::PipelinePair HudMemoryDetailsItem::createPipeline( + HudRenderer& renderer, + const HudPipelineKey& key) { + auto vk = m_device->vkd(); + + HudSpecConstants specConstants = renderer.getSpecConstants(key); + VkSpecializationInfo specInfo = renderer.getSpecInfo(&specConstants); + + std::array backgroundStages = { }; + backgroundStages[0] = m_vsBackground.stageInfo; + backgroundStages[1] = m_fsBackground.stageInfo; + backgroundStages[1].pSpecializationInfo = &specInfo; + + std::array visualizeStages = { }; + visualizeStages[0] = m_vsVisualize.stageInfo; + visualizeStages[1] = m_fsVisualize.stageInfo; + visualizeStages[1].pSpecializationInfo = &specInfo; + + VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO }; + rtInfo.colorAttachmentCount = 1; + rtInfo.pColorAttachmentFormats = &key.format; + + VkPipelineVertexInputStateCreateInfo viState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; + + VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; + iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + + VkPipelineViewportStateCreateInfo vpState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; + + VkPipelineRasterizationStateCreateInfo rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; + rsState.cullMode = VK_CULL_MODE_NONE; + rsState.polygonMode = VK_POLYGON_MODE_FILL; + rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rsState.lineWidth = 1.0f; + + constexpr uint32_t sampleMask = 0x1; + + VkPipelineMultisampleStateCreateInfo msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; + msState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + msState.pSampleMask = &sampleMask; + + VkPipelineColorBlendAttachmentState cbAttachment = { }; + cbAttachment.blendEnable = VK_TRUE; + cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + cbAttachment.colorBlendOp = VK_BLEND_OP_ADD; + cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + cbAttachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineColorBlendStateCreateInfo cbOpaqueState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; + cbOpaqueState.attachmentCount = 1; + cbOpaqueState.pAttachments = &cbAttachment; + + static const std::array dynStates = { + VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT, + VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT, + }; + + VkPipelineDynamicStateCreateInfo dynState = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; + dynState.dynamicStateCount = dynStates.size(); + dynState.pDynamicStates = dynStates.data(); + + VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &rtInfo }; + info.stageCount = backgroundStages.size(); + info.pStages = backgroundStages.data(); + info.pVertexInputState = &viState; + info.pInputAssemblyState = &iaState; + info.pViewportState = &vpState; + info.pRasterizationState = &rsState; + info.pMultisampleState = &msState; + info.pColorBlendState = &cbOpaqueState; + info.pDynamicState = &dynState; + info.layout = m_pipelineLayout; + info.basePipelineIndex = -1; + + PipelinePair pipelines = { }; + VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, + 1, &info, nullptr, &pipelines.background); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create HUD memory detail pipeline 1: ", vr)); + + info.stageCount = visualizeStages.size(); + info.pStages = visualizeStages.data(); + + vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, + 1, &info, nullptr, &pipelines.visualize); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create HUD memory detail pipeline 2: ", vr)); + + return pipelines; + } + + + HudMemoryDetailsItem::PipelinePair HudMemoryDetailsItem::getPipeline( + HudRenderer& renderer, + const HudPipelineKey& key) { + auto entry = m_pipelines.find(key); + + if (entry != m_pipelines.end()) + return entry->second; + + PipelinePair pipeline = createPipeline(renderer, key); + m_pipelines.insert({ key, pipeline }); + return pipeline; } @@ -918,31 +1453,20 @@ namespace dxvk::hud { HudPos HudCsThreadItem::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 0.25f, 1.0f, 0.25f, 1.0f }, - "CS chunks:"); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; + renderer.drawText(16, position, 0xff40ff40, "CS chunks:"); + renderer.drawText(16, { position.x + 132, position.y }, 0xffffffffu, m_csChunkString); - renderer.drawText(16.0f, - { position.x + 132.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_csChunkString); + position.y += 20; + renderer.drawText(16, position, 0xff40ff40, "CS syncs:"); + renderer.drawText(16, { position.x + 132, position.y }, 0xffffffffu, m_csSyncString); - position.y += 20.0f; - renderer.drawText(16.0f, - { position.x, position.y }, - { 0.25f, 1.0f, 0.25f, 1.0f }, - "CS syncs:"); - - renderer.drawText(16.0f, - { position.x + 132.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_csSyncString); - - position.y += 8.0f; + position.y += 8; return position; } @@ -979,21 +1503,16 @@ namespace dxvk::hud { HudPos HudGpuLoadItem::render( - HudRenderer& renderer, - HudPos position) { - position.y += 16.0f; + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { + position.y += 16; + renderer.drawText(16, position, 0xff408040u, "GPU:"); + renderer.drawText(16, { position.x + 60, position.y }, 0xffffffffu, m_gpuLoadString); - renderer.drawText(16.0f, - { position.x, position.y }, - { 0.25f, 0.5f, 0.25f, 1.0f }, - "GPU:"); - - renderer.drawText(16.0f, - { position.x + 60.0f, position.y }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - m_gpuLoadString); - - position.y += 8.0f; + position.y += 8; return position; } @@ -1047,18 +1566,18 @@ namespace dxvk::hud { HudPos HudCompilerActivityItem::render( - HudRenderer& renderer, - HudPos position) { + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) { if (m_show) { std::string string = "Compiling shaders..."; if (m_showPercentage) string = str::format(string, " (", computePercentage(), "%)"); - renderer.drawText(16.0f, - { position.x, float(renderer.surfaceSize().height) / renderer.scale() - 20.0f }, - { 1.0f, 1.0f, 1.0f, 1.0f }, - string); + renderer.drawText(16, { position.x, -20 }, 0xffffffffu, string); } return position; diff --git a/src/dxvk/hud/dxvk_hud_item.h b/src/dxvk/hud/dxvk_hud_item.h index 9516629b8..44a4c0df9 100644 --- a/src/dxvk/hud/dxvk_hud_item.h +++ b/src/dxvk/hud/dxvk_hud_item.h @@ -33,13 +33,18 @@ namespace dxvk::hud { /** * \brief Renders the HUD * - * \param [in] renderer HUD renderer + * \param [in] ctx Raw context objects + * \param [in] options HUD options + * \param [in] renderer HUD renderer for text rendering * \param [in] position Base offset * \returns Base offset for next item */ virtual HudPos render( - HudRenderer& renderer, - HudPos position) = 0; + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position) = 0; }; @@ -70,7 +75,10 @@ namespace dxvk::hud { * \returns Base offset for next item */ void render( - HudRenderer& renderer); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer); /** * \brief Creates a HUD item if enabled @@ -129,8 +137,11 @@ namespace dxvk::hud { public: HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); }; @@ -147,8 +158,11 @@ namespace dxvk::hud { ~HudClientApiItem(); HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); private: @@ -169,8 +183,11 @@ namespace dxvk::hud { ~HudDeviceInfoItem(); HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); private: @@ -195,8 +212,11 @@ namespace dxvk::hud { void update(dxvk::high_resolution_clock::time_point time); HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); private: @@ -210,29 +230,136 @@ namespace dxvk::hud { /** - * \brief HUD item to display the frame rate + * \brief Trackable GPU query pool */ - class HudFrameTimeItem : public HudItem { - constexpr static size_t NumDataPoints = 304; + class HudFrameTimeQueryPool : public DxvkResource { + public: - HudFrameTimeItem(); + HudFrameTimeQueryPool( + const Rc& device); - ~HudFrameTimeItem(); + ~HudFrameTimeQueryPool(); - void update(dxvk::high_resolution_clock::time_point time); - - HudPos render( - HudRenderer& renderer, - HudPos position); + VkQueryPool handle() const { + return m_pool; + } private: - dxvk::high_resolution_clock::time_point m_lastUpdate - = dxvk::high_resolution_clock::now(); + Rc m_vkd; + VkQueryPool m_pool = VK_NULL_HANDLE; - std::array m_dataPoints = {}; - uint32_t m_dataPointId = 0; + }; + + + /** + * \brief HUD item to display the frame rate + */ + class HudFrameTimeItem : public HudItem { + constexpr static size_t NumDataPoints = 420u; + constexpr static size_t NumTextDraws = 2u; + public: + + HudFrameTimeItem( + const Rc& device, + HudRenderer* renderer); + + ~HudFrameTimeItem(); + + HudPos render( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); + + private: + + struct ComputePushConstants { + float msPerTick; + uint32_t dataPoint; + int16_t textPosMinX; + int16_t textPosMinY; + int16_t textPosMaxX; + int16_t textPosMaxY; + }; + + struct RenderPushConstants { + HudPushConstants hud; + int16_t x; + int16_t y; + int16_t w; + int16_t h; + uint32_t frameIndex; + }; + + struct BufferLayout { + size_t timestampSize; + size_t drawInfoOffset; + size_t drawInfoSize; + size_t drawParamOffset; + size_t drawParamSize; + size_t textOffset; + size_t textSize; + size_t totalSize; + }; + + Rc m_device; + Rc m_gpuBuffer; + Rc m_textView; + Rc m_queryPool; + + VkDescriptorSetLayout m_computeSetLayout = VK_NULL_HANDLE; + VkPipelineLayout m_computePipelineLayout = VK_NULL_HANDLE; + VkPipeline m_computePipeline = VK_NULL_HANDLE; + + HudShaderModule m_vs; + HudShaderModule m_fs; + + VkDescriptorSetLayout m_gfxSetLayout = VK_NULL_HANDLE; + VkPipelineLayout m_gfxPipelineLayout = VK_NULL_HANDLE; + + std::unordered_map m_gfxPipelines; + + uint32_t m_nextDataPoint = 0u; + + void processFrameTimes( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + HudRenderer& renderer, + uint32_t dataPoint, + HudPos minPos, + HudPos maxPos); + + void drawFrameTimeGraph( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + HudRenderer& renderer, + uint32_t dataPoint, + HudPos graphPos, + HudPos graphSize); + + void createResources( + const DxvkContextObjects& ctx); + + void createComputePipeline( + HudRenderer& renderer); + + VkDescriptorSetLayout createDescriptorSetLayout(); + + VkPipelineLayout createPipelineLayout(); + + VkPipeline getPipeline( + HudRenderer& renderer, + const HudPipelineKey& key); + + VkPipeline createPipeline( + HudRenderer& renderer, + const HudPipelineKey& key); + + static BufferLayout computeBufferLayout(); }; @@ -251,8 +378,11 @@ namespace dxvk::hud { void update(dxvk::high_resolution_clock::time_point time); HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); private: @@ -289,8 +419,11 @@ namespace dxvk::hud { void update(dxvk::high_resolution_clock::time_point time); HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); private: @@ -323,8 +456,11 @@ namespace dxvk::hud { void update(dxvk::high_resolution_clock::time_point time); HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); private: @@ -351,8 +487,11 @@ namespace dxvk::hud { void update(dxvk::high_resolution_clock::time_point time); HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); private: @@ -378,8 +517,11 @@ namespace dxvk::hud { void update(dxvk::high_resolution_clock::time_point time); HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); private: @@ -397,26 +539,36 @@ namespace dxvk::hud { constexpr static int64_t UpdateInterval = 500'000; public: - HudMemoryDetailsItem(const Rc& device); + HudMemoryDetailsItem( + const Rc& device, + HudRenderer* renderer); ~HudMemoryDetailsItem(); void update(dxvk::high_resolution_clock::time_point time); HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); private: - struct ShaderArgs { - HudPos pos; - HudPos size; - HudPos scale; - float opacity; + struct DrawInfo { + int16_t x; + int16_t y; + int16_t w; + int16_t h; + uint16_t pageMask; + uint16_t pageCount; uint32_t color; - uint32_t maskIndex; - uint32_t pageCount; + }; + + struct PipelinePair { + VkPipeline background = VK_NULL_HANDLE; + VkPipeline visualize = VK_NULL_HANDLE; }; Rc m_device; @@ -425,24 +577,50 @@ namespace dxvk::hud { high_resolution_clock::time_point m_lastUpdate = { }; - bool m_displayCacheStats = false; + bool m_displayCacheStats = false; - Rc m_vs; - Rc m_fsBackground; - Rc m_fsVisualize; + Rc m_dataBuffer; + std::vector m_drawInfos; - Rc m_pageMaskBuffer; - Rc m_pageMaskView; + HudShaderModule m_vsBackground; + HudShaderModule m_fsBackground; - void uploadChunkData( - HudRenderer& renderer); + HudShaderModule m_vsVisualize; + HudShaderModule m_fsVisualize; + + VkDescriptorSetLayout m_setLayout = VK_NULL_HANDLE; + VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; + + std::unordered_map m_pipelines; void drawChunk( - HudRenderer& renderer, - HudPos pos, - HudPos size, - const VkMemoryType& memoryType, - const DxvkMemoryChunkStats& stats) const; + HudPos pos, + HudPos size, + uint32_t color, + const DxvkMemoryChunkStats& chunk); + + void flushDraws( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer); + + void updateDataBuffer( + VkDescriptorBufferInfo& drawDescriptor, + VkDescriptorBufferInfo& dataDescriptor); + + VkDescriptorSetLayout createSetLayout(); + + VkPipelineLayout createPipelineLayout(); + + PipelinePair createPipeline( + HudRenderer& renderer, + const HudPipelineKey& key); + + PipelinePair getPipeline( + HudRenderer& renderer, + const HudPipelineKey& key); }; @@ -461,8 +639,11 @@ namespace dxvk::hud { void update(dxvk::high_resolution_clock::time_point time); HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); private: @@ -500,8 +681,11 @@ namespace dxvk::hud { void update(dxvk::high_resolution_clock::time_point time); HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); private: @@ -532,8 +716,11 @@ namespace dxvk::hud { void update(dxvk::high_resolution_clock::time_point time); HudPos render( - HudRenderer& renderer, - HudPos position); + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const HudOptions& options, + HudRenderer& renderer, + HudPos position); private: diff --git a/src/dxvk/hud/dxvk_hud_renderer.cpp b/src/dxvk/hud/dxvk_hud_renderer.cpp index f34e97d9c..d63c84ca6 100644 --- a/src/dxvk/hud/dxvk_hud_renderer.cpp +++ b/src/dxvk/hud/dxvk_hud_renderer.cpp @@ -1,364 +1,634 @@ #include "dxvk_hud_renderer.h" -#include -#include - #include #include namespace dxvk::hud { - HudRenderer::HudRenderer(const Rc& device) - : m_mode (Mode::RenderNone), - m_scale (1.0f), - m_surfaceSize { 0, 0 }, - m_device (device), - m_textShaders (createTextShaders()), - m_graphShaders (createGraphShaders()), - m_dataBuffer (createDataBuffer()), - m_dataView (createDataView()), - m_dataOffset (0ull), - m_fontBuffer (createFontBuffer()), - m_fontBufferView(createFontBufferView()), - m_fontImage (createFontImage()), - m_fontView (createFontView()), - m_fontSampler (createFontSampler()) { + struct HudGlyphGpuData { + int16_t x; + int16_t y; + int16_t w; + int16_t h; + int16_t originX; + int16_t originY; + }; + + struct HudFontGpuData { + float size; + float advance; + uint32_t padding[2]; + HudGlyphGpuData glyphs[256]; + }; + + + + static const std::array HudSpecConstantMap = {{ + { 0, offsetof(HudSpecConstants, dstSpace), sizeof(VkColorSpaceKHR) }, + { 1, offsetof(HudSpecConstants, dstIsSrgb), sizeof(VkBool32) }, + }}; + + + + HudRenderer::HudRenderer(const Rc& device) + : m_device (device), + m_textSetLayout (createSetLayout()), + m_textPipelineLayout (createPipelineLayout()) { + createShaderModule(m_textVs, VK_SHADER_STAGE_VERTEX_BIT, sizeof(hud_text_vert), hud_text_vert); + createShaderModule(m_textFs, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(hud_text_frag), hud_text_frag); } HudRenderer::~HudRenderer() { - + auto vk = m_device->vkd(); + + for (const auto& p : m_textPipelines) + vk->vkDestroyPipeline(vk->device(), p.second, nullptr); + + vk->vkDestroyShaderModule(vk->device(), m_textVs.stageInfo.module, nullptr); + vk->vkDestroyShaderModule(vk->device(), m_textFs.stageInfo.module, nullptr); + + vk->vkDestroyPipelineLayout(vk->device(), m_textPipelineLayout, nullptr); + vk->vkDestroyDescriptorSetLayout(vk->device(), m_textSetLayout, nullptr); } void HudRenderer::beginFrame( - const Rc& context, - VkExtent2D surfaceSize, - float scale, - float opacity) { - if (!m_initialized) - this->initFontTexture(context); + const DxvkContextObjects& ctx, + const Rc& dstView, + VkColorSpaceKHR dstColorSpace, + const HudOptions& options) { + if (!m_fontTextureView) { + createFontResources(); + uploadFontResources(ctx); + } - m_mode = Mode::RenderNone; - m_scale = scale; - m_opacity = opacity; - m_surfaceSize = surfaceSize; - m_context = context; + VkExtent3D extent = dstView->mipLevelExtent(0u); + + m_pushConstants.surfaceSize = { extent.width, extent.height }; + m_pushConstants.opacity = options.opacity; + m_pushConstants.scale = options.scale; + + VkViewport viewport = { }; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = float(extent.width); + viewport.height = float(extent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = { }; + scissor.offset = { 0u, 0u }; + scissor.extent = { extent.width, extent.height }; + + ctx.cmd->cmdSetViewport(1, &viewport); + ctx.cmd->cmdSetScissor(1, &scissor); } void HudRenderer::drawText( - float size, - HudPos pos, - HudColor color, - const std::string& text) { + uint32_t size, + HudPos pos, + uint32_t color, + const std::string& text) { if (text.empty()) return; - beginTextRendering(); + auto& draw = m_textDraws.emplace_back(); + draw.textOffset = m_textData.size(); + draw.textLength = text.size(); + draw.fontSize = size; + draw.posX = pos.x; + draw.posY = pos.y; + draw.color = color; - // Copy string into string buffer, but extend it to cover a full cache - // line to avoid potential CPU performance issues with the upload. - std::string textCopy = text; - textCopy.resize(align(text.size(), CACHE_LINE_SIZE), ' '); - - VkDeviceSize offset = allocDataBuffer(textCopy.size()); - std::memcpy(m_dataBuffer->mapPtr(offset), textCopy.data(), textCopy.size()); - - // Enforce HUD opacity factor on alpha - if (m_opacity != 1.0f) - color.a *= m_opacity; - - // Fill in push constants for the next draw - HudTextPushConstants pushData; - pushData.color = color; - pushData.pos = pos; - pushData.offset = offset; - pushData.size = size; - pushData.scale.x = m_scale / std::max(float(m_surfaceSize.width), 1.0f); - pushData.scale.y = m_scale / std::max(float(m_surfaceSize.height), 1.0f); - - m_context->pushConstants(0, sizeof(pushData), &pushData); - - // Draw with orignal vertex count - m_context->draw(6 * text.size(), 1, 0, 0); + m_textData.resize(draw.textOffset + draw.textLength); + std::memcpy(&m_textData[draw.textOffset], text.data(), draw.textLength); } - - - void HudRenderer::drawGraph( - HudPos pos, - HudPos size, - size_t pointCount, - const HudGraphPoint* pointData) { - beginGraphRendering(); - VkDeviceSize dataSize = pointCount * sizeof(*pointData); - VkDeviceSize offset = allocDataBuffer(dataSize); - std::memcpy(m_dataBuffer->mapPtr(offset), pointData, dataSize); - HudGraphPushConstants pushData; - pushData.offset = offset / sizeof(*pointData); - pushData.count = pointCount; - pushData.pos = pos; - pushData.size = size; - pushData.scale.x = m_scale / std::max(float(m_surfaceSize.width), 1.0f); - pushData.scale.y = m_scale / std::max(float(m_surfaceSize.height), 1.0f); - pushData.opacity = m_opacity; + void HudRenderer::flushDraws( + const DxvkContextObjects& ctx, + const Rc& dstView, + VkColorSpaceKHR dstColorSpace, + const HudOptions& options) { + if (m_textDraws.empty()) + return; - m_context->pushConstants(0, sizeof(pushData), &pushData); - m_context->draw(4, 1, 0, 0); - } - - - void HudRenderer::beginTextRendering() { - if (m_mode != Mode::RenderText) { - m_mode = Mode::RenderText; + // Align text size so that we're guaranteed to be able to put draw + // parameters where we want, and can also upload data without + // running into perf issues due to incomplete cache lines. + size_t textSizeAligned = align(m_textData.size(), 256u); + m_textData.resize(textSizeAligned); - m_context->bindShader(Rc(m_textShaders.vert)); - m_context->bindShader(Rc(m_textShaders.frag)); - - m_context->bindResourceBufferView(VK_SHADER_STAGE_VERTEX_BIT, 0, Rc(m_fontBufferView)); - m_context->bindResourceBufferView(VK_SHADER_STAGE_VERTEX_BIT, 1, Rc(m_dataView)); - m_context->bindResourceSampler(VK_SHADER_STAGE_FRAGMENT_BIT, 2, Rc(m_fontSampler)); - m_context->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 2, Rc(m_fontView)); - - static const DxvkInputAssemblyState iaState = { - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - VK_FALSE, 0 }; - - m_context->setInputAssemblyState(iaState); + // We'll use indirect draws and then just use aligned subsections + // of the data buffer to write our draw parameters + size_t drawInfoSize = align(m_textDraws.size() * sizeof(HudTextDrawInfo), 256u); + size_t drawArgsSize = align(m_textDraws.size() * sizeof(VkDrawIndirectCommand), 256u); + + // Align buffer size to something large so we don't recreate it all the time + size_t bufferSize = align(textSizeAligned + drawInfoSize + drawArgsSize, 2048u); + + if (!m_textBuffer || m_textBuffer->info().size < bufferSize) { + DxvkBufferCreateInfo textBufferInfo = { }; + textBufferInfo.size = bufferSize; + textBufferInfo.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT + | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; + textBufferInfo.stages = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT + | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; + textBufferInfo.access = VK_ACCESS_SHADER_READ_BIT + | VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + + m_textBuffer = m_device->createBuffer(textBufferInfo, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + DxvkBufferViewKey textViewInfo = { }; + textViewInfo.format = VK_FORMAT_R8_UINT; + textViewInfo.offset = 0u; + textViewInfo.size = bufferSize; + textViewInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; + + m_textBufferView = m_textBuffer->createView(textViewInfo); + } else { + // Discard and invalidate buffer so we can safely update it + m_textBuffer->assignSlice(Rc(m_textBuffer->allocateSlice())); } - } - - void HudRenderer::beginGraphRendering() { - if (m_mode != Mode::RenderGraph) { - m_mode = Mode::RenderGraph; + // Upload aligned text data in such a way that we write full cache lines + std::memcpy(m_textBuffer->mapPtr(0), m_textData.data(), textSizeAligned); - m_context->bindShader(Rc(m_graphShaders.vert)); - m_context->bindShader(Rc(m_graphShaders.frag)); - - m_context->bindResourceBufferView(VK_SHADER_STAGE_FRAGMENT_BIT, 0, Rc(m_dataView)); + // Upload draw parameters and pad aligned region with zeroes + size_t drawInfoCopySize = m_textDraws.size() * sizeof(HudTextDrawInfo); + std::memcpy(m_textBuffer->mapPtr(textSizeAligned), m_textDraws.data(), drawInfoCopySize); + std::memset(m_textBuffer->mapPtr(textSizeAligned + drawInfoCopySize), 0, drawInfoSize - drawInfoCopySize); - static const DxvkInputAssemblyState iaState = { - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, - VK_FALSE, 0 }; + // Emit indirect draw parameters + size_t drawArgWriteSize = m_textDraws.size() * sizeof(VkDrawIndirectCommand); + size_t drawArgOffset = textSizeAligned + drawInfoSize; - m_context->setInputAssemblyState(iaState); + auto drawArgs = reinterpret_cast(m_textBuffer->mapPtr(drawArgOffset)); + + for (size_t i = 0; i < m_textDraws.size(); i++) { + drawArgs[i].vertexCount = 6u * m_textDraws[i].textLength; + drawArgs[i].instanceCount = 1u; + drawArgs[i].firstVertex = 0u; + drawArgs[i].firstInstance = 0u; } + + std::memset(m_textBuffer->mapPtr(drawArgOffset + drawArgWriteSize), 0, drawArgsSize - drawArgWriteSize); + + // Draw the actual text + VkDescriptorBufferInfo textBufferDescriptor = m_textBuffer->getDescriptor(textSizeAligned, drawInfoSize).buffer; + VkDescriptorBufferInfo drawBufferDescriptor = m_textBuffer->getDescriptor(drawArgOffset, drawArgWriteSize).buffer; + + drawTextIndirect(ctx, getPipelineKey(dstView, dstColorSpace), + drawBufferDescriptor, textBufferDescriptor, + m_textBufferView->handle(), m_textDraws.size()); + + // Ensure all used resources are kept alive + ctx.cmd->trackResource(m_textBuffer->getAllocation()); + ctx.cmd->trackResource(m_fontBuffer->getAllocation()); + ctx.cmd->trackResource(m_fontTexture->getAllocation()); + ctx.cmd->trackSampler(m_fontSampler); + + // Reset internal text buffers + m_textDraws.clear(); + m_textData.clear(); } - VkDeviceSize HudRenderer::allocDataBuffer(VkDeviceSize size) { - if (m_dataOffset + size > m_dataBuffer->info().size) { - m_context->invalidateBuffer(m_dataBuffer, m_dataBuffer->allocateSlice()); - m_dataOffset = 0; - } - - VkDeviceSize offset = m_dataOffset; - m_dataOffset = align(offset + size, 64); - return offset; - } - + void HudRenderer::drawTextIndirect( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const VkDescriptorBufferInfo& drawArgs, + const VkDescriptorBufferInfo& drawInfos, + VkBufferView text, + uint32_t drawCount) { + // Bind the correct pipeline for the swap chain + VkPipeline pipeline = getPipeline(key); - HudRenderer::ShaderPair HudRenderer::createTextShaders() { - ShaderPair result; + ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - SpirvCodeBuffer vsCode(hud_text_vert); - SpirvCodeBuffer fsCode(hud_text_frag); - - const std::array vsBindings = {{ - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_VERTEX_BIT, VK_ACCESS_SHADER_READ_BIT }, - { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_VERTEX_BIT, VK_ACCESS_SHADER_READ_BIT }, + // Bind resources + VkDescriptorSet set = ctx.descriptorPool->alloc(m_textSetLayout); + + VkDescriptorBufferInfo fontBufferDescriptor = m_fontBuffer->getDescriptor(0, m_fontBuffer->info().size).buffer; + + VkDescriptorImageInfo fontTextureDescriptor = { }; + fontTextureDescriptor.sampler = m_fontSampler->handle(); + fontTextureDescriptor.imageView = m_fontTextureView->handle(); + fontTextureDescriptor.imageLayout = m_fontTexture->info().layout; + + std::array descriptorWrites = {{ + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, + set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &fontBufferDescriptor }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, + set, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawInfos }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, + set, 2, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, nullptr, nullptr, &text }, + { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, + set, 3, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &fontTextureDescriptor }, }}; - const std::array fsBindings = {{ - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT }, - }}; + ctx.cmd->updateDescriptorSets( + descriptorWrites.size(), + descriptorWrites.data()); - DxvkShaderCreateInfo vsInfo; - vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - vsInfo.bindingCount = vsBindings.size(); - vsInfo.bindings = vsBindings.data(); - vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT; - vsInfo.pushConstSize = sizeof(HudTextPushConstants); - vsInfo.outputMask = 0x3; - result.vert = new DxvkShader(vsInfo, std::move(vsCode)); + ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, m_textPipelineLayout, + set, 0, nullptr); - DxvkShaderCreateInfo fsInfo; - fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fsInfo.bindingCount = fsBindings.size(); - fsInfo.bindings = fsBindings.data(); - fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT; - fsInfo.pushConstSize = sizeof(HudTextPushConstants); - fsInfo.inputMask = 0x3; - fsInfo.outputMask = 0x1; - result.frag = new DxvkShader(fsInfo, std::move(fsCode)); + ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_textPipelineLayout, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + 0, sizeof(m_pushConstants), &m_pushConstants); + // Emit the actual draw call + ctx.cmd->cmdDrawIndirect(drawArgs.buffer, drawArgs.offset, + drawCount, sizeof(VkDrawIndirectCommand)); + } + + + HudPipelineKey HudRenderer::getPipelineKey( + const Rc& dstView, + VkColorSpaceKHR dstColorSpace) const { + HudPipelineKey key; + key.format = dstView->info().format; + key.colorSpace = dstColorSpace; + return key; + } + + + HudSpecConstants HudRenderer::getSpecConstants( + const HudPipelineKey& key) const { + HudSpecConstants result = { }; + result.dstSpace = key.colorSpace; + result.dstIsSrgb = lookupFormatInfo(key.format)->flags.test(DxvkFormatFlag::ColorSpaceSrgb); return result; } - - - HudRenderer::ShaderPair HudRenderer::createGraphShaders() { - ShaderPair result; - SpirvCodeBuffer vsCode(hud_graph_vert); - SpirvCodeBuffer fsCode(hud_graph_frag); - - const std::array fsBindings = {{ - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT }, - }}; - DxvkShaderCreateInfo vsInfo; - vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - vsInfo.outputMask = 0x1; - vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; - vsInfo.pushConstSize = sizeof(HudGraphPushConstants); - result.vert = new DxvkShader(vsInfo, std::move(vsCode)); - - DxvkShaderCreateInfo fsInfo; - fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fsInfo.bindingCount = fsBindings.size(); - fsInfo.bindings = fsBindings.data(); - fsInfo.inputMask = 0x1; - fsInfo.outputMask = 0x1; - fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; - fsInfo.pushConstSize = sizeof(HudGraphPushConstants); - result.frag = new DxvkShader(fsInfo, std::move(fsCode)); - - return result; - } - - - Rc HudRenderer::createDataBuffer() { - DxvkBufferCreateInfo info; - info.size = DataBufferSize; - info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT - | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; - info.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT - | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - info.access = VK_ACCESS_SHADER_READ_BIT; - - return m_device->createBuffer(info, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + HudPushConstants HudRenderer::getPushConstants() const { + return m_pushConstants; } - Rc HudRenderer::createDataView() { - DxvkBufferViewKey info; - info.format = VK_FORMAT_R8_UINT; - info.offset = 0; - info.size = m_dataBuffer->info().size; - info.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; - - return m_dataBuffer->createView(info); + VkSpecializationInfo HudRenderer::getSpecInfo( + const HudSpecConstants* constants) const { + VkSpecializationInfo specInfo = { }; + specInfo.mapEntryCount = HudSpecConstantMap.size(); + specInfo.pMapEntries = HudSpecConstantMap.data(); + specInfo.dataSize = sizeof(*constants); + specInfo.pData = constants; + return specInfo; } - Rc HudRenderer::createFontBuffer() { - DxvkBufferCreateInfo info; - info.size = sizeof(HudFontGpuData); - info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT - | VK_BUFFER_USAGE_TRANSFER_DST_BIT; - info.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT - | VK_PIPELINE_STAGE_TRANSFER_BIT; - info.access = VK_ACCESS_SHADER_READ_BIT - | VK_ACCESS_TRANSFER_WRITE_BIT; - - return m_device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + void HudRenderer::createShaderModule( + HudShaderModule& shader, + VkShaderStageFlagBits stage, + size_t size, + const uint32_t* code) const { + shader.moduleInfo.codeSize = size; + shader.moduleInfo.pCode = code; + + shader.stageInfo.stage = stage; + shader.stageInfo.pName = "main"; + + if (m_device->features().khrMaintenance5.maintenance5 + || m_device->features().extGraphicsPipelineLibrary.graphicsPipelineLibrary) { + shader.stageInfo.pNext = &shader.moduleInfo; + return; + } + + auto vk = m_device->vkd(); + + VkResult vr = vk->vkCreateShaderModule(vk->device(), + &shader.moduleInfo, nullptr, &shader.stageInfo.module); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create swap chain blit shader module: ", vr)); } - Rc HudRenderer::createFontBufferView() { - DxvkBufferViewKey info; - info.format = VK_FORMAT_UNDEFINED; - info.offset = 0; - info.size = m_fontBuffer->info().size; - info.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; + void HudRenderer::createFontResources() { + DxvkBufferCreateInfo fontBufferInfo; + fontBufferInfo.size = sizeof(HudFontGpuData); + fontBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_TRANSFER_SRC_BIT + | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + fontBufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT + | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; + fontBufferInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT + | VK_ACCESS_TRANSFER_READ_BIT + | VK_ACCESS_SHADER_READ_BIT; - return m_fontBuffer->createView(info); - } + m_fontBuffer = m_device->createBuffer(fontBufferInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + DxvkImageCreateInfo fontTextureInfo; + fontTextureInfo.type = VK_IMAGE_TYPE_2D; + fontTextureInfo.format = VK_FORMAT_R8_UNORM; + fontTextureInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT; + fontTextureInfo.extent = { g_hudFont.width, g_hudFont.height, 1u }; + fontTextureInfo.numLayers = 1u; + fontTextureInfo.mipLevels = 1u; + fontTextureInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_TRANSFER_SRC_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT; + fontTextureInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT + | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + fontTextureInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT + | VK_ACCESS_TRANSFER_READ_BIT + | VK_ACCESS_SHADER_READ_BIT; + fontTextureInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + fontTextureInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + fontTextureInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - Rc HudRenderer::createFontImage() { - DxvkImageCreateInfo info; - info.type = VK_IMAGE_TYPE_2D; - info.format = VK_FORMAT_R8_UNORM; - info.flags = 0; - info.sampleCount = VK_SAMPLE_COUNT_1_BIT; - info.extent = { g_hudFont.width, g_hudFont.height, 1 }; - info.numLayers = 1; - info.mipLevels = 1; - info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT - | VK_IMAGE_USAGE_SAMPLED_BIT; - info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT - | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - info.access = VK_ACCESS_TRANSFER_WRITE_BIT - | VK_ACCESS_SHADER_READ_BIT; - info.tiling = VK_IMAGE_TILING_OPTIMAL; - info.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - return m_device->createImage(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - } - - - Rc HudRenderer::createFontView() { - DxvkImageViewKey info; - info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.format = m_fontImage->info().format; - info.usage = VK_IMAGE_USAGE_SAMPLED_BIT; - info.aspects = VK_IMAGE_ASPECT_COLOR_BIT; - info.mipIndex = 0; - info.mipCount = 1; - info.layerIndex = 0; - info.layerCount = 1; - - return m_fontImage->createView(info); - } - - - Rc HudRenderer::createFontSampler() { - DxvkSamplerKey info = { }; - info.setFilter(VK_FILTER_LINEAR, + m_fontTexture = m_device->createImage(fontTextureInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + DxvkImageViewKey fontTextureViewInfo; + fontTextureViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + fontTextureViewInfo.format = VK_FORMAT_R8_UNORM; + fontTextureViewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; + fontTextureViewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT; + fontTextureViewInfo.mipIndex = 0u; + fontTextureViewInfo.mipCount = 1u; + fontTextureViewInfo.layerIndex = 0u; + fontTextureViewInfo.layerCount = 1u; + + m_fontTextureView = m_fontTexture->createView(fontTextureViewInfo); + + DxvkSamplerKey samplerInfo; + samplerInfo.setFilter(VK_FILTER_LINEAR, VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST); - info.setAddressModes( + samplerInfo.setAddressModes( VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE); - info.setUsePixelCoordinates(true); - - return m_device->createSampler(info); + samplerInfo.setUsePixelCoordinates(true); + + m_fontSampler = m_device->createSampler(samplerInfo); } - - - void HudRenderer::initFontTexture( - const Rc& context) { - HudFontGpuData gpuData = { }; - gpuData.size = float(g_hudFont.size); - gpuData.advance = float(g_hudFont.advance); - for (uint32_t i = 0; i < g_hudFont.charCount; i++) { - auto src = &g_hudFont.glyphs[i]; - auto dst = &gpuData.glyphs[src->codePoint]; - dst->x = src->x; - dst->y = src->y; - dst->w = src->w; - dst->h = src->h; - dst->originX = src->originX; - dst->originY = src->originY; + void HudRenderer::uploadFontResources( + const DxvkContextObjects& ctx) { + size_t bufferDataSize = sizeof(HudFontGpuData); + size_t textureDataSize = g_hudFont.width * g_hudFont.height; + + DxvkBufferCreateInfo bufferInfo; + bufferInfo.size = bufferDataSize + textureDataSize; + bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT; + bufferInfo.access = VK_ACCESS_TRANSFER_READ_BIT; + + auto uploadBuffer = m_device->createBuffer(bufferInfo, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + HudFontGpuData glyphData = { }; + glyphData.size = float(g_hudFont.size); + glyphData.advance = float(g_hudFont.advance); + + for (size_t i = 0; i < g_hudFont.charCount; i++) { + auto& src = g_hudFont.glyphs[i]; + auto& dst = glyphData.glyphs[src.codePoint]; + + dst.x = src.x; + dst.y = src.y; + dst.w = src.w; + dst.h = src.h; + dst.originX = src.originX; + dst.originY = src.originY; } - context->uploadBuffer(m_fontBuffer, &gpuData); + std::memcpy(uploadBuffer->mapPtr(0), &glyphData, bufferDataSize); + std::memcpy(uploadBuffer->mapPtr(bufferDataSize), g_hudFont.texture, textureDataSize); - context->uploadImage(m_fontImage, - VkImageSubresourceLayers { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }, - g_hudFont.texture, g_hudFont.width, g_hudFont.width * g_hudFont.height); - - m_initialized = true; + auto uploadSlice = uploadBuffer->getSliceHandle(); + auto fontSlice = m_fontBuffer->getSliceHandle(); + + VkImageMemoryBarrier2 imageBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 }; + imageBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageBarrier.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageBarrier.newLayout = m_fontTexture->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageBarrier.image = m_fontTexture->handle(); + imageBarrier.subresourceRange = m_fontTexture->getAvailableSubresources(); + + VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO }; + depInfo.imageMemoryBarrierCount = 1u; + depInfo.pImageMemoryBarriers = &imageBarrier; + + ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo); + + VkBufferCopy2 bufferRegion = { VK_STRUCTURE_TYPE_BUFFER_COPY_2 }; + bufferRegion.srcOffset = uploadSlice.offset; + bufferRegion.dstOffset = fontSlice.offset; + bufferRegion.size = bufferDataSize; + + VkCopyBufferInfo2 bufferCopy = { VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2 }; + bufferCopy.srcBuffer = uploadSlice.handle; + bufferCopy.dstBuffer = fontSlice.handle; + bufferCopy.regionCount = 1; + bufferCopy.pRegions = &bufferRegion; + + ctx.cmd->cmdCopyBuffer(DxvkCmdBuffer::InitBuffer, &bufferCopy); + + VkBufferImageCopy2 imageRegion = { VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2 }; + imageRegion.bufferOffset = uploadSlice.offset + bufferDataSize; + imageRegion.imageExtent = m_fontTexture->info().extent; + imageRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageRegion.imageSubresource.layerCount = 1u; + + VkCopyBufferToImageInfo2 imageCopy = { VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2 }; + imageCopy.srcBuffer = uploadSlice.handle; + imageCopy.dstImage = m_fontTexture->handle(); + imageCopy.dstImageLayout = m_fontTexture->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + imageCopy.regionCount = 1; + imageCopy.pRegions = &imageRegion; + + ctx.cmd->cmdCopyBufferToImage(DxvkCmdBuffer::InitBuffer, &imageCopy); + + VkMemoryBarrier2 memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 }; + memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + memoryBarrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + memoryBarrier.dstAccessMask = m_fontBuffer->info().access; + memoryBarrier.dstStageMask = m_fontBuffer->info().stages; + + imageBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 }; + imageBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imageBarrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + imageBarrier.dstAccessMask = m_fontTexture->info().access; + imageBarrier.dstStageMask = m_fontTexture->info().stages; + imageBarrier.oldLayout = m_fontTexture->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + imageBarrier.newLayout = m_fontTexture->info().layout; + imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageBarrier.image = m_fontTexture->handle(); + imageBarrier.subresourceRange = m_fontTexture->getAvailableSubresources(); + + depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO }; + depInfo.memoryBarrierCount = 1u; + depInfo.pMemoryBarriers = &memoryBarrier; + depInfo.imageMemoryBarrierCount = 1u; + depInfo.pImageMemoryBarriers = &imageBarrier; + + ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo); + + ctx.cmd->trackResource(uploadBuffer->getAllocation()); + ctx.cmd->trackResource(m_fontBuffer->getAllocation()); + ctx.cmd->trackResource(m_fontTexture->getAllocation()); } - + + + VkDescriptorSetLayout HudRenderer::createSetLayout() { + auto vk = m_device->vkd(); + + static const std::array bindings = {{ + { 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT }, + { 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT }, + { 2, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT }, + { 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT }, + }}; + + VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + info.bindingCount = bindings.size(); + info.pBindings = bindings.data(); + + VkDescriptorSetLayout layout = VK_NULL_HANDLE; + VkResult vr = vk->vkCreateDescriptorSetLayout(vk->device(), &info, nullptr, &layout); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create HUD descriptor set layout: ", vr)); + + return layout; + } + + + VkPipelineLayout HudRenderer::createPipelineLayout() { + auto vk = m_device->vkd(); + + VkPushConstantRange pushConstantRange = { }; + pushConstantRange.offset = 0u; + pushConstantRange.size = sizeof(HudPushConstants); + pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + + VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + info.setLayoutCount = 1; + info.pSetLayouts = &m_textSetLayout; + info.pushConstantRangeCount = 1; + info.pPushConstantRanges = &pushConstantRange; + + VkPipelineLayout layout = VK_NULL_HANDLE; + VkResult vr = vk->vkCreatePipelineLayout(vk->device(), &info, nullptr, &layout); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create HUD descriptor set layout: ", vr)); + + return layout; + } + + + VkPipeline HudRenderer::createPipeline( + const HudPipelineKey& key) { + auto vk = m_device->vkd(); + + HudSpecConstants specConstants = getSpecConstants(key); + VkSpecializationInfo specInfo = getSpecInfo(&specConstants); + + std::array stages = { }; + stages[0] = m_textVs.stageInfo; + stages[1] = m_textFs.stageInfo; + stages[1].pSpecializationInfo = &specInfo; + + VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO }; + rtInfo.colorAttachmentCount = 1; + rtInfo.pColorAttachmentFormats = &key.format; + + VkPipelineVertexInputStateCreateInfo viState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; + + VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; + iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkPipelineViewportStateCreateInfo vpState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; + + VkPipelineRasterizationStateCreateInfo rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; + rsState.cullMode = VK_CULL_MODE_NONE; + rsState.polygonMode = VK_POLYGON_MODE_FILL; + rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rsState.lineWidth = 1.0f; + + constexpr uint32_t sampleMask = 0x1; + + VkPipelineMultisampleStateCreateInfo msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; + msState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + msState.pSampleMask = &sampleMask; + + VkPipelineColorBlendAttachmentState cbAttachment = { }; + cbAttachment.blendEnable = VK_TRUE; + cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + cbAttachment.colorBlendOp = VK_BLEND_OP_ADD; + cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + cbAttachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineColorBlendStateCreateInfo cbOpaqueState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; + cbOpaqueState.attachmentCount = 1; + cbOpaqueState.pAttachments = &cbAttachment; + + static const std::array dynStates = { + VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT, + VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT, + }; + + VkPipelineDynamicStateCreateInfo dynState = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; + dynState.dynamicStateCount = dynStates.size(); + dynState.pDynamicStates = dynStates.data(); + + VkGraphicsPipelineCreateInfo blitInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &rtInfo }; + blitInfo.stageCount = stages.size(); + blitInfo.pStages = stages.data(); + blitInfo.pVertexInputState = &viState; + blitInfo.pInputAssemblyState = &iaState; + blitInfo.pViewportState = &vpState; + blitInfo.pRasterizationState = &rsState; + blitInfo.pMultisampleState = &msState; + blitInfo.pColorBlendState = &cbOpaqueState; + blitInfo.pDynamicState = &dynState; + blitInfo.layout = m_textPipelineLayout; + blitInfo.basePipelineIndex = -1; + + VkPipeline pipeline = VK_NULL_HANDLE; + VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, + 1, &blitInfo, nullptr, &pipeline); + + if (vr != VK_SUCCESS) + throw DxvkError(str::format("Failed to create swap chain blit pipeline: ", vr)); + + return pipeline; + } + + + VkPipeline HudRenderer::getPipeline( + const HudPipelineKey& key) { + auto entry = m_textPipelines.find(key); + + if (entry != m_textPipelines.end()) + return entry->second; + + VkPipeline pipeline = createPipeline(key); + m_textPipelines.insert({ key, pipeline }); + return pipeline; + } + } diff --git a/src/dxvk/hud/dxvk_hud_renderer.h b/src/dxvk/hud/dxvk_hud_renderer.h index 45184be7a..068a0518f 100644 --- a/src/dxvk/hud/dxvk_hud_renderer.h +++ b/src/dxvk/hud/dxvk_hud_renderer.h @@ -5,7 +5,16 @@ #include "dxvk_hud_font.h" namespace dxvk::hud { - + + /** + * \brief HUD options + */ + struct HudOptions { + float scale = 1.0f; + float opacity = 1.0f; + }; + + /** * \brief HUD coordinates * @@ -13,82 +22,69 @@ namespace dxvk::hud { * corner of the swap image, in pixels. */ struct HudPos { - float x; - float y; - }; - - /** - * \brief Color - * - * SRGB color with alpha channel. The text - * will use this color for the most part. - */ - struct HudColor { - float r; - float g; - float b; - float a; + int32_t x = 0; + int32_t y = 0; }; - /** - * \brief Normalized color - * - * SRGB color with alpha channel. - */ - struct HudNormColor { - uint8_t a; - uint8_t b; - uint8_t g; - uint8_t r; - }; - - /** - * \brief Graph point with color - */ - struct HudGraphPoint { - float value; - HudNormColor color; - }; /** - * \brief HUD push constant data + * \brief Draw parameters for text */ - struct HudTextPushConstants { - HudColor color; - HudPos pos; - uint32_t offset; - float size; - HudPos scale; + struct HudTextDrawInfo { + uint32_t textOffset = 0u; + uint16_t textLength = 0u; + uint16_t fontSize = 0u; + int16_t posX = 0; + int16_t posY = 0; + uint32_t color = 0u; }; - struct HudGraphPushConstants { - uint32_t offset; - uint32_t count; - HudPos pos; - HudPos size; - HudPos scale; - float opacity; + + struct HudPushConstants { + VkExtent2D surfaceSize; + float opacity; + float scale; }; + /** - * \brief Glyph data + * \brief Pipeline key */ - struct HudGlyphGpuData { - int16_t x; - int16_t y; - int16_t w; - int16_t h; - int16_t originX; - int16_t originY; + struct HudPipelineKey { + VkFormat format = VK_FORMAT_UNDEFINED; + VkColorSpaceKHR colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + + size_t hash() const { + DxvkHashState hash; + hash.add(uint32_t(format)); + hash.add(uint32_t(colorSpace)); + return hash; + } + + bool eq(const HudPipelineKey& other) const { + return format == other.format && colorSpace == other.colorSpace; + } }; - struct HudFontGpuData { - float size; - float advance; - uint32_t padding[2]; - HudGlyphGpuData glyphs[256]; + + /** + * \brief Specialization constants + */ + struct HudSpecConstants { + VkColorSpaceKHR dstSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + VkBool32 dstIsSrgb = VK_FALSE; }; + + /** + * \brief Shader module info + */ + struct HudShaderModule { + VkShaderModuleCreateInfo moduleInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; + VkPipelineShaderStageCreateInfo stageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; + }; + + /** * \brief Text renderer for the HUD * @@ -103,99 +99,92 @@ namespace dxvk::hud { const Rc& device); ~HudRenderer(); - + void beginFrame( - const Rc& context, - VkExtent2D surfaceSize, - float scale, - float opacity); - + const DxvkContextObjects& ctx, + const Rc& dstView, + VkColorSpaceKHR dstColorSpace, + const HudOptions& options); + void drawText( - float size, - HudPos pos, - HudColor color, - const std::string& text); - - void drawGraph( - HudPos pos, - HudPos size, - size_t pointCount, - const HudGraphPoint* pointData); + uint32_t size, + HudPos pos, + uint32_t color, + const std::string& text); - DxvkContext* getContext() { - m_mode = Mode::RenderNone; - return m_context.ptr(); - } + void drawTextIndirect( + const DxvkContextObjects& ctx, + const HudPipelineKey& key, + const VkDescriptorBufferInfo& drawArgs, + const VkDescriptorBufferInfo& drawInfos, + VkBufferView text, + uint32_t drawCount); - VkExtent2D surfaceSize() const { - return m_surfaceSize; - } + void flushDraws( + const DxvkContextObjects& ctx, + const Rc& dstView, + VkColorSpaceKHR dstColorSpace, + const HudOptions& options); - float scale() const { - return m_scale; - } + HudPipelineKey getPipelineKey( + const Rc& dstView, + VkColorSpaceKHR dstColorSpace) const; + + HudSpecConstants getSpecConstants( + const HudPipelineKey& key) const; + + HudPushConstants getPushConstants() const; + + VkSpecializationInfo getSpecInfo( + const HudSpecConstants* constants) const; + + void createShaderModule( + HudShaderModule& shader, + VkShaderStageFlagBits stage, + size_t size, + const uint32_t* code) const; - float opacity() const { - return m_opacity; - } - private: - - enum class Mode { - RenderNone, - RenderText, - RenderGraph, - }; - struct ShaderPair { - Rc vert; - Rc frag; - }; - - Mode m_mode; - float m_scale; - float m_opacity; - VkExtent2D m_surfaceSize; + Rc m_device; - Rc m_device; - Rc m_context; - - ShaderPair m_textShaders; - ShaderPair m_graphShaders; - - Rc m_dataBuffer; - Rc m_dataView; - VkDeviceSize m_dataOffset; + Rc m_fontBuffer; + Rc m_fontTexture; + Rc m_fontTextureView; + Rc m_fontSampler; - Rc m_fontBuffer; - Rc m_fontBufferView; - Rc m_fontImage; - Rc m_fontView; - Rc m_fontSampler; + Rc m_textBuffer; + Rc m_textBufferView; - bool m_initialized = false; + std::vector m_textDraws; + std::vector m_textData; - void beginTextRendering(); - - void beginGraphRendering(); + HudShaderModule m_textVs; + HudShaderModule m_textFs; - VkDeviceSize allocDataBuffer(VkDeviceSize size); + VkDescriptorSetLayout m_textSetLayout = VK_NULL_HANDLE; + VkPipelineLayout m_textPipelineLayout = VK_NULL_HANDLE; - ShaderPair createTextShaders(); - ShaderPair createGraphShaders(); + HudPushConstants m_pushConstants = { }; - Rc createDataBuffer(); - Rc createDataView(); + std::unordered_map m_textPipelines; + + void createFontResources(); + + void uploadFontResources( + const DxvkContextObjects& ctx); + + VkDescriptorSetLayout createSetLayout(); + + VkPipelineLayout createPipelineLayout(); + + VkPipeline createPipeline( + const HudPipelineKey& key); + + VkPipeline getPipeline( + const HudPipelineKey& key); - Rc createFontBuffer(); - Rc createFontBufferView(); - Rc createFontImage(); - Rc createFontView(); - Rc createFontSampler(); - - void initFontTexture( - const Rc& context); - }; } \ No newline at end of file diff --git a/src/dxvk/hud/shaders/hud_chunk_frag_background.frag b/src/dxvk/hud/shaders/hud_chunk_frag_background.frag index ef836baef..4748abec3 100644 --- a/src/dxvk/hud/shaders/hud_chunk_frag_background.frag +++ b/src/dxvk/hud/shaders/hud_chunk_frag_background.frag @@ -4,20 +4,17 @@ #include "hud_frag_common.glsl" -layout(location = 0) out vec4 o_color; - layout(push_constant) uniform push_data_t { - vec2 pos; - vec2 size; - vec2 scale; + uvec2 surface_size; float opacity; - uint color; - uint maskIndex; - uint pageCount; + float scale; }; +layout(location = 0) out vec4 o_color; + void main() { - vec4 rgba = unpackUnorm4x8(color); - o_color = vec4(encodeOutput(rgba.rgb), rgba.a * opacity); + o_color = vec4(0.0f, 0.0f, 0.0f, 0.75f); + o_color.a *= opacity; + o_color = linear_to_output(o_color); } diff --git a/src/dxvk/hud/shaders/hud_chunk_frag_visualize.frag b/src/dxvk/hud/shaders/hud_chunk_frag_visualize.frag index 95d50f69e..52eabc28e 100644 --- a/src/dxvk/hud/shaders/hud_chunk_frag_visualize.frag +++ b/src/dxvk/hud/shaders/hud_chunk_frag_visualize.frag @@ -5,32 +5,31 @@ #include "hud_frag_common.glsl" layout(location = 0) in vec2 v_coord; + +layout(location = 1, component = 0) flat in uint v_color; +layout(location = 1, component = 1) flat in uint v_mask_index; +layout(location = 1, component = 2) flat in uint v_page_count; + layout(location = 0) out vec4 o_color; -layout(binding = 0, std430) +layout(binding = 1, std430) readonly buffer mask_data_t { uint masks[]; }; layout(push_constant) uniform push_data_t { - vec2 pos; - vec2 size; - vec2 scale; + uvec2 surface_size; float opacity; - uint color; - uint maskIndex; - uint pageCount; + float scale; }; void main() { - vec4 rgba = unpackUnorm4x8(color); - - float dx = dFdx(v_coord.x * float(pageCount)) / 2.0f - 0.5f; + float dx = dFdx(v_coord.x * float(v_page_count)) / 2.0f - 0.5f; uvec2 pageRange = uvec2(clamp( - (v_coord.xx * float(pageCount)) + vec2(-dx, dx), - vec2(0.0), vec2(float(pageCount - 1u)))); + (v_coord.xx * float(v_page_count)) + vec2(-dx, dx), + vec2(0.0), vec2(float(v_page_count - 1u)))); uint bitsTotal = max(pageRange.y - pageRange.x, 1u); uint bitsSet = 0u; @@ -40,26 +39,28 @@ void main() { if (shift + bitsTotal <= 32u) { bitsSet = bitCount(bitfieldExtract( - masks[maskIndex + index], int(shift), int(bitsTotal))); + masks[v_mask_index + index], int(shift), int(bitsTotal))); } else { - bitsSet = bitCount(masks[maskIndex + (index++)] >> shift); + bitsSet = bitCount(masks[v_mask_index + (index++)] >> shift); uint bitsCounted = 32u - shift; while (bitsCounted + 32u <= bitsTotal) { - bitsSet += bitCount(masks[maskIndex + (index++)]); + bitsSet += bitCount(masks[v_mask_index + (index++)]); bitsCounted += 32u; } if (bitsCounted < bitsTotal) { bitsSet += bitCount(bitfieldExtract( - masks[maskIndex + (index++)], 0, int(bitsTotal - bitsCounted))); + masks[v_mask_index + (index++)], 0, int(bitsTotal - bitsCounted))); } } if (bitsSet == 0u) discard; + vec4 color = unpackUnorm4x8(v_color); + float blendFactor = 0.5f * float(bitsSet) / max(float(bitsTotal), 1.0f); - o_color = vec4(mix(rgba.rgb, vec3(1.0f), blendFactor), rgba.a * opacity); - o_color.rgb = encodeOutput(o_color.rgb); + o_color = vec4(mix(color.rgb, vec3(1.0f), blendFactor), color.a * opacity); + o_color = linear_to_output(o_color); } diff --git a/src/dxvk/hud/shaders/hud_chunk_vert.vert b/src/dxvk/hud/shaders/hud_chunk_vert.vert deleted file mode 100644 index cab2fc6ba..000000000 --- a/src/dxvk/hud/shaders/hud_chunk_vert.vert +++ /dev/null @@ -1,25 +0,0 @@ -#version 450 - -layout(location = 0) out vec2 o_coord; - -layout(push_constant) -uniform push_data_t { - vec2 pos; - vec2 size; - vec2 scale; - float opacity; - uint color; - uint maskIndex; - uint pageCount; -}; - -void main() { - vec2 coord = vec2( - float(gl_VertexIndex & 1), - float(gl_VertexIndex >> 1)); - o_coord = coord; - - vec2 pixel_pos = pos + size * coord; - vec2 scaled_pos = 2.0f * scale * pixel_pos - 1.0f; - gl_Position = vec4(scaled_pos, 0.0f, 1.0f); -} diff --git a/src/dxvk/hud/shaders/hud_chunk_vert_background.vert b/src/dxvk/hud/shaders/hud_chunk_vert_background.vert new file mode 100644 index 000000000..381c42475 --- /dev/null +++ b/src/dxvk/hud/shaders/hud_chunk_vert_background.vert @@ -0,0 +1,47 @@ +#version 450 + +struct draw_info_t { + uint packed_xy; + uint packed_wh; + uint packed_range; + uint color; +}; + +layout(binding = 0, std430) +readonly buffer draw_data_t { + draw_info_t draw_infos[]; +}; + +layout(push_constant) +uniform push_data_t { + uvec2 surface_size; + float opacity; + float scale; +}; + +vec2 unpack_u16(uint v) { + // Inputs may be signed + int hi = int(v); + int lo = int(v << 16); + return vec2(float(lo >> 16), float(hi >> 16)); +} + +void main() { + draw_info_t draw = draw_infos[gl_InstanceIndex]; + + vec2 coord = vec2( + float(gl_VertexIndex & 1), + float(gl_VertexIndex >> 1)); + + vec2 surface_size_f = vec2(surface_size) / scale; + + vec2 pos = unpack_u16(draw.packed_xy); + vec2 size = unpack_u16(draw.packed_wh); + + pos = mix(pos, surface_size_f + pos, lessThan(pos, vec2(0.0f))); + + vec2 pixel_pos = pos + size * coord + (2.0f * coord - 1.0f); + vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f; + + gl_Position = vec4(scaled_pos, 0.0f, 1.0f); +} diff --git a/src/dxvk/hud/shaders/hud_chunk_vert_visualize.vert b/src/dxvk/hud/shaders/hud_chunk_vert_visualize.vert new file mode 100644 index 000000000..f17fdc7bd --- /dev/null +++ b/src/dxvk/hud/shaders/hud_chunk_vert_visualize.vert @@ -0,0 +1,58 @@ +#version 450 + +struct draw_info_t { + uint packed_xy; + uint packed_wh; + uint packed_range; + uint color; +}; + +layout(binding = 0, std430) +readonly buffer draw_data_t { + draw_info_t draw_infos[]; +}; + +layout(push_constant) +uniform push_data_t { + uvec2 surface_size; + float opacity; + float scale; +}; + +layout(location = 0) out vec2 o_coord; + +layout(location = 1, component = 0) out uint o_color; +layout(location = 1, component = 1) out uint o_mask_index; +layout(location = 1, component = 2) out uint o_page_count; + +vec2 unpack_u16(uint v) { + // Inputs may be signed + int hi = int(v); + int lo = int(v << 16); + return vec2(float(lo >> 16), float(hi >> 16)); +} + +void main() { + draw_info_t draw = draw_infos[gl_InstanceIndex]; + + vec2 coord = vec2( + float(gl_VertexIndex & 1), + float(gl_VertexIndex >> 1)); + + o_coord = coord; + o_color = draw.color; + o_mask_index = bitfieldExtract(draw.packed_range, 0, 16); + o_page_count = bitfieldExtract(draw.packed_range, 16, 16); + + vec2 surface_size_f = vec2(surface_size) / scale; + + vec2 pos = unpack_u16(draw.packed_xy); + vec2 size = unpack_u16(draw.packed_wh); + + pos = mix(pos, surface_size_f + pos, lessThan(pos, vec2(0.0f))); + + vec2 pixel_pos = pos + size * coord; + vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f; + + gl_Position = vec4(scaled_pos, 0.0f, 1.0f); +} diff --git a/src/dxvk/hud/shaders/hud_frag_common.glsl b/src/dxvk/hud/shaders/hud_frag_common.glsl index 20b51c537..17cb820a9 100644 --- a/src/dxvk/hud/shaders/hud_frag_common.glsl +++ b/src/dxvk/hud/shaders/hud_frag_common.glsl @@ -1,68 +1,27 @@ -#define VK_COLOR_SPACE_SRGB_NONLINEAR_KHR (0) -#define VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT (1000104002) -#define VK_COLOR_SPACE_HDR10_ST2084_EXT (1000104008) -#define VK_COLOR_SPACE_PASS_THROUGH_EXT (1000104013) +#include "../../shaders/dxvk_color_space.glsl" -#define HUD_NITS (203.0f) +layout(constant_id = 0) const uint s_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; +layout(constant_id = 1) const bool s_srgb = false; -const mat3 rec709_to_xyz = mat3( - 0.4123908, 0.2126390, 0.0193308, - 0.3575843, 0.7151687, 0.1191948, - 0.1804808, 0.0721923, 0.9505322); +vec4 linear_to_output(vec4 color) { + switch (s_color_space) { + case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: { + if (!s_srgb) + color.rgb = linear_to_srgb(color.rgb); -const mat3 xyz_to_rec2020 = mat3( - 1.7166512, -0.6666844, 0.0176399, - -0.3556708, 1.6164812, -0.0427706, - -0.2533663, 0.0157685, 0.9421031); - -const mat3 rec709_to_rec2020 = xyz_to_rec2020 * rec709_to_xyz; - -// Spec constants must always default to -// zero for DXVK to handle them properly -layout(constant_id = 0) const uint hud_color_space = 0; - -vec3 encodeSrgb(vec3 linear) { - bvec3 isLo = lessThanEqual(linear, vec3(0.0031308f)); - - vec3 loPart = linear * 12.92f; - vec3 hiPart = pow(linear, vec3(5.0f / 12.0f)) * 1.055f - 0.055f; - return mix(hiPart, loPart, isLo); -} - -vec3 encodePq(vec3 nits) { - const float c1 = 0.8359375f; - const float c2 = 18.8515625f; - const float c3 = 18.6875f; - const float m1 = 0.1593017578125f; - const float m2 = 78.84375f; - - vec3 y = clamp(nits / 10000.0f, vec3(0.0f), vec3(1.0f)); - vec3 y_m1 = pow(y, vec3(m1)); - - vec3 num = c1 + c2 * y_m1; - vec3 den = 1.0f + c3 * y_m1; - - return pow(num / den, vec3(m2)); -} - -vec3 encodeScRgb(vec3 nits) { - return nits / 80.0f; -} - -vec3 encodeOutput(vec3 linear) { - switch (hud_color_space) { - default: - return linear; - - case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: - return encodeSrgb(linear); - - case VK_COLOR_SPACE_HDR10_ST2084_EXT: { - vec3 rec2020 = rec709_to_rec2020 * linear; - return encodePq(rec2020 * HUD_NITS); + return color; } case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT: - return encodeScRgb(linear * HUD_NITS); + color.rgb = nits_to_sc_rgb(color.rgb * SDR_NITS); + return color; + + case VK_COLOR_SPACE_HDR10_ST2084_EXT: + color.rgb = rec709_to_rec2020 * color.rgb; + color.rgb = nits_to_pq(color.rgb * SDR_NITS); + return color; + + default: /* pass through */ + return color; } } diff --git a/src/dxvk/hud/shaders/hud_frame_time_eval.comp b/src/dxvk/hud/shaders/hud_frame_time_eval.comp new file mode 100644 index 000000000..3d5306722 --- /dev/null +++ b/src/dxvk/hud/shaders/hud_frame_time_eval.comp @@ -0,0 +1,197 @@ +#version 460 + +#extension GL_KHR_memory_scope_semantics : enable + +#define NUM_FRAME_TIME_STAMPS (420u) + +#define MAX_TEXT_LENGTH (16u) +#define MAX_TEXT_DRAWS (2u) + +struct draw_param_t { + uint vertex_count; + uint instance_count; + uint first_vertex; + uint first_instance; +}; + +struct draw_info_t { + uint text_offset; + uint text_length_and_size; + uint packed_xy; + uint color; +}; + +layout(binding = 0, std430) +workgroupcoherent buffer timestamp_buffer_t { + uvec2 frame_timestamps_raw[2u]; + float frame_interval_ms[NUM_FRAME_TIME_STAMPS]; + + float frame_time_avg_ms; + float frame_time_min_ms; + float frame_time_max_ms; +}; + +layout(binding = 1, std430) +writeonly buffer draw_param_buffer_t { + draw_param_t draw_params[]; +}; + +layout(binding = 2, std430) +writeonly buffer draw_info_buffer_t { + draw_info_t draw_infos[]; +}; + +layout(binding = 3) +uniform writeonly uimageBuffer text_buffer; + +layout(push_constant) +uniform push_data_t { + float ms_per_tick; + uint curr_data_point; + uint packed_xy_for_min; + uint packed_xy_for_max; +}; + + +layout(local_size_x = 256) in; + +uint extract_digit(inout float number) { + float high_part = floor(number / 10.0f + 0.03125f); + float digit = number - 10.0f * high_part; + number = high_part; + return uint(digit); +} + +// Three-way reduction: Sum, Min, Max +shared vec3 ms_shared[NUM_FRAME_TIME_STAMPS / 2u]; + +shared uint text_chars[MAX_TEXT_DRAWS][MAX_TEXT_LENGTH]; +shared uint text_length[MAX_TEXT_DRAWS]; + +void main() { + uint tid = gl_LocalInvocationIndex; + + if (tid == 0u) { + uint curr_index = curr_data_point & 1u; + uint prev_index = curr_index ^ 1u; + + uvec2 curr_time = frame_timestamps_raw[curr_index]; + uvec2 prev_time = frame_timestamps_raw[prev_index]; + + // We can't require 64-bit integer support, just do this manually + // and account for the possibility of timestamps wrapping around. + uvec2 borrow; + + uvec2 time_diff; + time_diff.x = usubBorrow(curr_time.x, prev_time.x, borrow.x); + time_diff.y = usubBorrow(curr_time.y, prev_time.y + borrow.x, borrow.y); + + if (borrow.y != 0u) + time_diff = uvec2(0u); + + // Ignore first frame that has no timestamp at all + if ((prev_time.x | prev_time.y) == 0u) + time_diff = uvec2(0u); + + // We will most likely lose a few bits here, but that's fine + float ticks = time_diff.x + (time_diff.y * pow(2.0f, 32.0f)); + frame_interval_ms[curr_data_point] = ticks * ms_per_tick; + } + + controlBarrier(gl_ScopeWorkgroup, gl_ScopeWorkgroup, + gl_StorageSemanticsBuffer, gl_SemanticsAcquireRelease); + + // Perform initial reduction on frame interval data and write + // everything to shared memory. There are more efficient ways + // to do reductions, but we cannot require a lot of shader + // features just for the frame time HUD. + if (2u * tid < NUM_FRAME_TIME_STAMPS) { + float a = frame_interval_ms[2u * tid + 0u]; + float b = frame_interval_ms[2u * tid + 1u]; + + ms_shared[tid] = vec3(a + b, min(a, b), max(a, b)); + } + + barrier(); + + uint input_count = NUM_FRAME_TIME_STAMPS / 2u; + + while (input_count > 1u) { + uint output_count = (input_count + 1u) / 2u; + + if (tid + output_count < input_count) { + vec3 a = ms_shared[tid]; + vec3 b = ms_shared[tid + output_count]; + + ms_shared[tid] = vec3(a.x + b.x, + min(a.y, b.y), max(a.z, b.z)); + } + + barrier(); + + input_count = output_count; + } + + // Write reduced stats to buffer + vec3 stats = ms_shared[0u]; + + if (tid == 0u) { + frame_time_avg_ms = stats.x / float(NUM_FRAME_TIME_STAMPS); + frame_time_min_ms = stats.y; + frame_time_max_ms = stats.z; + } + + if (tid < MAX_TEXT_DRAWS) { + // Convert number to string with a single decimal point. This + // is fairly naive code, but this is not performance-critical. + text_chars[tid][0] = 0x73; // 's' + text_chars[tid][1] = 0x6d; // 'm' + text_chars[tid][2] = 0x20; // ' ' + + uint zero = 0x30; // '0' + + float number = round(10.0f * (tid == 0u ? stats.y : stats.z)); + text_chars[tid][3] = zero + extract_digit(number); + text_chars[tid][4] = 0x2e; // '.' + text_chars[tid][5] = zero + extract_digit(number); + + uint len = 6u; + + while (number > 0.0f && len < MAX_TEXT_LENGTH) + text_chars[tid][len++] = zero + extract_digit(number); + + text_length[tid] = len; + + // Emit draw infos for the text + draw_infos[tid].text_offset = MAX_TEXT_LENGTH * tid; + draw_infos[tid].text_length_and_size = len | (12u << 16u); + draw_infos[tid].packed_xy = tid == 0u + ? packed_xy_for_min : packed_xy_for_max; + draw_infos[tid].color = 0xffffffffu; + + // Emit indirect draw parameters + draw_params[tid].vertex_count = 6u * len; + draw_params[tid].instance_count = 1u; + draw_params[tid].first_vertex = 0u; + draw_params[tid].first_instance = 0u; + } + + barrier(); + + // Use remaining threads to write text into the acual string + // buffer. Characters are stored in reverse order in LDS, so + // fix that up. + uint text_id = tid / MAX_TEXT_LENGTH; + uint text_ch = tid % MAX_TEXT_LENGTH; + + if (text_id < MAX_TEXT_DRAWS) { + uint len = text_length[text_id]; + + uint ch = 0x20; // ' ' + + if (text_ch < len) + ch = text_chars[text_id][len - text_ch - 1u]; + + imageStore(text_buffer, int(tid), uvec4(ch)); + } +} diff --git a/src/dxvk/hud/shaders/hud_graph_frag.frag b/src/dxvk/hud/shaders/hud_graph_frag.frag index e555d7334..34563ce8f 100644 --- a/src/dxvk/hud/shaders/hud_graph_frag.frag +++ b/src/dxvk/hud/shaders/hud_graph_frag.frag @@ -7,48 +7,80 @@ layout(location = 0) in vec2 v_coord; layout(location = 0) out vec4 o_color; -struct line_point_t { - float value; - uint color; -}; +#define NUM_FRAME_TIME_STAMPS (420) layout(binding = 0, std430) -readonly buffer line_data_t { - line_point_t points[]; +readonly buffer timestamp_buffer_t { + uvec2 frame_timestamps_raw[2u]; + float frame_interval_ms[NUM_FRAME_TIME_STAMPS]; + + float frame_time_avg_ms; + float frame_time_min_ms; + float frame_time_max_ms; }; layout(push_constant) uniform push_data_t { - uint offset; - uint count; - vec2 pos; - vec2 size; - vec2 scale; + uvec2 surface_size; float opacity; + float scale; + uint packed_xy; + uint packed_wh; + uint frame_index; }; -void main() { - float cx = v_coord.x * float(count); - float fx = fract(cx); +int max_index = NUM_FRAME_TIME_STAMPS - 1; - // We need to roll our own linear interpolation here - uint p0_idx = min(uint(floor(cx)), count - 1) + offset; - uint p1_idx = min(uint(ceil(cx)), count - 1) + offset; +int compute_real_index(int local_index) { + int real_index = int(frame_index) - local_index; - line_point_t p0 = points[p0_idx]; - line_point_t p1 = points[p1_idx]; + if (real_index < 0) + real_index += NUM_FRAME_TIME_STAMPS; - float value = mix(p0.value, p1.value, fx); - float alpha = value + v_coord.y - 1.0f; - alpha = min(alpha / dFdy(v_coord.y), 1.0f); - - if (alpha <= 0.0f) - discard; - - o_color = mix( - unpackUnorm4x8(p0.color), - unpackUnorm4x8(p1.color), fx); - o_color *= alpha * opacity; - - o_color.rgb = encodeOutput(o_color.rgb); + return real_index; +} + +float sample_at(float position) { + int local_index = int(position); + + int lo_index = compute_real_index(clamp(local_index, 0, max_index)); + int hi_index = compute_real_index(clamp(local_index + 1, 0, max_index)); + + float lo_value = frame_interval_ms[lo_index]; + float hi_value = frame_interval_ms[hi_index]; + + return mix(lo_value, hi_value, fract(position)); +} + +void main() { + float x_pos = (1.0f - v_coord.x) * float(NUM_FRAME_TIME_STAMPS); + + float x_delta = abs(dFdx(x_pos)) / 2.0f; + float y_delta = abs(dFdy(v_coord.y)); + + float ms_l = sample_at(x_pos - x_delta); + float ms_r = sample_at(x_pos + x_delta); + + float ms_lo = min(ms_l, ms_r); + float ms_hi = max(ms_l, ms_r); + + float ms_max = clamp(max(frame_time_max_ms, frame_time_avg_ms * 2.0f), 20.0f, 200.0f); + + float val_lo = min(ms_lo / ms_max, 1.0f) - y_delta; + float val_hi = min(ms_hi / ms_max, 1.0f) - y_delta; + + float diff_lo = min(v_coord.y - val_lo, 0.0f); + float diff_hi = min(val_hi - v_coord.y, 0.0f); + + float ms_y = ms_max * v_coord.y; + + vec4 line_color = vec4(0.776f, 0.812f, 0.882f, 1.0f); + vec4 bg_color = vec4(0.0f, 0.0f, 0.0f, 0.75f); + + // Try to draw a somewhat defined line + float diff = (diff_lo + diff_hi) + y_delta; + o_color = mix(bg_color, line_color, clamp(diff / y_delta, 0.0f, 1.0f)); + + o_color = linear_to_output(o_color); + o_color.a *= opacity; } diff --git a/src/dxvk/hud/shaders/hud_graph_vert.vert b/src/dxvk/hud/shaders/hud_graph_vert.vert index 44f3e83ca..2e49e8481 100644 --- a/src/dxvk/hud/shaders/hud_graph_vert.vert +++ b/src/dxvk/hud/shaders/hud_graph_vert.vert @@ -4,21 +4,35 @@ layout(location = 0) out vec2 o_coord; layout(push_constant) uniform push_data_t { - uint offset; - uint count; - vec2 pos; - vec2 size; - vec2 scale; + uvec2 surface_size; float opacity; + float scale; + uint packed_xy; + uint packed_wh; + uint frame_index; }; +vec2 unpack_u16(uint v) { + // Inputs may be signed + int hi = int(v); + int lo = int(v << 16); + return vec2(float(lo >> 16), float(hi >> 16)); +} + void main() { vec2 coord = vec2( float(gl_VertexIndex & 1), float(gl_VertexIndex >> 1)); - o_coord = coord; + o_coord = vec2(coord.x, 1.0f - coord.y); + + vec2 surface_size_f = vec2(surface_size) / scale; + + vec2 pos = unpack_u16(packed_xy); + pos = mix(pos, surface_size_f + pos, lessThan(pos, vec2(0.0f))); + + vec2 size = unpack_u16(packed_wh); vec2 pixel_pos = pos + size * coord; - vec2 scaled_pos = 2.0f * scale * pixel_pos - 1.0f; + vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f; gl_Position = vec4(scaled_pos, 0.0f, 1.0f); } diff --git a/src/dxvk/hud/shaders/hud_text_frag.frag b/src/dxvk/hud/shaders/hud_text_frag.frag index 83e23dd47..3f37f48d0 100644 --- a/src/dxvk/hud/shaders/hud_text_frag.frag +++ b/src/dxvk/hud/shaders/hud_text_frag.frag @@ -4,7 +4,14 @@ #include "hud_frag_common.glsl" -layout(binding = 2) uniform sampler2D s_font; +layout(binding = 3) uniform sampler2D s_font; + +layout(push_constant) +uniform push_data_t { + uvec2 surface_size; + float opacity; + float scale; +}; layout(location = 0) in vec2 v_texcoord; layout(location = 1) in vec4 v_color; @@ -25,8 +32,8 @@ void main() { vec3 r_shadow = vec3(0.0f, 0.0f, 0.0f); o_color.rgb = mix(r_shadow, r_center, r_alpha_center); - o_color.a = r_alpha_shadow * v_color.a; + o_color.a = r_alpha_shadow * v_color.a * opacity; o_color.rgb *= o_color.a; - o_color.rgb = encodeOutput(o_color.rgb); + o_color = linear_to_output(o_color); } diff --git a/src/dxvk/hud/shaders/hud_text_vert.vert b/src/dxvk/hud/shaders/hud_text_vert.vert index c0b0f1753..f63bb967e 100644 --- a/src/dxvk/hud/shaders/hud_text_vert.vert +++ b/src/dxvk/hud/shaders/hud_text_vert.vert @@ -1,4 +1,4 @@ -#version 450 +#version 460 struct font_info_t { float size; @@ -18,15 +18,25 @@ readonly buffer font_buffer_t { glyph_info_t glyph_data[]; }; -layout(binding = 1) uniform usamplerBuffer text_buffer; +struct draw_info_t { + uint text_offset; + uint text_length_and_size; + uint packed_xy; + uint color; +}; + +layout(binding = 1, std430) +readonly buffer draw_buffer_t { + draw_info_t draw_infos[]; +}; + +layout(binding = 2) uniform usamplerBuffer text_buffer; layout(push_constant) uniform push_data_t { - vec4 text_color; - vec2 text_pos; - uint text_offset; - float text_size; - vec2 hud_scale; + uvec2 surface_size; + float opacity; + float scale; }; layout(location = 0) out vec2 o_texcoord; @@ -42,7 +52,8 @@ vec2 unpack_u16(uint v) { } void main() { - o_color = text_color; + draw_info_t draw_info = draw_infos[gl_DrawID]; + o_color = unpackUnorm4x8(draw_info.color); // Compute character index and vertex index for the current // character. We'll render two triangles per character. @@ -50,7 +61,7 @@ void main() { uint vtx_idx = gl_VertexIndex - 6 * chr_idx; // Load glyph info based on vertex index - uint glyph_idx = texelFetch(text_buffer, int(text_offset + chr_idx)).x; + uint glyph_idx = texelFetch(text_buffer, int(draw_info.text_offset + chr_idx)).x; glyph_info_t glyph_info = glyph_data[glyph_idx]; // Compute texture coordinate from glyph data @@ -64,12 +75,18 @@ void main() { // Compute vertex position. We can easily do this here since our // font is a monospace font, otherwise we'd need to preprocess // the strings to render in a compute shader. - float size_factor = text_size / font_data.size; + uint text_size = bitfieldExtract(draw_info.text_length_and_size, 16, 16); + float size_factor = float(text_size) / font_data.size; + + vec2 surface_size_f = vec2(surface_size) / scale; + + vec2 text_pos = unpack_u16(draw_info.packed_xy); + text_pos = mix(text_pos, surface_size_f + text_pos, lessThan(text_pos, vec2(0.0f))); vec2 local_pos = tex_wh * coord - unpack_u16(glyph_info.packed_origin) + vec2(font_data.advance * float(chr_idx), 0.0f); vec2 pixel_pos = text_pos + size_factor * local_pos; - vec2 scaled_pos = 2.0f * hud_scale * pixel_pos - 1.0f; + vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f; gl_Position = vec4(scaled_pos, 0.0f, 1.0f); } diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index ee5fac5e2..a696fff60 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -53,7 +53,10 @@ dxvk_shaders = files([ 'hud/shaders/hud_chunk_frag_background.frag', 'hud/shaders/hud_chunk_frag_visualize.frag', - 'hud/shaders/hud_chunk_vert.vert', + 'hud/shaders/hud_chunk_vert_background.vert', + 'hud/shaders/hud_chunk_vert_visualize.vert', + + 'hud/shaders/hud_frame_time_eval.comp', 'hud/shaders/hud_graph_frag.frag', 'hud/shaders/hud_graph_vert.vert',