diff --git a/src/dxbc/dxbc_annotation.cpp b/src/dxbc/dxbc_annotation.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/dxbc/dxbc_annotation.h b/src/dxbc/dxbc_annotation.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/dxbc/dxbc_capability.cpp b/src/dxbc/dxbc_capability.cpp new file mode 100644 index 000000000..e717cb6a4 --- /dev/null +++ b/src/dxbc/dxbc_capability.cpp @@ -0,0 +1,25 @@ +#include "dxbc_capability.h" + +namespace dxvk { + + DxbcCapabilities:: DxbcCapabilities() { } + DxbcCapabilities::~DxbcCapabilities() { } + + + DxvkSpirvCodeBuffer DxbcCapabilities::code() const { + DxvkSpirvCodeBuffer code; + + for (auto cap : m_caps) { + code.putIns (spv::OpCapability, 2); + code.putWord(cap); + } + + return code; + } + + + void DxbcCapabilities::enable(spv::Capability cap) { + m_caps.insert(cap); + } + +} \ No newline at end of file diff --git a/src/dxbc/dxbc_capability.h b/src/dxbc/dxbc_capability.h new file mode 100644 index 000000000..12bc9fbc0 --- /dev/null +++ b/src/dxbc/dxbc_capability.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include "dxbc_include.h" + +namespace dxvk { + + /** + * \brief SPIR-V capability set + * + * Holds a code buffer solely for the \c OpCapability + * instructions in the generated SPIR-V shader module. + */ + class DxbcCapabilities { + + public: + + DxbcCapabilities(); + ~DxbcCapabilities(); + + /** + * \brief Code buffer + * + * Code buffer that contains the + * \c OpCapability instructions. + * \returns Code buffer + */ + DxvkSpirvCodeBuffer code() const; + + /** + * \brief Enables a capability + * + * If the given capability has not been explicitly + * enabled yet, this will generate an \c OpCapability + * instruction for the given capability. + * \param [in] cap The capability + */ + void enable(spv::Capability cap); + + private: + + std::unordered_set m_caps; + + }; + +} \ No newline at end of file diff --git a/src/dxbc/dxbc_common.cpp b/src/dxbc/dxbc_common.cpp new file mode 100644 index 000000000..ccc3e1ea2 --- /dev/null +++ b/src/dxbc/dxbc_common.cpp @@ -0,0 +1,32 @@ +#include "dxbc_common.h" + +namespace dxvk { + + VkShaderStageFlagBits DxbcProgramVersion::shaderStage() const { + switch (m_type) { + case DxbcProgramType::PixelShader : return VK_SHADER_STAGE_FRAGMENT_BIT; + case DxbcProgramType::VertexShader : return VK_SHADER_STAGE_VERTEX_BIT; + case DxbcProgramType::GeometryShader : return VK_SHADER_STAGE_GEOMETRY_BIT; + case DxbcProgramType::HullShader : return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; + case DxbcProgramType::DomainShader : return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; + case DxbcProgramType::ComputeShader : return VK_SHADER_STAGE_COMPUTE_BIT; + } + + throw DxvkError("DxbcProgramVersion::shaderStage: Unsupported program type"); + } + + + spv::ExecutionModel DxbcProgramVersion::executionModel() const { + switch (m_type) { + case DxbcProgramType::PixelShader : return spv::ExecutionModelFragment; + case DxbcProgramType::VertexShader : return spv::ExecutionModelVertex; + case DxbcProgramType::GeometryShader : return spv::ExecutionModelGeometry; + case DxbcProgramType::HullShader : return spv::ExecutionModelTessellationControl; + case DxbcProgramType::DomainShader : return spv::ExecutionModelTessellationEvaluation; + case DxbcProgramType::ComputeShader : return spv::ExecutionModelGLCompute; + } + + throw DxvkError("DxbcProgramVersion::executionModel: Unsupported program type"); + } + +} \ No newline at end of file diff --git a/src/dxbc/dxbc_common.h b/src/dxbc/dxbc_common.h index d78a121ae..447065e4b 100644 --- a/src/dxbc/dxbc_common.h +++ b/src/dxbc/dxbc_common.h @@ -59,6 +59,24 @@ namespace dxvk { return m_type; } + /** + * \brief Vulkan shader stage + * + * The \c VkShaderStageFlagBits constant + * that corresponds to the program type. + * \returns Vulkan shaer stage + */ + VkShaderStageFlagBits shaderStage() const; + + /** + * \brief SPIR-V execution model + * + * The execution model that corresponds + * to the Vulkan shader stage. + * \returns SPIR-V execution model + */ + spv::ExecutionModel executionModel() const; + private: uint8_t m_major = 0; diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 789ea9687..63da715d7 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -4,7 +4,26 @@ namespace dxvk { DxbcCompiler::DxbcCompiler(DxbcProgramVersion version) : m_version(version) { - this->enableCapability(spv::CapabilityShader); + m_spvCapabilities.enable(spv::CapabilityShader); + + m_spvEntryPoints.setMemoryModel( + spv::AddressingModelLogical, + spv::MemoryModelGLSL450); + + auto id = m_counter.nextId(); + m_spvEntryPoints.addEntryPoint(id, + spv::ExecutionModelGLCompute, + "main", 0, nullptr); + m_spvEntryPoints.setLocalSize(id, 64, 1, 1); + auto ft = m_spvTypeInfo.typeFunction(m_counter, + m_spvTypeInfo.typeVoid(m_counter), 0, nullptr); + m_spvCode.putIns (spv::OpFunction, 5); + m_spvCode.putWord (m_spvTypeInfo.typeVoid(m_counter)); + m_spvCode.putWord (id); + m_spvCode.putWord (0); + m_spvCode.putWord (ft); + m_spvCode.putIns (spv::OpFunctionEnd, 1); + m_entryPointId = m_counter.nextId(); } @@ -22,34 +41,13 @@ namespace dxvk { Rc DxbcCompiler::finalize() { DxvkSpirvCodeBuffer codeBuffer; codeBuffer.putHeader(m_counter.numIds()); - codeBuffer.append(m_spirvCapabilities); - codeBuffer.append(m_spirvProgramCode); + codeBuffer.append(m_spvCapabilities.code()); + codeBuffer.append(m_spvEntryPoints.code()); + codeBuffer.append(m_spvTypeInfo.code()); + codeBuffer.append(m_spvCode); - return new DxvkShader(this->shaderStage(), + return new DxvkShader(m_version.shaderStage(), std::move(codeBuffer), 0, nullptr); } - - VkShaderStageFlagBits DxbcCompiler::shaderStage() const { - switch (m_version.type()) { - case DxbcProgramType::PixelShader : return VK_SHADER_STAGE_FRAGMENT_BIT; - case DxbcProgramType::VertexShader : return VK_SHADER_STAGE_VERTEX_BIT; - case DxbcProgramType::GeometryShader : return VK_SHADER_STAGE_GEOMETRY_BIT; - case DxbcProgramType::HullShader : return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; - case DxbcProgramType::DomainShader : return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; - case DxbcProgramType::ComputeShader : return VK_SHADER_STAGE_COMPUTE_BIT; - } - - throw DxvkError("DxbcCompiler::shaderStage: Unknown program type"); - } - - - void DxbcCompiler::enableCapability(spv::Capability cap) { - if (m_capabilities.find(cap) == m_capabilities.end()) { - m_spirvCapabilities.putIns (spv::OpCapability, 2); - m_spirvCapabilities.putWord(cap); - m_capabilities.insert(cap); - } - } - } \ No newline at end of file diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index 11b333173..107d1bfb3 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -1,10 +1,9 @@ #pragma once -#include - -#include "../dxvk/dxvk_shader.h" - +#include "dxbc_capability.h" #include "dxbc_chunk_shex.h" +#include "dxbc_entrypoint.h" +#include "dxbc_typeinfo.h" namespace dxvk { @@ -39,14 +38,12 @@ namespace dxvk { DxbcProgramVersion m_version; DxvkSpirvIdCounter m_counter; - std::unordered_set m_capabilities; + DxbcCapabilities m_spvCapabilities; + DxbcEntryPoint m_spvEntryPoints; + DxbcTypeInfo m_spvTypeInfo; + DxvkSpirvCodeBuffer m_spvCode; - DxvkSpirvCodeBuffer m_spirvCapabilities; - DxvkSpirvCodeBuffer m_spirvProgramCode; - - VkShaderStageFlagBits shaderStage() const; - - void enableCapability(spv::Capability cap); + uint32_t m_entryPointId = 0; }; diff --git a/src/dxbc/dxbc_entrypoint.cpp b/src/dxbc/dxbc_entrypoint.cpp new file mode 100644 index 000000000..5f1b6a098 --- /dev/null +++ b/src/dxbc/dxbc_entrypoint.cpp @@ -0,0 +1,56 @@ +#include "dxbc_entrypoint.h" + +namespace dxvk { + + DxbcEntryPoint:: DxbcEntryPoint() { } + DxbcEntryPoint::~DxbcEntryPoint() { } + + + DxvkSpirvCodeBuffer DxbcEntryPoint::code() const { + DxvkSpirvCodeBuffer code; + code.append(m_memoryModel); + code.append(m_entryPoints); + code.append(m_execModeInfo); + return code; + } + + + void DxbcEntryPoint::setMemoryModel( + spv::AddressingModel addressModel, + spv::MemoryModel memoryModel) { + m_memoryModel.putIns (spv::OpMemoryModel, 3); + m_memoryModel.putWord (addressModel); + m_memoryModel.putWord (memoryModel); + } + + + void DxbcEntryPoint::addEntryPoint( + uint32_t functionId, + spv::ExecutionModel execModel, + const char* name, + uint32_t interfaceCount, + const uint32_t* interfaceIds) { + m_entryPoints.putIns (spv::OpEntryPoint, 3 + m_entryPoints.strLen(name) + interfaceCount); + m_entryPoints.putWord (execModel); + m_entryPoints.putWord (functionId); + m_entryPoints.putStr (name); + + for (uint32_t i = 0; i < interfaceCount; i++) + m_entryPoints.putWord(interfaceIds[i]); + } + + + void DxbcEntryPoint::setLocalSize( + uint32_t functionId, + uint32_t x, + uint32_t y, + uint32_t z) { + m_execModeInfo.putIns (spv::OpExecutionMode, 6); + m_execModeInfo.putWord(functionId); + m_execModeInfo.putWord(spv::ExecutionModeLocalSize); + m_execModeInfo.putWord(x); + m_execModeInfo.putWord(y); + m_execModeInfo.putWord(z); + } + +} \ No newline at end of file diff --git a/src/dxbc/dxbc_entrypoint.h b/src/dxbc/dxbc_entrypoint.h new file mode 100644 index 000000000..be75704ec --- /dev/null +++ b/src/dxbc/dxbc_entrypoint.h @@ -0,0 +1,82 @@ +#pragma once + +#include "dxbc_include.h" + +namespace dxvk { + + /** + * \brief SPIR-V entry point info + * + * Accumulates information about the entry + * point of the generated shader module, + * including execution mode info. + */ + class DxbcEntryPoint { + + public: + + DxbcEntryPoint(); + ~DxbcEntryPoint(); + + /** + * \brief Generates SPIR-V code + * \returns SPIR-V code buffer + */ + DxvkSpirvCodeBuffer code() const; + + /** + * \brief Sets memory model + * + * Generates an \c OpMemoryModel instruction. + * Only ever call this once, or otherwise the + * resulting shader will become undefined. + * \param [in] addressModel Address model + * \param [in] memoryModel Memory model + */ + void setMemoryModel( + spv::AddressingModel addressModel, + spv::MemoryModel memoryModel); + + /** + * \brief Adds an entry point + * + * Currently, DXVK expects there to be a single entry point + * with the name \c main. Do not create additional entry points. + * \param [in] functionId Entry point function ID + * \param [in] execModel Execution model for the function + * \param [in] name Entry point name that is used by Vulkan + * \param [in] interfaceCount Number of additional interface IDs + * \param [in] interfaceIds List of additional interface IDs + */ + void addEntryPoint( + uint32_t functionId, + spv::ExecutionModel execModel, + const char* name, + uint32_t interfaceCount, + const uint32_t* interfaceIds); + + /** + * \brief Sets local work group size for a compute shader + * + * Adds a \c OpExecutionMode instruction that sets + * the local work group size for a compute shader. + * \param [in] functionId Entry point ID + * \param [in] x Number of threads in X direction + * \param [in] y Number of threads in Y direction + * \param [in] z Number of threads in Z direction + */ + void setLocalSize( + uint32_t functionId, + uint32_t x, + uint32_t y, + uint32_t z); + + private: + + DxvkSpirvCodeBuffer m_memoryModel; + DxvkSpirvCodeBuffer m_entryPoints; + DxvkSpirvCodeBuffer m_execModeInfo; + + }; + +} \ No newline at end of file diff --git a/src/dxbc/dxbc_include.h b/src/dxbc/dxbc_include.h index 88f765e6d..9b86ade63 100644 --- a/src/dxbc/dxbc_include.h +++ b/src/dxbc/dxbc_include.h @@ -1,5 +1,7 @@ #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" diff --git a/src/dxbc/dxbc_typeinfo.cpp b/src/dxbc/dxbc_typeinfo.cpp new file mode 100644 index 000000000..8efb7522c --- /dev/null +++ b/src/dxbc/dxbc_typeinfo.cpp @@ -0,0 +1,151 @@ +#include "dxbc_typeinfo.h" + +namespace dxvk { + + DxbcTypeInfo:: DxbcTypeInfo() { } + DxbcTypeInfo::~DxbcTypeInfo() { } + + + DxvkSpirvCodeBuffer DxbcTypeInfo::code() const { + return m_code; + } + + + uint32_t DxbcTypeInfo::typeVoid( + DxvkSpirvIdCounter& ids) { + return this->getTypeId(ids, + spv::OpTypeVoid, 0, nullptr); + } + + + uint32_t DxbcTypeInfo::typeBool( + DxvkSpirvIdCounter& ids) { + return this->getTypeId(ids, + spv::OpTypeBool, 0, nullptr); + } + + + uint32_t DxbcTypeInfo::typeInt( + DxvkSpirvIdCounter& ids, + uint32_t width, + uint32_t isSigned) { + std::array args = {{ width, isSigned }}; + return this->getTypeId(ids, + spv::OpTypeInt, args.size(), args.data()); + } + + + uint32_t DxbcTypeInfo::typeFloat( + DxvkSpirvIdCounter& ids, + uint32_t width) { + return this->getTypeId(ids, + spv::OpTypeFloat, 1, &width); + } + + + uint32_t DxbcTypeInfo::typeVector( + DxvkSpirvIdCounter& ids, + uint32_t componentType, + uint32_t componentCount) { + std::array args = {{ componentType, componentCount }}; + return this->getTypeId(ids, + spv::OpTypeVector, args.size(), args.data()); + } + + + uint32_t DxbcTypeInfo::typeMatrix( + DxvkSpirvIdCounter& ids, + uint32_t colType, + uint32_t colCount) { + std::array args = {{ colType, colCount }}; + return this->getTypeId(ids, + spv::OpTypeMatrix, args.size(), args.data()); + } + + + uint32_t DxbcTypeInfo::typeArray( + DxvkSpirvIdCounter& ids, + uint32_t elementType, + uint32_t elementCount) { + std::array args = {{ elementType, elementCount }}; + return this->getTypeId(ids, + spv::OpTypeArray, args.size(), args.data()); + } + + + uint32_t DxbcTypeInfo::typeRuntimeArray( + DxvkSpirvIdCounter& ids, + uint32_t elementType) { + return this->getTypeId(ids, + spv::OpTypeRuntimeArray, 1, &elementType); + } + + + uint32_t DxbcTypeInfo::typePointer( + DxvkSpirvIdCounter& ids, + spv::StorageClass storageClass, + uint32_t type) { + std::array args = {{ storageClass, type }}; + return this->getTypeId(ids, + spv::OpTypePointer, args.size(), args.data()); + } + + + uint32_t DxbcTypeInfo::typeFunction( + DxvkSpirvIdCounter& ids, + uint32_t returnType, + uint32_t argCount, + const uint32_t* argTypes) { + std::vector args(argCount + 1); + args.at(0) = returnType; + + for (uint32_t i = 0; i < argCount; i++) + args.at(i + 1) = argTypes[i]; + + return this->getTypeId(ids, + spv::OpTypeFunction, + args.size(), args.data()); + } + + + uint32_t DxbcTypeInfo::typeStruct( + DxvkSpirvIdCounter& ids, + uint32_t memberCount, + const uint32_t* memberTypes) { + return this->getTypeId(ids, + spv::OpTypeStruct, + memberCount, + memberTypes); + } + + + uint32_t DxbcTypeInfo::getTypeId( + DxvkSpirvIdCounter& ids, + spv::Op op, + uint32_t argCount, + const uint32_t* args) { + // Since the type info is stored in the code buffer, + // we can use the code buffer to look up type IDs as + // well. Result IDs are always stored as argument 1. + for (auto ins : m_code) { + bool match = ins.opCode() == op; + + for (uint32_t i = 0; i < argCount && match; i++) + match &= ins.arg(2 + i) == args[i]; + + if (match) + return ins.arg(1); + } + + // Type not yet declared, create a new one. + uint32_t result = ids.nextId(); + m_code.putIns (op, 2 + argCount); + m_code.putWord(result); + + for (uint32_t i = 0; i < argCount; i++) + m_code.putWord(args[i]); + return result; + } + + +} \ No newline at end of file diff --git a/src/dxbc/dxbc_typeinfo.h b/src/dxbc/dxbc_typeinfo.h new file mode 100644 index 000000000..c4c93316b --- /dev/null +++ b/src/dxbc/dxbc_typeinfo.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +#include "dxbc_include.h" + +namespace dxvk { + + /** + * \brief SPIR-V type set + * + * Stores SPIR-V type definition so that + * each type will only be declared once. + */ + class DxbcTypeInfo { + + public: + + DxbcTypeInfo(); + ~DxbcTypeInfo(); + + DxvkSpirvCodeBuffer code() const; + + uint32_t typeVoid( + DxvkSpirvIdCounter& ids); + + uint32_t typeBool( + DxvkSpirvIdCounter& ids); + + uint32_t typeInt( + DxvkSpirvIdCounter& ids, + uint32_t width, + uint32_t isSigned); + + uint32_t typeFloat( + DxvkSpirvIdCounter& ids, + uint32_t width); + + uint32_t typeVector( + DxvkSpirvIdCounter& ids, + uint32_t componentType, + uint32_t componentCount); + + uint32_t typeMatrix( + DxvkSpirvIdCounter& ids, + uint32_t colType, + uint32_t colCount); + + uint32_t typeArray( + DxvkSpirvIdCounter& ids, + uint32_t elementType, + uint32_t elementCount); + + uint32_t typeRuntimeArray( + DxvkSpirvIdCounter& ids, + uint32_t elementType); + + uint32_t typePointer( + DxvkSpirvIdCounter& ids, + spv::StorageClass storageClass, + uint32_t type); + + uint32_t typeFunction( + DxvkSpirvIdCounter& ids, + uint32_t returnType, + uint32_t argCount, + const uint32_t* argTypes); + + uint32_t typeStruct( + DxvkSpirvIdCounter& ids, + uint32_t memberCount, + const uint32_t* memberTypes); + + private: + + DxvkSpirvCodeBuffer m_code; + + uint32_t getTypeId( + DxvkSpirvIdCounter& ids, + spv::Op op, + uint32_t argCount, + const uint32_t* args); + + }; + +} \ No newline at end of file diff --git a/src/dxbc/meson.build b/src/dxbc/meson.build index 219d9297d..cc3c89325 100644 --- a/src/dxbc/meson.build +++ b/src/dxbc/meson.build @@ -1,9 +1,14 @@ dxbc_src = files([ + 'dxbc_annotation.cpp', + 'dxbc_capability.cpp', 'dxbc_chunk_shex.cpp', + 'dxbc_common.cpp', 'dxbc_compiler.cpp', + 'dxbc_entrypoint.cpp', 'dxbc_header.cpp', 'dxbc_module.cpp', 'dxbc_reader.cpp', + 'dxbc_typeinfo.cpp', ]) dxbc_lib = static_library('dxbc', dxbc_src, diff --git a/src/dxvk/spirv/dxvk_spirv_code_buffer.cpp b/src/dxvk/spirv/dxvk_spirv_code_buffer.cpp index b31979ef8..a2edb5c25 100644 --- a/src/dxvk/spirv/dxvk_spirv_code_buffer.cpp +++ b/src/dxvk/spirv/dxvk_spirv_code_buffer.cpp @@ -33,7 +33,7 @@ namespace dxvk { uint32_t* dst = this->m_code.data(); const uint32_t* src = other.m_code.data(); - std::memcpy(dst + size, src, sizeof(uint32_t) * size); + std::memcpy(dst + size, src, other.size()); } } diff --git a/src/dxvk/spirv/dxvk_spirv_code_buffer.h b/src/dxvk/spirv/dxvk_spirv_code_buffer.h index a6e0d4b53..b7418845c 100644 --- a/src/dxvk/spirv/dxvk_spirv_code_buffer.h +++ b/src/dxvk/spirv/dxvk_spirv_code_buffer.h @@ -5,7 +5,7 @@ #include #include -#include "../dxvk_include.h" +#include "dxvk_spirv_instruction.h" namespace dxvk { @@ -40,6 +40,27 @@ namespace dxvk { return m_code.size() * sizeof(uint32_t); } + /** + * \brief Begin instruction iterator + * + * Points to the first instruction in the instruction + * block. The header, if any, will be skipped over. + * \returns Instruction iterator + */ + DxvkSpirvInstructionIterator begin() const { + return DxvkSpirvInstructionIterator(m_code.data(), m_code.size()); + } + + /** + * \brief End instruction iterator + * + * Points to the end of the instruction block. + * \returns Instruction iterator + */ + DxvkSpirvInstructionIterator end() const { + return DxvkSpirvInstructionIterator(nullptr, 0); + } + /** * \brief Merges two code buffers * diff --git a/src/dxvk/spirv/dxvk_spirv_id_counter.h b/src/dxvk/spirv/dxvk_spirv_id_counter.h index bf5eb1e50..dfa2d9689 100644 --- a/src/dxvk/spirv/dxvk_spirv_id_counter.h +++ b/src/dxvk/spirv/dxvk_spirv_id_counter.h @@ -15,8 +15,8 @@ namespace dxvk { public: - uint32_t nexId() { - return m_id++; + uint32_t nextId() { + return ++m_id; } uint32_t numIds() const { diff --git a/src/dxvk/spirv/dxvk_spirv_instruction.h b/src/dxvk/spirv/dxvk_spirv_instruction.h new file mode 100644 index 000000000..fddc8e03c --- /dev/null +++ b/src/dxvk/spirv/dxvk_spirv_instruction.h @@ -0,0 +1,117 @@ +#pragma once + +#include + +#include "../dxvk_include.h" + +namespace dxvk { + + /** + * \brief SPIR-V instruction + * + * Helps parsing a single instruction, providing + * access to the op code, instruction length and + * instruction arguments. + */ + class DxvkSpirvInstruction { + + public: + + DxvkSpirvInstruction() { } + DxvkSpirvInstruction( + const uint32_t* code, uint32_t size) + : m_code(code), m_size(size) { } + + /** + * \brief SPIR-V Op code + * \returns The op code + */ + spv::Op opCode() const { + return static_cast( + m_code[0] & spv::OpCodeMask); + } + + /** + * \brief Instruction length + * \returns Number of DWORDs + */ + uint32_t length() const { + return m_code[0] >> spv::WordCountShift; + } + + /** + * \brief Argument value + * + * Retrieves an argument DWORD. Note that some instructions + * take 64-bit arguments which require more than one DWORD. + * Arguments start at index 1. Calling this method with an + * argument ID of 0 will return the opcode token. + * \param [in] id Argument index, starting at 1 + * \returns The argument value + */ + uint32_t arg(uint32_t id) const { + return id < m_size ? m_code[id] : 0; + } + + private: + + uint32_t const* m_code = nullptr; + uint32_t m_size = 0; + + }; + + + /** + * \brief SPIR-V instruction + * + * Convenient iterator that can be used + * to process raw SPIR-V shader code. + */ + class DxvkSpirvInstructionIterator { + + public: + + DxvkSpirvInstructionIterator() { } + DxvkSpirvInstructionIterator(const uint32_t* code, uint32_t size) + : m_code(size != 0 ? code : nullptr), m_size(size) { + if ((size >= 5) && (m_code[0] == spv::MagicNumber)) + this->advance(5); + } + + DxvkSpirvInstructionIterator& operator ++ () { + this->advance(DxvkSpirvInstruction(m_code, m_size).length()); + return *this; + } + + DxvkSpirvInstruction operator * () const { + return DxvkSpirvInstruction(m_code, m_size); + } + + bool operator == (const DxvkSpirvInstructionIterator& other) const { + return this->m_code == other.m_code + && this->m_size == other.m_size; + } + + bool operator != (const DxvkSpirvInstructionIterator& other) const { + return this->m_code != other.m_code + && this->m_size != other.m_size; + } + + private: + + uint32_t const* m_code = nullptr; + uint32_t m_size = 0; + + void advance(uint32_t n) { + if (m_size >= n) { + m_code += n; + m_size -= n; + } else { + m_code = nullptr; + m_size = 0; + } + } + + }; + +} \ No newline at end of file