code:
#pragma once

#include <array>
#include <vector>

#include "../spirv/spirv_module.h"

#include "dxbc_analysis.h"
#include "dxbc_chunk_isgn.h"
#include "dxbc_decoder.h"
#include "dxbc_defs.h"
#include "dxbc_modinfo.h"
#include "dxbc_names.h"
#include "dxbc_util.h"

namespace dxvk {

  /**
   * \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 DxbcVectorType {
    DxbcScalarType    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 DxbcArrayType {
    DxbcScalarType    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 DxbcRegisterInfo {
    DxbcArrayType     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 DxbcRegisterValue {
    DxbcVectorType    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 DxbcRegisterPointer {
    DxbcVectorType    type;
    uint32_t          id;
  };

  struct DxbcXreg {
    uint32_t ccount  = 0;
    uint32_t alength = 0;
    uint32_t varId   = 0;
  };

  struct DxbcGreg {
    DxbcResourceType type  = DxbcResourceType::Raw;
    uint32_t elementStride = 0;
    uint32_t elementCount  = 0;
    uint32_t varId         = 0;
  };

  /**
   * \brief Specialization constant properties
   *
   * Stores the name, data type and initial
   * value of a specialization constant.
   */
  struct DxbcSpecConstant {
    DxbcScalarType  ctype;
    uint32_t        ccount;
    uint32_t        value;
    const char*     name;
  };

  /**
   * \brief Helper struct for conditional execution
   *
   * Stores a set of labels required to implement either
   * an if-then block or an if-then-else block. This is
   * not used to implement control flow instructions.
   */
  struct DxbcConditional {
    uint32_t labelIf   = 0;
    uint32_t labelElse = 0;
    uint32_t labelEnd  = 0;
  };

  struct DxbcXfbVar {
    uint32_t    varId     = 0;
    uint32_t    streamId  = 0;
    uint32_t    outputId  = 0;
    DxbcRegMask srcMask   = 0;
    DxbcRegMask dstMask   = 0;
    uint32_t    location  = 0;
    uint32_t    component = 0;
  };

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

