From 9355580c4f5abce073dd5424d3fbd5931eefd99d Mon Sep 17 00:00:00 2001
From: Philip Rebohle <philip.rebohle@tu-dortmund.de>
Date: Tue, 7 May 2019 22:05:35 +0200
Subject: [PATCH] [hud] Optimize HUD rendering

Saves some bandwidth by using more compact vertex formats, and
by using push constants for text colors instead of a vertex
attribute.
---
 src/dxvk/hud/dxvk_hud_fps.cpp                 |  12 +-
 src/dxvk/hud/dxvk_hud_renderer.cpp            | 258 ++++++++++--------
 src/dxvk/hud/dxvk_hud_renderer.h              |  61 +++--
 .../{hud_line.frag => hud_line_frag.frag}     |   2 +-
 src/dxvk/hud/shaders/hud_line_vert.vert       |  18 ++
 .../{hud_text.frag => hud_text_frag.frag}     |   9 +-
 .../{hud_vert.vert => hud_text_vert.vert}     |   3 -
 src/dxvk/meson.build                          |   8 +-
 8 files changed, 228 insertions(+), 143 deletions(-)
 rename src/dxvk/hud/shaders/{hud_line.frag => hud_line_frag.frag} (76%)
 create mode 100644 src/dxvk/hud/shaders/hud_line_vert.vert
 rename src/dxvk/hud/shaders/{hud_text.frag => hud_text_frag.frag} (84%)
 rename src/dxvk/hud/shaders/{hud_vert.vert => hud_text_vert.vert} (78%)

