mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-01-18 11:52:12 +01:00
[dxbc] Shader decoder and compiler overhaul (2/2)
Removed the old decoder and the old shader compiler and added documentation to the new structures.
This commit is contained in:
parent
47347e38da
commit
858913ec0c
@ -2,7 +2,6 @@
|
||||
|
||||
#include "dxbc_common.h"
|
||||
#include "dxbc_decoder.h"
|
||||
#include "dxbc_decoder_2.h"
|
||||
#include "dxbc_reader.h"
|
||||
|
||||
namespace dxvk {
|
||||
@ -30,14 +29,6 @@ namespace dxvk {
|
||||
m_code.data() + m_code.size());
|
||||
}
|
||||
|
||||
DxbcDecoder begin() const {
|
||||
return DxbcDecoder(m_code.data(), m_code.size());
|
||||
}
|
||||
|
||||
DxbcDecoder end() const {
|
||||
return DxbcDecoder();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
DxbcProgramVersion m_version;
|
||||
|
@ -1,6 +1,4 @@
|
||||
#include "dxbc_compiler.h"
|
||||
#include "dxbc_names.h"
|
||||
#include "dxbc_util.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
@ -9,7 +7,6 @@ namespace dxvk {
|
||||
constexpr uint32_t PerVertex_CullDist = 2;
|
||||
constexpr uint32_t PerVertex_ClipDist = 3;
|
||||
|
||||
|
||||
DxbcCompiler::DxbcCompiler(
|
||||
const DxbcProgramVersion& version,
|
||||
const Rc<DxbcIsgn>& isgn,
|
||||
@ -26,19 +23,21 @@ namespace dxvk {
|
||||
spv::AddressingModelLogical,
|
||||
spv::MemoryModelGLSL450);
|
||||
|
||||
// Make sure our interface registers don't
|
||||
// contain any valid IDs at the moment.
|
||||
for (size_t i = 0; i < DxbcMaxInterfaceRegs; i++) {
|
||||
m_vRegs[i] = 0;
|
||||
m_oRegs[i] = 0;
|
||||
// Make sure our interface registers are clear
|
||||
for (uint32_t i = 0; i < DxbcMaxInterfaceRegs; i++) {
|
||||
m_ps.oTypes.at(i).ctype = DxbcScalarType::Float32;
|
||||
m_ps.oTypes.at(i).ccount = 0;
|
||||
|
||||
m_vRegs.at(i) = 0;
|
||||
m_oRegs.at(i) = 0;
|
||||
}
|
||||
|
||||
// Initialize the shader module with capabilities
|
||||
// etc. Each shader type has its own peculiarities.
|
||||
switch (m_version.type()) {
|
||||
case DxbcProgramType::PixelShader: this->beginPixelShader (osgn); break;
|
||||
case DxbcProgramType::VertexShader: this->beginVertexShader(isgn); break;
|
||||
default: Logger::err("dxbc: Unsupported shader type");
|
||||
case DxbcProgramType::VertexShader: this->emitVsInit(); break;
|
||||
case DxbcProgramType::PixelShader: this->emitPsInit(); break;
|
||||
default: throw DxvkError("DxbcCompiler: Unsupported program type");
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,23 +47,86 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::processInstruction(const DxbcInstruction& ins) {
|
||||
DxbcInst parsedInst;
|
||||
DxbcError parseError = this->parseInstruction(ins, parsedInst);
|
||||
void DxbcCompiler::processInstruction(const DxbcShaderInstruction& ins) {
|
||||
switch (ins.op) {
|
||||
case DxbcOpcode::DclGlobalFlags:
|
||||
return this->emitDclGlobalFlags(ins);
|
||||
|
||||
if (parseError != DxbcError::sOk)
|
||||
return parseError;
|
||||
case DxbcOpcode::DclTemps:
|
||||
return this->emitDclTemps(ins);
|
||||
|
||||
switch (parsedInst.format.instructionClass) {
|
||||
case DxbcInstClass::Declaration: return this->handleDeclaration (parsedInst);
|
||||
case DxbcInstClass::ControlFlow: return this->handleControlFlow (parsedInst);
|
||||
case DxbcInstClass::TextureSample: return this->handleTextureSample(parsedInst);
|
||||
case DxbcInstClass::VectorAlu: return this->handleVectorAlu (parsedInst);
|
||||
case DxbcInstClass::VectorCmov: return this->handleVectorCmov (parsedInst);
|
||||
case DxbcInstClass::VectorCmp: return this->handleVectorCmp (parsedInst);
|
||||
case DxbcInstClass::VectorDot: return this->handleVectorDot (parsedInst);
|
||||
case DxbcInstClass::VectorSinCos: return this->handleVectorSinCos (parsedInst);
|
||||
default: return DxbcError::eUnhandledOpcode;
|
||||
case DxbcOpcode::DclInput:
|
||||
case DxbcOpcode::DclInputSgv:
|
||||
case DxbcOpcode::DclInputSiv:
|
||||
case DxbcOpcode::DclInputPs:
|
||||
case DxbcOpcode::DclInputPsSgv:
|
||||
case DxbcOpcode::DclInputPsSiv:
|
||||
case DxbcOpcode::DclOutput:
|
||||
case DxbcOpcode::DclOutputSgv:
|
||||
case DxbcOpcode::DclOutputSiv:
|
||||
return this->emitDclInterfaceReg(ins);
|
||||
|
||||
case DxbcOpcode::DclConstantBuffer:
|
||||
return this->emitDclConstantBuffer(ins);
|
||||
|
||||
case DxbcOpcode::DclSampler:
|
||||
return this->emitDclSampler(ins);
|
||||
|
||||
case DxbcOpcode::DclResource:
|
||||
return this->emitDclResource(ins);
|
||||
|
||||
case DxbcOpcode::Add:
|
||||
case DxbcOpcode::Div:
|
||||
case DxbcOpcode::Exp:
|
||||
case DxbcOpcode::Log:
|
||||
case DxbcOpcode::Mad:
|
||||
case DxbcOpcode::Max:
|
||||
case DxbcOpcode::Min:
|
||||
case DxbcOpcode::Mul:
|
||||
case DxbcOpcode::Mov:
|
||||
case DxbcOpcode::Rsq:
|
||||
case DxbcOpcode::Sqrt:
|
||||
case DxbcOpcode::IAdd:
|
||||
case DxbcOpcode::IMad:
|
||||
case DxbcOpcode::IMax:
|
||||
case DxbcOpcode::IMin:
|
||||
case DxbcOpcode::INeg:
|
||||
return this->emitVectorAlu(ins);
|
||||
|
||||
case DxbcOpcode::Movc:
|
||||
return this->emitVectorCmov(ins);
|
||||
|
||||
case DxbcOpcode::Eq:
|
||||
case DxbcOpcode::Ge:
|
||||
case DxbcOpcode::Lt:
|
||||
case DxbcOpcode::Ne:
|
||||
case DxbcOpcode::IEq:
|
||||
case DxbcOpcode::IGe:
|
||||
case DxbcOpcode::ILt:
|
||||
case DxbcOpcode::INe:
|
||||
return this->emitVectorCmp(ins);
|
||||
|
||||
case DxbcOpcode::Dp2:
|
||||
case DxbcOpcode::Dp3:
|
||||
case DxbcOpcode::Dp4:
|
||||
return this->emitVectorDot(ins);
|
||||
|
||||
case DxbcOpcode::IMul:
|
||||
return this->emitVectorImul(ins);
|
||||
|
||||
case DxbcOpcode::SinCos:
|
||||
return this->emitVectorSinCos(ins);
|
||||
|
||||
case DxbcOpcode::Sample:
|
||||
return this->emitSample(ins);
|
||||
|
||||
case DxbcOpcode::Ret:
|
||||
return this->emitRet(ins);
|
||||
|
||||
default:
|
||||
Logger::warn(
|
||||
str::format("DxbcCompiler: Unhandled opcode: ",
|
||||
ins.op));
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,12 +141,13 @@ namespace dxvk {
|
||||
spv::FunctionControlMaskNone);
|
||||
m_module.opLabel(m_module.allocateId());
|
||||
|
||||
// Depending on the shader type, this will prepare input registers,
|
||||
// call various shader functions and write back the output registers.
|
||||
// Depending on the shader type, this will prepare
|
||||
// input registers, call various shader functions
|
||||
// and write back the output registers.
|
||||
switch (m_version.type()) {
|
||||
case DxbcProgramType::PixelShader: this->endPixelShader (); break;
|
||||
case DxbcProgramType::VertexShader: this->endVertexShader(); break;
|
||||
default: Logger::err("dxbc: Unsupported shader type");
|
||||
case DxbcProgramType::VertexShader: this->emitVsFinalize(); break;
|
||||
case DxbcProgramType::PixelShader: this->emitPsFinalize(); break;
|
||||
default: throw DxvkError("DxbcCompiler: Unsupported program type");
|
||||
}
|
||||
|
||||
// End main function
|
||||
@ -108,195 +171,226 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::handleDeclaration(const DxbcInst& ins) {
|
||||
switch (ins.opcode) {
|
||||
case DxbcOpcode::DclGlobalFlags:
|
||||
return this->declareGlobalFlags(ins);
|
||||
void DxbcCompiler::emitDclGlobalFlags(const DxbcShaderInstruction& ins) {
|
||||
// TODO implement properly
|
||||
}
|
||||
|
||||
case DxbcOpcode::DclTemps:
|
||||
return this->declareTemps(ins);
|
||||
|
||||
case DxbcOpcode::DclInput:
|
||||
case DxbcOpcode::DclInputSiv:
|
||||
case DxbcOpcode::DclInputSgv:
|
||||
case DxbcOpcode::DclInputPs:
|
||||
case DxbcOpcode::DclInputPsSiv:
|
||||
case DxbcOpcode::DclInputPsSgv:
|
||||
case DxbcOpcode::DclOutput:
|
||||
case DxbcOpcode::DclOutputSiv:
|
||||
case DxbcOpcode::DclOutputSgv:
|
||||
return this->declareInterfaceVar(ins);
|
||||
void DxbcCompiler::emitDclTemps(const DxbcShaderInstruction& ins) {
|
||||
// dcl_temps has one operand:
|
||||
// (imm0) Number of temp registers
|
||||
const uint32_t oldCount = m_rRegs.size();
|
||||
const uint32_t newCount = ins.imm[0].u32;
|
||||
|
||||
case DxbcOpcode::DclConstantBuffer:
|
||||
return this->declareConstantBuffer(ins);
|
||||
if (newCount > oldCount) {
|
||||
m_rRegs.resize(newCount);
|
||||
|
||||
case DxbcOpcode::DclSampler:
|
||||
return this->declareSampler(ins);
|
||||
DxbcRegisterInfo info;
|
||||
info.type.ctype = DxbcScalarType::Float32;
|
||||
info.type.ccount = 4;
|
||||
info.sclass = spv::StorageClassPrivate;
|
||||
|
||||
case DxbcOpcode::DclResource:
|
||||
return this->declareResource(ins);
|
||||
|
||||
default:
|
||||
return DxbcError::eUnhandledOpcode;
|
||||
for (uint32_t i = oldCount; i < newCount; i++) {
|
||||
const uint32_t varId = this->emitNewVariable(info);
|
||||
m_module.setDebugName(varId, str::format("r", i).c_str());
|
||||
m_rRegs.at(i) = varId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::declareGlobalFlags(const DxbcInst& ins) {
|
||||
// TODO add support for double-precision floats
|
||||
// TODO add support for early depth-stencil
|
||||
return DxbcError::sOk;
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::declareTemps(const DxbcInst& ins) {
|
||||
if (ins.operands[0].type != DxbcOperandType::Imm32) {
|
||||
Logger::err("dxbc: Number of temps not a contant");
|
||||
return DxbcError::eInvalidOperand;
|
||||
}
|
||||
|
||||
// Some shader program types use multiple sets of temps,
|
||||
// so we'll just check if we need to create new ones.
|
||||
const uint32_t newSize = ins.operands[0].immediates[0];
|
||||
const uint32_t oldSize = m_rRegs.size();
|
||||
|
||||
if (newSize > oldSize) {
|
||||
m_rRegs.resize(newSize);
|
||||
|
||||
// r# registers are always 4x32-bit float vectors
|
||||
const uint32_t regTypeId = this->defineVectorType(
|
||||
DxbcScalarType::Float32, 4);
|
||||
|
||||
const uint32_t ptrTypeId = m_module.defPointerType(
|
||||
regTypeId, spv::StorageClassPrivate);
|
||||
|
||||
for (uint32_t i = oldSize; i < newSize; i++) {
|
||||
m_rRegs.at(i) = m_module.newVar(
|
||||
ptrTypeId, spv::StorageClassPrivate);
|
||||
|
||||
m_module.setDebugName(m_rRegs.at(i),
|
||||
str::format("r", i).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return DxbcError::sOk;
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::declareInterfaceVar(const DxbcInst& ins) {
|
||||
const DxbcInstOp& op = ins.operands[0];
|
||||
void DxbcCompiler::emitDclInterfaceReg(const DxbcShaderInstruction& ins) {
|
||||
// dcl_input and dcl_output instructions
|
||||
// have the following operands:
|
||||
// (dst0) The register to declare
|
||||
// (imm0) The system value (optional)
|
||||
uint32_t regDim = 0;
|
||||
uint32_t regIdx = 0;
|
||||
|
||||
// In the vertex and fragment shader stage, the
|
||||
// operand indices will have the following format:
|
||||
// (0) Register index
|
||||
//
|
||||
// In other stages, the input and output registers
|
||||
// are declared as arrays of a fixed size:
|
||||
// (0) Array size
|
||||
// may be declared as arrays of a fixed size:
|
||||
// (0) Array length
|
||||
// (1) Register index
|
||||
uint32_t regId = 0;
|
||||
uint32_t regDim = 0;
|
||||
|
||||
if (op.indexDim == 1) {
|
||||
if (op.index[0].type != DxbcIndexType::Immediate)
|
||||
return DxbcError::eInvalidOperandIndex;
|
||||
|
||||
regId = op.index[0].immediate;
|
||||
} else if (op.indexDim == 2) {
|
||||
if (op.index[0].type != DxbcIndexType::Immediate
|
||||
|| op.index[1].type != DxbcIndexType::Immediate)
|
||||
return DxbcError::eInvalidOperandIndex;
|
||||
|
||||
regDim = op.index[0].immediate;
|
||||
regId = op.index[1].immediate;
|
||||
if (ins.dst[0].idxDim == 2) {
|
||||
regDim = ins.dst[0].idx[0].offset;
|
||||
regIdx = ins.dst[0].idx[1].offset;
|
||||
} else if (ins.dst[0].idxDim == 1) {
|
||||
regIdx = ins.dst[0].idx[0].offset;
|
||||
} else {
|
||||
Logger::err("dxbc: Invalid index dimension for v#/o# declaration");
|
||||
return DxbcError::eInvalidOperandIndex;
|
||||
Logger::err(str::format(
|
||||
"DxbcCompiler: ", ins.op,
|
||||
": Invalid index dimension"));
|
||||
return;
|
||||
}
|
||||
|
||||
// This declaration may map an output register to a system
|
||||
// value. If that is the case, the system value type will
|
||||
// be stored in the second operand.
|
||||
const bool hasSv =
|
||||
ins.opcode == DxbcOpcode::DclInputSgv
|
||||
|| ins.opcode == DxbcOpcode::DclInputSiv
|
||||
|| ins.opcode == DxbcOpcode::DclInputPsSgv
|
||||
|| ins.opcode == DxbcOpcode::DclInputPsSiv
|
||||
|| ins.opcode == DxbcOpcode::DclOutputSgv
|
||||
|| ins.opcode == DxbcOpcode::DclOutputSiv;
|
||||
ins.op == DxbcOpcode::DclInputSgv
|
||||
|| ins.op == DxbcOpcode::DclInputSiv
|
||||
|| ins.op == DxbcOpcode::DclInputPsSgv
|
||||
|| ins.op == DxbcOpcode::DclInputPsSiv
|
||||
|| ins.op == DxbcOpcode::DclOutputSgv
|
||||
|| ins.op == DxbcOpcode::DclOutputSiv;
|
||||
|
||||
DxbcSystemValue sv = DxbcSystemValue::None;
|
||||
|
||||
if (hasSv) {
|
||||
if (ins.operands[1].type != DxbcOperandType::Imm32) {
|
||||
Logger::err("dxbc: Invalid system value in v#/o# declaration");
|
||||
return DxbcError::eInstructionFormat;
|
||||
}
|
||||
|
||||
sv = static_cast<DxbcSystemValue>(
|
||||
ins.operands[1].immediates[0]);
|
||||
}
|
||||
if (hasSv)
|
||||
sv = static_cast<DxbcSystemValue>(ins.imm[0].u32);
|
||||
|
||||
// In the pixel shader, inputs are declared with an
|
||||
// interpolation mode that is part of the op token.
|
||||
// const bool hasInterpolationMode =
|
||||
// ins.opcode == DxbcOpcode::DclInputPs
|
||||
// || ins.opcode == DxbcOpcode::DclInputPsSiv;
|
||||
const bool hasInterpolationMode =
|
||||
ins.op == DxbcOpcode::DclInputPs
|
||||
|| ins.op == DxbcOpcode::DclInputPsSiv;
|
||||
|
||||
DxbcInterpolationMode im = DxbcInterpolationMode::Undefined;
|
||||
|
||||
// TODO implement this
|
||||
// if (hasInterpolationMode) {
|
||||
// im = static_cast<DxbcInterpolationMode>(
|
||||
// bit::extract(ins.token().control(), 0, 3));
|
||||
// }
|
||||
if (hasInterpolationMode)
|
||||
im = ins.controls.interpolation;
|
||||
|
||||
// Declare the actual variable
|
||||
switch (op.type) {
|
||||
case DxbcOperandType::Input:
|
||||
return this->declareInputVar(
|
||||
regId, regDim, op.mask, sv, im);
|
||||
// Declare the actual input/output variable
|
||||
switch (ins.op) {
|
||||
case DxbcOpcode::DclInput:
|
||||
case DxbcOpcode::DclInputSgv:
|
||||
case DxbcOpcode::DclInputSiv:
|
||||
case DxbcOpcode::DclInputPs:
|
||||
case DxbcOpcode::DclInputPsSgv:
|
||||
case DxbcOpcode::DclInputPsSiv:
|
||||
this->emitDclInput(regIdx, regDim, ins.dst[0].mask, sv, im);
|
||||
break;
|
||||
|
||||
case DxbcOperandType::Output:
|
||||
return this->declareOutputVar(
|
||||
regId, regDim, op.mask, sv, im);
|
||||
case DxbcOpcode::DclOutput:
|
||||
case DxbcOpcode::DclOutputSgv:
|
||||
case DxbcOpcode::DclOutputSiv:
|
||||
this->emitDclOutput(regIdx, regDim, ins.dst[0].mask, sv, im);
|
||||
break;
|
||||
|
||||
default:
|
||||
// We shouldn't ever be here
|
||||
return DxbcError::eInternal;
|
||||
Logger::err(str::format(
|
||||
"DxbcCompiler: Unexpected opcode: ",
|
||||
ins.op));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::declareConstantBuffer(const DxbcInst& ins) {
|
||||
const DxbcInstOp& op = ins.operands[0];
|
||||
|
||||
// This instruction has one operand with two indices:
|
||||
// (1) Constant buffer register ID (cb#)
|
||||
// (2) Number of constants in the buffer
|
||||
if (op.indexDim != 2) {
|
||||
Logger::err("dxbc: Constant buffer declaration requires two indices");
|
||||
return DxbcError::eInvalidOperandIndex;
|
||||
void DxbcCompiler::emitDclInput(
|
||||
uint32_t regIdx,
|
||||
uint32_t regDim,
|
||||
DxbcRegMask regMask,
|
||||
DxbcSystemValue sv,
|
||||
DxbcInterpolationMode im) {
|
||||
if (regDim != 0) {
|
||||
Logger::err("DxbcCompiler: Input arrays not yet supported");
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t bufferId = op.index[0].immediate;
|
||||
const uint32_t elementCount = op.index[1].immediate;
|
||||
// Avoid declaring the same variable multiple times.
|
||||
// This may happen when multiple system values are
|
||||
// mapped to different parts of the same register.
|
||||
if (m_vRegs.at(regIdx) == 0) {
|
||||
DxbcRegisterInfo info;
|
||||
info.type.ctype = DxbcScalarType::Float32;
|
||||
info.type.ccount = 4;
|
||||
info.sclass = spv::StorageClassInput;
|
||||
|
||||
const uint32_t varId = this->emitNewVariable(info);
|
||||
|
||||
m_module.decorateLocation(varId, regIdx);
|
||||
m_module.setDebugName(varId, str::format("v", regIdx).c_str());
|
||||
m_entryPointInterfaces.push_back(varId);
|
||||
|
||||
m_vRegs.at(regIdx) = varId;
|
||||
|
||||
// Interpolation mode, used in pixel shaders
|
||||
if (im == DxbcInterpolationMode::Constant)
|
||||
m_module.decorate(varId, spv::DecorationFlat);
|
||||
|
||||
if (im == DxbcInterpolationMode::LinearCentroid
|
||||
|| im == DxbcInterpolationMode::LinearNoPerspectiveCentroid)
|
||||
m_module.decorate(varId, spv::DecorationCentroid);
|
||||
|
||||
if (im == DxbcInterpolationMode::LinearNoPerspective
|
||||
|| im == DxbcInterpolationMode::LinearNoPerspectiveCentroid
|
||||
|| im == DxbcInterpolationMode::LinearNoPerspectiveSample)
|
||||
m_module.decorate(varId, spv::DecorationNoPerspective);
|
||||
|
||||
if (im == DxbcInterpolationMode::LinearSample
|
||||
|| im == DxbcInterpolationMode::LinearNoPerspectiveSample)
|
||||
m_module.decorate(varId, spv::DecorationSample);
|
||||
}
|
||||
|
||||
// Add a new system value mapping if needed
|
||||
// TODO declare SV if necessary
|
||||
if (sv != DxbcSystemValue::None)
|
||||
m_vMappings.push_back({ regIdx, regMask, sv });
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::emitDclOutput(
|
||||
uint32_t regIdx,
|
||||
uint32_t regDim,
|
||||
DxbcRegMask regMask,
|
||||
DxbcSystemValue sv,
|
||||
DxbcInterpolationMode im) {
|
||||
if (regDim != 0) {
|
||||
Logger::err("DxbcCompiler: Output arrays not yet supported");
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid declaring the same variable multiple times.
|
||||
// This may happen when multiple system values are
|
||||
// mapped to different parts of the same register.
|
||||
if (m_oRegs.at(regIdx) == 0) {
|
||||
DxbcRegisterInfo info;
|
||||
info.type.ctype = DxbcScalarType::Float32;
|
||||
info.type.ccount = 4;
|
||||
info.sclass = spv::StorageClassOutput;
|
||||
|
||||
const uint32_t varId = this->emitNewVariable(info);
|
||||
|
||||
m_module.decorateLocation(varId, regIdx);
|
||||
m_module.setDebugName(varId, str::format("o", regIdx).c_str());
|
||||
m_entryPointInterfaces.push_back(varId);
|
||||
|
||||
m_oRegs.at(regIdx) = varId;
|
||||
}
|
||||
|
||||
|
||||
// Add a new system value mapping if needed
|
||||
// TODO declare SV if necessary
|
||||
if (sv != DxbcSystemValue::None)
|
||||
m_oMappings.push_back({ regIdx, regMask, sv });
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::emitDclConstantBuffer(const DxbcShaderInstruction& ins) {
|
||||
// dcl_constant_buffer has one operand with two indices:
|
||||
// (0) Constant buffer register ID (cb#)
|
||||
// (1) Number of constants in the buffer
|
||||
const uint32_t bufferId = ins.dst[0].idx[0].offset;
|
||||
const uint32_t elementCount = ins.dst[0].idx[1].offset;
|
||||
|
||||
// Uniform buffer data is stored as a fixed-size array
|
||||
// of 4x32-bit vectors. SPIR-V requires explicit strides.
|
||||
uint32_t arrayType = m_module.defArrayTypeUnique(
|
||||
this->defineVectorType(DxbcScalarType::Float32, 4),
|
||||
const uint32_t arrayType = m_module.defArrayTypeUnique(
|
||||
getVectorTypeId({ DxbcScalarType::Float32, 4 }),
|
||||
m_module.constu32(elementCount));
|
||||
m_module.decorateArrayStride(arrayType, 16);
|
||||
|
||||
// SPIR-V requires us to put that array into a
|
||||
// struct and decorate that struct as a block.
|
||||
uint32_t structType = m_module.defStructTypeUnique(1, &arrayType);
|
||||
const uint32_t structType = m_module.defStructTypeUnique(1, &arrayType);
|
||||
m_module.memberDecorateOffset(structType, 0, 0);
|
||||
m_module.decorateBlock(structType);
|
||||
|
||||
// Variable that we'll use to access the buffer
|
||||
uint32_t varId = m_module.newVar(
|
||||
const uint32_t varId = m_module.newVar(
|
||||
m_module.defPointerType(structType, spv::StorageClassUniform),
|
||||
spv::StorageClassUniform);
|
||||
|
||||
@ -308,7 +402,7 @@ namespace dxvk {
|
||||
|
||||
// Compute the DXVK binding slot index for the buffer.
|
||||
// D3D11 needs to bind the actual buffers to this slot.
|
||||
uint32_t bindingId = computeResourceSlotId(
|
||||
const uint32_t bindingId = computeResourceSlotId(
|
||||
m_version.type(), DxbcBindingType::ConstantBuffer,
|
||||
bufferId);
|
||||
|
||||
@ -320,31 +414,24 @@ namespace dxvk {
|
||||
resource.slot = bindingId;
|
||||
resource.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
m_resourceSlots.push_back(resource);
|
||||
return DxbcError::sOk;
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::declareSampler(const DxbcInst& ins) {
|
||||
void DxbcCompiler::emitDclSampler(const DxbcShaderInstruction& ins) {
|
||||
// dclSampler takes one operand:
|
||||
// (1) The sampler register ID
|
||||
// (dst0) The sampler register to declare
|
||||
// TODO implement sampler mode (default / comparison / mono)
|
||||
if (ins.operands[0].indexDim != 1) {
|
||||
Logger::err("dxbc: Invalid sampler index dimension");
|
||||
return DxbcError::eInvalidOperandIndex;
|
||||
}
|
||||
|
||||
uint32_t samplerId = ins.operands[0].index[0].immediate;
|
||||
const uint32_t samplerId = ins.dst[0].idx[0].offset;
|
||||
|
||||
// The sampler type is opaque, but we still have to
|
||||
// define a pointer and a variable in oder to use it
|
||||
uint32_t samplerType = m_module.defSamplerType();
|
||||
uint32_t samplerPtrType = m_module.defPointerType(
|
||||
const uint32_t samplerType = m_module.defSamplerType();
|
||||
const uint32_t samplerPtrType = m_module.defPointerType(
|
||||
samplerType, spv::StorageClassUniformConstant);
|
||||
|
||||
// Define the sampler variable
|
||||
uint32_t varId = m_module.newVar(samplerPtrType,
|
||||
const uint32_t varId = m_module.newVar(samplerPtrType,
|
||||
spv::StorageClassUniformConstant);
|
||||
|
||||
m_module.setDebugName(varId,
|
||||
str::format("s", samplerId).c_str());
|
||||
|
||||
@ -352,7 +439,7 @@ namespace dxvk {
|
||||
m_samplers.at(samplerId).typeId = samplerType;
|
||||
|
||||
// Compute binding slot index for the sampler
|
||||
uint32_t bindingId = computeResourceSlotId(
|
||||
const uint32_t bindingId = computeResourceSlotId(
|
||||
m_version.type(), DxbcBindingType::ImageSampler, samplerId);
|
||||
|
||||
m_module.decorateDescriptorSet(varId, 0);
|
||||
@ -363,40 +450,32 @@ namespace dxvk {
|
||||
resource.slot = bindingId;
|
||||
resource.type = VK_DESCRIPTOR_TYPE_SAMPLER;
|
||||
m_resourceSlots.push_back(resource);
|
||||
return DxbcError::sOk;
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::declareResource(const DxbcInst& ins) {
|
||||
void DxbcCompiler::emitDclResource(const DxbcShaderInstruction& ins) {
|
||||
// dclResource takes two operands:
|
||||
// (1) The resource register ID
|
||||
// (2) The resource return type
|
||||
const DxbcInstOp op = ins.operands[0];
|
||||
|
||||
if (op.indexDim != 1) {
|
||||
Logger::err("dxbc: dclResource: Invalid index dimension");
|
||||
return DxbcError::eInvalidOperandIndex;
|
||||
}
|
||||
|
||||
const uint32_t registerId = op.index[0].immediate;
|
||||
// (dst0) The resource register ID
|
||||
// (imm0) The resource return type
|
||||
const uint32_t registerId = ins.dst[0].idx[0].offset;
|
||||
|
||||
// Defines the type of the resource (texture2D, ...)
|
||||
const DxbcResourceDim resourceType = ins.control.resourceDim();
|
||||
const DxbcResourceDim resourceType = ins.controls.resourceDim;
|
||||
|
||||
// Defines the type of a read operation. DXBC has the ability
|
||||
// to define four different types whereas SPIR-V only allows
|
||||
// one, but in practice this should not be much of a problem.
|
||||
auto xType = static_cast<DxbcResourceReturnType>(
|
||||
bit::extract(ins.operands[1].immediates[0], 0, 3));
|
||||
bit::extract(ins.imm[0].u32, 0, 3));
|
||||
auto yType = static_cast<DxbcResourceReturnType>(
|
||||
bit::extract(ins.operands[1].immediates[0], 4, 7));
|
||||
bit::extract(ins.imm[0].u32, 4, 7));
|
||||
auto zType = static_cast<DxbcResourceReturnType>(
|
||||
bit::extract(ins.operands[1].immediates[0], 8, 11));
|
||||
bit::extract(ins.imm[0].u32, 8, 11));
|
||||
auto wType = static_cast<DxbcResourceReturnType>(
|
||||
bit::extract(ins.operands[1].immediates[0], 12, 15));
|
||||
bit::extract(ins.imm[0].u32, 12, 15));
|
||||
|
||||
if ((xType != yType) || (xType != zType) || (xType != wType))
|
||||
Logger::warn("DXBC: dclResource: Ignoring resource return types");
|
||||
Logger::warn("DxbcCompiler: dcl_resource: Ignoring resource return types");
|
||||
|
||||
// Declare the actual sampled type
|
||||
uint32_t sampledTypeId = 0;
|
||||
@ -405,9 +484,7 @@ namespace dxvk {
|
||||
case DxbcResourceReturnType::Float: sampledTypeId = m_module.defFloatType(32); break;
|
||||
case DxbcResourceReturnType::Sint: sampledTypeId = m_module.defIntType (32, 1); break;
|
||||
case DxbcResourceReturnType::Uint: sampledTypeId = m_module.defIntType (32, 0); break;
|
||||
default:
|
||||
Logger::err(str::format("dxbc: Invalid sampled type: ", xType));
|
||||
return DxbcError::eInvalidOperand;
|
||||
default: throw DxvkError(str::format("DxbcCompiler: Invalid sampled type: ", xType));
|
||||
}
|
||||
|
||||
// Declare the resource type
|
||||
@ -457,14 +534,13 @@ namespace dxvk {
|
||||
break;
|
||||
|
||||
default:
|
||||
Logger::err(str::format("dxbc: Unsupported resource type: ", resourceType));
|
||||
return DxbcError::eUnsupported;
|
||||
throw DxvkError(str::format("DxbcCompiler: Unsupported resource type: ", resourceType));
|
||||
}
|
||||
|
||||
uint32_t resourcePtrType = m_module.defPointerType(
|
||||
const uint32_t resourcePtrType = m_module.defPointerType(
|
||||
textureTypeId, spv::StorageClassUniformConstant);
|
||||
|
||||
uint32_t varId = m_module.newVar(resourcePtrType,
|
||||
const uint32_t varId = m_module.newVar(resourcePtrType,
|
||||
spv::StorageClassUniformConstant);
|
||||
|
||||
m_module.setDebugName(varId,
|
||||
@ -476,8 +552,8 @@ namespace dxvk {
|
||||
|
||||
// Compute the DXVK binding slot index for the resource.
|
||||
// D3D11 needs to bind the actual resource to this slot.
|
||||
uint32_t bindingId = computeResourceSlotId(m_version.type(),
|
||||
DxbcBindingType::ShaderResource, registerId);
|
||||
const uint32_t bindingId = computeResourceSlotId(
|
||||
m_version.type(), DxbcBindingType::ShaderResource, registerId);
|
||||
|
||||
m_module.decorateDescriptorSet(varId, 0);
|
||||
m_module.decorateBinding(varId, bindingId);
|
||||
@ -487,257 +563,127 @@ namespace dxvk {
|
||||
resource.slot = bindingId;
|
||||
resource.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
m_resourceSlots.push_back(resource);
|
||||
return DxbcError::sOk;
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::declareInputVar(
|
||||
uint32_t regId,
|
||||
uint32_t regDim,
|
||||
DxbcRegMask regMask,
|
||||
DxbcSystemValue sv,
|
||||
DxbcInterpolationMode im) {
|
||||
if (regDim != 0) {
|
||||
Logger::err("dxbc: Input arrays not yet supported");
|
||||
return DxbcError::eUnsupported;
|
||||
}
|
||||
void DxbcCompiler::emitVectorAlu(const DxbcShaderInstruction& ins) {
|
||||
std::array<DxbcRegisterValue, DxbcMaxOperandCount> src;
|
||||
|
||||
// A variable may be declared multiple
|
||||
// times when system values are involved
|
||||
if (m_vRegs.at(regId) == 0) {
|
||||
uint32_t regTypeId = m_module.defVectorType(
|
||||
m_module.defFloatType(32), 4);
|
||||
for (uint32_t i = 0; i < ins.srcCount; i++)
|
||||
src.at(i) = emitRegisterLoad(ins.src[i], ins.dst[0].mask);
|
||||
|
||||
uint32_t ptrTypeId = m_module.defPointerType(
|
||||
regTypeId, spv::StorageClassInput);
|
||||
DxbcRegisterValue dst;
|
||||
dst.type.ctype = ins.dst[0].dataType;
|
||||
dst.type.ccount = ins.dst[0].mask.setCount();
|
||||
|
||||
uint32_t varId = m_module.newVar(
|
||||
ptrTypeId, spv::StorageClassInput);
|
||||
const uint32_t typeId = getVectorTypeId(dst.type);
|
||||
|
||||
m_module.decorateLocation(varId, regId);
|
||||
m_module.setDebugName(varId, str::format("v", regId).c_str());
|
||||
m_entryPointInterfaces.push_back(varId);
|
||||
|
||||
m_vRegs.at(regId) = varId;
|
||||
}
|
||||
|
||||
return DxbcError::sOk;
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::declareOutputVar(
|
||||
uint32_t regId,
|
||||
uint32_t regDim,
|
||||
DxbcRegMask regMask,
|
||||
DxbcSystemValue sv,
|
||||
DxbcInterpolationMode im) {
|
||||
if (regDim != 0) {
|
||||
Logger::err("dxbc: Output arrays not yet supported");
|
||||
return DxbcError::eUnsupported;
|
||||
}
|
||||
|
||||
// Fragment shader outputs were defined earlier, based
|
||||
// on their signature. We cannot add new output registers.
|
||||
if (m_version.type() == DxbcProgramType::PixelShader)
|
||||
return DxbcError::sOk;
|
||||
|
||||
// Output variables may also be defined multiple times since
|
||||
// multiple vector components may be mapped to system values.
|
||||
if (m_oRegs.at(regId) == 0) {
|
||||
uint32_t regTypeId = m_module.defVectorType(
|
||||
m_module.defFloatType(32), 4);
|
||||
|
||||
uint32_t ptrTypeId = m_module.defPointerType(
|
||||
regTypeId, spv::StorageClassOutput);
|
||||
|
||||
uint32_t varId = m_module.newVar(
|
||||
ptrTypeId, spv::StorageClassOutput);
|
||||
|
||||
m_module.decorateLocation(varId, regId);
|
||||
m_module.setDebugName(varId, str::format("o", regId).c_str());
|
||||
m_entryPointInterfaces.push_back(varId);
|
||||
|
||||
m_oRegs.at(regId) = varId;
|
||||
}
|
||||
|
||||
// Add a new system value mapping if needed
|
||||
// TODO declare SV if necessary
|
||||
if (sv != DxbcSystemValue::None)
|
||||
m_oSvs.push_back({ regId, regMask, sv });
|
||||
|
||||
return DxbcError::sOk;
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::handleControlFlow(const DxbcInst& ins) {
|
||||
switch (ins.opcode) {
|
||||
case DxbcOpcode::Ret:
|
||||
m_module.opReturn();
|
||||
m_module.functionEnd();
|
||||
return DxbcError::sOk;
|
||||
|
||||
default:
|
||||
return DxbcError::eUnhandledOpcode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::handleTextureSample(const DxbcInst& ins) {
|
||||
// TODO support address offset
|
||||
// TODO support more sample ops
|
||||
|
||||
// sample has four operands:
|
||||
// (1) The destination register
|
||||
// (2) Texture coordinates
|
||||
// (3) The texture itself
|
||||
// (4) The sampler object
|
||||
const DxbcInstOp destOp = ins.operands[0];
|
||||
const DxbcInstOp coordOp = ins.operands[1];
|
||||
const DxbcInstOp textureOp = ins.operands[2];
|
||||
const DxbcInstOp samplerOp = ins.operands[3];
|
||||
|
||||
if (textureOp.indexDim != 1 || samplerOp.indexDim != 1) {
|
||||
Logger::err("dxbc: Texture and Sampler registers require one index");
|
||||
return DxbcError::eInvalidOperandIndex;
|
||||
}
|
||||
|
||||
// Texture and sampler register IDs
|
||||
const uint32_t textureId = textureOp.index[0].immediate;
|
||||
const uint32_t samplerId = samplerOp.index[0].immediate;
|
||||
|
||||
// Load the texture coordinates. SPIR-V allows these
|
||||
// to be float4 even if not all components are used.
|
||||
const DxbcValue coord = this->loadOp(coordOp,
|
||||
DxbcRegMask(true, true, true, true),
|
||||
DxbcScalarType::Float32);
|
||||
|
||||
// Combine the texture and the sampler into a sampled image
|
||||
uint32_t sampledImageType = m_module.defSampledImageType(
|
||||
m_textures.at(textureId).textureTypeId);
|
||||
|
||||
uint32_t sampledImageId = m_module.opSampledImage(
|
||||
sampledImageType,
|
||||
m_module.opLoad(
|
||||
m_textures.at(textureId).textureTypeId,
|
||||
m_textures.at(textureId).varId),
|
||||
m_module.opLoad(
|
||||
m_samplers.at(samplerId).typeId,
|
||||
m_samplers.at(samplerId).varId));
|
||||
|
||||
// Sampling an image in SPIR-V always returns a four-component
|
||||
// vector, so we need to declare the corresponding type here
|
||||
// TODO infer sampled type properly
|
||||
DxbcValue result;
|
||||
result.componentType = DxbcScalarType::Float32;
|
||||
result.componentCount = 4;
|
||||
result.valueId = m_module.opImageSampleImplicitLod(
|
||||
this->defineVectorType(result.componentType, result.componentCount),
|
||||
sampledImageId, coord.valueId);
|
||||
|
||||
// Swizzle components using the texture swizzle
|
||||
// and the destination operand's write mask
|
||||
result = this->swizzleReg(result, textureOp.swizzle, destOp.mask);
|
||||
|
||||
this->storeOp(destOp, result);
|
||||
return DxbcError::sOk;
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::handleVectorAlu(const DxbcInst& ins) {
|
||||
// Load input operands. Operands that are floating
|
||||
// point types will be affected by modifiers.
|
||||
DxbcValue arguments[DxbcMaxOperandCount - 1];
|
||||
|
||||
for (uint32_t i = 1; i < ins.format.operandCount; i++) {
|
||||
arguments[i - 1] = this->loadOp(
|
||||
ins.operands[i],
|
||||
ins.operands[0].mask,
|
||||
ins.format.operands[i].type);
|
||||
}
|
||||
|
||||
// Result that we will write to the destination operand
|
||||
DxbcValue result;
|
||||
result.componentType = arguments[0].componentType;
|
||||
result.componentCount = arguments[0].componentCount;
|
||||
|
||||
uint32_t resultTypeId = this->defineVectorType(
|
||||
result.componentType, result.componentCount);
|
||||
|
||||
switch (ins.opcode) {
|
||||
switch (ins.op) {
|
||||
case DxbcOpcode::Add:
|
||||
result.valueId = m_module.opFAdd(
|
||||
resultTypeId,
|
||||
arguments[0].valueId,
|
||||
arguments[1].valueId);
|
||||
dst.id = m_module.opFAdd(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Div:
|
||||
result.valueId = m_module.opFDiv(
|
||||
resultTypeId,
|
||||
arguments[0].valueId,
|
||||
arguments[1].valueId);
|
||||
dst.id = m_module.opFDiv(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Exp:
|
||||
dst.id = m_module.opExp2(
|
||||
typeId, src.at(0).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Log:
|
||||
dst.id = m_module.opLog2(
|
||||
typeId, src.at(0).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Mad:
|
||||
result.valueId = m_module.opFFma(
|
||||
resultTypeId,
|
||||
arguments[0].valueId,
|
||||
arguments[1].valueId,
|
||||
arguments[2].valueId);
|
||||
dst.id = m_module.opFFma(typeId,
|
||||
src.at(0).id, src.at(1).id, src.at(2).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Max:
|
||||
result.valueId = m_module.opFMax(
|
||||
resultTypeId,
|
||||
arguments[0].valueId,
|
||||
arguments[1].valueId);
|
||||
dst.id = m_module.opFMax(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Min:
|
||||
result.valueId = m_module.opFMin(
|
||||
resultTypeId,
|
||||
arguments[0].valueId,
|
||||
arguments[1].valueId);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Mov:
|
||||
result.valueId = arguments[0].valueId;
|
||||
dst.id = m_module.opFMin(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Mul:
|
||||
result.valueId = m_module.opFMul(
|
||||
resultTypeId,
|
||||
arguments[0].valueId,
|
||||
arguments[1].valueId);
|
||||
dst.id = m_module.opFMul(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Mov:
|
||||
dst.id = src.at(0).id;
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Sqrt:
|
||||
dst.id = m_module.opSqrt(
|
||||
typeId, src.at(0).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Rsq:
|
||||
result.valueId = m_module.opInverseSqrt(
|
||||
resultTypeId, arguments[0].valueId);
|
||||
dst.id = m_module.opInverseSqrt(
|
||||
typeId, src.at(0).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::IAdd:
|
||||
dst.id = m_module.opIAdd(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::IMad:
|
||||
dst.id = m_module.opIAdd(typeId,
|
||||
m_module.opIMul(typeId,
|
||||
src.at(0).id, src.at(1).id),
|
||||
src.at(2).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::IMax:
|
||||
dst.id = m_module.opSMax(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::IMin:
|
||||
dst.id = m_module.opSMin(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::INeg:
|
||||
dst.id = m_module.opSNegate(
|
||||
typeId, src.at(0).id);
|
||||
break;
|
||||
|
||||
default:
|
||||
return DxbcError::eUnhandledOpcode;
|
||||
Logger::warn(str::format(
|
||||
"DxbcCompiler: Unhandled instruction: ",
|
||||
ins.op));
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply result modifiers to floating-point results
|
||||
result = this->applyResultModifiers(result, ins.control);
|
||||
this->storeOp(ins.operands[0], result);
|
||||
return DxbcError::sOk;
|
||||
// Store computed value
|
||||
dst = emitDstOperandModifiers(dst, ins.modifiers);
|
||||
emitRegisterStore(ins.dst[0], dst);
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::handleVectorCmov(const DxbcInst& ins) {
|
||||
void DxbcCompiler::emitVectorCmov(const DxbcShaderInstruction& ins) {
|
||||
// movc has four operands:
|
||||
// (1) The destination register
|
||||
// (2) The condition vector
|
||||
// (3) Vector to select from if the condition is not 0
|
||||
// (4) Vector to select from if the condition is 0
|
||||
const DxbcValue condition = this->loadOp(ins.operands[1], ins.operands[0].mask, ins.format.operands[1].type);
|
||||
const DxbcValue selectTrue = this->loadOp(ins.operands[2], ins.operands[0].mask, ins.format.operands[2].type);
|
||||
const DxbcValue selectFalse = this->loadOp(ins.operands[3], ins.operands[0].mask, ins.format.operands[3].type);
|
||||
// (dst0) The destination register
|
||||
// (src0) The condition vector
|
||||
// (src0) Vector to select from if the condition is not 0
|
||||
// (src0) Vector to select from if the condition is 0
|
||||
const DxbcRegisterValue condition = emitRegisterLoad(ins.src[0], ins.dst[0].mask);
|
||||
const DxbcRegisterValue selectTrue = emitRegisterLoad(ins.src[1], ins.dst[0].mask);
|
||||
const DxbcRegisterValue selectFalse = emitRegisterLoad(ins.src[2], ins.dst[0].mask);
|
||||
|
||||
const uint32_t componentCount = ins.operands[0].mask.setCount();
|
||||
const uint32_t componentCount = ins.dst[0].mask.setCount();
|
||||
|
||||
// We'll compare against a vector of zeroes to generate a
|
||||
// boolean vector, which in turn will be used by OpSelect
|
||||
@ -754,40 +700,32 @@ namespace dxvk {
|
||||
zero = m_module.constComposite(zeroType, componentCount, zeroVec.data());
|
||||
}
|
||||
|
||||
|
||||
// Use the component mask to select the vector components
|
||||
DxbcValue result;
|
||||
result.componentType = ins.format.operands[0].type;
|
||||
result.componentCount = componentCount;
|
||||
result.valueId = m_module.opSelect(
|
||||
this->defineVectorType(result.componentType, result.componentCount),
|
||||
m_module.opINotEqual(boolType, condition.valueId, zero),
|
||||
selectTrue.valueId, selectFalse.valueId);
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = ins.dst[0].dataType;
|
||||
result.type.ccount = componentCount;
|
||||
result.id = m_module.opSelect(
|
||||
getVectorTypeId(result.type),
|
||||
m_module.opINotEqual(boolType, condition.id, zero),
|
||||
selectTrue.id, selectFalse.id);
|
||||
|
||||
// Apply result modifiers to floating-point results
|
||||
result = this->applyResultModifiers(result, ins.control);
|
||||
this->storeOp(ins.operands[0], result);
|
||||
return DxbcError::sOk;
|
||||
result = emitDstOperandModifiers(result, ins.modifiers);
|
||||
emitRegisterStore(ins.dst[0], result);
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::handleVectorCmp(const DxbcInst& ins) {
|
||||
void DxbcCompiler::emitVectorCmp(const DxbcShaderInstruction& ins) {
|
||||
// Compare instructions have three operands:
|
||||
// (1) The destination register
|
||||
// (2) The first vector to compare
|
||||
// (3) The second vector to compare
|
||||
DxbcValue arguments[2];
|
||||
// (dst0) The destination register
|
||||
// (src0) The first vector to compare
|
||||
// (src1) The second vector to compare
|
||||
const std::array<DxbcRegisterValue, 2> src = {
|
||||
emitRegisterLoad(ins.src[0], ins.dst[0].mask),
|
||||
emitRegisterLoad(ins.src[1], ins.dst[0].mask),
|
||||
};
|
||||
|
||||
if (ins.format.operandCount != 3)
|
||||
return DxbcError::eInvalidOperand;
|
||||
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
arguments[i] = this->loadOp(
|
||||
ins.operands[i + 1],
|
||||
ins.operands[0].mask,
|
||||
ins.format.operands[i + 1].type);
|
||||
}
|
||||
|
||||
const uint32_t componentCount = ins.operands[0].mask.setCount();
|
||||
const uint32_t componentCount = ins.dst[0].mask.setCount();
|
||||
|
||||
// Condition, which is a boolean vector used
|
||||
// to select between the ~0u and 0u vectors.
|
||||
@ -797,268 +735,295 @@ namespace dxvk {
|
||||
if (componentCount > 1)
|
||||
conditionType = m_module.defVectorType(conditionType, componentCount);
|
||||
|
||||
switch (ins.opcode) {
|
||||
switch (ins.op) {
|
||||
case DxbcOpcode::Eq:
|
||||
condition = m_module.opFOrdEqual(
|
||||
conditionType,
|
||||
arguments[0].valueId,
|
||||
arguments[1].valueId);
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Ge:
|
||||
condition = m_module.opFOrdGreaterThanEqual(
|
||||
conditionType,
|
||||
arguments[0].valueId,
|
||||
arguments[1].valueId);
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Lt:
|
||||
condition = m_module.opFOrdLessThan(
|
||||
conditionType,
|
||||
arguments[0].valueId,
|
||||
arguments[1].valueId);
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Ne:
|
||||
condition = m_module.opFOrdNotEqual(
|
||||
conditionType,
|
||||
arguments[0].valueId,
|
||||
arguments[1].valueId);
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::IEq:
|
||||
condition = m_module.opIEqual(
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::IGe:
|
||||
condition = m_module.opSGreaterThanEqual(
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::ILt:
|
||||
condition = m_module.opSLessThan(
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::INe:
|
||||
condition = m_module.opINotEqual(
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
default:
|
||||
return DxbcError::eUnhandledOpcode;
|
||||
Logger::warn(str::format(
|
||||
"DxbcCompiler: Unhandled instruction: ",
|
||||
ins.op));
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate constant vectors for selection
|
||||
uint32_t sFalse = m_module.constu32( 0u);
|
||||
uint32_t sTrue = m_module.constu32(~0u);
|
||||
|
||||
const uint32_t maskType = this->defineVectorType(
|
||||
DxbcScalarType::Uint32, componentCount);
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = DxbcScalarType::Uint32;
|
||||
result.type.ccount = componentCount;
|
||||
|
||||
const uint32_t typeId = getVectorTypeId(result.type);
|
||||
|
||||
if (componentCount > 1) {
|
||||
const std::array<uint32_t, 4> vFalse = { sFalse, sFalse, sFalse, sFalse };
|
||||
const std::array<uint32_t, 4> vTrue = { sTrue, sTrue, sTrue, sTrue };
|
||||
|
||||
sFalse = m_module.constComposite(maskType, componentCount, vFalse.data());
|
||||
sTrue = m_module.constComposite(maskType, componentCount, vTrue .data());
|
||||
sFalse = m_module.constComposite(typeId, componentCount, vFalse.data());
|
||||
sTrue = m_module.constComposite(typeId, componentCount, vTrue .data());
|
||||
}
|
||||
|
||||
// Perform component-wise mask selection
|
||||
// based on the condition evaluated above.
|
||||
DxbcValue result;
|
||||
result.componentType = DxbcScalarType::Uint32;
|
||||
result.componentCount = componentCount;
|
||||
result.valueId = m_module.opSelect(
|
||||
maskType, condition, sTrue, sFalse);
|
||||
result.id = m_module.opSelect(
|
||||
typeId, condition, sTrue, sFalse);
|
||||
|
||||
this->storeOp(ins.operands[0], result);
|
||||
return DxbcError::sOk;
|
||||
emitRegisterStore(ins.dst[0], result);
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::handleVectorDot(const DxbcInst& ins) {
|
||||
// Determine the component count and the source
|
||||
// operand mask. Since the result is scalar, we
|
||||
// cannot use the destination register mask.
|
||||
uint32_t numComponents = 0;
|
||||
void DxbcCompiler::emitVectorDot(const DxbcShaderInstruction& ins) {
|
||||
const DxbcRegMask srcMask(true,
|
||||
ins.op >= DxbcOpcode::Dp2,
|
||||
ins.op >= DxbcOpcode::Dp3,
|
||||
ins.op >= DxbcOpcode::Dp4);
|
||||
|
||||
switch (ins.opcode) {
|
||||
case DxbcOpcode::Dp2: numComponents = 2; break;
|
||||
case DxbcOpcode::Dp3: numComponents = 3; break;
|
||||
case DxbcOpcode::Dp4: numComponents = 4; break;
|
||||
default: return DxbcError::eUnhandledOpcode;
|
||||
}
|
||||
const std::array<DxbcRegisterValue, 2> src = {
|
||||
emitRegisterLoad(ins.src[0], srcMask),
|
||||
emitRegisterLoad(ins.src[1], srcMask),
|
||||
};
|
||||
|
||||
// We'll use xyz for dp3, xy for dp2
|
||||
const DxbcRegMask srcMask(
|
||||
numComponents >= 1, numComponents >= 2,
|
||||
numComponents >= 3, numComponents >= 4);
|
||||
DxbcRegisterValue dst;
|
||||
dst.type.ctype = ins.dst[0].dataType;
|
||||
dst.type.ccount = 1;
|
||||
|
||||
// Load input operands as floatig point numbers
|
||||
DxbcValue arguments[2];
|
||||
dst.id = m_module.opDot(
|
||||
getVectorTypeId(dst.type),
|
||||
src.at(0).id,
|
||||
src.at(1).id);
|
||||
|
||||
for (uint32_t i = 1; i <= 2; i++) {
|
||||
arguments[i - 1] = this->loadOp(
|
||||
ins.operands[i], srcMask,
|
||||
DxbcScalarType::Float32);
|
||||
}
|
||||
|
||||
DxbcValue result;
|
||||
result.componentType = DxbcScalarType::Float32;
|
||||
result.componentCount = 1;
|
||||
result.valueId = m_module.opDot(
|
||||
this->defineVectorType(
|
||||
result.componentType,
|
||||
result.componentCount),
|
||||
arguments[0].valueId,
|
||||
arguments[1].valueId);
|
||||
|
||||
// Apply result modifiers to floating-point results
|
||||
result = this->applyResultModifiers(result, ins.control);
|
||||
this->storeOp(ins.operands[0], result);
|
||||
return DxbcError::sOk;
|
||||
dst = emitDstOperandModifiers(dst, ins.modifiers);
|
||||
emitRegisterStore(ins.dst[0], dst);
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::handleVectorSinCos(const DxbcInst& ins) {
|
||||
void DxbcCompiler::emitVectorImul(const DxbcShaderInstruction& ins) {
|
||||
// imul and umul have four operands:
|
||||
// (dst0) High destination register
|
||||
// (dst1) Low destination register
|
||||
// (src0) The first vector to compare
|
||||
// (src1) The second vector to compare
|
||||
if (ins.dst[0].type == DxbcOperandType::Null) {
|
||||
if (ins.dst[1].type == DxbcOperandType::Null)
|
||||
return;
|
||||
|
||||
// If dst0 is NULL, this instruction behaves just
|
||||
// like any other three -operand ALU instruction
|
||||
const std::array<DxbcRegisterValue, 2> src = {
|
||||
emitRegisterLoad(ins.src[0], ins.dst[1].mask),
|
||||
emitRegisterLoad(ins.src[1], ins.dst[1].mask),
|
||||
};
|
||||
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = ins.dst[1].dataType;
|
||||
result.type.ccount = ins.dst[1].mask.setCount();
|
||||
result.id = m_module.opIMul(
|
||||
getVectorTypeId(result.type),
|
||||
src.at(0).id, src.at(1).id);
|
||||
|
||||
result = emitDstOperandModifiers(result, ins.modifiers);
|
||||
emitRegisterStore(ins.dst[1], result);
|
||||
} else {
|
||||
// TODO implement this
|
||||
Logger::warn("DxbcCompiler: Extended Imul not yet supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::emitVectorSinCos(const DxbcShaderInstruction& ins) {
|
||||
// sincos has three operands:
|
||||
// (1) Destination register for sin(x)
|
||||
// (2) Destination register for cos(x)
|
||||
// (3) Source operand x
|
||||
const DxbcInstOp dstSinOp = ins.operands[0];
|
||||
const DxbcInstOp dstCosOp = ins.operands[1];
|
||||
const DxbcInstOp srcOp = ins.operands[2];
|
||||
// (dst0) Destination register for sin(x)
|
||||
// (dst1) Destination register for cos(x)
|
||||
// (src0) Source operand x
|
||||
|
||||
// Load source operand as 32-bit float vector
|
||||
const DxbcValue srcValue = this->loadOp(srcOp,
|
||||
DxbcRegMask(true, true, true, true),
|
||||
DxbcScalarType::Float32);
|
||||
// Load source operand as 32-bit float vector.
|
||||
const DxbcRegisterValue srcValue = emitRegisterLoad(
|
||||
ins.src[0], DxbcRegMask(true, true, true, true));
|
||||
|
||||
// Either output may be DxbcOperandType::Null, in
|
||||
// which case we don't have to generate any code
|
||||
if (dstSinOp.type != DxbcOperandType::Null) {
|
||||
const DxbcValue sinInput = this->extractReg(
|
||||
srcValue, dstSinOp.mask);
|
||||
// which case we don't have to generate any code.
|
||||
if (ins.dst[0].type != DxbcOperandType::Null) {
|
||||
const DxbcRegisterValue sinInput =
|
||||
emitRegisterExtract(srcValue, ins.dst[0].mask);
|
||||
|
||||
DxbcValue sinValue;
|
||||
sinValue.componentType = srcValue.componentType;
|
||||
sinValue.componentCount = srcValue.componentCount;
|
||||
sinValue.valueId = m_module.opSin(
|
||||
this->defineVectorType(
|
||||
sinInput.componentType,
|
||||
sinInput.componentCount),
|
||||
sinInput.valueId);
|
||||
DxbcRegisterValue sin;
|
||||
sin.type = sinInput.type;
|
||||
sin.id = m_module.opSin(
|
||||
getVectorTypeId(sin.type),
|
||||
sinInput.id);
|
||||
|
||||
this->storeOp(dstSinOp, sinValue);
|
||||
emitRegisterStore(ins.dst[0], sin);
|
||||
}
|
||||
|
||||
if (dstCosOp.type != DxbcOperandType::Null) {
|
||||
const DxbcValue cosInput = this->extractReg(
|
||||
srcValue, dstCosOp.mask);
|
||||
if (ins.dst[1].type != DxbcOperandType::Null) {
|
||||
const DxbcRegisterValue cosInput =
|
||||
emitRegisterExtract(srcValue, ins.dst[1].mask);
|
||||
|
||||
DxbcValue cosValue;
|
||||
cosValue.componentType = srcValue.componentType;
|
||||
cosValue.componentCount = srcValue.componentCount;
|
||||
cosValue.valueId = m_module.opCos(
|
||||
this->defineVectorType(
|
||||
cosInput.componentType,
|
||||
cosInput.componentCount),
|
||||
cosInput.valueId);
|
||||
DxbcRegisterValue cos;
|
||||
cos.type = cosInput.type;
|
||||
cos.id = m_module.opSin(
|
||||
getVectorTypeId(cos.type),
|
||||
cosInput.id);
|
||||
|
||||
this->storeOp(dstCosOp, cosValue);
|
||||
emitRegisterStore(ins.dst[1], cos);
|
||||
}
|
||||
|
||||
return DxbcError::sOk;
|
||||
}
|
||||
|
||||
|
||||
DxbcValue DxbcCompiler::bitcastReg(
|
||||
const DxbcValue& src,
|
||||
DxbcScalarType type) {
|
||||
if (src.componentType == type)
|
||||
return src;
|
||||
void DxbcCompiler::emitSample(
|
||||
const DxbcShaderInstruction& ins) {
|
||||
// TODO support address offset
|
||||
// TODO support more sample ops
|
||||
|
||||
// TODO support 64-bit types by adjusting the component count
|
||||
uint32_t typeId = this->defineVectorType(type, src.componentCount);
|
||||
// sample has four operands:
|
||||
// (dst0) The destination register
|
||||
// (src0) Texture coordinates
|
||||
// (src1) The texture itself
|
||||
// (src2) The sampler object
|
||||
const DxbcRegister& texCoordReg = ins.src[0];
|
||||
const DxbcRegister& textureReg = ins.src[1];
|
||||
const DxbcRegister& samplerReg = ins.src[2];
|
||||
|
||||
DxbcValue result;
|
||||
result.componentType = type;
|
||||
result.componentCount = src.componentCount;
|
||||
result.valueId = m_module.opBitcast(typeId, src.valueId);
|
||||
// Texture and sampler register IDs
|
||||
const uint32_t textureId = textureReg.idx[0].offset;
|
||||
const uint32_t samplerId = samplerReg.idx[0].offset;
|
||||
|
||||
// Load the texture coordinates. SPIR-V allows these
|
||||
// to be float4 even if not all components are used.
|
||||
const DxbcRegisterValue coord = emitRegisterLoad(
|
||||
texCoordReg, DxbcRegMask(true, true, true, true));
|
||||
|
||||
// Combine the texture and the sampler into a sampled image
|
||||
const uint32_t sampledImageType = m_module.defSampledImageType(
|
||||
m_textures.at(textureId).textureTypeId);
|
||||
|
||||
const uint32_t sampledImageId = m_module.opSampledImage(
|
||||
sampledImageType,
|
||||
m_module.opLoad(
|
||||
m_textures.at(textureId).textureTypeId,
|
||||
m_textures.at(textureId).varId),
|
||||
m_module.opLoad(
|
||||
m_samplers.at(samplerId).typeId,
|
||||
m_samplers.at(samplerId).varId));
|
||||
|
||||
// Sampling an image in SPIR-V always returns a four-component
|
||||
// vector, so we need to declare the corresponding type here
|
||||
// TODO infer sampled type properly
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = DxbcScalarType::Float32;
|
||||
result.type.ccount = 4;
|
||||
result.id = m_module.opImageSampleImplicitLod(
|
||||
getVectorTypeId(result.type),
|
||||
sampledImageId, coord.id);
|
||||
|
||||
// Swizzle components using the texture swizzle
|
||||
// and the destination operand's write mask
|
||||
result = emitRegisterSwizzle(result,
|
||||
textureReg.swizzle, ins.dst[0].mask);
|
||||
|
||||
emitRegisterStore(ins.dst[0], result);
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::emitRet(const DxbcShaderInstruction& ins) {
|
||||
// TODO implement properly
|
||||
m_module.opReturn();
|
||||
m_module.functionEnd();
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler::emitRegisterBitcast(
|
||||
DxbcRegisterValue srcValue,
|
||||
DxbcScalarType dstType) {
|
||||
if (srcValue.type.ctype == dstType)
|
||||
return srcValue;
|
||||
|
||||
// TODO support 64-bit values
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = dstType;
|
||||
result.type.ccount = srcValue.type.ccount;
|
||||
result.id = m_module.opBitcast(
|
||||
getVectorTypeId(result.type),
|
||||
srcValue.id);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcValue DxbcCompiler::insertReg(
|
||||
const DxbcValue& dst,
|
||||
const DxbcValue& src,
|
||||
DxbcRegMask mask) {
|
||||
DxbcValue result;
|
||||
result.componentType = dst.componentType;
|
||||
result.componentCount = dst.componentCount;
|
||||
|
||||
const uint32_t resultTypeId = this->defineVectorType(
|
||||
result.componentType, result.componentCount);
|
||||
|
||||
if (dst.componentCount == 1) {
|
||||
// Both values are scalar, so the first component
|
||||
// of the write mask decides which one to take.
|
||||
result.valueId = mask[0] ? src.valueId : dst.valueId;
|
||||
} else if (src.componentCount == 1) {
|
||||
// The source value is scalar. Since OpVectorShuffle
|
||||
// requires both arguments to be vectors, we have to
|
||||
// use OpCompositeInsert to modify the vector instead.
|
||||
const uint32_t componentId = mask.firstSet();
|
||||
|
||||
result.valueId = m_module.opCompositeInsert(
|
||||
resultTypeId, src.valueId, dst.valueId,
|
||||
1, &componentId);
|
||||
} else {
|
||||
// Both arguments are vectors. We can determine which
|
||||
// components to take from which vector and use the
|
||||
// OpVectorShuffle instruction.
|
||||
uint32_t components[4];
|
||||
uint32_t srcComponentId = dst.componentCount;
|
||||
|
||||
for (uint32_t i = 0; i < dst.componentCount; i++)
|
||||
components[i] = mask[i] ? srcComponentId++ : i;
|
||||
|
||||
result.valueId = m_module.opVectorShuffle(
|
||||
resultTypeId, dst.valueId, src.valueId,
|
||||
dst.componentCount, components);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcValue DxbcCompiler::extractReg(
|
||||
const DxbcValue& src,
|
||||
DxbcRegMask mask) {
|
||||
return this->swizzleReg(src,
|
||||
DxbcRegSwizzle(0, 1, 2, 3), mask);
|
||||
}
|
||||
|
||||
|
||||
DxbcValue DxbcCompiler::swizzleReg(
|
||||
const DxbcValue& src,
|
||||
const DxbcRegSwizzle& swizzle,
|
||||
DxbcRegMask mask) {
|
||||
DxbcRegisterValue DxbcCompiler::emitRegisterSwizzle(
|
||||
DxbcRegisterValue value,
|
||||
DxbcRegSwizzle swizzle,
|
||||
DxbcRegMask writeMask) {
|
||||
std::array<uint32_t, 4> indices;
|
||||
|
||||
uint32_t dstIndex = 0;
|
||||
for (uint32_t i = 0; i < src.componentCount; i++) {
|
||||
if (mask[i])
|
||||
|
||||
for (uint32_t i = 0; i < value.type.ccount; i++) {
|
||||
if (writeMask[i])
|
||||
indices[dstIndex++] = swizzle[i];
|
||||
}
|
||||
|
||||
// If the swizzle combined with the mask can be reduced
|
||||
// to a no-op, we don't need to insert any instructions.
|
||||
bool isIdentitySwizzle = dstIndex == src.componentCount;
|
||||
bool isIdentitySwizzle = dstIndex == value.type.ccount;
|
||||
|
||||
for (uint32_t i = 0; i < dstIndex && isIdentitySwizzle; i++)
|
||||
isIdentitySwizzle &= indices[i] == i;
|
||||
|
||||
if (isIdentitySwizzle)
|
||||
return src;
|
||||
return value;
|
||||
|
||||
// Use OpCompositeExtract if the resulting vector contains
|
||||
// only one component, and OpVectorShuffle if it is a vector.
|
||||
DxbcValue result;
|
||||
result.componentType = src.componentType;
|
||||
result.componentCount = dstIndex;
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = value.type.ctype;
|
||||
result.type.ccount = dstIndex;
|
||||
|
||||
const uint32_t resultTypeId = this->defineVectorType(
|
||||
result.componentType, result.componentCount);
|
||||
const uint32_t typeId = getVectorTypeId(result.type);
|
||||
|
||||
if (dstIndex == 1) {
|
||||
result.valueId = m_module.opCompositeExtract(
|
||||
resultTypeId, src.valueId, 1, indices.data());
|
||||
result.id = m_module.opCompositeExtract(
|
||||
typeId, value.id, 1, indices.data());
|
||||
} else {
|
||||
result.valueId = m_module.opVectorShuffle(
|
||||
resultTypeId, src.valueId, src.valueId,
|
||||
result.id = m_module.opVectorShuffle(
|
||||
typeId, value.id, value.id,
|
||||
dstIndex, indices.data());
|
||||
}
|
||||
|
||||
@ -1066,334 +1031,435 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
DxbcValue DxbcCompiler::extendReg(
|
||||
const DxbcValue& src,
|
||||
uint32_t size) {
|
||||
if (size == 1)
|
||||
return src;
|
||||
DxbcRegisterValue DxbcCompiler::emitRegisterExtract(
|
||||
DxbcRegisterValue value,
|
||||
DxbcRegMask mask) {
|
||||
return emitRegisterSwizzle(value,
|
||||
DxbcRegSwizzle(0, 1, 2, 3), mask);
|
||||
}
|
||||
|
||||
std::array<uint32_t, 4> ids = {
|
||||
src.valueId, src.valueId,
|
||||
src.valueId, src.valueId,
|
||||
};
|
||||
|
||||
uint32_t typeId = this->defineVectorType(
|
||||
src.componentType, size);
|
||||
DxbcRegisterValue DxbcCompiler::emitRegisterInsert(
|
||||
DxbcRegisterValue dstValue,
|
||||
DxbcRegisterValue srcValue,
|
||||
DxbcRegMask srcMask) {
|
||||
DxbcRegisterValue result;
|
||||
result.type = dstValue.type;
|
||||
|
||||
const uint32_t typeId = getVectorTypeId(result.type);
|
||||
|
||||
if (srcMask.setCount() == 0) {
|
||||
// Nothing to do if the insertion mask is empty
|
||||
result.id = dstValue.id;
|
||||
} else if (dstValue.type.ccount == 1) {
|
||||
// Both values are scalar, so the first component
|
||||
// of the write mask decides which one to take.
|
||||
result.id = srcMask[0] ? srcValue.id : dstValue.id;
|
||||
} else if (srcValue.type.ccount == 1) {
|
||||
// The source value is scalar. Since OpVectorShuffle
|
||||
// requires both arguments to be vectors, we have to
|
||||
// use OpCompositeInsert to modify the vector instead.
|
||||
const uint32_t componentId = srcMask.firstSet();
|
||||
|
||||
result.id = m_module.opCompositeInsert(typeId,
|
||||
srcValue.id, dstValue.id, 1, &componentId);
|
||||
} else {
|
||||
// Both arguments are vectors. We can determine which
|
||||
// components to take from which vector and use the
|
||||
// OpVectorShuffle instruction.
|
||||
std::array<uint32_t, 4> components;
|
||||
uint32_t srcComponentId = dstValue.type.ccount;
|
||||
|
||||
for (uint32_t i = 0; i < dstValue.type.ccount; i++)
|
||||
components.at(i) = srcMask[i] ? srcComponentId++ : i;
|
||||
|
||||
result.id = m_module.opVectorShuffle(
|
||||
typeId, dstValue.id, srcValue.id,
|
||||
dstValue.type.ccount, components.data());
|
||||
}
|
||||
|
||||
DxbcValue result;
|
||||
result.componentType = src.componentType;
|
||||
result.componentCount = size;
|
||||
result.valueId = m_module.opCompositeConstruct(
|
||||
typeId, size, ids.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcValue DxbcCompiler::applyOperandModifiers(
|
||||
DxbcValue value,
|
||||
DxbcOperandModifiers modifiers) {
|
||||
uint32_t typeId = this->defineVectorType(
|
||||
value.componentType, value.componentCount);
|
||||
DxbcRegisterValue DxbcCompiler::emitRegisterExtend(
|
||||
DxbcRegisterValue value,
|
||||
uint32_t size) {
|
||||
if (size == 1)
|
||||
return value;
|
||||
|
||||
// Both modifiers can be applied to the same value.
|
||||
// In that case, the absolute value is negated.
|
||||
if (modifiers.test(DxbcOperandModifier::Abs))
|
||||
value.valueId = m_module.opFAbs(typeId, value.valueId);
|
||||
std::array<uint32_t, 4> ids = {
|
||||
value.id, value.id,
|
||||
value.id, value.id,
|
||||
};
|
||||
|
||||
if (modifiers.test(DxbcOperandModifier::Neg))
|
||||
value.valueId = m_module.opFNegate(typeId, value.valueId);
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = value.type.ctype;
|
||||
result.type.ccount = size;
|
||||
result.id = m_module.opCompositeConstruct(
|
||||
getVectorTypeId(result.type),
|
||||
size, ids.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler::emitRegisterAbsolute(
|
||||
DxbcRegisterValue value) {
|
||||
const uint32_t typeId = getVectorTypeId(value.type);
|
||||
|
||||
switch (value.type.ctype) {
|
||||
case DxbcScalarType::Float32: value.id = m_module.opFAbs(typeId, value.id); break;
|
||||
case DxbcScalarType::Sint32: value.id = m_module.opSAbs(typeId, value.id); break;
|
||||
default: Logger::warn("DxbcCompiler: Cannot get absolute value for given type");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
DxbcValue DxbcCompiler::applyResultModifiers(
|
||||
DxbcValue value,
|
||||
DxbcOpcodeControl control) {
|
||||
uint32_t typeId = this->defineVectorType(
|
||||
value.componentType, value.componentCount);
|
||||
DxbcRegisterValue DxbcCompiler::emitRegisterNegate(
|
||||
DxbcRegisterValue value) {
|
||||
const uint32_t typeId = getVectorTypeId(value.type);
|
||||
|
||||
if (control.saturateBit()) {
|
||||
value.valueId = m_module.opFClamp(
|
||||
typeId, value.valueId,
|
||||
switch (value.type.ctype) {
|
||||
case DxbcScalarType::Float32: value.id = m_module.opFNegate(typeId, value.id); break;
|
||||
case DxbcScalarType::Sint32: value.id = m_module.opSNegate(typeId, value.id); break;
|
||||
default: Logger::warn("DxbcCompiler: Cannot negate given type");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler::emitSrcOperandModifiers(
|
||||
DxbcRegisterValue value,
|
||||
DxbcRegModifiers modifiers) {
|
||||
if (modifiers.test(DxbcRegModifier::Abs))
|
||||
value = emitRegisterAbsolute(value);
|
||||
|
||||
if (modifiers.test(DxbcRegModifier::Neg))
|
||||
value = emitRegisterNegate(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler::emitDstOperandModifiers(
|
||||
DxbcRegisterValue value,
|
||||
DxbcOpModifiers modifiers) {
|
||||
const uint32_t typeId = getVectorTypeId(value.type);
|
||||
|
||||
if (value.type.ctype == DxbcScalarType::Float32) {
|
||||
// Saturating only makes sense on floats
|
||||
if (modifiers.saturate) {
|
||||
value.id = m_module.opFClamp(
|
||||
typeId, value.id,
|
||||
m_module.constf32(0.0f),
|
||||
m_module.constf32(1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
DxbcValue DxbcCompiler::loadOp(
|
||||
const DxbcInstOp& srcOp,
|
||||
DxbcRegMask srcMask,
|
||||
DxbcScalarType dstType) {
|
||||
if (srcOp.type == DxbcOperandType::Imm32) {
|
||||
return this->loadImm32(srcOp, srcMask, dstType);
|
||||
DxbcRegisterPointer DxbcCompiler::emitGetTempPtr(
|
||||
const DxbcRegister& operand) {
|
||||
// r# regs are indexed as follows:
|
||||
// (0) register index (immediate)
|
||||
DxbcRegisterPointer result;
|
||||
result.type.ctype = DxbcScalarType::Float32;
|
||||
result.type.ccount = 4;
|
||||
result.id = m_rRegs.at(operand.idx[0].offset);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterPointer DxbcCompiler::emitGetInputPtr(
|
||||
const DxbcRegister& operand) {
|
||||
// In the vertex and pixel stages,
|
||||
// v# regs are indexed as follows:
|
||||
// (0) register index (relative)
|
||||
//
|
||||
// In the tessellation and geometry
|
||||
// stages, the index has two dimensions:
|
||||
// (0) vertex index (relative)
|
||||
// (1) register index (relative)
|
||||
if (operand.idxDim != 1)
|
||||
throw DxvkError("DxbcCompiler: 2D index for v# not yet supported");
|
||||
|
||||
// We don't support two-dimensional indices yet
|
||||
const uint32_t registerId = operand.idx[0].offset;
|
||||
|
||||
DxbcRegisterPointer result;
|
||||
result.type.ctype = DxbcScalarType::Float32;
|
||||
result.type.ccount = 4;
|
||||
result.id = m_vRegs.at(registerId);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterPointer DxbcCompiler::emitGetOutputPtr(
|
||||
const DxbcRegister& operand) {
|
||||
// Same index format as input registers, except that
|
||||
// outputs cannot be accessed with a relative index.
|
||||
if (operand.idxDim != 1)
|
||||
throw DxvkError("DxbcCompiler: 2D index for o# not yet supported");
|
||||
|
||||
// We don't support two-dimensional indices yet
|
||||
const uint32_t registerId = operand.idx[0].offset;
|
||||
|
||||
// In the pixel shader, output registers are typed,
|
||||
// whereas they are float4 in all other stages.
|
||||
if (m_version.type() == DxbcProgramType::PixelShader) {
|
||||
DxbcRegisterPointer result;
|
||||
result.type = m_ps.oTypes.at(registerId);
|
||||
result.id = m_oRegs.at(registerId);
|
||||
return result;
|
||||
} else {
|
||||
// Load operand value from the operand pointer
|
||||
DxbcValue result = this->loadRegister(srcOp, srcMask, dstType);
|
||||
DxbcRegisterPointer result;
|
||||
result.type.ctype = DxbcScalarType::Float32;
|
||||
result.type.ccount = 4;
|
||||
result.id = m_oRegs.at(registerId);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the component swizzle or the selection,
|
||||
// depending on which mode the operand is in.
|
||||
if (srcOp.componentCount == 4) {
|
||||
switch (srcOp.componentMode) {
|
||||
case DxbcRegMode::Swizzle:
|
||||
result = this->swizzleReg(result, srcOp.swizzle, srcMask);
|
||||
break;
|
||||
|
||||
case DxbcRegMode::Select1:
|
||||
result = this->extractReg(result, 1u << srcOp.select1);
|
||||
break;
|
||||
DxbcRegisterPointer DxbcCompiler::emitGetConstBufPtr(
|
||||
const DxbcRegister& operand) {
|
||||
// Constant buffers take a two-dimensional index:
|
||||
// (0) register index (immediate)
|
||||
// (1) constant offset (relative)
|
||||
DxbcRegisterInfo info;
|
||||
info.type.ctype = DxbcScalarType::Float32;
|
||||
info.type.ccount = 4;
|
||||
info.sclass = spv::StorageClassUniform;
|
||||
|
||||
const uint32_t regId = operand.idx[0].offset;
|
||||
const DxbcRegisterValue constId = emitIndexLoad(operand.idx[1]);
|
||||
|
||||
const uint32_t ptrTypeId = getPointerTypeId(info);
|
||||
|
||||
const std::array<uint32_t, 2> indices = {
|
||||
m_module.consti32(0), constId.id
|
||||
};
|
||||
|
||||
DxbcRegisterPointer result;
|
||||
result.type = info.type;
|
||||
result.id = m_module.opAccessChain(ptrTypeId,
|
||||
m_constantBuffers.at(regId).varId,
|
||||
indices.size(), indices.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterPointer DxbcCompiler::emitGetOperandPtr(
|
||||
const DxbcRegister& operand) {
|
||||
switch (operand.type) {
|
||||
case DxbcOperandType::Temp:
|
||||
return emitGetTempPtr(operand);
|
||||
|
||||
case DxbcOperandType::Input:
|
||||
return emitGetInputPtr(operand);
|
||||
|
||||
case DxbcOperandType::Output:
|
||||
return emitGetOutputPtr(operand);
|
||||
|
||||
case DxbcOperandType::ConstantBuffer:
|
||||
return emitGetConstBufPtr(operand);
|
||||
|
||||
default:
|
||||
Logger::err("dxbc: Invalid component selection mode");
|
||||
}
|
||||
}
|
||||
|
||||
// Cast it to the requested type. We need to do
|
||||
// this after the swizzling for 64-bit types.
|
||||
if (result.componentType != dstType)
|
||||
result = this->bitcastReg(result, dstType);
|
||||
|
||||
// Apply operand modifiers
|
||||
return this->applyOperandModifiers(result, srcOp.modifiers);
|
||||
throw DxvkError(str::format(
|
||||
"DxbcCompiler: Unhandled operand type: ",
|
||||
operand.type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxbcValue DxbcCompiler::loadImm32(
|
||||
const DxbcInstOp& srcOp,
|
||||
DxbcRegMask srcMask,
|
||||
DxbcScalarType dstType) {
|
||||
// We will generate Uint32 constants because at this
|
||||
// point we don't know how they are going to be used.
|
||||
DxbcValue result;
|
||||
result.componentType = DxbcScalarType::Uint32;
|
||||
result.componentCount = srcMask.setCount();
|
||||
DxbcRegisterValue DxbcCompiler::emitIndexLoad(
|
||||
DxbcRegIndex index) {
|
||||
if (index.relReg != nullptr) {
|
||||
DxbcRegisterValue result = emitRegisterLoad(
|
||||
*index.relReg, DxbcRegMask(true, false, false, false));
|
||||
|
||||
uint32_t resultTypeId = this->defineVectorType(
|
||||
result.componentType, result.componentCount);
|
||||
|
||||
uint32_t constIds[4];
|
||||
uint32_t constIdx = 0;
|
||||
|
||||
// Generate scalar constants for each component
|
||||
// and pack them tightly in an array, so that we
|
||||
// can use them to generate a constant vector.
|
||||
if (srcOp.componentCount == 4) {
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
if (srcMask[i]) {
|
||||
constIds[constIdx++] = m_module.constu32(
|
||||
srcOp.immediates[i]);
|
||||
if (index.offset != 0) {
|
||||
result.id = m_module.opIAdd(
|
||||
getVectorTypeId(result.type), result.id,
|
||||
m_module.consti32(index.offset));
|
||||
}
|
||||
}
|
||||
} else if (srcOp.componentCount == 1) {
|
||||
constIds[0] = m_module.constu32(srcOp.immediates[0]);
|
||||
|
||||
return result;
|
||||
} else {
|
||||
Logger::err("dxbc: Invalid imm32 component count");
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = DxbcScalarType::Sint32;
|
||||
result.type.ccount = 1;
|
||||
result.id = m_module.consti32(index.offset);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// If the result is a vector, emit the final constant
|
||||
if (result.componentCount == 1) {
|
||||
result.valueId = constIds[0];
|
||||
} else {
|
||||
result.valueId = m_module.constComposite(
|
||||
resultTypeId, result.componentCount, constIds);
|
||||
}
|
||||
|
||||
// If necessary, cast the constant to the desired type
|
||||
if (result.componentType != dstType)
|
||||
result = this->bitcastReg(result, dstType);
|
||||
|
||||
DxbcRegisterValue DxbcCompiler::emitValueLoad(
|
||||
DxbcRegisterPointer ptr) {
|
||||
DxbcRegisterValue result;
|
||||
result.type = ptr.type;
|
||||
result.id = m_module.opLoad(
|
||||
getVectorTypeId(result.type),
|
||||
ptr.id);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcValue DxbcCompiler::loadRegister(
|
||||
const DxbcInstOp& srcOp,
|
||||
DxbcRegMask srcMask,
|
||||
DxbcScalarType dstType) {
|
||||
return this->loadPtr(
|
||||
this->getOperandPtr(srcOp));
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::storeOp(
|
||||
const DxbcInstOp& dstOp,
|
||||
const DxbcValue& srcValue) {
|
||||
this->storePtr(
|
||||
this->getOperandPtr(dstOp),
|
||||
srcValue, dstOp.mask);
|
||||
}
|
||||
|
||||
|
||||
DxbcValue DxbcCompiler::loadPtr(const DxbcPointer& ptr) {
|
||||
const uint32_t typeId = this->defineVectorType(
|
||||
ptr.componentType, ptr.componentCount);
|
||||
|
||||
DxbcValue result;
|
||||
result.componentType = ptr.componentType;
|
||||
result.componentCount = ptr.componentCount;
|
||||
result.valueId = m_module.opLoad(typeId, ptr.pointerId);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::storePtr(
|
||||
const DxbcPointer& ptr,
|
||||
const DxbcValue& value,
|
||||
DxbcRegMask mask) {
|
||||
DxbcValue srcValue = value;
|
||||
|
||||
// If the source value consists of only one component,
|
||||
// it is stored in all destination register components.
|
||||
if (srcValue.componentCount == 1)
|
||||
srcValue = this->extendReg(srcValue, mask.setCount());
|
||||
|
||||
void DxbcCompiler::emitValueStore(
|
||||
DxbcRegisterPointer ptr,
|
||||
DxbcRegisterValue value,
|
||||
DxbcRegMask writeMask) {
|
||||
// If the component types are not compatible,
|
||||
// we need to bit-cast the source variable.
|
||||
if (ptr.componentType != srcValue.componentType)
|
||||
srcValue = this->bitcastReg(srcValue, ptr.componentType);
|
||||
if (value.type.ctype != ptr.type.ctype)
|
||||
value = emitRegisterBitcast(value, ptr.type.ctype);
|
||||
|
||||
if (mask.setCount() == ptr.componentCount) {
|
||||
// If the source value consists of only one component,
|
||||
// it is stored in all components of the destination.
|
||||
if (value.type.ccount == 1)
|
||||
value = emitRegisterExtend(value, writeMask.setCount());
|
||||
|
||||
if (ptr.type.ccount == writeMask.setCount()) {
|
||||
// Simple case: We write to the entire register
|
||||
m_module.opStore(ptr.pointerId, srcValue.valueId);
|
||||
m_module.opStore(ptr.id, value.id);
|
||||
} else {
|
||||
// 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);
|
||||
DxbcRegisterValue tmp = emitValueLoad(ptr);
|
||||
tmp = emitRegisterInsert(tmp, value, writeMask);
|
||||
|
||||
m_module.opStore(ptr.pointerId, tmp.valueId);
|
||||
m_module.opStore(ptr.id, tmp.id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
DxbcValue DxbcCompiler::loadIndex(const DxbcInstOpIndex& idx) {
|
||||
DxbcValue constantPart;
|
||||
DxbcValue relativePart;
|
||||
DxbcRegisterValue DxbcCompiler::emitRegisterLoad(
|
||||
const DxbcRegister& reg,
|
||||
DxbcRegMask writeMask) {
|
||||
if (reg.type == DxbcOperandType::Imm32) {
|
||||
DxbcRegisterValue result;
|
||||
|
||||
if ((idx.type == DxbcIndexType::Immediate) || (idx.immediate != 0)) {
|
||||
constantPart.componentType = DxbcScalarType::Sint32;
|
||||
constantPart.componentCount = 1;
|
||||
constantPart.valueId = m_module.consti32(
|
||||
static_cast<int32_t>(idx.immediate));
|
||||
if (reg.componentCount == DxbcComponentCount::Component1) {
|
||||
// Create one single u32 constant
|
||||
result.type.ctype = DxbcScalarType::Uint32;
|
||||
result.type.ccount = 1;
|
||||
result.id = m_module.constu32(reg.imm.u32_1);
|
||||
} else if (reg.componentCount == DxbcComponentCount::Component4) {
|
||||
// Create a four-component u32 vector
|
||||
std::array<uint32_t, 4> indices = {
|
||||
m_module.constu32(reg.imm.u32_4[0]),
|
||||
m_module.constu32(reg.imm.u32_4[1]),
|
||||
m_module.constu32(reg.imm.u32_4[2]),
|
||||
m_module.constu32(reg.imm.u32_4[3]),
|
||||
};
|
||||
|
||||
result.type.ctype = DxbcScalarType::Uint32;
|
||||
result.type.ccount = 4;
|
||||
result.id = m_module.constComposite(
|
||||
getVectorTypeId(result.type),
|
||||
indices.size(), indices.data());
|
||||
} else {
|
||||
// Something went horribly wrong in the decoder or the shader is broken
|
||||
throw DxvkError("DxbcCompiler: Invalid component count for immediate operand");
|
||||
}
|
||||
|
||||
if (idx.type == DxbcIndexType::Relative) {
|
||||
DxbcInstOp offsetOp;
|
||||
offsetOp.type = DxbcOperandType::Temp;
|
||||
offsetOp.indexDim = 1;
|
||||
offsetOp.index[0].type = DxbcIndexType::Immediate;
|
||||
offsetOp.index[0].immediate = idx.tempRegId;
|
||||
offsetOp.componentCount = 4;
|
||||
offsetOp.componentMode = DxbcRegMode::Select1;
|
||||
offsetOp.select1 = idx.tempRegComponent;
|
||||
// Cast constants to the requested type
|
||||
return emitRegisterBitcast(result, reg.dataType);
|
||||
} else {
|
||||
// Load operand from the operand pointer
|
||||
DxbcRegisterPointer ptr = emitGetOperandPtr(reg);
|
||||
DxbcRegisterValue result = emitValueLoad(ptr);
|
||||
|
||||
relativePart = this->loadOp(offsetOp,
|
||||
DxbcRegMask(true, false, false, false),
|
||||
DxbcScalarType::Sint32);
|
||||
}
|
||||
// Apply operand swizzle to the operand value
|
||||
result = emitRegisterSwizzle(result, reg.swizzle, writeMask);
|
||||
|
||||
if (relativePart.valueId == 0) return constantPart;
|
||||
if (constantPart.valueId == 0) return relativePart;
|
||||
// Cast it to the requested type. We need to do
|
||||
// this after the swizzling for 64-bit types.
|
||||
result = emitRegisterBitcast(result, reg.dataType);
|
||||
|
||||
DxbcValue result;
|
||||
result.componentType = DxbcScalarType::Sint32;
|
||||
result.componentCount = 1;
|
||||
result.valueId = m_module.opIAdd(
|
||||
this->defineScalarType(result.componentType),
|
||||
relativePart.valueId, constantPart.valueId);
|
||||
// Apply operand modifiers
|
||||
result = emitSrcOperandModifiers(result, reg.modifiers);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxbcPointer DxbcCompiler::getOperandPtr(const DxbcInstOp& op) {
|
||||
DxbcPointer result;
|
||||
void DxbcCompiler::emitRegisterStore(
|
||||
const DxbcRegister& reg,
|
||||
DxbcRegisterValue value) {
|
||||
emitValueStore(emitGetOperandPtr(reg), value, reg.mask);
|
||||
}
|
||||
|
||||
switch (op.type) {
|
||||
case DxbcOperandType::Temp:
|
||||
result.componentType = DxbcScalarType::Float32;
|
||||
result.componentCount = 4;
|
||||
result.pointerId = m_rRegs.at(op.index[0].immediate);
|
||||
break;
|
||||
|
||||
// TODO implement properly
|
||||
case DxbcOperandType::Input:
|
||||
result.componentType = DxbcScalarType::Float32;
|
||||
result.componentCount = 4;
|
||||
result.pointerId = m_vRegs.at(op.index[0].immediate);
|
||||
break;
|
||||
void DxbcCompiler::emitVsInputSetup() {
|
||||
|
||||
// TODO implement properly
|
||||
case DxbcOperandType::Output:
|
||||
if (m_version.type() == DxbcProgramType::PixelShader)
|
||||
return m_ps.oregs.at(op.index[0].immediate);
|
||||
}
|
||||
|
||||
result.componentType = DxbcScalarType::Float32;
|
||||
result.componentCount = 4;
|
||||
result.pointerId = m_oRegs.at(op.index[0].immediate);
|
||||
break;
|
||||
|
||||
case DxbcOperandType::ConstantBuffer:
|
||||
return this->getConstantBufferPtr(op);
|
||||
void DxbcCompiler::emitPsInputSetup() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::emitVsOutputSetup() {
|
||||
for (const DxbcSvMapping& svMapping : m_oMappings) {
|
||||
switch (svMapping.sv) {
|
||||
case DxbcSystemValue::Position: {
|
||||
DxbcRegisterInfo info;
|
||||
info.type.ctype = DxbcScalarType::Float32;
|
||||
info.type.ccount = 4;
|
||||
info.sclass = spv::StorageClassOutput;
|
||||
|
||||
const uint32_t ptrTypeId = getPointerTypeId(info);
|
||||
const uint32_t memberId = m_module.constu32(PerVertex_Position);
|
||||
|
||||
DxbcRegisterPointer dstPtr;
|
||||
dstPtr.type = info.type;
|
||||
dstPtr.id = m_module.opAccessChain(
|
||||
ptrTypeId, m_perVertexOut, 1, &memberId);
|
||||
|
||||
DxbcRegisterPointer srcPtr;
|
||||
srcPtr.type = info.type;
|
||||
srcPtr.id = m_oRegs.at(svMapping.regId);
|
||||
|
||||
emitValueStore(dstPtr, emitValueLoad(srcPtr),
|
||||
DxbcRegMask(true, true, true, true));
|
||||
} break;
|
||||
|
||||
default:
|
||||
Logger::err("dxbc: Unhandled operand type");
|
||||
Logger::warn(str::format(
|
||||
"dxbc: Unhandled vertex sv output: ",
|
||||
svMapping.sv));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcPointer DxbcCompiler::getConstantBufferPtr(const DxbcInstOp& op) {
|
||||
if (op.indexDim != 2) {
|
||||
Logger::err("dxbc: Constant buffer reference needs two indices");
|
||||
return DxbcPointer();
|
||||
}
|
||||
void DxbcCompiler::emitPsOutputSetup() {
|
||||
|
||||
// The operand itself has two indices:
|
||||
// (1) The constant buffer ID (immediate)
|
||||
// (2) The constant offset (relative)
|
||||
const uint32_t bufferId = op.index[0].immediate;
|
||||
const DxbcValue offset = this->loadIndex(op.index[1]);
|
||||
|
||||
// The first index selects the struct member,
|
||||
// the second one selects the array element.
|
||||
std::array<uint32_t, 2> indices = {
|
||||
m_module.constu32(0), offset.valueId };
|
||||
|
||||
DxbcPointer result;
|
||||
result.componentType = DxbcScalarType::Float32;
|
||||
result.componentCount = 4;
|
||||
result.pointerId = m_module.opAccessChain(
|
||||
this->definePointerType(
|
||||
result.componentType,
|
||||
result.componentCount,
|
||||
spv::StorageClassUniform),
|
||||
m_constantBuffers.at(bufferId).varId,
|
||||
2, indices.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::beginVertexShader(const Rc<DxbcIsgn>& isgn) {
|
||||
void DxbcCompiler::emitVsInit() {
|
||||
m_module.enableCapability(spv::CapabilityShader);
|
||||
m_module.enableCapability(spv::CapabilityCullDistance);
|
||||
m_module.enableCapability(spv::CapabilityClipDistance);
|
||||
m_module.enableCapability(spv::CapabilityCullDistance);
|
||||
|
||||
// Declare the per-vertex output block. This is where
|
||||
// the vertex shader will write the vertex position.
|
||||
uint32_t perVertexStruct = this->definePerVertexBlock();
|
||||
uint32_t perVertexPointer = m_module.defPointerType(
|
||||
const uint32_t perVertexStruct = this->getPerVertexBlockId();
|
||||
const uint32_t perVertexPointer = m_module.defPointerType(
|
||||
perVertexStruct, spv::StorageClassOutput);
|
||||
|
||||
m_perVertexOut = m_module.newVar(
|
||||
perVertexPointer, spv::StorageClassOutput);
|
||||
m_entryPointInterfaces.push_back(m_perVertexOut);
|
||||
m_module.setDebugName(m_perVertexOut, "vs_block");
|
||||
m_module.setDebugName(m_perVertexOut, "vs_vertex_out");
|
||||
|
||||
// Main function of the vertex shader
|
||||
m_vs.functionId = m_module.allocateId();
|
||||
@ -1409,7 +1475,7 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::beginPixelShader(const Rc<DxbcIsgn>& osgn) {
|
||||
void DxbcCompiler::emitPsInit() {
|
||||
m_module.enableCapability(spv::CapabilityShader);
|
||||
m_module.setOriginUpperLeft(m_entryPointId);
|
||||
|
||||
@ -1418,22 +1484,19 @@ namespace dxvk {
|
||||
// the render target.
|
||||
for (auto e = m_osgn->begin(); e != m_osgn->end(); e++) {
|
||||
if (e->systemValue == DxbcSystemValue::None) {
|
||||
uint32_t regTypeId = this->defineVectorType(
|
||||
e->componentType, e->componentMask.setCount());
|
||||
DxbcRegisterInfo info;
|
||||
info.type.ctype = e->componentType;
|
||||
info.type.ccount = e->componentMask.setCount();
|
||||
info.sclass = spv::StorageClassOutput;
|
||||
|
||||
uint32_t ptrTypeId = m_module.defPointerType(
|
||||
regTypeId, spv::StorageClassOutput);
|
||||
|
||||
uint32_t varId = m_module.newVar(
|
||||
ptrTypeId, spv::StorageClassOutput);
|
||||
const uint32_t varId = emitNewVariable(info);
|
||||
|
||||
m_module.decorateLocation(varId, e->registerId);
|
||||
m_module.setDebugName(varId, str::format("o", e->registerId).c_str());
|
||||
m_entryPointInterfaces.push_back(varId);
|
||||
|
||||
m_ps.oregs.at(e->registerId).componentType = e->componentType;
|
||||
m_ps.oregs.at(e->registerId).componentCount = e->componentMask.setCount();
|
||||
m_ps.oregs.at(e->registerId).pointerId = varId;
|
||||
m_oRegs.at(e->registerId) = varId;
|
||||
m_ps.oTypes.at(e->registerId) = info.type;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1451,77 +1514,62 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::prepareVertexInputs() {
|
||||
// TODO implement
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::preparePixelInputs() {
|
||||
// TODO implement
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::prepareVertexOutputs() {
|
||||
for (const DxbcSvMapping& svMapping : m_oSvs) {
|
||||
switch (svMapping.sv) {
|
||||
case DxbcSystemValue::Position: {
|
||||
DxbcPointer dstPtr;
|
||||
dstPtr.componentType = DxbcScalarType::Float32;
|
||||
dstPtr.componentCount = 4;
|
||||
|
||||
uint32_t regTypeId = this->defineVectorType(
|
||||
dstPtr.componentType, dstPtr.componentCount);
|
||||
|
||||
uint32_t ptrTypeId = m_module.defPointerType(
|
||||
regTypeId, spv::StorageClassOutput);
|
||||
|
||||
uint32_t memberId = m_module.constu32(PerVertex_Position);
|
||||
|
||||
dstPtr.pointerId = m_module.opAccessChain(
|
||||
ptrTypeId, m_perVertexOut, 1, &memberId);
|
||||
|
||||
DxbcPointer srcPtr;
|
||||
srcPtr.componentType = DxbcScalarType::Float32;
|
||||
srcPtr.componentCount = 4;
|
||||
srcPtr.pointerId = m_oRegs.at(svMapping.regId);
|
||||
|
||||
this->storePtr(dstPtr, this->loadPtr(srcPtr),
|
||||
DxbcRegMask(true, true, true, true));
|
||||
} break;
|
||||
|
||||
default:
|
||||
Logger::err(str::format(
|
||||
"dxbc: Unsupported vertex sv output: ",
|
||||
svMapping.sv));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::preparePixelOutputs() {
|
||||
// TODO implement
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::endVertexShader() {
|
||||
this->prepareVertexInputs();
|
||||
void DxbcCompiler::emitVsFinalize() {
|
||||
this->emitVsInputSetup();
|
||||
m_module.opFunctionCall(
|
||||
m_module.defVoidType(),
|
||||
m_vs.functionId, 0, nullptr);
|
||||
this->prepareVertexOutputs();
|
||||
this->emitVsOutputSetup();
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler::endPixelShader() {
|
||||
this->preparePixelInputs();
|
||||
void DxbcCompiler::emitPsFinalize() {
|
||||
this->emitPsInputSetup();
|
||||
m_module.opFunctionCall(
|
||||
m_module.defVoidType(),
|
||||
m_ps.functionId, 0, nullptr);
|
||||
this->preparePixelOutputs();
|
||||
this->emitPsOutputSetup();
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler::definePerVertexBlock() {
|
||||
uint32_t DxbcCompiler::emitNewVariable(const DxbcRegisterInfo& info) {
|
||||
const uint32_t ptrTypeId = this->getPointerTypeId(info);
|
||||
return m_module.newVar(ptrTypeId, info.sclass);
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler::getScalarTypeId(DxbcScalarType type) {
|
||||
switch (type) {
|
||||
case DxbcScalarType::Uint32: return m_module.defIntType(32, 0);
|
||||
case DxbcScalarType::Uint64: return m_module.defIntType(64, 0);
|
||||
case DxbcScalarType::Sint32: return m_module.defIntType(32, 1);
|
||||
case DxbcScalarType::Sint64: return m_module.defIntType(64, 1);
|
||||
case DxbcScalarType::Float32: return m_module.defFloatType(32);
|
||||
case DxbcScalarType::Float64: return m_module.defFloatType(64);
|
||||
}
|
||||
|
||||
throw DxvkError("DxbcCompiler: Invalid scalar type");
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler::getVectorTypeId(const DxbcVectorType& type) {
|
||||
uint32_t typeId = this->getScalarTypeId(type.ctype);
|
||||
|
||||
if (type.ccount > 1)
|
||||
typeId = m_module.defVectorType(typeId, type.ccount);
|
||||
|
||||
return typeId;
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler::getPointerTypeId(const DxbcRegisterInfo& type) {
|
||||
return m_module.defPointerType(
|
||||
this->getVectorTypeId(type.type),
|
||||
type.sclass);
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler::getPerVertexBlockId() {
|
||||
uint32_t t_f32 = m_module.defFloatType(32);
|
||||
uint32_t t_f32_v4 = m_module.defVectorType(t_f32, 4);
|
||||
uint32_t t_f32_a2 = m_module.defArrayType(t_f32, m_module.constu32(2));
|
||||
@ -1549,155 +1597,4 @@ namespace dxvk {
|
||||
return typeId;
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler::defineScalarType(
|
||||
DxbcScalarType componentType) {
|
||||
switch (componentType) {
|
||||
case DxbcScalarType::Float32: return m_module.defFloatType(32);
|
||||
case DxbcScalarType::Float64: return m_module.defFloatType(64);
|
||||
case DxbcScalarType::Uint32: return m_module.defIntType(32, 0);
|
||||
case DxbcScalarType::Uint64: return m_module.defIntType(64, 0);
|
||||
case DxbcScalarType::Sint32: return m_module.defIntType(32, 1);
|
||||
case DxbcScalarType::Sint64: return m_module.defIntType(64, 1);
|
||||
}
|
||||
|
||||
Logger::err("dxbc: Invalid scalar type");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler::defineVectorType(
|
||||
DxbcScalarType componentType,
|
||||
uint32_t componentCount) {
|
||||
uint32_t typeId = this->defineScalarType(componentType);
|
||||
|
||||
if (componentCount > 1) {
|
||||
typeId = m_module.defVectorType(
|
||||
typeId, componentCount);
|
||||
}
|
||||
|
||||
return typeId;
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler::definePointerType(
|
||||
DxbcScalarType componentType,
|
||||
uint32_t componentCount,
|
||||
spv::StorageClass storageClass) {
|
||||
return m_module.defPointerType(
|
||||
this->defineVectorType(
|
||||
componentType, componentCount),
|
||||
storageClass);
|
||||
}
|
||||
|
||||
|
||||
DxbcError DxbcCompiler::parseInstruction(const DxbcInstruction& ins, DxbcInst& out) {
|
||||
out.opcode = ins.token().opcode();
|
||||
out.control = ins.token().control();
|
||||
out.format = dxbcInstructionFormat(out.opcode);
|
||||
|
||||
// TODO implement extended opcodes
|
||||
|
||||
uint32_t argOffset = 0;
|
||||
|
||||
for (uint32_t i = 0; i < out.format.operandCount; i++) {
|
||||
|
||||
if (out.format.operands[i].kind == DxbcOperandKind::Imm32) {
|
||||
// Some declarations use immediate DWORDs rather than the
|
||||
// immediate operand tokens, but we'll treat them the same.
|
||||
out.operands[i].type = DxbcOperandType::Imm32;
|
||||
out.operands[i].immediates[0] = ins.arg(argOffset);
|
||||
argOffset += 1;
|
||||
} else {
|
||||
// Most instructions use proper operand tokens, which either
|
||||
// store an immediate value or references a register from one
|
||||
// of the indexable register files.
|
||||
const DxbcOperand op = ins.operand(argOffset);
|
||||
out.operands[i].type = op.token().type();
|
||||
|
||||
// Parse operand modifiers
|
||||
DxbcOperandTokenExt token;
|
||||
|
||||
if (op.queryOperandExt(DxbcOperandExt::OperandModifier, token))
|
||||
out.operands[i].modifiers = DxbcOperandModifiers(token.data());
|
||||
|
||||
// Parse immediate values if applicable
|
||||
if (op.token().type() == DxbcOperandType::Imm32) {
|
||||
for (uint32_t j = 0; j < op.token().numComponents(); j++)
|
||||
out.operands[i].immediates[j] = op.imm32(j);
|
||||
}
|
||||
|
||||
if (op.token().type() == DxbcOperandType::Imm64) {
|
||||
Logger::err("dxbc: 64-bit immediates not supported");
|
||||
return DxbcError::eInstructionFormat;
|
||||
}
|
||||
|
||||
// Parse the indices. Each index is either a constant value
|
||||
// or the sum of a constant and a temporary register value.
|
||||
out.operands[i].indexDim = op.token().indexDimension();
|
||||
|
||||
for (uint32_t j = 0; j < op.token().indexDimension(); j++) {
|
||||
const DxbcOperandIndex index = op.index(j);
|
||||
|
||||
DxbcInstOpIndex& opIndex = out.operands[i].index[j];
|
||||
opIndex.type = index.hasRelPart()
|
||||
? DxbcIndexType::Relative
|
||||
: DxbcIndexType::Immediate;
|
||||
opIndex.immediate = index.immPart();
|
||||
|
||||
// If the index is relative, we have to parse another
|
||||
// operand token which must reference a r# register.
|
||||
if (index.hasRelPart()) {
|
||||
const DxbcOperand relPart = index.relPart();
|
||||
|
||||
if (relPart.token().type() != DxbcOperandType::Temp) {
|
||||
Logger::err("dxbc: Invalid index register type");
|
||||
return DxbcError::eInstructionFormat;
|
||||
}
|
||||
|
||||
if ((relPart.token().indexDimension() != 1)
|
||||
|| (relPart.index(0).hasRelPart())) {
|
||||
Logger::err("dxbc: Invalid index register index");
|
||||
return DxbcError::eInstructionFormat;
|
||||
}
|
||||
|
||||
if (relPart.token().selectionMode() != DxbcRegMode::Select1) {
|
||||
Logger::err("dxbc: Invalid index component selection mode");
|
||||
return DxbcError::eInstructionFormat;
|
||||
}
|
||||
|
||||
// Assign the index and the component selection
|
||||
opIndex.tempRegId = relPart.index(0).immPart();
|
||||
opIndex.tempRegComponent = relPart.token().select1();
|
||||
}
|
||||
}
|
||||
|
||||
// Parse component mask, swizzle or selection
|
||||
out.operands[i].componentCount = op.token().numComponents();
|
||||
out.operands[i].componentMode = op.token().selectionMode();
|
||||
|
||||
if (op.token().numComponents() == 4) {
|
||||
switch (op.token().selectionMode()) {
|
||||
case DxbcRegMode::Mask:
|
||||
out.operands[i].mask = op.token().mask();
|
||||
break;
|
||||
|
||||
case DxbcRegMode::Swizzle:
|
||||
out.operands[i].swizzle = op.token().swizzle();
|
||||
break;
|
||||
|
||||
case DxbcRegMode::Select1:
|
||||
out.operands[i].select1 = op.token().select1();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Move on to next operand
|
||||
argOffset += op.length();
|
||||
}
|
||||
}
|
||||
|
||||
return DxbcError::sOk;
|
||||
}
|
||||
|
||||
}
|
@ -1,133 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "../spirv/spirv_module.h"
|
||||
|
||||
#include "dxbc_chunk_isgn.h"
|
||||
#include "dxbc_decoder.h"
|
||||
#include "dxbc_defs.h"
|
||||
#include "dxbc_names.h"
|
||||
#include "dxbc_util.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
/**
|
||||
* \brief Expression value
|
||||
* \brief Vector type
|
||||
*
|
||||
* Tracks the type and the SPIR-V variable
|
||||
* ID when evaluating DXBC instructions.
|
||||
* Convenience struct that stores a scalar
|
||||
* type and a component count. The compiler
|
||||
* can use this to generate SPIR-V types.
|
||||
*/
|
||||
struct DxbcValue {
|
||||
DxbcScalarType componentType = DxbcScalarType::Float32;
|
||||
uint32_t componentCount = 0;
|
||||
uint32_t valueId = 0;
|
||||
struct DxbcVectorType {
|
||||
DxbcScalarType ctype;
|
||||
uint32_t ccount;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Variable pointer
|
||||
* \brief Register info
|
||||
*
|
||||
* Stores the SPIR-V pointer ID and the
|
||||
* type of the referenced variable. Used
|
||||
* to access variables and resources.
|
||||
* Stores the vector type of a register and
|
||||
* its storage class. The compiler can use
|
||||
* this to generate SPIR-V pointer types.
|
||||
*/
|
||||
struct DxbcPointer {
|
||||
DxbcScalarType componentType = DxbcScalarType::Float32;
|
||||
uint32_t componentCount = 0;
|
||||
uint32_t pointerId = 0;
|
||||
struct DxbcRegisterInfo {
|
||||
DxbcVectorType type;
|
||||
spv::StorageClass sclass;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Compiler error code
|
||||
* \brief Register value
|
||||
*
|
||||
* Helps identify the type of error
|
||||
* that may occur during compilation.
|
||||
* Stores a vector type and a SPIR-V ID that
|
||||
* represents an intermediate value. This is
|
||||
* used to track the type of such values.
|
||||
*/
|
||||
enum class DxbcError {
|
||||
sOk,
|
||||
eInternal,
|
||||
eInstructionFormat,
|
||||
eInvalidOperand,
|
||||
eInvalidOperandIndex,
|
||||
eTypeMismatch,
|
||||
eUnhandledOpcode,
|
||||
eUnsupported,
|
||||
struct DxbcRegisterValue {
|
||||
DxbcVectorType type;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Operand index type
|
||||
* \brief Register pointer
|
||||
*
|
||||
* Defines whether a register index
|
||||
* is relative or constant.
|
||||
* Stores a vector type and a SPIR-V ID that
|
||||
* represents a pointer to such a vector. This
|
||||
* can be used to load registers conveniently.
|
||||
*/
|
||||
enum class DxbcIndexType {
|
||||
Immediate, ///< Index is a constant value
|
||||
Relative, ///< Index depends on a r# register
|
||||
struct DxbcRegisterPointer {
|
||||
DxbcVectorType type;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Instruction operand index
|
||||
*
|
||||
* Stores the type of the index as well as the
|
||||
* register (if relative) and the constant offset.
|
||||
* \brief Vertex shader-specific structure
|
||||
*/
|
||||
struct DxbcInstOpIndex {
|
||||
DxbcIndexType type = DxbcIndexType::Immediate;
|
||||
uint32_t immediate = 0;
|
||||
uint32_t tempRegId = 0;
|
||||
uint32_t tempRegComponent = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Instruction operand
|
||||
*
|
||||
* Stores all information about a single
|
||||
* operand, including the register index.
|
||||
*/
|
||||
struct DxbcInstOp {
|
||||
DxbcOperandType type = DxbcOperandType::Temp;
|
||||
DxbcOperandModifiers modifiers = 0;
|
||||
uint32_t immediates[4] = { 0u, 0u, 0u, 0u };
|
||||
|
||||
uint32_t indexDim = 0;
|
||||
DxbcInstOpIndex index[3];
|
||||
|
||||
uint32_t componentCount = 0;
|
||||
DxbcRegMode componentMode = DxbcRegMode::Mask;
|
||||
|
||||
DxbcRegMask mask = { false, false, false, false };
|
||||
DxbcRegSwizzle swizzle = { 0, 0, 0, 0 };
|
||||
uint32_t select1 = 0;
|
||||
struct DxbcCompilerVsPart {
|
||||
uint32_t functionId;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Decoded instruction
|
||||
*
|
||||
* Stores all information about a single
|
||||
* instruction, including its operands.
|
||||
* \brief Pixel shader-specific structure
|
||||
*/
|
||||
struct DxbcInst {
|
||||
DxbcOpcode opcode = DxbcOpcode::Nop;
|
||||
DxbcOpcodeControl control = 0;
|
||||
DxbcInstFormat format;
|
||||
DxbcInstOp operands[DxbcMaxOperandCount];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Vertex shader-specific data
|
||||
*/
|
||||
struct DxbcVsSpecifics {
|
||||
uint32_t functionId = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Pixel shader-specific data
|
||||
*/
|
||||
struct DxbcPsSpecifics {
|
||||
uint32_t functionId = 0;
|
||||
|
||||
std::array<DxbcPointer, DxbcMaxInterfaceRegs> oregs;
|
||||
struct DxbcCompilerPsPart {
|
||||
uint32_t functionId;
|
||||
std::array<DxbcVectorType, DxbcMaxInterfaceRegs> oTypes;
|
||||
};
|
||||
|
||||
|
||||
@ -150,12 +101,10 @@ namespace dxvk {
|
||||
|
||||
/**
|
||||
* \brief Processes a single instruction
|
||||
*
|
||||
* \param [in] ins The instruction
|
||||
* \returns An error code, or \c sOK
|
||||
*/
|
||||
DxbcError processInstruction(
|
||||
const DxbcInstruction& ins);
|
||||
void processInstruction(
|
||||
const DxbcShaderInstruction& ins);
|
||||
|
||||
/**
|
||||
* \brief Finalizes the shader
|
||||
@ -184,12 +133,14 @@ namespace dxvk {
|
||||
// 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
|
||||
@ -198,12 +149,6 @@ namespace dxvk {
|
||||
std::array<DxbcSampler, 16> m_samplers;
|
||||
std::array<DxbcShaderResource, 128> m_textures;
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Input/Output system value mappings. These will need
|
||||
// to be set up before or after the main function runs.
|
||||
std::vector<DxbcSvMapping> m_vSvs;
|
||||
std::vector<DxbcSvMapping> m_oSvs;
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Array of input values. Since v# registers are indexable
|
||||
// in DXBC, we need to copy them into an array first.
|
||||
@ -221,190 +166,185 @@ namespace dxvk {
|
||||
std::vector<uint32_t> m_entryPointInterfaces;
|
||||
uint32_t m_entryPointId = 0;
|
||||
|
||||
////////////////////////////////////////
|
||||
// Data structures for each shader type
|
||||
DxbcVsSpecifics m_vs;
|
||||
DxbcPsSpecifics m_ps;
|
||||
///////////////////////////////////
|
||||
// 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
|
||||
DxbcError handleDeclaration(
|
||||
const DxbcInst& ins);
|
||||
void emitVectorAlu(
|
||||
const DxbcShaderInstruction& ins);
|
||||
|
||||
DxbcError handleControlFlow(
|
||||
const DxbcInst& ins);
|
||||
void emitVectorCmov(
|
||||
const DxbcShaderInstruction& ins);
|
||||
|
||||
DxbcError handleTextureSample(
|
||||
const DxbcInst& ins);
|
||||
void emitVectorCmp(
|
||||
const DxbcShaderInstruction& ins);
|
||||
|
||||
DxbcError handleVectorAlu(
|
||||
const DxbcInst& ins);
|
||||
void emitVectorDot(
|
||||
const DxbcShaderInstruction& ins);
|
||||
|
||||
DxbcError handleVectorCmov(
|
||||
const DxbcInst& ins);
|
||||
void emitVectorImul(
|
||||
const DxbcShaderInstruction& ins);
|
||||
|
||||
DxbcError handleVectorCmp(
|
||||
const DxbcInst& ins);
|
||||
void emitVectorSinCos(
|
||||
const DxbcShaderInstruction& ins);
|
||||
|
||||
DxbcError handleVectorDot(
|
||||
const DxbcInst& ins);
|
||||
void emitSample(
|
||||
const DxbcShaderInstruction& ins);
|
||||
|
||||
DxbcError handleVectorSinCos(
|
||||
const DxbcInst& ins);
|
||||
void emitRet(
|
||||
const DxbcShaderInstruction& ins);
|
||||
|
||||
///////////////////////
|
||||
// Declaration methods
|
||||
DxbcError declareGlobalFlags(
|
||||
const DxbcInst& ins);
|
||||
|
||||
DxbcError declareTemps(
|
||||
const DxbcInst& ins);
|
||||
/////////////////////////////////////////
|
||||
// Generic register manipulation methods
|
||||
DxbcRegisterValue emitRegisterBitcast(
|
||||
DxbcRegisterValue srcValue,
|
||||
DxbcScalarType dstType);
|
||||
|
||||
DxbcError declareInterfaceVar(
|
||||
const DxbcInst& ins);
|
||||
DxbcRegisterValue emitRegisterSwizzle(
|
||||
DxbcRegisterValue value,
|
||||
DxbcRegSwizzle swizzle,
|
||||
DxbcRegMask writeMask);
|
||||
|
||||
DxbcError declareConstantBuffer(
|
||||
const DxbcInst& ins);
|
||||
|
||||
DxbcError declareSampler(
|
||||
const DxbcInst& ins);
|
||||
|
||||
DxbcError declareResource(
|
||||
const DxbcInst& ins);
|
||||
|
||||
DxbcError declareInputVar(
|
||||
uint32_t regId,
|
||||
uint32_t regDim,
|
||||
DxbcRegMask regMask,
|
||||
DxbcSystemValue sv,
|
||||
DxbcInterpolationMode im);
|
||||
|
||||
DxbcError declareOutputVar(
|
||||
uint32_t regId,
|
||||
uint32_t regDim,
|
||||
DxbcRegMask regMask,
|
||||
DxbcSystemValue sv,
|
||||
DxbcInterpolationMode im);
|
||||
|
||||
////////////////////////////////////
|
||||
// Register manipulation operations
|
||||
DxbcValue bitcastReg(
|
||||
const DxbcValue& src,
|
||||
DxbcScalarType type);
|
||||
|
||||
DxbcValue insertReg(
|
||||
const DxbcValue& dst,
|
||||
const DxbcValue& src,
|
||||
DxbcRegisterValue emitRegisterExtract(
|
||||
DxbcRegisterValue value,
|
||||
DxbcRegMask mask);
|
||||
|
||||
DxbcValue extractReg(
|
||||
const DxbcValue& src,
|
||||
DxbcRegMask mask);
|
||||
DxbcRegisterValue emitRegisterInsert(
|
||||
DxbcRegisterValue dstValue,
|
||||
DxbcRegisterValue srcValue,
|
||||
DxbcRegMask srcMask);
|
||||
|
||||
DxbcValue swizzleReg(
|
||||
const DxbcValue& src,
|
||||
const DxbcRegSwizzle& swizzle,
|
||||
DxbcRegMask mask);
|
||||
|
||||
DxbcValue regVector(
|
||||
const DxbcValue& src,
|
||||
DxbcRegisterValue emitRegisterExtend(
|
||||
DxbcRegisterValue value,
|
||||
uint32_t size);
|
||||
|
||||
DxbcValue extendReg(
|
||||
const DxbcValue& src,
|
||||
uint32_t size);
|
||||
DxbcRegisterValue emitRegisterAbsolute(
|
||||
DxbcRegisterValue value);
|
||||
|
||||
////////////////////////////
|
||||
// Operand modifier methods
|
||||
DxbcValue applyOperandModifiers(
|
||||
DxbcValue value,
|
||||
DxbcOperandModifiers modifiers);
|
||||
DxbcRegisterValue emitRegisterNegate(
|
||||
DxbcRegisterValue value);
|
||||
|
||||
DxbcValue applyResultModifiers(
|
||||
DxbcValue value,
|
||||
DxbcOpcodeControl control);
|
||||
DxbcRegisterValue emitSrcOperandModifiers(
|
||||
DxbcRegisterValue value,
|
||||
DxbcRegModifiers modifiers);
|
||||
|
||||
/////////////////////////
|
||||
// Load/Store operations
|
||||
DxbcValue loadOp(
|
||||
const DxbcInstOp& srcOp,
|
||||
DxbcRegMask srcMask,
|
||||
DxbcScalarType dstType);
|
||||
DxbcRegisterValue emitDstOperandModifiers(
|
||||
DxbcRegisterValue value,
|
||||
DxbcOpModifiers modifiers);
|
||||
|
||||
DxbcValue loadImm32(
|
||||
const DxbcInstOp& srcOp,
|
||||
DxbcRegMask srcMask,
|
||||
DxbcScalarType dstType);
|
||||
////////////////////////
|
||||
// Address load methods
|
||||
DxbcRegisterPointer emitGetTempPtr(
|
||||
const DxbcRegister& operand);
|
||||
|
||||
DxbcValue loadRegister(
|
||||
const DxbcInstOp& srcOp,
|
||||
DxbcRegMask srcMask,
|
||||
DxbcScalarType dstType);
|
||||
DxbcRegisterPointer emitGetInputPtr(
|
||||
const DxbcRegister& operand);
|
||||
|
||||
void storeOp(
|
||||
const DxbcInstOp& dstOp,
|
||||
const DxbcValue& srcValue);
|
||||
DxbcRegisterPointer emitGetOutputPtr(
|
||||
const DxbcRegister& operand);
|
||||
|
||||
DxbcValue loadPtr(
|
||||
const DxbcPointer& ptr);
|
||||
DxbcRegisterPointer emitGetConstBufPtr(
|
||||
const DxbcRegister& operand);
|
||||
|
||||
void storePtr(
|
||||
const DxbcPointer& ptr,
|
||||
const DxbcValue& value,
|
||||
DxbcRegMask mask);
|
||||
DxbcRegisterPointer emitGetOperandPtr(
|
||||
const DxbcRegister& operand);
|
||||
|
||||
DxbcValue loadIndex(
|
||||
const DxbcInstOpIndex& idx);
|
||||
//////////////////////////////
|
||||
// Operand load/store methods
|
||||
DxbcRegisterValue emitIndexLoad(
|
||||
DxbcRegIndex index);
|
||||
|
||||
///////////////////////////
|
||||
// Operand pointer methods
|
||||
DxbcPointer getOperandPtr(
|
||||
const DxbcInstOp& op);
|
||||
DxbcRegisterValue emitValueLoad(
|
||||
DxbcRegisterPointer ptr);
|
||||
|
||||
DxbcPointer getConstantBufferPtr(
|
||||
const DxbcInstOp& op);
|
||||
void emitValueStore(
|
||||
DxbcRegisterPointer ptr,
|
||||
DxbcRegisterValue value,
|
||||
DxbcRegMask writeMask);
|
||||
|
||||
/////////////////////////////////
|
||||
// Shader initialization methods
|
||||
void beginVertexShader(const Rc<DxbcIsgn>& isgn);
|
||||
void beginPixelShader (const Rc<DxbcIsgn>& osgn);
|
||||
DxbcRegisterValue emitRegisterLoad(
|
||||
const DxbcRegister& reg,
|
||||
DxbcRegMask writeMask);
|
||||
|
||||
void emitRegisterStore(
|
||||
const DxbcRegister& reg,
|
||||
DxbcRegisterValue value);
|
||||
|
||||
/////////////////////////////
|
||||
// Input preparation methods
|
||||
void prepareVertexInputs();
|
||||
void preparePixelInputs();
|
||||
void emitVsInputSetup();
|
||||
void emitPsInputSetup();
|
||||
|
||||
//////////////////////////////
|
||||
// Output preparation methods
|
||||
void prepareVertexOutputs();
|
||||
void preparePixelOutputs();
|
||||
void emitVsOutputSetup();
|
||||
void emitPsOutputSetup();
|
||||
|
||||
/////////////////////////////////
|
||||
// Shader initialization methods
|
||||
void emitVsInit();
|
||||
void emitPsInit();
|
||||
|
||||
///////////////////////////////
|
||||
// Shader finalization methods
|
||||
void endVertexShader();
|
||||
void endPixelShader();
|
||||
void emitVsFinalize();
|
||||
void emitPsFinalize();
|
||||
|
||||
///////////////////////////////
|
||||
// Variable definition methods
|
||||
uint32_t emitNewVariable(
|
||||
const DxbcRegisterInfo& info);
|
||||
|
||||
///////////////////////////
|
||||
// Type definition methods
|
||||
uint32_t definePerVertexBlock();
|
||||
uint32_t getScalarTypeId(
|
||||
DxbcScalarType type);
|
||||
|
||||
uint32_t defineScalarType(
|
||||
DxbcScalarType componentType);
|
||||
uint32_t getVectorTypeId(
|
||||
const DxbcVectorType& type);
|
||||
|
||||
uint32_t defineVectorType(
|
||||
DxbcScalarType componentType,
|
||||
uint32_t componentCount);
|
||||
uint32_t getPointerTypeId(
|
||||
const DxbcRegisterInfo& type);
|
||||
|
||||
uint32_t definePointerType(
|
||||
DxbcScalarType componentType,
|
||||
uint32_t componentCount,
|
||||
spv::StorageClass storageClass);
|
||||
|
||||
/////////////////////////
|
||||
// DXBC decoding methods
|
||||
DxbcError parseInstruction(
|
||||
const DxbcInstruction& ins,
|
||||
DxbcInst& out);
|
||||
uint32_t getPerVertexBlockId();
|
||||
|
||||
};
|
||||
|
||||
|
@ -1,1600 +0,0 @@
|
||||
#include "dxbc_compiler_2.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
constexpr uint32_t PerVertex_Position = 0;
|
||||
constexpr uint32_t PerVertex_PointSize = 1;
|
||||
constexpr uint32_t PerVertex_CullDist = 2;
|
||||
constexpr uint32_t PerVertex_ClipDist = 3;
|
||||
|
||||
DxbcCompiler2::DxbcCompiler2(
|
||||
const DxbcProgramVersion& version,
|
||||
const Rc<DxbcIsgn>& isgn,
|
||||
const Rc<DxbcIsgn>& osgn)
|
||||
: m_version (version),
|
||||
m_isgn (isgn),
|
||||
m_osgn (osgn) {
|
||||
// Declare an entry point ID. We'll need it during the
|
||||
// initialization phase where the execution mode is set.
|
||||
m_entryPointId = m_module.allocateId();
|
||||
|
||||
// Set the memory model. This is the same for all shaders.
|
||||
m_module.setMemoryModel(
|
||||
spv::AddressingModelLogical,
|
||||
spv::MemoryModelGLSL450);
|
||||
|
||||
// Make sure our interface registers are clear
|
||||
for (uint32_t i = 0; i < DxbcMaxInterfaceRegs; i++) {
|
||||
m_ps.oTypes.at(i).ctype = DxbcScalarType::Float32;
|
||||
m_ps.oTypes.at(i).ccount = 0;
|
||||
|
||||
m_vRegs.at(i) = 0;
|
||||
m_oRegs.at(i) = 0;
|
||||
}
|
||||
|
||||
// Initialize the shader module with capabilities
|
||||
// etc. Each shader type has its own peculiarities.
|
||||
switch (m_version.type()) {
|
||||
case DxbcProgramType::VertexShader: this->emitVsInit(); break;
|
||||
case DxbcProgramType::PixelShader: this->emitPsInit(); break;
|
||||
default: throw DxvkError("DxbcCompiler: Unsupported program type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxbcCompiler2::~DxbcCompiler2() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::processInstruction(const DxbcShaderInstruction& ins) {
|
||||
switch (ins.op) {
|
||||
case DxbcOpcode::DclGlobalFlags:
|
||||
return this->emitDclGlobalFlags(ins);
|
||||
|
||||
case DxbcOpcode::DclTemps:
|
||||
return this->emitDclTemps(ins);
|
||||
|
||||
case DxbcOpcode::DclInput:
|
||||
case DxbcOpcode::DclInputSgv:
|
||||
case DxbcOpcode::DclInputSiv:
|
||||
case DxbcOpcode::DclInputPs:
|
||||
case DxbcOpcode::DclInputPsSgv:
|
||||
case DxbcOpcode::DclInputPsSiv:
|
||||
case DxbcOpcode::DclOutput:
|
||||
case DxbcOpcode::DclOutputSgv:
|
||||
case DxbcOpcode::DclOutputSiv:
|
||||
return this->emitDclInterfaceReg(ins);
|
||||
|
||||
case DxbcOpcode::DclConstantBuffer:
|
||||
return this->emitDclConstantBuffer(ins);
|
||||
|
||||
case DxbcOpcode::DclSampler:
|
||||
return this->emitDclSampler(ins);
|
||||
|
||||
case DxbcOpcode::DclResource:
|
||||
return this->emitDclResource(ins);
|
||||
|
||||
case DxbcOpcode::Add:
|
||||
case DxbcOpcode::Div:
|
||||
case DxbcOpcode::Exp:
|
||||
case DxbcOpcode::Log:
|
||||
case DxbcOpcode::Mad:
|
||||
case DxbcOpcode::Max:
|
||||
case DxbcOpcode::Min:
|
||||
case DxbcOpcode::Mul:
|
||||
case DxbcOpcode::Mov:
|
||||
case DxbcOpcode::Rsq:
|
||||
case DxbcOpcode::Sqrt:
|
||||
case DxbcOpcode::IAdd:
|
||||
case DxbcOpcode::IMad:
|
||||
case DxbcOpcode::IMax:
|
||||
case DxbcOpcode::IMin:
|
||||
case DxbcOpcode::INeg:
|
||||
return this->emitVectorAlu(ins);
|
||||
|
||||
case DxbcOpcode::Movc:
|
||||
return this->emitVectorCmov(ins);
|
||||
|
||||
case DxbcOpcode::Eq:
|
||||
case DxbcOpcode::Ge:
|
||||
case DxbcOpcode::Lt:
|
||||
case DxbcOpcode::Ne:
|
||||
case DxbcOpcode::IEq:
|
||||
case DxbcOpcode::IGe:
|
||||
case DxbcOpcode::ILt:
|
||||
case DxbcOpcode::INe:
|
||||
return this->emitVectorCmp(ins);
|
||||
|
||||
case DxbcOpcode::Dp2:
|
||||
case DxbcOpcode::Dp3:
|
||||
case DxbcOpcode::Dp4:
|
||||
return this->emitVectorDot(ins);
|
||||
|
||||
case DxbcOpcode::IMul:
|
||||
return this->emitVectorImul(ins);
|
||||
|
||||
case DxbcOpcode::SinCos:
|
||||
return this->emitVectorSinCos(ins);
|
||||
|
||||
case DxbcOpcode::Sample:
|
||||
return this->emitSample(ins);
|
||||
|
||||
case DxbcOpcode::Ret:
|
||||
return this->emitRet(ins);
|
||||
|
||||
default:
|
||||
Logger::warn(
|
||||
str::format("DxbcCompiler: Unhandled opcode: ",
|
||||
ins.op));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rc<DxvkShader> DxbcCompiler2::finalize() {
|
||||
// Define the actual 'main' function of the shader
|
||||
m_module.functionBegin(
|
||||
m_module.defVoidType(),
|
||||
m_entryPointId,
|
||||
m_module.defFunctionType(
|
||||
m_module.defVoidType(), 0, nullptr),
|
||||
spv::FunctionControlMaskNone);
|
||||
m_module.opLabel(m_module.allocateId());
|
||||
|
||||
// Depending on the shader type, this will prepare
|
||||
// input registers, call various shader functions
|
||||
// and write back the output registers.
|
||||
switch (m_version.type()) {
|
||||
case DxbcProgramType::VertexShader: this->emitVsFinalize(); break;
|
||||
case DxbcProgramType::PixelShader: this->emitPsFinalize(); break;
|
||||
default: throw DxvkError("DxbcCompiler: Unsupported program type");
|
||||
}
|
||||
|
||||
// End main function
|
||||
m_module.opReturn();
|
||||
m_module.functionEnd();
|
||||
|
||||
// Declare the entry point, we now have all the
|
||||
// information we need, including the interfaces
|
||||
m_module.addEntryPoint(m_entryPointId,
|
||||
m_version.executionModel(), "main",
|
||||
m_entryPointInterfaces.size(),
|
||||
m_entryPointInterfaces.data());
|
||||
m_module.setDebugName(m_entryPointId, "main");
|
||||
|
||||
// Create the shader module object
|
||||
return new DxvkShader(
|
||||
m_version.shaderStage(),
|
||||
m_resourceSlots.size(),
|
||||
m_resourceSlots.data(),
|
||||
m_module.compile());
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitDclGlobalFlags(const DxbcShaderInstruction& ins) {
|
||||
// TODO implement properly
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitDclTemps(const DxbcShaderInstruction& ins) {
|
||||
// dcl_temps has one operand:
|
||||
// (imm0) Number of temp registers
|
||||
const uint32_t oldCount = m_rRegs.size();
|
||||
const uint32_t newCount = ins.imm[0].u32;
|
||||
|
||||
if (newCount > oldCount) {
|
||||
m_rRegs.resize(newCount);
|
||||
|
||||
DxbcRegisterInfo info;
|
||||
info.type.ctype = DxbcScalarType::Float32;
|
||||
info.type.ccount = 4;
|
||||
info.sclass = spv::StorageClassPrivate;
|
||||
|
||||
for (uint32_t i = oldCount; i < newCount; i++) {
|
||||
const uint32_t varId = this->emitNewVariable(info);
|
||||
m_module.setDebugName(varId, str::format("r", i).c_str());
|
||||
m_rRegs.at(i) = varId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitDclInterfaceReg(const DxbcShaderInstruction& ins) {
|
||||
// dcl_input and dcl_output instructions
|
||||
// have the following operands:
|
||||
// (dst0) The register to declare
|
||||
// (imm0) The system value (optional)
|
||||
uint32_t regDim = 0;
|
||||
uint32_t regIdx = 0;
|
||||
|
||||
// In the vertex and fragment shader stage, the
|
||||
// operand indices will have the following format:
|
||||
// (0) Register index
|
||||
//
|
||||
// In other stages, the input and output registers
|
||||
// may be declared as arrays of a fixed size:
|
||||
// (0) Array length
|
||||
// (1) Register index
|
||||
if (ins.dst[0].idxDim == 2) {
|
||||
regDim = ins.dst[0].idx[0].offset;
|
||||
regIdx = ins.dst[0].idx[1].offset;
|
||||
} else if (ins.dst[0].idxDim == 1) {
|
||||
regIdx = ins.dst[0].idx[0].offset;
|
||||
} else {
|
||||
Logger::err(str::format(
|
||||
"DxbcCompiler: ", ins.op,
|
||||
": Invalid index dimension"));
|
||||
return;
|
||||
}
|
||||
|
||||
// This declaration may map an output register to a system
|
||||
// value. If that is the case, the system value type will
|
||||
// be stored in the second operand.
|
||||
const bool hasSv =
|
||||
ins.op == DxbcOpcode::DclInputSgv
|
||||
|| ins.op == DxbcOpcode::DclInputSiv
|
||||
|| ins.op == DxbcOpcode::DclInputPsSgv
|
||||
|| ins.op == DxbcOpcode::DclInputPsSiv
|
||||
|| ins.op == DxbcOpcode::DclOutputSgv
|
||||
|| ins.op == DxbcOpcode::DclOutputSiv;
|
||||
|
||||
DxbcSystemValue sv = DxbcSystemValue::None;
|
||||
|
||||
if (hasSv)
|
||||
sv = static_cast<DxbcSystemValue>(ins.imm[0].u32);
|
||||
|
||||
// In the pixel shader, inputs are declared with an
|
||||
// interpolation mode that is part of the op token.
|
||||
const bool hasInterpolationMode =
|
||||
ins.op == DxbcOpcode::DclInputPs
|
||||
|| ins.op == DxbcOpcode::DclInputPsSiv;
|
||||
|
||||
DxbcInterpolationMode im = DxbcInterpolationMode::Undefined;
|
||||
|
||||
if (hasInterpolationMode)
|
||||
im = ins.controls.interpolation;
|
||||
|
||||
// Declare the actual input/output variable
|
||||
switch (ins.op) {
|
||||
case DxbcOpcode::DclInput:
|
||||
case DxbcOpcode::DclInputSgv:
|
||||
case DxbcOpcode::DclInputSiv:
|
||||
case DxbcOpcode::DclInputPs:
|
||||
case DxbcOpcode::DclInputPsSgv:
|
||||
case DxbcOpcode::DclInputPsSiv:
|
||||
this->emitDclInput(regIdx, regDim, ins.dst[0].mask, sv, im);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::DclOutput:
|
||||
case DxbcOpcode::DclOutputSgv:
|
||||
case DxbcOpcode::DclOutputSiv:
|
||||
this->emitDclOutput(regIdx, regDim, ins.dst[0].mask, sv, im);
|
||||
break;
|
||||
|
||||
default:
|
||||
Logger::err(str::format(
|
||||
"DxbcCompiler: Unexpected opcode: ",
|
||||
ins.op));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitDclInput(
|
||||
uint32_t regIdx,
|
||||
uint32_t regDim,
|
||||
DxbcRegMask regMask,
|
||||
DxbcSystemValue sv,
|
||||
DxbcInterpolationMode im) {
|
||||
if (regDim != 0) {
|
||||
Logger::err("DxbcCompiler: Input arrays not yet supported");
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid declaring the same variable multiple times.
|
||||
// This may happen when multiple system values are
|
||||
// mapped to different parts of the same register.
|
||||
if (m_vRegs.at(regIdx) == 0) {
|
||||
DxbcRegisterInfo info;
|
||||
info.type.ctype = DxbcScalarType::Float32;
|
||||
info.type.ccount = 4;
|
||||
info.sclass = spv::StorageClassInput;
|
||||
|
||||
const uint32_t varId = this->emitNewVariable(info);
|
||||
|
||||
m_module.decorateLocation(varId, regIdx);
|
||||
m_module.setDebugName(varId, str::format("v", regIdx).c_str());
|
||||
m_entryPointInterfaces.push_back(varId);
|
||||
|
||||
m_vRegs.at(regIdx) = varId;
|
||||
|
||||
// Interpolation mode, used in pixel shaders
|
||||
if (im == DxbcInterpolationMode::Constant)
|
||||
m_module.decorate(varId, spv::DecorationFlat);
|
||||
|
||||
if (im == DxbcInterpolationMode::LinearCentroid
|
||||
|| im == DxbcInterpolationMode::LinearNoPerspectiveCentroid)
|
||||
m_module.decorate(varId, spv::DecorationCentroid);
|
||||
|
||||
if (im == DxbcInterpolationMode::LinearNoPerspective
|
||||
|| im == DxbcInterpolationMode::LinearNoPerspectiveCentroid
|
||||
|| im == DxbcInterpolationMode::LinearNoPerspectiveSample)
|
||||
m_module.decorate(varId, spv::DecorationNoPerspective);
|
||||
|
||||
if (im == DxbcInterpolationMode::LinearSample
|
||||
|| im == DxbcInterpolationMode::LinearNoPerspectiveSample)
|
||||
m_module.decorate(varId, spv::DecorationSample);
|
||||
}
|
||||
|
||||
// Add a new system value mapping if needed
|
||||
// TODO declare SV if necessary
|
||||
if (sv != DxbcSystemValue::None)
|
||||
m_vMappings.push_back({ regIdx, regMask, sv });
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitDclOutput(
|
||||
uint32_t regIdx,
|
||||
uint32_t regDim,
|
||||
DxbcRegMask regMask,
|
||||
DxbcSystemValue sv,
|
||||
DxbcInterpolationMode im) {
|
||||
if (regDim != 0) {
|
||||
Logger::err("DxbcCompiler: Output arrays not yet supported");
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid declaring the same variable multiple times.
|
||||
// This may happen when multiple system values are
|
||||
// mapped to different parts of the same register.
|
||||
if (m_oRegs.at(regIdx) == 0) {
|
||||
DxbcRegisterInfo info;
|
||||
info.type.ctype = DxbcScalarType::Float32;
|
||||
info.type.ccount = 4;
|
||||
info.sclass = spv::StorageClassOutput;
|
||||
|
||||
const uint32_t varId = this->emitNewVariable(info);
|
||||
|
||||
m_module.decorateLocation(varId, regIdx);
|
||||
m_module.setDebugName(varId, str::format("o", regIdx).c_str());
|
||||
m_entryPointInterfaces.push_back(varId);
|
||||
|
||||
m_oRegs.at(regIdx) = varId;
|
||||
}
|
||||
|
||||
|
||||
// Add a new system value mapping if needed
|
||||
// TODO declare SV if necessary
|
||||
if (sv != DxbcSystemValue::None)
|
||||
m_oMappings.push_back({ regIdx, regMask, sv });
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitDclConstantBuffer(const DxbcShaderInstruction& ins) {
|
||||
// dcl_constant_buffer has one operand with two indices:
|
||||
// (0) Constant buffer register ID (cb#)
|
||||
// (1) Number of constants in the buffer
|
||||
const uint32_t bufferId = ins.dst[0].idx[0].offset;
|
||||
const uint32_t elementCount = ins.dst[0].idx[1].offset;
|
||||
|
||||
// Uniform buffer data is stored as a fixed-size array
|
||||
// of 4x32-bit vectors. SPIR-V requires explicit strides.
|
||||
const uint32_t arrayType = m_module.defArrayTypeUnique(
|
||||
getVectorTypeId({ DxbcScalarType::Float32, 4 }),
|
||||
m_module.constu32(elementCount));
|
||||
m_module.decorateArrayStride(arrayType, 16);
|
||||
|
||||
// SPIR-V requires us to put that array into a
|
||||
// struct and decorate that struct as a block.
|
||||
const uint32_t structType = m_module.defStructTypeUnique(1, &arrayType);
|
||||
m_module.memberDecorateOffset(structType, 0, 0);
|
||||
m_module.decorateBlock(structType);
|
||||
|
||||
// Variable that we'll use to access the buffer
|
||||
const uint32_t varId = m_module.newVar(
|
||||
m_module.defPointerType(structType, spv::StorageClassUniform),
|
||||
spv::StorageClassUniform);
|
||||
|
||||
m_module.setDebugName(varId,
|
||||
str::format("cb", bufferId).c_str());
|
||||
|
||||
m_constantBuffers.at(bufferId).varId = varId;
|
||||
m_constantBuffers.at(bufferId).size = elementCount;
|
||||
|
||||
// Compute the DXVK binding slot index for the buffer.
|
||||
// D3D11 needs to bind the actual buffers to this slot.
|
||||
const uint32_t bindingId = computeResourceSlotId(
|
||||
m_version.type(), DxbcBindingType::ConstantBuffer,
|
||||
bufferId);
|
||||
|
||||
m_module.decorateDescriptorSet(varId, 0);
|
||||
m_module.decorateBinding(varId, bindingId);
|
||||
|
||||
// Store descriptor info for the shader interface
|
||||
DxvkResourceSlot resource;
|
||||
resource.slot = bindingId;
|
||||
resource.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
m_resourceSlots.push_back(resource);
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitDclSampler(const DxbcShaderInstruction& ins) {
|
||||
// dclSampler takes one operand:
|
||||
// (dst0) The sampler register to declare
|
||||
// TODO implement sampler mode (default / comparison / mono)
|
||||
const uint32_t samplerId = ins.dst[0].idx[0].offset;
|
||||
|
||||
// The sampler type is opaque, but we still have to
|
||||
// define a pointer and a variable in oder to use it
|
||||
const uint32_t samplerType = m_module.defSamplerType();
|
||||
const uint32_t samplerPtrType = m_module.defPointerType(
|
||||
samplerType, spv::StorageClassUniformConstant);
|
||||
|
||||
// Define the sampler variable
|
||||
const uint32_t varId = m_module.newVar(samplerPtrType,
|
||||
spv::StorageClassUniformConstant);
|
||||
m_module.setDebugName(varId,
|
||||
str::format("s", samplerId).c_str());
|
||||
|
||||
m_samplers.at(samplerId).varId = varId;
|
||||
m_samplers.at(samplerId).typeId = samplerType;
|
||||
|
||||
// Compute binding slot index for the sampler
|
||||
const uint32_t bindingId = computeResourceSlotId(
|
||||
m_version.type(), DxbcBindingType::ImageSampler, samplerId);
|
||||
|
||||
m_module.decorateDescriptorSet(varId, 0);
|
||||
m_module.decorateBinding(varId, bindingId);
|
||||
|
||||
// Store descriptor info for the shader interface
|
||||
DxvkResourceSlot resource;
|
||||
resource.slot = bindingId;
|
||||
resource.type = VK_DESCRIPTOR_TYPE_SAMPLER;
|
||||
m_resourceSlots.push_back(resource);
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitDclResource(const DxbcShaderInstruction& ins) {
|
||||
// dclResource takes two operands:
|
||||
// (dst0) The resource register ID
|
||||
// (imm0) The resource return type
|
||||
const uint32_t registerId = ins.dst[0].idx[0].offset;
|
||||
|
||||
// Defines the type of the resource (texture2D, ...)
|
||||
const DxbcResourceDim resourceType = ins.controls.resourceDim;
|
||||
|
||||
// Defines the type of a read operation. DXBC has the ability
|
||||
// to define four different types whereas SPIR-V only allows
|
||||
// one, but in practice this should not be much of a problem.
|
||||
auto xType = static_cast<DxbcResourceReturnType>(
|
||||
bit::extract(ins.imm[0].u32, 0, 3));
|
||||
auto yType = static_cast<DxbcResourceReturnType>(
|
||||
bit::extract(ins.imm[0].u32, 4, 7));
|
||||
auto zType = static_cast<DxbcResourceReturnType>(
|
||||
bit::extract(ins.imm[0].u32, 8, 11));
|
||||
auto wType = static_cast<DxbcResourceReturnType>(
|
||||
bit::extract(ins.imm[0].u32, 12, 15));
|
||||
|
||||
if ((xType != yType) || (xType != zType) || (xType != wType))
|
||||
Logger::warn("DxbcCompiler: dcl_resource: Ignoring resource return types");
|
||||
|
||||
// Declare the actual sampled type
|
||||
uint32_t sampledTypeId = 0;
|
||||
|
||||
switch (xType) {
|
||||
case DxbcResourceReturnType::Float: sampledTypeId = m_module.defFloatType(32); break;
|
||||
case DxbcResourceReturnType::Sint: sampledTypeId = m_module.defIntType (32, 1); break;
|
||||
case DxbcResourceReturnType::Uint: sampledTypeId = m_module.defIntType (32, 0); break;
|
||||
default: throw DxvkError(str::format("DxbcCompiler: Invalid sampled type: ", xType));
|
||||
}
|
||||
|
||||
// Declare the resource type
|
||||
uint32_t textureTypeId = 0;
|
||||
|
||||
switch (resourceType) {
|
||||
case DxbcResourceDim::Texture1D:
|
||||
textureTypeId = m_module.defImageType(
|
||||
sampledTypeId, spv::Dim1D, 0, 0, 0, 1,
|
||||
spv::ImageFormatUnknown);
|
||||
break;
|
||||
|
||||
case DxbcResourceDim::Texture1DArr:
|
||||
textureTypeId = m_module.defImageType(
|
||||
sampledTypeId, spv::Dim1D, 0, 1, 0, 1,
|
||||
spv::ImageFormatUnknown);
|
||||
break;
|
||||
|
||||
case DxbcResourceDim::Texture2D:
|
||||
textureTypeId = m_module.defImageType(
|
||||
sampledTypeId, spv::Dim2D, 0, 0, 0, 1,
|
||||
spv::ImageFormatUnknown);
|
||||
break;
|
||||
|
||||
case DxbcResourceDim::Texture2DArr:
|
||||
textureTypeId = m_module.defImageType(
|
||||
sampledTypeId, spv::Dim2D, 0, 1, 0, 1,
|
||||
spv::ImageFormatUnknown);
|
||||
break;
|
||||
|
||||
case DxbcResourceDim::Texture3D:
|
||||
textureTypeId = m_module.defImageType(
|
||||
sampledTypeId, spv::Dim3D, 0, 0, 0, 1,
|
||||
spv::ImageFormatUnknown);
|
||||
break;
|
||||
|
||||
case DxbcResourceDim::TextureCube:
|
||||
textureTypeId = m_module.defImageType(
|
||||
sampledTypeId, spv::DimCube, 0, 0, 0, 1,
|
||||
spv::ImageFormatUnknown);
|
||||
break;
|
||||
|
||||
case DxbcResourceDim::TextureCubeArr:
|
||||
textureTypeId = m_module.defImageType(
|
||||
sampledTypeId, spv::DimCube, 0, 1, 0, 1,
|
||||
spv::ImageFormatUnknown);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw DxvkError(str::format("DxbcCompiler: Unsupported resource type: ", resourceType));
|
||||
}
|
||||
|
||||
const uint32_t resourcePtrType = m_module.defPointerType(
|
||||
textureTypeId, spv::StorageClassUniformConstant);
|
||||
|
||||
const uint32_t varId = m_module.newVar(resourcePtrType,
|
||||
spv::StorageClassUniformConstant);
|
||||
|
||||
m_module.setDebugName(varId,
|
||||
str::format("t", registerId).c_str());
|
||||
|
||||
m_textures.at(registerId).varId = varId;
|
||||
m_textures.at(registerId).sampledTypeId = sampledTypeId;
|
||||
m_textures.at(registerId).textureTypeId = textureTypeId;
|
||||
|
||||
// Compute the DXVK binding slot index for the resource.
|
||||
// D3D11 needs to bind the actual resource to this slot.
|
||||
const uint32_t bindingId = computeResourceSlotId(
|
||||
m_version.type(), DxbcBindingType::ShaderResource, registerId);
|
||||
|
||||
m_module.decorateDescriptorSet(varId, 0);
|
||||
m_module.decorateBinding(varId, bindingId);
|
||||
|
||||
// Store descriptor info for the shader interface
|
||||
DxvkResourceSlot resource;
|
||||
resource.slot = bindingId;
|
||||
resource.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
m_resourceSlots.push_back(resource);
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitVectorAlu(const DxbcShaderInstruction& ins) {
|
||||
std::array<DxbcRegisterValue, DxbcMaxOperandCount> src;
|
||||
|
||||
for (uint32_t i = 0; i < ins.srcCount; i++)
|
||||
src.at(i) = emitRegisterLoad(ins.src[i], ins.dst[0].mask);
|
||||
|
||||
DxbcRegisterValue dst;
|
||||
dst.type.ctype = ins.dst[0].dataType;
|
||||
dst.type.ccount = ins.dst[0].mask.setCount();
|
||||
|
||||
const uint32_t typeId = getVectorTypeId(dst.type);
|
||||
|
||||
switch (ins.op) {
|
||||
case DxbcOpcode::Add:
|
||||
dst.id = m_module.opFAdd(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Div:
|
||||
dst.id = m_module.opFDiv(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Exp:
|
||||
dst.id = m_module.opExp2(
|
||||
typeId, src.at(0).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Log:
|
||||
dst.id = m_module.opLog2(
|
||||
typeId, src.at(0).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Mad:
|
||||
dst.id = m_module.opFFma(typeId,
|
||||
src.at(0).id, src.at(1).id, src.at(2).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Max:
|
||||
dst.id = m_module.opFMax(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Min:
|
||||
dst.id = m_module.opFMin(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Mul:
|
||||
dst.id = m_module.opFMul(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Mov:
|
||||
dst.id = src.at(0).id;
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Sqrt:
|
||||
dst.id = m_module.opSqrt(
|
||||
typeId, src.at(0).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Rsq:
|
||||
dst.id = m_module.opInverseSqrt(
|
||||
typeId, src.at(0).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::IAdd:
|
||||
dst.id = m_module.opIAdd(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::IMad:
|
||||
dst.id = m_module.opIAdd(typeId,
|
||||
m_module.opIMul(typeId,
|
||||
src.at(0).id, src.at(1).id),
|
||||
src.at(2).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::IMax:
|
||||
dst.id = m_module.opSMax(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::IMin:
|
||||
dst.id = m_module.opSMin(typeId,
|
||||
src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::INeg:
|
||||
dst.id = m_module.opSNegate(
|
||||
typeId, src.at(0).id);
|
||||
break;
|
||||
|
||||
default:
|
||||
Logger::warn(str::format(
|
||||
"DxbcCompiler: Unhandled instruction: ",
|
||||
ins.op));
|
||||
return;
|
||||
}
|
||||
|
||||
// Store computed value
|
||||
dst = emitDstOperandModifiers(dst, ins.modifiers);
|
||||
emitRegisterStore(ins.dst[0], dst);
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitVectorCmov(const DxbcShaderInstruction& ins) {
|
||||
// movc has four operands:
|
||||
// (dst0) The destination register
|
||||
// (src0) The condition vector
|
||||
// (src0) Vector to select from if the condition is not 0
|
||||
// (src0) Vector to select from if the condition is 0
|
||||
const DxbcRegisterValue condition = emitRegisterLoad(ins.src[0], ins.dst[0].mask);
|
||||
const DxbcRegisterValue selectTrue = emitRegisterLoad(ins.src[1], ins.dst[0].mask);
|
||||
const DxbcRegisterValue selectFalse = emitRegisterLoad(ins.src[2], ins.dst[0].mask);
|
||||
|
||||
const uint32_t componentCount = ins.dst[0].mask.setCount();
|
||||
|
||||
// We'll compare against a vector of zeroes to generate a
|
||||
// boolean vector, which in turn will be used by OpSelect
|
||||
uint32_t zeroType = m_module.defIntType(32, 0);
|
||||
uint32_t boolType = m_module.defBoolType();
|
||||
|
||||
uint32_t zero = m_module.constu32(0);
|
||||
|
||||
if (componentCount > 1) {
|
||||
zeroType = m_module.defVectorType(zeroType, componentCount);
|
||||
boolType = m_module.defVectorType(boolType, componentCount);
|
||||
|
||||
const std::array<uint32_t, 4> zeroVec = { zero, zero, zero, zero };
|
||||
zero = m_module.constComposite(zeroType, componentCount, zeroVec.data());
|
||||
}
|
||||
|
||||
|
||||
// Use the component mask to select the vector components
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = ins.dst[0].dataType;
|
||||
result.type.ccount = componentCount;
|
||||
result.id = m_module.opSelect(
|
||||
getVectorTypeId(result.type),
|
||||
m_module.opINotEqual(boolType, condition.id, zero),
|
||||
selectTrue.id, selectFalse.id);
|
||||
|
||||
// Apply result modifiers to floating-point results
|
||||
result = emitDstOperandModifiers(result, ins.modifiers);
|
||||
emitRegisterStore(ins.dst[0], result);
|
||||
}
|
||||
|
||||
void DxbcCompiler2::emitVectorCmp(const DxbcShaderInstruction& ins) {
|
||||
// Compare instructions have three operands:
|
||||
// (dst0) The destination register
|
||||
// (src0) The first vector to compare
|
||||
// (src1) The second vector to compare
|
||||
const std::array<DxbcRegisterValue, 2> src = {
|
||||
emitRegisterLoad(ins.src[0], ins.dst[0].mask),
|
||||
emitRegisterLoad(ins.src[1], ins.dst[0].mask),
|
||||
};
|
||||
|
||||
const uint32_t componentCount = ins.dst[0].mask.setCount();
|
||||
|
||||
// Condition, which is a boolean vector used
|
||||
// to select between the ~0u and 0u vectors.
|
||||
uint32_t condition = 0;
|
||||
uint32_t conditionType = m_module.defBoolType();
|
||||
|
||||
if (componentCount > 1)
|
||||
conditionType = m_module.defVectorType(conditionType, componentCount);
|
||||
|
||||
switch (ins.op) {
|
||||
case DxbcOpcode::Eq:
|
||||
condition = m_module.opFOrdEqual(
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Ge:
|
||||
condition = m_module.opFOrdGreaterThanEqual(
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Lt:
|
||||
condition = m_module.opFOrdLessThan(
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::Ne:
|
||||
condition = m_module.opFOrdNotEqual(
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::IEq:
|
||||
condition = m_module.opIEqual(
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::IGe:
|
||||
condition = m_module.opSGreaterThanEqual(
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::ILt:
|
||||
condition = m_module.opSLessThan(
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
case DxbcOpcode::INe:
|
||||
condition = m_module.opINotEqual(
|
||||
conditionType, src.at(0).id, src.at(1).id);
|
||||
break;
|
||||
|
||||
default:
|
||||
Logger::warn(str::format(
|
||||
"DxbcCompiler: Unhandled instruction: ",
|
||||
ins.op));
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate constant vectors for selection
|
||||
uint32_t sFalse = m_module.constu32( 0u);
|
||||
uint32_t sTrue = m_module.constu32(~0u);
|
||||
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = DxbcScalarType::Uint32;
|
||||
result.type.ccount = componentCount;
|
||||
|
||||
const uint32_t typeId = getVectorTypeId(result.type);
|
||||
|
||||
if (componentCount > 1) {
|
||||
const std::array<uint32_t, 4> vFalse = { sFalse, sFalse, sFalse, sFalse };
|
||||
const std::array<uint32_t, 4> vTrue = { sTrue, sTrue, sTrue, sTrue };
|
||||
|
||||
sFalse = m_module.constComposite(typeId, componentCount, vFalse.data());
|
||||
sTrue = m_module.constComposite(typeId, componentCount, vTrue .data());
|
||||
}
|
||||
|
||||
// Perform component-wise mask selection
|
||||
// based on the condition evaluated above.
|
||||
result.id = m_module.opSelect(
|
||||
typeId, condition, sTrue, sFalse);
|
||||
|
||||
emitRegisterStore(ins.dst[0], result);
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitVectorDot(const DxbcShaderInstruction& ins) {
|
||||
const DxbcRegMask srcMask(true,
|
||||
ins.op >= DxbcOpcode::Dp2,
|
||||
ins.op >= DxbcOpcode::Dp3,
|
||||
ins.op >= DxbcOpcode::Dp4);
|
||||
|
||||
const std::array<DxbcRegisterValue, 2> src = {
|
||||
emitRegisterLoad(ins.src[0], srcMask),
|
||||
emitRegisterLoad(ins.src[1], srcMask),
|
||||
};
|
||||
|
||||
DxbcRegisterValue dst;
|
||||
dst.type.ctype = ins.dst[0].dataType;
|
||||
dst.type.ccount = 1;
|
||||
|
||||
dst.id = m_module.opDot(
|
||||
getVectorTypeId(dst.type),
|
||||
src.at(0).id,
|
||||
src.at(1).id);
|
||||
|
||||
dst = emitDstOperandModifiers(dst, ins.modifiers);
|
||||
emitRegisterStore(ins.dst[0], dst);
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitVectorImul(const DxbcShaderInstruction& ins) {
|
||||
// imul and umul have four operands:
|
||||
// (dst0) High destination register
|
||||
// (dst1) Low destination register
|
||||
// (src0) The first vector to compare
|
||||
// (src1) The second vector to compare
|
||||
if (ins.dst[0].type == DxbcOperandType::Null) {
|
||||
if (ins.dst[1].type == DxbcOperandType::Null)
|
||||
return;
|
||||
|
||||
// If dst0 is NULL, this instruction behaves just
|
||||
// like any other three -operand ALU instruction
|
||||
const std::array<DxbcRegisterValue, 2> src = {
|
||||
emitRegisterLoad(ins.src[0], ins.dst[1].mask),
|
||||
emitRegisterLoad(ins.src[1], ins.dst[1].mask),
|
||||
};
|
||||
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = ins.dst[1].dataType;
|
||||
result.type.ccount = ins.dst[1].mask.setCount();
|
||||
result.id = m_module.opIMul(
|
||||
getVectorTypeId(result.type),
|
||||
src.at(0).id, src.at(1).id);
|
||||
|
||||
result = emitDstOperandModifiers(result, ins.modifiers);
|
||||
emitRegisterStore(ins.dst[1], result);
|
||||
} else {
|
||||
// TODO implement this
|
||||
Logger::warn("DxbcCompiler: Extended Imul not yet supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitVectorSinCos(const DxbcShaderInstruction& ins) {
|
||||
// sincos has three operands:
|
||||
// (dst0) Destination register for sin(x)
|
||||
// (dst1) Destination register for cos(x)
|
||||
// (src0) Source operand x
|
||||
|
||||
// Load source operand as 32-bit float vector.
|
||||
const DxbcRegisterValue srcValue = emitRegisterLoad(
|
||||
ins.src[0], DxbcRegMask(true, true, true, true));
|
||||
|
||||
// Either output may be DxbcOperandType::Null, in
|
||||
// which case we don't have to generate any code.
|
||||
if (ins.dst[0].type != DxbcOperandType::Null) {
|
||||
const DxbcRegisterValue sinInput =
|
||||
emitRegisterExtract(srcValue, ins.dst[0].mask);
|
||||
|
||||
DxbcRegisterValue sin;
|
||||
sin.type = sinInput.type;
|
||||
sin.id = m_module.opSin(
|
||||
getVectorTypeId(sin.type),
|
||||
sinInput.id);
|
||||
|
||||
emitRegisterStore(ins.dst[0], sin);
|
||||
}
|
||||
|
||||
if (ins.dst[1].type != DxbcOperandType::Null) {
|
||||
const DxbcRegisterValue cosInput =
|
||||
emitRegisterExtract(srcValue, ins.dst[1].mask);
|
||||
|
||||
DxbcRegisterValue cos;
|
||||
cos.type = cosInput.type;
|
||||
cos.id = m_module.opSin(
|
||||
getVectorTypeId(cos.type),
|
||||
cosInput.id);
|
||||
|
||||
emitRegisterStore(ins.dst[1], cos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitSample(
|
||||
const DxbcShaderInstruction& ins) {
|
||||
// TODO support address offset
|
||||
// TODO support more sample ops
|
||||
|
||||
// sample has four operands:
|
||||
// (dst0) The destination register
|
||||
// (src0) Texture coordinates
|
||||
// (src1) The texture itself
|
||||
// (src2) The sampler object
|
||||
const DxbcRegister& texCoordReg = ins.src[0];
|
||||
const DxbcRegister& textureReg = ins.src[1];
|
||||
const DxbcRegister& samplerReg = ins.src[2];
|
||||
|
||||
// Texture and sampler register IDs
|
||||
const uint32_t textureId = textureReg.idx[0].offset;
|
||||
const uint32_t samplerId = samplerReg.idx[0].offset;
|
||||
|
||||
// Load the texture coordinates. SPIR-V allows these
|
||||
// to be float4 even if not all components are used.
|
||||
const DxbcRegisterValue coord = emitRegisterLoad(
|
||||
texCoordReg, DxbcRegMask(true, true, true, true));
|
||||
|
||||
// Combine the texture and the sampler into a sampled image
|
||||
const uint32_t sampledImageType = m_module.defSampledImageType(
|
||||
m_textures.at(textureId).textureTypeId);
|
||||
|
||||
const uint32_t sampledImageId = m_module.opSampledImage(
|
||||
sampledImageType,
|
||||
m_module.opLoad(
|
||||
m_textures.at(textureId).textureTypeId,
|
||||
m_textures.at(textureId).varId),
|
||||
m_module.opLoad(
|
||||
m_samplers.at(samplerId).typeId,
|
||||
m_samplers.at(samplerId).varId));
|
||||
|
||||
// Sampling an image in SPIR-V always returns a four-component
|
||||
// vector, so we need to declare the corresponding type here
|
||||
// TODO infer sampled type properly
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = DxbcScalarType::Float32;
|
||||
result.type.ccount = 4;
|
||||
result.id = m_module.opImageSampleImplicitLod(
|
||||
getVectorTypeId(result.type),
|
||||
sampledImageId, coord.id);
|
||||
|
||||
// Swizzle components using the texture swizzle
|
||||
// and the destination operand's write mask
|
||||
result = emitRegisterSwizzle(result,
|
||||
textureReg.swizzle, ins.dst[0].mask);
|
||||
|
||||
emitRegisterStore(ins.dst[0], result);
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitRet(const DxbcShaderInstruction& ins) {
|
||||
// TODO implement properly
|
||||
m_module.opReturn();
|
||||
m_module.functionEnd();
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler2::emitRegisterBitcast(
|
||||
DxbcRegisterValue srcValue,
|
||||
DxbcScalarType dstType) {
|
||||
if (srcValue.type.ctype == dstType)
|
||||
return srcValue;
|
||||
|
||||
// TODO support 64-bit values
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = dstType;
|
||||
result.type.ccount = srcValue.type.ccount;
|
||||
result.id = m_module.opBitcast(
|
||||
getVectorTypeId(result.type),
|
||||
srcValue.id);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler2::emitRegisterSwizzle(
|
||||
DxbcRegisterValue value,
|
||||
DxbcRegSwizzle swizzle,
|
||||
DxbcRegMask writeMask) {
|
||||
std::array<uint32_t, 4> indices;
|
||||
|
||||
uint32_t dstIndex = 0;
|
||||
|
||||
for (uint32_t i = 0; i < value.type.ccount; i++) {
|
||||
if (writeMask[i])
|
||||
indices[dstIndex++] = swizzle[i];
|
||||
}
|
||||
|
||||
// If the swizzle combined with the mask can be reduced
|
||||
// to a no-op, we don't need to insert any instructions.
|
||||
bool isIdentitySwizzle = dstIndex == value.type.ccount;
|
||||
|
||||
for (uint32_t i = 0; i < dstIndex && isIdentitySwizzle; i++)
|
||||
isIdentitySwizzle &= indices[i] == i;
|
||||
|
||||
if (isIdentitySwizzle)
|
||||
return value;
|
||||
|
||||
// Use OpCompositeExtract if the resulting vector contains
|
||||
// only one component, and OpVectorShuffle if it is a vector.
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = value.type.ctype;
|
||||
result.type.ccount = dstIndex;
|
||||
|
||||
const uint32_t typeId = getVectorTypeId(result.type);
|
||||
|
||||
if (dstIndex == 1) {
|
||||
result.id = m_module.opCompositeExtract(
|
||||
typeId, value.id, 1, indices.data());
|
||||
} else {
|
||||
result.id = m_module.opVectorShuffle(
|
||||
typeId, value.id, value.id,
|
||||
dstIndex, indices.data());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler2::emitRegisterExtract(
|
||||
DxbcRegisterValue value,
|
||||
DxbcRegMask mask) {
|
||||
return emitRegisterSwizzle(value,
|
||||
DxbcRegSwizzle(0, 1, 2, 3), mask);
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler2::emitRegisterInsert(
|
||||
DxbcRegisterValue dstValue,
|
||||
DxbcRegisterValue srcValue,
|
||||
DxbcRegMask srcMask) {
|
||||
DxbcRegisterValue result;
|
||||
result.type = dstValue.type;
|
||||
|
||||
const uint32_t typeId = getVectorTypeId(result.type);
|
||||
|
||||
if (srcMask.setCount() == 0) {
|
||||
// Nothing to do if the insertion mask is empty
|
||||
result.id = dstValue.id;
|
||||
} else if (dstValue.type.ccount == 1) {
|
||||
// Both values are scalar, so the first component
|
||||
// of the write mask decides which one to take.
|
||||
result.id = srcMask[0] ? srcValue.id : dstValue.id;
|
||||
} else if (srcValue.type.ccount == 1) {
|
||||
// The source value is scalar. Since OpVectorShuffle
|
||||
// requires both arguments to be vectors, we have to
|
||||
// use OpCompositeInsert to modify the vector instead.
|
||||
const uint32_t componentId = srcMask.firstSet();
|
||||
|
||||
result.id = m_module.opCompositeInsert(typeId,
|
||||
srcValue.id, dstValue.id, 1, &componentId);
|
||||
} else {
|
||||
// Both arguments are vectors. We can determine which
|
||||
// components to take from which vector and use the
|
||||
// OpVectorShuffle instruction.
|
||||
std::array<uint32_t, 4> components;
|
||||
uint32_t srcComponentId = dstValue.type.ccount;
|
||||
|
||||
for (uint32_t i = 0; i < dstValue.type.ccount; i++)
|
||||
components.at(i) = srcMask[i] ? srcComponentId++ : i;
|
||||
|
||||
result.id = m_module.opVectorShuffle(
|
||||
typeId, dstValue.id, srcValue.id,
|
||||
dstValue.type.ccount, components.data());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler2::emitRegisterExtend(
|
||||
DxbcRegisterValue value,
|
||||
uint32_t size) {
|
||||
if (size == 1)
|
||||
return value;
|
||||
|
||||
std::array<uint32_t, 4> ids = {
|
||||
value.id, value.id,
|
||||
value.id, value.id,
|
||||
};
|
||||
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = value.type.ctype;
|
||||
result.type.ccount = size;
|
||||
result.id = m_module.opCompositeConstruct(
|
||||
getVectorTypeId(result.type),
|
||||
size, ids.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler2::emitRegisterAbsolute(
|
||||
DxbcRegisterValue value) {
|
||||
const uint32_t typeId = getVectorTypeId(value.type);
|
||||
|
||||
switch (value.type.ctype) {
|
||||
case DxbcScalarType::Float32: value.id = m_module.opFAbs(typeId, value.id); break;
|
||||
case DxbcScalarType::Sint32: value.id = m_module.opSAbs(typeId, value.id); break;
|
||||
default: Logger::warn("DxbcCompiler: Cannot get absolute value for given type");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler2::emitRegisterNegate(
|
||||
DxbcRegisterValue value) {
|
||||
const uint32_t typeId = getVectorTypeId(value.type);
|
||||
|
||||
switch (value.type.ctype) {
|
||||
case DxbcScalarType::Float32: value.id = m_module.opFNegate(typeId, value.id); break;
|
||||
case DxbcScalarType::Sint32: value.id = m_module.opSNegate(typeId, value.id); break;
|
||||
default: Logger::warn("DxbcCompiler: Cannot negate given type");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler2::emitSrcOperandModifiers(
|
||||
DxbcRegisterValue value,
|
||||
DxbcRegModifiers modifiers) {
|
||||
if (modifiers.test(DxbcRegModifier::Abs))
|
||||
value = emitRegisterAbsolute(value);
|
||||
|
||||
if (modifiers.test(DxbcRegModifier::Neg))
|
||||
value = emitRegisterNegate(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler2::emitDstOperandModifiers(
|
||||
DxbcRegisterValue value,
|
||||
DxbcOpModifiers modifiers) {
|
||||
const uint32_t typeId = getVectorTypeId(value.type);
|
||||
|
||||
if (value.type.ctype == DxbcScalarType::Float32) {
|
||||
// Saturating only makes sense on floats
|
||||
if (modifiers.saturate) {
|
||||
value.id = m_module.opFClamp(
|
||||
typeId, value.id,
|
||||
m_module.constf32(0.0f),
|
||||
m_module.constf32(1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterPointer DxbcCompiler2::emitGetTempPtr(
|
||||
const DxbcRegister& operand) {
|
||||
// r# regs are indexed as follows:
|
||||
// (0) register index (immediate)
|
||||
DxbcRegisterPointer result;
|
||||
result.type.ctype = DxbcScalarType::Float32;
|
||||
result.type.ccount = 4;
|
||||
result.id = m_rRegs.at(operand.idx[0].offset);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterPointer DxbcCompiler2::emitGetInputPtr(
|
||||
const DxbcRegister& operand) {
|
||||
// In the vertex and pixel stages,
|
||||
// v# regs are indexed as follows:
|
||||
// (0) register index (relative)
|
||||
//
|
||||
// In the tessellation and geometry
|
||||
// stages, the index has two dimensions:
|
||||
// (0) vertex index (relative)
|
||||
// (1) register index (relative)
|
||||
if (operand.idxDim != 1)
|
||||
throw DxvkError("DxbcCompiler: 2D index for v# not yet supported");
|
||||
|
||||
// We don't support two-dimensional indices yet
|
||||
const uint32_t registerId = operand.idx[0].offset;
|
||||
|
||||
DxbcRegisterPointer result;
|
||||
result.type.ctype = DxbcScalarType::Float32;
|
||||
result.type.ccount = 4;
|
||||
result.id = m_vRegs.at(registerId);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterPointer DxbcCompiler2::emitGetOutputPtr(
|
||||
const DxbcRegister& operand) {
|
||||
// Same index format as input registers, except that
|
||||
// outputs cannot be accessed with a relative index.
|
||||
if (operand.idxDim != 1)
|
||||
throw DxvkError("DxbcCompiler: 2D index for o# not yet supported");
|
||||
|
||||
// We don't support two-dimensional indices yet
|
||||
const uint32_t registerId = operand.idx[0].offset;
|
||||
|
||||
// In the pixel shader, output registers are typed,
|
||||
// whereas they are float4 in all other stages.
|
||||
if (m_version.type() == DxbcProgramType::PixelShader) {
|
||||
DxbcRegisterPointer result;
|
||||
result.type = m_ps.oTypes.at(registerId);
|
||||
result.id = m_oRegs.at(registerId);
|
||||
return result;
|
||||
} else {
|
||||
DxbcRegisterPointer result;
|
||||
result.type.ctype = DxbcScalarType::Float32;
|
||||
result.type.ccount = 4;
|
||||
result.id = m_oRegs.at(registerId);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterPointer DxbcCompiler2::emitGetConstBufPtr(
|
||||
const DxbcRegister& operand) {
|
||||
// Constant buffers take a two-dimensional index:
|
||||
// (0) register index (immediate)
|
||||
// (1) constant offset (relative)
|
||||
DxbcRegisterInfo info;
|
||||
info.type.ctype = DxbcScalarType::Float32;
|
||||
info.type.ccount = 4;
|
||||
info.sclass = spv::StorageClassUniform;
|
||||
|
||||
const uint32_t regId = operand.idx[0].offset;
|
||||
const DxbcRegisterValue constId = emitIndexLoad(operand.idx[1]);
|
||||
|
||||
const uint32_t ptrTypeId = getPointerTypeId(info);
|
||||
|
||||
const std::array<uint32_t, 2> indices = {
|
||||
m_module.consti32(0), constId.id
|
||||
};
|
||||
|
||||
DxbcRegisterPointer result;
|
||||
result.type = info.type;
|
||||
result.id = m_module.opAccessChain(ptrTypeId,
|
||||
m_constantBuffers.at(regId).varId,
|
||||
indices.size(), indices.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterPointer DxbcCompiler2::emitGetOperandPtr(
|
||||
const DxbcRegister& operand) {
|
||||
switch (operand.type) {
|
||||
case DxbcOperandType::Temp:
|
||||
return emitGetTempPtr(operand);
|
||||
|
||||
case DxbcOperandType::Input:
|
||||
return emitGetInputPtr(operand);
|
||||
|
||||
case DxbcOperandType::Output:
|
||||
return emitGetOutputPtr(operand);
|
||||
|
||||
case DxbcOperandType::ConstantBuffer:
|
||||
return emitGetConstBufPtr(operand);
|
||||
|
||||
default:
|
||||
throw DxvkError(str::format(
|
||||
"DxbcCompiler: Unhandled operand type: ",
|
||||
operand.type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler2::emitIndexLoad(
|
||||
DxbcRegIndex index) {
|
||||
if (index.relReg != nullptr) {
|
||||
DxbcRegisterValue result = emitRegisterLoad(
|
||||
*index.relReg, DxbcRegMask(true, false, false, false));
|
||||
|
||||
if (index.offset != 0) {
|
||||
result.id = m_module.opIAdd(
|
||||
getVectorTypeId(result.type), result.id,
|
||||
m_module.consti32(index.offset));
|
||||
}
|
||||
|
||||
return result;
|
||||
} else {
|
||||
DxbcRegisterValue result;
|
||||
result.type.ctype = DxbcScalarType::Sint32;
|
||||
result.type.ccount = 1;
|
||||
result.id = m_module.consti32(index.offset);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler2::emitValueLoad(
|
||||
DxbcRegisterPointer ptr) {
|
||||
DxbcRegisterValue result;
|
||||
result.type = ptr.type;
|
||||
result.id = m_module.opLoad(
|
||||
getVectorTypeId(result.type),
|
||||
ptr.id);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitValueStore(
|
||||
DxbcRegisterPointer ptr,
|
||||
DxbcRegisterValue value,
|
||||
DxbcRegMask writeMask) {
|
||||
// If the component types are not compatible,
|
||||
// we need to bit-cast the source variable.
|
||||
if (value.type.ctype != ptr.type.ctype)
|
||||
value = emitRegisterBitcast(value, ptr.type.ctype);
|
||||
|
||||
// If the source value consists of only one component,
|
||||
// it is stored in all components of the destination.
|
||||
if (value.type.ccount == 1)
|
||||
value = emitRegisterExtend(value, writeMask.setCount());
|
||||
|
||||
if (ptr.type.ccount == writeMask.setCount()) {
|
||||
// Simple case: We write to the entire register
|
||||
m_module.opStore(ptr.id, value.id);
|
||||
} else {
|
||||
// We only write to part of the destination
|
||||
// register, so we need to load and modify it
|
||||
DxbcRegisterValue tmp = emitValueLoad(ptr);
|
||||
tmp = emitRegisterInsert(tmp, value, writeMask);
|
||||
|
||||
m_module.opStore(ptr.id, tmp.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxbcRegisterValue DxbcCompiler2::emitRegisterLoad(
|
||||
const DxbcRegister& reg,
|
||||
DxbcRegMask writeMask) {
|
||||
if (reg.type == DxbcOperandType::Imm32) {
|
||||
DxbcRegisterValue result;
|
||||
|
||||
if (reg.componentCount == DxbcRegComponentCount::c1) {
|
||||
// Create one single u32 constant
|
||||
result.type.ctype = DxbcScalarType::Uint32;
|
||||
result.type.ccount = 1;
|
||||
result.id = m_module.constu32(reg.imm.u32_1);
|
||||
} else if (reg.componentCount == DxbcRegComponentCount::c4) {
|
||||
// Create a four-component u32 vector
|
||||
std::array<uint32_t, 4> indices = {
|
||||
m_module.constu32(reg.imm.u32_4[0]),
|
||||
m_module.constu32(reg.imm.u32_4[1]),
|
||||
m_module.constu32(reg.imm.u32_4[2]),
|
||||
m_module.constu32(reg.imm.u32_4[3]),
|
||||
};
|
||||
|
||||
result.type.ctype = DxbcScalarType::Uint32;
|
||||
result.type.ccount = 4;
|
||||
result.id = m_module.constComposite(
|
||||
getVectorTypeId(result.type),
|
||||
indices.size(), indices.data());
|
||||
} else {
|
||||
// Something went horribly wrong in the decoder or the shader is broken
|
||||
throw DxvkError("DxbcCompiler: Invalid component count for immediate operand");
|
||||
}
|
||||
|
||||
// Cast constants to the requested type
|
||||
return emitRegisterBitcast(result, reg.dataType);
|
||||
} else {
|
||||
// Load operand from the operand pointer
|
||||
DxbcRegisterPointer ptr = emitGetOperandPtr(reg);
|
||||
DxbcRegisterValue result = emitValueLoad(ptr);
|
||||
|
||||
// Apply operand swizzle to the operand value
|
||||
result = emitRegisterSwizzle(result, reg.swizzle, writeMask);
|
||||
|
||||
// Cast it to the requested type. We need to do
|
||||
// this after the swizzling for 64-bit types.
|
||||
result = emitRegisterBitcast(result, reg.dataType);
|
||||
|
||||
// Apply operand modifiers
|
||||
result = emitSrcOperandModifiers(result, reg.modifiers);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitRegisterStore(
|
||||
const DxbcRegister& reg,
|
||||
DxbcRegisterValue value) {
|
||||
emitValueStore(emitGetOperandPtr(reg), value, reg.mask);
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitVsInputSetup() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitPsInputSetup() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitVsOutputSetup() {
|
||||
for (const DxbcSvMapping& svMapping : m_oMappings) {
|
||||
switch (svMapping.sv) {
|
||||
case DxbcSystemValue::Position: {
|
||||
DxbcRegisterInfo info;
|
||||
info.type.ctype = DxbcScalarType::Float32;
|
||||
info.type.ccount = 4;
|
||||
info.sclass = spv::StorageClassOutput;
|
||||
|
||||
const uint32_t ptrTypeId = getPointerTypeId(info);
|
||||
const uint32_t memberId = m_module.constu32(PerVertex_Position);
|
||||
|
||||
DxbcRegisterPointer dstPtr;
|
||||
dstPtr.type = info.type;
|
||||
dstPtr.id = m_module.opAccessChain(
|
||||
ptrTypeId, m_perVertexOut, 1, &memberId);
|
||||
|
||||
DxbcRegisterPointer srcPtr;
|
||||
srcPtr.type = info.type;
|
||||
srcPtr.id = m_oRegs.at(svMapping.regId);
|
||||
|
||||
emitValueStore(dstPtr, emitValueLoad(srcPtr),
|
||||
DxbcRegMask(true, true, true, true));
|
||||
} break;
|
||||
|
||||
default:
|
||||
Logger::warn(str::format(
|
||||
"dxbc: Unhandled vertex sv output: ",
|
||||
svMapping.sv));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitPsOutputSetup() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitVsInit() {
|
||||
m_module.enableCapability(spv::CapabilityShader);
|
||||
m_module.enableCapability(spv::CapabilityClipDistance);
|
||||
m_module.enableCapability(spv::CapabilityCullDistance);
|
||||
|
||||
// Declare the per-vertex output block. This is where
|
||||
// the vertex shader will write the vertex position.
|
||||
const uint32_t perVertexStruct = this->getPerVertexBlockId();
|
||||
const uint32_t perVertexPointer = m_module.defPointerType(
|
||||
perVertexStruct, spv::StorageClassOutput);
|
||||
|
||||
m_perVertexOut = m_module.newVar(
|
||||
perVertexPointer, spv::StorageClassOutput);
|
||||
m_entryPointInterfaces.push_back(m_perVertexOut);
|
||||
m_module.setDebugName(m_perVertexOut, "vs_vertex_out");
|
||||
|
||||
// Main function of the vertex shader
|
||||
m_vs.functionId = m_module.allocateId();
|
||||
m_module.setDebugName(m_vs.functionId, "vs_main");
|
||||
|
||||
m_module.functionBegin(
|
||||
m_module.defVoidType(),
|
||||
m_vs.functionId,
|
||||
m_module.defFunctionType(
|
||||
m_module.defVoidType(), 0, nullptr),
|
||||
spv::FunctionControlMaskNone);
|
||||
m_module.opLabel(m_module.allocateId());
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitPsInit() {
|
||||
m_module.enableCapability(spv::CapabilityShader);
|
||||
m_module.setOriginUpperLeft(m_entryPointId);
|
||||
|
||||
// Declare pixel shader outputs. According to the Vulkan
|
||||
// documentation, they are required to match the type of
|
||||
// the render target.
|
||||
for (auto e = m_osgn->begin(); e != m_osgn->end(); e++) {
|
||||
if (e->systemValue == DxbcSystemValue::None) {
|
||||
DxbcRegisterInfo info;
|
||||
info.type.ctype = e->componentType;
|
||||
info.type.ccount = e->componentMask.setCount();
|
||||
info.sclass = spv::StorageClassOutput;
|
||||
|
||||
const uint32_t varId = emitNewVariable(info);
|
||||
|
||||
m_module.decorateLocation(varId, e->registerId);
|
||||
m_module.setDebugName(varId, str::format("o", e->registerId).c_str());
|
||||
m_entryPointInterfaces.push_back(varId);
|
||||
|
||||
m_oRegs.at(e->registerId) = varId;
|
||||
m_ps.oTypes.at(e->registerId) = info.type;
|
||||
}
|
||||
}
|
||||
|
||||
// Main function of the pixel shader
|
||||
m_ps.functionId = m_module.allocateId();
|
||||
m_module.setDebugName(m_ps.functionId, "ps_main");
|
||||
|
||||
m_module.functionBegin(
|
||||
m_module.defVoidType(),
|
||||
m_ps.functionId,
|
||||
m_module.defFunctionType(
|
||||
m_module.defVoidType(), 0, nullptr),
|
||||
spv::FunctionControlMaskNone);
|
||||
m_module.opLabel(m_module.allocateId());
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitVsFinalize() {
|
||||
this->emitVsInputSetup();
|
||||
m_module.opFunctionCall(
|
||||
m_module.defVoidType(),
|
||||
m_vs.functionId, 0, nullptr);
|
||||
this->emitVsOutputSetup();
|
||||
}
|
||||
|
||||
|
||||
void DxbcCompiler2::emitPsFinalize() {
|
||||
this->emitPsInputSetup();
|
||||
m_module.opFunctionCall(
|
||||
m_module.defVoidType(),
|
||||
m_ps.functionId, 0, nullptr);
|
||||
this->emitPsOutputSetup();
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler2::emitNewVariable(const DxbcRegisterInfo& info) {
|
||||
const uint32_t ptrTypeId = this->getPointerTypeId(info);
|
||||
return m_module.newVar(ptrTypeId, info.sclass);
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler2::getScalarTypeId(DxbcScalarType type) {
|
||||
switch (type) {
|
||||
case DxbcScalarType::Uint32: return m_module.defIntType(32, 0);
|
||||
case DxbcScalarType::Uint64: return m_module.defIntType(64, 0);
|
||||
case DxbcScalarType::Sint32: return m_module.defIntType(32, 1);
|
||||
case DxbcScalarType::Sint64: return m_module.defIntType(64, 1);
|
||||
case DxbcScalarType::Float32: return m_module.defFloatType(32);
|
||||
case DxbcScalarType::Float64: return m_module.defFloatType(64);
|
||||
}
|
||||
|
||||
throw DxvkError("DxbcCompiler: Invalid scalar type");
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler2::getVectorTypeId(const DxbcVectorType& type) {
|
||||
uint32_t typeId = this->getScalarTypeId(type.ctype);
|
||||
|
||||
if (type.ccount > 1)
|
||||
typeId = m_module.defVectorType(typeId, type.ccount);
|
||||
|
||||
return typeId;
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler2::getPointerTypeId(const DxbcRegisterInfo& type) {
|
||||
return m_module.defPointerType(
|
||||
this->getVectorTypeId(type.type),
|
||||
type.sclass);
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcCompiler2::getPerVertexBlockId() {
|
||||
uint32_t t_f32 = m_module.defFloatType(32);
|
||||
uint32_t t_f32_v4 = m_module.defVectorType(t_f32, 4);
|
||||
uint32_t t_f32_a2 = m_module.defArrayType(t_f32, m_module.constu32(2));
|
||||
|
||||
std::array<uint32_t, 4> members;
|
||||
members[PerVertex_Position] = t_f32_v4;
|
||||
members[PerVertex_PointSize] = t_f32;
|
||||
members[PerVertex_CullDist] = t_f32_a2;
|
||||
members[PerVertex_ClipDist] = t_f32_a2;
|
||||
|
||||
uint32_t typeId = m_module.defStructTypeUnique(
|
||||
members.size(), members.data());
|
||||
|
||||
m_module.memberDecorateBuiltIn(typeId, PerVertex_Position, spv::BuiltInPosition);
|
||||
m_module.memberDecorateBuiltIn(typeId, PerVertex_PointSize, spv::BuiltInPointSize);
|
||||
m_module.memberDecorateBuiltIn(typeId, PerVertex_CullDist, spv::BuiltInCullDistance);
|
||||
m_module.memberDecorateBuiltIn(typeId, PerVertex_ClipDist, spv::BuiltInClipDistance);
|
||||
m_module.decorateBlock(typeId);
|
||||
|
||||
m_module.setDebugName(typeId, "per_vertex");
|
||||
m_module.setDebugMemberName(typeId, PerVertex_Position, "position");
|
||||
m_module.setDebugMemberName(typeId, PerVertex_PointSize, "point_size");
|
||||
m_module.setDebugMemberName(typeId, PerVertex_CullDist, "cull_dist");
|
||||
m_module.setDebugMemberName(typeId, PerVertex_ClipDist, "clip_dist");
|
||||
return typeId;
|
||||
}
|
||||
|
||||
}
|
@ -1,311 +0,0 @@
|
||||
#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();
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -2,208 +2,332 @@
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
DxbcCodeReader& DxbcCodeReader::operator ++ () {
|
||||
return this->operator += (1);
|
||||
uint32_t DxbcCodeSlice::at(uint32_t id) const {
|
||||
if (m_ptr + id >= m_end)
|
||||
throw DxvkError("DxbcCodeSlice: End of stream");
|
||||
return m_ptr[id];
|
||||
}
|
||||
|
||||
|
||||
DxbcCodeReader& DxbcCodeReader::operator += (uint32_t n) {
|
||||
if (n < m_size) {
|
||||
m_code += n;
|
||||
m_size -= n;
|
||||
} else {
|
||||
m_code = nullptr;
|
||||
m_size = 0;
|
||||
}
|
||||
return *this;
|
||||
uint32_t DxbcCodeSlice::read() {
|
||||
if (m_ptr >= m_end)
|
||||
throw DxvkError("DxbcCodeSlice: End of stream");
|
||||
return *(m_ptr++);
|
||||
}
|
||||
|
||||
|
||||
DxbcCodeReader DxbcCodeReader::operator + (uint32_t n) const {
|
||||
return n < m_size
|
||||
? DxbcCodeReader(m_code + n, m_size - n)
|
||||
: DxbcCodeReader();
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
bool DxbcCodeReader::operator == (const DxbcCodeReader& other) const {
|
||||
return m_code == other.m_code && m_size == other.m_size;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
bool DxbcCodeReader::operator != (const DxbcCodeReader& other) const {
|
||||
return !this->operator == (other);
|
||||
}
|
||||
|
||||
void DxbcDecodeContext::decodeInstruction(DxbcCodeSlice& code) {
|
||||
const uint32_t token0 = code.at(0);
|
||||
|
||||
uint32_t DxbcOperandIndex::length() const {
|
||||
switch (m_rep) {
|
||||
case DxbcOperandIndexRepresentation::Imm32: return 1;
|
||||
case DxbcOperandIndexRepresentation::Imm64: return 2;
|
||||
case DxbcOperandIndexRepresentation::Relative: return this->relPart().length();
|
||||
case DxbcOperandIndexRepresentation::Imm32Relative: return this->relPart().length() + 1;
|
||||
case DxbcOperandIndexRepresentation::Imm64Relative: return this->relPart().length() + 2;
|
||||
}
|
||||
// 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();
|
||||
|
||||
throw DxvkError(str::format("DXBC: Unknown index representation: ", m_rep));
|
||||
}
|
||||
// Reset the index pointer, which may still contain
|
||||
// a non-zero value from the previous iteration
|
||||
m_indexId = 0;
|
||||
|
||||
|
||||
bool DxbcOperandIndex::hasImmPart() const {
|
||||
return m_rep == DxbcOperandIndexRepresentation::Imm32
|
||||
|| m_rep == DxbcOperandIndexRepresentation::Imm64
|
||||
|| m_rep == DxbcOperandIndexRepresentation::Imm32Relative
|
||||
|| m_rep == DxbcOperandIndexRepresentation::Imm64Relative;
|
||||
}
|
||||
|
||||
|
||||
bool DxbcOperandIndex::hasRelPart() const {
|
||||
return m_rep == DxbcOperandIndexRepresentation::Relative
|
||||
|| m_rep == DxbcOperandIndexRepresentation::Imm32Relative
|
||||
|| m_rep == DxbcOperandIndexRepresentation::Imm64Relative;
|
||||
}
|
||||
|
||||
|
||||
uint64_t DxbcOperandIndex::immPart() const {
|
||||
switch (m_rep) {
|
||||
case DxbcOperandIndexRepresentation::Imm32:
|
||||
case DxbcOperandIndexRepresentation::Imm32Relative:
|
||||
return m_code.getWord(0);
|
||||
|
||||
case DxbcOperandIndexRepresentation::Imm64:
|
||||
case DxbcOperandIndexRepresentation::Imm64Relative:
|
||||
return (static_cast<uint64_t>(m_code.getWord(0)) << 32)
|
||||
| (static_cast<uint64_t>(m_code.getWord(1)));
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxbcOperand DxbcOperandIndex::relPart() const {
|
||||
switch (m_rep) {
|
||||
case DxbcOperandIndexRepresentation::Relative:
|
||||
return DxbcOperand(m_code);
|
||||
|
||||
case DxbcOperandIndexRepresentation::Imm32Relative:
|
||||
return DxbcOperand(m_code + 1);
|
||||
|
||||
case DxbcOperandIndexRepresentation::Imm64Relative:
|
||||
return DxbcOperand(m_code + 2);
|
||||
|
||||
default:
|
||||
throw DxvkError("DXBC: Operand index is not relative");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DxbcOperand::DxbcOperand(const DxbcCodeReader& code)
|
||||
: m_info(code) {
|
||||
const DxbcOperandToken token(m_info.getWord(0));
|
||||
|
||||
uint32_t numTokens = 1;
|
||||
|
||||
// Count extended operand tokens
|
||||
if (token.isExtended()) {
|
||||
while (DxbcOperandTokenExt(m_info.getWord(numTokens++)).isExtended())
|
||||
continue;
|
||||
}
|
||||
|
||||
m_data = m_info + numTokens;
|
||||
|
||||
// Immediate operands
|
||||
// Instruction length, in DWORDs. This includes the token
|
||||
// itself and any other prefix that an instruction may have.
|
||||
uint32_t length = 0;
|
||||
|
||||
if (token.type() == DxbcOperandType::Imm32
|
||||
|| token.type() == DxbcOperandType::Imm64)
|
||||
length += token.numComponents();
|
||||
|
||||
// Indices into the register file, may contain additional operands
|
||||
for (uint32_t i = 0; i < token.indexDimension(); i++) {
|
||||
m_indexOffsets[i] = length;
|
||||
length += this->index(i).length();
|
||||
}
|
||||
|
||||
m_length = length + numTokens;
|
||||
}
|
||||
|
||||
|
||||
DxbcOperandIndex DxbcOperand::index(uint32_t dim) const {
|
||||
return DxbcOperandIndex(
|
||||
m_data + m_indexOffsets.at(dim),
|
||||
this->token().indexRepresentation(dim));
|
||||
}
|
||||
|
||||
|
||||
bool DxbcOperand::queryOperandExt(DxbcOperandExt ext, DxbcOperandTokenExt& token) const {
|
||||
if (!this->token().isExtended())
|
||||
return false;
|
||||
|
||||
uint32_t extTokenId = 1;
|
||||
DxbcOperandTokenExt extToken;
|
||||
|
||||
do {
|
||||
extToken = m_info.getWord(extTokenId++);
|
||||
|
||||
if (extToken.type() == ext) {
|
||||
token = extToken;
|
||||
return true;
|
||||
}
|
||||
} while (extToken.isExtended());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
DxbcInstruction::DxbcInstruction(const DxbcCodeReader& code)
|
||||
: m_op(code) {
|
||||
DxbcOpcodeToken token(m_op.getWord(0));
|
||||
|
||||
if (token.opcode() == DxbcOpcode::CustomData) {
|
||||
// Custom data blocks have a special format,
|
||||
// the length is stored in a separate DWORD
|
||||
m_args = m_op + 2;
|
||||
if (m_instruction.op == DxbcOpcode::CustomData) {
|
||||
length = code.at(1);
|
||||
this->decodeCustomData(code.take(length));
|
||||
} else {
|
||||
// For normal instructions, we just count
|
||||
// the number of extended opcode tokens.
|
||||
uint32_t numOpcodeTokens = 1;
|
||||
|
||||
if (token.isExtended()) {
|
||||
numOpcodeTokens += 1;
|
||||
while (DxbcOpcodeTokenExt(m_op.getWord(numOpcodeTokens)).isExtended())
|
||||
numOpcodeTokens += 1;
|
||||
length = bit::extract(token0, 24, 30);
|
||||
this->decodeOperation(code.take(length));
|
||||
}
|
||||
|
||||
m_args = m_op + numOpcodeTokens;
|
||||
// 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<DxbcComponentCount>(bit::extract(token, 0, 1));
|
||||
|
||||
switch (reg.componentCount) {
|
||||
// No components - used for samplers etc.
|
||||
case DxbcComponentCount::Component0:
|
||||
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 DxbcComponentCount::Component1:
|
||||
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 DxbcComponentCount::Component4: {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t DxbcInstruction::length() const {
|
||||
auto token = this->token();
|
||||
return token.opcode() != DxbcOpcode::CustomData
|
||||
? token.length() : m_op.getWord(1);
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DxbcInstruction::queryOpcodeExt(DxbcExtOpcode extOpcode, DxbcOpcodeTokenExt& token) const {
|
||||
if (!this->token().isExtended())
|
||||
return false;
|
||||
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 DxbcComponentCount::Component1: {
|
||||
reg.imm.u32_1 = code.read();
|
||||
} break;
|
||||
|
||||
uint32_t extTokenId = 1;
|
||||
DxbcOpcodeTokenExt extToken;
|
||||
// Typical four-component vector
|
||||
case DxbcComponentCount::Component4: {
|
||||
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;
|
||||
|
||||
do {
|
||||
extToken = m_op.getWord(extTokenId++);
|
||||
|
||||
if (extToken.opcode() == extOpcode) {
|
||||
token = extToken;
|
||||
return true;
|
||||
default:
|
||||
Logger::warn("DxbcDecodeContext: Invalid component count for immediate operand");
|
||||
}
|
||||
} else if (reg.type == DxbcOperandType::Imm64) {
|
||||
Logger::warn("DxbcDecodeContext: 64-bit immediates not supported");
|
||||
}
|
||||
}
|
||||
} while (extToken.isExtended());
|
||||
|
||||
return false;
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
|
||||
#include "dxbc_common.h"
|
||||
#include "dxbc_decoder.h"
|
||||
#include "dxbc_defs.h"
|
||||
#include "dxbc_enums.h"
|
||||
#include "dxbc_names.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
class DxbcOperand;
|
||||
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<DxbcRegModifier>;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Constant buffer binding
|
||||
@ -121,671 +140,193 @@ namespace dxvk {
|
||||
};
|
||||
|
||||
|
||||
struct DxbcRegIndex {
|
||||
DxbcRegister* relReg;
|
||||
int32_t offset;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Basic control info
|
||||
* \brief Instruction operand
|
||||
*
|
||||
*
|
||||
* Parses instruction-specific control bits. Whether
|
||||
* these are well defined depends on the instruction.
|
||||
*/
|
||||
class DxbcOpcodeControl {
|
||||
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.
|
||||
*/
|
||||
struct DxbcShaderOpcodeControls {
|
||||
DxbcZeroTest zeroTest;
|
||||
DxbcSyncFlags syncFlags;
|
||||
DxbcResourceDim resourceDim;
|
||||
DxbcResinfoType resinfoType;
|
||||
DxbcInterpolationMode interpolation;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \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.
|
||||
*/
|
||||
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:
|
||||
|
||||
DxbcOpcodeControl() { }
|
||||
DxbcOpcodeControl(uint32_t control)
|
||||
: m_control(control) { }
|
||||
DxbcCodeSlice(
|
||||
const uint32_t* ptr,
|
||||
const uint32_t* end)
|
||||
: m_ptr(ptr), m_end(end) { }
|
||||
|
||||
/**
|
||||
* \brief Saturation hint
|
||||
*
|
||||
* If set, the result of the given instruction
|
||||
* is clamped to the [0..1] range.
|
||||
*/
|
||||
bool saturateBit() const {
|
||||
return bit::extract(m_control, 2, 2) != 0;
|
||||
}
|
||||
uint32_t at(uint32_t id) const;
|
||||
uint32_t read();
|
||||
|
||||
/**
|
||||
* \brief Precision hint
|
||||
*/
|
||||
bool preciseBit() const {
|
||||
return bit::extract(m_control, 8, 11) != 0;
|
||||
}
|
||||
DxbcCodeSlice take(uint32_t n) const;
|
||||
DxbcCodeSlice skip(uint32_t n) const;
|
||||
|
||||
/**
|
||||
* \brief Zero test
|
||||
*
|
||||
* For conditional instructions, this defines
|
||||
* whether the test shall pass when the given
|
||||
* operand is zero or non-zero.
|
||||
*/
|
||||
DxbcZeroTest zeroTest() const {
|
||||
return static_cast<DxbcZeroTest>(
|
||||
bit::extract(m_control, 7, 7));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Resinfo return type
|
||||
*
|
||||
* Control bits specifically for
|
||||
* the \c resinfo instruction.
|
||||
*/
|
||||
DxbcResinfoType resinfoType() const {
|
||||
return static_cast<DxbcResinfoType>(
|
||||
bit::extract(m_control, 0, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sync flags
|
||||
*
|
||||
* Defines the exact operation of sync
|
||||
* instructions in compute shaders.
|
||||
*/
|
||||
DxbcSyncFlags syncFlags() const {
|
||||
return bit::extract(m_control, 0, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Resource dimension
|
||||
*
|
||||
* Defines the type of a resource.
|
||||
* Only valid for dcl_resource etc.
|
||||
*/
|
||||
DxbcResourceDim resourceDim() const {
|
||||
return static_cast<DxbcResourceDim>(
|
||||
bit::extract(m_control, 0, 4));
|
||||
bool atEnd() const {
|
||||
return m_ptr == m_end;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_control = 0;
|
||||
const uint32_t* m_ptr = nullptr;
|
||||
const uint32_t* m_end = nullptr;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief DXBC instruction token
|
||||
* \brief Decode context
|
||||
*
|
||||
* Initial token at the beginning of each instruction.
|
||||
* This encodes the actual op code, the length of the
|
||||
* entire instruction in DWORDs, as well as some flags
|
||||
* controlling the specific instruction.
|
||||
* 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 DxbcOpcodeToken {
|
||||
class DxbcDecodeContext {
|
||||
|
||||
public:
|
||||
|
||||
DxbcOpcodeToken() { }
|
||||
DxbcOpcodeToken(uint32_t token)
|
||||
: m_token(token) { }
|
||||
|
||||
/**
|
||||
* \brief Opcode
|
||||
* \returns Opcode
|
||||
*/
|
||||
DxbcOpcode opcode() const {
|
||||
return static_cast<DxbcOpcode>(
|
||||
bit::extract(m_token, 0, 10));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Control info
|
||||
* \brief Retrieves current instruction
|
||||
*
|
||||
* Instruction-specific control info. Undefined
|
||||
* if the opcode is \c DxbcOpcode::CustomData.
|
||||
* \returns Control info
|
||||
* This is only valid after a call to \ref decode.
|
||||
* \returns Reference to last decoded instruction
|
||||
*/
|
||||
uint32_t control() const {
|
||||
return bit::extract(m_token, 11, 23);
|
||||
const DxbcShaderInstruction& getInstruction() const {
|
||||
return m_instruction;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Instruction length
|
||||
* \brief Decodes an instruction
|
||||
*
|
||||
* Undefind if the opcode is \c DxbcOpcode::CustomData.
|
||||
* In that case, the instruction length will be stored
|
||||
* in the DWORD immediately following the opcode token.
|
||||
* \returns Instruction length, in DWORDs
|
||||
* This also advances the given code slice by the
|
||||
* number of dwords consumed by the instruction.
|
||||
* \param [in] code Code slice
|
||||
*/
|
||||
uint32_t length() const {
|
||||
return bit::extract(m_token, 24, 30);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Checks whether the opcode is extended
|
||||
*
|
||||
* Additional information is encoded in extended
|
||||
* opcode tokens if this flag is set. Note that
|
||||
* multiple extended opcode tokens may be chained.
|
||||
* \returns \c true if the opcode is extended.
|
||||
*/
|
||||
bool isExtended() const {
|
||||
return !!bit::extract(m_token, 31, 31);
|
||||
}
|
||||
void decodeInstruction(DxbcCodeSlice& code);
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_token = 0;
|
||||
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;
|
||||
|
||||
/**
|
||||
* \brief DXBC extended instruction token
|
||||
*
|
||||
* Some instruction may encode additional control
|
||||
* modifiers in extended opcode tokens. The format
|
||||
* of these tokens differs from that of the the
|
||||
* initial opcode tokens.
|
||||
*/
|
||||
class DxbcOpcodeTokenExt {
|
||||
void decodeCustomData(DxbcCodeSlice code);
|
||||
void decodeOperation(DxbcCodeSlice code);
|
||||
|
||||
public:
|
||||
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);
|
||||
|
||||
DxbcOpcodeTokenExt() { }
|
||||
DxbcOpcodeTokenExt(uint32_t token)
|
||||
: m_token(token) { }
|
||||
void decodeRegister(DxbcCodeSlice& code, DxbcRegister& reg, DxbcScalarType type);
|
||||
void decodeImm32(DxbcCodeSlice& code, DxbcImmediate& imm, DxbcScalarType type);
|
||||
|
||||
/**
|
||||
* \brief Extended opcode
|
||||
* \returns Extended opcode
|
||||
*/
|
||||
DxbcExtOpcode opcode() const {
|
||||
return static_cast<DxbcExtOpcode>(
|
||||
bit::extract(m_token, 0, 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Control info
|
||||
*
|
||||
* Instruction-specific control info. Undefined
|
||||
* if the opcode is \c DxbcOpcode::CustomData.
|
||||
* \returns Control info
|
||||
*/
|
||||
uint32_t control() const {
|
||||
return bit::extract(m_token, 6, 30);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Extended opcode length
|
||||
*
|
||||
* Number of DWORDs that belong to this extended
|
||||
* opcode information. Currently, there are no
|
||||
* extended opcodes with a length greater than 1.
|
||||
* \returns Exteded opcode length, in DWORDs
|
||||
*/
|
||||
uint32_t length() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Checks whether there are additional modifiers
|
||||
* \returns \c true if the extended opcode is extended
|
||||
*/
|
||||
bool isExtended() const {
|
||||
return !!bit::extract(m_token, 31, 31);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_token = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Operand token
|
||||
*
|
||||
* Stores general information about one operand of an
|
||||
* instruction. Like opcode tokens, operand tokens may
|
||||
* be extended.
|
||||
*/
|
||||
class DxbcOperandToken {
|
||||
|
||||
public:
|
||||
|
||||
DxbcOperandToken(uint32_t token)
|
||||
: m_token(token) { }
|
||||
|
||||
/**
|
||||
* \brief Number of operand components
|
||||
*
|
||||
* The number of components that the operand
|
||||
* has. Can be zero, one, or four.
|
||||
* \returns Number of components
|
||||
*/
|
||||
uint32_t numComponents() const {
|
||||
std::array<uint32_t, 4> count = { 0, 1, 4, 0 };
|
||||
return count.at(bit::extract(m_token, 0, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Component selection mode
|
||||
*
|
||||
* Operands can be either masked so that some components
|
||||
* will not be used, or they can be swizzled so that only
|
||||
* a given set of components is used.
|
||||
* \returns Component selection mode
|
||||
*/
|
||||
DxbcRegMode selectionMode() const {
|
||||
return static_cast<DxbcRegMode>(
|
||||
bit::extract(m_token, 2, 3));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Component mask
|
||||
*
|
||||
* Used when the component selection
|
||||
* mode is \c DxbcRegMode::Mask.
|
||||
* \returns The component mask
|
||||
*/
|
||||
DxbcRegMask mask() const {
|
||||
return DxbcRegMask(bit::extract(m_token, 4, 7));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Component swizzle
|
||||
*
|
||||
* Used when the component selection
|
||||
* mode is \c DxbcRegMode::Swizzle.
|
||||
* \returns The component swizzle
|
||||
*/
|
||||
DxbcRegSwizzle swizzle() const {
|
||||
return DxbcRegSwizzle(
|
||||
bit::extract(m_token, 4, 5),
|
||||
bit::extract(m_token, 6, 7),
|
||||
bit::extract(m_token, 8, 9),
|
||||
bit::extract(m_token, 10, 11));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Single component selection
|
||||
*
|
||||
* Used when the component selection
|
||||
* mode is \c DxbcRegMode::Select1.
|
||||
* \returns The component index
|
||||
*/
|
||||
uint32_t select1() const {
|
||||
return bit::extract(m_token, 4, 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Operand type
|
||||
*
|
||||
* Selects the type of the operand, i.e. whether the
|
||||
* operand is a temporary register, a shader resource
|
||||
* or a builtin interface variable.
|
||||
* \returns Operand type
|
||||
*/
|
||||
DxbcOperandType type() const {
|
||||
return static_cast<DxbcOperandType>(
|
||||
bit::extract(m_token, 12, 19));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Index dimension
|
||||
*
|
||||
* Number of indices. In DXBC, each register file has
|
||||
* a dimensionality, e.g. the temporary registers are
|
||||
* one-dimensional and therefore require one index.
|
||||
* \returns Number of index dimensions
|
||||
*/
|
||||
uint32_t indexDimension() const {
|
||||
return bit::extract(m_token, 20, 21);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Index representation
|
||||
*
|
||||
* Stores whether an index is stored as an
|
||||
* immediate value or as a relative value
|
||||
* which requires another operand token.
|
||||
* \param [in] dim Index dimension to query
|
||||
* \returns Representation of that index
|
||||
*/
|
||||
DxbcOperandIndexRepresentation indexRepresentation(uint32_t dim) const {
|
||||
return static_cast<DxbcOperandIndexRepresentation>(
|
||||
bit::extract(m_token, 22 + 3 * dim, 24 + 3 * dim));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Checks whether the operand is extended
|
||||
*
|
||||
* Operands can be modified with extended tokens.
|
||||
* \returns \c true if the operand is extended
|
||||
*/
|
||||
bool isExtended() const {
|
||||
return !!bit::extract(m_token, 31, 31);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_token = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Extended operand token
|
||||
*
|
||||
* Stores additional properties for an operand that
|
||||
* cannot be stored in the initial operand token.
|
||||
*/
|
||||
class DxbcOperandTokenExt {
|
||||
|
||||
public:
|
||||
|
||||
DxbcOperandTokenExt() { }
|
||||
DxbcOperandTokenExt(uint32_t token)
|
||||
: m_token(token) { }
|
||||
|
||||
/**
|
||||
* \brief Operand extension type
|
||||
* \returns Operand extension type
|
||||
*/
|
||||
DxbcOperandExt type() const {
|
||||
return static_cast<DxbcOperandExt>(
|
||||
bit::extract(m_token, 0, 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Data flags
|
||||
* \returns Data flags
|
||||
*/
|
||||
uint32_t data() const {
|
||||
return bit::extract(m_token, 6, 30);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Checks whether the operand is extended
|
||||
*
|
||||
* Like extended opcode tokens, extended
|
||||
* operand tokens can be chained.
|
||||
* \returns \c true if extended
|
||||
*/
|
||||
bool isExtended() const {
|
||||
return !!bit::extract(m_token, 31, 31);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_token = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief DXBC code DxbcCodeReader
|
||||
*
|
||||
* Helper class that can read DWORDs from a sized slice.
|
||||
* Returns undefined numbers on out-of-bounds access, but
|
||||
* makes sure not to access memory locations outside the
|
||||
* original code array.
|
||||
*/
|
||||
class DxbcCodeReader {
|
||||
|
||||
public:
|
||||
|
||||
DxbcCodeReader() { }
|
||||
DxbcCodeReader(
|
||||
const uint32_t* code,
|
||||
uint32_t size)
|
||||
: m_code(size != 0 ? code : nullptr),
|
||||
m_size(size) { }
|
||||
|
||||
uint32_t getWord(uint32_t id) const {
|
||||
return id < m_size ? m_code[id] : 0;
|
||||
}
|
||||
|
||||
DxbcCodeReader& operator ++ ();
|
||||
DxbcCodeReader& operator += (uint32_t n);
|
||||
DxbcCodeReader operator + (uint32_t n) const;
|
||||
|
||||
bool operator == (const DxbcCodeReader& other) const;
|
||||
bool operator != (const DxbcCodeReader& other) const;
|
||||
|
||||
private:
|
||||
|
||||
const uint32_t* m_code = nullptr;
|
||||
uint32_t m_size = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief DXBC operand index
|
||||
*
|
||||
* Represents an index into an indexable register file.
|
||||
* For each register file dimension, one operand index
|
||||
* must be read from the encoded instruction.
|
||||
*/
|
||||
class DxbcOperandIndex {
|
||||
|
||||
public:
|
||||
|
||||
DxbcOperandIndex() { }
|
||||
DxbcOperandIndex(
|
||||
const DxbcCodeReader& code,
|
||||
DxbcOperandIndexRepresentation rep)
|
||||
: m_code(code), m_rep(rep) { }
|
||||
|
||||
uint32_t length() const;
|
||||
|
||||
bool hasImmPart() const;
|
||||
bool hasRelPart() const;
|
||||
|
||||
uint64_t immPart() const;
|
||||
DxbcOperand relPart() const;
|
||||
|
||||
private:
|
||||
|
||||
DxbcCodeReader m_code;
|
||||
DxbcOperandIndexRepresentation m_rep;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief DXBC operand
|
||||
*
|
||||
* Provides methods to query the operand token
|
||||
* including extended operand tokens, which may
|
||||
* modify the operand's return value.
|
||||
*/
|
||||
class DxbcOperand {
|
||||
|
||||
public:
|
||||
|
||||
DxbcOperand() { }
|
||||
DxbcOperand(const DxbcCodeReader& code);
|
||||
|
||||
/**
|
||||
* \brief Operand token
|
||||
* \returns Operand token
|
||||
*/
|
||||
DxbcOperandToken token() const {
|
||||
return DxbcOperandToken(m_info.getWord(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Operand length, in DWORDs
|
||||
* \returns Number of DWORDs
|
||||
*/
|
||||
uint32_t length() const {
|
||||
return m_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Operand index for a single dimension
|
||||
*
|
||||
* \param [in] dim Dimension to query
|
||||
* \returns Operand index
|
||||
*/
|
||||
DxbcOperandIndex index(uint32_t dim) const;
|
||||
|
||||
/**
|
||||
* \brief Queries an operand extension
|
||||
*
|
||||
* If an extended operand token with the given
|
||||
* operand extension exists, return that token.
|
||||
* \param [in] ext The operand extension
|
||||
* \param [out] token The extended token
|
||||
* \returns \c true if the token was defined
|
||||
*/
|
||||
bool queryOperandExt(
|
||||
DxbcOperandExt ext,
|
||||
DxbcOperandTokenExt& token) const;
|
||||
|
||||
/**
|
||||
* \brief Reads 32-bit immediate integer
|
||||
*
|
||||
* \param [in] idx Component index
|
||||
* \returns The immediate operand
|
||||
*/
|
||||
uint32_t imm32(uint32_t idx) const {
|
||||
return m_data.getWord(idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reads 64-bit immediate integer
|
||||
*
|
||||
* \param [in] idx Component index
|
||||
* \returns The immediate operand
|
||||
*/
|
||||
uint64_t imm64(uint32_t idx) const {
|
||||
uint64_t hi = m_data.getWord(2 * idx + 0);
|
||||
uint64_t lo = m_data.getWord(2 * idx + 1);
|
||||
return (hi << 32) | (lo);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
DxbcCodeReader m_info;
|
||||
DxbcCodeReader m_data;
|
||||
|
||||
uint32_t m_length = 0;
|
||||
std::array<uint32_t, 3> m_indexOffsets = { 0, 0, 0 };
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief DXBC instruction
|
||||
*
|
||||
* Provides methods to query the opcode token
|
||||
* including extended opcode tokens, as well
|
||||
* as convenience methods to read operands.
|
||||
*/
|
||||
class DxbcInstruction {
|
||||
|
||||
public:
|
||||
|
||||
DxbcInstruction() { }
|
||||
DxbcInstruction(const DxbcCodeReader& code);
|
||||
|
||||
/**
|
||||
* \brief Opcode token
|
||||
* \returns Opcode token
|
||||
*/
|
||||
DxbcOpcodeToken token() const {
|
||||
return DxbcOpcodeToken(m_op.getWord(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Instruction length, in DWORDs
|
||||
* \returns Instruction length, in DWORDs
|
||||
*/
|
||||
uint32_t length() const;
|
||||
|
||||
/**
|
||||
* \brief Queries an opcode extension
|
||||
*
|
||||
* If an extended opcode token with the given
|
||||
* opcode exists, the token will be returned.
|
||||
* \param [in] extOpcode Extended opcode
|
||||
* \param [out] token The extended token
|
||||
* \returns \c true if the token was defined
|
||||
*/
|
||||
bool queryOpcodeExt(
|
||||
DxbcExtOpcode extOpcode,
|
||||
DxbcOpcodeTokenExt& token) const;
|
||||
|
||||
/**
|
||||
* \brief Retrieves argument word
|
||||
*
|
||||
* Instruction arguments immediately follow the opcode
|
||||
* tokens, including the extended opcodes. Argument 0
|
||||
* will therefore be the first DWORD that is part of
|
||||
* an instruction operand or an immediate number.
|
||||
* \param [in] idx Argument word index
|
||||
* \returns The word at the given index
|
||||
*/
|
||||
uint32_t arg(uint32_t idx) const {
|
||||
return m_args.getWord(idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reads an argument enum
|
||||
*
|
||||
* Casts the word at the given location to an enum.
|
||||
* Some instructions take name tokens as operands.
|
||||
* \param [in] idx Argument word index
|
||||
* \returns The enum value of the given word
|
||||
*/
|
||||
template<typename T>
|
||||
T readEnum(uint32_t idx) const {
|
||||
return static_cast<T>(arg(idx));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Retrieves an operand
|
||||
*
|
||||
* \param [in] idx Argument word index
|
||||
* \returns The operand object
|
||||
*/
|
||||
DxbcOperand operand(uint32_t idx) const {
|
||||
return DxbcOperand(m_args + idx);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
DxbcCodeReader m_op;
|
||||
DxbcCodeReader m_args;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief DXBC instruction decoder
|
||||
*
|
||||
* 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 DxbcDecoder {
|
||||
|
||||
public:
|
||||
|
||||
DxbcDecoder() { }
|
||||
DxbcDecoder(const uint32_t* code, uint32_t size)
|
||||
: m_code(code, size) { }
|
||||
|
||||
DxbcDecoder& operator ++ () {
|
||||
m_code += DxbcInstruction(m_code).length();
|
||||
return *this;
|
||||
}
|
||||
|
||||
DxbcInstruction operator * () const {
|
||||
return DxbcInstruction(m_code);
|
||||
}
|
||||
|
||||
bool operator == (const DxbcDecoder& other) const { return m_code == other.m_code; }
|
||||
bool operator != (const DxbcDecoder& other) const { return m_code != other.m_code; }
|
||||
|
||||
private:
|
||||
|
||||
DxbcCodeReader m_code;
|
||||
void decodeOperand(DxbcCodeSlice& code, const DxbcInstOperandFormat& format);
|
||||
|
||||
};
|
||||
|
||||
|
@ -1,333 +0,0 @@
|
||||
#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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,192 +0,0 @@
|
||||
#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);
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -436,14 +436,6 @@ namespace dxvk {
|
||||
|
||||
using DxbcGlobalFlags = Flags<DxbcGlobalFlag>;
|
||||
|
||||
|
||||
enum class DxbcOperandModifier : uint32_t {
|
||||
Neg = 0,
|
||||
Abs = 1,
|
||||
};
|
||||
|
||||
using DxbcOperandModifiers = Flags<DxbcOperandModifier>;
|
||||
|
||||
enum class DxbcZeroTest : uint32_t {
|
||||
TestZ = 0,
|
||||
TestNz = 1,
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "dxbc_compiler.h"
|
||||
#include "dxbc_compiler_2.h"
|
||||
#include "dxbc_module.h"
|
||||
|
||||
namespace dxvk {
|
||||
@ -47,7 +46,7 @@ namespace dxvk {
|
||||
|
||||
DxbcCodeSlice slice = m_shexChunk->slice();
|
||||
|
||||
DxbcCompiler2 compiler(
|
||||
DxbcCompiler compiler(
|
||||
m_shexChunk->version(),
|
||||
m_isgnChunk, m_osgnChunk);
|
||||
|
||||
|
@ -3,10 +3,8 @@ 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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user