[dxbc] Added DXBC to SPIR-V compiler stub

This commit is contained in:
Philip Rebohle 2017-10-16 17:50:09 +02:00
parent 6e27b7c0cc
commit bb5b588d23
19 changed files with 893 additions and 0 deletions

View File

@ -0,0 +1,24 @@
#include "dxbc_chunk_shex.h"
namespace dxvk {
DxbcShex::DxbcShex(DxbcReader reader) {
// The shader version and type are stored in a 32-bit unit,
// where the first byte contains the major and minor version
// numbers, and the high word contains the program type.
auto pVersion = reader.readu16() & 0xFF;
auto pType = reader.readEnum<DxbcProgramType>();
m_version = DxbcProgramVersion(pVersion >> 4, pVersion & 0xF, pType);
// Read the actual shader code as an array of DWORDs.
auto codeLength = reader.readu32() - 2;
m_code.resize(codeLength);
reader.read(m_code.data(), codeLength * sizeof(uint32_t));
}
DxbcShex::~DxbcShex() {
}
}

View File

@ -0,0 +1,42 @@
#pragma once
#include "dxbc_common.h"
#include "dxbc_instruction.h"
#include "dxbc_reader.h"
namespace dxvk {
/**
* \brief Shader code chunk
*
* Stores the DXBC shader code itself, as well
* as some meta info about the shader, i.e. what
* type of shader this is.
*/
class DxbcShex : public RcObject {
public:
DxbcShex(DxbcReader reader);
~DxbcShex();
DxbcProgramVersion version() const {
return m_version;
}
DxbcInstructionIterator begin() const {
return DxbcInstructionIterator(m_code.data());
}
DxbcInstructionIterator end() const {
return DxbcInstructionIterator(m_code.data() + m_code.size());
}
private:
DxbcProgramVersion m_version;
std::vector<uint32_t> m_code;
};
}

70
src/dxbc/dxbc_common.h Normal file
View File

@ -0,0 +1,70 @@
#pragma once
#include "dxbc_include.h"
namespace dxvk {
/**
* \brief DXBC Program type
*
* Defines the shader stage that a DXBC
* module has been compiled form.
*/
enum class DxbcProgramType : uint16_t {
PixelShader = 0,
VertexShader = 1,
GeometryShader = 2,
HullShader = 3,
DomainShader = 4,
ComputeShader = 5,
};
/**
* \brief DXBC shader version info
*
* Stores the shader model version
* as well as the program type.
*/
class DxbcProgramVersion {
public:
DxbcProgramVersion() { }
DxbcProgramVersion(
uint8_t major, uint8_t minor, DxbcProgramType type)
: m_major(major), m_minor(minor), m_type(type) { }
/**
* \brief Major version
* \returns Major version
*/
uint32_t major() const {
return m_major;
}
/**
* \brief Minor version
* \returns Minor version
*/
uint32_t minor() const {
return m_minor;
}
/**
* \brief Program type
* \returns Program type
*/
DxbcProgramType type() const {
return m_type;
}
private:
uint8_t m_major = 0;
uint8_t m_minor = 0;
DxbcProgramType m_type = DxbcProgramType::PixelShader;
};
}

View File

@ -0,0 +1,26 @@
#include "dxbc_compiler.h"
namespace dxvk {
DxbcCompiler::DxbcCompiler(DxbcProgramVersion version) {
}
DxbcCompiler::~DxbcCompiler() {
}
void DxbcCompiler::processInstruction(DxbcInstruction ins) {
Logger::info(str::format(
static_cast<uint32_t>(ins.opcode())));
}
Rc<DxvkShader> DxbcCompiler::finalize() {
return new DxvkShader(VK_SHADER_STAGE_COMPUTE_BIT,
DxvkSpirvCodeBuffer(), 0, nullptr);
}
}

39
src/dxbc/dxbc_compiler.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include "../dxvk/dxvk_shader.h"
#include "dxbc_chunk_shex.h"
namespace dxvk {
/**
* \brief DXBC to SPIR-V compiler
*
*
*/
class DxbcCompiler {
public:
DxbcCompiler(DxbcProgramVersion version);
~DxbcCompiler();
/**
* \brief Processes a single instruction
* \param [in] ins The instruction
*/
void processInstruction(DxbcInstruction ins);
/**
* \brief Creates actual shader object
*
* Combines all information gatherd during the
* shader compilation into one shader object.
*/
Rc<DxvkShader> finalize();
private:
};
}

