[dxbc] Shader decoder and compiler overhaul (1/2)

Major rewrite of the entire shader decoder to generate easy
to parse data structures for the compiler, which ultimately
allows new instructions to be implemented more easily.
This commit is contained in:
Philip Rebohle 2017-12-18 00:28:54 +01:00
parent 2f99be9546
commit 47347e38da
15 changed files with 2752 additions and 74 deletions

View File

@ -2,6 +2,7 @@
#include "dxbc_common.h"
#include "dxbc_decoder.h"
#include "dxbc_decoder_2.h"
#include "dxbc_reader.h"
namespace dxvk {
@ -24,6 +25,11 @@ namespace dxvk {
return m_version;
}
DxbcCodeSlice slice() const {
return DxbcCodeSlice(m_code.data(),
m_code.data() + m_code.size());
}
DxbcDecoder begin() const {
return DxbcDecoder(m_code.data(), m_code.size());
}

View File

@ -1263,7 +1263,7 @@ namespace dxvk {
// We only write to part of the destination
// register, so we need to load and modify it
DxbcValue tmp = this->loadPtr(ptr);
tmp = this->insertReg(tmp, srcValue, mask);
tmp = this->insertReg(tmp, srcValue, mask);
m_module.opStore(ptr.pointerId, tmp.valueId);
}

View File

@ -33,53 +33,6 @@ namespace dxvk {
uint32_t pointerId = 0;
};
/**
* \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 Shader resource binding
*
* Stores a resource variable
* and associated type IDs.
*/
struct DxbcShaderResource {
uint32_t varId = 0;
uint32_t sampledTypeId = 0;
uint32_t textureTypeId = 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;
};
/**
* \brief Compiler error code
*

1600
src/dxbc/dxbc_compiler_2.cpp Normal file

File diff suppressed because it is too large Load Diff

311
src/dxbc/dxbc_compiler_2.h Normal file
View File

@ -0,0 +1,311 @@
#pragma once
#include <array>
#include <vector>
#include "../spirv/spirv_module.h"
#include "dxbc_chunk_isgn.h"
#include "dxbc_decoder_2.h"
#include "dxbc_defs.h"
#include "dxbc_names.h"
#include "dxbc_util.h"
namespace dxvk {
struct DxbcVectorType {
DxbcScalarType ctype;
uint32_t ccount;
};
struct DxbcRegisterInfo {
DxbcVectorType type;
spv::StorageClass sclass;
};
struct DxbcRegisterValue {
DxbcVectorType type;
uint32_t id;
};
struct DxbcRegisterPointer {
DxbcVectorType type;
uint32_t id;
};
struct DxbcCompilerVsPart {
uint32_t functionId;
};
struct DxbcCompilerPsPart {
uint32_t functionId;
std::array<DxbcVectorType, DxbcMaxInterfaceRegs> oTypes;
};
/**
* \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 DxbcCompiler2 {
public:
DxbcCompiler2(
const DxbcProgramVersion& version,
const Rc<DxbcIsgn>& isgn,
const Rc<DxbcIsgn>& osgn);
~DxbcCompiler2();
/**
* \brief Processes a single instruction
* \param [in] ins The instruction
*/
void processInstruction(
const DxbcShaderInstruction& ins);
/**
* \brief Finalizes the shader
* \returns The final shader object
*/
Rc<DxvkShader> finalize();
private:
DxbcProgramVersion m_version;
SpirvModule m_module;
Rc<DxbcIsgn> m_isgn;
Rc<DxbcIsgn> m_osgn;
///////////////////////////////////////////////////////
// Resource slot description for the shader. This will
// be used to map D3D11 bindings to DXVK bindings.
std::vector<DxvkResourceSlot> m_resourceSlots;
///////////////////////////////
// r# registers of type float4
std::vector<uint32_t> m_rRegs;
///////////////////////////////////////////////////////////
// v# registers as defined by the shader. The type of each
// of these inputs is either float4 or an array of float4.
std::array<uint32_t, 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<uint32_t, DxbcMaxInterfaceRegs> m_oRegs;
std::vector<DxbcSvMapping> m_oMappings;
//////////////////////////////////////////////////////
// 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;
///////////////////////////////////////////////////////////
// Array of input values. Since v# registers are indexable
// in DXBC, we need to copy them into an array first.
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;
///////////////////////////////////////////////////
// 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;
///////////////////////////////////
// Shader-specific data structures
DxbcCompilerVsPart m_vs;
DxbcCompilerPsPart m_ps;
/////////////////////////////////////////////////////
// Shader interface and metadata declaration methods
void emitDclGlobalFlags(
const DxbcShaderInstruction& ins);
void emitDclTemps(
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 emitDclSampler(
const DxbcShaderInstruction& ins);
void emitDclResource(
const DxbcShaderInstruction& ins);
//////////////////////////////
// Instruction class handlers
void emitVectorAlu(
const DxbcShaderInstruction& ins);
void emitVectorCmov(
const DxbcShaderInstruction& ins);
void emitVectorCmp(
const DxbcShaderInstruction& ins);
void emitVectorDot(
const DxbcShaderInstruction& ins);
void emitVectorImul(
const DxbcShaderInstruction& ins);
void emitVectorSinCos(
const DxbcShaderInstruction& ins);
void emitSample(
const DxbcShaderInstruction& ins);
void emitRet(
const DxbcShaderInstruction& ins);
/////////////////////////////////////////
// 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 emitRegisterExtend(
DxbcRegisterValue value,
uint32_t size);
DxbcRegisterValue emitRegisterAbsolute(
DxbcRegisterValue value);
DxbcRegisterValue emitRegisterNegate(
DxbcRegisterValue value);
DxbcRegisterValue emitSrcOperandModifiers(
DxbcRegisterValue value,
DxbcRegModifiers modifiers);
DxbcRegisterValue emitDstOperandModifiers(
DxbcRegisterValue value,
DxbcOpModifiers modifiers);
////////////////////////
// Address load methods
DxbcRegisterPointer emitGetTempPtr(
const DxbcRegister& operand);
DxbcRegisterPointer emitGetInputPtr(
const DxbcRegister& operand);
DxbcRegisterPointer emitGetOutputPtr(
const DxbcRegister& operand);
DxbcRegisterPointer emitGetConstBufPtr(
const DxbcRegister& operand);
DxbcRegisterPointer emitGetOperandPtr(
const DxbcRegister& operand);
//////////////////////////////
// Operand load/store methods
DxbcRegisterValue emitIndexLoad(
DxbcRegIndex index);
DxbcRegisterValue emitValueLoad(
DxbcRegisterPointer ptr);
void emitValueStore(
DxbcRegisterPointer ptr,
DxbcRegisterValue value,
DxbcRegMask writeMask);
DxbcRegisterValue emitRegisterLoad(
const DxbcRegister& reg,
DxbcRegMask writeMask);
void emitRegisterStore(
const DxbcRegister& reg,
DxbcRegisterValue value);
/////////////////////////////
// Input preparation methods
void emitVsInputSetup();
void emitPsInputSetup();
//////////////////////////////
// Output preparation methods
void emitVsOutputSetup();
void emitPsOutputSetup();
/////////////////////////////////
// Shader initialization methods
void emitVsInit();
void emitPsInit();
///////////////////////////////
// Shader finalization methods
void emitVsFinalize();
void emitPsFinalize();
///////////////////////////////
// Variable definition methods
uint32_t emitNewVariable(
const DxbcRegisterInfo& info);
///////////////////////////
// Type definition methods
uint32_t getScalarTypeId(
DxbcScalarType type);
uint32_t getVectorTypeId(
const DxbcVectorType& type);
uint32_t getPointerTypeId(
const DxbcRegisterInfo& type);
uint32_t getPerVertexBlockId();
};
}

