From 200da4cf6055854aed457b6845b1f80c2204678a Mon Sep 17 00:00:00 2001
From: Philip Rebohle <philip.rebohle@tu-dortmund.de>
Date: Thu, 26 Oct 2017 15:40:39 +0200
Subject: [PATCH] [spirv] Added SpirvModule class to generate SPIR-V code

---
 src/dxbc/dxbc_compiler.cpp             |  25 +-
 src/dxbc/dxbc_compiler.h               |  19 +-
 src/spirv/gen/spirv_gen_capability.cpp |  21 --
 src/spirv/gen/spirv_gen_capability.h   |  47 ----
 src/spirv/gen/spirv_gen_constant.cpp   |  13 -
 src/spirv/gen/spirv_gen_constant.h     |  30 --
 src/spirv/gen/spirv_gen_debuginfo.cpp  |  17 --
 src/spirv/gen/spirv_gen_debuginfo.h    |  34 ---
 src/spirv/gen/spirv_gen_decoration.cpp |   8 -
 src/spirv/gen/spirv_gen_decoration.h   |  32 ---
 src/spirv/gen/spirv_gen_entrypoint.cpp |  56 ----
 src/spirv/gen/spirv_gen_entrypoint.h   |  88 ------
 src/spirv/gen/spirv_gen_id.h           |  32 ---
 src/spirv/gen/spirv_gen_typeinfo.cpp   | 146 ----------
 src/spirv/gen/spirv_gen_typeinfo.h     |  88 ------
 src/spirv/gen/spirv_gen_variable.cpp   |   8 -
 src/spirv/gen/spirv_gen_variable.h     |  31 ---
 src/spirv/meson.build                  |   9 +-
 src/spirv/spirv_module.cpp             | 372 +++++++++++++++++++++++++
 src/spirv/spirv_module.h               | 157 +++++++++++
 20 files changed, 539 insertions(+), 694 deletions(-)
 delete mode 100644 src/spirv/gen/spirv_gen_capability.cpp
 delete mode 100644 src/spirv/gen/spirv_gen_capability.h
 delete mode 100644 src/spirv/gen/spirv_gen_constant.cpp
 delete mode 100644 src/spirv/gen/spirv_gen_constant.h
 delete mode 100644 src/spirv/gen/spirv_gen_debuginfo.cpp
 delete mode 100644 src/spirv/gen/spirv_gen_debuginfo.h
 delete mode 100644 src/spirv/gen/spirv_gen_decoration.cpp
 delete mode 100644 src/spirv/gen/spirv_gen_decoration.h
 delete mode 100644 src/spirv/gen/spirv_gen_entrypoint.cpp
 delete mode 100644 src/spirv/gen/spirv_gen_entrypoint.h
 delete mode 100644 src/spirv/gen/spirv_gen_id.h
 delete mode 100644 src/spirv/gen/spirv_gen_typeinfo.cpp
 delete mode 100644 src/spirv/gen/spirv_gen_typeinfo.h
 delete mode 100644 src/spirv/gen/spirv_gen_variable.cpp
 delete mode 100644 src/spirv/gen/spirv_gen_variable.h
 create mode 100644 src/spirv/spirv_module.cpp
 create mode 100644 src/spirv/spirv_module.h

diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp
index 0d2830fff..9e48f048a 100644
--- a/src/dxbc/dxbc_compiler.cpp
+++ b/src/dxbc/dxbc_compiler.cpp
@@ -4,7 +4,7 @@ namespace dxvk {
   
   DxbcCompiler::DxbcCompiler(DxbcProgramVersion version)
   : m_version(version) {
-    m_entryPointId = m_counter.nextId();
+    m_entryPointId = m_module.allocateId();
     
     this->declareCapabilities();
     this->declareMemoryModel();
@@ -21,7 +21,7 @@ namespace dxvk {
     
     switch (token.opcode()) {
       case DxbcOpcode::DclThreadGroup: {
-        m_spvEntryPoints.setLocalSize(
+        m_module.setLocalSize(
           m_entryPointId,
           ins.getArgWord(0),
           ins.getArgWord(1),
@@ -37,33 +37,22 @@ namespace dxvk {
   
   
   Rc<DxvkShader> DxbcCompiler::finalize() {
-    SpirvCodeBuffer codeBuffer;
-    codeBuffer.putHeader(m_counter.numIds());
-    codeBuffer.append(m_spvCapabilities.code());
-    codeBuffer.append(m_spvEntryPoints.code());
-    codeBuffer.append(m_spvDebugInfo.code());
-    codeBuffer.append(m_spvDecorations.code());
-    codeBuffer.append(m_spvTypeInfo.code());
-    codeBuffer.append(m_spvConstants.code());
-    codeBuffer.append(m_spvVariables.code());
-    codeBuffer.append(m_spvCode);
-    
     return new DxvkShader(m_version.shaderStage(),
-      std::move(codeBuffer), 0, nullptr);
+      m_module.compile(), 0, nullptr);
   }
   
   
   void DxbcCompiler::declareCapabilities() {
-    m_spvCapabilities.enable(spv::CapabilityShader);
+    m_module.enableCapability(spv::CapabilityShader);
     
     switch (m_version.type()) {
       case DxbcProgramType::GeometryShader:
-        m_spvCapabilities.enable(spv::CapabilityGeometry);
+        m_module.enableCapability(spv::CapabilityGeometry);
         break;
         
       case DxbcProgramType::HullShader:
       case DxbcProgramType::DomainShader:
-        m_spvCapabilities.enable(spv::CapabilityTessellation);
+        m_module.enableCapability(spv::CapabilityTessellation);
         break;
         
       default:
@@ -73,7 +62,7 @@ namespace dxvk {
   
   
   void DxbcCompiler::declareMemoryModel() {
-    m_spvEntryPoints.setMemoryModel(
+    m_module.setMemoryModel(
       spv::AddressingModelLogical,
       spv::MemoryModelGLSL450);
   }
diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h
index 785665d97..91d39e810 100644
--- a/src/dxbc/dxbc_compiler.h
+++ b/src/dxbc/dxbc_compiler.h
@@ -2,13 +2,7 @@
 
 #include "dxbc_chunk_shex.h"
 
-#include "../spirv/gen/spirv_gen_capability.h"
-#include "../spirv/gen/spirv_gen_constant.h"
-#include "../spirv/gen/spirv_gen_debuginfo.h"
-#include "../spirv/gen/spirv_gen_decoration.h"
-#include "../spirv/gen/spirv_gen_entrypoint.h"
-#include "../spirv/gen/spirv_gen_typeinfo.h"
-#include "../spirv/gen/spirv_gen_variable.h"
+#include "../spirv/spirv_module.h"
 
 namespace dxvk {
   
@@ -47,16 +41,7 @@ namespace dxvk {
   private:
     
     DxbcProgramVersion  m_version;
-    SpirvIdCounter      m_counter;
-    
-    SpirvCapabilities   m_spvCapabilities;
-    SpirvEntryPoint     m_spvEntryPoints;
-    SpirvDebugInfo      m_spvDebugInfo;
-    SpirvDecorations    m_spvDecorations;
-    SpirvTypeInfo       m_spvTypeInfo;
-    SpirvConstants      m_spvConstants;
-    SpirvVariables      m_spvVariables;
-    SpirvCodeBuffer     m_spvCode;
+    SpirvModule         m_module;
     
     uint32_t m_entryPointId = 0;
     
diff --git a/src/spirv/gen/spirv_gen_capability.cpp b/src/spirv/gen/spirv_gen_capability.cpp
deleted file mode 100644
index 487205adc..000000000
--- a/src/spirv/gen/spirv_gen_capability.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "spirv_gen_capability.h"
-
-namespace dxvk {
-  
-  SpirvCapabilities:: SpirvCapabilities() { }
-  SpirvCapabilities::~SpirvCapabilities() { }
-  
-  
-  void SpirvCapabilities::enable(spv::Capability cap) {
-    // Scan the generated instructions to check
-    // whether we already enabled the capability.
-    for (auto ins : m_code) {
-      if (ins.opCode() == spv::OpCapability && ins.arg(1) == cap)
-        return;
-    }
-    
-    m_code.putIns (spv::OpCapability, 2);
-    m_code.putWord(cap);
-  }
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_capability.h b/src/spirv/gen/spirv_gen_capability.h
deleted file mode 100644
index f47cecdd8..000000000
--- a/src/spirv/gen/spirv_gen_capability.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma once
-
-#include "../spirv_code_buffer.h"
-
-namespace dxvk {
-  
-  /**
-   * \brief SPIR-V capability set
-   * 
-   * Holds a code buffer solely for the \c OpCapability
-   * instructions in the generated SPIR-V shader module.
-   */
-  class SpirvCapabilities {
-    
-  public:
-    
-    SpirvCapabilities();
-    ~SpirvCapabilities();
-    
-    /**
-     * \brief Code buffer
-     * 
-     * Code buffer that contains the
-     * \c OpCapability instructions.
-     * \returns Code buffer
-     */
-    const SpirvCodeBuffer& code() const {
-      return m_code;
-    }
-    
-    /**
-     * \brief Enables a capability
-     * 
-     * If the given capability has not been explicitly
-     * enabled yet, this will generate an \c OpCapability
-     * instruction for the given capability.
-     * \param [in] cap The capability
-     */
-    void enable(spv::Capability cap);
-    
-  private:
-    
-    SpirvCodeBuffer m_code;
-    
-  };
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_constant.cpp b/src/spirv/gen/spirv_gen_constant.cpp
deleted file mode 100644
index cf41273f4..000000000
--- a/src/spirv/gen/spirv_gen_constant.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "spirv_gen_constant.h"
-
-namespace dxvk {
-  
-  SpirvConstants:: SpirvConstants() { }
-  SpirvConstants::~SpirvConstants() { }
-  
-  
-  SpirvCodeBuffer SpirvConstants::code() const {
-    return m_code;
-  }
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_constant.h b/src/spirv/gen/spirv_gen_constant.h
deleted file mode 100644
index 2981254a1..000000000
--- a/src/spirv/gen/spirv_gen_constant.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-#include "../spirv_code_buffer.h"
-
-#include "spirv_gen_id.h"
-
-namespace dxvk {
-  
-  /**
-   * \brief SPIR-V constant generator
-   * 
-   * Provides convenient methods to
-   * generate SPIR-V constants.
-   */
-  class SpirvConstants {
-    
-  public:
-    
-    SpirvConstants();
-    ~SpirvConstants();
-    
-    SpirvCodeBuffer code() const;
-    
-  private:
-    
-    SpirvCodeBuffer m_code;
-    
-  };
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_debuginfo.cpp b/src/spirv/gen/spirv_gen_debuginfo.cpp
deleted file mode 100644
index a8f383386..000000000
--- a/src/spirv/gen/spirv_gen_debuginfo.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-#include "spirv_gen_debuginfo.h"
-
-namespace dxvk {
-  
-  SpirvDebugInfo:: SpirvDebugInfo() { }
-  SpirvDebugInfo::~SpirvDebugInfo() { }
-  
-  
-  void SpirvDebugInfo::assignName(
-          uint32_t  id,
-    const char*     name) {
-    m_code.putIns (spv::OpName, 2 + m_code.strLen(name));
-    m_code.putWord(id);
-    m_code.putStr (name);
-  }
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_debuginfo.h b/src/spirv/gen/spirv_gen_debuginfo.h
deleted file mode 100644
index b3fbfb7a3..000000000
--- a/src/spirv/gen/spirv_gen_debuginfo.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include "../spirv_code_buffer.h"
-
-namespace dxvk {
-  
-  /**
-   * \brief SPIR-V debug info generator
-   * 
-   * Can be used to assign names to result IDs,
-   * such as variables, for debugging purposes.
-   */
-  class SpirvDebugInfo {
-    
-  public:
-    
-    SpirvDebugInfo();
-    ~SpirvDebugInfo();
-    
-    const SpirvCodeBuffer& code() const {
-      return m_code;
-    }
-    
-    void assignName(
-            uint32_t  id,
-      const char*     name);
-    
-  private:
-    
-    SpirvCodeBuffer m_code;
-    
-  };
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_decoration.cpp b/src/spirv/gen/spirv_gen_decoration.cpp
deleted file mode 100644
index 3c5406a29..000000000
--- a/src/spirv/gen/spirv_gen_decoration.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
-#include "spirv_gen_decoration.h"
-
-namespace dxvk {
-  
-  SpirvDecorations:: SpirvDecorations() { }
-  SpirvDecorations::~SpirvDecorations() { }
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_decoration.h b/src/spirv/gen/spirv_gen_decoration.h
deleted file mode 100644
index c82d91509..000000000
--- a/src/spirv/gen/spirv_gen_decoration.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#pragma once
-
-#include "../spirv_code_buffer.h"
-
-#include "spirv_gen_id.h"
-
-namespace dxvk {
-  
-  /**
-   * \brief SPIR-V decoration generator
-   * 
-   * Generates instructions for descriptor
-   * bindings and builtin variable decorations.
-   */
-  class SpirvDecorations {
-    
-  public:
-    
-    SpirvDecorations();
-    ~SpirvDecorations();
-    
-    const SpirvCodeBuffer& code() const {
-      return m_code;
-    }
-     
-  private:
-    
-    SpirvCodeBuffer m_code;
-    
-  };
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_entrypoint.cpp b/src/spirv/gen/spirv_gen_entrypoint.cpp
deleted file mode 100644
index fc99d837b..000000000
--- a/src/spirv/gen/spirv_gen_entrypoint.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-#include "spirv_gen_entrypoint.h"
-
-namespace dxvk {
-  
-  SpirvEntryPoint:: SpirvEntryPoint() { }
-  SpirvEntryPoint::~SpirvEntryPoint() { }
-  
-  
-  SpirvCodeBuffer SpirvEntryPoint::code() const {
-    SpirvCodeBuffer code;
-    code.append(m_memoryModel);
-    code.append(m_entryPoints);
-    code.append(m_execModeInfo);
-    return code;
-  }
-  
-  
-  void SpirvEntryPoint::setMemoryModel(
-          spv::AddressingModel  addressModel,
-          spv::MemoryModel      memoryModel) {
-    m_memoryModel.putIns  (spv::OpMemoryModel, 3);
-    m_memoryModel.putWord (addressModel);
-    m_memoryModel.putWord (memoryModel);
-  }
-  
-  
-  void SpirvEntryPoint::addEntryPoint(
-          uint32_t              functionId,
-          spv::ExecutionModel   execModel,
-    const char*                 name,
-          uint32_t              interfaceCount,
-    const uint32_t*             interfaceIds) {
-    m_entryPoints.putIns  (spv::OpEntryPoint, 3 + m_entryPoints.strLen(name) + interfaceCount);
-    m_entryPoints.putWord (execModel);
-    m_entryPoints.putWord (functionId);
-    m_entryPoints.putStr  (name);
-    
-    for (uint32_t i = 0; i < interfaceCount; i++)
-      m_entryPoints.putWord(interfaceIds[i]);
-  }
-  
-  
-  void SpirvEntryPoint::setLocalSize(
-          uint32_t              functionId,
-          uint32_t              x,
-          uint32_t              y,
-          uint32_t              z) {
-    m_execModeInfo.putIns (spv::OpExecutionMode, 6);
-    m_execModeInfo.putWord(functionId);
-    m_execModeInfo.putWord(spv::ExecutionModeLocalSize);
-    m_execModeInfo.putWord(x);
-    m_execModeInfo.putWord(y);
-    m_execModeInfo.putWord(z);
-  }
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_entrypoint.h b/src/spirv/gen/spirv_gen_entrypoint.h
deleted file mode 100644
index 492c184c7..000000000
--- a/src/spirv/gen/spirv_gen_entrypoint.h
+++ /dev/null
@@ -1,88 +0,0 @@
-#pragma once
-
-#include "../spirv_code_buffer.h"
-
-namespace dxvk {
-  
-  /**
-   * \brief SPIR-V entry point info
-   * 
-   * Accumulates information about the entry
-   * point of the generated shader module,
-   * including execution mode info.
-   */
-  class SpirvEntryPoint {
-    
-  public:
-    
-    SpirvEntryPoint();
-    ~SpirvEntryPoint();
-    
-    /**
-     * \brief Generates SPIR-V code
-     * \returns SPIR-V code buffer
-     */
-    SpirvCodeBuffer code() const;
-    
-    /**
-     * \brief Sets memory model
-     * 
-     * Generates an \c OpMemoryModel instruction.
-     * Only ever call this once, or otherwise the
-     * resulting shader will become undefined.
-     * \param [in] addressModel Address model
-     * \param [in] memoryModel Memory model
-     */
-    void setMemoryModel(
-            spv::AddressingModel  addressModel,
-            spv::MemoryModel      memoryModel);
-    
-    /**
-     * \brief Adds an entry point
-     * 
-     * Currently, DXVK expects there to be a single entry point
-     * with the name \c main. Do not create additional entry points.
-     * \param [in] functionId Entry point function ID
-     * \param [in] execModel Execution model for the function
-     * \param [in] name Entry point name that is used by Vulkan
-     * \param [in] interfaceCount Number of additional interface IDs
-     * \param [in] interfaceIds List of additional interface IDs
-     */
-    void addEntryPoint(
-            uint32_t              functionId,
-            spv::ExecutionModel   execModel,
-      const char*                 name,
-            uint32_t              interfaceCount,
-      const uint32_t*             interfaceIds);
-    
-    /**
-     * \brief Enables early fragment tests
-     */
-    void enableEarlyFragmentTests(
-            uint32_t              functionId);
-    
-    /**
-     * \brief Sets local work group size for a compute shader
-     * 
-     * Adds a \c OpExecutionMode instruction that sets
-     * the local work group size for a compute shader.
-     * \param [in] functionId Entry point ID
-     * \param [in] x Number of threads in X direction
-     * \param [in] y Number of threads in Y direction
-     * \param [in] z Number of threads in Z direction
-     */
-    void setLocalSize(
-            uint32_t              functionId,
-            uint32_t              x,
-            uint32_t              y,
-            uint32_t              z);
-    
-  private:
-    
-    SpirvCodeBuffer m_memoryModel;
-    SpirvCodeBuffer m_entryPoints;
-    SpirvCodeBuffer m_execModeInfo;
-    
-  };
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_id.h b/src/spirv/gen/spirv_gen_id.h
deleted file mode 100644
index f0f108b62..000000000
--- a/src/spirv/gen/spirv_gen_id.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#pragma once
-
-#include "../spirv_include.h"
-
-namespace dxvk {
-  
-  /**
-   * \brief SPIR-V ID counter
-   * 
-   * Allocates IDs, starting at zero. This is meant
-   * to be used to allocate unique IDs during code
-   * generation.
-   */
-  class SpirvIdCounter {
-    
-  public:
-    
-    uint32_t nextId() {
-      return ++m_id;
-    }
-    
-    uint32_t numIds() const {
-      return m_id;
-    }
-    
-  private:
-    
-    uint32_t m_id = 0;
-    
-  };
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_typeinfo.cpp b/src/spirv/gen/spirv_gen_typeinfo.cpp
deleted file mode 100644
index b78d1085c..000000000
--- a/src/spirv/gen/spirv_gen_typeinfo.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-#include <array>
-
-#include "spirv_gen_typeinfo.h"
-
-namespace dxvk {
-  
-  SpirvTypeInfo:: SpirvTypeInfo() { }
-  SpirvTypeInfo::~SpirvTypeInfo() { }
-  
-  
-  uint32_t SpirvTypeInfo::typeVoid(SpirvIdCounter& ids) {
-    return this->getTypeId(ids,
-      spv::OpTypeVoid, 0, nullptr);
-  }
-  
-  
-  uint32_t SpirvTypeInfo::typeBool(SpirvIdCounter& ids) {
-    return this->getTypeId(ids,
-      spv::OpTypeBool, 0, nullptr);
-  }
-  
-  
-  uint32_t SpirvTypeInfo::typeInt(
-          SpirvIdCounter&     ids,
-          uint32_t            width,
-          uint32_t            isSigned) {
-    std::array<uint32_t, 2> args = {{ width, isSigned }};
-    return this->getTypeId(ids,
-      spv::OpTypeInt, args.size(), args.data());
-  }
-  
-  
-  uint32_t SpirvTypeInfo::typeFloat(
-          SpirvIdCounter&     ids,
-          uint32_t            width) {
-    return this->getTypeId(ids,
-      spv::OpTypeFloat, 1, &width);
-  }
-  
-  
-  uint32_t SpirvTypeInfo::typeVector(
-          SpirvIdCounter& ids,
-          uint32_t            componentType,
-          uint32_t            componentCount) {
-    std::array<uint32_t, 2> args = {{ componentType, componentCount }};
-    return this->getTypeId(ids,
-      spv::OpTypeVector, args.size(), args.data());
-  }
-  
-  
-  uint32_t SpirvTypeInfo::typeMatrix(
-          SpirvIdCounter& ids,
-          uint32_t            colType,
-          uint32_t            colCount) {
-    std::array<uint32_t, 2> args = {{ colType, colCount }};
-    return this->getTypeId(ids,
-      spv::OpTypeMatrix, args.size(), args.data());
-  }
-  
-  
-  uint32_t SpirvTypeInfo::typeArray(
-          SpirvIdCounter& ids,
-          uint32_t            elementType,
-          uint32_t            elementCount) {
-    std::array<uint32_t, 2> args = {{ elementType, elementCount }};
-    return this->getTypeId(ids,
-      spv::OpTypeArray, args.size(), args.data());
-  }
-  
-  
-  uint32_t SpirvTypeInfo::typeRuntimeArray(
-          SpirvIdCounter& ids,
-          uint32_t            elementType) {
-    return this->getTypeId(ids,
-      spv::OpTypeRuntimeArray, 1, &elementType);
-  }
-  
-  
-  uint32_t SpirvTypeInfo::typePointer(
-          SpirvIdCounter&     ids,
-          spv::StorageClass   storageClass,
-          uint32_t            type) {
-    std::array<uint32_t, 2> args = {{ storageClass, type }};
-    return this->getTypeId(ids,
-      spv::OpTypePointer, args.size(), args.data());
-  }
-  
-  
-  uint32_t SpirvTypeInfo::typeFunction(
-          SpirvIdCounter&     ids,
-          uint32_t            returnType,
-          uint32_t            argCount,
-    const uint32_t*           argTypes) {
-    std::vector<uint32_t> args(argCount + 1);
-    args.at(0) = returnType;
-    
-    for (uint32_t i = 0; i < argCount; i++)
-      args.at(i + 1) = argTypes[i];
-    
-    return this->getTypeId(ids,
-      spv::OpTypeFunction,
-      args.size(), args.data());
-  }
-  
-  
-  uint32_t SpirvTypeInfo::typeStruct(
-          SpirvIdCounter&     ids,
-          uint32_t            memberCount,
-    const uint32_t*           memberTypes) {
-    return this->getTypeId(ids,
-      spv::OpTypeStruct,
-      memberCount,
-      memberTypes);
-  }
-  
-  
-  uint32_t SpirvTypeInfo::getTypeId(
-          SpirvIdCounter&     ids,
-          spv::Op             op,
-          uint32_t            argCount,
-    const uint32_t*           args) {
-    // Since the type info is stored in the code buffer,
-    // we can use the code buffer to look up type IDs as
-    // well. Result IDs are always stored as argument 1.
-    for (auto ins : m_code) {
-      bool match = ins.opCode() == op;
-      
-      for (uint32_t i = 0; i < argCount && match; i++)
-        match &= ins.arg(2 + i) == args[i];
-      
-      if (match)
-        return ins.arg(1);
-    }
-    
-    // Type not yet declared, create a new one.
-    uint32_t result = ids.nextId();
-    m_code.putIns (op, 2 + argCount);
-    m_code.putWord(result);
-    
-    for (uint32_t i = 0; i < argCount; i++)
-      m_code.putWord(args[i]);
-    return result;
-  }
-  
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_typeinfo.h b/src/spirv/gen/spirv_gen_typeinfo.h
deleted file mode 100644
index 637f32fba..000000000
--- a/src/spirv/gen/spirv_gen_typeinfo.h
+++ /dev/null
@@ -1,88 +0,0 @@
-#pragma once
-
-#include "../spirv_code_buffer.h"
-
-#include "spirv_gen_id.h"
-
-namespace dxvk {
-  
-  /**
-   * \brief SPIR-V type set
-   * 
-   * Stores SPIR-V type definition so that
-   * each type will only be declared once.
-   */
-  class SpirvTypeInfo {
-    
-  public:
-    
-    SpirvTypeInfo();
-    ~SpirvTypeInfo();
-    
-    const SpirvCodeBuffer& code() const {
-      return m_code;
-    }
-    
-    uint32_t typeVoid(
-            SpirvIdCounter&     ids);
-    
-    uint32_t typeBool(
-            SpirvIdCounter&     ids);
-    
-    uint32_t typeInt(
-            SpirvIdCounter&     ids,
-            uint32_t            width,
-            uint32_t            isSigned);
-    
-    uint32_t typeFloat(
-            SpirvIdCounter&     ids,
-            uint32_t            width);
-    
-    uint32_t typeVector(
-            SpirvIdCounter&     ids,
-            uint32_t            componentType,
-            uint32_t            componentCount);
-    
-    uint32_t typeMatrix(
-            SpirvIdCounter&     ids,
-            uint32_t            colType,
-            uint32_t            colCount);
-    
-    uint32_t typeArray(
-            SpirvIdCounter&     ids,
-            uint32_t            elementType,
-            uint32_t            elementCount);
-    
-    uint32_t typeRuntimeArray(
-            SpirvIdCounter&     ids,
-            uint32_t            elementType);
-    
-    uint32_t typePointer(
-            SpirvIdCounter&     ids,
-            spv::StorageClass   storageClass,
-            uint32_t            type);
-    
-    uint32_t typeFunction(
-            SpirvIdCounter&     ids,
-            uint32_t            returnType,
-            uint32_t            argCount,
-      const uint32_t*           argTypes);
-    
-    uint32_t typeStruct(
-            SpirvIdCounter&     ids,
-            uint32_t            memberCount,
-      const uint32_t*           memberTypes);
-    
-  private:
-    
-    SpirvCodeBuffer m_code;
-    
-    uint32_t getTypeId(
-            SpirvIdCounter&     ids,
-            spv::Op             op,
-            uint32_t            argCount,
-      const uint32_t*           args);
-    
-  };
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_variable.cpp b/src/spirv/gen/spirv_gen_variable.cpp
deleted file mode 100644
index e41a33369..000000000
--- a/src/spirv/gen/spirv_gen_variable.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
-#include "spirv_gen_variable.h"
-
-namespace dxvk {
-  
-  SpirvVariables:: SpirvVariables() { }
-  SpirvVariables::~SpirvVariables() { }
-  
-}
\ No newline at end of file
diff --git a/src/spirv/gen/spirv_gen_variable.h b/src/spirv/gen/spirv_gen_variable.h
deleted file mode 100644
index a695ab9fd..000000000
--- a/src/spirv/gen/spirv_gen_variable.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include "../spirv_code_buffer.h"
-
-#include "spirv_gen_id.h"
-
-namespace dxvk {
-  
-  /**
-   * \brief SPIR-V variable generator
-   * 
-   * Generates global variable declarations.
-   */
-  class SpirvVariables {
-    
-  public:
-    
-    SpirvVariables();
-    ~SpirvVariables();
-    
-    const SpirvCodeBuffer& code() const {
-      return m_code;
-    }
-    
-  private:
-    
-    SpirvCodeBuffer m_code;
-    
-  };
-  
-}
\ No newline at end of file
diff --git a/src/spirv/meson.build b/src/spirv/meson.build
index c31b799d8..dd1452f3d 100644
--- a/src/spirv/meson.build
+++ b/src/spirv/meson.build
@@ -1,13 +1,6 @@
 spirv_src = files([
   'spirv_code_buffer.cpp',
-  
-  'gen/spirv_gen_capability.cpp',
-  'gen/spirv_gen_constant.cpp',
-  'gen/spirv_gen_debuginfo.cpp',
-  'gen/spirv_gen_decoration.cpp',
-  'gen/spirv_gen_entrypoint.cpp',
-  'gen/spirv_gen_typeinfo.cpp',
-  'gen/spirv_gen_variable.cpp',
+  'spirv_module.cpp',
 ])
 
 spirv_lib = static_library('spirv', spirv_src,
diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp
new file mode 100644
index 000000000..388eaae07
--- /dev/null
+++ b/src/spirv/spirv_module.cpp
@@ -0,0 +1,372 @@
+#include "spirv_module.h"
+
+namespace dxvk {
+  
+  SpirvModule:: SpirvModule() { }
+  SpirvModule::~SpirvModule() { }
+  
+  
+  SpirvCodeBuffer SpirvModule::compile() const {
+    SpirvCodeBuffer result;
+    result.putHeader(m_id);
+    result.append(m_capabilities);
+    result.append(m_memoryModel);
+    result.append(m_entryPoints);
+    result.append(m_execModeInfo);
+    result.append(m_debugNames);
+    result.append(m_annotations);
+    result.append(m_typeDefs);
+    result.append(m_constDefs);
+    result.append(m_variables);
+    result.append(m_code);
+    return result;
+  }
+  
+  
+  uint32_t SpirvModule::allocateId() {
+    return ++m_id;
+  }
+  
+  
+  void SpirvModule::enableCapability(
+          spv::Capability         capability) {
+    // Scan the generated instructions to check
+    // whether we already enabled the capability.
+    for (auto ins : m_capabilities) {
+      if (ins.opCode() == spv::OpCapability && ins.arg(1) == capability)
+        return;
+    }
+    
+    m_capabilities.putIns (spv::OpCapability, 2);
+    m_capabilities.putWord(capability);
+  }
+  
+  
+  void SpirvModule::addEntryPoint(
+          uint32_t                entryPointId,
+          spv::ExecutionModel     executionModel,
+    const char*                   name,
+          uint32_t                interfaceCount,
+    const uint32_t*               interfaceIds) {
+    
+  }
+  
+  
+  void SpirvModule::setMemoryModel(
+          spv::AddressingModel    addressModel,
+          spv::MemoryModel        memoryModel) {
+    
+  }
+  
+  
+  void SpirvModule::enableEarlyFragmentTests(
+          uint32_t                entryPointId) {
+    
+  }
+  
+  
+  void SpirvModule::setLocalSize(
+          uint32_t                entryPointId,
+          uint32_t                x,
+          uint32_t                y,
+          uint32_t                z) {
+    
+  }
+  
+  
+  void SpirvModule::setDebugName(
+          uint32_t                expressionId,
+    const char*                   debugName) {
+    
+  }
+  
+  
+  uint32_t SpirvModule::constBool(
+          bool                    v) {
+    uint32_t typeId   = this->defBoolType();
+    uint32_t resultId = this->allocateId();
+    
+    m_constDefs.putIns  (v ? spv::OpConstantTrue : spv::OpConstantFalse, 3);
+    m_constDefs.putWord (typeId);
+    m_constDefs.putWord (resultId);
+    return resultId;
+  }
+  
+  
+  uint32_t SpirvModule::consti32(
+          int32_t                 v) {
+    uint32_t typeId   = this->defIntType(32, 1);
+    uint32_t resultId = this->allocateId();
+    
+    m_constDefs.putIns  (spv::OpConstant, 4);
+    m_constDefs.putWord (typeId);
+    m_constDefs.putWord (resultId);
+    m_constDefs.putInt32(v);
+    return resultId;
+  }
+  
+  
+  uint32_t SpirvModule::consti64(
+          int64_t                 v) {
+    uint32_t typeId   = this->defIntType(64, 1);
+    uint32_t resultId = this->allocateId();
+    
+    m_constDefs.putIns  (spv::OpConstant, 5);
+    m_constDefs.putWord (typeId);
+    m_constDefs.putWord (resultId);
+    m_constDefs.putInt64(v);
+    return resultId;
+  }
+  
+  
+  uint32_t SpirvModule::constu32(
+          uint32_t                v) {
+    uint32_t typeId   = this->defIntType(32, 0);
+    uint32_t resultId = this->allocateId();
+    
+    m_constDefs.putIns  (spv::OpConstant, 4);
+    m_constDefs.putWord (typeId);
+    m_constDefs.putWord (resultId);
+    m_constDefs.putInt32(v);
+    return resultId;
+  }
+  
+  
+  uint32_t SpirvModule::constu64(
+          uint64_t                v) {
+    uint32_t typeId   = this->defIntType(64, 0);
+    uint32_t resultId = this->allocateId();
+    
+    m_constDefs.putIns  (spv::OpConstant, 5);
+    m_constDefs.putWord (typeId);
+    m_constDefs.putWord (resultId);
+    m_constDefs.putInt64(v);
+    return resultId;
+  }
+  
+  
+  uint32_t SpirvModule::constf32(
+          float                   v) {
+    uint32_t typeId   = this->defFloatType(32);
+    uint32_t resultId = this->allocateId();
+    
+    m_constDefs.putIns  (spv::OpConstant, 4);
+    m_constDefs.putWord (typeId);
+    m_constDefs.putWord (resultId);
+    m_constDefs.putFloat32(v);
+    return resultId;
+  }
+  
+  
+  uint32_t SpirvModule::constf64(
+          double                  v) {
+    uint32_t typeId   = this->defFloatType(64);
+    uint32_t resultId = this->allocateId();
+    
+    m_constDefs.putIns  (spv::OpConstant, 5);
+    m_constDefs.putWord (typeId);
+    m_constDefs.putWord (resultId);
+    m_constDefs.putFloat64(v);
+    return resultId;
+  }
+  
+  
+  uint32_t SpirvModule::constComposite(
+          uint32_t                typeId,
+          uint32_t                constCount,
+    const uint32_t*               constIds) {
+    uint32_t resultId = this->allocateId();
+    
+    m_constDefs.putIns  (spv::OpConstantComposite, 3 + constCount);
+    m_constDefs.putWord (typeId);
+    m_constDefs.putWord (resultId);
+    
+    for (uint32_t i = 0; i < constCount; i++)
+      m_constDefs.putWord(constIds[i]);
+    return resultId;
+  }
+  
+  
+  uint32_t SpirvModule::defVoidType() {
+    return this->defType(spv::OpTypeVoid, 0, nullptr);
+  }
+  
+  
+  uint32_t SpirvModule::defBoolType() {
+    return this->defType(spv::OpTypeBool, 0, nullptr);
+  }
+  
+  
+  uint32_t SpirvModule::defIntType(
+          uint32_t                width,
+          uint32_t                isSigned) {
+    std::array<uint32_t, 2> args = { width, isSigned };
+    return this->defType(spv::OpTypeInt,
+      args.size(), args.data());
+  }
+  
+  
+  uint32_t SpirvModule::defFloatType(
+          uint32_t                width) {
+    std::array<uint32_t, 1> args = { width };
+    return this->defType(spv::OpTypeFloat,
+      args.size(), args.data());
+  }
+  
+  
+  uint32_t SpirvModule::defVectorType(
+          uint32_t                elementType,
+          uint32_t                elementCount) {
+    std::array<uint32_t, 2> args = {
+      elementType,
+      elementCount
+    };
+    
+    return this->defType(spv::OpTypeVector,
+      args.size(), args.data());
+  }
+  
+  
+  uint32_t SpirvModule::defMatrixType(
+          uint32_t                columnType,
+          uint32_t                columnCount) {
+    std::array<uint32_t, 2> args = {
+      columnType,
+      columnCount
+    };
+    
+    return this->defType(spv::OpTypeMatrix,
+      args.size(), args.data());
+  }
+  
+  
+  uint32_t SpirvModule::defArrayType(
+          uint32_t                typeId,
+          uint32_t                length) {
+    std::array<uint32_t, 2> args = { typeId, length };
+    
+    return this->defType(spv::OpTypeArray,
+      args.size(), args.data());
+  }
+  
+  
+  uint32_t SpirvModule::defRuntimeArrayType(
+          uint32_t                typeId) {
+    std::array<uint32_t, 1> args = { typeId };
+    
+    return this->defType(spv::OpTypeRuntimeArray,
+      args.size(), args.data());
+  }
+  
+  
+  uint32_t SpirvModule::defFunctionType(
+          uint32_t                returnType,
+          uint32_t                argCount,
+    const uint32_t*               argTypes) {
+    std::vector<uint32_t> args;
+    args.push_back(returnType);
+    
+    for (uint32_t i = 0; i < argCount; i++)
+      args.push_back(argTypes[i]);
+    
+    return this->defType(spv::OpTypeFunction,
+      args.size(), args.data());
+  }
+  
+  
+  uint32_t SpirvModule::defStructType(
+          uint32_t                memberCount,
+    const uint32_t*               memberTypes) {
+    return this->defType(spv::OpTypeStruct,
+      memberCount, memberTypes);
+  }
+  
+  
+  uint32_t SpirvModule::defPointerType(
+          uint32_t                variableType,
+          spv::StorageClass       storageClass) {
+    std::array<uint32_t, 2> args = {
+      variableType,
+      storageClass,
+    };
+    
+    return this->defType(spv::OpTypePointer,
+      args.size(), args.data());
+  }
+  
+  
+  void SpirvModule::functionBegin(
+          uint32_t                returnType,
+          uint32_t                functionId,
+          uint32_t                functionType,
+    spv::FunctionControlMask      functionControl) {
+    m_code.putIns (spv::OpFunction, 5);
+    m_code.putWord(returnType);
+    m_code.putWord(functionId);
+    m_code.putWord(functionControl);
+    m_code.putWord(functionType);
+  }
+  
+  
+  uint32_t SpirvModule::functionParameter(
+          uint32_t                parameterType) {
+    uint32_t parameterId = this->allocateId();
+    
+    m_code.putIns (spv::OpFunctionParameter, 3);
+    m_code.putWord(parameterType);
+    m_code.putWord(parameterId);
+    return parameterId;
+  }
+  
+  
+  void SpirvModule::functionEnd() {
+    m_code.putIns (spv::OpFunctionEnd, 1);
+  }
+  
+  
+  uint32_t SpirvModule::opFunctionCall(
+          uint32_t                resultType,
+          uint32_t                functionId,
+          uint32_t                argCount,
+    const uint32_t*               argIds) {
+    uint32_t resultId = this->allocateId();
+    
+    m_code.putIns (spv::OpFunctionCall, 4 + argCount);
+    m_code.putWord(resultType);
+    m_code.putWord(resultId);
+    m_code.putWord(functionId);
+    
+    for (uint32_t i = 0; i < argCount; i++)
+      m_code.putWord(argIds[i]);
+    return resultId;
+  }
+  
+  
+  uint32_t SpirvModule::defType(
+          spv::Op                 op, 
+          uint32_t                argCount,
+    const uint32_t*               argIds) {
+    // Since the type info is stored in the code buffer,
+    // we can use the code buffer to look up type IDs as
+    // well. Result IDs are always stored as argument 1.
+    for (auto ins : m_typeDefs) {
+      bool match = ins.opCode() == op;
+      
+      for (uint32_t i = 0; i < argCount && match; i++)
+        match &= ins.arg(2 + i) == argIds[i];
+      
+      if (match)
+        return ins.arg(1);
+    }
+    
+    // Type not yet declared, create a new one.
+    uint32_t resultId = this->allocateId();
+    m_typeDefs.putIns (op, 2 + argCount);
+    m_typeDefs.putWord(resultId);
+    
+    for (uint32_t i = 0; i < argCount; i++)
+      m_typeDefs.putWord(argIds[i]);
+    return resultId;
+  }
+  
+}
\ No newline at end of file
diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h
new file mode 100644
index 000000000..4a3376332
--- /dev/null
+++ b/src/spirv/spirv_module.h
@@ -0,0 +1,157 @@
+#pragma once
+
+#include "spirv_code_buffer.h"
+
+namespace dxvk {
+  
+  /**
+   * \brief SPIR-V module
+   * 
+   * This class generates a code buffer containing a full
+   * SPIR-V shader module. Ensures that the module layout
+   * is valid, as defined in the SPIR-V 1.0 specification,
+   * section 2.4 "Logical Layout of a Module".
+   */
+  class SpirvModule {
+    
+  public:
+    
+    SpirvModule();
+    ~SpirvModule();
+    
+    SpirvCodeBuffer compile() const;
+    
+    uint32_t allocateId();
+    
+    void enableCapability(
+            spv::Capability         capability);
+    
+    void addEntryPoint(
+            uint32_t                entryPointId,
+            spv::ExecutionModel     executionModel,
+      const char*                   name,
+            uint32_t                interfaceCount,
+      const uint32_t*               interfaceIds);
+    
+    void setMemoryModel(
+            spv::AddressingModel    addressModel,
+            spv::MemoryModel        memoryModel);
+    
+    void enableEarlyFragmentTests(
+            uint32_t                entryPointId);
+    
+    void setLocalSize(
+            uint32_t                entryPointId,
+            uint32_t                x,
+            uint32_t                y,
+            uint32_t                z);
+    
+    void setDebugName(
+            uint32_t                expressionId,
+      const char*                   debugName);
+    
+    uint32_t constBool(
+            bool                    v);
+    
+    uint32_t consti32(
+            int32_t                 v);
+    
+    uint32_t consti64(
+            int64_t                 v);
+    
+    uint32_t constu32(
+            uint32_t                v);
+    
+    uint32_t constu64(
+            uint64_t                v);
+    
+    uint32_t constf32(
+            float                   v);
+    
+    uint32_t constf64(
+            double                  v);
+    
+    uint32_t constComposite(
+            uint32_t                typeId,
+            uint32_t                constCount,
+      const uint32_t*               constIds);
+    
+    uint32_t defVoidType();
+    
+    uint32_t defBoolType();
+    
+    uint32_t defIntType(
+            uint32_t                width,
+            uint32_t                isSigned);
+    
+    uint32_t defFloatType(
+            uint32_t                width);
+    
+    uint32_t defVectorType(
+            uint32_t                elementType,
+            uint32_t                elementCount);
+    
+    uint32_t defMatrixType(
+            uint32_t                columnType,
+            uint32_t                columnCount);
+    
+    uint32_t defArrayType(
+            uint32_t                typeId,
+            uint32_t                length);
+    
+    uint32_t defRuntimeArrayType(
+            uint32_t                typeId);
+    
+    uint32_t defFunctionType(
+            uint32_t                returnType,
+            uint32_t                argCount,
+      const uint32_t*               argTypes);
+    
+    uint32_t defStructType(
+            uint32_t                memberCount,
+      const uint32_t*               memberTypes);
+    
+    uint32_t defPointerType(
+            uint32_t                variableType,
+            spv::StorageClass       storageClass);
+    
+    void functionBegin(
+            uint32_t                returnType,
+            uint32_t                functionId,
+            uint32_t                functionType,
+      spv::FunctionControlMask      functionControl);
+    
+    uint32_t functionParameter(
+            uint32_t                parameterType);
+    
+    void functionEnd();
+    
+    uint32_t opFunctionCall(
+            uint32_t                resultType,
+            uint32_t                functionId,
+            uint32_t                argCount,
+      const uint32_t*               argIds);
+    
+  private:
+    
+    uint32_t m_id = 0;
+    
+    SpirvCodeBuffer m_capabilities;
+    SpirvCodeBuffer m_memoryModel;
+    SpirvCodeBuffer m_entryPoints;
+    SpirvCodeBuffer m_execModeInfo;
+    SpirvCodeBuffer m_debugNames;
+    SpirvCodeBuffer m_annotations;
+    SpirvCodeBuffer m_typeDefs;
+    SpirvCodeBuffer m_constDefs;
+    SpirvCodeBuffer m_variables;
+    SpirvCodeBuffer m_code;
+    
+    uint32_t defType(
+            spv::Op                 op, 
+            uint32_t                argCount,
+      const uint32_t*               argIds);
+    
+  };
+  
+}
\ No newline at end of file