30
src/dxbc/dxbc_header.cpp Normal file
View File

@ -0,0 +1,30 @@
#include "dxbc_header.h"
namespace dxvk {
DxbcHeader::DxbcHeader(DxbcReader& reader) {
// FourCC at the start of the file, must be 'DXBC'
DxbcTag fourcc = reader.readTag();
if (fourcc != "DXBC")
throw DxvkError("DxbcHeader::DxbcHeader: Invalid fourcc, expected 'DXBC'");
// Stuff we don't actually need to store
reader.skip(4 * sizeof(uint32_t)); // Check sum
reader.skip(1 * sizeof(uint32_t)); // Constant 1
reader.skip(1 * sizeof(uint32_t)); // Bytecode length
// Number of chunks in the file
uint32_t chunkCount = reader.readu32();
// Chunk offsets are stored immediately after
for (uint32_t i = 0; i < chunkCount; i++)
m_chunkOffsets.push_back(reader.readu32());
}
DxbcHeader::~DxbcHeader() {
}
}

48
src/dxbc/dxbc_header.h Normal file
View File

@ -0,0 +1,48 @@
#pragma once
#include <vector>
#include "dxbc_reader.h"
namespace dxvk {
/**
* \brief DXBC header
*
* Stores information about the shader file itself
* and the data chunks stored inside the file.
*/
class DxbcHeader {
public:
DxbcHeader(DxbcReader& reader);
~DxbcHeader();
/**
* \brief Number of chunks
* \returns Chunk count
*/
uint32_t numChunks() const {
return m_chunkOffsets.size();
}
/**
* \brief Chunk offset
*
* Retrieves the offset of a chunk, in
* bytes, from the start of the file.
* \param [in] chunkId Chunk index
* \returns Byte offset of that chunk
*/
uint32_t chunkOffset(uint32_t chunkId) const {
return m_chunkOffsets.at(chunkId);
}
private:
std::vector<uint32_t> m_chunkOffsets;
};
}

16
src/dxbc/dxbc_include.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include "../util/com/com_guid.h"
#include "../util/com/com_object.h"
#include "../util/com/com_pointer.h"
#include "../util/log/log.h"
#include "../util/log/log_debug.h"
#include "../util/rc/util_rc.h"
#include "../util/rc/util_rc_ptr.h"
#include "../util/util_bit.h"
#include "../util/util_enum.h"
#include "../util/util_error.h"
#include "../util/util_string.h"

View File

@ -0,0 +1,85 @@
#pragma once
#include "dxbc_opcode.h"
namespace dxvk {
/**
* \brief DXBC instruction
*
* Provides convenience methods to extract the
* opcode, instruction length, and instruction
* arguments from an instruction.
*/
class DxbcInstruction {
public:
DxbcInstruction() { }
DxbcInstruction(const uint32_t* code)
: m_code(code) { }
/**
* \brief Instruction code
* \returns The operation code
*/
DxbcOpcode opcode() const {
return static_cast<DxbcOpcode>(
bit::extract<uint32_t, 0, 10>(m_code[0]));
}
/**
* \brief Instruction length
*
* Number of DWORDs for this instruction,
* including the initial opcode token.
* \returns Instruction length
*/
uint32_t length() const {
return this->opcode() != DxbcOpcode::CustomData
? bit::extract<uint32_t, 24, 30>(m_code[0])
: m_code[1];
}
private:
const uint32_t* m_code = nullptr;
};
/**
* \brief DXBC instruction iterator
*
* Iterator that walks over DXBC instructions.
* Instruction boundaries are easy to find as
* the length of each instruction is encoded
* in the opcode token, much like in SPIR-V.
*/
class DxbcInstructionIterator {
public:
DxbcInstructionIterator() { }
DxbcInstructionIterator(const uint32_t* code)
: m_code(code) { }
DxbcInstructionIterator& operator ++ () {
m_code += DxbcInstruction(m_code).length();
return *this;
}
DxbcInstruction operator * () const {
return DxbcInstruction(m_code);
}
bool operator == (const DxbcInstructionIterator& other) const { return m_code == other.m_code; }
bool operator != (const DxbcInstructionIterator& other) const { return m_code != other.m_code; }
private:
const uint32_t* m_code = nullptr;
};
}

