code:
#pragma once

#include "dxso_decoder.h"
#include "dxso_header.h"
#include "dxso_modinfo.h"
#include "dxso_isgn.h"
#include "dxso_util.h"

#include "../d3d9/d3d9_constant_layout.h"
#include "../d3d9/d3d9_shader_permutations.h"
#include "../spirv/spirv_module.h"

namespace dxvk {

  /**
   * \brief Scalar value type
   *
   * Enumerates possible register component
   * types. Scalar types are represented as
   * a one-component vector type.
   */
  enum class DxsoScalarType : uint32_t {
    Uint32    = 0,
    Sint32    = 1,
    Float32   = 2,
    Bool      = 3,
  };

  /**
   * \brief Vector type
   *
   * Convenience struct that stores a scalar
   * type and a component count. The compiler
   * can use this to generate SPIR-V types.
   */
  struct DxsoVectorType {
    DxsoScalarType    ctype;
    uint32_t          ccount;
  };

  /**
   * \brief Array type
   *
   * Convenience struct that stores a scalar type, a
   * component count and an array size. An array of
   * length 0 will be evaluated to a vector type. The
   * compiler can use this to generate SPIR-V types.
   */
  struct DxsoArrayType {
    DxsoScalarType    ctype;
    uint32_t          ccount;
    uint32_t          alength;
  };

  /**
   * \brief Register info
   *
   * Stores the array type of a register and
   * its storage class. The compiler can use
   * this to generate SPIR-V pointer types.
   */
  struct DxsoRegisterInfo {
    DxsoArrayType     type;
    spv::StorageClass sclass;
  };

  /**
   * \brief Register value
   *
   * Stores a vector type and a SPIR-V ID that
   * represents an intermediate value. This is
   * used to track the type of such values.
   */
  struct DxsoRegisterValue {
    DxsoVectorType    type;
    uint32_t          id;
  };

  /**
   * \brief Register pointer
   *
   * Stores a vector type and a SPIR-V ID that
   * represents a pointer to such a vector. This
   * can be used to load registers conveniently.
   */
  struct DxsoRegisterPointer {
    DxsoVectorType    type;
    uint32_t          id   = 0;
  };

  /**
   * \brief Sampler info
   *
   * Stores a vector type and a SPIR-V ID that
   * represents a pointer to such a vector. This
   * can be used to load registers conveniently.
   */
  struct DxsoSamplerInfo {
    uint32_t dimensions = 0;

    uint32_t varId = 0;
    uint32_t typeId = 0;

    uint32_t imageTypeId = 0;
  };

  enum DxsoSamplerType : uint32_t {
    SamplerTypeTexture2D = 0,
    SamplerTypeTexture3D = 1,
    SamplerTypeTextureCube,

    SamplerTypeCount
  };

  inline auto SamplerTypeFromTextureType(DxsoTextureType type) {
    switch (type) {
      default:
      case DxsoTextureType::Texture2D:   return SamplerTypeTexture2D;   break;
      case DxsoTextureType::Texture3D:   return SamplerTypeTexture3D;   break;
      case DxsoTextureType::TextureCube: return SamplerTypeTextureCube; break;
    }
  }

  struct DxsoSampler {
    DxsoSamplerInfo color[SamplerTypeCount];
    DxsoSamplerInfo depth[SamplerTypeCount];

    uint32_t boundConst;

    DxsoTextureType type;
  };

  struct DxsoAnalysisInfo;

  /**
   * \brief Vertex shader-specific structure
   */
  struct DxsoCompilerVsPart {
    uint32_t functionId       = 0;

    ////////////////////
    // Address register
    DxsoRegisterPointer addr;

    //////////////////////////////
    // Rasterizer output registers
    DxsoRegisterPointer oPos;
    DxsoRegisterPointer oPSize;
  };

  /**
   * \brief Pixel shader-specific structure
   */
  struct DxsoCompilerPsPart {
    uint32_t functionId         = 0;
    uint32_t samplerTypeSpec    = 0;
    uint32_t projectionSpec     = 0;
    uint32_t fetch4Spec         = 0;

    //////////////
    // Misc Types
    DxsoRegisterPointer vPos;
    DxsoRegisterPointer vFace;

    ///////////////////
    // Colour Outputs
    std::array<DxsoRegisterPointer, 4> oColor;

    ////////////////
    // Depth output
    DxsoRegisterPointer oDepth;

    ////////////////
    // Shared State
    uint32_t sharedState        = 0;

    uint32_t killState          = 0;
    uint32_t builtinLaneId      = 0;

    uint32_t diffuseColorIn  = 0;
    uint32_t specularColorIn = 0;
  };

  struct DxsoCfgBlockIf {
    uint32_t ztestId;
    uint32_t labelIf;
    uint32_t labelElse;
    uint32_t labelEnd;
    size_t   headerPtr;
  };

  struct DxsoCfgBlockLoop {
    uint32_t labelHeader;
    uint32_t labelBegin;
    uint32_t labelContinue;
    uint32_t labelBreak;
    uint32_t iteratorPtr;

    uint32_t strideVar;
    uint32_t countBackup;
  };

  enum class DxsoCfgBlockType : uint32_t {
    If, Loop
  };

  struct DxsoCfgBlock {
    DxsoCfgBlockType type;

    union {
      DxsoCfgBlockIf     b_if;
      DxsoCfgBlockLoop   b_loop;
    };
  };

  using DxsoSrcArray = std::array<DxsoRegisterValue, DxsoMaxOperandCount>;

  class DxsoCompiler {

  public:

    DxsoCompiler(
      const std::string&        fileName,
      const DxsoModuleInfo&     moduleInfo,
      const DxsoProgramInfo&    programInfo,
      const DxsoAnalysisInfo&   analysis,
      const D3D9ConstantLayout& layout);

    /**
     * \brief Processes a single instruction
     * \param [in] ins The instruction
     */
    void processInstruction(
      const DxsoInstructionContext& ctx,
            uint32_t                currentCoissueIdx = 0);

    /**
     * \brief Finalizes the shader
     */
    void finalize();

    /**
     * \brief Compiles the shader
     * \returns The final shader objects
     */
    DxsoPermutations compile();

    const DxsoIsgn& isgn() { return m_isgn; }
    const DxsoIsgn& osgn() { return m_osgn; }

    const DxsoShaderMetaInfo& meta() { return m_meta; }
    const DxsoDefinedConstants& constants() { return m_constants; }
    uint32_t usedSamplers() const { return m_usedSamplers; }
    uint32_t usedRTs() const { return m_usedRTs; }
    uint32_t maxDefinedConstant() const { return m_maxDefinedConstant; }

  private:

    DxsoModuleInfo             m_moduleInfo;
    DxsoProgramInfo            m_programInfo;
    const DxsoAnalysisInfo*    m_analysis;
    const D3D9ConstantLayout*  m_layout;

    DxsoShaderMetaInfo         m_meta;
    DxsoDefinedConstants       m_constants;
    uint32_t                   m_maxDefinedConstant;

    SpirvModule                m_module;

    uint32_t                   m_boolSpecConstant;
    uint32_t                   m_depthSpecConstant;

    ///////////////////////////////////////////////////////
    // Resource slot description for the shader. This will
    // be used to map D3D9 bindings to DXVK bindings.
    std::vector<DxvkResourceSlot> m_resourceSlots;

    ////////////////////////////////////////////////
    // Temporary r# vector registers with immediate
    // indexing, and x# vector array registers.
    std::array<
      DxsoRegisterPointer,
      DxsoMaxTempRegs> m_rRegs;

    ////////////////////////////////////////////////
    // Predicate registers
    std::array<
      DxsoRegisterPointer,
      1> m_pRegs;

    //////////////////////////////////////////////////////////////////
    // Array of input values. Since v# and o# registers are indexable
    // in DXSO, we need to copy them into an array first.
    uint32_t m_vArray = 0;
    uint32_t m_oArray = 0;

    ////////////////////////////////
    // Input and output signatures
    DxsoIsgn m_isgn;
    DxsoIsgn m_osgn;

    ////////////////////////////////////
    // Ptr to the constant buffer array
    uint32_t m_cBuffer = 0;
    uint32_t m_cFloatBuffer = 0;
    uint32_t m_cIntBuffer = 0;
    uint32_t m_cBoolBuffer = 0;

    ////////////////////////////////////////
    // Constant buffer deffed mappings
    std::array<uint32_t, caps::MaxFloatConstantsSoftware> m_cFloat;
    std::array<uint32_t, caps::MaxOtherConstantsSoftware> m_cInt;
    std::array<uint32_t, caps::MaxOtherConstantsSoftware> m_cBool;

    //////////////////////
    // Loop counter
    DxsoRegisterPointer m_loopCounter;

    ///////////////////////////////////
    // Working tex/coord registers (PS)
    std::array<
      DxsoRegisterPointer,
      DxsoMaxTextureRegs> m_tRegs;

    ///////////////////////////////////////////////
    // Control flow information. Stores labels for
    // currently active if-else blocks and loops.
    std::vector<DxsoCfgBlock> m_controlFlowBlocks;

