From cded7726a7140538d62742b2dc0aecaed027223c Mon Sep 17 00:00:00 2001
From: Philip Rebohle <philip.rebohle@tu-dortmund.de>
Date: Thu, 16 Nov 2017 02:07:10 +0100
Subject: [PATCH] [dxbc] Added pixel shader code generator stub

---
 src/dxbc/dxbc_compiler.cpp       |  66 ++++++-------
 src/dxbc/gen/dxbc_gen_common.cpp |   7 +-
 src/dxbc/gen/dxbc_gen_pixel.cpp  | 155 +++++++++++++++++++++++++++++++
 src/dxbc/gen/dxbc_gen_pixel.h    |  52 +++++++++++
 src/dxbc/meson.build             |   1 +
 5 files changed, 247 insertions(+), 34 deletions(-)
 create mode 100644 src/dxbc/gen/dxbc_gen_pixel.cpp
 create mode 100644 src/dxbc/gen/dxbc_gen_pixel.h

diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp
index ec61b178e..f460a11d3 100644
--- a/src/dxbc/dxbc_compiler.cpp
+++ b/src/dxbc/dxbc_compiler.cpp
@@ -112,7 +112,6 @@ namespace dxvk {
   void DxbcCompiler::opMov(const DxbcInstruction& ins) {
     auto dstOp = ins.operand(0);
     auto srcOp = ins.operand(dstOp.length());
-    
     DxbcComponentMask mask = this->getDstOperandMask(dstOp);
     
     DxbcValue value = this->loadOperand(srcOp, mask, DxbcScalarType::Float32);
@@ -229,9 +228,13 @@ namespace dxvk {
         return m_gen->regExtract(opValue,
           opToken.componentSelection());
         
+      case DxbcComponentSelectionMode::Mask:
+        return m_gen->regExtract(opValue,
+          opToken.componentMask());
+      
       default:
         throw DxvkError(str::format(
-          "DxbcCompiler::loadOperand: Invalid component selection mode: ",
+          "DxbcCompiler::selectOperandComponents: Invalid selection mode: ",
           opToken.selectionMode()));
     }
   }
@@ -257,38 +260,35 @@ namespace dxvk {
     
     DxbcValue result;
     
-    switch (token.type()) {
-      case DxbcOperandType::Imm32: {
-        if (token.numComponents() == 1) {
-          result = m_gen->defConstScalar(operand.imm32(0));
-        } else if (token.numComponents() == 4) {
-          result = m_gen->defConstVector(
-            operand.imm32(0), operand.imm32(1),
-            operand.imm32(2), operand.imm32(3));
-        } else {
-          throw DxvkError(str::format(
-            "DxbcCompiler::loadOperand [imm32]: Invalid number of components: ",
-            token.numComponents()));
-        }
-      } break;
+    if (token.type() == DxbcOperandType::Imm32) {
+      if (token.numComponents() == 1) {
+        result = m_gen->defConstScalar(operand.imm32(0));
+      } else if (token.numComponents() == 4) {
+        result = m_gen->defConstVector(
+          operand.imm32(0), operand.imm32(1),
+          operand.imm32(2), operand.imm32(3));
+      } else {
+        throw DxvkError(str::format(
+          "DxbcCompiler::loadOperand [imm32]: Invalid number of components: ",
+          token.numComponents()));
+      }
       
-      default: {
-        result = m_gen->regLoad(
-          this->getOperandPtr(operand));
-      };
-    }
-    
-    // Apply the source operand swizzle
-    if (token.numComponents() == 4)
-      result = this->selectOperandComponents(token, result, dstMask);
-    
-    // Apply source operand modifiers, if any
-    auto operandModifiers = operand.queryOperandExt(
-      DxbcOperandExt::OperandModifier);
-    
-    if (operandModifiers) {
-      result = this->applyOperandModifiers(
-        result, operandModifiers->data());
+      result = m_gen->regExtract(result, dstMask);
+    } else {
+      result = m_gen->regLoad(this->getOperandPtr(operand));
+      
+      // Apply the source operand swizzle
+      if (token.numComponents() == 4)
+        result = this->selectOperandComponents(token, result, dstMask);
+      
+      // Apply source operand modifiers, if any
+      auto operandModifiers = operand.queryOperandExt(
+        DxbcOperandExt::OperandModifier);
+      
+      if (operandModifiers) {
+        result = this->applyOperandModifiers(
+          result, operandModifiers->data());
+      }
     }
     
     return result;
diff --git a/src/dxbc/gen/dxbc_gen_common.cpp b/src/dxbc/gen/dxbc_gen_common.cpp
index 5b770d9c3..2463ba594 100644
--- a/src/dxbc/gen/dxbc_gen_common.cpp
+++ b/src/dxbc/gen/dxbc_gen_common.cpp
@@ -1,4 +1,5 @@
 #include "dxbc_gen_common.h"
+#include "dxbc_gen_pixel.h"
 #include "dxbc_gen_vertex.h"
 
 #include "../dxbc_names.h"
@@ -145,7 +146,8 @@ namespace dxvk {
   DxbcValue DxbcCodeGen::regExtract(
     const DxbcValue&            src,
           DxbcComponentMask     mask) {
-    // TODO implement
+    return this->regSwizzle(src,
+      DxbcComponentSwizzle(), mask);
   }
   
   
@@ -263,6 +265,9 @@ namespace dxvk {
   Rc<DxbcCodeGen> DxbcCodeGen::create(
     const DxbcProgramVersion& version) {
     switch (version.type()) {
+      case DxbcProgramType::PixelShader:
+        return new DxbcPsCodeGen();
+      
       case DxbcProgramType::VertexShader:
         return new DxbcVsCodeGen();
       
diff --git a/src/dxbc/gen/dxbc_gen_pixel.cpp b/src/dxbc/gen/dxbc_gen_pixel.cpp
new file mode 100644
index 000000000..a9d9b46f9
--- /dev/null
+++ b/src/dxbc/gen/dxbc_gen_pixel.cpp
@@ -0,0 +1,155 @@
+#include "dxbc_gen_pixel.h"
+
+namespace dxvk {
+  
+  DxbcPsCodeGen::DxbcPsCodeGen() {
+    m_module.enableCapability(spv::CapabilityShader);
+    m_module.enableCapability(spv::CapabilityCullDistance);
+    m_module.enableCapability(spv::CapabilityClipDistance);
+    
+    m_function = m_module.allocateId();
+    m_module.setDebugName(m_function, "ps_main");
+    
+    m_module.functionBegin(
+      m_module.defVoidType(),
+      m_function,
+      m_module.defFunctionType(
+        m_module.defVoidType(), 0, nullptr),
+      spv::FunctionControlMaskNone);
+    m_module.opLabel(m_module.allocateId());
+  }
+  
+  
+  DxbcPsCodeGen::~DxbcPsCodeGen() {
+    
+  }
+  
+  
+  void DxbcPsCodeGen::dclInterfaceVar(
+          DxbcOperandType   regType,
+          uint32_t          regId,
+          uint32_t          regDim,
+          DxbcComponentMask regMask,
+          DxbcSystemValue   sv) {
+    switch (regType) {
+      case DxbcOperandType::Input: {
+        if (sv == DxbcSystemValue::None) {
+          if (m_vRegs.at(regId).valueId == 0) {
+            m_vRegs.at(regId) = this->defVar(
+              DxbcValueType(DxbcScalarType::Float32, 4),
+              spv::StorageClassInput);
+            m_module.setDebugName(m_vRegs.at(regId).valueId,
+              str::format("v", regId).c_str());
+            m_module.decorateLocation(
+              m_vRegs.at(regId).valueId, regId);
+            m_entryPointInterfaces.push_back(
+              m_vRegs.at(regId).valueId);
+          }
+        } else {
+          if (m_vRegsSv.at(regId).valueId == 0) {
+            m_vRegsSv.at(regId) = this->defVar(
+              DxbcValueType(DxbcScalarType::Float32, 4),
+              spv::StorageClassPrivate);
+            m_module.setDebugName(m_vRegsSv.at(regId).valueId,
+              str::format("sv", regId).c_str());
+          }
+        }
+      } break;
+      
+      case DxbcOperandType::Output: {
+        if (sv != DxbcSystemValue::None) {
+          throw DxvkError(str::format(
+            "DxbcPsCodeGen::dclInterfaceVar: Cannot map output register to system value: ",
+            sv));
+        }
+        
+        if (m_oRegs.at(regId).valueId == 0) {
+          m_oRegs.at(regId) = this->defVar(
+            DxbcValueType(DxbcScalarType::Float32, 4),
+            spv::StorageClassOutput);
+          m_module.setDebugName(m_oRegs.at(regId).valueId,
+            str::format("o", regId).c_str());
+          m_module.decorateLocation(
+            m_oRegs.at(regId).valueId, regId);
+          m_entryPointInterfaces.push_back(
+            m_oRegs.at(regId).valueId);
+        }
+      } break;
+      
+      default:
+        throw DxvkError(str::format(
+          "DxbcPsCodeGen::dclInterfaceVar: Unhandled operand type: ",
+          regType));
+    }
+  }
+  
+  
+  DxbcPointer DxbcPsCodeGen::ptrInterfaceVar(
+          DxbcOperandType   regType,
+          uint32_t          regId) {
+    switch (regType) {
+      case DxbcOperandType::Input:
+        return m_vRegsSv.at(regId).valueId != 0
+          ? m_vRegsSv.at(regId)
+          : m_vRegs  .at(regId);
+      
+      case DxbcOperandType::Output:
+        return m_oRegs.at(regId);
+        
+      default:
+        throw DxvkError(str::format(
+          "DxbcPsCodeGen::ptrInterfaceVar: Unhandled operand type: ",
+          regType));
+    }
+  }
+  
+  
+  DxbcPointer DxbcPsCodeGen::ptrInterfaceVarIndexed(
+          DxbcOperandType   regType,
+          uint32_t          regId,
+    const DxbcValue&        index) {
+    throw DxvkError(str::format(
+      "DxbcPsCodeGen::ptrInterfaceVarIndexed:\n",
+      "Pixel shaders do not support indexed interface variables"));
+  }
+  
+  
+  Rc<DxvkShader> DxbcPsCodeGen::finalize() {
+    m_module.functionBegin(
+      m_module.defVoidType(),
+      m_entryPointId,
+      m_module.defFunctionType(
+        m_module.defVoidType(), 0, nullptr),
+      spv::FunctionControlMaskNone);
+    m_module.opLabel(m_module.allocateId());
+    
+    this->prepareSvInputs();
+    m_module.opFunctionCall(
+      m_module.defVoidType(),
+      m_function, 0, nullptr);
+    this->prepareSvOutputs();
+    
+    m_module.opReturn();
+    m_module.functionEnd();
+    
+    m_module.addEntryPoint(m_entryPointId,
+      spv::ExecutionModelFragment, "main",
+      m_entryPointInterfaces.size(),
+      m_entryPointInterfaces.data());
+    m_module.setDebugName(m_entryPointId, "main");
+    
+    return new DxvkShader(VK_SHADER_STAGE_FRAGMENT_BIT,
+      m_module.compile(), 0, nullptr);
+  }
+  
+  
+  void DxbcPsCodeGen::prepareSvInputs() {
+    
+  }
+  
+  
+  void DxbcPsCodeGen::prepareSvOutputs() {
+    
+  }
+  
+}
\ No newline at end of file
diff --git a/src/dxbc/gen/dxbc_gen_pixel.h b/src/dxbc/gen/dxbc_gen_pixel.h
new file mode 100644
index 000000000..eb280491a
--- /dev/null
+++ b/src/dxbc/gen/dxbc_gen_pixel.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "dxbc_gen_common.h"
+
+namespace dxvk {
+  
+  /**
+   * \brief Pixel shader code generator
+   */
+  class DxbcPsCodeGen : public DxbcCodeGen {
+    
+  public:
+    
+    DxbcPsCodeGen();
+    ~DxbcPsCodeGen();
+    
+    void dclInterfaceVar(
+            DxbcOperandType   regType,
+            uint32_t          regId,
+            uint32_t          regDim,
+            DxbcComponentMask regMask,
+            DxbcSystemValue   sv);
+    
+    DxbcPointer ptrInterfaceVar(
+            DxbcOperandType   regType,
+            uint32_t          regId);
+    
+    DxbcPointer ptrInterfaceVarIndexed(
+            DxbcOperandType   regType,
+            uint32_t          regId,
+      const DxbcValue&        index);
+    
+    Rc<DxvkShader> finalize() final;
+    
+  private:
+    
+    uint32_t m_function = 0;
+    
+    DxbcPointer m_svPosition;
+    
+    std::array<DxbcPointer, 32> m_vRegs;
+    std::array<DxbcPointer, 32> m_vRegsSv;
+    std::array<DxbcPointer, 32> m_oRegs;
+    
+    std::vector<DxbcSvMapping> m_svInputs;
+    
+    void prepareSvInputs();
+    void prepareSvOutputs();
+    
+  };
+  
+}
\ No newline at end of file
diff --git a/src/dxbc/meson.build b/src/dxbc/meson.build
index 037568dc5..f94cf64f2 100644
--- a/src/dxbc/meson.build
+++ b/src/dxbc/meson.build
@@ -11,6 +11,7 @@ dxbc_src = files([
   'dxbc_type.cpp',
   
   'gen/dxbc_gen_common.cpp',
+  'gen/dxbc_gen_pixel.cpp',
   'gen/dxbc_gen_vertex.cpp',
 ])