diff --git a/src/dxbc/dxbc_analysis.cpp b/src/dxbc/dxbc_analysis.cpp
index a5bf4fc19..2a6747b05 100644
--- a/src/dxbc/dxbc_analysis.cpp
+++ b/src/dxbc/dxbc_analysis.cpp
@@ -108,6 +108,47 @@ namespace dxvk {
         m_analysis->uavInfos[registerId].nonInvariantAccess = true;
       } break;
 
+      case DxbcInstClass::Declaration: {
+        switch (ins.op) {
+          case DxbcOpcode::DclConstantBuffer: {
+            uint32_t registerId = ins.dst[0].idx[0].offset;
+
+            if (registerId < DxbcConstBufBindingCount)
+              m_analysis->bindings.cbvMask |= 1u << registerId;
+          } break;
+
+          case DxbcOpcode::DclSampler: {
+            uint32_t registerId = ins.dst[0].idx[0].offset;
+
+            if (registerId < DxbcSamplerBindingCount)
+              m_analysis->bindings.samplerMask |= 1u << registerId;
+          } break;
+
+          case DxbcOpcode::DclResource:
+          case DxbcOpcode::DclResourceRaw:
+          case DxbcOpcode::DclResourceStructured: {
+            uint32_t registerId = ins.dst[0].idx[0].offset;
+
+            uint32_t idx = registerId / 64u;
+            uint32_t bit = registerId % 64u;
+
+            if (registerId < DxbcResourceBindingCount)
+              m_analysis->bindings.srvMask[idx] |= uint64_t(1u) << bit;
+          } break;
+
+          case DxbcOpcode::DclUavTyped:
+          case DxbcOpcode::DclUavRaw:
+          case DxbcOpcode::DclUavStructured: {
+            uint32_t registerId = ins.dst[0].idx[0].offset;
+
+            if (registerId < DxbcUavBindingCount)
+              m_analysis->bindings.uavMask |= uint64_t(1u) << registerId;
+          } break;
+
+          default: ;
+        }
+      } break;
+
       default:
         break;
     }
diff --git a/src/dxbc/dxbc_analysis.h b/src/dxbc/dxbc_analysis.h
index fa589f4fd..64e987038 100644
--- a/src/dxbc/dxbc_analysis.h
+++ b/src/dxbc/dxbc_analysis.h
@@ -53,6 +53,8 @@ namespace dxvk {
     
     DxbcClipCullInfo clipCullIn;
     DxbcClipCullInfo clipCullOut;
+
+    DxbcBindingMask bindings = { };
     
     bool usesDerivatives  = false;
     bool usesKill         = false;
diff --git a/src/dxbc/dxbc_common.cpp b/src/dxbc/dxbc_common.cpp
index d150c585b..db3d71529 100644
--- a/src/dxbc/dxbc_common.cpp
+++ b/src/dxbc/dxbc_common.cpp
@@ -10,9 +10,8 @@ namespace dxvk {
       case DxbcProgramType::HullShader     : return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
       case DxbcProgramType::DomainShader   : return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
       case DxbcProgramType::ComputeShader  : return VK_SHADER_STAGE_COMPUTE_BIT;
+      default: throw DxvkError("DxbcProgramInfo::shaderStage: Unsupported program type");
     }
-    
-    throw DxvkError("DxbcProgramInfo::shaderStage: Unsupported program type");
   }
   
   
@@ -24,9 +23,8 @@ namespace dxvk {
       case DxbcProgramType::HullShader     : return spv::ExecutionModelTessellationControl;
       case DxbcProgramType::DomainShader   : return spv::ExecutionModelTessellationEvaluation;
       case DxbcProgramType::ComputeShader  : return spv::ExecutionModelGLCompute;
+      default: throw DxvkError("DxbcProgramInfo::executionModel: Unsupported program type");
     }
-    
-    throw DxvkError("DxbcProgramInfo::executionModel: Unsupported program type");
   }
   
-}
\ No newline at end of file
+}
diff --git a/src/dxbc/dxbc_common.h b/src/dxbc/dxbc_common.h
index 9c490d62c..d9d420767 100644
--- a/src/dxbc/dxbc_common.h
+++ b/src/dxbc/dxbc_common.h
@@ -17,7 +17,11 @@ namespace dxvk {
     HullShader      = 3,
     DomainShader    = 4,
     ComputeShader   = 5,
+
+    Count
   };