    //////////////////////////////////////////////
    // Function state tracking. Required in order
    // to properly end functions in some cases.
    bool m_insideFunction = false;

    ////////////
    // Samplers
    std::array<DxsoSampler, 17> m_samplers;

    ////////////////////////////////////////////
    // What io regswe need to
    // NOT generate semantics for
    uint16_t m_explicitInputs  = 0;
    uint16_t m_explicitOutputs = 0;

    ///////////////////////////////////////////////////
    // Entry point description - we'll need to declare
    // the function ID and all input/output variables.
    std::vector<uint32_t> m_entryPointInterfaces;
    uint32_t m_entryPointId = 0;

    ////////////////////////////////////////////
    // Inter-stage shader interface slots. Also
    // covers vertex input and fragment output.
    uint32_t m_inputMask = 0u;
    uint32_t m_outputMask = 0u;
    uint32_t m_pushConstOffset = 0u;
    uint32_t m_pushConstSize = 0u;

    ///////////////////////////////////
    // Shader-specific data structures
    DxsoCompilerVsPart m_vs;
    DxsoCompilerPsPart m_ps;

    DxsoRegisterPointer m_fog;

    //////////////////////////////////////////
    // Bit masks containing used samplers
    // and render targets for hazard tracking
    uint32_t m_usedSamplers;
    uint32_t m_usedRTs;

    uint32_t m_rsBlock = 0;
    uint32_t m_mainFuncLabel = 0;

    //////////////////////////////////////
    // Common function definition methods
    void emitInit();

    //////////////////////
    // Common shader dcls
    template<DxsoConstantBufferType ConstantBufferType>
    int emitDclSwvpConstantBuffer();

    void emitDclConstantBuffer();

    void emitDclInputArray();
    void emitDclOutputArray();

    /////////////////////////////////
    // Shader initialization methods
    void emitVsInit();

    void emitPsSharedConstants();
    void emitPsInit();

    void emitFunctionBegin(
            uint32_t                entryPoint,
            uint32_t                returnType,
            uint32_t                funcType);

    void emitFunctionEnd();

    uint32_t emitFunctionLabel();

    void emitMainFunctionBegin();

    ///////////////////////////////
    // Variable definition methods
    uint32_t emitNewVariable(
      const DxsoRegisterInfo& info);

    uint32_t emitNewVariableDefault(
      const DxsoRegisterInfo& info,
            uint32_t          value);

    uint32_t emitNewBuiltinVariable(
      const DxsoRegisterInfo& info,
            spv::BuiltIn      builtIn,
      const char*             name,
            uint32_t          value);

    DxsoCfgBlock* cfgFindBlock(
      const std::initializer_list<DxsoCfgBlockType>& types);

    void emitDclInterface(
            bool         input,
            uint32_t     regNumber,
            DxsoSemantic semantic,
            DxsoRegMask  mask,
            bool         centroid);

    void emitDclSampler(
            uint32_t        idx,
            DxsoTextureType type);

    bool defineInput(uint32_t idx) {
      bool alreadyDefined = m_inputMask & 1u << idx;
      m_inputMask |= 1u << idx;
      return alreadyDefined;
    }

    bool defineOutput(uint32_t idx) {
      bool alreadyDefined = m_outputMask & 1u << idx;
      m_outputMask |= 1u << idx;
      return alreadyDefined;
    }

    uint32_t emitArrayIndex(
            uint32_t          idx,
      const DxsoBaseRegister* relative);

    DxsoRegisterPointer emitInputPtr(
            bool              texture,
      const DxsoBaseRegister& reg,
      const DxsoBaseRegister* relative);

    DxsoRegisterPointer emitRegisterPtr(
      const char*             name,
            DxsoScalarType    ctype,
            uint32_t          ccount,
            uint32_t          defaultVal,
            spv::StorageClass storageClass = spv::StorageClassPrivate,
            spv::BuiltIn      builtIn      = spv::BuiltInMax);

    DxsoRegisterValue emitLoadConstant(
      const DxsoBaseRegister& reg,
      const DxsoBaseRegister* relative);

    DxsoRegisterPointer emitOutputPtr(
            bool              texcrdOut,
      const DxsoBaseRegister& reg,
      const DxsoBaseRegister* relative);

    DxsoRegisterPointer emitGetOperandPtr(
      const DxsoBaseRegister& reg,
      const DxsoBaseRegister* relative);

    DxsoRegisterPointer emitGetOperandPtr(
      const DxsoRegister& reg) {
      return this->emitGetOperandPtr(
        reg,
        reg.hasRelative ? &reg.relative : nullptr);
    }

    uint32_t emitBoolComparison(DxsoVectorType type, DxsoComparison cmp, uint32_t a, uint32_t b);

    DxsoRegisterValue emitValueLoad(
            DxsoRegisterPointer ptr);

    void emitDstStore(
            DxsoRegisterPointer     ptr,
            DxsoRegisterValue       value,
            DxsoRegMask             writeMask,
            bool                    saturate,
            DxsoRegisterValue       predicate,
            int8_t                  shift,
            DxsoRegisterId          regId) {
      if (regId.type == DxsoRegisterType::RasterizerOut && regId.num == RasterOutFog)
        saturate = true;

      if (value.type.ctype == DxsoScalarType::Float32) {
        const uint32_t typeId = getVectorTypeId(value.type);

        // There doesn't seem to be a nice float bitshift method for float vectors
        // in Spirv that I can see... Resorting to multiplication.
        if (shift != 0) {
          float shiftAmount = shift < 0
            ? 1.0f / (1 << -shift)
            : float(1 << shift);

          uint32_t shiftConst = m_module.constf32(shiftAmount);

          if (value.type.ccount == 1)
            value.id = m_module.opFMul(typeId, value.id, shiftConst);
          else
            value.id = m_module.opVectorTimesScalar(typeId, value.id, shiftConst);
        }

        // Saturating only makes sense on floats
        if (saturate) {
          value.id = m_module.opNClamp(
            typeId, value.id,
            m_module.constfReplicant(0.0f, value.type.ccount),
            m_module.constfReplicant(1.0f, value.type.ccount));
        }
      }

      this->emitValueStore(ptr, value, writeMask, predicate);
    }

    DxsoRegisterValue applyPredicate(DxsoRegisterValue pred, DxsoRegisterValue dst, DxsoRegisterValue src);

    void emitValueStore(
            DxsoRegisterPointer     ptr,
            DxsoRegisterValue       value,
            DxsoRegMask             writeMask,
            DxsoRegisterValue       predicate);

    DxsoRegisterValue emitClampBoundReplicant(
            DxsoRegisterValue       srcValue,
            float                   lb,
            float                   ub);

    DxsoRegisterValue emitSaturate(
            DxsoRegisterValue       srcValue);

    DxsoRegisterValue emitMulOperand(
            DxsoRegisterValue       operand,
            DxsoRegisterValue       other);

    DxsoRegisterValue emitMul(
            DxsoRegisterValue       a,
            DxsoRegisterValue       b);

    DxsoRegisterValue emitFma(
            DxsoRegisterValue       a,
            DxsoRegisterValue       b,
            DxsoRegisterValue       c);

    DxsoRegisterValue emitDot(
            DxsoRegisterValue       a,
            DxsoRegisterValue       b);

    DxsoRegisterValue emitMix(
            DxsoRegisterValue       x,
            DxsoRegisterValue       y,
            DxsoRegisterValue       a);

    DxsoRegisterValue emitCross(
            DxsoRegisterValue       a,
            DxsoRegisterValue       b);

    DxsoRegisterValue emitRegisterInsert(
            DxsoRegisterValue       dstValue,
            DxsoRegisterValue       srcValue,
            DxsoRegMask             srcMask);

    DxsoRegisterValue emitRegisterLoadRaw(
      const DxsoBaseRegister& reg,
      const DxsoBaseRegister* relative);

    DxsoRegisterValue emitRegisterExtend(
            DxsoRegisterValue       value,
            uint32_t size);

    DxsoRegisterValue emitSrcOperandPreSwizzleModifiers(
            DxsoRegisterValue       value,
            DxsoRegModifier         modifier);

    DxsoRegisterValue emitSrcOperandPostSwizzleModifiers(
            DxsoRegisterValue       value,
            DxsoRegModifier         modifier);

    DxsoRegisterValue emitRegisterSwizzle(
            DxsoRegisterValue       value,
            DxsoRegSwizzle          swizzle,
            DxsoRegMask             writeMask);

    DxsoRegisterValue emitRegisterLoad(
      const DxsoBaseRegister& reg,
            DxsoRegMask       writeMask,
      const DxsoBaseRegister* relative);

    DxsoRegisterValue emitRegisterLoad(
      const DxsoRegister& reg,
            DxsoRegMask   writeMask) {
      return this->emitRegisterLoad(
        reg, writeMask,
        reg.hasRelative ? &reg.relative : nullptr);
    }

    DxsoRegisterValue emitPredicateLoad(const DxsoInstructionContext& ctx) {
      if (!ctx.instruction.predicated)
        return DxsoRegisterValue();

      return emitRegisterLoad(ctx.pred, IdentityWriteMask);
    }