View File

@ -9,6 +9,41 @@ namespace dxvk {
class DxbcOperand;
/**
* \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 Shader resource binding
*
* Stores a resource variable
* and associated type IDs.
*/
struct DxbcShaderResource {
uint32_t varId = 0;
uint32_t sampledTypeId = 0;
uint32_t textureTypeId = 0;
};
/**
* \brief Component swizzle
*
@ -73,6 +108,19 @@ namespace dxvk {
};
/**
* \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;
};
/**
* \brief Basic control info
*

333
src/dxbc/dxbc_decoder_2.cpp Normal file
View File

@ -0,0 +1,333 @@
#include "dxbc_decoder_2.h"
namespace dxvk {
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.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();
// 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) {
Logger::warn("DxbcDecodeContext::decodeCustomData: Not implemented");
}
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.zeroTest = static_cast<DxbcZeroTest> (bit::extract(token, 18, 18));
m_instruction.controls.syncFlags = static_cast<DxbcSyncFlags> (bit::extract(token, 11, 14));
m_instruction.controls.resourceDim = static_cast<DxbcResourceDim> (bit::extract(token, 11, 15));
m_instruction.controls.resinfoType = static_cast<DxbcResinfoType> (bit::extract(token, 11, 12));
m_instruction.controls.interpolation = static_cast<DxbcInterpolationMode>(bit::extract(token, 11, 14));
// 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;
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);
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<DxbcRegComponentCount>(bit::extract(token, 0, 1));
switch (reg.componentCount) {
// No components - used for samplers etc.
case DxbcRegComponentCount::c0:
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 DxbcRegComponentCount::c1:
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 DxbcRegComponentCount::c4: {
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) {
switch (reg.componentCount) {
// This is commonly used if only one vector
// component is involved in an operation
case DxbcRegComponentCount::c1: {
reg.imm.u32_1 = code.read();
} break;
// Typical four-component vector
case DxbcRegComponentCount::c4: {
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");
}
} else if (reg.type == DxbcOperandType::Imm64) {
Logger::warn("DxbcDecodeContext: 64-bit immediates not supported");
}
}
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");
}
}
}

192
src/dxbc/dxbc_decoder_2.h Normal file
View File

@ -0,0 +1,192 @@
#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;
enum class DxbcRegComponentCount : uint32_t {
c0 = 0,
c1 = 1,
c4 = 2,
};
enum class DxbcRegModifier : uint32_t {
Neg = 0,
Abs = 1,
};
using DxbcRegModifiers = Flags<DxbcRegModifier>;
struct DxbcRegIndex {
DxbcRegister* relReg;
int32_t offset;
};
struct DxbcRegister {
DxbcOperandType type;
DxbcScalarType dataType;
DxbcRegComponentCount componentCount;
uint32_t idxDim;
DxbcRegIndex idx[DxbcMaxRegIndexDim];
DxbcRegMask mask;
DxbcRegSwizzle swizzle;
DxbcRegModifiers modifiers;
union {
uint32_t u32_4[4];
uint32_t u32_1;
} imm;
};
struct DxbcOpModifiers {
bool saturate;
bool precise;
};
struct DxbcShaderOpcodeControls {
DxbcZeroTest zeroTest;
DxbcSyncFlags syncFlags;
DxbcResourceDim resourceDim;
DxbcResinfoType resinfoType;
DxbcInterpolationMode interpolation;
};
struct DxbcShaderSampleControls {
int u, v, w;
};
union DxbcImmediate {
uint32_t u32;
uint64_t u64;
};
/**
* \brief Shader instruction
*/
struct DxbcShaderInstruction {
DxbcOpcode op;
DxbcOpModifiers modifiers;
DxbcShaderOpcodeControls controls;
DxbcShaderSampleControls sampleControls;
uint32_t dstCount;
uint32_t srcCount;
uint32_t immCount;
const DxbcRegister* dst;
const DxbcRegister* src;
const DxbcImmediate* imm;
};
/**
* \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) { }
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, 4> m_dstOperands;
std::array<DxbcRegister, 4> 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);
};
}

View File

@ -78,7 +78,10 @@ namespace dxvk {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
} },
/* Exp */
{ },
{ 2, DxbcInstClass::VectorAlu, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Float32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
} },
/* Frc */
{ },
/* FtoI */
@ -92,27 +95,68 @@ namespace dxvk {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
} },
/* IAdd */
{ },
{ 3, DxbcInstClass::VectorAlu, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
} },
/* If */
{ },
/* IEq */
{ },
{ 3, DxbcInstClass::VectorCmp, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
} },
/* IGe */
{ },
{ 3, DxbcInstClass::VectorCmp, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
} },
/* ILt */
{ },
{ 3, DxbcInstClass::VectorCmp, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
} },
/* IMad */
{ },
{ 4, DxbcInstClass::VectorAlu, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
} },
/* IMax */
{ },
{ 3, DxbcInstClass::VectorAlu, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
} },
/* IMin */
{ },
{ 3, DxbcInstClass::VectorAlu, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
} },
/* IMul */
{ },
{ 4, DxbcInstClass::VectorImul, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Float32 },
{ DxbcOperandKind::DstReg, DxbcScalarType::Float32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
} },
/* INe */
{ },
{ 3, DxbcInstClass::VectorCmp, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
} },
/* INeg */
{ },
{ 2, DxbcInstClass::VectorAlu, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Sint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Sint32 },
} },
/* IShl */
{ },
/* IShr */
@ -126,7 +170,10 @@ namespace dxvk {
/* LdMs */
{ },
/* Log */
{ },
{ 2, DxbcInstClass::VectorAlu, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Float32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
} },
/* Loop */
{ },
/* Lt */
@ -223,7 +270,10 @@ namespace dxvk {
/* SampleB */
{ },
/* Sqrt */
{ },
{ 2, DxbcInstClass::VectorAlu, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Float32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
} },
/* Switch */
{ },
/* SinCos */

