From 41d660f220dd907333402b57442b7023382429de Mon Sep 17 00:00:00 2001
From: Philip Rebohle <philip.rebohle@tu-dortmund.de>
Date: Wed, 20 Dec 2017 20:21:44 +0100
Subject: [PATCH] [spirv] Added image operand structure for more flexible
 sample ops

---
 src/dxbc/dxbc_compiler.cpp  | 26 ++++++++----
 src/dxgi/dxgi_presenter.cpp |  3 +-
 src/spirv/spirv_module.cpp  | 83 ++++++++++++++++++++++++++++++++-----
 src/spirv/spirv_module.h    | 29 +++++++++++--
 4 files changed, 116 insertions(+), 25 deletions(-)

diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp
index 1e79b9b6..03021c11 100644
--- a/src/dxbc/dxbc_compiler.cpp
+++ b/src/dxbc/dxbc_compiler.cpp
@@ -1193,7 +1193,6 @@ namespace dxvk {
   
   
   void DxbcCompiler::emitSample(const DxbcShaderInstruction& ins) {
-    // TODO support address offset
     // TODO support more sample ops
     
     // All sample instructions have at least these operands:
@@ -1240,6 +1239,11 @@ namespace dxvk {
         m_samplers.at(samplerId).typeId,
         m_samplers.at(samplerId).varId));
     
+    // Accumulate additional image operands. These are
+    // not part of the actual operand token in SPIR-V.
+    SpirvImageOperands imageOperands;
+    // TODO implement sample controls
+    
     // Sampling an image always returns a four-component
     // vector, whereas depth-compare ops return a scalar.
     DxbcRegisterValue result;
@@ -1247,25 +1251,29 @@ namespace dxvk {
     result.type.ccount = isDepthCompare ? 1 : 4;
     
     switch (ins.op) {
+      // Simple image sample operation
       case DxbcOpcode::Sample: {
         result.id = m_module.opImageSampleImplicitLod(
           getVectorTypeId(result.type),
-          sampledImageId, coord.id);
+          sampledImageId, coord.id,
+          imageOperands);
       } break;
       
+      // Depth-compare operation
       case DxbcOpcode::SampleC: {
         result.id = m_module.opImageSampleDrefImplicitLod(
-          getVectorTypeId(result.type),
-          sampledImageId, coord.id,
-          referenceValue.id);
+          getVectorTypeId(result.type), sampledImageId, coord.id,
+          referenceValue.id, imageOperands);
       } break;
       
+      // Depth-compare operation on mip level zero
       case DxbcOpcode::SampleClz: {
+        imageOperands.flags |= spv::ImageOperandsLodMask;
+        imageOperands.sLod = m_module.constf32(0.0f);
+        
         result.id = m_module.opImageSampleDrefExplicitLod(
-          getVectorTypeId(result.type),
-          sampledImageId, coord.id,
-          referenceValue.id,
-          m_module.constf32(0.0f));
+          getVectorTypeId(result.type), sampledImageId, coord.id,
+          referenceValue.id, imageOperands);
       } break;
       
       default:
diff --git a/src/dxgi/dxgi_presenter.cpp b/src/dxgi/dxgi_presenter.cpp
index 8cadfeaa..493f8821 100644
--- a/src/dxgi/dxgi_presenter.cpp
+++ b/src/dxgi/dxgi_presenter.cpp
@@ -472,7 +472,8 @@ namespace dxvk {
           typeSampledTex,
           module.opLoad(typeTexture, rcTexture),
           module.opLoad(typeSampler, rcSampler)),
-        module.opLoad(typeVec2, inTexCoord)));
+        module.opLoad(typeVec2, inTexCoord),
+        SpirvImageOperands()));
     
     module.opReturn();
     module.functionEnd();
diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp
index 4e11c226..737b7c9c 100644
--- a/src/spirv/spirv_module.cpp
+++ b/src/spirv/spirv_module.cpp
@@ -1521,14 +1521,18 @@ namespace dxvk {
   uint32_t SpirvModule::opImageSampleImplicitLod(
           uint32_t                resultType,
           uint32_t                sampledImage,
-          uint32_t                coordinates) {
+          uint32_t                coordinates,
+    const SpirvImageOperands&     operands) {
     uint32_t resultId = this->allocateId();
     
-    m_code.putIns (spv::OpImageSampleImplicitLod, 5);
+    m_code.putIns(spv::OpImageSampleImplicitLod,
+      5 + getImageOperandWordCount(operands));
     m_code.putWord(resultType);
     m_code.putWord(resultId);
     m_code.putWord(sampledImage);
     m_code.putWord(coordinates);
+    
+    putImageOperands(operands);
     return resultId;
   }
   
@@ -1537,16 +1541,18 @@ namespace dxvk {
           uint32_t                resultType,
           uint32_t                sampledImage,
           uint32_t                coordinates,
-          uint32_t                lod) {
+    const SpirvImageOperands&     operands) {
     uint32_t resultId = this->allocateId();
     
-    m_code.putIns (spv::OpImageSampleExplicitLod, 7);
+    m_code.putIns(spv::OpImageSampleExplicitLod,
+      5 + getImageOperandWordCount(operands));
     m_code.putWord(resultType);
     m_code.putWord(resultId);
     m_code.putWord(sampledImage);
     m_code.putWord(coordinates);
     m_code.putWord(spv::ImageOperandsLodMask);
-    m_code.putWord(lod);
+    
+    putImageOperands(operands);
     return resultId;
   }
   
@@ -1555,15 +1561,19 @@ namespace dxvk {
           uint32_t                resultType,
           uint32_t                sampledImage,
           uint32_t                coordinates,
-          uint32_t                reference) {
+          uint32_t                reference,
+    const SpirvImageOperands&     operands) {
     uint32_t resultId = this->allocateId();
     
-    m_code.putIns (spv::OpImageSampleDrefImplicitLod, 6);
+    m_code.putIns(spv::OpImageSampleDrefImplicitLod,
+      6 + getImageOperandWordCount(operands));
     m_code.putWord(resultType);
     m_code.putWord(resultId);
     m_code.putWord(sampledImage);
     m_code.putWord(coordinates);
     m_code.putWord(reference);
+    
+    putImageOperands(operands);
     return resultId;
   }
   
@@ -1573,17 +1583,18 @@ namespace dxvk {
           uint32_t                sampledImage,
           uint32_t                coordinates,
           uint32_t                reference,
-          uint32_t                lod) {
+    const SpirvImageOperands&     operands) {
     uint32_t resultId = this->allocateId();
     
-    m_code.putIns (spv::OpImageSampleDrefExplicitLod, 8);
+    m_code.putIns(spv::OpImageSampleDrefExplicitLod,
+      6 + getImageOperandWordCount(operands));
     m_code.putWord(resultType);
     m_code.putWord(resultId);
     m_code.putWord(sampledImage);
     m_code.putWord(coordinates);
     m_code.putWord(reference);
-    m_code.putWord(spv::ImageOperandsLodMask);
-    m_code.putWord(lod);
+    
+    putImageOperands(operands);
     return resultId;
   }
   
@@ -1683,4 +1694,54 @@ namespace dxvk {
     m_instExt.putStr (name);
   }
   
+  
+  uint32_t SpirvModule::getImageOperandWordCount(const SpirvImageOperands& op) const {
+    // Each flag may add one or more operands
+    const uint32_t result
+      = ((op.flags & spv::ImageOperandsBiasMask)        ? 1 : 0)
+      + ((op.flags & spv::ImageOperandsLodMask)         ? 1 : 0)
+      + ((op.flags & spv::ImageOperandsConstOffsetMask) ? 1 : 0)
+      + ((op.flags & spv::ImageOperandsGradMask)        ? 2 : 0)
+      + ((op.flags & spv::ImageOperandsOffsetMask)      ? 1 : 0)
+      + ((op.flags & spv::ImageOperandsConstOffsetsMask)? 1 : 0)
+      + ((op.flags & spv::ImageOperandsSampleMask)      ? 1 : 0)
+      + ((op.flags & spv::ImageOperandsMinLodMask)      ? 1 : 0);
+    
+    // Add a DWORD for the operand mask if it is non-zero
+    return result != 0 ? result + 1 : 0;
+  }
+  
+  
+  void SpirvModule::putImageOperands(const SpirvImageOperands& op) {
+    if (op.flags != 0) {
+      m_code.putWord(op.flags);
+      
+      if (op.flags & spv::ImageOperandsBiasMask)
+        m_code.putWord(op.sLodBias);
+      
+      if (op.flags & spv::ImageOperandsLodMask)
+        m_code.putWord(op.sLod);
+      
+      if (op.flags & spv::ImageOperandsConstOffsetMask)
+        m_code.putWord(op.sConstOffset);
+      
+      if (op.flags & spv::ImageOperandsGradMask) {
+        m_code.putWord(op.sGradX);
+        m_code.putWord(op.sGradY);
+      }
+      
+      if (op.flags & spv::ImageOperandsOffsetMask)
+        m_code.putWord(op.gOffset);
+      
+      if (op.flags & spv::ImageOperandsConstOffsetsMask)
+        m_code.putWord(op.gConstOffsets);
+      
+      if (op.flags & spv::ImageOperandsSampleMask)
+        m_code.putWord(op.sSampleId);
+      
+      if (op.flags & spv::ImageOperandsMinLodMask)
+        m_code.putWord(op.sMinLod);
+    }
+  }
+  
 }