    DxsoRegisterValue emitRegisterLoadTexcoord(
      const DxsoRegister& reg,
            DxsoRegMask   writeMask) {
      DxsoRegister lookup = reg;
      if (reg.id.type == DxsoRegisterType::Texture)
        lookup.id.type = DxsoRegisterType::PixelTexcoord;

      return this->emitRegisterLoad(lookup, writeMask);
    }

    Rc<DxvkShader> compileShader();

    ///////////////////////////////
    // Handle shader ops
    void emitDcl(const DxsoInstructionContext& ctx);

    void emitDef(const DxsoInstructionContext& ctx);
    void emitDefF(const DxsoInstructionContext& ctx);
    void emitDefI(const DxsoInstructionContext& ctx);
    void emitDefB(const DxsoInstructionContext& ctx);

    bool isScalarRegister(DxsoRegisterId id);

    void emitMov(const DxsoInstructionContext& ctx);
    void emitPredicateOp(const DxsoInstructionContext& ctx);
    void emitVectorAlu(const DxsoInstructionContext& ctx);
    void emitMatrixAlu(const DxsoInstructionContext& ctx);

    void emitControlFlowGenericLoop(
            bool     count,
            uint32_t initialVar,
            uint32_t strideVar,
            uint32_t iterationCountVar);

    void emitControlFlowGenericLoopEnd();

    void emitControlFlowRep(const DxsoInstructionContext& ctx);
    void emitControlFlowEndRep(const DxsoInstructionContext& ctx);

    void emitControlFlowLoop(const DxsoInstructionContext& ctx);
    void emitControlFlowEndLoop(const DxsoInstructionContext& ctx);

    void emitControlFlowBreak(const DxsoInstructionContext& ctx);
    void emitControlFlowBreakC(const DxsoInstructionContext& ctx);

    void emitControlFlowIf(const DxsoInstructionContext& ctx);
    void emitControlFlowElse(const DxsoInstructionContext& ctx);
    void emitControlFlowEndIf(const DxsoInstructionContext& ctx);

    void emitTexCoord(const DxsoInstructionContext& ctx);
    void emitTextureSample(const DxsoInstructionContext& ctx);
    void emitTextureKill(const DxsoInstructionContext& ctx);
    void emitTextureDepth(const DxsoInstructionContext& ctx);

    uint32_t emitSample(
            bool                    projected,
            uint32_t                resultType,
            DxsoSamplerInfo&        samplerInfo,
            DxsoRegisterValue       coordinates,
            uint32_t                reference,
            uint32_t                fetch4,
      const SpirvImageOperands&     operands);

    ///////////////////////////////
    // Shader finalization methods
    void emitInputSetup();

    void emitVsClipping();
    void setupRenderStateInfo();
    void emitFog();
    void emitPsProcessing();
    void emitOutputDepthClamp();

    void emitLinkerOutputSetup();

    void emitVsFinalize();
    void emitPsFinalize();

    ///////////////////////////
    // Type definition methods
    uint32_t getScalarTypeId(
            DxsoScalarType type);

    uint32_t getVectorTypeId(
      const DxsoVectorType& type);

    uint32_t getArrayTypeId(
      const DxsoArrayType& type);

    uint32_t getPointerTypeId(
      const DxsoRegisterInfo& type);

    bool isSwvp() {
      return m_layout->bitmaskCount != 1;
    }

  };

}

repo_name: orochi/dxvk


code:
#include "dxso_ctab.h"

namespace dxvk {

  DxsoCtab::DxsoCtab(DxsoReader& reader, uint32_t commentTokenCount) {
    m_size          = reader.readu32();

    if (m_size != sizeof(DxsoCtab))
      throw DxvkError("DxsoCtab: ctab size invalid");

    m_creator       = reader.readu32();
    m_version       = reader.readu32();
    m_constants     = reader.readu32();
    m_constantInfo  = reader.readu32();
    m_flags         = reader.readu32();
    m_target        = reader.readu32();
  }

}


code:
#pragma once

#include "dxso_common.h"

#include "dxso_reader.h"

namespace dxvk {

  /**
    * \brief DXSO CTAB
    *
    * Stores meta information about the shader
    */
  class DxsoCtab : public RcObject {

  public:

    DxsoCtab(DxsoReader& reader, uint32_t commentTokenCount);

  private:

    uint32_t m_size;
    uint32_t m_creator;
    uint32_t m_version;
    uint32_t m_constants;
    uint32_t m_constantInfo;
    uint32_t m_flags;
    uint32_t m_target;

  };

}


code:
#include "dxso_decoder.h"

#include "dxso_tables.h"

namespace dxvk {

  bool DxsoSemantic::operator== (const DxsoSemantic& b) const {
    return usage == b.usage && usageIndex == b.usageIndex;
  }

  bool DxsoSemantic::operator!= (const DxsoSemantic& b) const {
    return usage != b.usage || usageIndex != b.usageIndex;
  }

  uint32_t DxsoDecodeContext::decodeInstructionLength(uint32_t token) {
    auto opcode = m_ctx.instruction.opcode;

    uint32_t length  = 0;
    const auto& info = this->getProgramInfo();

    // Comment ops have their own system for getting length.
    if (opcode == DxsoOpcode::Comment)
      return (token & 0x7fff0000) >> 16;

    if (opcode == DxsoOpcode::End)
      return 0;

    // SM2.0 and above has the length of the op in instruction count baked into it.
    // SM1.4 and below have fixed lengths and run off expectation.
    // Phase does not respect the following rules. :shrug:
    if (opcode != DxsoOpcode::Phase) {
      if (info.majorVersion() >= 2)
        length = (token & 0x0f000000) >> 24;
      else
        length = DxsoGetDefaultOpcodeLength(opcode);
    }

    // We've already logged this...
    if (length == InvalidOpcodeLength)
      return 0;

    // SM 1.4 has an extra param on Tex and TexCoord
    // As stated before, it also doesn't have the length of the op baked into the opcode
    if (info.majorVersion() == 1
     && info.minorVersion() == 4) {
      switch (opcode) {
        case DxsoOpcode::TexCoord:
        case DxsoOpcode::Tex: length += 1;
        default: break;
      }
    }

    return length;
  }

  bool DxsoDecodeContext::relativeAddressingUsesToken(
          DxsoInstructionArgumentType type) {
    auto& info = this->getProgramInfo();

    return (info.majorVersion() >= 2 && type == DxsoInstructionArgumentType::Source)
        || (info.majorVersion() >= 3 && type == DxsoInstructionArgumentType::Destination);
  }

  void DxsoDecodeContext::decodeDeclaration(DxsoCodeIter& iter) {
    uint32_t dclToken = iter.read();

    m_ctx.dcl.textureType          = static_cast<DxsoTextureType>((dclToken & 0x78000000) >> 27);
    m_ctx.dcl.semantic.usage       = static_cast<DxsoUsage>(dclToken & 0x0000000f);
    m_ctx.dcl.semantic.usageIndex  = (dclToken & 0x000f0000) >> 16;
  }

  void DxsoDecodeContext::decodeDefinition(DxsoOpcode opcode, DxsoCodeIter& iter) {
    const uint32_t instructionLength = std::min(m_ctx.instruction.tokenLength - 1, 4u);

    for (uint32_t i = 0; i < instructionLength; i++)
      m_ctx.def.uint32[i] = iter.read();
  }

  void DxsoDecodeContext::decodeBaseRegister(
            DxsoBaseRegister& reg,
            uint32_t          token) {
    reg.id.type = static_cast<DxsoRegisterType>(
        ((token & 0x00001800) >> 8)
      | ((token & 0x70000000) >> 28));

    reg.id.num = token & 0x000007ff;
  }

  void DxsoDecodeContext::decodeGenericRegister(
            DxsoRegister& reg,
            uint32_t      token) {
    this->decodeBaseRegister(reg, token);

    reg.hasRelative = (token & (1 << 13)) == 8192;
    reg.relative.id = DxsoRegisterId {
      DxsoRegisterType::Addr, 0 };
    reg.relative.swizzle = IdentitySwizzle;

    reg.centroid         = token & (4 << 20);
    reg.partialPrecision = token & (2 << 20);
  }

   void DxsoDecodeContext::decodeRelativeRegister(
            DxsoBaseRegister& reg,
            uint32_t          token) {
    this->decodeBaseRegister(reg, token);

    reg.swizzle = DxsoRegSwizzle(
      uint8_t((token & 0x00ff0000) >> 16));
  }

  bool DxsoDecodeContext::decodeDestinationRegister(DxsoCodeIter& iter) {
    uint32_t token = iter.read();

    this->decodeGenericRegister(m_ctx.dst, token);

    m_ctx.dst.mask = DxsoRegMask(
      uint8_t((token & 0x000f0000) >> 16));

    m_ctx.dst.saturate = (token & (1 << 20)) != 0;

    m_ctx.dst.shift    = (token & 0x0f000000) >> 24;
    m_ctx.dst.shift    = (m_ctx.dst.shift & 0x7) - (m_ctx.dst.shift & 0x8);

    const bool extraToken =
      relativeAddressingUsesToken(DxsoInstructionArgumentType::Destination);

    if (m_ctx.dst.hasRelative && extraToken) {
      this->decodeRelativeRegister(m_ctx.dst.relative, iter.read());
      return true;
    }

    return false;
  }