    uint32_t builtinVertexId     = 0;
    uint32_t builtinInstanceId   = 0;
    uint32_t builtinBaseVertex   = 0;
    uint32_t builtinBaseInstance = 0;
  };

  /**
   * \brief Geometry shader-specific structure
   */
  struct DxbcCompilerGsPart {
    DxbcPrimitive         inputPrimitive      = DxbcPrimitive::Undefined;
    DxbcPrimitiveTopology outputTopology      = DxbcPrimitiveTopology::Undefined;
    uint32_t              outputVertexCount   = 0;
    uint32_t              functionId          = 0;

    uint32_t builtinLayer         = 0;
    uint32_t builtinViewportId    = 0;
    uint32_t builtinInvocationId  = 0;
    uint32_t invocationCount      = 0;
  };

  /**
   * \brief Pixel shader-specific structure
   */
  struct DxbcCompilerPsPart {
    uint32_t functionId = 0;

    uint32_t builtinFragCoord     = 0;
    uint32_t builtinDepth         = 0;
    uint32_t builtinStencilRef    = 0;
    uint32_t builtinIsFrontFace   = 0;
    uint32_t builtinSampleId      = 0;
    uint32_t builtinSampleMaskIn  = 0;
    uint32_t builtinSampleMaskOut = 0;
    uint32_t builtinLayer         = 0;
    uint32_t builtinViewportId    = 0;

    uint32_t builtinLaneId        = 0;
    uint32_t killState            = 0;

    uint32_t specRsSampleCount    = 0;
  };

  /**
   * \brief Compute shader-specific structure
   */
  struct DxbcCompilerCsPart {
    uint32_t functionId = 0;

    uint32_t workgroupSizeX               = 0;
    uint32_t workgroupSizeY               = 0;
    uint32_t workgroupSizeZ               = 0;

    uint32_t builtinGlobalInvocationId    = 0;
    uint32_t builtinLocalInvocationId     = 0;
    uint32_t builtinLocalInvocationIndex  = 0;
    uint32_t builtinWorkgroupId           = 0;
  };

  /**
   * \brief Hull shader fork/join phase
   *
   * Defines a function and built-in variables
   * for a single fork or join phase sub-program.
   */
  struct DxbcCompilerHsForkJoinPhase {
    uint32_t functionId         = 0;
    uint32_t instanceCount      = 1;

    uint32_t instanceId         = 0;
    uint32_t instanceIdPtr      = 0;
  };

  /**
   * \brief Hull shader control point phase
   *
   * Defines the function for the control
   * point phase program of a hull shader.
   */
  struct DxbcCompilerHsControlPointPhase {
    uint32_t functionId             = 0;
  };

  /**
   * \brief Hull shader phase
   *
   * Used to identify the current
   * phase and function ID.
   */
  enum class DxbcCompilerHsPhase : uint32_t {
    None,         ///< No active phase
    Decl,         ///< \c hs_decls
    ControlPoint, ///< \c hs_control_point_phase
    Fork,         ///< \c hs_fork_phase
    Join,         ///< \c hs_join_phase
  };

  /**
   * \brief Hull shader-specific structure
   */
  struct DxbcCompilerHsPart {
    DxbcCompilerHsPhase currPhaseType   = DxbcCompilerHsPhase::None;
    size_t              currPhaseId     = 0;

    float maxTessFactor = 64.0f;

    uint32_t vertexCountIn   = 0;
    uint32_t vertexCountOut  = 0;

    uint32_t builtinInvocationId   = 0;
    uint32_t builtinTessLevelOuter = 0;
    uint32_t builtinTessLevelInner = 0;

    uint32_t outputPerPatch        = 0;
    uint32_t outputPerVertex       = 0;

    uint32_t invocationBlockBegin  = 0;
    uint32_t invocationBlockEnd    = 0;

    uint32_t outputPerPatchMask    = 0;

    DxbcCompilerHsControlPointPhase          cpPhase;
    std::vector<DxbcCompilerHsForkJoinPhase> forkPhases;
    std::vector<DxbcCompilerHsForkJoinPhase> joinPhases;
  };

  /**
   * \brief Domain shader-specific structure
   */
  struct DxbcCompilerDsPart {
    uint32_t functionId            = 0;

    uint32_t builtinTessCoord      = 0;
    uint32_t builtinTessLevelOuter = 0;
    uint32_t builtinTessLevelInner = 0;

    uint32_t vertexCountIn         = 0;

    uint32_t inputPerPatch         = 0;
    uint32_t inputPerVertex        = 0;
  };

  enum class DxbcCfgBlockType : uint32_t {
    If, Loop, Switch,
  };

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

  struct DxbcCfgBlockLoop {
    uint32_t labelHeader;
    uint32_t labelBegin;
    uint32_t labelContinue;
    uint32_t labelBreak;
  };

  struct DxbcSwitchLabel {
    SpirvSwitchCaseLabel desc;
    DxbcSwitchLabel*     next;
  };

  struct DxbcCfgBlockSwitch {
    size_t            insertPtr;
    uint32_t          selectorId;
    uint32_t          labelBreak;
    uint32_t          labelCase;
    uint32_t          labelDefault;
    DxbcSwitchLabel*  labelCases;
  };

  struct DxbcCfgBlock {
    DxbcCfgBlockType type;

    union {
      DxbcCfgBlockIf     b_if;
      DxbcCfgBlockLoop   b_loop;
      DxbcCfgBlockSwitch b_switch;
    };
  };

  struct DxbcBufferInfo {
    DxbcImageInfo image;
    DxbcScalarType stype;
    DxbcResourceType type;
    uint32_t typeId;
    uint32_t varId;
    uint32_t specId;
    uint32_t stride;
    uint32_t align;
  };

  /**
   * \brief SPIR-V extension set
   *
   * Keeps track of which optional SPIR-V extensions
   * are enabled so that any required setup code is
   * only run once.
   */
  struct DxbcSpirvExtensions {
    bool shaderViewportIndexLayer = false;
  };

  /**
   * \brief DXBC to SPIR-V shader compiler
   *
   * Processes instructions from a DXBC shader and creates
   * a DXVK shader object, which contains the SPIR-V module
   * and information about the shader resource bindings.
   */
  class DxbcCompiler {

  public:

    DxbcCompiler(
      const std::string&        fileName,
      const DxbcModuleInfo&     moduleInfo,
      const DxbcProgramInfo&    programInfo,
      const Rc<DxbcIsgn>&       isgn,
      const Rc<DxbcIsgn>&       osgn,
      const Rc<DxbcIsgn>&       psgn,
      const DxbcAnalysisInfo&   analysis);
    ~DxbcCompiler();

    /**
     * \brief Processes a single instruction
     * \param [in] ins The instruction
     */
    void processInstruction(
      const DxbcShaderInstruction&  ins);

    /**
     * \brief Emits transform feedback passthrough
     *
     * Writes all captured input variables to the
     * corresponding xfb outputs, and sets up the
     * geometry shader for point-to-point mode.
     */
    void processXfbPassthrough();

    /**
     * \brief Finalizes the shader
     * \returns The final shader object
     */
    Rc<DxvkShader> finalize();

  private:

    DxbcModuleInfo      m_moduleInfo;
    DxbcProgramInfo     m_programInfo;
    SpirvModule         m_module;

    Rc<DxbcIsgn>        m_isgn;
    Rc<DxbcIsgn>        m_osgn;
    Rc<DxbcIsgn>        m_psgn;

    const DxbcAnalysisInfo* m_analysis;

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

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

    /////////////////////////////////////////////
    // Thread group shared memory (g#) registers
    std::vector<DxbcGreg> m_gRegs;

    ///////////////////////////////////////////////////////////
    // v# registers as defined by the shader. The type of each
    // of these inputs is either float4 or an array of float4.
    std::array<
      DxbcRegisterPointer,
      DxbcMaxInterfaceRegs>     m_vRegs;
    std::vector<DxbcSvMapping>  m_vMappings;

    //////////////////////////////////////////////////////////
    // o# registers as defined by the shader. In the fragment
    // shader stage, these registers are typed by the signature,
    // in all other stages, they are float4 registers or arrays.
    std::array<
      DxbcRegisterPointer,
      DxbcMaxInterfaceRegs>     m_oRegs;
    std::vector<DxbcSvMapping>  m_oMappings;

    /////////////////////////////////////////////
    // xfb output registers for geometry shaders
    std::vector<DxbcXfbVar> m_xfbVars;

    //////////////////////////////////////////////////////
    // Shader resource variables. These provide access to
    // constant buffers, samplers, textures, and UAVs.
    std::array<DxbcConstantBuffer,  16> m_constantBuffers;
    std::array<DxbcSampler,         16> m_samplers;
    std::array<DxbcShaderResource, 128> m_textures;
    std::array<DxbcUav,             64> m_uavs;

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

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

    ///////////////////////////////////////////////////////////
    // Array of input values. Since v# registers are indexable
    // in DXBC, we need to copy them into an array first.
    uint32_t m_vArrayLength   = 0;
    uint32_t m_vArrayLengthId = 0;

    uint32_t m_vArray = 0;

    ////////////////////////////////////////////////////
    // Per-vertex input and output blocks. Depending on
    // the shader stage, these may be declared as arrays.
    uint32_t m_perVertexIn  = 0;
    uint32_t m_perVertexOut = 0;

    uint32_t m_clipDistances = 0;
    uint32_t m_cullDistances = 0;

    uint32_t m_primitiveIdIn  = 0;
    uint32_t m_primitiveIdOut = 0;

    //////////////////////////////////////////////////
    // Immediate constant buffer. If defined, this is
    // an array of four-component uint32 vectors.
    uint32_t m_immConstBuf = 0;
    std::vector<char> m_immConstData;

    ///////////////////////////////////////////////////
    // Sample pos array. If defined, this iis an array
    // of 32 four-component float vectors.
    uint32_t m_samplePositions = 0;

    ////////////////////////////////////////////
    // Struct type used for UAV counter buffers
    uint32_t m_uavCtrStructType  = 0;
    uint32_t m_uavCtrPointerType = 0;

    ////////////////////////////////
    // Function IDs for subroutines
    std::unordered_map<uint32_t, uint32_t> m_subroutines;

    ///////////////////////////////////////////////////
    // 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;

    ///////////////////////////////////
    // Shader-specific data structures
    DxbcCompilerVsPart m_vs;
    DxbcCompilerHsPart m_hs;
    DxbcCompilerDsPart m_ds;
    DxbcCompilerGsPart m_gs;
    DxbcCompilerPsPart m_ps;
    DxbcCompilerCsPart m_cs;

    /////////////////////////////
    // Enabled SPIR-V extensions
    DxbcSpirvExtensions m_extensions;

    //////////////////////
    // Global state stuff
    bool m_precise = true;

    DxbcOpcode m_lastOp = DxbcOpcode::Nop;
    DxbcOpcode m_currOp = DxbcOpcode::Nop;

    /////////////////////////////////////////////////////
    // Shader interface and metadata declaration methods
    void emitDcl(
      const DxbcShaderInstruction&  ins);

    void emitDclGlobalFlags(
      const DxbcShaderInstruction&  ins);

    void emitDclTemps(
      const DxbcShaderInstruction&  ins);

    void emitDclIndexableTemp(
      const DxbcShaderInstruction&  ins);

    void emitDclInterfaceReg(
      const DxbcShaderInstruction&  ins);

    void emitDclInput(
            uint32_t                regIdx,
            uint32_t                regDim,
            DxbcRegMask             regMask,
            DxbcSystemValue         sv,
            DxbcInterpolationMode   im);

    void emitDclOutput(
            uint32_t                regIdx,
            uint32_t                regDim,
            DxbcRegMask             regMask,
            DxbcSystemValue         sv,
            DxbcInterpolationMode   im);

    void emitDclConstantBuffer(
      const DxbcShaderInstruction&  ins);

    void emitDclConstantBufferVar(
            uint32_t                regIdx,
            uint32_t                numConstants,
      const char*                   name,
            bool                    asSsbo);

    void emitDclSampler(
      const DxbcShaderInstruction&  ins);

    void emitDclStream(
      const DxbcShaderInstruction&  ins);

    void emitDclResourceTyped(
      const DxbcShaderInstruction&  ins);

    void emitDclResourceRawStructured(
      const DxbcShaderInstruction&  ins);

    void emitDclThreadGroupSharedMemory(
      const DxbcShaderInstruction&  ins);

    void emitDclGsInputPrimitive(
      const DxbcShaderInstruction&  ins);

    void emitDclGsOutputTopology(
      const DxbcShaderInstruction&  ins);

    void emitDclMaxOutputVertexCount(
      const DxbcShaderInstruction&  ins);

    void emitDclInputControlPointCount(
      const DxbcShaderInstruction&  ins);

    void emitDclOutputControlPointCount(
      const DxbcShaderInstruction&  ins);

    void emitDclHsMaxTessFactor(
      const DxbcShaderInstruction&  ins);

    void emitDclTessDomain(
      const DxbcShaderInstruction&  ins);

    void emitDclTessPartitioning(
      const DxbcShaderInstruction&  ins);

    void emitDclTessOutputPrimitive(
      const DxbcShaderInstruction&  ins);

    void emitDclThreadGroup(
      const DxbcShaderInstruction&  ins);

    void emitDclGsInstanceCount(
      const DxbcShaderInstruction&  ins);

    uint32_t emitDclUavCounter(
            uint32_t                regId);

    ////////////////////////
    // Custom data handlers
    void emitDclImmediateConstantBuffer(
      const DxbcShaderInstruction&  ins);

    void emitDclImmediateConstantBufferBaked(
            uint32_t                dwordCount,
      const uint32_t*               dwordArray);

    void emitDclImmediateConstantBufferUbo(
            uint32_t                dwordCount,
      const uint32_t*               dwordArray);

    void emitCustomData(
      const DxbcShaderInstruction&  ins);

    //////////////////////////////
    // Instruction class handlers
    void emitVectorAlu(
      const DxbcShaderInstruction&  ins);

    void emitVectorCmov(
      const DxbcShaderInstruction&  ins);

    void emitVectorCmp(
      const DxbcShaderInstruction&  ins);

    void emitVectorDeriv(
      const DxbcShaderInstruction&  ins);

    void emitVectorDot(
      const DxbcShaderInstruction&  ins);

    void emitVectorIdiv(
      const DxbcShaderInstruction&  ins);

    void emitVectorImul(
      const DxbcShaderInstruction&  ins);

    void emitVectorMsad(
      const DxbcShaderInstruction&  ins);

    void emitVectorShift(
      const DxbcShaderInstruction&  ins);

    void emitVectorSinCos(
      const DxbcShaderInstruction&  ins);

    void emitGeometryEmit(
      const DxbcShaderInstruction&  ins);

    void emitAtomic(
      const DxbcShaderInstruction&  ins);

    void emitAtomicCounter(
      const DxbcShaderInstruction&  ins);

    void emitBarrier(
      const DxbcShaderInstruction&  ins);

    void emitBitExtract(
      const DxbcShaderInstruction&  ins);

    void emitBitInsert(
      const DxbcShaderInstruction&  ins);

    void emitBitScan(
      const DxbcShaderInstruction&  ins);

    void emitBufferQuery(
      const DxbcShaderInstruction&  ins);

    void emitBufferLoad(
      const DxbcShaderInstruction&  ins);

    void emitBufferStore(
      const DxbcShaderInstruction&  ins);

    void emitConvertFloat16(
      const DxbcShaderInstruction&  ins);

    void emitConvertFloat64(
      const DxbcShaderInstruction&  ins);

    void emitHullShaderPhase(
      const DxbcShaderInstruction&  ins);

    void emitHullShaderInstCnt(
      const DxbcShaderInstruction&  ins);

    void emitInterpolate(
      const DxbcShaderInstruction&  ins);

    void emitTextureQuery(
      const DxbcShaderInstruction&  ins);

    void emitTextureQueryLod(
      const DxbcShaderInstruction&  ins);

    void emitTextureQueryMs(
      const DxbcShaderInstruction&  ins);

    void emitTextureQueryMsPos(
      const DxbcShaderInstruction&  ins);

    void emitTextureFetch(
      const DxbcShaderInstruction&  ins);

    void emitTextureGather(
      const DxbcShaderInstruction&  ins);

    void emitTextureSample(
      const DxbcShaderInstruction&  ins);

    void emitTypedUavLoad(
      const DxbcShaderInstruction&  ins);

    void emitTypedUavStore(
      const DxbcShaderInstruction&  ins);

    /////////////////////////////////////
    // Control flow instruction handlers
    void emitControlFlowIf(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowElse(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowEndIf(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowSwitch(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowCase(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowDefault(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowEndSwitch(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowLoop(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowEndLoop(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowBreak(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowBreakc(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowRet(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowRetc(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowDiscard(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowLabel(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowCall(
      const DxbcShaderInstruction&  ins);

    void emitControlFlowCallc(
      const DxbcShaderInstruction&  ins);

    void emitControlFlow(
      const DxbcShaderInstruction&  ins);

    ////////////////////////////////////////////////
    // Constant building methods. These are used to
    // generate constant vectors that store the same
    // value in each component.
    DxbcRegisterValue emitBuildConstVecf32(
            float                   x,
            float                   y,
            float                   z,
            float                   w,
      const DxbcRegMask&            writeMask);

    DxbcRegisterValue emitBuildConstVecu32(
            uint32_t                x,
            uint32_t                y,
            uint32_t                z,
            uint32_t                w,
      const DxbcRegMask&            writeMask);

    DxbcRegisterValue emitBuildConstVeci32(
            int32_t                 x,
            int32_t                 y,
            int32_t                 z,
            int32_t                 w,
      const DxbcRegMask&            writeMask);

    DxbcRegisterValue emitBuildConstVecf64(
            double                  xy,
            double                  zw,
      const DxbcRegMask&            writeMask);

    DxbcRegisterValue emitBuildVector(
            DxbcRegisterValue       scalar,
            uint32_t                count);

    DxbcRegisterValue emitBuildZeroVector(
            DxbcVectorType          type);

    /////////////////////////////////////////
    // Generic register manipulation methods
    DxbcRegisterValue emitRegisterBitcast(
            DxbcRegisterValue       srcValue,
            DxbcScalarType          dstType);

    DxbcRegisterValue emitRegisterSwizzle(
            DxbcRegisterValue       value,
            DxbcRegSwizzle          swizzle,
            DxbcRegMask             writeMask);

    DxbcRegisterValue emitRegisterExtract(
            DxbcRegisterValue       value,
            DxbcRegMask             mask);

    DxbcRegisterValue emitRegisterInsert(
            DxbcRegisterValue       dstValue,
            DxbcRegisterValue       srcValue,
            DxbcRegMask             srcMask);

    DxbcRegisterValue emitRegisterConcat(
            DxbcRegisterValue       value1,
            DxbcRegisterValue       value2);

    DxbcRegisterValue emitRegisterExtend(
            DxbcRegisterValue       value,
            uint32_t                size);

    DxbcRegisterValue emitRegisterAbsolute(
            DxbcRegisterValue       value);

    DxbcRegisterValue emitRegisterNegate(
            DxbcRegisterValue       value);

    DxbcRegisterValue emitRegisterZeroTest(
            DxbcRegisterValue       value,
            DxbcZeroTest            test);

    DxbcRegisterValue emitRegisterMaskBits(
            DxbcRegisterValue       value,
            uint32_t                mask);

    DxbcRegisterValue emitSrcOperandModifiers(
            DxbcRegisterValue       value,
            DxbcRegModifiers        modifiers);

    DxbcRegisterValue emitDstOperandModifiers(
            DxbcRegisterValue       value,
            DxbcOpModifiers         modifiers);

    ////////////////////////////////
    // Pointer manipulation methods
    DxbcRegisterPointer emitArrayAccess(
            DxbcRegisterPointer     pointer,
            spv::StorageClass       sclass,
            uint32_t                index);

    ///////////////////////////////////////
    // Image register manipulation methods
    uint32_t emitLoadSampledImage(
      const DxbcShaderResource&     textureResource,
      const DxbcSampler&            samplerResource,
            bool                    isDepthCompare);

    ////////////////////////
    // Address load methods
    DxbcRegisterPointer emitGetTempPtr(
      const DxbcRegister&           operand);

    DxbcRegisterPointer emitGetIndexableTempPtr(
      const DxbcRegister&           operand);

    DxbcRegisterPointer emitGetInputPtr(
      const DxbcRegister&           operand);

    DxbcRegisterPointer emitGetOutputPtr(
      const DxbcRegister&           operand);

    DxbcRegisterPointer emitGetConstBufPtr(
      const DxbcRegister&           operand);

    DxbcRegisterPointer emitGetImmConstBufPtr(
      const DxbcRegister&           operand);

    DxbcRegisterPointer emitGetOperandPtr(
      const DxbcRegister&           operand);

    DxbcRegisterPointer emitGetAtomicPointer(
      const DxbcRegister&           operand,
      const DxbcRegister&           address);

    ///////////////////////////////
    // Resource load/store methods
    DxbcRegisterValue emitRawBufferLoad(
      const DxbcRegister&           operand,
            DxbcRegisterValue       elementIndex,
            DxbcRegMask             writeMask);

    void emitRawBufferStore(
      const DxbcRegister&           operand,
            DxbcRegisterValue       elementIndex,
            DxbcRegisterValue       value);

    //////////////////////////
    // Resource query methods
    DxbcRegisterValue emitQueryBufferSize(
      const DxbcRegister&           resource);

    DxbcRegisterValue emitQueryTexelBufferSize(
      const DxbcRegister&           resource);

    DxbcRegisterValue emitQueryTextureLods(
      const DxbcRegister&           resource);

    DxbcRegisterValue emitQueryTextureSamples(
      const DxbcRegister&           resource);

    DxbcRegisterValue emitQueryTextureSize(
      const DxbcRegister&           resource,
            DxbcRegisterValue       lod);

    ////////////////////////////////////
    // Buffer index calculation methods
    DxbcRegisterValue emitCalcBufferIndexStructured(
            DxbcRegisterValue       structId,
            DxbcRegisterValue       structOffset,
            uint32_t                structStride);

    DxbcRegisterValue emitCalcBufferIndexRaw(
            DxbcRegisterValue       byteOffset);

    DxbcRegisterValue emitCalcTexCoord(
            DxbcRegisterValue       coordVector,
      const DxbcImageInfo&          imageInfo);

    DxbcRegisterValue emitLoadTexCoord(
      const DxbcRegister&           coordReg,
      const DxbcImageInfo&          imageInfo);

    //////////////////////////////
    // Operand load/store methods
    DxbcRegisterValue emitIndexLoad(
            DxbcRegIndex            index);

    DxbcRegisterValue emitValueLoad(
            DxbcRegisterPointer     ptr);

    void emitValueStore(
            DxbcRegisterPointer     ptr,
            DxbcRegisterValue       value,
            DxbcRegMask             writeMask);

    DxbcRegisterValue emitRegisterLoadRaw(
      const DxbcRegister&           reg);

    DxbcRegisterValue emitConstantBufferLoad(
      const DxbcRegister&           reg,
            DxbcRegMask             writeMask);

    DxbcRegisterValue emitRegisterLoad(
      const DxbcRegister&           reg,
            DxbcRegMask             writeMask);

    void emitRegisterStore(
      const DxbcRegister&           reg,
            DxbcRegisterValue       value);

    ////////////////////////////////////////
    // Spec constant declaration and access
    uint32_t emitNewSpecConstant(
            DxvkSpecConstantId      specId,
            DxbcScalarType          type,
            uint32_t                value,
      const char*                   name);

    ////////////////////////////
    // Input/output preparation
    void emitInputSetup();
    void emitInputSetup(uint32_t vertexCount);

    void emitOutputSetup();
    void emitOutputMapping();
    void emitOutputDepthClamp();

    void emitInitWorkgroupMemory();

    //////////////////////////////////////////
    // System value load methods (per shader)
    DxbcRegisterValue emitVsSystemValueLoad(
            DxbcSystemValue         sv,
            DxbcRegMask             mask);

    DxbcRegisterValue emitGsSystemValueLoad(
            DxbcSystemValue         sv,
            DxbcRegMask             mask,
            uint32_t                vertexId);

    DxbcRegisterValue emitPsSystemValueLoad(
            DxbcSystemValue         sv,
            DxbcRegMask             mask);

    ///////////////////////////////////////////
    // System value store methods (per shader)
    void emitVsSystemValueStore(
            DxbcSystemValue         sv,
            DxbcRegMask             mask,
      const DxbcRegisterValue&      value);

    void emitHsSystemValueStore(
            DxbcSystemValue         sv,
            DxbcRegMask             mask,
      const DxbcRegisterValue&      value);

    void emitDsSystemValueStore(
            DxbcSystemValue         sv,
            DxbcRegMask             mask,
      const DxbcRegisterValue&      value);

    void emitGsSystemValueStore(
            DxbcSystemValue         sv,
            DxbcRegMask             mask,
      const DxbcRegisterValue&      value);

    void emitPsSystemValueStore(
            DxbcSystemValue         sv,
            DxbcRegMask             mask,
      const DxbcRegisterValue&      value);

    ///////////////////////////////
    // Special system value stores
    void emitClipCullStore(
            DxbcSystemValue         sv,
            uint32_t                dstArray);

    void emitClipCullLoad(
            DxbcSystemValue         sv,
            uint32_t                srcArray);

    ///////////////////////////////
    // Some state checking methods
    uint32_t emitUavWriteTest(
      const DxbcBufferInfo&         uav);

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

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

    void emitFunctionEnd();

    void emitFunctionLabel();

    void emitMainFunctionBegin();

    /////////////////////////////////
    // Shader initialization methods
    void emitVsInit();
    void emitHsInit();
    void emitDsInit();
    void emitGsInit();
    void emitPsInit();
    void emitCsInit();

    ///////////////////////////////
    // Shader finalization methods
    void emitVsFinalize();
    void emitHsFinalize();
    void emitDsFinalize();
    void emitGsFinalize();
    void emitPsFinalize();
    void emitCsFinalize();

    ///////////////////////
    // Xfb related methods
    void emitXfbOutputDeclarations();

    void emitXfbOutputSetup(
            uint32_t                          streamId,
            bool                              passthrough);

    ///////////////////////////////
    // Hull shader phase methods
    void emitHsControlPointPhase(
      const DxbcCompilerHsControlPointPhase&  phase);

    void emitHsForkJoinPhase(
      const DxbcCompilerHsForkJoinPhase&      phase);

    void emitHsPhaseBarrier();

    void emitHsInvocationBlockBegin(
            uint32_t                          count);

    void emitHsInvocationBlockEnd();

    void emitHsOutputSetup();

    uint32_t emitTessInterfacePerPatch(
            spv::StorageClass                 storageClass);

    uint32_t emitTessInterfacePerVertex(
            spv::StorageClass                 storageClass,
            uint32_t                          vertexCount);

    //////////////
    // Misc stuff
    void emitDclInputArray(
            uint32_t          vertexCount);

    void emitDclInputPerVertex(
            uint32_t          vertexCount,
      const char*             varName);

    uint32_t emitDclClipCullDistanceArray(
            uint32_t          length,
            spv::BuiltIn      builtIn,
            spv::StorageClass storageClass);

    DxbcCompilerHsControlPointPhase emitNewHullShaderControlPointPhase();

    DxbcCompilerHsControlPointPhase emitNewHullShaderPassthroughPhase();

    DxbcCompilerHsForkJoinPhase emitNewHullShaderForkJoinPhase();

    uint32_t emitSamplePosArray();

    void emitFloatControl();

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

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

    uint32_t emitBuiltinTessLevelOuter(
            spv::StorageClass storageClass);

    uint32_t emitBuiltinTessLevelInner(
            spv::StorageClass storageClass);

    ////////////////////////////////
    // Extension enablement methods
    void enableShaderViewportIndexLayer();

    ////////////////
    // Misc methods
    DxbcCfgBlock* cfgFindBlock(
      const std::initializer_list<DxbcCfgBlockType>& types);

    DxbcBufferInfo getBufferInfo(
      const DxbcRegister& reg);

    uint32_t getTexSizeDim(
      const DxbcImageInfo& imageType) const;

    uint32_t getTexLayerDim(
      const DxbcImageInfo& imageType) const;

    uint32_t getTexCoordDim(
      const DxbcImageInfo& imageType) const;

    DxbcRegMask getTexCoordMask(
      const DxbcImageInfo& imageType) const;

    DxbcVectorType getInputRegType(
            uint32_t regIdx) const;

    DxbcVectorType getOutputRegType(
            uint32_t regIdx) const;

    DxbcImageInfo getResourceType(
            DxbcResourceDim   resourceType,
            bool              isUav) const;

    spv::ImageFormat getScalarImageFormat(
            DxbcScalarType type) const;

    bool isDoubleType(
            DxbcScalarType type) const;

    DxbcRegisterPointer getIndexableTempPtr(
      const DxbcRegister&           operand,
            DxbcRegisterValue       vectorId);

    bool caseBlockIsFallthrough() const;

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

    uint32_t getVectorTypeId(
      const DxbcVectorType& type);

    uint32_t getArrayTypeId(
      const DxbcArrayType& type);

    uint32_t getPointerTypeId(
      const DxbcRegisterInfo& type);

    uint32_t getPerVertexBlockId();

    uint32_t getFunctionId(
            uint32_t          functionNr);

    DxbcCompilerHsForkJoinPhase* getCurrentHsForkJoinPhase();

  };

}

repo_name: orochi/dxvk


code:
#include "dxbc_decoder.h"

namespace dxvk {

  const uint32_t* DxbcCodeSlice::ptrAt(uint32_t id) const {
    if (m_ptr + id >= m_end)
      throw DxvkError("DxbcCodeSlice: End of stream");
    return m_ptr + id;
  }

  uint32_t DxbcCodeSlice::at(uint32_t id) const {
    if (m_ptr + id >= m_end)
      throw DxvkError("DxbcCodeSlice: End of stream");
    return m_ptr[id];
  }

  uint32_t DxbcCodeSlice::read() {
    if (m_ptr >= m_end)
      throw DxvkError("DxbcCodeSlice: End of stream");
    return *(m_ptr++);
  }

  DxbcCodeSlice DxbcCodeSlice::take(uint32_t n) const {
    if (m_ptr + n > m_end)
      throw DxvkError("DxbcCodeSlice: End of stream");
    return DxbcCodeSlice(m_ptr, m_ptr + n);
  }

  DxbcCodeSlice DxbcCodeSlice::skip(uint32_t n) const {
    if (m_ptr + n > m_end)
      throw DxvkError("DxbcCodeSlice: End of stream");
    return DxbcCodeSlice(m_ptr + n, m_end);
  }

  void DxbcDecodeContext::decodeInstruction(DxbcCodeSlice& code) {
    const uint32_t token0 = code.at(0);

    // Initialize the instruction structure. Some of these values
    // may not get written otherwise while decoding the instruction.
    m_instruction.op             = static_cast<DxbcOpcode>(bit::extract(token0, 0, 10));
    m_instruction.opClass        = DxbcInstClass::Undefined;
    m_instruction.sampleControls = { 0, 0, 0 };
    m_instruction.dstCount       = 0;
    m_instruction.srcCount       = 0;
    m_instruction.immCount       = 0;
    m_instruction.dst            = m_dstOperands.data();
    m_instruction.src            = m_srcOperands.data();
    m_instruction.imm            = m_immOperands.data();
    m_instruction.customDataType = DxbcCustomDataClass::Comment;
    m_instruction.customDataSize = 0;
    m_instruction.customData     = nullptr;

    // Reset the index pointer, which may still contain
    // a non-zero value from the previous iteration
    m_indexId = 0;

    // Instruction length, in DWORDs. This includes the token
    // itself and any other prefix that an instruction may have.
    uint32_t length = 0;

    if (m_instruction.op == DxbcOpcode::CustomData) {
      length = code.at(1);
      this->decodeCustomData(code.take(length));
    } else {
      length = bit::extract(token0, 24, 30);
      this->decodeOperation(code.take(length));
    }

    // Advance the caller's slice to the next token so that
    // they can make consecutive calls to decodeInstruction()
    code = code.skip(length);
  }

  void DxbcDecodeContext::decodeCustomData(DxbcCodeSlice code) {
    const uint32_t blockLength = code.at(1);

    if (blockLength < 2) {
      Logger::err("DxbcDecodeContext: Invalid custom data block");
      return;
    }

    // Custom data blocks have their own instruction class
    m_instruction.op      = DxbcOpcode::CustomData;
    m_instruction.opClass = DxbcInstClass::CustomData;

    // We'll point into the code buffer rather than making a copy
    m_instruction.customDataType = static_cast<DxbcCustomDataClass>(
      bit::extract(code.at(0), 11, 31));
    m_instruction.customDataSize = blockLength - 2;
    m_instruction.customData     = code.ptrAt(2);
  }

  void DxbcDecodeContext::decodeOperation(DxbcCodeSlice code) {
    uint32_t token = code.read();

    // Result modifiers, which are applied to common ALU ops
    m_instruction.modifiers.saturate = !!bit::extract(token, 13, 13);
    m_instruction.modifiers.precise  = !!bit::extract(token, 19, 22);

    // Opcode controls. It will depend on the
    // opcode itself which ones are valid.
    m_instruction.controls = DxbcShaderOpcodeControls(token);

    // Process extended opcode tokens
    while (bit::extract(token, 31, 31)) {
      token = code.read();

      const DxbcExtOpcode extOpcode
        = static_cast<DxbcExtOpcode>(bit::extract(token, 0, 5));

      switch (extOpcode) {
        case DxbcExtOpcode::SampleControls: {
          struct {
            int u : 4;
            int v : 4;
            int w : 4;
          } aoffimmi;

          aoffimmi.u = bit::extract(token,  9, 12);
          aoffimmi.v = bit::extract(token, 13, 16);
          aoffimmi.w = bit::extract(token, 17, 20);

          // Four-bit signed numbers, sign-extend them
          m_instruction.sampleControls.u = aoffimmi.u;
          m_instruction.sampleControls.v = aoffimmi.v;
          m_instruction.sampleControls.w = aoffimmi.w;
        } break;

        case DxbcExtOpcode::ResourceDim:
        case DxbcExtOpcode::ResourceReturnType:
          break;  // part of resource description

        default:
          Logger::warn(str::format(
            "DxbcDecodeContext: Unhandled extended opcode: ",
            extOpcode));
      }
    }

    // Retrieve the instruction format in order to parse the
    // operands. Doing this mostly automatically means that
    // the compiler can rely on the operands being valid.
    const DxbcInstFormat format = dxbcInstructionFormat(m_instruction.op);
    m_instruction.opClass = format.instructionClass;

    for (uint32_t i = 0; i < format.operandCount; i++)
      this->decodeOperand(code, format.operands[i]);
  }

  void DxbcDecodeContext::decodeComponentSelection(DxbcRegister& reg, uint32_t token) {
    // Pick the correct component selection mode based on the
    // component count. We'll simplify this here so that the
    // compiler can assume that everything is a 4D vector.
    reg.componentCount = static_cast<DxbcComponentCount>(bit::extract(token, 0, 1));

    switch (reg.componentCount) {
      // No components - used for samplers etc.
      case DxbcComponentCount::Component0:
        reg.mask    = DxbcRegMask(false, false, false, false);
        reg.swizzle = DxbcRegSwizzle(0, 0, 0, 0);
        break;

      // One component - used for immediates
      // and a few built-in registers.
      case DxbcComponentCount::Component1:
        reg.mask    = DxbcRegMask(true, false, false, false);
        reg.swizzle = DxbcRegSwizzle(0, 0, 0, 0);
        break;

      // Four components - everything else. This requires us
      // to actually parse the component selection mode.
      case DxbcComponentCount::Component4: {
        const DxbcRegMode componentMode =
          static_cast<DxbcRegMode>(bit::extract(token, 2, 3));

        switch (componentMode) {
          // Write mask for destination operands
          case DxbcRegMode::Mask:
            reg.mask    = bit::extract(token, 4, 7);
            reg.swizzle = DxbcRegSwizzle(0, 1, 2, 3);
            break;

          // Swizzle for source operands (including resources)
          case DxbcRegMode::Swizzle:
            reg.mask    = DxbcRegMask(true, true, true, true);
            reg.swizzle = DxbcRegSwizzle(
              bit::extract(token,  4,  5),
              bit::extract(token,  6,  7),
              bit::extract(token,  8,  9),
              bit::extract(token, 10, 11));
            break;

          // Selection of one component. We can generate both a
          // mask and a swizzle for this so that the compiler
          // won't have to deal with this case specifically.
          case DxbcRegMode::Select1: {
            const uint32_t n = bit::extract(token, 4, 5);
            reg.mask    = DxbcRegMask(n == 0, n == 1, n == 2, n == 3);
            reg.swizzle = DxbcRegSwizzle(n, n, n, n);
          } break;

          default:
            Logger::warn("DxbcDecodeContext: Invalid component selection mode");
        }
      } break;

      default:
        Logger::warn("DxbcDecodeContext: Invalid component count");
    }
  }

  void DxbcDecodeContext::decodeOperandExtensions(DxbcCodeSlice& code, DxbcRegister& reg, uint32_t token) {
    while (bit::extract(token, 31, 31)) {
      token = code.read();

      // Type of the extended operand token
      const DxbcOperandExt extTokenType =
        static_cast<DxbcOperandExt>(bit::extract(token, 0, 5));

      switch (extTokenType) {
        // Operand modifiers, which are used to manipulate the
        // value of a source operand during the load operation
        case DxbcOperandExt::OperandModifier:
          reg.modifiers = bit::extract(token, 6, 13);
          break;

        default:
          Logger::warn(str::format(
            "DxbcDecodeContext: Unhandled extended operand token: ",
            extTokenType));
      }
    }
  }

  void DxbcDecodeContext::decodeOperandImmediates(DxbcCodeSlice& code, DxbcRegister& reg) {
    if (reg.type == DxbcOperandType::Imm32
     || reg.type == DxbcOperandType::Imm64) {
      switch (reg.componentCount) {
        // This is commonly used if only one vector
        // component is involved in an operation
        case DxbcComponentCount::Component1: {
          reg.imm.u32_1 = code.read();
        } break;

        // Typical four-component vector
        case DxbcComponentCount::Component4: {
          reg.imm.u32_4[0] = code.read();
          reg.imm.u32_4[1] = code.read();
          reg.imm.u32_4[2] = code.read();
          reg.imm.u32_4[3] = code.read();
        } break;

        default:
          Logger::warn("DxbcDecodeContext: Invalid component count for immediate operand");
      }
    }
  }

  void DxbcDecodeContext::decodeOperandIndex(DxbcCodeSlice& code, DxbcRegister& reg, uint32_t token) {
    reg.idxDim = bit::extract(token, 20, 21);

    for (uint32_t i = 0; i < reg.idxDim; i++) {
      // An index can be encoded in various different ways
      const DxbcOperandIndexRepresentation repr =
        static_cast<DxbcOperandIndexRepresentation>(
          bit::extract(token, 22 + 3 * i, 24 + 3 * i));

      switch (repr) {
        case DxbcOperandIndexRepresentation::Imm32:
          reg.idx[i].offset = static_cast<int32_t>(code.read());
          reg.idx[i].relReg = nullptr;
          break;

        case DxbcOperandIndexRepresentation::Relative:
          reg.idx[i].offset = 0;
          reg.idx[i].relReg = &m_indices.at(m_indexId);

          this->decodeRegister(code,
            m_indices.at(m_indexId++),
            DxbcScalarType::Sint32);
          break;

        case DxbcOperandIndexRepresentation::Imm32Relative:
          reg.idx[i].offset = static_cast<int32_t>(code.read());
          reg.idx[i].relReg = &m_indices.at(m_indexId);

          this->decodeRegister(code,
            m_indices.at(m_indexId++),
            DxbcScalarType::Sint32);
          break;

        default:
          Logger::warn(str::format(
            "DxbcDecodeContext: Unhandled index representation: ",
            repr));
      }
    }
  }

  void DxbcDecodeContext::decodeRegister(DxbcCodeSlice& code, DxbcRegister& reg, DxbcScalarType type) {
    const uint32_t token = code.read();

    reg.type            = static_cast<DxbcOperandType>(bit::extract(token, 12, 19));
    reg.dataType        = type;
    reg.modifiers       = 0;
    reg.idxDim          = 0;

    for (uint32_t i = 0; i < DxbcMaxRegIndexDim; i++) {
      reg.idx[i].relReg = nullptr;
      reg.idx[i].offset = 0;
    }

    this->decodeComponentSelection(reg, token);
    this->decodeOperandExtensions(code, reg, token);
    this->decodeOperandImmediates(code, reg);
    this->decodeOperandIndex(code, reg, token);
  }

  void DxbcDecodeContext::decodeImm32(DxbcCodeSlice& code, DxbcImmediate& imm, DxbcScalarType type) {
    imm.u32 = code.read();
  }

  void DxbcDecodeContext::decodeOperand(DxbcCodeSlice& code, const DxbcInstOperandFormat& format) {
    switch (format.kind) {
      case DxbcOperandKind::DstReg: {
        const uint32_t operandId = m_instruction.dstCount++;
        this->decodeRegister(code, m_dstOperands.at(operandId), format.type);
      } break;

      case DxbcOperandKind::SrcReg: {
        const uint32_t operandId = m_instruction.srcCount++;
        this->decodeRegister(code, m_srcOperands.at(operandId), format.type);
      } break;

      case DxbcOperandKind::Imm32: {
        const uint32_t operandId = m_instruction.immCount++;
        this->decodeImm32(code, m_immOperands.at(operandId), format.type);
      } break;

      default:
        throw DxvkError("DxbcDecodeContext: Invalid operand format");
    }
  }

}


code:
#pragma once

#include <array>

#include "dxbc_common.h"
#include "dxbc_decoder.h"
#include "dxbc_defs.h"
#include "dxbc_enums.h"
#include "dxbc_names.h"

namespace dxvk {

  constexpr size_t DxbcMaxRegIndexDim = 3;

  struct DxbcRegister;

  /**
   * \brief Source operand modifiers
   *
   * These are applied after loading
   * an operand register.
   */
  enum class DxbcRegModifier : uint32_t {
    Neg = 0,
    Abs = 1,
  };

  using DxbcRegModifiers = Flags<DxbcRegModifier>;

  /**
   * \brief Constant buffer binding
   *
   * Stores information required to
   * access a constant buffer.
   */
  struct DxbcConstantBuffer {
    uint32_t varId  = 0;
    uint32_t size   = 0;
  };

  /**
   * \brief Sampler binding
   *
   * Stores a sampler variable that can be
   * used together with a texture resource.
   */
  struct DxbcSampler {
    uint32_t varId  = 0;
    uint32_t typeId = 0;
  };

  /**
   * \brief Image type information
   */
  struct DxbcImageInfo {
    spv::Dim        dim     = spv::Dim1D;
    uint32_t        array   = 0;
    uint32_t        ms      = 0;
    uint32_t        sampled = 0;
    VkImageViewType vtype   = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
  };

  /**
   * \brief Shader resource binding
   *
   * Stores a resource variable
   * and associated type IDs.
   */
  struct DxbcShaderResource {
    DxbcResourceType  type          = DxbcResourceType::Typed;
    DxbcImageInfo     imageInfo;
    uint32_t          varId         = 0;
    uint32_t          specId        = 0;
    DxbcScalarType    sampledType   = DxbcScalarType::Float32;
    uint32_t          sampledTypeId = 0;
    uint32_t          imageTypeId   = 0;
    uint32_t          colorTypeId   = 0;
    uint32_t          depthTypeId   = 0;
    uint32_t          structStride  = 0;
    uint32_t          structAlign   = 0;
  };

  /**
   * \brief Unordered access binding
   *
   * Stores a resource variable that is provided
   * by a UAV, as well as associated type IDs.
   */
  struct DxbcUav {
    DxbcResourceType  type          = DxbcResourceType::Typed;
    DxbcImageInfo     imageInfo;
    uint32_t          varId         = 0;
    uint32_t          ctrId         = 0;
    uint32_t          specId        = 0;
    DxbcScalarType    sampledType   = DxbcScalarType::Float32;
    uint32_t          sampledTypeId = 0;
    uint32_t          imageTypeId   = 0;
    uint32_t          structStride  = 0;
    uint32_t          structAlign   = 0;
  };

  /**
   * \brief Component swizzle
   *
   * Maps vector components to
   * other vector components.
   */
  class DxbcRegSwizzle {

  public:

    DxbcRegSwizzle() { }
    DxbcRegSwizzle(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 DxbcRegSwizzle& other) const { return m_mask == other.m_mask; }
    bool operator != (const DxbcRegSwizzle& other) const { return m_mask != other.m_mask; }

  private:

    uint8_t m_mask = 0;

  };

  /**
   * \brief Component mask
   *
   * Enables access to certain
   * subset of vector components.
   */
  class DxbcRegMask {

  public:

    DxbcRegMask() { }
    DxbcRegMask(uint32_t mask) : m_mask(mask) { }
    DxbcRegMask(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 >> id) & 1;
    }

    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 DxbcRegMask& other) const { return m_mask == other.m_mask; }
    bool operator != (const DxbcRegMask& other) const { return m_mask != other.m_mask; }

    DxbcRegMask& operator |= (const DxbcRegMask& other) {
      m_mask |= other.m_mask;
      return *this;
    }

    static DxbcRegMask firstN(uint32_t n) {
      return DxbcRegMask(n >= 1, n >= 2, n >= 3, n >= 4);
    }

    static DxbcRegMask select(uint32_t n) {
      return DxbcRegMask(n == 0, n == 1, n == 2, n == 3);
    }

    std::string maskString() const {
      std::string out = "";
      out += (m_mask & 0x1) ? "x" : "";
      out += (m_mask & 0x2) ? "y" : "";
      out += (m_mask & 0x4) ? "z" : "";
      out += (m_mask & 0x8) ? "w" : "";
      return out;
    }

    operator bool () const {
      return m_mask != 0;
    }

  private:

    uint8_t m_mask = 0;

  };

  /**
   * \brief System value mapping
   *
   * Maps a system value to a given set of
   * components of an input or output register.
   */
  struct DxbcSvMapping {
    uint32_t        regId;
    DxbcRegMask     regMask;
    DxbcSystemValue sv;
  };

  struct DxbcRegIndex {
    DxbcRegister*     relReg;
    int32_t           offset;
  };

  /**
   * \brief Instruction operand
   */
  struct DxbcRegister {
    DxbcOperandType       type;
    DxbcScalarType        dataType;
    DxbcComponentCount    componentCount;

    uint32_t              idxDim;
    DxbcRegIndex          idx[DxbcMaxRegIndexDim];

    DxbcRegMask           mask;
    DxbcRegSwizzle        swizzle;
    DxbcRegModifiers      modifiers;

    union {
      uint32_t            u32_4[4];
      uint32_t            u32_1;
    } imm;
  };

  /**
   * \brief Instruction result modifiers
   *
   * Modifiers that are applied
   * to all destination operands.
   */
  struct DxbcOpModifiers {
    bool saturate;
    bool precise;
  };

  /**
   * \brief Opcode controls
   *
   * Instruction-specific controls. Usually,
   * only one of the members will be valid.
   */
  class DxbcShaderOpcodeControls {

  public:

    DxbcShaderOpcodeControls()
    : m_bits(0) { }

    DxbcShaderOpcodeControls(uint32_t bits)
    : m_bits(bits) { }

    DxbcInstructionReturnType returnType() const {
      return DxbcInstructionReturnType(bit::extract(m_bits, 11, 11));
    }

    DxbcGlobalFlags globalFlags() const {
      return DxbcGlobalFlags(bit::extract(m_bits, 11, 14));
    }

    DxbcZeroTest zeroTest() const {
      return DxbcZeroTest(bit::extract(m_bits, 18, 18));
    }

    DxbcSyncFlags syncFlags() const {
      return DxbcSyncFlags(bit::extract(m_bits, 11, 14));
    }

    DxbcResourceDim resourceDim() const {
      return DxbcResourceDim(bit::extract(m_bits, 11, 15));
    }

    DxbcResinfoType resinfoType() const {
      return DxbcResinfoType(bit::extract(m_bits, 11, 12));
    }

    DxbcInterpolationMode interpolation() const {
      return DxbcInterpolationMode(bit::extract(m_bits, 11, 14));
    }

    DxbcSamplerMode samplerMode() const {
      return DxbcSamplerMode(bit::extract(m_bits, 11, 14));
    }

    DxbcPrimitiveTopology primitiveTopology() const {
      return DxbcPrimitiveTopology(bit::extract(m_bits, 11, 17));
    }

    DxbcPrimitive primitive() const {
      return DxbcPrimitive(bit::extract(m_bits, 11, 16));
    }

    DxbcTessDomain tessDomain() const {
      return DxbcTessDomain(bit::extract(m_bits, 11, 12));
    }

    DxbcTessOutputPrimitive tessOutputPrimitive() const {
      return DxbcTessOutputPrimitive(bit::extract(m_bits, 11, 13));
    }

    DxbcTessPartitioning tessPartitioning() const {
      return DxbcTessPartitioning(bit::extract(m_bits, 11, 13));
    }

    DxbcUavFlags uavFlags() const {
      return DxbcUavFlags(bit::extract(m_bits, 16, 16));
    }

    DxbcConstantBufferAccessType accessType() const {
      return DxbcConstantBufferAccessType(bit::extract(m_bits, 11, 11));
    }

    uint32_t controlPointCount() const {
      return bit::extract(m_bits, 11, 16);
    }

    bool precise() const {
      return bit::extract(m_bits, 19, 22) != 0;
    }

  private:

    uint32_t m_bits;

  };

  /**
   * \brief Sample controls
   *
   * Constant texel offset with
   * values raning from -8 to 7.
   */
  struct DxbcShaderSampleControls {
    int u, v, w;
  };

  /**
   * \brief Immediate value
   *
   * Immediate argument represented either
   * as a 32-bit or 64-bit unsigned integer,
   * or a 32-bit or 32-bit floating point number.
   */
  union DxbcImmediate {
    float    f32;
    double   f64;
    uint32_t u32;
    uint64_t u64;
  };

  /**
   * \brief Shader instruction
   *
   * Note that this structure may store pointer to
   * external structures, such as the original code
   * buffer. This is safe to use if and only if:
   * - The \ref DxbcDecodeContext that created it
   *   still exists and was not moved
   * - The code buffer that was being decoded
   *   still exists and was not moved.
   */
  struct DxbcShaderInstruction {
    DxbcOpcode                op;
    DxbcInstClass             opClass;
    DxbcOpModifiers           modifiers;
    DxbcShaderOpcodeControls  controls;
    DxbcShaderSampleControls  sampleControls;

    uint32_t                  dstCount;
    uint32_t                  srcCount;
    uint32_t                  immCount;

    const DxbcRegister*       dst;
    const DxbcRegister*       src;
    const DxbcImmediate*      imm;

    DxbcCustomDataClass       customDataType;
    uint32_t                  customDataSize;
    const uint32_t*           customData;
  };

  /**
   * \brief DXBC code slice
   *
   * Convenient pointer pair that allows
   * reading the code word stream safely.
   */
  class DxbcCodeSlice {

  public:

    DxbcCodeSlice(
      const uint32_t* ptr,
      const uint32_t* end)
    : m_ptr(ptr), m_end(end) { }

    const uint32_t* ptrAt(uint32_t id) const;

    uint32_t at(uint32_t id) const;
    uint32_t read();

    DxbcCodeSlice take(uint32_t n) const;
    DxbcCodeSlice skip(uint32_t n) const;

    bool atEnd() const {
      return m_ptr == m_end;
    }

  private:

    const uint32_t* m_ptr = nullptr;
    const uint32_t* m_end = nullptr;

  };

  /**
   * \brief Decode context
   *
   * Stores data that is required to decode a single
   * instruction. This data is not persistent, so it
   * should be forwarded to the compiler right away.
   */
  class DxbcDecodeContext {

  public:

    /**
     * \brief Retrieves current instruction
     *
     * This is only valid after a call to \ref decode.
     * \returns Reference to last decoded instruction
     */
    const DxbcShaderInstruction& getInstruction() const {
      return m_instruction;
    }

    /**
     * \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
     */
    void decodeInstruction(DxbcCodeSlice& code);

  private:

    DxbcShaderInstruction m_instruction;

    std::array<DxbcRegister,  8> m_dstOperands;
    std::array<DxbcRegister,  8> m_srcOperands;
    std::array<DxbcImmediate, 4> m_immOperands;
    std::array<DxbcRegister, 12> m_indices;

    // Index into the indices array. Used when decoding
    // instruction operands with relative indexing.
    uint32_t m_indexId = 0;

    void decodeCustomData(DxbcCodeSlice code);
    void decodeOperation(DxbcCodeSlice code);

    void decodeComponentSelection(DxbcRegister& reg, uint32_t token);
    void decodeOperandExtensions(DxbcCodeSlice& code, DxbcRegister& reg, uint32_t token);
    void decodeOperandImmediates(DxbcCodeSlice& code, DxbcRegister& reg);
    void decodeOperandIndex(DxbcCodeSlice& code, DxbcRegister& reg, uint32_t token);

    void decodeRegister(DxbcCodeSlice& code, DxbcRegister& reg, DxbcScalarType type);
    void decodeImm32(DxbcCodeSlice& code, DxbcImmediate& imm, DxbcScalarType type);

    void decodeOperand(DxbcCodeSlice& code, const DxbcInstOperandFormat& format);

  };

}