[dxbc] Added type info struct

This commit is contained in:
Philip Rebohle 2017-10-17 13:02:57 +02:00
parent 77f38c33c4
commit c1e9d3236f
18 changed files with 679 additions and 42 deletions

View File

View File

View File

@ -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);
}
}

View File

@ -0,0 +1,47 @@
#pragma once
#include <unordered_set>
#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<spv::Capability> m_caps;
};
}

32
src/dxbc/dxbc_common.cpp Normal file
View File

@ -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");
}
}

View File

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

View File

@ -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<DxvkShader> 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);
}
}
}

View File

@ -1,10 +1,9 @@
#pragma once
#include <unordered_set>
#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<spv::Capability> 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;
};

View File

@ -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);
}
}

View File

@ -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;
};
}

View File

@ -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"

151
src/dxbc/dxbc_typeinfo.cpp Normal file
View File

@ -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<uint32_t, 2> 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<uint32_t, 2> 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<uint32_t, 2> 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<uint32_t, 2> 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<uint32_t, 2> 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<uint32_t> args(argCount + 1);
args.at(0) = returnType;
for (uint32_t i = 0; i < argCount; i++)
args.at(i + 1) = argTypes[i];
return this->getTypeId(ids,
spv::OpTypeFunction,
args.size(), args.data());
}
uint32_t 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;
}
}

86
src/dxbc/dxbc_typeinfo.h Normal file
View File

@ -0,0 +1,86 @@
#pragma once
#include <initializer_list>
#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);
};
}

View File

@ -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,

View File

@ -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());
}
}

View File

@ -5,7 +5,7 @@
#include <iostream>
#include <vector>
#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
*

View File

@ -15,8 +15,8 @@ namespace dxvk {
public:
uint32_t nexId() {
return m_id++;
uint32_t nextId() {
return ++m_id;
}
uint32_t numIds() const {

View File

@ -0,0 +1,117 @@
#pragma once
#include <spirv/spirv.hpp>
#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<spv::Op>(
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;
}
}
};
}