  bool DxsoDecodeContext::decodeSourceRegister(uint32_t i, DxsoCodeIter& iter) {
    if (i >= m_ctx.src.size())
      throw DxvkError("DxsoDecodeContext::decodeSourceRegister: source register out of range.");

    uint32_t token = iter.read();

    this->decodeGenericRegister(m_ctx.src[i], token);

    m_ctx.src[i].swizzle = DxsoRegSwizzle(
      uint8_t((token & 0x00ff0000) >> 16));

    m_ctx.src[i].modifier = static_cast<DxsoRegModifier>(
      (token & 0x0f000000) >> 24);

    const bool extraToken =
      relativeAddressingUsesToken(DxsoInstructionArgumentType::Source);

    if (m_ctx.src[i].hasRelative && extraToken) {
      this->decodeRelativeRegister(m_ctx.src[i].relative, iter.read());
      return true;
    }

    return false;
  }

  void DxsoDecodeContext::decodePredicateRegister(DxsoCodeIter& iter) {
    uint32_t token = iter.read();

    this->decodeGenericRegister(m_ctx.pred, token);

    m_ctx.pred.swizzle = DxsoRegSwizzle(
      uint8_t((token & 0x00ff0000) >> 16));

    m_ctx.pred.modifier = static_cast<DxsoRegModifier>(
      (token & 0x0f000000) >> 24);
  }

  bool DxsoDecodeContext::decodeInstruction(DxsoCodeIter& iter) {
    uint32_t token = iter.read();

    m_ctx.instructionIdx++;

    m_ctx.instruction.opcode = static_cast<DxsoOpcode>(
      token & 0x0000ffff);

    m_ctx.instruction.predicated = token & (1 << 28);

    m_ctx.instruction.coissue    = token & 0x40000000;

    m_ctx.instruction.specificData.uint32 =
      (token & 0x00ff0000) >> 16;

    m_ctx.instruction.tokenLength =
      this->decodeInstructionLength(token);

    uint32_t tokenLength =
      m_ctx.instruction.tokenLength;

    switch (m_ctx.instruction.opcode) {
      case DxsoOpcode::If:
      case DxsoOpcode::Ifc:
      case DxsoOpcode::Rep:
      case DxsoOpcode::Loop:
      case DxsoOpcode::BreakC:
      case DxsoOpcode::BreakP: {
        uint32_t sourceIdx = 0;
        for (uint32_t i = 0; i < tokenLength; i++) {
          if (this->decodeSourceRegister(sourceIdx, iter))
            i++;

          sourceIdx++;
        }
        return true;
      }

      case DxsoOpcode::Dcl:
        this->decodeDeclaration(iter);
        this->decodeDestinationRegister(iter);
        return true;

      case DxsoOpcode::Def:
      case DxsoOpcode::DefI:
      case DxsoOpcode::DefB:
        this->decodeDestinationRegister(iter);
        this->decodeDefinition(
          m_ctx.instruction.opcode, iter);
        return true;

      case DxsoOpcode::Comment:
        iter = iter.skip(tokenLength);
        return true;

      default: {
        uint32_t sourceIdx = 0;
        for (uint32_t i = 0; i < tokenLength; i++) {
          if (i == 0) {
            if (this->decodeDestinationRegister(iter))
              i++;
          }
          else if (i == 1 && m_ctx.instruction.predicated) {
            // Relative addressing makes no sense
            // for predicate registers.
            this->decodePredicateRegister(iter);
          }
          else {
            if (this->decodeSourceRegister(sourceIdx, iter))
              i++;

            sourceIdx++;
          }
        }
        return true;
      }

      case DxsoOpcode::End:
        return false;
    }
  }

  std::ostream& operator << (std::ostream& os, DxsoUsage usage) {
    switch (usage) {
      case DxsoUsage::Position:     os << "Position"; break;
      case DxsoUsage::BlendWeight:  os << "BlendWeight"; break;
      case DxsoUsage::BlendIndices: os << "BlendIndices"; break;
      case DxsoUsage::Normal:       os << "Normal"; break;
      case DxsoUsage::PointSize:    os << "PointSize"; break;
      case DxsoUsage::Texcoord:     os << "Texcoord"; break;
      case DxsoUsage::Tangent:      os << "Tangent"; break;
      case DxsoUsage::Binormal:     os << "Binormal"; break;
      case DxsoUsage::TessFactor:   os << "TessFactor"; break;
      case DxsoUsage::PositionT:    os << "PositionT"; break;
      case DxsoUsage::Color:        os << "Color"; break;
      case DxsoUsage::Fog:          os << "Fog"; break;
      case DxsoUsage::Depth:        os << "Depth"; break;
      case DxsoUsage::Sample:       os << "Sample"; break;
      default:
        os << "Invalid Format (" << static_cast<uint32_t>(usage) << ")"; break;
    }

    return os;
  }

}


code:
#pragma once

#include "dxso_common.h"
#include "dxso_enums.h"
#include "dxso_code.h"

namespace dxvk {

  constexpr size_t DxsoMaxTempRegs      = 32;
  constexpr size_t DxsoMaxTextureRegs   = 10;
  constexpr size_t DxsoMaxInterfaceRegs = 16;
  constexpr size_t DxsoMaxOperandCount  = 8;

  constexpr uint32_t DxsoRegModifierShift = 24;

  class DxsoDecodeContext;

  /**
   * \brief Source operand modifiers
   *
   * These are applied after loading
   * an operand register.
   */
  enum class DxsoRegModifier : uint32_t {
    None    = 0,  // r
    Neg     = 1,  // -r
    Bias    = 2,  // r - 0.5
    BiasNeg = 3,  // -(r - 0.5)
    Sign    = 4,  // fma(r, 2.0f, -1.0f)
    SignNeg = 5,  // -fma(r, 2.0f, -1.0f)
    Comp    = 6,  // 1 - r
    X2      = 7,  // r * 2
    X2Neg   = 8,  // -r * 2
    Dz      = 9,  // r / r.z
    Dw      = 10, // r / r.w
    Abs     = 11, // abs(r)
    AbsNeg  = 12, // -abs(r)
    Not     = 13, // !r
  };

  enum class DxsoInstructionArgumentType : uint16_t {
    Source,
    Destination
  };

  enum class DxsoComparison : uint32_t {
                             // < = >
    Never        = 0,        // 0 0 0
    GreaterThan  = 1,        // 0 0 1
    Equal        = 2,        // 0 1 0
    GreaterEqual = 3,        // 0 1 1
    LessThan     = 4,        // 1 0 0
    NotEqual     = 5,        // 1 0 1
    LessEqual    = 6,        // 1 1 0
    Always       = 7         // 1 1 1
  };

  enum class DxsoTexLdMode : uint32_t {
    Regular      = 0,
    Project      = 1,
    Bias         = 2
  };

  union DxsoOpcodeSpecificData {
    DxsoComparison comparison;
    DxsoTexLdMode  texld;

    uint32_t       uint32;
  };

  struct DxsoShaderInstruction {
    DxsoOpcode             opcode;
    bool                   predicated;
    bool                   coissue;
    DxsoOpcodeSpecificData specificData;

    uint32_t               tokenLength;
  };

  struct DxsoRegisterId {
    DxsoRegisterType type;
    uint32_t         num;

    bool operator == (const DxsoRegisterId& other) const { return type == other.type && num == other.num; }
    bool operator != (const DxsoRegisterId& other) const { return type != other.type || num != other.num; }
  };

  class DxsoRegMask {

  public:

    DxsoRegMask(uint8_t mask)
      : m_mask(mask) { }

    DxsoRegMask(bool x, bool y, bool z, bool w)
    : m_mask((x ? 0x1 : 0) | (y ? 0x2 : 0)
           | (z ? 0x4 : 0) | (w ? 0x8 : 0)) { }

    bool operator [] (uint32_t id) const {
      return ((m_mask & (1u << id)) != 0);
    }

    uint32_t popCount() const {
      const uint8_t n[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
                              1, 2, 2, 3, 2, 3, 3, 4 };
      return n[m_mask & 0xF];
    }

    uint32_t firstSet() const {
      const uint8_t n[16] = { 4, 0, 1, 0, 2, 0, 1, 0,
                              3, 0, 1, 0, 2, 0, 1, 0 };
      return n[m_mask & 0xF];
    }

    uint32_t minComponents() const {
      const uint8_t n[16] = { 0, 1, 2, 2, 3, 3, 3, 3,
                              4, 4, 4, 4, 4, 4, 4, 4 };
      return n[m_mask & 0xF];
    }

    bool operator == (const DxsoRegMask& other) const { return m_mask == other.m_mask; }
    bool operator != (const DxsoRegMask& other) const { return m_mask != other.m_mask; }

  private:

    uint8_t m_mask;

  };

  const DxsoRegMask IdentityWriteMask = DxsoRegMask(true, true, true, true);

  class DxsoRegSwizzle {

  public:

    DxsoRegSwizzle(uint8_t mask)
      : m_mask(mask) { }