\ No newline at end of file
diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h
index 414f4e0d..1717972f 100644
--- a/src/spirv/spirv_module.h
+++ b/src/spirv/spirv_module.h
@@ -4,6 +4,19 @@
 
 namespace dxvk {
   
+  struct SpirvImageOperands {
+    uint32_t flags         = 0;
+    uint32_t sLodBias      = 0;
+    uint32_t sLod          = 0;
+    uint32_t sConstOffset  = 0;
+    uint32_t sGradX        = 0;
+    uint32_t sGradY        = 0;
+    uint32_t gOffset       = 0;
+    uint32_t gConstOffsets = 0;
+    uint32_t sSampleId     = 0;
+    uint32_t sMinLod       = 0;
+  };
+  
   /**
    * \brief SPIR-V module
    * 
@@ -518,26 +531,28 @@ namespace dxvk {
     uint32_t opImageSampleImplicitLod(
             uint32_t                resultType,
             uint32_t                sampledImage,
-            uint32_t                coordinates);
+            uint32_t                coordinates,
+      const SpirvImageOperands&     operands);
     
     uint32_t opImageSampleExplicitLod(
             uint32_t                resultType,
             uint32_t                sampledImage,
             uint32_t                coordinates,
-            uint32_t                lod);
+      const SpirvImageOperands&     operands);
     
     uint32_t opImageSampleDrefImplicitLod(
             uint32_t                resultType,
             uint32_t                sampledImage,
             uint32_t                coordinates,
-            uint32_t                reference);
+            uint32_t                reference,
+      const SpirvImageOperands&     operands);
     
     uint32_t opImageSampleDrefExplicitLod(
             uint32_t                resultType,
             uint32_t                sampledImage,
             uint32_t                coordinates,
             uint32_t                reference,
-            uint32_t                lod);
+      const SpirvImageOperands&     operands);
     
     void opLoopMerge(
             uint32_t                mergeBlock,
@@ -587,6 +602,12 @@ namespace dxvk {
     
     void instImportGlsl450();
     
+    uint32_t getImageOperandWordCount(
+      const SpirvImageOperands&     op) const;
+    
+    void putImageOperands(
+      const SpirvImageOperands&     op);
+    
   };
   
 }
\ No newline at end of file