diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp
index f299df2c..8dc5fcc0 100644
--- a/src/d3d11/d3d11_context.cpp
+++ b/src/d3d11/d3d11_context.cpp
@@ -2557,11 +2557,24 @@ namespace dxvk {
   void D3D11DeviceContext::BindShader(
           DxbcProgramType       ShaderStage,
     const D3D11CommonShader*    pShaderModule) {
+    // Bind the shader and the ICB at once
+    const uint32_t slotId = computeResourceSlotId(
+      ShaderStage, DxbcBindingType::ConstantBuffer,
+      D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT);
+    
     EmitCs([
+      cSlotId = slotId,
       cStage  = GetShaderStage(ShaderStage),
-      cShader = pShaderModule != nullptr ? pShaderModule->GetShader() : nullptr
+      cSlice  = pShaderModule           != nullptr
+             && pShaderModule->GetIcb() != nullptr
+        ? DxvkBufferSlice(pShaderModule->GetIcb())
+        : DxvkBufferSlice(),
+      cShader = pShaderModule != nullptr
+        ? pShaderModule->GetShader()
+        : nullptr
     ] (DxvkContext* ctx) {
-      ctx->bindShader(cStage, cShader);
+      ctx->bindShader        (cStage, cShader);
+      ctx->bindResourceBuffer(cSlotId, cSlice);
     });
   }
 
diff --git a/src/d3d11/d3d11_device.cpp b/src/d3d11/d3d11_device.cpp
index 3696f177..a1a48e69 100644
--- a/src/d3d11/d3d11_device.cpp
+++ b/src/d3d11/d3d11_device.cpp
@@ -1862,7 +1862,7 @@ namespace dxvk {
       Logger::warn("D3D11Device::CreateShaderModule: Class linkage not supported");
     
     try {
-      *pShaderModule = m_shaderModules.GetShaderModule(
+      *pShaderModule = m_shaderModules.GetShaderModule(this,
         pModuleInfo, pShaderBytecode, BytecodeLength, ProgramType);
       return S_OK;
     } catch (const DxvkError& e) {
diff --git a/src/d3d11/d3d11_shader.cpp b/src/d3d11/d3d11_shader.cpp
index 9e809396..c3369574 100644
--- a/src/d3d11/d3d11_shader.cpp
+++ b/src/d3d11/d3d11_shader.cpp
@@ -39,6 +39,7 @@ namespace dxvk {
   
   
   D3D11CommonShader::D3D11CommonShader(
+          D3D11Device*    pDevice,
     const D3D11ShaderKey* pShaderKey,
     const DxbcModuleInfo* pDxbcModuleInfo,
     const void*           pShaderBytecode,
@@ -92,6 +93,7 @@ namespace dxvk {
   
   
   D3D11CommonShader D3D11ShaderModuleSet::GetShaderModule(
+          D3D11Device*    pDevice,
     const DxbcModuleInfo* pDxbcModuleInfo,
     const void*           pShaderBytecode,
           size_t          BytecodeLength,
@@ -108,7 +110,8 @@ namespace dxvk {
     
     // This shader has not been compiled yet, so we have to create a
     // new module. This takes a while, so we won't lock the structure.
-    D3D11CommonShader module(&key, pDxbcModuleInfo, pShaderBytecode, BytecodeLength);
+    D3D11CommonShader module(pDevice, &key,
+      pDxbcModuleInfo, pShaderBytecode, BytecodeLength);
     
     // Insert the new module into the lookup table. If another thread
     // has compiled the same shader in the meantime, we should return
diff --git a/src/d3d11/d3d11_shader.h b/src/d3d11/d3d11_shader.h
index 2c942348..5ec88a69 100644
--- a/src/d3d11/d3d11_shader.h
+++ b/src/d3d11/d3d11_shader.h
@@ -69,17 +69,20 @@ namespace dxvk {
     
     D3D11CommonShader();
     D3D11CommonShader(
+            D3D11Device*    pDevice,
       const D3D11ShaderKey* pShaderKey,
       const DxbcModuleInfo* pDxbcModuleInfo,
       const void*           pShaderBytecode,
             size_t          BytecodeLength);
     ~D3D11CommonShader();
 
-    DxbcProgramType GetProgramType() const;
-    
     Rc<DxvkShader> GetShader() const {
       return m_shader;
     }
+
+    Rc<DxvkBuffer> GetIcb() const {
+      return m_buffer;
+    }
     
     std::string GetName() const {
       return m_name;
@@ -89,6 +92,7 @@ namespace dxvk {
     
     std::string    m_name;
     Rc<DxvkShader> m_shader;
+    Rc<DxvkBuffer> m_buffer;
     
   };
   
@@ -163,6 +167,7 @@ namespace dxvk {
     ~D3D11ShaderModuleSet();
     
     D3D11CommonShader GetShaderModule(
+            D3D11Device*    pDevice,
       const DxbcModuleInfo* pDxbcModuleInfo,
       const void*           pShaderBytecode,
             size_t          BytecodeLength,
diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp
index 0545c3d1..194d37e6 100644
--- a/src/dxbc/dxbc_compiler.cpp
+++ b/src/dxbc/dxbc_compiler.cpp
@@ -205,7 +205,8 @@ namespace dxvk {
       m_resourceSlots.size(),
       m_resourceSlots.data(),
       m_interfaceSlots,
-      m_module.compile());
+      m_module.compile(),
+      DxvkShaderConstData());
   }
   
   
diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp
index a9cfc738..e0cae8ce 100644
--- a/src/dxvk/dxvk_device.cpp
+++ b/src/dxvk/dxvk_device.cpp
@@ -190,7 +190,8 @@ namespace dxvk {
     const DxvkInterfaceSlots&       iface,
     const SpirvCodeBuffer&          code) {
     return new DxvkShader(stage,
-      slotCount, slotInfos, iface, code);
+      slotCount, slotInfos, iface,
+      code, DxvkShaderConstData());
   }
   
   
diff --git a/src/dxvk/dxvk_shader.cpp b/src/dxvk/dxvk_shader.cpp
index a8389557..3979519c 100644
--- a/src/dxvk/dxvk_shader.cpp
+++ b/src/dxvk/dxvk_shader.cpp
@@ -2,6 +2,42 @@
 
 namespace dxvk {
   
+  DxvkShaderConstData::DxvkShaderConstData() {
+
+  }
+
+
+  DxvkShaderConstData::DxvkShaderConstData(
+          size_t                dwordCount,
+    const uint32_t*             dwordArray)
+  : m_size(dwordCount), m_data(new uint32_t[dwordCount]) {
+    for (size_t i = 0; i < dwordCount; i++)
+      m_data[i] = dwordArray[i];
+  }
+
+
+  DxvkShaderConstData::DxvkShaderConstData(DxvkShaderConstData&& other)
+  : m_size(other.m_size), m_data(other.m_data) {
+    other.m_size = 0;
+    other.m_data = nullptr;
+  }
+
+
+  DxvkShaderConstData& DxvkShaderConstData::operator = (DxvkShaderConstData&& other) {
+    delete[] m_data;
+    this->m_size = other.m_size;
+    this->m_data = other.m_data;
+    other.m_size = 0;
+    other.m_data = nullptr;
+    return *this;
+  }
+
+
+  DxvkShaderConstData::~DxvkShaderConstData() {
+    delete[] m_data;
+  }
+
+
   DxvkShaderModule::DxvkShaderModule(
     const Rc<vk::DeviceFn>&     vkd,
     const Rc<DxvkShader>&       shader,
@@ -44,8 +80,10 @@ namespace dxvk {
           uint32_t                slotCount,
     const DxvkResourceSlot*       slotInfos,
     const DxvkInterfaceSlots&     iface,
-    const SpirvCodeBuffer&        code)
-  : m_stage(stage), m_code(code), m_interface(iface) {
+    const SpirvCodeBuffer&        code,
+          DxvkShaderConstData&&   constData)
+  : m_stage(stage), m_code(code), m_interface(iface),
+    m_constData(std::move(constData)) {
     // Write back resource slot infos
     for (uint32_t i = 0; i < slotCount; i++)
       m_slots.push_back(slotInfos[i]);
diff --git a/src/dxvk/dxvk_shader.h b/src/dxvk/dxvk_shader.h
index 0aa092dc..5c7878e7 100644
--- a/src/dxvk/dxvk_shader.h
+++ b/src/dxvk/dxvk_shader.h
@@ -38,6 +38,45 @@ namespace dxvk {
     uint32_t inputSlots  = 0;
     uint32_t outputSlots = 0;
   };
+
+
+  /**
+   * \brief Shader constants
+   * 
+   * Each shader can have constant data associated
+   * with it, which needs to be copied to a uniform
+   * buffer. The client API must then bind that buffer
+   * to an API-specific buffer binding when using the
+   * shader for rendering.
+   */
+  class DxvkShaderConstData {
+
+  public:
+
+    DxvkShaderConstData();
+    DxvkShaderConstData(
+            size_t                dwordCount,
+      const uint32_t*             dwordArray);
+
+    DxvkShaderConstData             (DxvkShaderConstData&& other);
+    DxvkShaderConstData& operator = (DxvkShaderConstData&& other);
+
+    ~DxvkShaderConstData();
+
+    const uint32_t* data() const {
+      return m_data;
+    }
+
+    size_t sizeInBytes() const {
+      return m_size * sizeof(uint32_t);
+    }
+
+  private:
+
+    size_t    m_size = 0;
+    uint32_t* m_data = nullptr;
+
+  };
   
   
   /**
@@ -110,7 +149,8 @@ namespace dxvk {
             uint32_t                slotCount,
       const DxvkResourceSlot*       slotInfos,
       const DxvkInterfaceSlots&     iface,
-      const SpirvCodeBuffer&        code);
+      const SpirvCodeBuffer&        code,
+            DxvkShaderConstData&&   constData);
     
     ~DxvkShader();
     
@@ -166,6 +206,18 @@ namespace dxvk {
     DxvkInterfaceSlots interfaceSlots() const {
       return m_interface;
     }
+
+    /**
+     * \brief Shader constant data
+     * 
+     * Returns a read-only reference to the 
+     * constant data associated with this
+     * shader object.
+     * \returns Shader constant data
+     */
+    const DxvkShaderConstData& shaderConstants() const {
+      return m_constData;
+    }
     
     /**
      * \brief Dumps SPIR-V shader
@@ -210,6 +262,7 @@ namespace dxvk {
     std::vector<DxvkResourceSlot> m_slots;
     std::vector<size_t>           m_idOffsets;
     DxvkInterfaceSlots            m_interface;
+    DxvkShaderConstData           m_constData;
     std::string                   m_debugName;
     
   };
diff --git a/src/dxvk/hud/dxvk_hud_renderer.cpp b/src/dxvk/hud/dxvk_hud_renderer.cpp
index 5fa083dd..0430ad34 100644
--- a/src/dxvk/hud/dxvk_hud_renderer.cpp
+++ b/src/dxvk/hud/dxvk_hud_renderer.cpp
@@ -190,7 +190,7 @@ namespace dxvk::hud {
       { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_IMAGE_VIEW_TYPE_MAX_ENUM },
     }};
     
-    return new DxvkShader(
+    return device->createShader(
       VK_SHADER_STAGE_VERTEX_BIT,
       resourceSlots.size(),
       resourceSlots.data(),
@@ -208,7 +208,7 @@ namespace dxvk::hud {
       { 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_IMAGE_VIEW_TYPE_2D       },
     }};
     
-    return new DxvkShader(
+    return device->createShader(
       VK_SHADER_STAGE_FRAGMENT_BIT,
       resourceSlots.size(),
       resourceSlots.data(),
@@ -220,7 +220,7 @@ namespace dxvk::hud {
   Rc<DxvkShader> HudRenderer::createLineShader(const Rc<DxvkDevice>& device) {
     const SpirvCodeBuffer codeBuffer(hud_line);
     
-    return new DxvkShader(
+    return device->createShader(
       VK_SHADER_STAGE_FRAGMENT_BIT,
       0, nullptr, { 0x2, 0x1 },
       codeBuffer);