    DxsoRegSwizzle(uint32_t x, uint32_t y, uint32_t z, uint32_t w)
      : m_mask((x << 0) | (y << 2) | (z << 4) | (w << 6)) {}

    uint32_t operator [] (uint32_t id) const {
      return (m_mask >> (id + id)) & 0x3;
    }

    bool operator == (const DxsoRegSwizzle& other) const { return m_mask == other.m_mask; }
    bool operator != (const DxsoRegSwizzle& other) const { return m_mask != other.m_mask; }

  private:

    uint8_t m_mask;

  };

  const DxsoRegSwizzle IdentitySwizzle{ 0, 1, 2, 3 };

  struct DxsoBaseRegister {
    DxsoRegisterId  id               = { DxsoRegisterType::Temp, 0 };
    bool            centroid         = false;
    bool            partialPrecision = false;
    bool            saturate         = false;
    DxsoRegModifier modifier         = DxsoRegModifier::None;
    DxsoRegMask     mask             = IdentityWriteMask;
    DxsoRegSwizzle  swizzle          = IdentitySwizzle;
    int8_t          shift            = 0;
  };

  struct DxsoRegister : public DxsoBaseRegister {
    bool hasRelative = false;
    DxsoBaseRegister relative;
  };

  struct DxsoSemantic {
    DxsoUsage       usage;
    uint32_t        usageIndex;

    bool operator== (const DxsoSemantic& b) const;
    bool operator!= (const DxsoSemantic& b) const;
  };

  struct DxsoDeclaration {
    DxsoSemantic    semantic;

    DxsoTextureType textureType;
  };

  union DxsoDefinition {
    float    float32[4];
    int32_t  int32[4];

    // Not a type we actually use in compiler, but used for decoding.
    uint32_t uint32[4];
  };

  struct DxsoInstructionContext {
    uint32_t                    instructionIdx;

    DxsoShaderInstruction       instruction;

    DxsoRegister                pred;

    DxsoRegister                dst;
    std::array<
      DxsoRegister,
      DxsoMaxOperandCount>      src;

    DxsoDefinition              def;

    DxsoDeclaration             dcl;
  };

  class DxsoDecodeContext {

  public:

    DxsoDecodeContext(const DxsoProgramInfo& programInfo)
      : m_programInfo( programInfo ) {
      m_ctx.instructionIdx = 0;
    }

    /**
     * \brief Retrieves current instruction context
     *
     * This is only valid after a call to \ref decode.
     * \returns Reference to last decoded instruction & its context
     */
    const DxsoInstructionContext& getInstructionContext() const {
      return m_ctx;
    }

    const DxsoProgramInfo& getProgramInfo() const {
      return m_programInfo;
    }

    /**
     * \brief Decodes an instruction
     *
     * This also advances the given code slice by the
     * number of dwords consumed by the instruction.
     * \param [in] code Code slice
     */
    bool decodeInstruction(DxsoCodeIter& iter);

  private:

    uint32_t decodeInstructionLength(uint32_t token);

    void decodeBaseRegister(
            DxsoBaseRegister& reg,
            uint32_t          token);
    void decodeGenericRegister(
            DxsoRegister& reg,
            uint32_t      token);
    void decodeRelativeRegister(
            DxsoBaseRegister& reg,
            uint32_t          token);

    // Returns whether an extra token was read.
    bool decodeDestinationRegister(DxsoCodeIter& iter);
    bool decodeSourceRegister(uint32_t i, DxsoCodeIter& iter);
    void decodePredicateRegister(DxsoCodeIter& iter);

    void decodeDeclaration(DxsoCodeIter& iter);
    void decodeDefinition(DxsoOpcode opcode, DxsoCodeIter& iter);

    bool relativeAddressingUsesToken(DxsoInstructionArgumentType type);

    const DxsoProgramInfo&      m_programInfo;

    DxsoInstructionContext      m_ctx;

  };

  std::ostream& operator << (std::ostream& os, DxsoUsage usage);

}


code:
#include "dxso_enums.h"

namespace dxvk {

  std::ostream& operator << (std::ostream& os, DxsoOpcode opcode) {
    switch (opcode) {
      case DxsoOpcode::Nop: os << "Nop"; break;
      case DxsoOpcode::Mov: os << "Mov"; break;
      case DxsoOpcode::Add: os << "Add"; break;
      case DxsoOpcode::Sub: os << "Sub"; break;
      case DxsoOpcode::Mad: os << "Mad"; break;
      case DxsoOpcode::Mul: os << "Mul"; break;
      case DxsoOpcode::Rcp: os << "Rcp"; break;
      case DxsoOpcode::Rsq: os << "Rsq"; break;
      case DxsoOpcode::Dp3: os << "Dp3"; break;
      case DxsoOpcode::Dp4: os << "Dp4"; break;
      case DxsoOpcode::Min: os << "Min"; break;
      case DxsoOpcode::Max: os << "Max"; break;
      case DxsoOpcode::Slt: os << "Slt"; break;
      case DxsoOpcode::Sge: os << "Sge"; break;
      case DxsoOpcode::Exp: os << "Exp"; break;
      case DxsoOpcode::Log: os << "Log"; break;
      case DxsoOpcode::Lit: os << "Lit"; break;
      case DxsoOpcode::Dst: os << "Dst"; break;
      case DxsoOpcode::Lrp: os << "Lrp"; break;
      case DxsoOpcode::Frc: os << "Frc"; break;
      case DxsoOpcode::M4x4: os << "M4x4"; break;
      case DxsoOpcode::M4x3: os << "M4x3"; break;
      case DxsoOpcode::M3x4: os << "M3x4"; break;
      case DxsoOpcode::M3x3: os << "M3x3"; break;
      case DxsoOpcode::M3x2: os << "M3x2"; break;
      case DxsoOpcode::Call: os << "Call"; break;
      case DxsoOpcode::CallNz: os << "CallNz"; break;
      case DxsoOpcode::Loop: os << "Loop"; break;
      case DxsoOpcode::Ret: os << "Ret"; break;
      case DxsoOpcode::EndLoop: os << "EndLoop"; break;
      case DxsoOpcode::Label: os << "Label"; break;
      case DxsoOpcode::Dcl: os << "Dcl"; break;
      case DxsoOpcode::Pow: os << "Pow"; break;
      case DxsoOpcode::Crs: os << "Crs"; break;
      case DxsoOpcode::Sgn: os << "Sgn"; break;
      case DxsoOpcode::Abs: os << "Abs"; break;
      case DxsoOpcode::Nrm: os << "Nrm"; break;
      case DxsoOpcode::SinCos: os << "SinCos"; break;
      case DxsoOpcode::Rep: os << "Rep"; break;
      case DxsoOpcode::EndRep: os << "EndRep"; break;
      case DxsoOpcode::If: os << "If"; break;
      case DxsoOpcode::Ifc: os << "Ifc"; break;
      case DxsoOpcode::Else: os << "Else"; break;
      case DxsoOpcode::EndIf: os << "EndIf"; break;
      case DxsoOpcode::Break: os << "Break"; break;
      case DxsoOpcode::BreakC: os << "BreakC"; break;
      case DxsoOpcode::Mova: os << "Mova"; break;
      case DxsoOpcode::DefB: os << "DefB"; break;
      case DxsoOpcode::DefI: os << "DefI"; break;

      case DxsoOpcode::TexCoord: os << "TexCoord"; break;
      case DxsoOpcode::TexKill: os << "TexKill"; break;
      case DxsoOpcode::Tex: os << "Tex"; break;
      case DxsoOpcode::TexBem: os << "TexBem"; break;
      case DxsoOpcode::TexBemL: os << "TexBemL"; break;
      case DxsoOpcode::TexReg2Ar: os << "TexReg2Ar"; break;
      case DxsoOpcode::TexReg2Gb: os << "TexReg2Gb"; break;
      case DxsoOpcode::TexM3x2Pad: os << "TexM3x2Pad"; break;
      case DxsoOpcode::TexM3x2Tex: os << "TexM3x2Tex"; break;
      case DxsoOpcode::TexM3x3Pad: os << "TexM3x3Pad"; break;
      case DxsoOpcode::TexM3x3Tex: os << "TexM3x3Tex"; break;
      case DxsoOpcode::Reserved0: os << "Reserved0"; break;
      case DxsoOpcode::TexM3x3Spec: os << "TexM3x3Spec"; break;
      case DxsoOpcode::TexM3x3VSpec: os << "TexM3x3VSpec"; break;
      case DxsoOpcode::ExpP: os << "ExpP"; break;
      case DxsoOpcode::LogP: os << "LogP"; break;
      case DxsoOpcode::Cnd: os << "Cnd"; break;
      case DxsoOpcode::Def: os << "Def"; break;
      case DxsoOpcode::TexReg2Rgb: os << "TexReg2Rgb"; break;
      case DxsoOpcode::TexDp3Tex: os << "TexDp3Tex"; break;
      case DxsoOpcode::TexM3x2Depth: os << "TexM3x2Depth"; break;
      case DxsoOpcode::TexDp3: os << "TexDp3"; break;
      case DxsoOpcode::TexM3x3: os << "TexM3x3"; break;
      case DxsoOpcode::TexDepth: os << "TexDepth"; break;
      case DxsoOpcode::Cmp: os << "Cmp"; break;
      case DxsoOpcode::Bem: os << "Bem"; break;
      case DxsoOpcode::Dp2Add: os << "Dp2Add"; break;
      case DxsoOpcode::DsX: os << "DsX"; break;
      case DxsoOpcode::DsY: os << "DsY"; break;
      case DxsoOpcode::TexLdd: os << "TexLdd"; break;
      case DxsoOpcode::SetP: os << "SetP"; break;
      case DxsoOpcode::TexLdl: os << "TexLdl"; break;
      case DxsoOpcode::BreakP: os << "BreakP"; break;

      case DxsoOpcode::Phase: os << "Phase"; break;
      case DxsoOpcode::Comment: os << "Comment"; break;
      case DxsoOpcode::End: os << "End"; break;
      default:
        os << "Invalid Opcode (" << static_cast<uint32_t>(opcode) << ")"; break;
    }

    return os;
  }

}