View File

@ -34,6 +34,7 @@ namespace dxvk {
VectorCmov, ///< Component-wise conditional move
VectorCmp, ///< Component-wise vector comparison
VectorDot, ///< Dot product instruction
VectorImul, ///< Component-wise integer multiplication
VectorSinCos, ///< Sine and Cosine instruction
ControlFlow, ///< Control flow instructions
Undefined, ///< Instruction code not defined

View File

@ -451,7 +451,7 @@ namespace dxvk {
enum class DxbcResinfoType : uint32_t {
Float = 0,
RcpFloat = 1, // ?
RcpFloat = 1,
Uint = 2,
};

View File

@ -1,4 +1,5 @@
#include "dxbc_compiler.h"
#include "dxbc_compiler_2.h"
#include "dxbc_module.h"
namespace dxvk {
@ -44,19 +45,19 @@ namespace dxvk {
if (m_shexChunk == nullptr)
throw DxvkError("DxbcModule::compile: No SHDR/SHEX chunk");
DxbcCompiler compiler(
DxbcCodeSlice slice = m_shexChunk->slice();
DxbcCompiler2 compiler(
m_shexChunk->version(),
m_isgnChunk, m_osgnChunk);
for (auto ins : *m_shexChunk) {
const DxbcError error = compiler.processInstruction(ins);
DxbcDecodeContext decoder;
while (!slice.atEnd()) {
decoder.decodeInstruction(slice);
if (error != DxbcError::sOk) {
Logger::err(str::format(
"dxbc: Error while processing ",
ins.token().opcode(), ": Error ",
static_cast<uint32_t>(error)));
}
compiler.processInstruction(
decoder.getInstruction());
}
return compiler.finalize();

View File

@ -3,8 +3,10 @@ dxbc_src = files([
'dxbc_chunk_shex.cpp',
'dxbc_common.cpp',
'dxbc_compiler.cpp',
'dxbc_compiler_2.cpp',
'dxbc_defs.cpp',
'dxbc_decoder.cpp',
'dxbc_decoder_2.cpp',
'dxbc_header.cpp',
'dxbc_module.cpp',
'dxbc_names.cpp',

View File

@ -836,6 +836,40 @@ namespace dxvk {
}
uint32_t SpirvModule::opSMax(
uint32_t resultType,
uint32_t a,
uint32_t b) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpExtInst, 7);
m_code.putWord(resultType);
m_code.putWord(resultId);
m_code.putWord(m_instExtGlsl450);
m_code.putWord(spv::GLSLstd450SMax);
m_code.putWord(a);
m_code.putWord(b);
return resultId;
}
uint32_t SpirvModule::opSMin(
uint32_t resultType,
uint32_t a,
uint32_t b) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpExtInst, 7);
m_code.putWord(resultType);
m_code.putWord(resultId);
m_code.putWord(m_instExtGlsl450);
m_code.putWord(spv::GLSLstd450SMin);
m_code.putWord(a);
m_code.putWord(b);
return resultId;
}
uint32_t SpirvModule::opFClamp(
uint32_t resultType,
uint32_t x,
@ -885,6 +919,66 @@ namespace dxvk {
}
uint32_t SpirvModule::opSLessThan(
uint32_t resultType,
uint32_t vector1,
uint32_t vector2) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpSLessThan, 5);
m_code.putWord(resultType);
m_code.putWord(resultId);
m_code.putWord(vector1);
m_code.putWord(vector2);
return resultId;
}
uint32_t SpirvModule::opSLessThanEqual(
uint32_t resultType,
uint32_t vector1,
uint32_t vector2) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpSLessThanEqual, 5);
m_code.putWord(resultType);
m_code.putWord(resultId);
m_code.putWord(vector1);
m_code.putWord(vector2);
return resultId;
}
uint32_t SpirvModule::opSGreaterThan(
uint32_t resultType,
uint32_t vector1,
uint32_t vector2) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpSGreaterThan, 5);
m_code.putWord(resultType);
m_code.putWord(resultId);
m_code.putWord(vector1);
m_code.putWord(vector2);
return resultId;
}
uint32_t SpirvModule::opSGreaterThanEqual(
uint32_t resultType,
uint32_t vector1,
uint32_t vector2) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpSGreaterThanEqual, 5);
m_code.putWord(resultType);
m_code.putWord(resultId);
m_code.putWord(vector1);
m_code.putWord(vector2);
return resultId;
}
uint32_t SpirvModule::opFOrdEqual(
uint32_t resultType,
uint32_t vector1,
@ -1020,9 +1114,24 @@ namespace dxvk {
}
uint32_t SpirvModule::opSqrt(
uint32_t resultType,
uint32_t operand) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpExtInst, 6);
m_code.putWord(resultType);
m_code.putWord(resultId);
m_code.putWord(m_instExtGlsl450);
m_code.putWord(spv::GLSLstd450Sqrt);
m_code.putWord(operand);
return resultId;
}
uint32_t SpirvModule::opInverseSqrt(
uint32_t resultType,
uint32_t x) {
uint32_t operand) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpExtInst, 6);
@ -1030,7 +1139,37 @@ namespace dxvk {
m_code.putWord(resultId);
m_code.putWord(m_instExtGlsl450);
m_code.putWord(spv::GLSLstd450InverseSqrt);
m_code.putWord(x);
m_code.putWord(operand);
return resultId;
}
uint32_t SpirvModule::opExp2(
uint32_t resultType,
uint32_t operand) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpExtInst, 6);
m_code.putWord(resultType);
m_code.putWord(resultId);
m_code.putWord(m_instExtGlsl450);
m_code.putWord(spv::GLSLstd450Exp2);
m_code.putWord(operand);
return resultId;
}
uint32_t SpirvModule::opLog2(
uint32_t resultType,
uint32_t operand) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpExtInst, 6);
m_code.putWord(resultType);
m_code.putWord(resultId);
m_code.putWord(m_instExtGlsl450);
m_code.putWord(spv::GLSLstd450Log2);
m_code.putWord(operand);
return resultId;
}