+
+  using DxbcProgramTypeFlags = Flags<DxbcProgramType>;
   
   
   /**
diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp
index f0f362334..9e58e6497 100644
--- a/src/dxbc/dxbc_compiler.cpp
+++ b/src/dxbc/dxbc_compiler.cpp
@@ -237,6 +237,7 @@ namespace dxvk {
       case DxbcProgramType::GeometryShader: this->emitGsFinalize(); break;
       case DxbcProgramType::PixelShader:    this->emitPsFinalize(); break;
       case DxbcProgramType::ComputeShader:  this->emitCsFinalize(); break;
+      default: throw DxvkError("Invalid shader stage");
     }
 
     // Emit float control mode if the extension is supported
@@ -6138,7 +6139,7 @@ namespace dxvk {
         case DxbcProgramType::HullShader:     emitHsSystemValueStore(sv, mask, value); break;
         case DxbcProgramType::DomainShader:   emitDsSystemValueStore(sv, mask, value); break;
         case DxbcProgramType::PixelShader:    emitPsSystemValueStore(sv, mask, value); break;
-        case DxbcProgramType::ComputeShader:  break;
+        default: break;
       }
     }
   }
@@ -6822,6 +6823,7 @@ namespace dxvk {
       case DxbcProgramType::GeometryShader: emitGsInit(); break;
       case DxbcProgramType::PixelShader:    emitPsInit(); break;
       case DxbcProgramType::ComputeShader:  emitCsInit(); break;
+      default: throw DxvkError("Invalid shader stage");
     }
   }
   
diff --git a/src/dxbc/dxbc_module.cpp b/src/dxbc/dxbc_module.cpp
index d406bf292..16e37ebbb 100644
--- a/src/dxbc/dxbc_module.cpp
+++ b/src/dxbc/dxbc_module.cpp
@@ -42,7 +42,7 @@ namespace dxvk {
   
   Rc<DxvkShader> DxbcModule::compile(
     const DxbcModuleInfo& moduleInfo,
-    const std::string&    fileName) const {
+    const std::string&    fileName) {
     if (m_shexChunk == nullptr)
       throw DxvkError("DxbcModule::compile: No SHDR/SHEX chunk");
     
@@ -54,6 +54,8 @@ namespace dxvk {
       m_psgnChunk, analysisInfo);
     
     this->runAnalyzer(analyzer, m_shexChunk->slice());
+
+    m_bindings = std::make_optional(analysisInfo.bindings);
     
     DxbcCompiler compiler(
       fileName, moduleInfo,
@@ -62,7 +64,7 @@ namespace dxvk {
       m_psgnChunk, analysisInfo);
     
     this->runCompiler(compiler, m_shexChunk->slice());
-    
+
     return compiler.finalize();
   }
   
diff --git a/src/dxbc/dxbc_module.h b/src/dxbc/dxbc_module.h
index 7609e2322..7d6088bae 100644
--- a/src/dxbc/dxbc_module.h
+++ b/src/dxbc/dxbc_module.h
@@ -7,6 +7,7 @@
 #include "dxbc_header.h"
 #include "dxbc_modinfo.h"
 #include "dxbc_reader.h"
+#include "dxbc_util.h"
 
 // References used for figuring out DXBC:
 // - https://github.com/tgjones/slimshader-cpp
@@ -41,7 +42,16 @@ namespace dxvk {
 
       return m_shexChunk->programInfo();
     }
-    
+
+    /**
+     * \brief Queries shader binding mask
+     *
+     * Only valid after successfully compiling the shader.
+     */
+    std::optional<DxbcBindingMask> bindings() const {
+      return m_bindings;
+    }
+
     /**
      * \brief Input and output signature chunks
      * 
@@ -50,7 +60,7 @@ namespace dxvk {
      */
     Rc<DxbcIsgn> isgn() const { return m_isgnChunk; }
     Rc<DxbcIsgn> osgn() const { return m_osgnChunk; }
-    
+
     /**
      * \brief Compiles DXBC shader to SPIR-V module
      * 
@@ -61,7 +71,7 @@ namespace dxvk {
      */
     Rc<DxvkShader> compile(
       const DxbcModuleInfo& moduleInfo,
-      const std::string&    fileName) const;
+      const std::string&    fileName);
     
     /**
      * \brief Compiles a pass-through geometry shader
@@ -85,6 +95,8 @@ namespace dxvk {
     Rc<DxbcIsgn> m_osgnChunk;
     Rc<DxbcIsgn> m_psgnChunk;
     Rc<DxbcShex> m_shexChunk;
+
+    std::optional<DxbcBindingMask> m_bindings;
     
     void runAnalyzer(
             DxbcAnalyzer&       analyzer,
diff --git a/src/dxbc/dxbc_util.h b/src/dxbc/dxbc_util.h
index 04bec752a..53394c2a9 100644
--- a/src/dxbc/dxbc_util.h
+++ b/src/dxbc/dxbc_util.h
@@ -33,6 +33,43 @@ namespace dxvk {
   };
 
   
+  /**
+   * \brief Shader binding mask
+   *
+   * Stores a bit masks of resource bindings
+   * that are accessed by any given shader.
+   */
+  struct DxbcBindingMask {
+    uint32_t cbvMask      = 0u;
+    uint32_t samplerMask  = 0u;
+    uint64_t uavMask      = 0u;
+    std::array<uint64_t, 2> srvMask = { };
+
+    void reset() {
+      cbvMask = 0u;
+      samplerMask = 0u;
+      uavMask = 0u;
+      srvMask = { };
+    }
+
+    bool empty() const {
+      uint64_t mask = (uint64_t(cbvMask) | uint64_t(samplerMask) << 32u)
+                    | (uavMask | srvMask[0] | srvMask[1]);
+      return !mask;
+    }
+
+    DxbcBindingMask operator & (const DxbcBindingMask& other) const {
+      DxbcBindingMask result = *this;
+      result.cbvMask      &= other.cbvMask;
+      result.samplerMask  &= other.samplerMask;
+      result.uavMask      &= other.uavMask;
+      result.srvMask[0]   &= other.srvMask[0];
+      result.srvMask[1]   &= other.srvMask[1];
+      return result;
+    }
+  };
+
+
   /**
    * \brief Computes first binding index for a given stage
    *
@@ -124,4 +161,4 @@ namespace dxvk {
   uint32_t primitiveVertexCount(
           DxbcPrimitive   primitive);
   
-}
\ No newline at end of file
+}