code:
#pragma once

#include "dxso_include.h"

#include <cstdint>

namespace dxvk {

  /**
   * \brief Instruction code listing
   */
  enum class DxsoOpcode : uint32_t {
    Nop          = 0,
    Mov          ,
    Add          ,
    Sub          ,
    Mad          ,
    Mul          ,
    Rcp          ,
    Rsq          ,
    Dp3          ,
    Dp4          ,
    Min          ,
    Max          ,
    Slt          ,
    Sge          ,
    Exp          ,
    Log          ,
    Lit          ,
    Dst          ,
    Lrp          ,
    Frc          ,
    M4x4         ,
    M4x3         ,
    M3x4         ,
    M3x3         ,
    M3x2         ,
    Call         ,
    CallNz       ,
    Loop         ,
    Ret          ,
    EndLoop      ,
    Label        ,
    Dcl          ,
    Pow          ,
    Crs          ,
    Sgn          ,
    Abs          ,
    Nrm          ,
    SinCos       ,
    Rep          ,
    EndRep       ,
    If           ,
    Ifc          ,
    Else         ,
    EndIf        ,
    Break        ,
    BreakC       ,
    Mova         ,
    DefB         ,
    DefI         ,

    TexCoord     = 64,
    TexKill      ,
    Tex          ,
    TexBem       ,
    TexBemL      ,
    TexReg2Ar    ,
    TexReg2Gb    ,
    TexM3x2Pad   ,
    TexM3x2Tex   ,
    TexM3x3Pad   ,
    TexM3x3Tex   ,
    Reserved0    ,
    TexM3x3Spec  ,
    TexM3x3VSpec ,
    ExpP         ,
    LogP         ,
    Cnd          ,
    Def          ,
    TexReg2Rgb   ,
    TexDp3Tex    ,
    TexM3x2Depth ,
    TexDp3       ,
    TexM3x3      ,
    TexDepth     ,
    Cmp          ,
    Bem          ,
    Dp2Add       ,
    DsX          ,
    DsY          ,
    TexLdd       ,
    SetP         ,
    TexLdl       ,
    BreakP       ,

    Phase        = 0xfffd,
    Comment      = 0xfffe,
    End          = 0xffff
  };

  std::ostream& operator << (std::ostream& os, DxsoOpcode opcode);

  enum class DxsoRegisterType : uint32_t {
    Temp           =  0, // Temporary Register File
    Input          =  1, // Input Register File
    Const          =  2, // Constant Register File
    Addr           =  3, // Address Register (VS)
    Texture        =  3, // Texture Register File (PS)
    RasterizerOut  =  4, // Rasterizer Register File
    AttributeOut   =  5, // Attribute Output Register File
    TexcoordOut    =  6, // Texture Coordinate Output Register File
    Output         =  6, // Output register file for VS3.0+
    ConstInt       =  7, // Constant Integer Vector Register File
    ColorOut       =  8, // Color Output Register File
    DepthOut       =  9, // Depth Output Register File
    Sampler        = 10, // Sampler State Register File
    Const2         = 11, // Constant Register File  2048 - 4095
    Const3         = 12, // Constant Register File  4096 - 6143
    Const4         = 13, // Constant Register File  6144 - 8191
    ConstBool      = 14, // Constant Boolean register file
    Loop           = 15, // Loop counter register file
    TempFloat16    = 16, // 16-bit float temp register file
    MiscType       = 17, // Miscellaneous (single) registers.
    Label          = 18, // Label
    Predicate      = 19, // Predicate register
    PixelTexcoord  = 20
  };

  enum class DxsoUsage : uint32_t {
    Position        = 0,
    BlendWeight,   // 1
    BlendIndices,  // 2
    Normal,        // 3
    PointSize,     // 4
    Texcoord,      // 5
    Tangent,       // 6
    Binormal,      // 7
    TessFactor,    // 8
    PositionT,     // 9
    Color,         // 10
    Fog,           // 11
    Depth,         // 12
    Sample,        // 13
  };

  enum class DxsoTextureType : uint32_t {
    Texture2D   = 2,
    TextureCube = 3,
    Texture3D   = 4
  };

  enum DxsoReasterizerOutIndices : uint32_t {
    RasterOutPosition  = 0,
    RasterOutFog       = 1,
    RasterOutPointSize = 2
  };

  enum DxsoMiscTypeIndices : uint32_t {
    MiscTypePosition,
    MiscTypeFace,
  };

}


code:
#include "dxso_header.h"

namespace dxvk {

  DxsoHeader::DxsoHeader(DxsoReader& reader) {
    uint32_t headerToken = reader.readu32();

    uint32_t headerTypeMask = headerToken & 0xffff0000;

    DxsoProgramType programType;
    if (headerTypeMask == 0xffff0000)
      programType = DxsoProgramTypes::PixelShader;
    else if (headerTypeMask == 0xfffe0000)
      programType = DxsoProgramTypes::VertexShader;
    else
      throw DxvkError("DxsoHeader: invalid header - invalid version");

    const uint32_t majorVersion = (headerToken >> 8) & 0xff;
    const uint32_t minorVersion = headerToken & 0xff;

    m_info = DxsoProgramInfo{ programType, minorVersion, majorVersion };
  }

}


code:
#pragma once

#include "dxso_common.h"

#include "dxso_reader.h"

namespace dxvk {

/**
  * \brief DXSO header
  *
  * Stores meta information about the shader such
  * as the version and the type.
  */
  class DxsoHeader {

  public:

    DxsoHeader(DxsoReader& reader);

    const DxsoProgramInfo& info() const {
      return m_info;
    }

  private:

    DxsoProgramInfo m_info;

  };

}


code:
#pragma once

#include "../dxvk/dxvk_shader.h"

#include "../util/com/com_guid.h"
#include "../util/com/com_object.h"
#include "../util/com/com_pointer.h"

#include "../util/log/log.h"
#include "../util/log/log_debug.h"

#include "../util/rc/util_rc.h"
#include "../util/rc/util_rc_ptr.h"

#include "../util/util_bit.h"
#include "../util/util_enum.h"
#include "../util/util_error.h"
#include "../util/util_string.h"


code:
#pragma once

#include "dxso_decoder.h"

namespace dxvk {

  struct DxsoIsgnEntry {
    uint32_t     regNumber = 0;
    uint32_t     slot      = 0;
    DxsoSemantic semantic  = DxsoSemantic{ DxsoUsage::Position, 0 };
    DxsoRegMask  mask      = IdentityWriteMask;
    bool         centroid  = false;
  };

  struct DxsoIsgn {
    std::array<
      DxsoIsgnEntry,
      2 * DxsoMaxInterfaceRegs> elems;
    uint32_t elemCount = 0;
  };

  struct DxsoDefinedConstant {
    uint32_t uboIdx;

    // Only float constants may be indexed.
    // So that's the only ones we care about putting in the UBO.
    float    float32[4];
  };

  using DxsoDefinedConstants = std::vector<DxsoDefinedConstant>;

  struct DxsoShaderMetaInfo {
    bool needsConstantCopies = false;
    uint32_t maxConstIndexF = 0;
    uint32_t maxConstIndexI = 0;
    uint32_t maxConstIndexB = 0;

    uint32_t boolConstantMask = 0;
  };

}


code:
#pragma once

#include "dxso_options.h"

namespace dxvk {

  /**
   * \brief Shader module info
   *
   * Stores information which may affect shader compilation.
   * This data can be supplied by the client API implementation.
   */
  struct DxsoModuleInfo {
    DxsoOptions options;
  };

}


code:
#include "dxso_module.h"

#include "dxso_code.h"
#include "dxso_compiler.h"

#include <memory>

namespace dxvk {

  DxsoModule::DxsoModule(DxsoReader& reader)
    : m_header( reader )
    , m_code  ( reader ) { }

  DxsoAnalysisInfo DxsoModule::analyze() {
    DxsoAnalysisInfo info;

    DxsoAnalyzer analyzer(info);

    this->runAnalyzer(analyzer, m_code.iter());

    return info;
  }