View File

@ -296,6 +296,16 @@ namespace dxvk {
uint32_t a,
uint32_t b);
uint32_t opSMax(
uint32_t resultType,
uint32_t a,
uint32_t b);
uint32_t opSMin(
uint32_t resultType,
uint32_t a,
uint32_t b);
uint32_t opFClamp(
uint32_t resultType,
uint32_t x,
@ -312,6 +322,26 @@ namespace dxvk {
uint32_t vector1,
uint32_t vector2);
uint32_t opSLessThan(
uint32_t resultType,
uint32_t vector1,
uint32_t vector2);
uint32_t opSLessThanEqual(
uint32_t resultType,
uint32_t vector1,
uint32_t vector2);
uint32_t opSGreaterThan(
uint32_t resultType,
uint32_t vector1,
uint32_t vector2);
uint32_t opSGreaterThanEqual(
uint32_t resultType,
uint32_t vector1,
uint32_t vector2);
uint32_t opFOrdEqual(
uint32_t resultType,
uint32_t vector1,
@ -355,9 +385,21 @@ namespace dxvk {
uint32_t resultType,
uint32_t vector);
uint32_t opSqrt(
uint32_t resultType,
uint32_t operand);
uint32_t opInverseSqrt(
uint32_t resultType,
uint32_t x);
uint32_t operand);
uint32_t opExp2(
uint32_t resultType,
uint32_t operand);
uint32_t opLog2(
uint32_t resultType,
uint32_t operand);
uint32_t opSelect(
uint32_t resultType,