#pragma once #include #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; /** * \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; bool isRawSsbo = false; }; /** * \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 coherence = 0; bool isRawSsbo = false; }; /** * \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)) { } uint32_t raw() const { return m_mask; } 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; } explicit 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, 17)); } 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 m_dstOperands; std::array m_srcOperands; std::array m_immOperands; std::array 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); }; }