diff --git a/src/dxvk/hud/dxvk_hud_fps.cpp b/src/dxvk/hud/dxvk_hud_fps.cpp
index d01a8ba4..f8cb6e7d 100644
--- a/src/dxvk/hud/dxvk_hud_fps.cpp
+++ b/src/dxvk/hud/dxvk_hud_fps.cpp
@@ -77,7 +77,7 @@ namespace dxvk::hud {
     const Rc<DxvkContext>&  context,
           HudRenderer&      renderer,
           HudPos            position) {
-    std::array<HudVertex, NumDataPoints * 2> vData;
+    std::array<HudLineVertex, NumDataPoints * 2> vData;
     
     // 60 FPS = optimal, 10 FPS = worst
     const float targetUs =  16'666.6f;
@@ -100,8 +100,10 @@ namespace dxvk::hud {
       float g = std::min(std::max( 3.0f - us / targetUs, 0.0f), 1.0f);
       float l = std::sqrt(r * r + g * g);
       
-      HudTexCoord tc = { 0u, 0u };
-      HudColor color = { r / l, g / l, 0.0f, 1.0f };
+      HudNormColor color = {
+        uint8_t(255.0f * (r / l)),
+        uint8_t(255.0f * (g / l)),
+        uint8_t(0), uint8_t(255) };
       
       float x = position.x + float(i);
       float y = position.y + 24.0f;
@@ -110,8 +112,8 @@ namespace dxvk::hud {
                  / std::log2((maxUs - minUs) / targetUs);
       float h = std::min(std::max(40.0f * hVal, 2.0f), 40.0f);
       
-      vData[2 * i + 0] = HudVertex { { x, y     }, tc, color };
-      vData[2 * i + 1] = HudVertex { { x, y - h }, tc, color };
+      vData[2 * i + 0] = HudLineVertex { { x, y     }, color };
+      vData[2 * i + 1] = HudLineVertex { { x, y - h }, color };
     }
     
     renderer.drawLines(context, vData.size(), vData.data());
diff --git a/src/dxvk/hud/dxvk_hud_renderer.cpp b/src/dxvk/hud/dxvk_hud_renderer.cpp
index 4964fb99..ad99ddc4 100644
--- a/src/dxvk/hud/dxvk_hud_renderer.cpp
+++ b/src/dxvk/hud/dxvk_hud_renderer.cpp
@@ -1,17 +1,18 @@
 #include "dxvk_hud_renderer.h"
 
-#include <hud_line.h>
-#include <hud_text.h>
-#include <hud_vert.h>
+#include <hud_line_frag.h>
+#include <hud_line_vert.h>
+
+#include <hud_text_frag.h>
+#include <hud_text_vert.h>
 
 namespace dxvk::hud {
   
   HudRenderer::HudRenderer(const Rc<DxvkDevice>& device)
   : m_mode          (Mode::RenderNone),
     m_surfaceSize   { 0, 0 },
-    m_vertShader    (createVertexShader(device)),
-    m_textShader    (createTextShader(device)),
-    m_lineShader    (createLineShader(device)),
+    m_textShaders   (createTextShaders(device)),
+    m_lineShaders   (createLineShaders(device)),
     m_fontImage     (createFontImage(device)),
     m_fontView      (createFontView(device)),
     m_fontSampler   (createFontSampler(device)),
@@ -30,32 +31,11 @@ namespace dxvk::hud {
     auto vertexSlice = m_vertexBuffer->allocSlice();
     context->invalidateBuffer(m_vertexBuffer, vertexSlice);
     
-    const std::array<DxvkVertexAttribute, 3> ilAttributes = {{
-      { 0, 0, VK_FORMAT_R32G32_SFLOAT,       offsetof(HudVertex, position) },
-      { 1, 0, VK_FORMAT_R32G32_UINT,         offsetof(HudVertex, texcoord) },
-      { 2, 0, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(HudVertex, color)    },
-    }};
-    
-    const std::array<DxvkVertexBinding, 1> ilBindings = {{
-      { 0, VK_VERTEX_INPUT_RATE_VERTEX },
-    }};
-    
-    context->setInputLayout(
-      ilAttributes.size(),
-      ilAttributes.data(),
-      ilBindings.size(),
-      ilBindings.data());
-    
-    context->bindVertexBuffer(0,
-      DxvkBufferSlice(m_vertexBuffer),
-      sizeof(HudVertex));
-    
     context->bindResourceSampler(1, m_fontSampler);
     context->bindResourceView   (1, m_fontView, nullptr);
     
     m_mode        = Mode::RenderNone;
     m_surfaceSize = surfaceSize;
-    m_vertexIndex = 0;
   }
   
   
@@ -65,12 +45,19 @@ namespace dxvk::hud {
           HudPos            pos,
           HudColor          color,
     const std::string&      text) {
-    this->setRenderMode(context, Mode::RenderText);
+    beginTextRendering(context);
+
+    uint32_t vertexCount = 6 * text.size();
+
+    auto vertexSlice = allocVertexBuffer(context,
+      vertexCount * sizeof(HudTextVertex));
     
-    const size_t vertexIndex = m_vertexIndex;
-    
-    HudVertex* vertexData = reinterpret_cast<HudVertex*>(
-      m_vertexBuffer->mapPtr(vertexIndex * sizeof(HudVertex)));
+    context->bindVertexBuffer(0, vertexSlice, sizeof(HudTextVertex));
+    context->pushConstants(0, sizeof(color), &color);
+    context->draw(vertexCount, 1, 0, 0);
+
+    auto vertexData = reinterpret_cast<HudTextVertex*>(
+      vertexSlice.getSliceHandle().mapPtr);
     
     const float sizeFactor = size / static_cast<float>(g_hudFont.size);
     
@@ -99,132 +86,183 @@ namespace dxvk::hud {
       
       vertexData[6 * i + 0].position = { posTl.x, posTl.y };
       vertexData[6 * i + 0].texcoord = { texTl.u, texTl.v };
-      vertexData[6 * i + 0].color    = color;
       
       vertexData[6 * i + 1].position = { posBr.x, posTl.y };
       vertexData[6 * i + 1].texcoord = { texBr.u, texTl.v };
-      vertexData[6 * i + 1].color    = color;
       
       vertexData[6 * i + 2].position = { posTl.x, posBr.y };
       vertexData[6 * i + 2].texcoord = { texTl.u, texBr.v };
-      vertexData[6 * i + 2].color    = color;
       
       vertexData[6 * i + 3].position = { posBr.x, posBr.y };
       vertexData[6 * i + 3].texcoord = { texBr.u, texBr.v };
-      vertexData[6 * i + 3].color    = color;
       
       vertexData[6 * i + 4].position = { posTl.x, posBr.y };
       vertexData[6 * i + 4].texcoord = { texTl.u, texBr.v };
-      vertexData[6 * i + 4].color    = color;
       
       vertexData[6 * i + 5].position = { posBr.x, posTl.y };
       vertexData[6 * i + 5].texcoord = { texBr.u, texTl.v };
-      vertexData[6 * i + 5].color    = color;
       
       pos.x += sizeFactor * static_cast<float>(g_hudFont.advance);
     }
-    
-    const uint32_t vertexCount = 6 * text.size();
-    context->draw(vertexCount, 1, vertexIndex, 0);
-    m_vertexIndex += vertexCount;
   }
   
   
   void HudRenderer::drawLines(
     const Rc<DxvkContext>&  context,
           size_t            vertexCount,
-    const HudVertex*        vertexData) {
-    this->setRenderMode(context, Mode::RenderLines);
-    const size_t vertexIndex = m_vertexIndex;
+    const HudLineVertex*    vertexData) {
+    beginLineRendering(context);
+
+    auto vertexSlice = allocVertexBuffer(context,
+      vertexCount * sizeof(HudLineVertex));
     
-    HudVertex* dstVertexData = reinterpret_cast<HudVertex*>(
-      m_vertexBuffer->mapPtr(vertexIndex * sizeof(HudVertex)));
+    context->bindVertexBuffer(0, vertexSlice, sizeof(HudLineVertex));
+    context->draw(vertexCount, 1, 0, 0);
+    
+    auto dstVertexData = reinterpret_cast<HudLineVertex*>(
+      vertexSlice.getSliceHandle().mapPtr);
     
     for (size_t i = 0; i < vertexCount; i++)
       dstVertexData[i] = vertexData[i];
-    
-    context->draw(vertexCount, 1, vertexIndex, 0);
-    m_vertexIndex += vertexCount;
   }
   
   
-  void HudRenderer::setRenderMode(
+  DxvkBufferSlice HudRenderer::allocVertexBuffer(
     const Rc<DxvkContext>&  context,
-          Mode              mode) {
-    if (m_mode != mode) {
-      m_mode = mode;
-    
-      switch (mode) {
-        case Mode::RenderNone:
-          break;
-        
-        case Mode::RenderText: {
-          context->bindShader(VK_SHADER_STAGE_VERTEX_BIT,   m_vertShader);
-          context->bindShader(VK_SHADER_STAGE_FRAGMENT_BIT, m_textShader);
-          
-          DxvkInputAssemblyState iaState;
-          iaState.primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
-          iaState.primitiveRestart  = VK_FALSE;
-          iaState.patchVertexCount  = 0;
-          context->setInputAssemblyState(iaState);
-        } break;
-        
-        case Mode::RenderLines: {
-          context->bindShader(VK_SHADER_STAGE_VERTEX_BIT,   m_vertShader);
-          context->bindShader(VK_SHADER_STAGE_FRAGMENT_BIT, m_lineShader);
-          
-          DxvkInputAssemblyState iaState;
-          iaState.primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
-          iaState.primitiveRestart  = VK_FALSE;
-          iaState.patchVertexCount  = 0;
-          context->setInputAssemblyState(iaState);
-        } break;
-      }
+          VkDeviceSize      dataSize) {
+    dataSize = align(dataSize, 64);
+
+    if (m_vertexOffset + dataSize > m_vertexBuffer->info().size) {
+      context->invalidateBuffer(m_vertexBuffer, m_vertexBuffer->allocSlice());
+      m_vertexOffset = 0;
+    }
+
+    DxvkBufferSlice slice(m_vertexBuffer, m_vertexOffset, dataSize);
+    m_vertexOffset += dataSize;
+    return slice;
+  }
+  
+
+  void HudRenderer::beginTextRendering(
+    const Rc<DxvkContext>&  context) {
+    if (m_mode != Mode::RenderText) {
+      m_mode = Mode::RenderText;
+
+      context->bindShader(VK_SHADER_STAGE_VERTEX_BIT,   m_textShaders.vert);
+      context->bindShader(VK_SHADER_STAGE_FRAGMENT_BIT, m_textShaders.frag);
+      
+      static const DxvkInputAssemblyState iaState = {
+        VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+        VK_FALSE, 0 };
+
+      static const std::array<DxvkVertexAttribute, 2> ilAttributes = {{
+        { 0, 0, VK_FORMAT_R32G32_SFLOAT,       offsetof(HudTextVertex, position) },
+        { 1, 0, VK_FORMAT_R32G32_UINT,         offsetof(HudTextVertex, texcoord) },
+      }};
+      
+      static const std::array<DxvkVertexBinding, 1> ilBindings = {{
+        { 0, VK_VERTEX_INPUT_RATE_VERTEX },
+      }};
+      
+      context->setInputAssemblyState(iaState);
+      context->setInputLayout(
+        ilAttributes.size(),
+        ilAttributes.data(),
+        ilBindings.size(),
+        ilBindings.data());
+    }
+  }
+
+  
+  void HudRenderer::beginLineRendering(
+    const Rc<DxvkContext>&  context) {
+    if (m_mode != Mode::RenderLines) {
+      m_mode = Mode::RenderLines;
+
+      context->bindShader(VK_SHADER_STAGE_VERTEX_BIT,   m_lineShaders.vert);
+      context->bindShader(VK_SHADER_STAGE_FRAGMENT_BIT, m_lineShaders.frag);
+      
+      static const DxvkInputAssemblyState iaState = {
+        VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
+        VK_FALSE, 0 };
+
+      static const std::array<DxvkVertexAttribute, 2> ilAttributes = {{
+        { 0, 0, VK_FORMAT_R32G32_SFLOAT,  offsetof(HudLineVertex, position) },
+        { 1, 0, VK_FORMAT_R8G8B8A8_UNORM, offsetof(HudLineVertex, color)    },
+      }};
+      
+      static const std::array<DxvkVertexBinding, 1> ilBindings = {{
+        { 0, VK_VERTEX_INPUT_RATE_VERTEX },
+      }};
+      
+      context->setInputAssemblyState(iaState);
+      context->setInputLayout(
+        ilAttributes.size(),
+        ilAttributes.data(),
+        ilBindings.size(),
+        ilBindings.data());
     }
   }
   
-  
-  Rc<DxvkShader> HudRenderer::createVertexShader(const Rc<DxvkDevice>& device) {
-    const SpirvCodeBuffer codeBuffer(hud_vert);
+
+  HudRenderer::ShaderPair HudRenderer::createTextShaders(const Rc<DxvkDevice>& device) {
+    ShaderPair result;
+
+    const SpirvCodeBuffer vsCode(hud_text_vert);
+    const SpirvCodeBuffer fsCode(hud_text_frag);
     
     // One shader resource: Global HUD uniform buffer
-    const std::array<DxvkResourceSlot, 1> resourceSlots = {{
+    const std::array<DxvkResourceSlot, 1> vsResources = {{
       { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_IMAGE_VIEW_TYPE_MAX_ENUM },
     }};
-    
-    return device->createShader(
-      VK_SHADER_STAGE_VERTEX_BIT,
-      resourceSlots.size(),
-      resourceSlots.data(),
-      { 0x7, 0x3 },
-      codeBuffer);
-  }
-  
-  
-  Rc<DxvkShader> HudRenderer::createTextShader(const Rc<DxvkDevice>& device) {
-    const SpirvCodeBuffer codeBuffer(hud_text);
-    
+
     // Two shader resources: Font texture and sampler
-    const std::array<DxvkResourceSlot, 1> resourceSlots = {{
+    const std::array<DxvkResourceSlot, 1> fsResources = {{
       { 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_IMAGE_VIEW_TYPE_2D },
     }};
     
-    return device->createShader(
-      VK_SHADER_STAGE_FRAGMENT_BIT,
-      resourceSlots.size(),
-      resourceSlots.data(),
+    result.vert = device->createShader(
+      VK_SHADER_STAGE_VERTEX_BIT,
+      vsResources.size(),
+      vsResources.data(),
       { 0x3, 0x1 },
-      codeBuffer);
+      vsCode);
+    
+    result.frag = device->createShader(
+      VK_SHADER_STAGE_FRAGMENT_BIT,
+      fsResources.size(),
+      fsResources.data(),
+      { 0x1, 0x1, 0, sizeof(HudColor) },
+      fsCode);
+    
+    return result;
   }
   
   
-  Rc<DxvkShader> HudRenderer::createLineShader(const Rc<DxvkDevice>& device) {
-    const SpirvCodeBuffer codeBuffer(hud_line);
+  HudRenderer::ShaderPair HudRenderer::createLineShaders(const Rc<DxvkDevice>& device) {
+    ShaderPair result;
+
+    const SpirvCodeBuffer vsCode(hud_line_vert);
+    const SpirvCodeBuffer fsCode(hud_line_frag);
     
-    return device->createShader(
+    // One shader resource: Global HUD uniform buffer
+    const std::array<DxvkResourceSlot, 1> vsResources = {{
+      { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_IMAGE_VIEW_TYPE_MAX_ENUM },
+    }};
+
+    result.vert = device->createShader(
+      VK_SHADER_STAGE_VERTEX_BIT,
+      vsResources.size(),
+      vsResources.data(),
+      { 0x3, 0x1 },
+      vsCode);
+    
+    result.frag = device->createShader(
       VK_SHADER_STAGE_FRAGMENT_BIT,
-      0, nullptr, { 0x2, 0x1 },
-      codeBuffer);
+      0, nullptr, { 0x1, 0x1 },
+      fsCode);
+    
+    return result;
   }
   
   
@@ -289,7 +327,7 @@ namespace dxvk::hud {
   
   Rc<DxvkBuffer> HudRenderer::createVertexBuffer(const Rc<DxvkDevice>& device) {
     DxvkBufferCreateInfo info;
-    info.size           = MaxVertexCount * sizeof(HudVertex);
+    info.size           = 1 << 16;
     info.usage          = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
     info.stages         = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
     info.access         = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
diff --git a/src/dxvk/hud/dxvk_hud_renderer.h b/src/dxvk/hud/dxvk_hud_renderer.h
index 41a7fa00..8d30091a 100644
--- a/src/dxvk/hud/dxvk_hud_renderer.h
+++ b/src/dxvk/hud/dxvk_hud_renderer.h
@@ -40,14 +40,32 @@ namespace dxvk::hud {
     float b;
     float a;
   };
+
+  /**
+   * \brief Normalized color
+   * SRGB color with alpha channel.
+   */
+  struct HudNormColor {
+    uint8_t r;
+    uint8_t g;
+    uint8_t b;
+    uint8_t a;
+  };
   
   /**
-   * \brief Vertex
+   * \brief Text vertex and texture coordinates
    */
-  struct HudVertex {
-    HudPos      position;
-    HudTexCoord texcoord;
-    HudColor    color;
+  struct HudTextVertex {
+    HudPos        position;
+    HudTexCoord   texcoord;
+  };
+
+  /**
+   * \brief Line vertex and color
+   */
+  struct HudLineVertex {
+    HudPos        position;
+    HudNormColor  color;
   };
   
   /**
@@ -57,7 +75,7 @@ namespace dxvk::hud {
    * display performance and driver information.
    */
   class HudRenderer {
-    constexpr static VkDeviceSize MaxVertexCount = 1 << 16;
+
   public:
     
     HudRenderer(
@@ -79,7 +97,7 @@ namespace dxvk::hud {
     void drawLines(
       const Rc<DxvkContext>&  context,
             size_t            vertexCount,
-      const HudVertex*        vertexData);
+      const HudLineVertex*    vertexData);
     
     VkExtent2D surfaceSize() const {
       return m_surfaceSize;
@@ -92,34 +110,41 @@ namespace dxvk::hud {
       RenderText,
       RenderLines,
     };
+
+    struct ShaderPair {
+      Rc<DxvkShader> vert;
+      Rc<DxvkShader> frag;
+    };
     
     std::array<uint8_t, 256> m_charMap;
     
     Mode                m_mode;
     VkExtent2D          m_surfaceSize;
     
-    Rc<DxvkShader>      m_vertShader;
-    Rc<DxvkShader>      m_textShader;
-    Rc<DxvkShader>      m_lineShader;
+    ShaderPair          m_textShaders;
+    ShaderPair          m_lineShaders;
     
     Rc<DxvkImage>       m_fontImage;
     Rc<DxvkImageView>   m_fontView;
     Rc<DxvkSampler>     m_fontSampler;
     
     Rc<DxvkBuffer>      m_vertexBuffer;
-    size_t              m_vertexIndex = 0;
+    VkDeviceSize        m_vertexOffset = 0;
     
-    void setRenderMode(
+    DxvkBufferSlice allocVertexBuffer(
       const Rc<DxvkContext>&  context,
-            Mode              mode);
+            VkDeviceSize      dataSize);
+
+    void beginTextRendering(
+      const Rc<DxvkContext>&  context);
     
-    Rc<DxvkShader> createVertexShader(
+    void beginLineRendering(
+      const Rc<DxvkContext>&  context);
+    
+    ShaderPair createTextShaders(
       const Rc<DxvkDevice>& device);
     
-    Rc<DxvkShader> createTextShader(
-      const Rc<DxvkDevice>& device);
-    
-    Rc<DxvkShader> createLineShader(
+    ShaderPair createLineShaders(
       const Rc<DxvkDevice>& device);
     
     Rc<DxvkImage> createFontImage(
diff --git a/src/dxvk/hud/shaders/hud_line.frag b/src/dxvk/hud/shaders/hud_line_frag.frag
similarity index 76%
rename from src/dxvk/hud/shaders/hud_line.frag
rename to src/dxvk/hud/shaders/hud_line_frag.frag
index 2612605a..7e4843ab 100644
--- a/src/dxvk/hud/shaders/hud_line.frag
+++ b/src/dxvk/hud/shaders/hud_line_frag.frag
@@ -1,6 +1,6 @@
 #version 450
 
-layout(location = 1) in  vec4 v_color;
+layout(location = 0) in  vec4 v_color;
 layout(location = 0) out vec4 o_color;
 
 void main() {
diff --git a/src/dxvk/hud/shaders/hud_line_vert.vert b/src/dxvk/hud/shaders/hud_line_vert.vert
new file mode 100644
index 00000000..09139158
--- /dev/null
+++ b/src/dxvk/hud/shaders/hud_line_vert.vert
@@ -0,0 +1,18 @@
+#version 450
+
+layout(set = 0, binding = 0, std140)
+uniform u_hud {
+  uvec2 size;
+} g_hud;
+
+layout(location = 0) in  vec2 v_position;
+layout(location = 1) in  vec4 v_color;
+
+layout(location = 0) out vec4 o_color;
+
+void main() {
+  o_color = v_color;
+  
+  vec2 pos = 2.0f * (v_position / vec2(g_hud.size)) - 1.0f;
+  gl_Position = vec4(pos, 0.0f, 1.0f);
+}
\ No newline at end of file
diff --git a/src/dxvk/hud/shaders/hud_text.frag b/src/dxvk/hud/shaders/hud_text_frag.frag
similarity index 84%
rename from src/dxvk/hud/shaders/hud_text.frag
rename to src/dxvk/hud/shaders/hud_text_frag.frag
index 573f014d..875474f1 100644
--- a/src/dxvk/hud/shaders/hud_text.frag
+++ b/src/dxvk/hud/shaders/hud_text_frag.frag
@@ -3,10 +3,13 @@
 layout(set = 0, binding = 1) uniform sampler2D s_font;
 
 layout(location = 0) in vec2 v_texcoord;
-layout(location = 1) in vec4 v_color;
-
 layout(location = 0) out vec4 o_color;
 
+layout(push_constant)
+uniform push_data {
+  vec4 color;
+};
+
 float sampleAlpha(float alpha_bias, float dist_range) {
   float value = texture(s_font, v_texcoord).r + alpha_bias - 0.5f;
   float dist  = value * dot(vec2(dist_range, dist_range), 1.0f / fwidth(v_texcoord.xy));
@@ -17,7 +20,7 @@ void main() {
   float r_alpha_center = sampleAlpha(0.0f, 5.0f);
   float r_alpha_shadow = sampleAlpha(0.3f, 5.0f);
   
-  vec4 r_center = vec4(v_color.rgb, v_color.a * r_alpha_center);
+  vec4 r_center = vec4(color.rgb, color.a * r_alpha_center);
   vec4 r_shadow = vec4(0.0f, 0.0f, 0.0f, r_alpha_shadow);
   
   o_color = mix(r_shadow, r_center, r_alpha_center);
diff --git a/src/dxvk/hud/shaders/hud_vert.vert b/src/dxvk/hud/shaders/hud_text_vert.vert
similarity index 78%
rename from src/dxvk/hud/shaders/hud_vert.vert
rename to src/dxvk/hud/shaders/hud_text_vert.vert
index 4c1d6ce5..3011664e 100644
--- a/src/dxvk/hud/shaders/hud_vert.vert
+++ b/src/dxvk/hud/shaders/hud_text_vert.vert
@@ -7,14 +7,11 @@ uniform u_hud {
 
 layout(location = 0) in  vec2 v_position;
 layout(location = 1) in uvec2 v_texcoord;
-layout(location = 2) in  vec4 v_color;
 
 layout(location = 0) out vec2 o_texcoord;
-layout(location = 1) out vec4 o_color;
 
 void main() {
   o_texcoord = vec2(v_texcoord);
-  o_color    = v_color;
   
   vec2 pos = 2.0f * (v_position / vec2(g_hud.size)) - 1.0f;
   gl_Position = vec4(pos, 0.0f, 1.0f);
diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build
index 0ccdbff5..64f20998 100644
--- a/src/dxvk/meson.build
+++ b/src/dxvk/meson.build
@@ -39,9 +39,11 @@ dxvk_shaders = files([
   'shaders/dxvk_unpack_d24s8.comp',
   'shaders/dxvk_unpack_d32s8.comp',
   
-  'hud/shaders/hud_line.frag',
-  'hud/shaders/hud_text.frag',
-  'hud/shaders/hud_vert.vert',
+  'hud/shaders/hud_line_frag.frag',
+  'hud/shaders/hud_line_vert.vert',
+
+  'hud/shaders/hud_text_frag.frag',
+  'hud/shaders/hud_text_vert.vert',
 ])
 
 dxvk_src = files([