  DxsoPermutations DxsoModule::compile(
    const DxsoModuleInfo&     moduleInfo,
    const std::string&        fileName,
    const DxsoAnalysisInfo&   analysis,
    const D3D9ConstantLayout& layout) {
    auto compiler = std::make_unique<DxsoCompiler>(
      fileName, moduleInfo,
      m_header.info(), analysis,
      layout);

    this->runCompiler(*compiler, m_code.iter());
    m_isgn = compiler->isgn();

    m_meta            = compiler->meta();
    m_constants       = compiler->constants();
    m_maxDefinedConst = compiler->maxDefinedConstant();
    m_usedSamplers    = compiler->usedSamplers();
    m_usedRTs         = compiler->usedRTs();

    compiler->finalize();

    return compiler->compile();
  }

  void DxsoModule::runAnalyzer(
          DxsoAnalyzer&       analyzer,
          DxsoCodeIter        iter) const {
    DxsoCodeIter start = iter;

    DxsoDecodeContext decoder(m_header.info());

    while (decoder.decodeInstruction(iter))
      analyzer.processInstruction(
        decoder.getInstructionContext());

    size_t tokenCount = size_t(iter.ptrAt(0) - start.ptrAt(0));

    // We need to account for the header token in the bytecode size...

    // At this point, start is offset by the header due to us this being
    // a *code* iterator, and not the general reader class.
    // [start token] ^(start caret)^ [frog rendering code] [end token] ^(end caret)^
    // where the tokenCount above is inbetween the start and end carets.

    // We need to account for this otherwise it will show up as us not
    // accounting for the *end* token in GetFunction due to the total size being
    // offset by -1.
    // [start token] [frog rendering code] (end of tokenCount) [end token]
    tokenCount += 1;

    analyzer.finalize(tokenCount);
  }

  void DxsoModule::runCompiler(
          DxsoCompiler&       compiler,
          DxsoCodeIter        iter) const {
    DxsoDecodeContext decoder(m_header.info());

    while (decoder.decodeInstruction(iter))
      compiler.processInstruction(
        decoder.getInstructionContext());
  }

}


code:
#pragma once

#include "dxso_reader.h"
#include "dxso_code.h"
#include "dxso_header.h"
#include "dxso_ctab.h"

#include "dxso_isgn.h"
#include "dxso_analysis.h"

#include "../d3d9/d3d9_constant_layout.h"
#include "../d3d9/d3d9_shader_permutations.h"

#include <vector>

namespace dxvk {

  class DxsoCompiler;
  class DxsoCode;
  struct DxsoModuleInfo;

  /**
   * \brief DXSO shader module, a d3d9 shader object.
   */
  class DxsoModule {

  public:

    DxsoModule(DxsoReader& reader);

    const DxsoProgramInfo& info() {
      return m_header.info();
    }

    DxsoAnalysisInfo analyze();

    /**
     * \brief Compiles DXSO shader to SPIR-V module
     *
     * \param [in] moduleInfo DXSO module info
     * \param [in] fileName File name, will be added to
     *        the compiled SPIR-V for debugging purposes.
     * \returns The compiled shader object
     */
    DxsoPermutations compile(
      const DxsoModuleInfo&     moduleInfo,
      const std::string&        fileName,
      const DxsoAnalysisInfo&   analysis,
      const D3D9ConstantLayout& layout);

    const DxsoIsgn& isgn() {
      return m_isgn;
    }

    const DxsoShaderMetaInfo& meta() { return m_meta; }

    const DxsoDefinedConstants& constants() { return m_constants; }

    uint32_t usedSamplers() { return m_usedSamplers; }

    uint32_t usedRTs() { return m_usedRTs; }

    uint32_t maxDefinedConstant() { return m_maxDefinedConst; }

  private:

    void runCompiler(
            DxsoCompiler&       compiler,
            DxsoCodeIter        iter) const;

    void runAnalyzer(
            DxsoAnalyzer&       analyzer,
            DxsoCodeIter        iter) const;

    DxsoHeader      m_header;
    DxsoCode        m_code;

    DxsoIsgn        m_isgn;
    uint32_t        m_usedSamplers;
    uint32_t        m_usedRTs;

    DxsoShaderMetaInfo   m_meta;
    uint32_t             m_maxDefinedConst;
    DxsoDefinedConstants m_constants;

  };

}


code:
#include "dxso_options.h"

#include "../d3d9/d3d9_device.h"

namespace dxvk {

  DxsoOptions::DxsoOptions() {}

  DxsoOptions::DxsoOptions(D3D9DeviceEx* pDevice, const D3D9Options& options) {
    const Rc<DxvkDevice> device = pDevice->GetDXVKDevice();

    const Rc<DxvkAdapter> adapter = device->adapter();

    const DxvkDeviceFeatures& devFeatures = device->features();
    const DxvkDeviceInfo& devInfo = adapter->devicePropertiesExt();

    useDemoteToHelperInvocation
      = (devFeatures.extShaderDemoteToHelperInvocation.shaderDemoteToHelperInvocation);

    useSubgroupOpsForEarlyDiscard
       = (devInfo.coreSubgroup.subgroupSize >= 4)
      && (devInfo.coreSubgroup.supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT)
      && (devInfo.coreSubgroup.supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT);

    // Disable early discard on Nvidia because it may hurt performance
    if (adapter->matchesDriver(DxvkGpuVendor::Nvidia, VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR, 0, 0))
      useSubgroupOpsForEarlyDiscard = false;

    // Apply shader-related options
    strictConstantCopies = options.strictConstantCopies;

    strictPow            = options.strictPow;
    d3d9FloatEmulation   = options.d3d9FloatEmulation;

    shaderModel          = options.shaderModel;

    invariantPosition    = options.invariantPosition;

    forceSamplerTypeSpecConstants = options.forceSamplerTypeSpecConstants;

    vertexFloatConstantBufferAsSSBO = pDevice->GetVertexConstantLayout().floatSize() > devInfo.core.properties.limits.maxUniformBufferRange;

    longMad = options.longMad;

    alphaTestWiggleRoom = options.alphaTestWiggleRoom;

    robustness2Supported = devFeatures.extRobustness2.robustBufferAccess2;
  }

}


code:
#pragma once

#include "../dxvk/dxvk_device.h"
#include "../d3d9/d3d9_options.h"

namespace dxvk {

  class D3D9DeviceEx;
  struct D3D9Options;

  struct DxsoOptions {
    DxsoOptions();
    DxsoOptions(D3D9DeviceEx* pDevice, const D3D9Options& options);

    /// Use a SPIR-V extension to implement D3D-style discards
    bool useDemoteToHelperInvocation = false;

    /// Use subgroup operations to discard fragment
    /// shader invocations if derivatives remain valid.
    bool useSubgroupOpsForEarlyDiscard = false;

    /// True:  Copy our constant set into UBO if we are relative indexing ever.
    /// False: Copy our constant set into UBO if we are relative indexing at the start of a defined constant
    /// Why?:  In theory, FXC should never generate code where this would be an issue.
    bool strictConstantCopies;

    /// Whether to emulate d3d9 float behaviour using clampps
    /// True:  Perform emulation to emulate behaviour (ie. anything * 0 = 0)
    /// False: Don't do anything.
    D3D9FloatEmulation d3d9FloatEmulation;

    /// Whether or not we should care about pow(0, 0) = 1
    bool strictPow;

    /// Max version of shader to support
    uint32_t shaderModel;

    /// Work around a NV driver quirk
    /// Fixes flickering/z-fighting in some games.
    bool invariantPosition;

    /// Always use a spec constant to determine sampler type (instead of just in PS 1.x)
    /// Works around a game bug in Halo CE where it gives cube textures to 2d/volume samplers
    bool forceSamplerTypeSpecConstants;

    /// Should the SWVP float constant buffer be a SSBO (because of the size on NV)
    bool vertexFloatConstantBufferAsSSBO;

    /// Should we make our Mads a FFma or do it the long way with an FMul and an FAdd?
    /// This solves some rendering bugs in games that have z-pass shaders which
    /// don't match entirely to the regular vertex shader in this way.
    bool longMad;

    /// Workaround for games using alpha test == 1.0, etc due to wonky interpolation or
    /// misc. imprecision on some vendors
    bool alphaTestWiggleRoom;

    /// Whether or not we can rely on robustness2 to handle oob constant access
    bool robustness2Supported;
  };

}


code:
#include "dxso_reader.h"

#include <cstring>

namespace dxvk {

  DxbcTag DxsoReader::readTag() {
    DxbcTag tag;
    this->read(&tag, 4);
    return tag;
  }

  void DxsoReader::read(void* dst, size_t n) {
    std::memcpy(dst, m_data + m_pos, n);
    m_pos += n;
  }

  void DxsoReader::skip(size_t n) {
    m_pos += n;
  }

  void DxsoReader::store(std::ostream && stream, size_t size) const {
    stream.write(m_data, size);
  }

}


code:
#pragma once

#include "dxso_include.h"

#include "../dxbc/dxbc_tag.h"

#include <cstdint>

namespace dxvk {

  /**
    * \brief DXSO (d3d9) bytecode reader
    *
    * Holds references to the shader byte code and
    * provides methods to read
    */
  class DxsoReader {

  public:

    DxsoReader(const char* data)
      : DxsoReader(data, 0) { }

