2017-10-16 17:50:09 +02:00
|
|
|
#include "dxbc_compiler.h"
|
|
|
|
|
|
|
|
namespace dxvk {
|
|
|
|
|
2017-11-01 00:01:40 +01:00
|
|
|
DxbcCompiler::DxbcCompiler(
|
2017-11-13 00:22:52 +01:00
|
|
|
const DxbcProgramVersion& version)
|
|
|
|
: m_gen(DxbcCodeGen::create(version)) { }
|
2017-10-16 17:50:09 +02:00
|
|
|
|
|
|
|
|
|
|
|
DxbcCompiler::~DxbcCompiler() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-01 00:01:40 +01:00
|
|
|
void DxbcCompiler::processInstruction(const DxbcInstruction& ins) {
|
2017-11-13 00:22:52 +01:00
|
|
|
const DxbcOpcodeToken token = ins.token();
|
2017-10-22 23:13:29 +02:00
|
|
|
|
2017-11-13 00:22:52 +01:00
|
|
|
switch (token.opcode()) {
|
|
|
|
case DxbcOpcode::DclGlobalFlags:
|
|
|
|
return this->dclGlobalFlags(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:
|
2017-11-13 02:07:13 +01:00
|
|
|
return this->dclInterfaceVar(ins);
|
2017-11-13 00:22:52 +01:00
|
|
|
|
|
|
|
case DxbcOpcode::DclTemps:
|
|
|
|
return this->dclTemps(ins);
|
|
|
|
|
2017-11-17 11:41:56 +01:00
|
|
|
case DxbcOpcode::Add:
|
|
|
|
return this->opAdd(ins);
|
|
|
|
|
2017-11-16 01:30:17 +01:00
|
|
|
case DxbcOpcode::Mov:
|
|
|
|
return this->opMov(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::Ret:
|
|
|
|
return this->opRet(ins);
|
|
|
|
|
2017-11-13 00:22:52 +01:00
|
|
|
default:
|
|
|
|
Logger::err(str::format(
|
|
|
|
"DxbcCompiler::processInstruction: Unhandled opcode: ",
|
|
|
|
token.opcode()));
|
|
|
|
}
|
2017-10-16 17:50:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-20 14:03:00 +01:00
|
|
|
SpirvCodeBuffer DxbcCompiler::finalize() {
|
2017-11-13 00:22:52 +01:00
|
|
|
return m_gen->finalize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::dclGlobalFlags(const DxbcInstruction& ins) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-13 02:07:13 +01:00
|
|
|
void DxbcCompiler::dclInterfaceVar(const DxbcInstruction& ins) {
|
2017-11-16 01:30:17 +01:00
|
|
|
auto op = ins.operand(0);
|
|
|
|
auto opcode = ins.token().opcode();
|
2017-11-13 00:22:52 +01:00
|
|
|
|
2017-11-16 01:30:17 +01:00
|
|
|
switch (op.token().type()) {
|
|
|
|
case DxbcOperandType::Input:
|
|
|
|
case DxbcOperandType::Output: {
|
|
|
|
uint32_t regId = 0;
|
|
|
|
uint32_t regDim = 0;
|
|
|
|
|
|
|
|
if (op.token().indexDimension() == 1) {
|
|
|
|
regId = op.index(0).immPart();
|
|
|
|
} else if (op.token().indexDimension() == 2) {
|
|
|
|
regDim = op.index(0).immPart();
|
|
|
|
regId = op.index(1).immPart();
|
|
|
|
} else {
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler::dclInterfaceVar: Invalid index dimension: ",
|
|
|
|
op.token().indexDimension()));
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool hasSv =
|
|
|
|
opcode == DxbcOpcode::DclInputSgv
|
|
|
|
|| opcode == DxbcOpcode::DclInputSiv
|
|
|
|
|| opcode == DxbcOpcode::DclInputPsSgv
|
|
|
|
|| opcode == DxbcOpcode::DclInputPsSiv
|
|
|
|
|| opcode == DxbcOpcode::DclOutputSgv
|
|
|
|
|| opcode == DxbcOpcode::DclOutputSiv;
|
|
|
|
|
|
|
|
DxbcSystemValue sv = DxbcSystemValue::None;
|
|
|
|
|
|
|
|
if (hasSv)
|
|
|
|
sv = ins.readEnum<DxbcSystemValue>(op.length());
|
|
|
|
|
|
|
|
m_gen->dclInterfaceVar(
|
|
|
|
op.token().type(), regId, regDim,
|
|
|
|
op.token().componentMask(), sv);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler::dclInterfaceVar: Unhandled operand type: ",
|
|
|
|
op.token().type()));
|
|
|
|
}
|
2017-11-13 00:22:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::dclTemps(const DxbcInstruction& ins) {
|
|
|
|
m_gen->dclTemps(ins.arg(0));
|
2017-10-16 19:53:17 +02:00
|
|
|
}
|
|
|
|
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-11-17 11:41:56 +01:00
|
|
|
void DxbcCompiler::opAdd(const DxbcInstruction& ins) {
|
|
|
|
auto dstOp = ins.operand(0);
|
|
|
|
auto srcOp1 = ins.operand(dstOp.length());
|
|
|
|
auto srcOp2 = ins.operand(dstOp.length() + srcOp1.length());
|
|
|
|
DxbcComponentMask mask = this->getDstOperandMask(dstOp);
|
|
|
|
|
|
|
|
DxbcValue src1 = this->loadOperand(srcOp1, mask, DxbcScalarType::Float32);
|
|
|
|
DxbcValue src2 = this->loadOperand(srcOp2, mask, DxbcScalarType::Float32);
|
|
|
|
DxbcValue val = m_gen->opAdd(src1, src2);
|
|
|
|
val = this->applyResultModifiers(val, ins.token().control());
|
|
|
|
this->storeOperand(dstOp, val, mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-16 01:30:17 +01:00
|
|
|
void DxbcCompiler::opMov(const DxbcInstruction& ins) {
|
|
|
|
auto dstOp = ins.operand(0);
|
|
|
|
auto srcOp = ins.operand(dstOp.length());
|
|
|
|
DxbcComponentMask mask = this->getDstOperandMask(dstOp);
|
|
|
|
|
|
|
|
DxbcValue value = this->loadOperand(srcOp, mask, DxbcScalarType::Float32);
|
2017-11-17 11:41:56 +01:00
|
|
|
value = this->applyResultModifiers(value, ins.token().control());
|
2017-11-16 01:30:17 +01:00
|
|
|
this->storeOperand(dstOp, value, mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::opRet(const DxbcInstruction& ins) {
|
|
|
|
m_gen->fnReturn();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcComponentMask DxbcCompiler::getDstOperandMask(const DxbcOperand& operand) {
|
|
|
|
const DxbcOperandToken token = operand.token();
|
|
|
|
|
|
|
|
if (token.numComponents() == 1) {
|
|
|
|
return DxbcComponentMask(true, false, false, false);
|
|
|
|
} else if (token.numComponents() == 4) {
|
|
|
|
switch (token.selectionMode()) {
|
|
|
|
case DxbcComponentSelectionMode::Mask:
|
|
|
|
return token.componentMask();
|
|
|
|
|
|
|
|
case DxbcComponentSelectionMode::Select1:
|
|
|
|
return token.componentSelection();
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler::getDstOperandMask: Invalid component selection mode: ",
|
|
|
|
token.selectionMode()));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler::getDstOperandMask: Invalid component count: ",
|
|
|
|
token.numComponents()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcPointer DxbcCompiler::getTempOperandPtr(const DxbcOperand& operand) {
|
|
|
|
if (operand.token().indexDimension() != 1) {
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler::getTempOperandPtr: Invalid index dimension: ",
|
|
|
|
operand.token().indexDimension()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (operand.token().indexRepresentation(0) != DxbcOperandIndexRepresentation::Imm32) {
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler::getTempOperandPtr: Invalid index representation: ",
|
|
|
|
operand.token().indexRepresentation(0)));
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_gen->ptrTempReg(operand.index(0).immPart());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcPointer DxbcCompiler::getInterfaceOperandPtr(const DxbcOperand& operand) {
|
|
|
|
const uint32_t indexDim = operand.token().indexDimension();
|
|
|
|
|
|
|
|
// Vertex index ID is unused if the index dimension
|
|
|
|
// is 1. The element index is always the last index.
|
|
|
|
// const uint32_t vIndexId = 0;
|
|
|
|
const uint32_t eIndexId = indexDim - 1;
|
|
|
|
|
|
|
|
if (operand.token().indexRepresentation(eIndexId) != DxbcOperandIndexRepresentation::Imm32) {
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler::getInterfaceOperandPtr: Invalid element index representation: ",
|
|
|
|
operand.token().indexRepresentation(eIndexId)));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (indexDim == 1) {
|
|
|
|
return m_gen->ptrInterfaceVar(
|
|
|
|
operand.token().type(),
|
|
|
|
operand.index(eIndexId).immPart());
|
|
|
|
} else {
|
|
|
|
// TODO implement index dimension 2
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler::getInterfaceOperandPtr: Invalid index dimension: ",
|
|
|
|
indexDim));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcPointer DxbcCompiler::getOperandPtr(const DxbcOperand& operand) {
|
|
|
|
switch (operand.token().type()) {
|
|
|
|
case DxbcOperandType::Temp:
|
|
|
|
return this->getTempOperandPtr(operand);
|
|
|
|
|
|
|
|
case DxbcOperandType::Input:
|
|
|
|
case DxbcOperandType::Output:
|
|
|
|
return this->getInterfaceOperandPtr(operand);
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler::getOperandPtr: Unhandled operand type: ",
|
|
|
|
operand.token().type()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcValue DxbcCompiler::selectOperandComponents(
|
|
|
|
const DxbcOperandToken& opToken,
|
|
|
|
const DxbcValue& opValue,
|
|
|
|
DxbcComponentMask dstMask) {
|
|
|
|
// Four-component source operands can provide either a
|
|
|
|
// swizzle to select multiple components, or a component
|
|
|
|
// index that is used to select one single component.
|
|
|
|
switch (opToken.selectionMode()) {
|
|
|
|
case DxbcComponentSelectionMode::Swizzle:
|
|
|
|
return m_gen->regSwizzle(opValue,
|
|
|
|
opToken.componentSwizzle(), dstMask);
|
|
|
|
|
|
|
|
case DxbcComponentSelectionMode::Select1:
|
|
|
|
return m_gen->regExtract(opValue,
|
|
|
|
opToken.componentSelection());
|
|
|
|
|
2017-11-16 02:07:10 +01:00
|
|
|
case DxbcComponentSelectionMode::Mask:
|
|
|
|
return m_gen->regExtract(opValue,
|
|
|
|
opToken.componentMask());
|
|
|
|
|
2017-11-16 01:30:17 +01:00
|
|
|
default:
|
|
|
|
throw DxvkError(str::format(
|
2017-11-16 02:07:10 +01:00
|
|
|
"DxbcCompiler::selectOperandComponents: Invalid selection mode: ",
|
2017-11-16 01:30:17 +01:00
|
|
|
opToken.selectionMode()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcValue DxbcCompiler::applyOperandModifiers(
|
|
|
|
DxbcValue value,
|
|
|
|
DxbcOperandModifiers modifiers) {
|
|
|
|
if (modifiers.test(DxbcOperandModifier::Abs))
|
|
|
|
value = m_gen->opAbs(value);
|
|
|
|
|
|
|
|
if (modifiers.test(DxbcOperandModifier::Neg))
|
|
|
|
value = m_gen->opNeg(value);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-17 11:41:56 +01:00
|
|
|
DxbcValue DxbcCompiler::applyResultModifiers(
|
|
|
|
DxbcValue value,
|
|
|
|
DxbcOpcodeControl control) {
|
|
|
|
if (control.saturateBit())
|
|
|
|
value = m_gen->opSaturate(value);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-16 01:30:17 +01:00
|
|
|
DxbcValue DxbcCompiler::loadOperand(
|
|
|
|
const DxbcOperand& operand,
|
|
|
|
DxbcComponentMask dstMask,
|
|
|
|
DxbcScalarType dstType) {
|
|
|
|
const DxbcOperandToken token = operand.token();
|
|
|
|
|
|
|
|
DxbcValue result;
|
|
|
|
|
2017-11-16 02:07:10 +01:00
|
|
|
if (token.type() == DxbcOperandType::Imm32) {
|
|
|
|
if (token.numComponents() == 1) {
|
|
|
|
result = m_gen->defConstScalar(operand.imm32(0));
|
|
|
|
} else if (token.numComponents() == 4) {
|
|
|
|
result = m_gen->defConstVector(
|
|
|
|
operand.imm32(0), operand.imm32(1),
|
|
|
|
operand.imm32(2), operand.imm32(3));
|
2017-11-17 11:41:56 +01:00
|
|
|
result = m_gen->regExtract(result, dstMask);
|
2017-11-16 02:07:10 +01:00
|
|
|
} else {
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler::loadOperand [imm32]: Invalid number of components: ",
|
|
|
|
token.numComponents()));
|
|
|
|
}
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-11-17 11:41:56 +01:00
|
|
|
result = m_gen->regCast(result, DxbcValueType(
|
|
|
|
dstType, result.type.componentCount));
|
2017-11-16 02:07:10 +01:00
|
|
|
} else {
|
|
|
|
result = m_gen->regLoad(this->getOperandPtr(operand));
|
|
|
|
|
2017-11-17 11:41:56 +01:00
|
|
|
// Cast register to requested type
|
|
|
|
result = m_gen->regCast(result, DxbcValueType(
|
|
|
|
dstType, result.type.componentCount));
|
|
|
|
|
2017-11-16 02:07:10 +01:00
|
|
|
// Apply the source operand swizzle
|
|
|
|
if (token.numComponents() == 4)
|
|
|
|
result = this->selectOperandComponents(token, result, dstMask);
|
|
|
|
|
|
|
|
// Apply source operand modifiers, if any
|
|
|
|
auto operandModifiers = operand.queryOperandExt(
|
|
|
|
DxbcOperandExt::OperandModifier);
|
|
|
|
|
|
|
|
if (operandModifiers) {
|
|
|
|
result = this->applyOperandModifiers(
|
|
|
|
result, operandModifiers->data());
|
|
|
|
}
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::storeOperand(
|
|
|
|
const DxbcOperand& operand,
|
|
|
|
DxbcValue value,
|
|
|
|
DxbcComponentMask mask) {
|
|
|
|
const DxbcPointer ptr = this->getOperandPtr(operand);
|
|
|
|
|
|
|
|
// Cast source value to destination register type.
|
|
|
|
// TODO verify that this actually works as intended.
|
|
|
|
DxbcValueType dstType;
|
|
|
|
dstType.componentType = ptr.type.valueType.componentType;
|
|
|
|
dstType.componentCount = mask.componentCount();
|
|
|
|
value = m_gen->regCast(value, dstType);
|
|
|
|
|
|
|
|
m_gen->regStore(ptr, value, mask);
|
|
|
|
}
|
|
|
|
|
2017-10-16 17:50:09 +02:00
|
|
|
}
|