43
src/dxbc/dxbc_module.cpp Normal file
View File

@ -0,0 +1,43 @@
#include "dxbc_compiler.h"
#include "dxbc_module.h"
namespace dxvk {
DxbcModule::DxbcModule(DxbcReader& reader)
: m_header(reader) {
for (uint32_t i = 0; i < m_header.numChunks(); i++) {
// The chunk tag is stored at the beginning of each chunk
auto chunkReader = reader.clone(m_header.chunkOffset(i));
auto tag = chunkReader.readTag();
// The chunk size follows right after the four-character
// code. This does not include the eight bytes that are
// consumed by the FourCC and chunk length entry.
auto chunkLength = chunkReader.readu32() + 8;
chunkReader = chunkReader.resize(chunkLength);
if ((tag == "SHDR") || (tag == "SHEX"))
m_shexChunk = new DxbcShex(chunkReader);
}
}
DxbcModule::~DxbcModule() {
}
Rc<DxvkShader> DxbcModule::compile() const {
if (m_shexChunk == nullptr)
throw DxvkError("DxbcModule::compile: No SHDR/SHEX chunk");
DxbcCompiler compiler(m_shexChunk->version());
for (auto ins : *m_shexChunk)
compiler.processInstruction(ins);
return compiler.finalize();
}
}

43
src/dxbc/dxbc_module.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include "../dxvk/dxvk_shader.h"
#include "dxbc_chunk_shex.h"
#include "dxbc_header.h"
#include "dxbc_reader.h"
// References used for figuring out DXBC:
// - https://github.com/tgjones/slimshader-cpp
// - Wine
namespace dxvk {
/**
* \brief DXBC shader module
*
* Reads the DXBC byte code and extracts information
* about the resource bindings and the instruction
* stream. A module can then be compiled to SPIR-V.
*/
class DxbcModule {
public:
DxbcModule(DxbcReader& reader);
~DxbcModule();
/**
* \brief Compiles DXBC shader to SPIR-V module
* \returns The compiled DXVK shader object
*/
Rc<DxvkShader> compile() const;
private:
DxbcHeader m_header;
Rc<DxbcShex> m_shexChunk;
};
}

220
src/dxbc/dxbc_opcode.h Normal file
View File