    size_t pos() {
      return m_pos;
    }

    auto readu32() { return this->readNum<uint32_t> (); }
    auto readf32() { return this->readNum<float>    (); }

    DxbcTag readTag();

    void read(void* dst, size_t n);

    void skip(size_t n);

    void store(std::ostream&& stream, size_t size) const;

    const char* currentPtr() {
      return m_data + m_pos;
    }

  private:

    DxsoReader(const char* data, size_t pos)
      : m_data(data), m_pos(pos) { }

    const char* m_data = nullptr;
    size_t      m_pos = 0;

    template<typename T>
    T readNum() {
      T result;
      this->read(&result, sizeof(result));
      return result;
    }

  };

}


code:
#include "dxso_tables.h"

namespace dxvk {

  uint32_t DxsoGetDefaultOpcodeLength(DxsoOpcode opcode) {

    switch (opcode) {
      case DxsoOpcode::Nop: return 0;
      case DxsoOpcode::Mov: return 2;
      case DxsoOpcode::Add: return 3;
      case DxsoOpcode::Sub: return 3;
      case DxsoOpcode::Mad: return 4;
      case DxsoOpcode::Mul: return 3;
      case DxsoOpcode::Rcp: return 2;
      case DxsoOpcode::Rsq: return 2;
      case DxsoOpcode::Dp3: return 3;
      case DxsoOpcode::Dp4: return 3;
      case DxsoOpcode::Min: return 3;
      case DxsoOpcode::Max: return 3;
      case DxsoOpcode::Slt: return 3;
      case DxsoOpcode::Sge: return 3;
      case DxsoOpcode::Exp: return 2;
      case DxsoOpcode::Log: return 2;
      case DxsoOpcode::Lit: return 2;
      case DxsoOpcode::Dst: return 3;
      case DxsoOpcode::Lrp: return 4;
      case DxsoOpcode::Frc: return 2;
      case DxsoOpcode::M4x4: return 3;
      case DxsoOpcode::M4x3: return 3;
      case DxsoOpcode::M3x4: return 3;
      case DxsoOpcode::M3x3: return 3;
      case DxsoOpcode::M3x2: return 3;
      case DxsoOpcode::Call: return 1;
      case DxsoOpcode::CallNz: return 2;
      case DxsoOpcode::Loop: return 2;
      case DxsoOpcode::Ret: return 0;
      case DxsoOpcode::EndLoop: return 0;
      case DxsoOpcode::Label: return 1;
      case DxsoOpcode::Dcl: return 2;
      case DxsoOpcode::Pow: return 3;
      case DxsoOpcode::Crs: return 3;
      case DxsoOpcode::Sgn: return 4;
      case DxsoOpcode::Abs: return 2;
      case DxsoOpcode::Nrm: return 2;
      case DxsoOpcode::SinCos: return 4;
      case DxsoOpcode::Rep: return 1;
      case DxsoOpcode::EndRep: return 0;
      case DxsoOpcode::If: return 1;
      case DxsoOpcode::Ifc: return 2;
      case DxsoOpcode::Else: return 0;
      case DxsoOpcode::EndIf: return 0;
      case DxsoOpcode::Break: return 0;
      case DxsoOpcode::BreakC: return 2;
      case DxsoOpcode::Mova: return 2;
      case DxsoOpcode::DefB: return 2;
      case DxsoOpcode::DefI: return 5;
      case DxsoOpcode::TexCoord: return 1;
      case DxsoOpcode::TexKill: return 1;
      case DxsoOpcode::Tex: return 1;
      case DxsoOpcode::TexBem: return 2;
      case DxsoOpcode::TexBemL: return 2;
      case DxsoOpcode::TexReg2Ar: return 2;
      case DxsoOpcode::TexReg2Gb: return 2;
      case DxsoOpcode::TexM3x2Pad: return 2;
      case DxsoOpcode::TexM3x2Tex: return 2;
      case DxsoOpcode::TexM3x3Pad: return 2;
      case DxsoOpcode::TexM3x3Tex: return 2;
      case DxsoOpcode::TexM3x3Spec: return 3;
      case DxsoOpcode::TexM3x3VSpec: return 2;
      case DxsoOpcode::ExpP: return 2;
      case DxsoOpcode::LogP: return 2;
      case DxsoOpcode::Cnd: return 4;
      case DxsoOpcode::Def: return 5;
      case DxsoOpcode::TexReg2Rgb: return 2;
      case DxsoOpcode::TexDp3Tex: return 2;
      case DxsoOpcode::TexM3x2Depth: return 2;
      case DxsoOpcode::TexDp3: return 2;
      case DxsoOpcode::TexM3x3: return 2;
      case DxsoOpcode::TexDepth: return 1;
      case DxsoOpcode::Cmp: return 4;
      case DxsoOpcode::Bem: return 3;
      case DxsoOpcode::Dp2Add: return 4;
      case DxsoOpcode::DsX: return 2;
      case DxsoOpcode::DsY: return 2;
      case DxsoOpcode::TexLdd: return 5;
      case DxsoOpcode::SetP: return 3;
      case DxsoOpcode::TexLdl: return 3;
      case DxsoOpcode::BreakP: return 2;
      default: Logger::warn("DxsoGetDefaultOpcodeLength: unknown opcode to get default length for."); return UINT32_MAX;
    }
  }

}


code:
#pragma once

#include "dxso_enums.h"

namespace dxvk {

  constexpr uint32_t InvalidOpcodeLength = UINT32_MAX;

  uint32_t DxsoGetDefaultOpcodeLength(DxsoOpcode opcode);

}


code:
#include "dxso_util.h"

#include "dxso_include.h"

namespace dxvk {

  dxvk::mutex                  g_linkerSlotMutex;
  uint32_t                     g_linkerSlotCount = 0;
  std::array<DxsoSemantic, 32> g_linkerSlots;

  uint32_t RegisterLinkerSlot(DxsoSemantic semantic) {
    // Lock, because games could be trying
    // to make multiple shaders at a time.
    std::lock_guard<dxvk::mutex> lock(g_linkerSlotMutex);

    // Need to chose a slot that maps nicely and similarly
    // between vertex and pixel shaders

    // Find or map a slot.
    uint32_t slot = g_linkerSlotCount;
    for (uint32_t j = 0; j < g_linkerSlotCount; j++) {
      if (g_linkerSlots[j] == semantic) {
        slot = j;
        break;
      }
    }

    if (slot == g_linkerSlotCount)
      g_linkerSlots[g_linkerSlotCount++] = semantic;

    return slot;
  }

}


code:
#pragma once

#include <cstdint>

#include "dxso_common.h"
#include "dxso_decoder.h"

#include "../d3d9/d3d9_caps.h"

namespace dxvk {

  enum class DxsoBindingType : uint32_t {
    ConstantBuffer,
    Image,
  };

  enum class DxsoConstantBufferType : uint32_t {
    Float,
    Int,
    Bool
  };

  enum DxsoConstantBuffers : uint32_t {
    VSConstantBuffer = 0,
    VSFloatConstantBuffer = 0,
    VSIntConstantBuffer = 1,
    VSBoolConstantBuffer = 2,
    VSClipPlanes     = 3,
    VSFixedFunction  = 4,
    VSVertexBlendData = 5,
    VSCount,

    PSConstantBuffer = 0,
    PSFixedFunction  = 1,
    PSShared         = 2,
    PSCount
  };

  constexpr uint32_t computeResourceSlotId(
        DxsoProgramType shaderStage,
        DxsoBindingType bindingType,
        uint32_t        bindingIndex) {
    const uint32_t stageOffset = (DxsoConstantBuffers::VSCount + caps::MaxTexturesVS) * uint32_t(shaderStage);

    if (bindingType == DxsoBindingType::ConstantBuffer)
      return bindingIndex + stageOffset;
    else // if (bindingType == DxsoBindingType::Image)
      return bindingIndex + stageOffset + (shaderStage == DxsoProgramType::PixelShader ? DxsoConstantBuffers::PSCount : DxsoConstantBuffers::VSCount);
  }

  // TODO: Intergrate into compute resource slot ID/refactor all of this?
  constexpr uint32_t getSWVPBufferSlot() {
    return DxsoConstantBuffers::VSCount + caps::MaxTexturesVS + DxsoConstantBuffers::PSCount + caps::MaxTexturesPS + 1; // From last pixel shader slot, above.
  }

  uint32_t RegisterLinkerSlot(DxsoSemantic semantic);

}


code:
dxso_src = files([
  'dxso_common.cpp',
  'dxso_options.cpp',
  'dxso_module.cpp',
  'dxso_reader.cpp',
  'dxso_header.cpp',
  'dxso_ctab.cpp',
  'dxso_util.cpp',
  'dxso_code.cpp',
  'dxso_tables.cpp',
  'dxso_decoder.cpp',
  'dxso_analysis.cpp',
  'dxso_compiler.cpp',
  'dxso_enums.cpp'
])

dxso_lib = static_library('dxso', dxso_src,
  include_directories : [ dxvk_include_path ],
)

dxso_dep = declare_dependency(
  link_with           : [ dxso_lib ],
  include_directories : [ dxvk_include_path ],
)