@ -0,0 +1,220 @@
#pragma once
#include "dxbc_include.h"
namespace dxvk {
/**
* \brief Instruction code listing
*/
enum class DxbcOpcode : uint32_t {
Add = 0,
And = 1,
Break = 2,
Breakc = 3,
Call = 4,
Callc = 5,
Case = 6,
Continue = 7,
Continuec = 8,
Cut = 9,
Default = 10,
DerivRtx = 11,
DerivRty = 12,
Discard = 13,
Div = 14,
Dp2 = 15,
Dp3 = 16,
Dp4 = 17,
Else = 18,
Emit = 19,
EmitThenCut = 20,
EndIf = 21,
EndLoop = 22,
EndSwitch = 23,
Eq = 24,
Exp = 25,
Frc = 26,
FtoI = 27,
FtoU = 28,
Ge = 29,
IAdd = 30,
If = 31,
IEq = 32,
IGe = 33,
ILt = 34,
IMad = 35,
IMax = 36,
IMin = 37,
IMul = 38,
INe = 39,
INeg = 40,
IShl = 41,
IShr = 42,
ItoF = 43,
Label = 44,
Ld = 45,
LdMs = 46,
Log = 47,
Loop = 48,
Lt = 49,
Mad = 50,
Min = 51,
Max = 52,
CustomData = 53,
Mov = 54,
Movc = 55,
Mul = 56,
Ne = 57,
Nop = 58,
Not = 59,
Or = 60,
ResInfo = 61,
Ret = 62,
Retc = 63,
RoundNe = 64,
RoundNi = 65,
RoundPi = 66,
RoundZ = 67,
Rsq = 68,
Sample = 69,
SampleC = 70,
SampleClz = 71,
SampleL = 72,
SampleD = 73,
SampleB = 74,
Sqrt = 75,
Switch = 76,
SinCos = 77,
UDiv = 78,
ULt = 79,
UGe = 80,
UMul = 81,
UMad = 82,
UMax = 83,
UMin = 84,
UShr = 85,
UtoF = 86,
Xor = 87,
DclResource = 88,
DclConstantBuffer = 89,
DclSampler = 90,
DclIndexRange = 91,
DclGsOutputPrimitiveTopology = 92,
DclGsInputPrimitive = 93,
DclMaxOutputVertexCount = 94,
DclInput = 95,
DclInputSgv = 96,
DclInputSiv = 97,
DclInputPs = 98,
DclInputPsSgv = 99,
DclInputPsSiv = 100,
DclOutput = 101,
DclOutputSgv = 102,
DclOutputSiv = 103,
DclTemps = 104,
DclIndexableTemp = 105,
DclGlobalFlags = 106,
Reserved0 = 107,
Lod = 108,
Gather4 = 109,
SamplePos = 110,
SampleInfo = 111,
Reserved1 = 112,
HsDecls = 113,
HsControlPointPhase = 114,
HsForkPhase = 115,
HsJoinPhase = 116,
EmitStream = 117,
CutStream = 118,
EmitThenCutStream = 119,
InterfaceCall = 120,
BufInfo = 121,
DerivRtxCoarse = 122,
DerivRtxFine = 123,
DerivRtyCoarse = 124,
DerivRtyFine = 125,
Gather4C = 126,
Gather4Po = 127,
Gather4PoC = 128,
Rcp = 129,
F32toF16 = 130,
F16toF32 = 131,
UAddc = 132,
USubb = 133,
CountBits = 134,
FirstBitHi = 135,
FirstBitLo = 136,
FirstBitShi = 137,
UBfe = 138,
IBfe = 139,
Bfi = 140,
BfRev = 141,
Swapc = 142,
DclStream = 143,
DclFunctionBody = 144,
DclFunctionTable = 145,
DclInterface = 146,
DclInputControlPointCount = 147,
DclOutputControlPointCount = 148,
DclTessDomain = 149,
DclTessPartitioning = 150,
DclTessOutputPrimitive = 151,
DclHsMaxTessFactor = 152,
DclHsForkPhaseInstanceCount = 153,
DclHsJoinPhaseInstanceCount = 154,
DclThreadGroup = 155,
DclUnorderedAccessViewTyped = 156,
DclUnorderedAccessViewRaw = 157,
DclUnorderedAccessViewStructured = 158,
DclThreadGroupSharedMemoryRaw = 159,
DclThreadGroupSharedMemoryStructured = 160,
DclResourceRaw = 161,
DclResourceStructured = 162,
LdUavTyped = 163,
StoreUavTyped = 164,
LdRaw = 165,
StoreRaw = 166,
LdStructured = 167,
StoreStructured = 168,
AtomicAnd = 169,
AtomicOr = 170,
AtomicXor = 171,
AtomicCmpStore = 172,
AtomicIAdd = 173,
AtomicIMax = 174,
AtomicIMin = 175,
AtomicUMax = 176,
AtomicUMin = 177,
ImmAtomicAlloc = 178,
ImmAtomicConsume = 179,
ImmAtomicIAdd = 180,
ImmAtomicAnd = 181,
ImmAtomicOr = 182,
ImmAtomicXor = 183,
ImmAtomicExch = 184,
ImmAtomicCmpExch = 185,
ImmAtomicImax = 186,
ImmAtomicImin = 187,
ImmAtomicUmax = 188,
ImmAtomicUmin = 189,
Sync = 190,
DAdd = 191,
DMax = 192,
DMin = 193,
DMul = 194,
DEq = 195,
DGe = 196,
DLt = 197,
DNe = 198,
DMov = 199,
DMovc = 200,
DtoF = 201,
FtoD = 202,
EvalSnapped = 203,
EvalSampleIndex = 204,
EvalCentroid = 205,
DclGsInstanceCount = 206,
};
}

53
src/dxbc/dxbc_reader.cpp Normal file
View File

@ -0,0 +1,53 @@
#include <cstring>
#include "dxbc_reader.h"
namespace dxvk {
DxbcTag DxbcReader::readTag() {
DxbcTag tag;
this->read(&tag, 4);
return tag;
}
std::string DxbcReader::readString() {
std::string result;
while (m_data[m_pos] != '\0')
result.push_back(m_data[m_pos++]);
m_pos++;
return result;
}
void DxbcReader::read(void* dst, size_t n) {
if (m_pos + n > m_size)
throw DxvkError("DxbcReader::read: Unexpected end of file");
std::memcpy(dst, m_data + m_pos, n);
m_pos += n;
}
void DxbcReader::skip(size_t n) {
if (m_pos + n > m_size)
throw DxvkError("DxbcReader::skip: Unexpected end of file");
m_pos += n;
}
DxbcReader DxbcReader::clone(size_t pos) const {
if (pos > m_size)
throw DxvkError("DxbcReader::clone: Invalid offset");
return DxbcReader(m_data + pos, m_size - pos);
}
DxbcReader DxbcReader::resize(size_t size) const {
if (size > m_size)
throw DxvkError("DxbcReader::resize: Invalid size");
return DxbcReader(m_data, size, m_pos);
}
}

76
src/dxbc/dxbc_reader.h Normal file
View File

@ -0,0 +1,76 @@
#pragma once
#include <cstddef>
#include <string>
#include "dxbc_tag.h"
namespace dxvk {
/**
* \brief DXBC bytecode reader
*
* Holds references to the shader byte code and
* provides methods to read
*/
class DxbcReader {
public:
DxbcReader(const char* data, size_t size)
: DxbcReader(data, size, 0) { }
auto readu8 () { return this->readNum<uint8_t> (); }
auto readu16() { return this->readNum<uint16_t>(); }
auto readu32() { return this->readNum<uint32_t>(); }
auto readu64() { return this->readNum<uint64_t>(); }
auto readi8 () { return this->readNum<int8_t> (); }
auto readi16() { return this->readNum<int16_t> (); }
auto readi32() { return this->readNum<int32_t> (); }
auto readi64() { return this->readNum<int64_t> (); }
auto readf32() { return this->readNum<float> (); }
auto readf64() { return this->readNum<double> (); }
template<typename T>
auto readEnum() {
using Tx = std::underlying_type_t<T>;
return static_cast<T>(this->readNum<Tx>());
}
DxbcTag readTag();
std::string readString();
void read(void* dst, size_t n);
void skip(size_t n);
DxbcReader clone(size_t pos) const;
DxbcReader resize(size_t size) const;
bool eof() const {
return m_pos >= m_size;
}
private:
DxbcReader(const char* data, size_t size, size_t pos)
: m_data(data), m_size(size), m_pos(pos) { }
const char* m_data = nullptr;
size_t m_size = 0;
size_t m_pos = 0;
template<typename T>
T readNum() {
T result;
this->read(&result, sizeof(result));
return result;
}
};
}

47
src/dxbc/dxbc_tag.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include "dxbc_include.h"
namespace dxvk {
/**
* \brief Four-character tag
*
* Used to identify chunks in the
* compiled DXBC file by name.
*/
class DxbcTag {
public:
DxbcTag() {
for (size_t i = 0; i < 4; i++)
m_chars[i] = '\0';
}
DxbcTag(const char* tag) {
for (size_t i = 0; i < 4; i++)
m_chars[i] = tag[i];
}
bool operator == (const DxbcTag& other) const {
bool result = true;
for (size_t i = 0; i < 4; i++)
result &= m_chars[i] == other.m_chars[i];
return result;
}
bool operator != (const DxbcTag& other) const {
return !this->operator == (other);
}
const char* operator & () const { return m_chars; }
char* operator & () { return m_chars; }
private:
char m_chars[4];
};
}

14
src/dxbc/meson.build Normal file
View File

@ -0,0 +1,14 @@
dxbc_src = files([
'dxbc_chunk_shex.cpp',
'dxbc_compiler.cpp',
'dxbc_header.cpp',
'dxbc_module.cpp',
'dxbc_reader.cpp',
])
dxbc_lib = static_library('dxbc', dxbc_src,
include_directories : [ dxvk_include_path ])
dxbc_dep = declare_dependency(
link_with : [ dxbc_lib ],
include_directories : [ dxvk_include_path, include_directories('.') ])

View File

@ -1,4 +1,5 @@
subdir('util')
subdir('dxvk')
subdir('dxgi')
subdir('dxbc')
subdir('d3d11')

10
src/util/util_bit.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
namespace dxvk::bit {
template<typename T, T Fst, T Lst>
constexpr T extract(T value) {
return (value >> Fst) & ~(~T(0) << (Lst - Fst + 1));
}
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <locale>
#include <codecvt>
#include <string>
#include <sstream>
@ -20,4 +22,8 @@ namespace dxvk::str {
return stream.str();
}
inline std::string fromws(const std::wstring& ws) {
return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(ws);
}
}