2017-10-16 17:50:09 +02:00
|
|
|
#include "dxbc_compiler.h"
|
|
|
|
|
|
|
|
namespace dxvk {
|
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
constexpr uint32_t PerVertex_Position = 0;
|
2017-12-21 16:00:36 +01:00
|
|
|
constexpr uint32_t PerVertex_CullDist = 1;
|
|
|
|
constexpr uint32_t PerVertex_ClipDist = 2;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-14 12:53:53 +01:00
|
|
|
DxbcCompiler::DxbcCompiler(
|
2017-12-07 16:29:34 +01:00
|
|
|
const DxbcProgramVersion& version,
|
|
|
|
const Rc<DxbcIsgn>& isgn,
|
|
|
|
const Rc<DxbcIsgn>& osgn)
|
2017-12-13 15:32:54 +01:00
|
|
|
: 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);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// 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;
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
2017-12-24 13:33:22 +01:00
|
|
|
// Set up common capabilities for all shaders
|
|
|
|
m_module.enableCapability(spv::CapabilityShader);
|
2017-12-27 01:37:15 +01:00
|
|
|
m_module.enableCapability(spv::CapabilityImageQuery);
|
2017-12-24 13:33:22 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// Initialize the shader module with capabilities
|
|
|
|
// etc. Each shader type has its own peculiarities.
|
|
|
|
switch (m_version.type()) {
|
2017-12-18 16:41:05 +01:00
|
|
|
case DxbcProgramType::VertexShader: this->emitVsInit(); break;
|
|
|
|
case DxbcProgramType::GeometryShader: this->emitGsInit(); break;
|
|
|
|
case DxbcProgramType::PixelShader: this->emitPsInit(); break;
|
2017-12-21 17:27:40 +01:00
|
|
|
case DxbcProgramType::ComputeShader: this->emitCsInit(); break;
|
2017-12-18 00:46:44 +01:00
|
|
|
default: throw DxvkError("DxbcCompiler: Unsupported program type");
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
}
|
2017-10-16 17:50:09 +02:00
|
|
|
|
|
|
|
|
2017-12-14 12:53:53 +01:00
|
|
|
DxbcCompiler::~DxbcCompiler() {
|
2017-10-16 17:50:09 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::processInstruction(const DxbcShaderInstruction& ins) {
|
2017-12-18 11:53:28 +01:00
|
|
|
switch (ins.opClass) {
|
|
|
|
case DxbcInstClass::Declaration:
|
|
|
|
return this->emitDcl(ins);
|
2017-12-19 17:41:23 +01:00
|
|
|
|
|
|
|
case DxbcInstClass::CustomData:
|
|
|
|
return this->emitCustomData(ins);
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcInstClass::ControlFlow:
|
|
|
|
return this->emitControlFlow(ins);
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
case DxbcInstClass::GeometryEmit:
|
|
|
|
return this->emitGeometryEmit(ins);
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
case DxbcInstClass::Atomic:
|
|
|
|
return this->emitAtomic(ins);
|
|
|
|
|
|
|
|
case DxbcInstClass::BufferLoad:
|
|
|
|
return this->emitBufferLoad(ins);
|
|
|
|
|
|
|
|
case DxbcInstClass::BufferStore:
|
|
|
|
return this->emitBufferStore(ins);
|
|
|
|
|
2017-12-27 01:37:15 +01:00
|
|
|
case DxbcInstClass::TextureQuery:
|
|
|
|
return this->emitTextureQuery(ins);
|
|
|
|
|
|
|
|
case DxbcInstClass::TextureFetch:
|
|
|
|
return this->emitTextureFetch(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcInstClass::TextureSample:
|
2017-12-27 01:37:15 +01:00
|
|
|
return this->emitTextureSample(ins);
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcInstClass::VectorAlu:
|
2017-12-18 00:46:44 +01:00
|
|
|
return this->emitVectorAlu(ins);
|
2017-12-18 11:53:28 +01:00
|
|
|
|
|
|
|
case DxbcInstClass::VectorCmov:
|
2017-12-18 00:46:44 +01:00
|
|
|
return this->emitVectorCmov(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcInstClass::VectorCmp:
|
2017-12-18 00:46:44 +01:00
|
|
|
return this->emitVectorCmp(ins);
|
2017-12-18 11:53:28 +01:00
|
|
|
|
2017-12-19 20:26:05 +01:00
|
|
|
case DxbcInstClass::VectorDeriv:
|
|
|
|
return this->emitVectorDeriv(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcInstClass::VectorDot:
|
2017-12-18 00:46:44 +01:00
|
|
|
return this->emitVectorDot(ins);
|
2017-12-18 11:53:28 +01:00
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
case DxbcInstClass::VectorIdiv:
|
|
|
|
return this->emitVectorIdiv(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcInstClass::VectorImul:
|
2017-12-18 00:46:44 +01:00
|
|
|
return this->emitVectorImul(ins);
|
|
|
|
|
2017-12-20 23:50:39 +01:00
|
|
|
case DxbcInstClass::VectorShift:
|
|
|
|
return this->emitVectorShift(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcInstClass::VectorSinCos:
|
2017-12-18 00:46:44 +01:00
|
|
|
return this->emitVectorSinCos(ins);
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(
|
2017-12-18 11:53:28 +01:00
|
|
|
str::format("DxbcCompiler: Unhandled opcode class: ",
|
2017-12-18 00:46:44 +01:00
|
|
|
ins.op));
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-14 12:53:53 +01:00
|
|
|
Rc<DxvkShader> DxbcCompiler::finalize() {
|
2017-12-13 15:32:54 +01:00
|
|
|
// 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());
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// Depending on the shader type, this will prepare
|
|
|
|
// input registers, call various shader functions
|
|
|
|
// and write back the output registers.
|
2017-12-13 15:32:54 +01:00
|
|
|
switch (m_version.type()) {
|
2017-12-18 16:41:05 +01:00
|
|
|
case DxbcProgramType::VertexShader: this->emitVsFinalize(); break;
|
|
|
|
case DxbcProgramType::GeometryShader: this->emitGsFinalize(); break;
|
|
|
|
case DxbcProgramType::PixelShader: this->emitPsFinalize(); break;
|
2017-12-21 17:27:40 +01:00
|
|
|
case DxbcProgramType::ComputeShader: this->emitCsFinalize(); break;
|
2017-12-18 00:46:44 +01:00
|
|
|
default: throw DxvkError("DxbcCompiler: Unsupported program type");
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
void DxbcCompiler::emitDcl(const DxbcShaderInstruction& ins) {
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::DclGlobalFlags:
|
|
|
|
return this->emitDclGlobalFlags(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::DclTemps:
|
|
|
|
return this->emitDclTemps(ins);
|
|
|
|
|
2017-12-20 22:50:05 +01:00
|
|
|
case DxbcOpcode::DclIndexableTemp:
|
|
|
|
return this->emitDclIndexableTemp(ins);
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
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);
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
// case DxbcOpcode::DclUavTyped:
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcOpcode::DclResource:
|
2017-12-28 16:03:17 +01:00
|
|
|
return this->emitDclResourceTyped(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::DclUavRaw:
|
|
|
|
case DxbcOpcode::DclResourceRaw:
|
|
|
|
case DxbcOpcode::DclUavStructured:
|
|
|
|
case DxbcOpcode::DclResourceStructured:
|
|
|
|
return this->emitDclResourceRawStructured(ins);
|
2017-12-18 11:53:28 +01:00
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
case DxbcOpcode::DclThreadGroupSharedMemoryRaw:
|
|
|
|
case DxbcOpcode::DclThreadGroupSharedMemoryStructured:
|
|
|
|
return this->emitDclThreadGroupSharedMemory(ins);
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
case DxbcOpcode::DclGsInputPrimitive:
|
|
|
|
return this->emitDclGsInputPrimitive(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::DclGsOutputPrimitiveTopology:
|
|
|
|
return this->emitDclGsOutputTopology(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::DclMaxOutputVertexCount:
|
|
|
|
return this->emitDclMaxOutputVertexCount(ins);
|
2017-12-28 16:03:17 +01:00
|
|
|
|
|
|
|
case DxbcOpcode::DclThreadGroup:
|
|
|
|
return this->emitDclThreadGroup(ins);
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
default:
|
|
|
|
Logger::warn(
|
|
|
|
str::format("DxbcCompiler: Unhandled opcode: ",
|
|
|
|
ins.op));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitDclGlobalFlags(const DxbcShaderInstruction& ins) {
|
|
|
|
// TODO implement properly
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
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;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
if (newCount > oldCount) {
|
|
|
|
m_rRegs.resize(newCount);
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterInfo info;
|
2017-12-18 16:41:05 +01:00
|
|
|
info.type.ctype = DxbcScalarType::Float32;
|
|
|
|
info.type.ccount = 4;
|
|
|
|
info.type.alength = 0;
|
|
|
|
info.sclass = spv::StorageClassPrivate;
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
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;
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-20 22:50:05 +01:00
|
|
|
void DxbcCompiler::emitDclIndexableTemp(const DxbcShaderInstruction& ins) {
|
|
|
|
// dcl_indexable_temps has three operands:
|
|
|
|
// (imm0) Array register index (x#)
|
|
|
|
// (imm1) Number of vectors stored in the array
|
|
|
|
// (imm2) Component count of each individual vector
|
|
|
|
DxbcRegisterInfo info;
|
|
|
|
info.type.ctype = DxbcScalarType::Float32;
|
|
|
|
info.type.ccount = ins.imm[2].u32;
|
|
|
|
info.type.alength = ins.imm[1].u32;
|
|
|
|
info.sclass = spv::StorageClassPrivate;
|
|
|
|
|
|
|
|
const uint32_t regId = ins.imm[0].u32;
|
|
|
|
|
|
|
|
if (regId >= m_xRegs.size())
|
|
|
|
m_xRegs.resize(regId + 1);
|
|
|
|
|
|
|
|
m_xRegs.at(regId).ccount = info.type.ccount;
|
|
|
|
m_xRegs.at(regId).varId = emitNewVariable(info);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitDclInterfaceReg(const DxbcShaderInstruction& ins) {
|
2017-12-19 12:58:40 +01:00
|
|
|
switch (ins.dst[0].type) {
|
|
|
|
case DxbcOperandType::Input:
|
|
|
|
case DxbcOperandType::Output: {
|
|
|
|
// 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));
|
|
|
|
}
|
|
|
|
} break;
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-11-13 00:22:52 +01:00
|
|
|
default:
|
2017-12-18 00:46:44 +01:00
|
|
|
Logger::err(str::format(
|
2017-12-19 12:58:40 +01:00
|
|
|
"DxbcCompiler: Unsupported operand type declaration: ",
|
|
|
|
ins.dst[0].type));
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclInput(
|
|
|
|
uint32_t regIdx,
|
|
|
|
uint32_t regDim,
|
|
|
|
DxbcRegMask regMask,
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcInterpolationMode im) {
|
|
|
|
// Avoid declaring the same variable multiple times.
|
|
|
|
// This may happen when multiple system values are
|
|
|
|
// mapped to different parts of the same register.
|
2017-12-21 12:37:20 +01:00
|
|
|
if (m_vRegs.at(regIdx) == 0 && sv == DxbcSystemValue::None) {
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterInfo info;
|
2017-12-18 16:41:05 +01:00
|
|
|
info.type.ctype = DxbcScalarType::Float32;
|
|
|
|
info.type.ccount = 4;
|
|
|
|
info.type.alength = regDim;
|
2017-12-18 00:46:44 +01:00
|
|
|
info.sclass = spv::StorageClassInput;
|
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
const uint32_t varId = emitNewVariable(info);
|
2017-12-18 00:46:44 +01:00
|
|
|
|
|
|
|
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);
|
2017-12-21 12:37:20 +01:00
|
|
|
} else if (sv != DxbcSystemValue::None) {
|
|
|
|
// Add a new system value mapping if needed
|
2017-12-18 00:46:44 +01:00
|
|
|
m_vMappings.push_back({ regIdx, regMask, sv });
|
2017-12-21 12:37:20 +01:00
|
|
|
}
|
2017-10-16 17:50:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitDclOutput(
|
|
|
|
uint32_t regIdx,
|
|
|
|
uint32_t regDim,
|
|
|
|
DxbcRegMask regMask,
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcInterpolationMode im) {
|
|
|
|
// 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;
|
2017-12-18 16:41:05 +01:00
|
|
|
info.type.ctype = DxbcScalarType::Float32;
|
|
|
|
info.type.ccount = 4;
|
|
|
|
info.type.alength = regDim;
|
2017-12-18 00:46:44 +01:00
|
|
|
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;
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
|
|
|
|
// Add a new system value mapping if needed
|
|
|
|
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;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// Uniform buffer data is stored as a fixed-size array
|
|
|
|
// of 4x32-bit vectors. SPIR-V requires explicit strides.
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t arrayType = m_module.defArrayTypeUnique(
|
|
|
|
getVectorTypeId({ DxbcScalarType::Float32, 4 }),
|
2017-12-13 15:32:54 +01:00
|
|
|
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.
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t structType = m_module.defStructTypeUnique(1, &arrayType);
|
2017-12-13 15:32:54 +01:00
|
|
|
m_module.memberDecorateOffset(structType, 0, 0);
|
|
|
|
m_module.decorateBlock(structType);
|
|
|
|
|
|
|
|
// Variable that we'll use to access the buffer
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t varId = m_module.newVar(
|
2017-12-13 15:32:54 +01:00
|
|
|
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.
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t bindingId = computeResourceSlotId(
|
2017-12-13 15:32:54 +01:00
|
|
|
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);
|
2017-12-08 17:08:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitDclSampler(const DxbcShaderInstruction& ins) {
|
2017-12-13 15:32:54 +01:00
|
|
|
// dclSampler takes one operand:
|
2017-12-18 00:46:44 +01:00
|
|
|
// (dst0) The sampler register to declare
|
|
|
|
const uint32_t samplerId = ins.dst[0].idx[0].offset;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// The sampler type is opaque, but we still have to
|
|
|
|
// define a pointer and a variable in oder to use it
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t samplerType = m_module.defSamplerType();
|
|
|
|
const uint32_t samplerPtrType = m_module.defPointerType(
|
2017-12-13 15:32:54 +01:00
|
|
|
samplerType, spv::StorageClassUniformConstant);
|
2017-11-13 00:22:52 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// Define the sampler variable
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t varId = m_module.newVar(samplerPtrType,
|
2017-12-13 15:32:54 +01:00
|
|
|
spv::StorageClassUniformConstant);
|
|
|
|
m_module.setDebugName(varId,
|
|
|
|
str::format("s", samplerId).c_str());
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
m_samplers.at(samplerId).varId = varId;
|
|
|
|
m_samplers.at(samplerId).typeId = samplerType;
|
|
|
|
|
|
|
|
// Compute binding slot index for the sampler
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t bindingId = computeResourceSlotId(
|
2017-12-13 15:32:54 +01:00
|
|
|
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);
|
2017-11-13 00:22:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
void DxbcCompiler::emitDclResourceTyped(const DxbcShaderInstruction& ins) {
|
2017-12-10 10:34:18 +01:00
|
|
|
// dclResource takes two operands:
|
2017-12-28 16:03:17 +01:00
|
|
|
// (dst0) The resource register ID
|
|
|
|
// (imm0) The resource return type
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t registerId = ins.dst[0].idx[0].offset;
|
2017-12-10 10:34:18 +01:00
|
|
|
|
|
|
|
// Defines the type of the resource (texture2D, ...)
|
2017-12-18 00:46:44 +01:00
|
|
|
const DxbcResourceDim resourceType = ins.controls.resourceDim;
|
2017-12-10 10:34:18 +01:00
|
|
|
|
|
|
|
// 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>(
|
2017-12-18 00:46:44 +01:00
|
|
|
bit::extract(ins.imm[0].u32, 0, 3));
|
2017-12-10 10:34:18 +01:00
|
|
|
auto yType = static_cast<DxbcResourceReturnType>(
|
2017-12-18 00:46:44 +01:00
|
|
|
bit::extract(ins.imm[0].u32, 4, 7));
|
2017-12-10 10:34:18 +01:00
|
|
|
auto zType = static_cast<DxbcResourceReturnType>(
|
2017-12-18 00:46:44 +01:00
|
|
|
bit::extract(ins.imm[0].u32, 8, 11));
|
2017-12-10 10:34:18 +01:00
|
|
|
auto wType = static_cast<DxbcResourceReturnType>(
|
2017-12-18 00:46:44 +01:00
|
|
|
bit::extract(ins.imm[0].u32, 12, 15));
|
2017-12-10 10:34:18 +01:00
|
|
|
|
|
|
|
if ((xType != yType) || (xType != zType) || (xType != wType))
|
2017-12-18 00:46:44 +01:00
|
|
|
Logger::warn("DxbcCompiler: dcl_resource: Ignoring resource return types");
|
2017-12-10 10:34:18 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// Declare the actual sampled type
|
2017-12-20 13:41:04 +01:00
|
|
|
const DxbcScalarType sampledType = [xType] {
|
|
|
|
switch (xType) {
|
|
|
|
case DxbcResourceReturnType::Float: return DxbcScalarType::Float32;
|
|
|
|
case DxbcResourceReturnType::Sint: return DxbcScalarType::Sint32;
|
|
|
|
case DxbcResourceReturnType::Uint: return DxbcScalarType::Uint32;
|
|
|
|
default: throw DxvkError(str::format("DxbcCompiler: Invalid sampled type: ", xType));
|
|
|
|
}
|
|
|
|
}();
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-20 13:41:04 +01:00
|
|
|
const uint32_t sampledTypeId = getScalarTypeId(sampledType);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// Declare the resource type
|
2017-12-21 12:37:20 +01:00
|
|
|
const DxbcImageInfo typeInfo = [resourceType] () -> DxbcImageInfo {
|
2017-12-20 13:41:04 +01:00
|
|
|
switch (resourceType) {
|
2017-12-24 13:33:22 +01:00
|
|
|
case DxbcResourceDim::Buffer: return { spv::DimBuffer, 0, 0, 1 };
|
2017-12-20 13:41:04 +01:00
|
|
|
case DxbcResourceDim::Texture1D: return { spv::Dim1D, 0, 0, 1 };
|
|
|
|
case DxbcResourceDim::Texture1DArr: return { spv::Dim1D, 1, 0, 1 };
|
|
|
|
case DxbcResourceDim::Texture2D: return { spv::Dim2D, 0, 0, 1 };
|
|
|
|
case DxbcResourceDim::Texture2DArr: return { spv::Dim2D, 1, 0, 1 };
|
2017-12-24 13:33:22 +01:00
|
|
|
case DxbcResourceDim::Texture2DMs: return { spv::Dim2D, 0, 1, 0 };
|
|
|
|
case DxbcResourceDim::Texture2DMsArr: return { spv::Dim2D, 1, 1, 0 };
|
2017-12-20 13:41:04 +01:00
|
|
|
case DxbcResourceDim::Texture3D: return { spv::Dim3D, 0, 0, 1 };
|
2017-12-22 20:15:44 +01:00
|
|
|
case DxbcResourceDim::TextureCube: return { spv::DimCube, 0, 0, 1 };
|
2017-12-24 13:33:22 +01:00
|
|
|
case DxbcResourceDim::TextureCubeArr: return { spv::DimCube, 1, 0, 1 };
|
2017-12-20 13:41:04 +01:00
|
|
|
default: throw DxvkError(str::format("DxbcCompiler: Unsupported resource type: ", resourceType));
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
2017-12-24 13:33:22 +01:00
|
|
|
// Declare additional capabilities if necessary
|
|
|
|
switch (resourceType) {
|
|
|
|
case DxbcResourceDim::Buffer: m_module.enableCapability(spv::CapabilityImageBuffer); break;
|
|
|
|
case DxbcResourceDim::Texture1D: m_module.enableCapability(spv::CapabilityImage1D); break;
|
|
|
|
case DxbcResourceDim::Texture1DArr: m_module.enableCapability(spv::CapabilityImage1D); break;
|
|
|
|
case DxbcResourceDim::TextureCubeArr: m_module.enableCapability(spv::CapabilityImageCubeArray); break;
|
|
|
|
case DxbcResourceDim::Texture2DMsArr: m_module.enableCapability(spv::CapabilityImageMSArray); break;
|
|
|
|
default: break; // No additional capabilities required
|
|
|
|
}
|
|
|
|
|
2017-12-20 13:41:04 +01:00
|
|
|
// We do not know whether the image is going to be used as a color
|
|
|
|
// image or a depth image yet, so we'll declare types for both.
|
|
|
|
const uint32_t colorTypeId = m_module.defImageType(sampledTypeId,
|
|
|
|
typeInfo.dim, 0, typeInfo.array, typeInfo.ms, typeInfo.sampled,
|
|
|
|
spv::ImageFormatUnknown);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-20 13:41:04 +01:00
|
|
|
const uint32_t depthTypeId = m_module.defImageType(sampledTypeId,
|
|
|
|
typeInfo.dim, 1, typeInfo.array, typeInfo.ms, typeInfo.sampled,
|
|
|
|
spv::ImageFormatUnknown);
|
|
|
|
|
|
|
|
// We'll declare the texture variable with the color type
|
|
|
|
// and decide which one to use when the texture is sampled.
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t resourcePtrType = m_module.defPointerType(
|
2017-12-20 13:41:04 +01:00
|
|
|
colorTypeId, spv::StorageClassUniformConstant);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t varId = m_module.newVar(resourcePtrType,
|
2017-12-13 15:32:54 +01:00
|
|
|
spv::StorageClassUniformConstant);
|
|
|
|
|
|
|
|
m_module.setDebugName(varId,
|
|
|
|
str::format("t", registerId).c_str());
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
DxbcShaderResource res;
|
|
|
|
res.type = DxbcResourceType::Typed;
|
|
|
|
res.imageInfo = typeInfo;
|
|
|
|
res.varId = varId;
|
|
|
|
res.sampledType = sampledType;
|
|
|
|
res.sampledTypeId = sampledTypeId;
|
|
|
|
res.colorTypeId = colorTypeId;
|
|
|
|
res.depthTypeId = depthTypeId;
|
|
|
|
res.structStride = 0;
|
|
|
|
|
|
|
|
m_textures.at(registerId) = res;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// Compute the DXVK binding slot index for the resource.
|
|
|
|
// D3D11 needs to bind the actual resource to this slot.
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t bindingId = computeResourceSlotId(
|
|
|
|
m_version.type(), DxbcBindingType::ShaderResource, registerId);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
m_module.decorateDescriptorSet(varId, 0);
|
|
|
|
m_module.decorateBinding(varId, bindingId);
|
|
|
|
|
|
|
|
// Store descriptor info for the shader interface
|
|
|
|
DxvkResourceSlot resource;
|
|
|
|
resource.slot = bindingId;
|
2017-12-27 14:31:38 +01:00
|
|
|
resource.type = resourceType == DxbcResourceDim::Buffer
|
|
|
|
? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
|
|
|
|
: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
2017-12-13 15:32:54 +01:00
|
|
|
m_resourceSlots.push_back(resource);
|
2017-12-11 14:36:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
void DxbcCompiler::emitDclResourceRawStructured(const DxbcShaderInstruction& ins) {
|
|
|
|
// dcl_resource_raw and dcl_uav_raw take one argument:
|
|
|
|
// (dst0) The resource register ID
|
|
|
|
// dcl_resource_structured and dcl_uav_structured take two arguments:
|
|
|
|
// (dst0) The resource register ID
|
|
|
|
// (imm0) Structure stride, in bytes
|
|
|
|
const uint32_t registerId = ins.dst[0].idx[0].offset;
|
|
|
|
|
|
|
|
const bool isUav = ins.op == DxbcOpcode::DclUavRaw
|
|
|
|
|| ins.op == DxbcOpcode::DclUavStructured;
|
|
|
|
|
|
|
|
const bool isStructured = ins.op == DxbcOpcode::DclUavStructured
|
|
|
|
|| ins.op == DxbcOpcode::DclResourceStructured;
|
|
|
|
|
|
|
|
// Structured and raw buffers are represented as
|
|
|
|
// texel buffers consisting of 32-bit integers.
|
|
|
|
m_module.enableCapability(spv::CapabilityImageBuffer);
|
|
|
|
|
|
|
|
const DxbcScalarType sampledType = DxbcScalarType::Uint32;
|
|
|
|
const uint32_t sampledTypeId = getScalarTypeId(sampledType);
|
|
|
|
|
|
|
|
const DxbcImageInfo typeInfo = { spv::DimBuffer, 0, 0, isUav ? 2u : 1u };
|
|
|
|
|
|
|
|
// Declare the resource type
|
|
|
|
const uint32_t resTypeId = m_module.defImageType(sampledTypeId,
|
|
|
|
typeInfo.dim, 0, typeInfo.array, typeInfo.ms, typeInfo.sampled,
|
|
|
|
spv::ImageFormatR32ui);
|
|
|
|
|
|
|
|
const uint32_t varId = m_module.newVar(
|
|
|
|
m_module.defPointerType(resTypeId, spv::StorageClassUniformConstant),
|
|
|
|
spv::StorageClassUniformConstant);
|
|
|
|
|
|
|
|
m_module.setDebugName(varId,
|
|
|
|
str::format(isUav ? "u" : "t", registerId).c_str());
|
|
|
|
|
|
|
|
// Write back resource info
|
|
|
|
const DxbcResourceType resType = isStructured
|
|
|
|
? DxbcResourceType::Structured
|
|
|
|
: DxbcResourceType::Raw;
|
|
|
|
|
|
|
|
const uint32_t resStride = isStructured
|
|
|
|
? ins.imm[0].u32
|
|
|
|
: 0;
|
|
|
|
|
|
|
|
if (isUav) {
|
|
|
|
DxbcUav uav;
|
|
|
|
uav.type = resType;
|
|
|
|
uav.imageInfo = typeInfo;
|
|
|
|
uav.varId = varId;
|
|
|
|
uav.sampledType = sampledType;
|
|
|
|
uav.sampledTypeId = sampledTypeId;
|
|
|
|
uav.imageTypeId = resTypeId;
|
|
|
|
uav.structStride = resStride;
|
|
|
|
m_uavs.at(registerId) = uav;
|
|
|
|
} else {
|
|
|
|
DxbcShaderResource res;
|
|
|
|
res.type = resType;
|
|
|
|
res.imageInfo = typeInfo;
|
|
|
|
res.varId = varId;
|
|
|
|
res.sampledType = sampledType;
|
|
|
|
res.sampledTypeId = sampledTypeId;
|
|
|
|
res.colorTypeId = resTypeId;
|
|
|
|
res.depthTypeId = resTypeId;
|
|
|
|
res.structStride = resStride;
|
|
|
|
m_textures.at(registerId) = res;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the DXVK binding slot index for the resource.
|
|
|
|
const uint32_t bindingId = computeResourceSlotId(
|
|
|
|
m_version.type(), isUav
|
|
|
|
? DxbcBindingType::UnorderedAccessView
|
|
|
|
: 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 = isUav
|
|
|
|
? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER
|
|
|
|
: VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
|
|
|
|
m_resourceSlots.push_back(resource);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclThreadGroupSharedMemory(const DxbcShaderInstruction& ins) {
|
|
|
|
// dcl_tgsm_raw takes two arguments:
|
|
|
|
// (dst0) The resource register ID
|
|
|
|
// (imm0) Block size, in DWORDs
|
|
|
|
// dcl_tgsm_structured takes three arguments:
|
|
|
|
// (dst0) The resource register ID
|
|
|
|
// (imm0) Structure stride, in bytes
|
|
|
|
// (imm0) Structure count
|
|
|
|
Logger::err("DxbcCompiler: emitDclThreadGroupSharedMemory not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
void DxbcCompiler::emitDclGsInputPrimitive(const DxbcShaderInstruction& ins) {
|
|
|
|
// The input primitive type is stored within in the
|
|
|
|
// control bits of the opcode token. In SPIR-V, we
|
|
|
|
// have to define an execution mode.
|
|
|
|
const spv::ExecutionMode mode = [&] {
|
|
|
|
switch (ins.controls.primitive) {
|
|
|
|
case DxbcPrimitive::Point: return spv::ExecutionModeInputPoints;
|
|
|
|
case DxbcPrimitive::Line: return spv::ExecutionModeInputLines;
|
|
|
|
case DxbcPrimitive::Triangle: return spv::ExecutionModeTriangles;
|
|
|
|
case DxbcPrimitive::LineAdj: return spv::ExecutionModeInputLinesAdjacency;
|
|
|
|
case DxbcPrimitive::TriangleAdj: return spv::ExecutionModeInputTrianglesAdjacency;
|
|
|
|
default: throw DxvkError("DxbcCompiler: Unsupported primitive type");
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
m_gs.inputPrimitive = ins.controls.primitive;
|
2017-12-18 16:41:05 +01:00
|
|
|
m_module.setExecutionMode(m_entryPointId, mode);
|
2017-12-21 12:37:20 +01:00
|
|
|
|
|
|
|
const uint32_t vertexCount
|
|
|
|
= primitiveVertexCount(m_gs.inputPrimitive);
|
|
|
|
|
|
|
|
emitDclInputArray(vertexCount);
|
2017-12-21 16:00:36 +01:00
|
|
|
emitDclInputPerVertex(vertexCount, "gs_vertex_in");
|
2017-12-21 12:37:20 +01:00
|
|
|
emitGsInitBuiltins(vertexCount);
|
2017-12-18 16:41:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclGsOutputTopology(const DxbcShaderInstruction& ins) {
|
|
|
|
// The input primitive topology is stored within in the
|
|
|
|
// control bits of the opcode token. In SPIR-V, we have
|
|
|
|
// to define an execution mode.
|
|
|
|
const spv::ExecutionMode mode = [&] {
|
|
|
|
switch (ins.controls.primitiveTopology) {
|
|
|
|
case DxbcPrimitiveTopology::PointList: return spv::ExecutionModeOutputPoints;
|
|
|
|
case DxbcPrimitiveTopology::LineStrip: return spv::ExecutionModeOutputLineStrip;
|
|
|
|
case DxbcPrimitiveTopology::TriangleStrip: return spv::ExecutionModeOutputTriangleStrip;
|
|
|
|
default: throw DxvkError("DxbcCompiler: Unsupported primitive topology");
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
m_module.setExecutionMode(m_entryPointId, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitDclMaxOutputVertexCount(const DxbcShaderInstruction& ins) {
|
|
|
|
// dcl_max_output_vertex_count has one operand:
|
|
|
|
// (imm0) The maximum number of vertices
|
|
|
|
m_gs.outputVertexCount = ins.imm[0].u32;
|
|
|
|
m_module.setOutputVertices(m_entryPointId, m_gs.outputVertexCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
void DxbcCompiler::emitDclThreadGroup(const DxbcShaderInstruction& ins) {
|
|
|
|
// dcl_thread_group has three operands:
|
|
|
|
// (imm0) Number of threads in X dimension
|
|
|
|
// (imm1) Number of threads in Y dimension
|
|
|
|
// (imm2) Number of threads in Z dimension
|
|
|
|
m_module.setLocalSize(m_entryPointId,
|
|
|
|
ins.imm[0].u32, ins.imm[1].u32, ins.imm[2].u32);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-19 17:41:23 +01:00
|
|
|
void DxbcCompiler::emitDclImmediateConstantBuffer(const DxbcShaderInstruction& ins) {
|
|
|
|
if (m_immConstBuf != 0)
|
|
|
|
throw DxvkError("DxbcCompiler: Immediate constant buffer already declared");
|
|
|
|
|
|
|
|
if ((ins.customDataSize & 0x3) != 0)
|
|
|
|
throw DxvkError("DxbcCompiler: Immediate constant buffer size not a multiple of four DWORDs");
|
|
|
|
|
|
|
|
// Declare individual vector constants as 4x32-bit vectors
|
|
|
|
std::array<uint32_t, 4096> vectorIds;
|
|
|
|
|
|
|
|
DxbcVectorType vecType;
|
|
|
|
vecType.ctype = DxbcScalarType::Uint32;
|
|
|
|
vecType.ccount = 4;
|
|
|
|
|
|
|
|
const uint32_t vectorTypeId = getVectorTypeId(vecType);
|
|
|
|
const uint32_t vectorCount = ins.customDataSize / 4;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < vectorCount; i++) {
|
|
|
|
std::array<uint32_t, 4> scalarIds = {
|
|
|
|
m_module.constu32(ins.customData[4 * i + 0]),
|
|
|
|
m_module.constu32(ins.customData[4 * i + 1]),
|
|
|
|
m_module.constu32(ins.customData[4 * i + 2]),
|
|
|
|
m_module.constu32(ins.customData[4 * i + 3]),
|
|
|
|
};
|
|
|
|
|
|
|
|
vectorIds.at(i) = m_module.constComposite(
|
|
|
|
vectorTypeId, scalarIds.size(), scalarIds.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Declare the array that contains all the vectors
|
|
|
|
DxbcArrayType arrInfo;
|
|
|
|
arrInfo.ctype = DxbcScalarType::Uint32;
|
|
|
|
arrInfo.ccount = 4;
|
|
|
|
arrInfo.alength = vectorCount;
|
|
|
|
|
|
|
|
const uint32_t arrayTypeId = getArrayTypeId(arrInfo);
|
|
|
|
const uint32_t arrayId = m_module.constComposite(
|
|
|
|
arrayTypeId, vectorCount, vectorIds.data());
|
|
|
|
|
|
|
|
// Declare the variable that will hold the constant
|
|
|
|
// data and initialize it with the constant array.
|
|
|
|
const uint32_t pointerTypeId = m_module.defPointerType(
|
|
|
|
arrayTypeId, spv::StorageClassPrivate);
|
|
|
|
|
|
|
|
m_immConstBuf = m_module.newVarInit(
|
|
|
|
pointerTypeId, spv::StorageClassPrivate,
|
|
|
|
arrayId);
|
|
|
|
m_module.setDebugName(m_immConstBuf, "icb");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitCustomData(const DxbcShaderInstruction& ins) {
|
|
|
|
switch (ins.customDataType) {
|
|
|
|
case DxbcCustomDataClass::ImmConstBuf:
|
|
|
|
return emitDclImmediateConstantBuffer(ins);
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unsupported custom data block: ",
|
|
|
|
ins.customDataType));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVectorAlu(const DxbcShaderInstruction& ins) {
|
|
|
|
std::array<DxbcRegisterValue, DxbcMaxOperandCount> src;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
for (uint32_t i = 0; i < ins.srcCount; i++)
|
|
|
|
src.at(i) = emitRegisterLoad(ins.src[i], ins.dst[0].mask);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue dst;
|
|
|
|
dst.type.ctype = ins.dst[0].dataType;
|
|
|
|
dst.type.ccount = ins.dst[0].mask.setCount();
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t typeId = getVectorTypeId(dst.type);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
switch (ins.op) {
|
2017-12-19 00:45:31 +01:00
|
|
|
/////////////////////
|
|
|
|
// Move instructions
|
|
|
|
case DxbcOpcode::Mov:
|
|
|
|
dst.id = src.at(0).id;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/////////////////////////////////////
|
|
|
|
// ALU operations on float32 numbers
|
2017-12-13 15:32:54 +01:00
|
|
|
case DxbcOpcode::Add:
|
2017-12-18 00:46:44 +01:00
|
|
|
dst.id = m_module.opFAdd(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
2017-12-13 15:32:54 +01:00
|
|
|
break;
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-17 01:36:41 +01:00
|
|
|
case DxbcOpcode::Div:
|
2017-12-18 00:46:44 +01:00
|
|
|
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);
|
2017-12-17 01:36:41 +01:00
|
|
|
break;
|
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
case DxbcOpcode::Mad:
|
2017-12-18 00:46:44 +01:00
|
|
|
dst.id = m_module.opFFma(typeId,
|
|
|
|
src.at(0).id, src.at(1).id, src.at(2).id);
|
2017-12-13 15:32:54 +01:00
|
|
|
break;
|
|
|
|
|
2017-12-13 16:35:01 +01:00
|
|
|
case DxbcOpcode::Max:
|
2017-12-18 00:46:44 +01:00
|
|
|
dst.id = m_module.opFMax(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
2017-12-13 16:35:01 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Min:
|
2017-12-18 00:46:44 +01:00
|
|
|
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);
|
2017-12-13 16:35:01 +01:00
|
|
|
break;
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
case DxbcOpcode::Sqrt:
|
|
|
|
dst.id = m_module.opSqrt(
|
|
|
|
typeId, src.at(0).id);
|
2017-12-13 15:32:54 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Rsq:
|
2017-12-18 00:46:44 +01:00
|
|
|
dst.id = m_module.opInverseSqrt(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
2017-12-19 00:45:31 +01:00
|
|
|
/////////////////////////////////////
|
|
|
|
// ALU operations on signed integers
|
2017-12-18 00:46:44 +01:00
|
|
|
case DxbcOpcode::IAdd:
|
|
|
|
dst.id = m_module.opIAdd(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::IMad:
|
2017-12-20 23:50:39 +01:00
|
|
|
case DxbcOpcode::UMad:
|
2017-12-18 00:46:44 +01:00
|
|
|
dst.id = m_module.opIAdd(typeId,
|
|
|
|
m_module.opIMul(typeId,
|
|
|
|
src.at(0).id, src.at(1).id),
|
|
|
|
src.at(2).id);
|
2017-12-13 15:32:54 +01:00
|
|
|
break;
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
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;
|
2017-12-19 00:45:31 +01:00
|
|
|
|
2017-12-20 23:50:39 +01:00
|
|
|
///////////////////////////////////////
|
|
|
|
// ALU operations on unsigned integers
|
|
|
|
case DxbcOpcode::UMax:
|
|
|
|
dst.id = m_module.opUMax(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::UMin:
|
|
|
|
dst.id = m_module.opUMin(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
2017-12-19 00:45:31 +01:00
|
|
|
///////////////////////////////////////
|
|
|
|
// Bit operations on unsigned integers
|
|
|
|
case DxbcOpcode::And:
|
|
|
|
dst.id = m_module.opBitwiseAnd(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Not:
|
|
|
|
dst.id = m_module.opNot(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-19 00:45:31 +01:00
|
|
|
case DxbcOpcode::Or:
|
|
|
|
dst.id = m_module.opBitwiseOr(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Xor:
|
|
|
|
dst.id = m_module.opBitwiseXor(typeId,
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
2017-12-19 18:12:18 +01:00
|
|
|
///////////////////////////
|
|
|
|
// Conversion instructions
|
|
|
|
case DxbcOpcode::ItoF:
|
|
|
|
dst.id = m_module.opConvertStoF(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::UtoF:
|
|
|
|
dst.id = m_module.opConvertUtoF(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::FtoI:
|
|
|
|
dst.id = m_module.opConvertFtoS(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::FtoU:
|
|
|
|
dst.id = m_module.opConvertFtoU(
|
|
|
|
typeId, src.at(0).id);
|
|
|
|
break;
|
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
default:
|
2017-12-18 00:46:44 +01:00
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
return;
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
2017-12-11 14:36:35 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// Store computed value
|
|
|
|
dst = emitDstOperandModifiers(dst, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[0], dst);
|
2017-11-17 11:41:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVectorCmov(const DxbcShaderInstruction& ins) {
|
2017-12-17 01:36:41 +01:00
|
|
|
// movc has four operands:
|
2017-12-18 00:46:44 +01:00
|
|
|
// (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);
|
2017-12-17 01:36:41 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t componentCount = ins.dst[0].mask.setCount();
|
2017-12-17 01:36:41 +01:00
|
|
|
|
|
|
|
// 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());
|
|
|
|
}
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-17 01:36:41 +01:00
|
|
|
// Use the component mask to select the vector components
|
2017-12-18 00:46:44 +01:00
|
|
|
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);
|
2017-12-17 01:36:41 +01:00
|
|
|
|
|
|
|
// Apply result modifiers to floating-point results
|
2017-12-18 00:46:44 +01:00
|
|
|
result = emitDstOperandModifiers(result, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[0], result);
|
2017-12-17 01:36:41 +01:00
|
|
|
}
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVectorCmp(const DxbcShaderInstruction& ins) {
|
2017-12-17 01:36:41 +01:00
|
|
|
// Compare instructions have three operands:
|
2017-12-18 00:46:44 +01:00
|
|
|
// (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),
|
|
|
|
};
|
2017-12-17 01:36:41 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t componentCount = ins.dst[0].mask.setCount();
|
2017-12-17 01:36:41 +01:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
switch (ins.op) {
|
2017-12-17 01:36:41 +01:00
|
|
|
case DxbcOpcode::Eq:
|
|
|
|
condition = m_module.opFOrdEqual(
|
2017-12-18 00:46:44 +01:00
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
2017-12-17 01:36:41 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Ge:
|
|
|
|
condition = m_module.opFOrdGreaterThanEqual(
|
2017-12-18 00:46:44 +01:00
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
2017-12-17 01:36:41 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Lt:
|
|
|
|
condition = m_module.opFOrdLessThan(
|
2017-12-18 00:46:44 +01:00
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
2017-12-17 01:36:41 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Ne:
|
|
|
|
condition = m_module.opFOrdNotEqual(
|
2017-12-18 00:46:44 +01:00
|
|
|
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);
|
2017-12-17 01:36:41 +01:00
|
|
|
break;
|
|
|
|
|
2017-12-21 17:14:11 +01:00
|
|
|
case DxbcOpcode::UGe:
|
|
|
|
condition = m_module.opUGreaterThanEqual(
|
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::ULt:
|
|
|
|
condition = m_module.opULessThan(
|
|
|
|
conditionType, src.at(0).id, src.at(1).id);
|
|
|
|
break;
|
|
|
|
|
2017-12-17 01:36:41 +01:00
|
|
|
default:
|
2017-12-18 00:46:44 +01:00
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
return;
|
2017-12-17 01:36:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate constant vectors for selection
|
|
|
|
uint32_t sFalse = m_module.constu32( 0u);
|
|
|
|
uint32_t sTrue = m_module.constu32(~0u);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = componentCount;
|
|
|
|
|
|
|
|
const uint32_t typeId = getVectorTypeId(result.type);
|
2017-12-17 01:36:41 +01:00
|
|
|
|
|
|
|
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 };
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
sFalse = m_module.constComposite(typeId, componentCount, vFalse.data());
|
|
|
|
sTrue = m_module.constComposite(typeId, componentCount, vTrue .data());
|
2017-12-17 01:36:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Perform component-wise mask selection
|
|
|
|
// based on the condition evaluated above.
|
2017-12-18 00:46:44 +01:00
|
|
|
result.id = m_module.opSelect(
|
|
|
|
typeId, condition, sTrue, sFalse);
|
|
|
|
|
|
|
|
emitRegisterStore(ins.dst[0], result);
|
2017-12-17 01:36:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-19 20:26:05 +01:00
|
|
|
void DxbcCompiler::emitVectorDeriv(const DxbcShaderInstruction& ins) {
|
|
|
|
// Derivative instructions have two operands:
|
|
|
|
// (dst0) Destination register for the derivative
|
|
|
|
// (src0) The operand to compute the derivative of
|
|
|
|
DxbcRegisterValue value = emitRegisterLoad(ins.src[0], ins.dst[0].mask);
|
|
|
|
const uint32_t typeId = getVectorTypeId(value.type);
|
|
|
|
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::DerivRtx:
|
|
|
|
value.id = m_module.opDpdx(typeId, value.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DerivRty:
|
|
|
|
value.id = m_module.opDpdy(typeId, value.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DerivRtxCoarse:
|
|
|
|
value.id = m_module.opDpdxCoarse(typeId, value.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DerivRtyCoarse:
|
|
|
|
value.id = m_module.opDpdyCoarse(typeId, value.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DerivRtxFine:
|
|
|
|
value.id = m_module.opDpdxFine(typeId, value.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::DerivRtyFine:
|
|
|
|
value.id = m_module.opDpdyFine(typeId, value.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = emitDstOperandModifiers(value, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[0], value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVectorDot(const DxbcShaderInstruction& ins) {
|
|
|
|
const DxbcRegMask srcMask(true,
|
|
|
|
ins.op >= DxbcOpcode::Dp2,
|
|
|
|
ins.op >= DxbcOpcode::Dp3,
|
|
|
|
ins.op >= DxbcOpcode::Dp4);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
const std::array<DxbcRegisterValue, 2> src = {
|
|
|
|
emitRegisterLoad(ins.src[0], srcMask),
|
|
|
|
emitRegisterLoad(ins.src[1], srcMask),
|
|
|
|
};
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue dst;
|
|
|
|
dst.type.ctype = ins.dst[0].dataType;
|
|
|
|
dst.type.ccount = 1;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
dst.id = m_module.opDot(
|
|
|
|
getVectorTypeId(dst.type),
|
|
|
|
src.at(0).id,
|
|
|
|
src.at(1).id);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
dst = emitDstOperandModifiers(dst, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[0], dst);
|
2017-12-09 01:49:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
void DxbcCompiler::emitVectorIdiv(const DxbcShaderInstruction& ins) {
|
|
|
|
// udiv has four operands:
|
|
|
|
// (dst0) Quotient destination register
|
|
|
|
// (dst1) Remainder destination register
|
|
|
|
// (src0) The first vector to compare
|
|
|
|
// (src1) The second vector to compare
|
|
|
|
if (ins.dst[0].type == DxbcOperandType::Null
|
|
|
|
&& ins.dst[1].type == DxbcOperandType::Null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// FIXME support this if applications require it
|
|
|
|
if (ins.dst[0].type != DxbcOperandType::Null
|
|
|
|
&& ins.dst[1].type != DxbcOperandType::Null
|
|
|
|
&& ins.dst[0].mask != ins.dst[1].mask) {
|
|
|
|
Logger::warn("DxbcCompiler: Umul with different destination masks not supported");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load source operands as integers with the
|
|
|
|
// mask of one non-NULL destination operand
|
|
|
|
const DxbcRegMask srcMask =
|
|
|
|
ins.dst[0].type != DxbcOperandType::Null
|
|
|
|
? ins.dst[0].mask
|
|
|
|
: ins.dst[1].mask;
|
|
|
|
|
|
|
|
const std::array<DxbcRegisterValue, 2> src = {
|
|
|
|
emitRegisterLoad(ins.src[0], srcMask),
|
|
|
|
emitRegisterLoad(ins.src[1], srcMask),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Compute results only if the destination
|
|
|
|
// operands are not NULL.
|
|
|
|
if (ins.dst[0].type != DxbcOperandType::Null) {
|
|
|
|
DxbcRegisterValue quotient;
|
|
|
|
quotient.type.ctype = ins.dst[0].dataType;
|
|
|
|
quotient.type.ccount = ins.dst[0].mask.setCount();
|
|
|
|
|
|
|
|
quotient.id = m_module.opUDiv(
|
|
|
|
getVectorTypeId(quotient.type),
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
|
|
|
|
quotient = emitDstOperandModifiers(quotient, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[0], quotient);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ins.dst[1].type != DxbcOperandType::Null) {
|
|
|
|
DxbcRegisterValue remainder;
|
|
|
|
remainder.type.ctype = ins.dst[1].dataType;
|
|
|
|
remainder.type.ccount = ins.dst[1].mask.setCount();
|
|
|
|
|
|
|
|
remainder.id = m_module.opUMod(
|
|
|
|
getVectorTypeId(remainder.type),
|
|
|
|
src.at(0).id, src.at(1).id);
|
|
|
|
|
|
|
|
remainder = emitDstOperandModifiers(remainder, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[1], remainder);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
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
|
2017-12-18 16:41:05 +01:00
|
|
|
// like any other three-operand ALU instruction
|
2017-12-18 00:46:44 +01:00
|
|
|
const std::array<DxbcRegisterValue, 2> src = {
|
|
|
|
emitRegisterLoad(ins.src[0], ins.dst[1].mask),
|
|
|
|
emitRegisterLoad(ins.src[1], ins.dst[1].mask),
|
|
|
|
};
|
2017-12-13 16:35:01 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
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);
|
2017-12-13 16:35:01 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
result = emitDstOperandModifiers(result, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[1], result);
|
|
|
|
} else {
|
|
|
|
// TODO implement this
|
|
|
|
Logger::warn("DxbcCompiler: Extended Imul not yet supported");
|
2017-12-13 16:35:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-20 23:50:39 +01:00
|
|
|
void DxbcCompiler::emitVectorShift(const DxbcShaderInstruction& ins) {
|
|
|
|
// Shift operations have three operands:
|
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) The register to shift
|
|
|
|
// (src1) The shift amount (scalar)
|
|
|
|
const DxbcRegisterValue shiftReg = emitRegisterLoad(
|
|
|
|
ins.src[0], ins.dst[0].mask);
|
|
|
|
|
|
|
|
DxbcRegisterValue countReg = emitRegisterLoad(
|
|
|
|
ins.src[1], DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
// Unlike in DXBC, SPIR-V shift operations allow different
|
|
|
|
// shift amounts per component, so we'll extend the count
|
|
|
|
// register to a vector.
|
|
|
|
countReg = emitRegisterExtend(countReg, shiftReg.type.ccount);
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = ins.dst[0].dataType;
|
|
|
|
result.type.ccount = ins.dst[0].mask.setCount();
|
|
|
|
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::IShl:
|
|
|
|
result.id = m_module.opShiftLeftLogical(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
shiftReg.id, countReg.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::IShr:
|
|
|
|
result.id = m_module.opShiftRightArithmetic(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
shiftReg.id, countReg.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DxbcOpcode::UShr:
|
|
|
|
result.id = m_module.opShiftRightLogical(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
shiftReg.id, countReg.id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = emitDstOperandModifiers(result, ins.modifiers);
|
|
|
|
emitRegisterStore(ins.dst[0], result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVectorSinCos(const DxbcShaderInstruction& ins) {
|
|
|
|
// sincos has three operands:
|
|
|
|
// (dst0) Destination register for sin(x)
|
|
|
|
// (dst1) Destination register for cos(x)
|
|
|
|
// (src0) Source operand x
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// Load source operand as 32-bit float vector.
|
|
|
|
const DxbcRegisterValue srcValue = emitRegisterLoad(
|
|
|
|
ins.src[0], DxbcRegMask(true, true, true, true));
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// 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);
|
|
|
|
}
|
2017-12-08 17:08:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
void DxbcCompiler::emitGeometryEmit(const DxbcShaderInstruction& ins) {
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::Emit: {
|
2017-12-21 16:00:36 +01:00
|
|
|
emitOutputSetup();
|
2017-12-18 16:41:05 +01:00
|
|
|
m_module.opEmitVertex();
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcOpcode::Cut: {
|
|
|
|
m_module.opEndPrimitive();
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
void DxbcCompiler::emitAtomic(const DxbcShaderInstruction& ins) {
|
|
|
|
Logger::err("DxbcCompiler: emitAtomic not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitBufferLoad(const DxbcShaderInstruction& ins) {
|
|
|
|
// ld_raw takes three arguments:
|
|
|
|
// (dst0) Destination register
|
|
|
|
// (src0) Byte offset
|
|
|
|
// (src1) Source register
|
|
|
|
// ld_structured takes four arguments:
|
|
|
|
// (dst0) Destination register
|
|
|
|
// (src0) Structure index
|
|
|
|
// (src1) Byte offset
|
|
|
|
// (src2) Source register
|
|
|
|
const bool isStructured = ins.op == DxbcOpcode::LdStructured;
|
|
|
|
|
|
|
|
// Source register. The exact way we access
|
|
|
|
// the data depends on the register type.
|
|
|
|
const DxbcRegister& dstReg = ins.dst[0];
|
|
|
|
const DxbcRegister& srcReg = isStructured ? ins.src[2] : ins.src[1];
|
|
|
|
|
|
|
|
// Retrieve common info about the buffer
|
|
|
|
const DxbcBufferInfo bufferInfo = getBufferInfo(srcReg);
|
|
|
|
|
|
|
|
// Compute element index
|
|
|
|
const DxbcRegisterValue elementIndex = isStructured
|
|
|
|
? emitCalcBufferIndexStructured(
|
|
|
|
emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false)),
|
|
|
|
emitRegisterLoad(ins.src[1], DxbcRegMask(true, false, false, false)),
|
|
|
|
bufferInfo.stride)
|
|
|
|
: emitCalcBufferIndexRaw(
|
|
|
|
emitRegisterLoad(ins.src[0], DxbcRegMask(true, false, false, false)));
|
|
|
|
|
|
|
|
emitRegisterStore(dstReg,
|
|
|
|
emitRawBufferLoad(srcReg, elementIndex, dstReg.mask));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitBufferStore(const DxbcShaderInstruction& ins) {
|
|
|
|
Logger::err("DxbcCompiler: emitBufferStore not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-27 01:37:15 +01:00
|
|
|
void DxbcCompiler::emitTextureQuery(const DxbcShaderInstruction& ins) {
|
|
|
|
// resinfo has three operands:
|
2017-12-28 16:03:17 +01:00
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) Resource LOD to query
|
|
|
|
// (src1) Resource to query
|
2017-12-27 01:37:15 +01:00
|
|
|
const DxbcResinfoType resinfoType = ins.controls.resinfoType;
|
|
|
|
|
|
|
|
if (ins.src[1].type != DxbcOperandType::Resource) {
|
|
|
|
Logger::err("DxbcCompiler: resinfo: UAVs not yet supported");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO support UAVs
|
|
|
|
const uint32_t textureId = ins.src[1].idx[0].offset;
|
|
|
|
const uint32_t imageId = m_module.opLoad(
|
|
|
|
m_textures.at(textureId).colorTypeId,
|
|
|
|
m_textures.at(textureId).varId);
|
|
|
|
|
|
|
|
// Read the exact LOD for the image query
|
|
|
|
const DxbcRegisterValue mipLod = emitRegisterLoad(
|
|
|
|
ins.src[0], DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
// Image type, which stores the image dimensions etc.
|
|
|
|
const DxbcImageInfo imageType = m_textures.at(textureId).imageInfo;
|
|
|
|
|
|
|
|
const uint32_t imageDim = [&] {
|
|
|
|
switch (imageType.dim) {
|
|
|
|
case spv::Dim1D: return 1;
|
|
|
|
case spv::Dim2D: return 2;
|
|
|
|
case spv::Dim3D: return 3;
|
|
|
|
case spv::DimCube: return 2;
|
|
|
|
default: throw DxvkError("DxbcCompiler: resinfo: Unsupported image dim");
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
const DxbcScalarType returnType = resinfoType == DxbcResinfoType::Uint
|
|
|
|
? DxbcScalarType::Uint32 : DxbcScalarType::Float32;
|
|
|
|
|
|
|
|
// Query image size. This will be written to the
|
|
|
|
// first components of the destination register.
|
|
|
|
DxbcRegisterValue imageSize;
|
|
|
|
imageSize.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
imageSize.type.ccount = imageDim + imageType.array;
|
|
|
|
imageSize.id = m_module.opImageQuerySizeLod(
|
|
|
|
getVectorTypeId(imageSize.type),
|
|
|
|
imageId, mipLod.id);
|
|
|
|
|
|
|
|
// Query image levels. This will be written to
|
|
|
|
// the w component of the destination register.
|
|
|
|
DxbcRegisterValue imageLevels;
|
|
|
|
imageLevels.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
imageLevels.type.ccount = 1;
|
|
|
|
imageLevels.id = m_module.opImageQueryLevels(
|
|
|
|
getVectorTypeId(imageLevels.type),
|
|
|
|
imageId);
|
|
|
|
|
|
|
|
// Convert intermediates to the requested type
|
|
|
|
if (returnType == DxbcScalarType::Float32) {
|
|
|
|
imageSize.type.ctype = DxbcScalarType::Float32;
|
|
|
|
imageSize.id = m_module.opConvertUtoF(
|
|
|
|
getVectorTypeId(imageSize.type),
|
|
|
|
imageSize.id);
|
|
|
|
|
|
|
|
imageLevels.type.ctype = DxbcScalarType::Float32;
|
|
|
|
imageLevels.id = m_module.opConvertUtoF(
|
|
|
|
getVectorTypeId(imageLevels.type),
|
|
|
|
imageLevels.id);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the selected return type is rcpFloat, we need
|
|
|
|
// to compute the reciprocal of the image dimensions,
|
|
|
|
// but not the array size, so we need to separate it.
|
|
|
|
DxbcRegisterValue imageLayers;
|
|
|
|
imageLayers.type = imageSize.type;
|
|
|
|
imageLayers.id = 0;
|
|
|
|
|
|
|
|
if (resinfoType == DxbcResinfoType::RcpFloat && imageType.array) {
|
|
|
|
imageLayers = emitRegisterExtract(imageSize, DxbcRegMask::select(imageDim));
|
|
|
|
imageSize = emitRegisterExtract(imageSize, DxbcRegMask::firstN(imageDim));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (resinfoType == DxbcResinfoType::RcpFloat) {
|
|
|
|
const uint32_t typeId = getVectorTypeId(imageSize.type);
|
|
|
|
|
|
|
|
const uint32_t one = m_module.constf32(1.0f);
|
|
|
|
std::array<uint32_t, 4> constIds = { one, one, one, one };
|
|
|
|
|
|
|
|
imageSize.id = m_module.opFDiv(typeId,
|
|
|
|
m_module.constComposite(typeId,
|
|
|
|
imageSize.type.ccount, constIds.data()),
|
|
|
|
imageSize.id);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Concatenate result vectors and scalars to form a
|
|
|
|
// 4D vector. Unused components will be set to zero.
|
|
|
|
std::array<uint32_t, 4> vectorIds = { imageSize.id, 0, 0, 0 };
|
|
|
|
uint32_t numVectorIds = 1;
|
|
|
|
|
|
|
|
if (imageLayers.id != 0)
|
|
|
|
vectorIds[numVectorIds++] = imageLayers.id;
|
|
|
|
|
|
|
|
if (imageDim + imageType.array < 3) {
|
|
|
|
const uint32_t zero = returnType == DxbcScalarType::Uint32
|
|
|
|
? m_module.constu32(0)
|
|
|
|
: m_module.constf32(0.0f);
|
|
|
|
|
|
|
|
for (uint32_t i = imageDim + imageType.array; i < 3; i++)
|
|
|
|
vectorIds[numVectorIds++] = zero;
|
|
|
|
}
|
|
|
|
|
|
|
|
vectorIds[numVectorIds++] = imageLevels.id;
|
|
|
|
|
|
|
|
// Create the actual result vector
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = returnType;
|
|
|
|
result.type.ccount = 4;
|
|
|
|
|
|
|
|
result.id = m_module.opCompositeConstruct(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
numVectorIds, vectorIds.data());
|
|
|
|
|
|
|
|
// Swizzle components using the resource swizzle
|
|
|
|
// and the destination operand's write mask
|
|
|
|
result = emitRegisterSwizzle(result,
|
|
|
|
ins.src[1].swizzle, ins.dst[0].mask);
|
|
|
|
emitRegisterStore(ins.dst[0], result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitTextureFetch(const DxbcShaderInstruction& ins) {
|
|
|
|
// ld has three operands:
|
2017-12-28 16:03:17 +01:00
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) Source address
|
|
|
|
// (src1) Source texture
|
2017-12-27 01:37:15 +01:00
|
|
|
const uint32_t textureId = ins.src[1].idx[0].offset;
|
|
|
|
|
|
|
|
// Image type, which stores the image dimensions etc.
|
|
|
|
const DxbcImageInfo imageType = m_textures.at(textureId).imageInfo;
|
|
|
|
|
|
|
|
const uint32_t imageLayerDim = [&] {
|
|
|
|
switch (imageType.dim) {
|
|
|
|
case spv::DimBuffer: return 1;
|
|
|
|
case spv::Dim1D: return 1;
|
|
|
|
case spv::Dim2D: return 2;
|
|
|
|
case spv::Dim3D: return 3;
|
|
|
|
default: throw DxvkError("DxbcCompiler: ld: Unsupported image dim");
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
const DxbcRegMask coordArrayMask =
|
|
|
|
DxbcRegMask::firstN(imageLayerDim + imageType.array);
|
|
|
|
|
|
|
|
// Load the texture coordinates. The last component
|
|
|
|
// contains the LOD if the resource is an image.
|
|
|
|
const DxbcRegisterValue coord = emitRegisterLoad(
|
|
|
|
ins.src[0], DxbcRegMask(true, true, true, true));
|
|
|
|
|
|
|
|
// Additional image operands. This will store
|
|
|
|
// the LOD and the address offset if present.
|
|
|
|
SpirvImageOperands imageOperands;
|
|
|
|
|
|
|
|
if (ins.sampleControls.u != 0 || ins.sampleControls.v != 0 || ins.sampleControls.w != 0) {
|
|
|
|
const std::array<uint32_t, 3> offsetIds = {
|
|
|
|
imageLayerDim >= 1 ? m_module.consti32(ins.sampleControls.u) : 0,
|
|
|
|
imageLayerDim >= 2 ? m_module.consti32(ins.sampleControls.v) : 0,
|
|
|
|
imageLayerDim >= 3 ? m_module.consti32(ins.sampleControls.w) : 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
imageOperands.flags |= spv::ImageOperandsConstOffsetMask;
|
|
|
|
imageOperands.sConstOffset = m_module.constComposite(
|
|
|
|
getVectorTypeId({ DxbcScalarType::Sint32, imageLayerDim }),
|
|
|
|
imageLayerDim, offsetIds.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (imageType.dim != spv::DimBuffer) {
|
|
|
|
imageOperands.flags |= spv::ImageOperandsLodMask;
|
|
|
|
imageOperands.sLod = emitRegisterExtract(coord,
|
|
|
|
DxbcRegMask(false, false, false, true)).id;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load image variable, no sampler needed
|
|
|
|
const uint32_t imageId = m_module.opLoad(
|
|
|
|
m_textures.at(textureId).colorTypeId,
|
|
|
|
m_textures.at(textureId).varId);
|
|
|
|
|
|
|
|
// Reading a typed image or buffer view
|
|
|
|
// always returns a four-component vector.
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = m_textures.at(textureId).sampledType;
|
|
|
|
result.type.ccount = 4;
|
|
|
|
|
|
|
|
result.id = m_module.opImageFetch(
|
|
|
|
getVectorTypeId(result.type), imageId,
|
|
|
|
emitRegisterExtract(coord, coordArrayMask).id,
|
|
|
|
imageOperands);
|
|
|
|
|
|
|
|
// Swizzle components using the texture swizzle
|
|
|
|
// and the destination operand's write mask
|
|
|
|
result = emitRegisterSwizzle(result,
|
|
|
|
ins.src[1].swizzle, ins.dst[0].mask);
|
|
|
|
emitRegisterStore(ins.dst[0], result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitTextureSample(const DxbcShaderInstruction& ins) {
|
|
|
|
// TODO support remaining sample ops
|
2017-12-10 20:01:38 +01:00
|
|
|
|
2017-12-20 00:16:49 +01:00
|
|
|
// All sample instructions have at least these operands:
|
2017-12-28 16:03:17 +01:00
|
|
|
// (dst0) The destination register
|
|
|
|
// (src0) Texture coordinates
|
|
|
|
// (src1) The texture itself
|
|
|
|
// (src2) The sampler object
|
2017-12-18 00:46:44 +01:00
|
|
|
const DxbcRegister& texCoordReg = ins.src[0];
|
|
|
|
const DxbcRegister& textureReg = ins.src[1];
|
|
|
|
const DxbcRegister& samplerReg = ins.src[2];
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// Texture and sampler register IDs
|
|
|
|
const uint32_t textureId = textureReg.idx[0].offset;
|
|
|
|
const uint32_t samplerId = samplerReg.idx[0].offset;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
// Image type, which stores the image dimensions etc.
|
|
|
|
const DxbcImageInfo imageType = m_textures.at(textureId).imageInfo;
|
|
|
|
|
|
|
|
const uint32_t imageLayerDim = [&] {
|
|
|
|
switch (imageType.dim) {
|
|
|
|
case spv::DimBuffer: return 1;
|
|
|
|
case spv::Dim1D: return 1;
|
|
|
|
case spv::Dim2D: return 2;
|
|
|
|
case spv::Dim3D: return 3;
|
|
|
|
case spv::DimCube: return 3;
|
|
|
|
default: throw DxvkError("DxbcCompiler: Unsupported image dim");
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
const DxbcRegMask coordArrayMask =
|
|
|
|
DxbcRegMask::firstN(imageLayerDim + imageType.array);
|
|
|
|
|
|
|
|
const DxbcRegMask coordLayerMask =
|
|
|
|
DxbcRegMask::firstN(imageLayerDim);
|
2017-12-20 23:50:39 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// Load the texture coordinates. SPIR-V allows these
|
|
|
|
// to be float4 even if not all components are used.
|
2017-12-20 23:50:39 +01:00
|
|
|
const DxbcRegisterValue coord = emitRegisterLoad(texCoordReg, coordArrayMask);
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-20 23:50:39 +01:00
|
|
|
// Load reference value for depth-compare operations
|
2017-12-20 13:41:04 +01:00
|
|
|
const bool isDepthCompare = ins.op == DxbcOpcode::SampleC
|
|
|
|
|| ins.op == DxbcOpcode::SampleClz;
|
|
|
|
|
|
|
|
const DxbcRegisterValue referenceValue = isDepthCompare
|
|
|
|
? emitRegisterLoad(ins.src[3], DxbcRegMask(true, false, false, false))
|
|
|
|
: DxbcRegisterValue();
|
2017-12-20 00:16:49 +01:00
|
|
|
|
2017-12-20 23:50:39 +01:00
|
|
|
// Load explicit gradients for sample operations that require them
|
2017-12-21 17:14:11 +01:00
|
|
|
const bool hasExplicitGradients = ins.op == DxbcOpcode::SampleD;
|
2017-12-20 23:50:39 +01:00
|
|
|
|
2017-12-21 17:14:11 +01:00
|
|
|
const DxbcRegisterValue explicitGradientX = hasExplicitGradients
|
2017-12-20 23:50:39 +01:00
|
|
|
? emitRegisterLoad(ins.src[3], coordLayerMask)
|
|
|
|
: DxbcRegisterValue();
|
|
|
|
|
2017-12-21 17:14:11 +01:00
|
|
|
const DxbcRegisterValue explicitGradientY = hasExplicitGradients
|
2017-12-20 23:50:39 +01:00
|
|
|
? emitRegisterLoad(ins.src[4], coordLayerMask)
|
|
|
|
: DxbcRegisterValue();
|
|
|
|
|
2017-12-21 17:14:11 +01:00
|
|
|
// Explicit LOD value for certain sample operations
|
|
|
|
const bool hasExplicitLod = ins.op == DxbcOpcode::SampleL;
|
|
|
|
|
|
|
|
const DxbcRegisterValue explicitLod = hasExplicitLod
|
|
|
|
? emitRegisterLoad(ins.src[3], DxbcRegMask(true, false, false, false))
|
|
|
|
: DxbcRegisterValue();
|
|
|
|
|
2017-12-20 13:41:04 +01:00
|
|
|
// Determine the sampled image type based on the opcode.
|
2017-12-28 16:03:17 +01:00
|
|
|
// FIXME while this is in line what the offical glsl compiler
|
2017-12-20 13:41:04 +01:00
|
|
|
// does, this might actually violate the SPIR-V specification.
|
|
|
|
const uint32_t sampledImageType = isDepthCompare
|
|
|
|
? m_module.defSampledImageType(m_textures.at(textureId).depthTypeId)
|
|
|
|
: m_module.defSampledImageType(m_textures.at(textureId).colorTypeId);
|
2017-12-20 00:16:49 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// Combine the texture and the sampler into a sampled image
|
|
|
|
const uint32_t sampledImageId = m_module.opSampledImage(
|
|
|
|
sampledImageType,
|
|
|
|
m_module.opLoad(
|
2017-12-20 13:41:04 +01:00
|
|
|
m_textures.at(textureId).colorTypeId,
|
2017-12-18 00:46:44 +01:00
|
|
|
m_textures.at(textureId).varId),
|
|
|
|
m_module.opLoad(
|
|
|
|
m_samplers.at(samplerId).typeId,
|
|
|
|
m_samplers.at(samplerId).varId));
|
|
|
|
|
2017-12-20 20:21:44 +01:00
|
|
|
// Accumulate additional image operands. These are
|
|
|
|
// not part of the actual operand token in SPIR-V.
|
|
|
|
SpirvImageOperands imageOperands;
|
2017-12-21 12:37:20 +01:00
|
|
|
|
|
|
|
if (ins.sampleControls.u != 0 || ins.sampleControls.v != 0 || ins.sampleControls.w != 0) {
|
|
|
|
const std::array<uint32_t, 3> offsetIds = {
|
2017-12-21 16:00:36 +01:00
|
|
|
imageLayerDim >= 1 ? m_module.consti32(ins.sampleControls.u) : 0,
|
|
|
|
imageLayerDim >= 2 ? m_module.consti32(ins.sampleControls.v) : 0,
|
|
|
|
imageLayerDim >= 3 ? m_module.consti32(ins.sampleControls.w) : 0,
|
2017-12-21 12:37:20 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
imageOperands.flags |= spv::ImageOperandsConstOffsetMask;
|
|
|
|
imageOperands.sConstOffset = m_module.constComposite(
|
|
|
|
getVectorTypeId({ DxbcScalarType::Sint32, imageLayerDim }),
|
|
|
|
imageLayerDim, offsetIds.data());
|
|
|
|
}
|
2017-12-20 20:21:44 +01:00
|
|
|
|
2017-12-20 13:41:04 +01:00
|
|
|
// Sampling an image always returns a four-component
|
|
|
|
// vector, whereas depth-compare ops return a scalar.
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue result;
|
2017-12-20 13:41:04 +01:00
|
|
|
result.type.ctype = m_textures.at(textureId).sampledType;
|
|
|
|
result.type.ccount = isDepthCompare ? 1 : 4;
|
2017-12-20 00:16:49 +01:00
|
|
|
|
|
|
|
switch (ins.op) {
|
2017-12-20 20:21:44 +01:00
|
|
|
// Simple image sample operation
|
2017-12-20 00:16:49 +01:00
|
|
|
case DxbcOpcode::Sample: {
|
|
|
|
result.id = m_module.opImageSampleImplicitLod(
|
|
|
|
getVectorTypeId(result.type),
|
2017-12-20 20:21:44 +01:00
|
|
|
sampledImageId, coord.id,
|
|
|
|
imageOperands);
|
2017-12-20 00:16:49 +01:00
|
|
|
} break;
|
|
|
|
|
2017-12-20 20:21:44 +01:00
|
|
|
// Depth-compare operation
|
2017-12-20 14:54:24 +01:00
|
|
|
case DxbcOpcode::SampleC: {
|
|
|
|
result.id = m_module.opImageSampleDrefImplicitLod(
|
2017-12-20 20:21:44 +01:00
|
|
|
getVectorTypeId(result.type), sampledImageId, coord.id,
|
|
|
|
referenceValue.id, imageOperands);
|
2017-12-20 14:54:24 +01:00
|
|
|
} break;
|
|
|
|
|
2017-12-20 20:21:44 +01:00
|
|
|
// Depth-compare operation on mip level zero
|
2017-12-20 00:16:49 +01:00
|
|
|
case DxbcOpcode::SampleClz: {
|
2017-12-20 20:21:44 +01:00
|
|
|
imageOperands.flags |= spv::ImageOperandsLodMask;
|
|
|
|
imageOperands.sLod = m_module.constf32(0.0f);
|
|
|
|
|
2017-12-20 00:16:49 +01:00
|
|
|
result.id = m_module.opImageSampleDrefExplicitLod(
|
2017-12-20 20:21:44 +01:00
|
|
|
getVectorTypeId(result.type), sampledImageId, coord.id,
|
|
|
|
referenceValue.id, imageOperands);
|
2017-12-20 00:16:49 +01:00
|
|
|
} break;
|
|
|
|
|
2017-12-20 23:50:39 +01:00
|
|
|
// Sample operation with explicit gradients
|
|
|
|
case DxbcOpcode::SampleD: {
|
|
|
|
imageOperands.flags |= spv::ImageOperandsGradMask;
|
|
|
|
imageOperands.sGradX = explicitGradientX.id;
|
|
|
|
imageOperands.sGradY = explicitGradientY.id;
|
|
|
|
|
|
|
|
result.id = m_module.opImageSampleExplicitLod(
|
|
|
|
getVectorTypeId(result.type), sampledImageId, coord.id,
|
|
|
|
imageOperands);
|
|
|
|
} break;
|
|
|
|
|
2017-12-21 17:14:11 +01:00
|
|
|
// Sample operation with explicit LOD
|
|
|
|
case DxbcOpcode::SampleL: {
|
|
|
|
imageOperands.flags |= spv::ImageOperandsLodMask;
|
|
|
|
imageOperands.sLod = m_module.constf32(explicitLod.id);
|
|
|
|
|
|
|
|
result.id = m_module.opImageSampleExplicitLod(
|
|
|
|
getVectorTypeId(result.type), sampledImageId, coord.id,
|
|
|
|
imageOperands);
|
|
|
|
} break;
|
|
|
|
|
2017-12-20 00:16:49 +01:00
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
return;
|
|
|
|
}
|
2017-12-18 00:46:44 +01:00
|
|
|
|
|
|
|
// Swizzle components using the texture swizzle
|
|
|
|
// and the destination operand's write mask
|
2017-12-20 00:16:49 +01:00
|
|
|
if (result.type.ccount != 1) {
|
|
|
|
result = emitRegisterSwizzle(result,
|
|
|
|
textureReg.swizzle, ins.dst[0].mask);
|
|
|
|
}
|
2017-12-18 00:46:44 +01:00
|
|
|
|
|
|
|
emitRegisterStore(ins.dst[0], result);
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
|
|
|
|
void DxbcCompiler::emitControlFlowIf(const DxbcShaderInstruction& ins) {
|
|
|
|
// Load the first component of the condition
|
|
|
|
// operand and perform a zero test on it.
|
|
|
|
const DxbcRegisterValue condition = emitRegisterLoad(
|
|
|
|
ins.src[0], DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
const DxbcRegisterValue zeroTest = emitRegisterZeroTest(
|
|
|
|
condition, ins.controls.zeroTest);
|
|
|
|
|
|
|
|
// Declare the 'if' block. We do not know if there
|
|
|
|
// will be an 'else' block or not, so we'll assume
|
|
|
|
// that there is one and leave it empty otherwise.
|
|
|
|
DxbcCfgBlock block;
|
|
|
|
block.type = DxbcCfgBlockType::If;
|
|
|
|
block.b_if.labelIf = m_module.allocateId();
|
|
|
|
block.b_if.labelElse = m_module.allocateId();
|
|
|
|
block.b_if.labelEnd = m_module.allocateId();
|
|
|
|
block.b_if.hadElse = false;
|
|
|
|
m_controlFlowBlocks.push_back(block);
|
|
|
|
|
|
|
|
m_module.opSelectionMerge(
|
|
|
|
block.b_if.labelEnd,
|
|
|
|
spv::SelectionControlMaskNone);
|
|
|
|
|
|
|
|
m_module.opBranchConditional(
|
|
|
|
zeroTest.id,
|
|
|
|
block.b_if.labelIf,
|
|
|
|
block.b_if.labelElse);
|
|
|
|
|
|
|
|
m_module.opLabel(block.b_if.labelIf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitControlFlowElse(const DxbcShaderInstruction& ins) {
|
|
|
|
if (m_controlFlowBlocks.size() == 0
|
|
|
|
|| m_controlFlowBlocks.back().type != DxbcCfgBlockType::If
|
|
|
|
|| m_controlFlowBlocks.back().b_if.hadElse)
|
|
|
|
throw DxvkError("DxbcCompiler: 'Else' without 'If' found");
|
|
|
|
|
|
|
|
// Set the 'Else' flag so that we do
|
|
|
|
// not insert a dummy block on 'EndIf'
|
|
|
|
DxbcCfgBlock& block = m_controlFlowBlocks.back();
|
|
|
|
block.b_if.hadElse = true;
|
|
|
|
|
|
|
|
// Close the 'If' block by branching to
|
|
|
|
// the merge block we declared earlier
|
|
|
|
m_module.opBranch(block.b_if.labelEnd);
|
|
|
|
m_module.opLabel (block.b_if.labelElse);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitControlFlowEndIf(const DxbcShaderInstruction& ins) {
|
|
|
|
if (m_controlFlowBlocks.size() == 0
|
|
|
|
|| m_controlFlowBlocks.back().type != DxbcCfgBlockType::If)
|
|
|
|
throw DxvkError("DxbcCompiler: 'EndIf' without 'If' found");
|
|
|
|
|
|
|
|
// Remove the block from the stack, it's closed
|
|
|
|
const DxbcCfgBlock block = m_controlFlowBlocks.back();
|
|
|
|
m_controlFlowBlocks.pop_back();
|
|
|
|
|
|
|
|
// End the active 'if' or 'else' block
|
|
|
|
m_module.opBranch(block.b_if.labelEnd);
|
|
|
|
|
|
|
|
// If there was no 'else' block in this construct, we still
|
|
|
|
// have to declare it because we used it as a branch target.
|
|
|
|
if (!block.b_if.hadElse) {
|
|
|
|
m_module.opLabel (block.b_if.labelElse);
|
|
|
|
m_module.opBranch(block.b_if.labelEnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Declare the merge block
|
|
|
|
m_module.opLabel(block.b_if.labelEnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitControlFlowLoop(const DxbcShaderInstruction& ins) {
|
|
|
|
// Declare the 'loop' block
|
|
|
|
DxbcCfgBlock block;
|
|
|
|
block.type = DxbcCfgBlockType::Loop;
|
|
|
|
block.b_loop.labelHeader = m_module.allocateId();
|
|
|
|
block.b_loop.labelBegin = m_module.allocateId();
|
|
|
|
block.b_loop.labelContinue = m_module.allocateId();
|
|
|
|
block.b_loop.labelBreak = m_module.allocateId();
|
|
|
|
m_controlFlowBlocks.push_back(block);
|
|
|
|
|
|
|
|
m_module.opBranch(block.b_loop.labelHeader);
|
|
|
|
m_module.opLabel (block.b_loop.labelHeader);
|
|
|
|
|
|
|
|
m_module.opLoopMerge(
|
|
|
|
block.b_loop.labelBreak,
|
|
|
|
block.b_loop.labelContinue,
|
|
|
|
spv::LoopControlMaskNone);
|
|
|
|
|
|
|
|
m_module.opBranch(block.b_loop.labelBegin);
|
|
|
|
m_module.opLabel (block.b_loop.labelBegin);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitControlFlowEndLoop(const DxbcShaderInstruction& ins) {
|
|
|
|
if (m_controlFlowBlocks.size() == 0
|
|
|
|
|| m_controlFlowBlocks.back().type != DxbcCfgBlockType::Loop)
|
|
|
|
throw DxvkError("DxbcCompiler: 'EndLoop' without 'Loop' found");
|
|
|
|
|
|
|
|
// Remove the block from the stack, it's closed
|
|
|
|
const DxbcCfgBlock block = m_controlFlowBlocks.back();
|
|
|
|
m_controlFlowBlocks.pop_back();
|
|
|
|
|
|
|
|
// Declare the continue block
|
|
|
|
m_module.opBranch(block.b_loop.labelContinue);
|
|
|
|
m_module.opLabel (block.b_loop.labelContinue);
|
|
|
|
|
|
|
|
// Declare the merge block
|
|
|
|
m_module.opBranch(block.b_loop.labelHeader);
|
|
|
|
m_module.opLabel (block.b_loop.labelBreak);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitControlFlowBreakc(const DxbcShaderInstruction& ins) {
|
|
|
|
DxbcCfgBlock* loopBlock = cfgFindLoopBlock();
|
2017-12-18 00:46:44 +01:00
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
if (loopBlock == nullptr)
|
|
|
|
throw DxvkError("DxbcCompiler: 'Breakc' outside 'Loop' found");
|
|
|
|
|
|
|
|
// Perform zero test on the first component of the condition
|
|
|
|
const DxbcRegisterValue condition = emitRegisterLoad(
|
|
|
|
ins.src[0], DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
const DxbcRegisterValue zeroTest = emitRegisterZeroTest(
|
|
|
|
condition, ins.controls.zeroTest);
|
|
|
|
|
|
|
|
// We basically have to wrap this into an 'if' block
|
|
|
|
const uint32_t breakBlock = m_module.allocateId();
|
|
|
|
const uint32_t mergeBlock = m_module.allocateId();
|
|
|
|
|
|
|
|
m_module.opSelectionMerge(mergeBlock,
|
|
|
|
spv::SelectionControlMaskNone);
|
|
|
|
|
|
|
|
m_module.opBranchConditional(
|
|
|
|
zeroTest.id, breakBlock, mergeBlock);
|
|
|
|
|
|
|
|
m_module.opLabel(breakBlock);
|
|
|
|
m_module.opBranch(loopBlock->b_loop.labelBreak);
|
|
|
|
|
|
|
|
m_module.opLabel(mergeBlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitControlFlowRet(const DxbcShaderInstruction& ins) {
|
2017-12-18 00:46:44 +01:00
|
|
|
// TODO implement properly
|
|
|
|
m_module.opReturn();
|
|
|
|
m_module.functionEnd();
|
|
|
|
}
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
void DxbcCompiler::emitControlFlowDiscard(const DxbcShaderInstruction& ins) {
|
|
|
|
// Discard actually has an operand that determines
|
|
|
|
// whether or not the fragment should be discarded
|
|
|
|
const DxbcRegisterValue condition = emitRegisterLoad(
|
|
|
|
ins.src[0], DxbcRegMask(true, false, false, false));
|
|
|
|
|
|
|
|
const DxbcRegisterValue zeroTest = emitRegisterZeroTest(
|
|
|
|
condition, ins.controls.zeroTest);
|
|
|
|
|
|
|
|
// Insert a Pseudo-'If' block
|
|
|
|
const uint32_t discardBlock = m_module.allocateId();
|
|
|
|
const uint32_t mergeBlock = m_module.allocateId();
|
|
|
|
|
|
|
|
m_module.opSelectionMerge(mergeBlock,
|
|
|
|
spv::SelectionControlMaskNone);
|
|
|
|
|
|
|
|
m_module.opBranchConditional(
|
|
|
|
zeroTest.id, discardBlock, mergeBlock);
|
|
|
|
|
|
|
|
// OpKill terminates the block
|
|
|
|
m_module.opLabel(discardBlock);
|
|
|
|
m_module.opKill();
|
|
|
|
|
|
|
|
m_module.opLabel(mergeBlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
void DxbcCompiler::emitControlFlow(const DxbcShaderInstruction& ins) {
|
|
|
|
switch (ins.op) {
|
|
|
|
case DxbcOpcode::If:
|
|
|
|
return this->emitControlFlowIf(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::Else:
|
|
|
|
return this->emitControlFlowElse(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::EndIf:
|
|
|
|
return this->emitControlFlowEndIf(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::Loop:
|
|
|
|
return this->emitControlFlowLoop(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::EndLoop:
|
|
|
|
return this->emitControlFlowEndLoop(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::Breakc:
|
|
|
|
return this->emitControlFlowBreakc(ins);
|
|
|
|
|
|
|
|
case DxbcOpcode::Ret:
|
|
|
|
return this->emitControlFlowRet(ins);
|
2017-12-18 16:41:05 +01:00
|
|
|
|
|
|
|
case DxbcOpcode::Discard:
|
|
|
|
return this->emitControlFlowDiscard(ins);
|
2017-12-18 11:53:28 +01:00
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled instruction: ",
|
|
|
|
ins.op));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
|
|
|
|
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;
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterSwizzle(
|
|
|
|
DxbcRegisterValue value,
|
|
|
|
DxbcRegSwizzle swizzle,
|
|
|
|
DxbcRegMask writeMask) {
|
2017-12-13 15:32:54 +01:00
|
|
|
std::array<uint32_t, 4> indices;
|
2017-12-10 12:08:20 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
uint32_t dstIndex = 0;
|
2017-12-18 00:46:44 +01:00
|
|
|
|
|
|
|
for (uint32_t i = 0; i < value.type.ccount; i++) {
|
|
|
|
if (writeMask[i])
|
2017-12-13 15:32:54 +01:00
|
|
|
indices[dstIndex++] = swizzle[i];
|
|
|
|
}
|
2017-12-10 12:08:20 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// If the swizzle combined with the mask can be reduced
|
|
|
|
// to a no-op, we don't need to insert any instructions.
|
2017-12-18 00:46:44 +01:00
|
|
|
bool isIdentitySwizzle = dstIndex == value.type.ccount;
|
2017-12-10 12:08:20 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
for (uint32_t i = 0; i < dstIndex && isIdentitySwizzle; i++)
|
|
|
|
isIdentitySwizzle &= indices[i] == i;
|
|
|
|
|
|
|
|
if (isIdentitySwizzle)
|
2017-12-18 00:46:44 +01:00
|
|
|
return value;
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// Use OpCompositeExtract if the resulting vector contains
|
|
|
|
// only one component, and OpVectorShuffle if it is a vector.
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = value.type.ctype;
|
|
|
|
result.type.ccount = dstIndex;
|
|
|
|
|
|
|
|
const uint32_t typeId = getVectorTypeId(result.type);
|
2017-12-10 12:08:20 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
if (dstIndex == 1) {
|
2017-12-18 00:46:44 +01:00
|
|
|
result.id = m_module.opCompositeExtract(
|
|
|
|
typeId, value.id, 1, indices.data());
|
2017-12-13 15:32:54 +01:00
|
|
|
} else {
|
2017-12-18 00:46:44 +01:00
|
|
|
result.id = m_module.opVectorShuffle(
|
|
|
|
typeId, value.id, value.id,
|
2017-12-13 15:32:54 +01:00
|
|
|
dstIndex, indices.data());
|
|
|
|
}
|
2017-12-10 12:08:20 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
return result;
|
2017-12-10 12:08:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterExtract(
|
|
|
|
DxbcRegisterValue value,
|
|
|
|
DxbcRegMask mask) {
|
|
|
|
return emitRegisterSwizzle(value,
|
|
|
|
DxbcRegSwizzle(0, 1, 2, 3), mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterExtend(
|
|
|
|
DxbcRegisterValue value,
|
|
|
|
uint32_t size) {
|
2017-12-13 15:32:54 +01:00
|
|
|
if (size == 1)
|
2017-12-18 00:46:44 +01:00
|
|
|
return value;
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
std::array<uint32_t, 4> ids = {
|
2017-12-18 00:46:44 +01:00
|
|
|
value.id, value.id,
|
|
|
|
value.id, value.id,
|
2017-12-13 15:32:54 +01:00
|
|
|
};
|
2017-12-08 17:08:26 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = value.type.ctype;
|
|
|
|
result.type.ccount = size;
|
|
|
|
result.id = m_module.opCompositeConstruct(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
size, ids.data());
|
2017-12-13 15:32:54 +01:00
|
|
|
return result;
|
2017-12-08 17:08:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterAbsolute(
|
|
|
|
DxbcRegisterValue value) {
|
|
|
|
const uint32_t typeId = getVectorTypeId(value.type);
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
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");
|
|
|
|
}
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterNegate(
|
|
|
|
DxbcRegisterValue value) {
|
|
|
|
const uint32_t typeId = getVectorTypeId(value.type);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
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");
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterZeroTest(
|
|
|
|
DxbcRegisterValue value,
|
|
|
|
DxbcZeroTest test) {
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Bool;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
|
|
|
|
const uint32_t zeroId = m_module.constu32(0u);
|
|
|
|
const uint32_t typeId = getVectorTypeId(result.type);
|
|
|
|
|
|
|
|
result.id = test == DxbcZeroTest::TestZ
|
|
|
|
? m_module.opIEqual (typeId, value.id, zeroId)
|
|
|
|
: m_module.opINotEqual(typeId, value.id, zeroId);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
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;
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
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));
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterPointer DxbcCompiler::emitGetTempPtr(
|
2017-12-20 22:50:05 +01:00
|
|
|
const DxbcRegister& operand) {
|
2017-12-18 00:46:44 +01:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-20 22:50:05 +01:00
|
|
|
DxbcRegisterPointer DxbcCompiler::emitGetIndexableTempPtr(
|
|
|
|
const DxbcRegister& operand) {
|
|
|
|
// x# regs are indexed as follows:
|
|
|
|
// (0) register index (immediate)
|
|
|
|
// (1) element index (relative)
|
|
|
|
const uint32_t regId = operand.idx[0].offset;
|
|
|
|
|
|
|
|
const DxbcRegisterValue vectorId
|
|
|
|
= emitIndexLoad(operand.idx[1]);
|
|
|
|
|
|
|
|
DxbcRegisterInfo info;
|
|
|
|
info.type.ctype = DxbcScalarType::Float32;
|
|
|
|
info.type.ccount = m_xRegs[regId].ccount;
|
|
|
|
info.type.alength = 0;
|
|
|
|
info.sclass = spv::StorageClassPrivate;
|
|
|
|
|
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type.ctype = info.type.ctype;
|
|
|
|
result.type.ccount = info.type.ccount;
|
|
|
|
result.id = m_module.opAccessChain(
|
|
|
|
getPointerTypeId(info),
|
|
|
|
m_xRegs.at(regId).varId,
|
|
|
|
1, &vectorId.id);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
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)
|
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type.ctype = DxbcScalarType::Float32;
|
|
|
|
result.type.ccount = 4;
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
std::array<uint32_t, 2> indices = { 0, 0 };
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < operand.idxDim; i++)
|
|
|
|
indices.at(i) = emitIndexLoad(operand.idx[i]).id;
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
DxbcRegisterInfo info;
|
|
|
|
info.type.ctype = result.type.ctype;
|
|
|
|
info.type.ccount = result.type.ccount;
|
|
|
|
info.type.alength = 0;
|
|
|
|
info.sclass = spv::StorageClassPrivate;
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
result.id = m_module.opAccessChain(
|
|
|
|
getPointerTypeId(info), m_vArray,
|
|
|
|
operand.idxDim, indices.data());
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
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;
|
2017-12-13 15:32:54 +01:00
|
|
|
} else {
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type.ctype = DxbcScalarType::Float32;
|
|
|
|
result.type.ccount = 4;
|
|
|
|
result.id = m_oRegs.at(registerId);
|
|
|
|
return result;
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
2017-12-18 00:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterPointer DxbcCompiler::emitGetConstBufPtr(
|
|
|
|
const DxbcRegister& operand) {
|
|
|
|
// Constant buffers take a two-dimensional index:
|
|
|
|
// (0) register index (immediate)
|
|
|
|
// (1) constant offset (relative)
|
|
|
|
DxbcRegisterInfo info;
|
2017-12-18 16:41:05 +01:00
|
|
|
info.type.ctype = DxbcScalarType::Float32;
|
|
|
|
info.type.ccount = 4;
|
|
|
|
info.type.alength = 0;
|
2017-12-18 00:46:44 +01:00
|
|
|
info.sclass = spv::StorageClassUniform;
|
|
|
|
|
|
|
|
const uint32_t regId = operand.idx[0].offset;
|
|
|
|
const DxbcRegisterValue constId = emitIndexLoad(operand.idx[1]);
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t ptrTypeId = getPointerTypeId(info);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
const std::array<uint32_t, 2> indices = {
|
|
|
|
m_module.consti32(0), constId.id
|
|
|
|
};
|
|
|
|
|
|
|
|
DxbcRegisterPointer result;
|
2017-12-18 16:41:05 +01:00
|
|
|
result.type.ctype = info.type.ctype;
|
|
|
|
result.type.ccount = info.type.ccount;
|
2017-12-18 00:46:44 +01:00
|
|
|
result.id = m_module.opAccessChain(ptrTypeId,
|
|
|
|
m_constantBuffers.at(regId).varId,
|
|
|
|
indices.size(), indices.data());
|
2017-12-13 15:32:54 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-19 17:41:23 +01:00
|
|
|
DxbcRegisterPointer DxbcCompiler::emitGetImmConstBufPtr(
|
|
|
|
const DxbcRegister& operand) {
|
|
|
|
if (m_immConstBuf == 0)
|
|
|
|
throw DxvkError("DxbcCompiler: Immediate constant buffer not defined");
|
|
|
|
|
|
|
|
const DxbcRegisterValue constId
|
|
|
|
= emitIndexLoad(operand.idx[0]);
|
|
|
|
|
|
|
|
DxbcRegisterInfo ptrInfo;
|
|
|
|
ptrInfo.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
ptrInfo.type.ccount = 4;
|
|
|
|
ptrInfo.type.alength = 0;
|
|
|
|
ptrInfo.sclass = spv::StorageClassPrivate;
|
|
|
|
|
|
|
|
DxbcRegisterPointer result;
|
|
|
|
result.type.ctype = ptrInfo.type.ctype;
|
|
|
|
result.type.ccount = ptrInfo.type.ccount;
|
|
|
|
result.id = m_module.opAccessChain(
|
|
|
|
getPointerTypeId(ptrInfo),
|
|
|
|
m_immConstBuf, 1, &constId.id);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterPointer DxbcCompiler::emitGetOperandPtr(
|
|
|
|
const DxbcRegister& operand) {
|
|
|
|
switch (operand.type) {
|
|
|
|
case DxbcOperandType::Temp:
|
|
|
|
return emitGetTempPtr(operand);
|
|
|
|
|
2017-12-20 22:50:05 +01:00
|
|
|
case DxbcOperandType::IndexableTemp:
|
|
|
|
return emitGetIndexableTempPtr(operand);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
case DxbcOperandType::Input:
|
|
|
|
return emitGetInputPtr(operand);
|
|
|
|
|
|
|
|
case DxbcOperandType::Output:
|
|
|
|
return emitGetOutputPtr(operand);
|
|
|
|
|
|
|
|
case DxbcOperandType::ConstantBuffer:
|
|
|
|
return emitGetConstBufPtr(operand);
|
|
|
|
|
2017-12-19 17:41:23 +01:00
|
|
|
case DxbcOperandType::ImmediateConstantBuffer:
|
|
|
|
return emitGetImmConstBufPtr(operand);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
default:
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler: Unhandled operand type: ",
|
|
|
|
operand.type));
|
|
|
|
}
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRawBufferLoad(
|
|
|
|
const DxbcRegister& operand,
|
|
|
|
DxbcRegisterValue elementIndex,
|
|
|
|
DxbcRegMask writeMask) {
|
|
|
|
const DxbcBufferInfo bufferInfo = getBufferInfo(operand);
|
|
|
|
|
|
|
|
// Shared memory is the only type of buffer that
|
|
|
|
// is not accessed through a texel buffer view
|
|
|
|
const bool isTgsm = operand.type == DxbcOperandType::ThreadGroupSharedMemory;
|
|
|
|
|
|
|
|
const uint32_t bufferId = isTgsm
|
|
|
|
? 0 : m_module.opLoad(bufferInfo.typeId, bufferInfo.varId);
|
|
|
|
|
|
|
|
// Since all data is represented as a sequence of 32-bit
|
|
|
|
// integers, we have to load each component individually.
|
|
|
|
std::array<uint32_t, 4> componentIds = { 0, 0, 0, 0 };
|
|
|
|
std::array<uint32_t, 4> swizzleIds = { 0, 0, 0, 0 };
|
|
|
|
|
|
|
|
uint32_t componentIndex = 0;
|
|
|
|
|
|
|
|
const uint32_t vectorTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 4 });
|
|
|
|
const uint32_t scalarTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 1 });
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < 4; i++) {
|
|
|
|
// We'll apply both the write mask and the source operand swizzle
|
|
|
|
// immediately. Unused components are not loaded, and the scalar
|
|
|
|
// IDs are written to the array in the order they are requested.
|
|
|
|
if (writeMask[i]) {
|
|
|
|
const uint32_t swizzleIndex = operand.swizzle[i];
|
|
|
|
|
|
|
|
if (componentIds[swizzleIndex] == 0) {
|
|
|
|
// Add the component offset to the element index
|
|
|
|
const uint32_t elementIndexAdjusted = swizzleIndex != 0
|
|
|
|
? m_module.opIAdd(getVectorTypeId(elementIndex.type),
|
|
|
|
elementIndex.id, m_module.consti32(swizzleIndex))
|
|
|
|
: elementIndex.id;
|
|
|
|
|
|
|
|
// Load requested component from the buffer
|
|
|
|
componentIds[swizzleIndex] = [&] {
|
|
|
|
const uint32_t zero = 0;
|
|
|
|
|
|
|
|
switch (operand.type) {
|
|
|
|
case DxbcOperandType::Resource:
|
|
|
|
return m_module.opCompositeExtract(scalarTypeId,
|
|
|
|
m_module.opImageFetch(vectorTypeId,
|
|
|
|
bufferId, elementIndexAdjusted,
|
|
|
|
SpirvImageOperands()), 1, &zero);
|
|
|
|
|
|
|
|
case DxbcOperandType::UnorderedAccessView:
|
|
|
|
return m_module.opImageRead(
|
|
|
|
scalarTypeId, bufferId, elementIndexAdjusted,
|
|
|
|
SpirvImageOperands());
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw DxvkError("DxbcCompiler: Invalid operand type for strucured/raw load");
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append current component to the list of scalar IDs.
|
|
|
|
// These will be used to construct the resulting vector.
|
|
|
|
swizzleIds[componentIndex++] = componentIds[swizzleIndex];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create result vector
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = writeMask.setCount();
|
|
|
|
|
|
|
|
result.id = result.type.ccount > 1
|
|
|
|
? m_module.opCompositeConstruct(getVectorTypeId(result.type),
|
|
|
|
result.type.ccount, swizzleIds.data())
|
|
|
|
: swizzleIds[0];
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitRawBufferStore(
|
|
|
|
const DxbcRegister& operand,
|
|
|
|
DxbcRegisterValue elementIndex,
|
|
|
|
DxbcRegisterValue value) {
|
|
|
|
const DxbcBufferInfo bufferInfo = getBufferInfo(operand);
|
|
|
|
// TODO implement
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitCalcBufferIndexStructured(
|
|
|
|
DxbcRegisterValue structId,
|
|
|
|
DxbcRegisterValue structOffset,
|
|
|
|
uint32_t structStride) {
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Sint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
|
|
|
|
const uint32_t typeId = getVectorTypeId(result.type);
|
|
|
|
|
|
|
|
result.id = m_module.opShiftRightArithmetic(typeId,
|
|
|
|
m_module.opIAdd(typeId,
|
|
|
|
m_module.opIMul(typeId, structId.id,
|
|
|
|
m_module.consti32(structStride)),
|
|
|
|
structOffset.id),
|
|
|
|
m_module.consti32(2));
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitCalcBufferIndexRaw(
|
|
|
|
DxbcRegisterValue byteOffset) {
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Sint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
result.id = m_module.opShiftRightArithmetic(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
byteOffset.id,
|
|
|
|
m_module.consti32(2));
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::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;
|
|
|
|
}
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitValueLoad(
|
|
|
|
DxbcRegisterPointer ptr) {
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type = ptr.type;
|
|
|
|
result.id = m_module.opLoad(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
ptr.id);
|
2017-12-13 15:32:54 +01:00
|
|
|
return result;
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitValueStore(
|
|
|
|
DxbcRegisterPointer ptr,
|
|
|
|
DxbcRegisterValue value,
|
|
|
|
DxbcRegMask writeMask) {
|
2017-12-13 15:32:54 +01:00
|
|
|
// If the component types are not compatible,
|
|
|
|
// we need to bit-cast the source variable.
|
2017-12-18 00:46:44 +01:00
|
|
|
if (value.type.ctype != ptr.type.ctype)
|
|
|
|
value = emitRegisterBitcast(value, ptr.type.ctype);
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
// 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()) {
|
2017-12-13 15:32:54 +01:00
|
|
|
// Simple case: We write to the entire register
|
2017-12-18 00:46:44 +01:00
|
|
|
m_module.opStore(ptr.id, value.id);
|
2017-11-16 01:30:17 +01:00
|
|
|
} else {
|
2017-12-13 15:32:54 +01:00
|
|
|
// We only write to part of the destination
|
|
|
|
// register, so we need to load and modify it
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue tmp = emitValueLoad(ptr);
|
|
|
|
tmp = emitRegisterInsert(tmp, value, writeMask);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
m_module.opStore(ptr.id, tmp.id);
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitRegisterLoad(
|
|
|
|
const DxbcRegister& reg,
|
|
|
|
DxbcRegMask writeMask) {
|
|
|
|
if (reg.type == DxbcOperandType::Imm32) {
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
|
|
|
|
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) {
|
2017-12-18 18:02:15 +01:00
|
|
|
// Create a u32 vector with as many components as needed
|
|
|
|
std::array<uint32_t, 4> indices;
|
|
|
|
uint32_t indexId = 0;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < indices.size(); i++) {
|
|
|
|
if (writeMask[i]) {
|
|
|
|
indices.at(indexId++) =
|
|
|
|
m_module.constu32(reg.imm.u32_4[i]);
|
|
|
|
}
|
|
|
|
}
|
2017-12-18 00:46:44 +01:00
|
|
|
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
2017-12-18 18:02:15 +01:00
|
|
|
result.type.ccount = writeMask.setCount();
|
|
|
|
result.id = indices.at(0);
|
|
|
|
|
|
|
|
if (indexId > 1) {
|
|
|
|
result.id = m_module.constComposite(
|
|
|
|
getVectorTypeId(result.type),
|
|
|
|
result.type.ccount, indices.data());
|
|
|
|
}
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
} 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;
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
2017-12-18 00:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitRegisterStore(
|
|
|
|
const DxbcRegister& reg,
|
|
|
|
DxbcRegisterValue value) {
|
|
|
|
emitValueStore(emitGetOperandPtr(reg), value, reg.mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
void DxbcCompiler::emitInputSetup() {
|
|
|
|
// Copy all defined v# registers into the input array
|
|
|
|
const uint32_t vecTypeId = m_module.defVectorType(m_module.defFloatType(32), 4);
|
|
|
|
const uint32_t ptrTypeId = m_module.defPointerType(vecTypeId, spv::StorageClassPrivate);
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
for (uint32_t i = 0; i < m_vRegs.size(); i++) {
|
|
|
|
if (m_vRegs.at(i) != 0) {
|
2017-12-21 16:00:36 +01:00
|
|
|
const uint32_t registerId = m_module.consti32(i);
|
2017-12-21 12:37:20 +01:00
|
|
|
m_module.opStore(
|
|
|
|
m_module.opAccessChain(ptrTypeId, m_vArray, 1, ®isterId),
|
|
|
|
m_module.opLoad(vecTypeId, m_vRegs.at(i)));
|
|
|
|
}
|
|
|
|
}
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
// Copy all system value registers into the array,
|
|
|
|
// preserving any previously written contents.
|
2017-12-21 16:00:36 +01:00
|
|
|
for (const DxbcSvMapping& map : m_vMappings) {
|
|
|
|
const uint32_t registerId = m_module.consti32(map.regId);
|
|
|
|
|
|
|
|
const DxbcRegisterValue value = [&] {
|
|
|
|
switch (m_version.type()) {
|
2017-12-28 16:03:17 +01:00
|
|
|
case DxbcProgramType::VertexShader: return emitVsSystemValueLoad(map.sv, map.regMask);
|
|
|
|
case DxbcProgramType::PixelShader: return emitPsSystemValueLoad(map.sv, map.regMask);
|
|
|
|
case DxbcProgramType::ComputeShader: return emitCsSystemValueLoad(map.sv, map.regMask);
|
2017-12-21 16:00:36 +01:00
|
|
|
default: throw DxvkError(str::format("DxbcCompiler: Unexpected stage: ", m_version.type()));
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
DxbcRegisterPointer inputReg;
|
|
|
|
inputReg.type.ctype = DxbcScalarType::Float32;
|
|
|
|
inputReg.type.ccount = 4;
|
|
|
|
inputReg.id = m_module.opAccessChain(
|
|
|
|
ptrTypeId, m_vArray, 1, ®isterId);
|
|
|
|
emitValueStore(inputReg, value, map.regMask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitInputSetup(uint32_t vertexCount) {
|
|
|
|
// Copy all defined v# registers into the input array. Note
|
|
|
|
// that the outer index of the array is the vertex index.
|
|
|
|
const uint32_t vecTypeId = m_module.defVectorType(m_module.defFloatType(32), 4);
|
|
|
|
const uint32_t dstPtrTypeId = m_module.defPointerType(vecTypeId, spv::StorageClassPrivate);
|
|
|
|
const uint32_t srcPtrTypeId = m_module.defPointerType(vecTypeId, spv::StorageClassInput);
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < m_vRegs.size(); i++) {
|
|
|
|
if (m_vRegs.at(i) != 0) {
|
|
|
|
const uint32_t registerId = m_module.consti32(i);
|
|
|
|
|
|
|
|
for (uint32_t v = 0; v < vertexCount; v++) {
|
|
|
|
std::array<uint32_t, 2> indices = {
|
|
|
|
m_module.consti32(v), registerId,
|
|
|
|
};
|
|
|
|
|
|
|
|
m_module.opStore(
|
|
|
|
m_module.opAccessChain(dstPtrTypeId,
|
|
|
|
m_vArray, indices.size(), indices.data()),
|
|
|
|
m_module.opLoad(vecTypeId,
|
|
|
|
m_module.opAccessChain(srcPtrTypeId,
|
|
|
|
m_vRegs.at(i), 1, indices.data())));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy all system value registers into the array,
|
|
|
|
// preserving any previously written contents.
|
|
|
|
for (const DxbcSvMapping& map : m_vMappings) {
|
|
|
|
const uint32_t registerId = m_module.consti32(map.regId);
|
|
|
|
|
|
|
|
for (uint32_t v = 0; v < vertexCount; v++) {
|
|
|
|
const DxbcRegisterValue value = [&] {
|
|
|
|
switch (m_version.type()) {
|
|
|
|
case DxbcProgramType::GeometryShader: return emitGsSystemValueLoad(map.sv, map.regMask, v);
|
|
|
|
default: throw DxvkError(str::format("DxbcCompiler: Unexpected stage: ", m_version.type()));
|
2017-12-21 12:37:20 +01:00
|
|
|
}
|
2017-12-21 16:00:36 +01:00
|
|
|
}();
|
|
|
|
|
|
|
|
std::array<uint32_t, 2> indices = {
|
|
|
|
m_module.consti32(v), registerId,
|
|
|
|
};
|
2017-12-18 16:41:05 +01:00
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
DxbcRegisterPointer inputReg;
|
|
|
|
inputReg.type.ctype = DxbcScalarType::Float32;
|
|
|
|
inputReg.type.ccount = 4;
|
|
|
|
inputReg.id = m_module.opAccessChain(dstPtrTypeId,
|
|
|
|
m_vArray, indices.size(), indices.data());
|
|
|
|
emitValueStore(inputReg, value, map.regMask);
|
2017-12-18 16:41:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
void DxbcCompiler::emitOutputSetup() {
|
2017-12-18 16:41:05 +01:00
|
|
|
for (const DxbcSvMapping& svMapping : m_oMappings) {
|
2017-12-21 16:00:36 +01:00
|
|
|
DxbcRegisterPointer outputReg;
|
|
|
|
outputReg.type.ctype = DxbcScalarType::Float32;
|
|
|
|
outputReg.type.ccount = 4;
|
|
|
|
outputReg.id = m_oRegs.at(svMapping.regId);
|
|
|
|
|
|
|
|
emitVsSystemValueStore(
|
|
|
|
svMapping.sv,
|
|
|
|
svMapping.regMask,
|
|
|
|
emitValueLoad(outputReg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitVsSystemValueLoad(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask) {
|
|
|
|
switch (sv) {
|
|
|
|
case DxbcSystemValue::VertexId: {
|
2017-12-27 12:49:25 +01:00
|
|
|
const uint32_t typeId = getScalarTypeId(DxbcScalarType::Uint32);
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
result.id = m_module.opISub(typeId,
|
|
|
|
m_module.opLoad(typeId, m_vs.builtinVertexId),
|
|
|
|
m_module.opLoad(typeId, m_vs.builtinBaseVertex));
|
|
|
|
return result;
|
2017-12-21 16:00:36 +01:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcSystemValue::InstanceId: {
|
2017-12-27 12:49:25 +01:00
|
|
|
const uint32_t typeId = getScalarTypeId(DxbcScalarType::Uint32);
|
|
|
|
|
|
|
|
DxbcRegisterValue result;
|
|
|
|
result.type.ctype = DxbcScalarType::Uint32;
|
|
|
|
result.type.ccount = 1;
|
|
|
|
result.id = m_module.opISub(typeId,
|
|
|
|
m_module.opLoad(typeId, m_vs.builtinInstanceId),
|
|
|
|
m_module.opLoad(typeId, m_vs.builtinBaseInstance));
|
|
|
|
return result;
|
2017-12-21 16:00:36 +01:00
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler: Unhandled VS SV input: ", sv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitGsSystemValueLoad(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask,
|
|
|
|
uint32_t vertexId) {
|
|
|
|
switch (sv) {
|
|
|
|
case DxbcSystemValue::Position: {
|
|
|
|
const std::array<uint32_t, 2> indices = {
|
|
|
|
m_module.consti32(vertexId),
|
|
|
|
m_module.consti32(PerVertex_Position),
|
|
|
|
};
|
|
|
|
|
|
|
|
DxbcRegisterPointer ptrIn;
|
|
|
|
ptrIn.type.ctype = DxbcScalarType::Float32;
|
|
|
|
ptrIn.type.ccount = 4;
|
|
|
|
|
|
|
|
ptrIn.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(
|
|
|
|
getVectorTypeId(ptrIn.type),
|
|
|
|
spv::StorageClassInput),
|
|
|
|
m_perVertexIn,
|
|
|
|
indices.size(),
|
|
|
|
indices.data());
|
|
|
|
|
|
|
|
return emitRegisterExtract(
|
|
|
|
emitValueLoad(ptrIn), mask);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler: Unhandled GS SV input: ", sv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DxbcRegisterValue DxbcCompiler::emitPsSystemValueLoad(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask) {
|
|
|
|
switch (sv) {
|
|
|
|
case DxbcSystemValue::Position: {
|
|
|
|
DxbcRegisterPointer ptrIn;
|
|
|
|
ptrIn.type.ctype = DxbcScalarType::Float32;
|
|
|
|
ptrIn.type.ccount = 4;
|
|
|
|
ptrIn.id = m_ps.builtinFragCoord;
|
|
|
|
|
|
|
|
return emitRegisterExtract(
|
|
|
|
emitValueLoad(ptrIn), mask);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler: Unhandled PS SV input: ", sv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
DxbcRegisterValue DxbcCompiler::emitCsSystemValueLoad(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask) {
|
|
|
|
switch (sv) {
|
|
|
|
default:
|
|
|
|
throw DxvkError(str::format(
|
|
|
|
"DxbcCompiler: Unhandled CS SV input: ", sv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
void DxbcCompiler::emitVsSystemValueStore(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask,
|
|
|
|
const DxbcRegisterValue& value) {
|
|
|
|
switch (sv) {
|
|
|
|
case DxbcSystemValue::Position: {
|
|
|
|
const uint32_t memberId = m_module.consti32(PerVertex_Position);
|
|
|
|
|
|
|
|
DxbcRegisterPointer ptr;
|
|
|
|
ptr.type.ctype = DxbcScalarType::Float32;
|
|
|
|
ptr.type.ccount = 4;
|
|
|
|
|
|
|
|
ptr.id = m_module.opAccessChain(
|
|
|
|
m_module.defPointerType(
|
|
|
|
getVectorTypeId(ptr.type),
|
|
|
|
spv::StorageClassOutput),
|
|
|
|
m_perVertexOut, 1, &memberId);
|
|
|
|
|
|
|
|
emitValueStore(ptr, value, mask);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled VS SV output: ", sv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitGsSystemValueStore(
|
|
|
|
DxbcSystemValue sv,
|
|
|
|
DxbcRegMask mask,
|
|
|
|
const DxbcRegisterValue& value) {
|
|
|
|
switch (sv) {
|
|
|
|
case DxbcSystemValue::Position:
|
|
|
|
case DxbcSystemValue::CullDistance:
|
|
|
|
case DxbcSystemValue::ClipDistance:
|
|
|
|
emitVsSystemValueStore(sv, mask, value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Logger::warn(str::format(
|
|
|
|
"DxbcCompiler: Unhandled GS SV output: ", sv));
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
void DxbcCompiler::emitVsInitBuiltins() {
|
|
|
|
m_vs.builtinVertexId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
2017-12-27 12:49:25 +01:00
|
|
|
spv::BuiltInVertexIndex,
|
2017-12-21 12:37:20 +01:00
|
|
|
"vs_vertex_index");
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
m_vs.builtinInstanceId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
2017-12-27 12:49:25 +01:00
|
|
|
spv::BuiltInInstanceIndex, // TODO test
|
2017-12-21 12:37:20 +01:00
|
|
|
"vs_instance_index");
|
2017-12-27 12:49:25 +01:00
|
|
|
|
|
|
|
m_vs.builtinBaseVertex = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInBaseVertex,
|
|
|
|
"vs_base_vertex");
|
|
|
|
|
|
|
|
m_vs.builtinBaseInstance = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInBaseInstance,
|
|
|
|
"vs_base_instance");
|
2017-12-21 12:37:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitGsInitBuiltins(uint32_t vertexCount) {
|
2017-12-21 17:27:40 +01:00
|
|
|
// TODO implement
|
2017-12-21 12:37:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DxbcCompiler::emitPsInitBuiltins() {
|
|
|
|
m_ps.builtinFragCoord = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Float32, 4, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInFragCoord,
|
|
|
|
"ps_frag_coord");
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 17:27:40 +01:00
|
|
|
void DxbcCompiler::emitCsInitBuiltins() {
|
2017-12-28 16:03:17 +01:00
|
|
|
m_cs.builtinGlobalInvocationId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 3, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInGlobalInvocationId,
|
|
|
|
"cs_global_invocation_id");
|
|
|
|
|
|
|
|
m_cs.builtinLocalInvocationId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 3, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInLocalInvocationId,
|
|
|
|
"cs_local_invocation_id");
|
|
|
|
|
|
|
|
// FIXME Vulkan might not support this? not documented
|
|
|
|
m_cs.builtinLocalInvocationIndex = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 1, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInLocalInvocationIndex,
|
|
|
|
"cs_local_invocation_index");
|
|
|
|
|
|
|
|
m_cs.builtinWorkgroupId = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 3, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInWorkgroupId,
|
|
|
|
"cs_workgroup_id");
|
|
|
|
|
|
|
|
m_cs.builtinWorkgroupSize = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 3, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInWorkgroupSize,
|
|
|
|
"cs_workgroup_size");
|
|
|
|
|
|
|
|
m_cs.builtinWorkgroupCount = emitNewBuiltinVariable({
|
|
|
|
{ DxbcScalarType::Uint32, 3, 0 },
|
|
|
|
spv::StorageClassInput },
|
|
|
|
spv::BuiltInNumWorkgroups,
|
|
|
|
"cs_workgroup_count");
|
2017-12-21 17:27:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVsInit() {
|
2017-12-13 15:32:54 +01:00
|
|
|
m_module.enableCapability(spv::CapabilityClipDistance);
|
2017-12-18 00:46:44 +01:00
|
|
|
m_module.enableCapability(spv::CapabilityCullDistance);
|
2017-12-27 14:31:38 +01:00
|
|
|
m_module.enableCapability(spv::CapabilityDrawParameters);
|
|
|
|
|
|
|
|
m_module.enableExtension("SPV_KHR_shader_draw_parameters");
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// Declare the per-vertex output block. This is where
|
|
|
|
// the vertex shader will write the vertex position.
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t perVertexStruct = this->getPerVertexBlockId();
|
|
|
|
const uint32_t perVertexPointer = m_module.defPointerType(
|
2017-12-13 15:32:54 +01:00
|
|
|
perVertexStruct, spv::StorageClassOutput);
|
|
|
|
|
|
|
|
m_perVertexOut = m_module.newVar(
|
|
|
|
perVertexPointer, spv::StorageClassOutput);
|
|
|
|
m_entryPointInterfaces.push_back(m_perVertexOut);
|
2017-12-18 00:46:44 +01:00
|
|
|
m_module.setDebugName(m_perVertexOut, "vs_vertex_out");
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
// Standard input array
|
|
|
|
emitDclInputArray(0);
|
|
|
|
emitVsInitBuiltins();
|
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// 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());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
void DxbcCompiler::emitGsInit() {
|
|
|
|
m_module.enableCapability(spv::CapabilityGeometry);
|
|
|
|
m_module.enableCapability(spv::CapabilityClipDistance);
|
|
|
|
m_module.enableCapability(spv::CapabilityCullDistance);
|
|
|
|
|
|
|
|
// Declare the per-vertex output block. Outputs are not
|
|
|
|
// declared as arrays, instead they will be flushed when
|
|
|
|
// calling EmitVertex.
|
|
|
|
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, "gs_vertex_out");
|
|
|
|
|
|
|
|
// Main function of the vertex shader
|
|
|
|
m_gs.functionId = m_module.allocateId();
|
|
|
|
m_module.setDebugName(m_gs.functionId, "gs_main");
|
|
|
|
|
|
|
|
m_module.functionBegin(
|
|
|
|
m_module.defVoidType(),
|
|
|
|
m_gs.functionId,
|
|
|
|
m_module.defFunctionType(
|
|
|
|
m_module.defVoidType(), 0, nullptr),
|
|
|
|
spv::FunctionControlMaskNone);
|
|
|
|
m_module.opLabel(m_module.allocateId());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitPsInit() {
|
2017-12-27 01:37:15 +01:00
|
|
|
m_module.enableCapability(
|
|
|
|
spv::CapabilityDerivativeControl);
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
m_module.setExecutionMode(m_entryPointId,
|
|
|
|
spv::ExecutionModeOriginUpperLeft);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
// 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++) {
|
2017-12-19 12:58:40 +01:00
|
|
|
if (e->systemValue == DxbcSystemValue::None
|
|
|
|
&& e->registerId != 0xFFFFFFFF /* depth */) {
|
2017-12-18 00:46:44 +01:00
|
|
|
DxbcRegisterInfo info;
|
2017-12-18 16:41:05 +01:00
|
|
|
info.type.ctype = e->componentType;
|
|
|
|
info.type.ccount = e->componentMask.setCount();
|
|
|
|
info.type.alength = 0;
|
2017-12-18 00:46:44 +01:00
|
|
|
info.sclass = spv::StorageClassOutput;
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
const uint32_t varId = emitNewVariable(info);
|
2017-12-13 15:32:54 +01:00
|
|
|
|
|
|
|
m_module.decorateLocation(varId, e->registerId);
|
|
|
|
m_module.setDebugName(varId, str::format("o", e->registerId).c_str());
|
|
|
|
m_entryPointInterfaces.push_back(varId);
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
m_oRegs.at(e->registerId) = varId;
|
2017-12-18 16:41:05 +01:00
|
|
|
m_ps.oTypes.at(e->registerId).ctype = info.type.ctype;
|
|
|
|
m_ps.oTypes.at(e->registerId).ccount = info.type.ccount;
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
2017-12-13 15:32:54 +01:00
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
// Standard input array
|
|
|
|
emitDclInputArray(0);
|
|
|
|
emitPsInitBuiltins();
|
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
// 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());
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 17:27:40 +01:00
|
|
|
void DxbcCompiler::emitCsInit() {
|
|
|
|
// There are no input or output
|
|
|
|
// variables for compute shaders
|
|
|
|
emitCsInitBuiltins();
|
|
|
|
|
|
|
|
// Main function of the compute shader
|
|
|
|
m_cs.functionId = m_module.allocateId();
|
2017-12-22 20:15:44 +01:00
|
|
|
m_module.setDebugName(m_cs.functionId, "cs_main");
|
2017-12-21 17:27:40 +01:00
|
|
|
|
|
|
|
m_module.functionBegin(
|
|
|
|
m_module.defVoidType(),
|
|
|
|
m_cs.functionId,
|
|
|
|
m_module.defFunctionType(
|
|
|
|
m_module.defVoidType(), 0, nullptr),
|
|
|
|
spv::FunctionControlMaskNone);
|
|
|
|
m_module.opLabel(m_module.allocateId());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitVsFinalize() {
|
2017-12-21 16:00:36 +01:00
|
|
|
this->emitInputSetup();
|
2017-12-18 00:46:44 +01:00
|
|
|
m_module.opFunctionCall(
|
|
|
|
m_module.defVoidType(),
|
|
|
|
m_vs.functionId, 0, nullptr);
|
2017-12-21 16:00:36 +01:00
|
|
|
this->emitOutputSetup();
|
2017-11-16 01:30:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
void DxbcCompiler::emitGsFinalize() {
|
2017-12-21 12:37:20 +01:00
|
|
|
this->emitInputSetup(
|
|
|
|
primitiveVertexCount(m_gs.inputPrimitive));
|
2017-12-18 16:41:05 +01:00
|
|
|
m_module.opFunctionCall(
|
|
|
|
m_module.defVoidType(),
|
|
|
|
m_gs.functionId, 0, nullptr);
|
|
|
|
// No output setup at this point as that was
|
|
|
|
// already done during the EmitVertex step
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
void DxbcCompiler::emitPsFinalize() {
|
2017-12-21 16:00:36 +01:00
|
|
|
this->emitInputSetup();
|
2017-12-18 00:46:44 +01:00
|
|
|
m_module.opFunctionCall(
|
|
|
|
m_module.defVoidType(),
|
|
|
|
m_ps.functionId, 0, nullptr);
|
2017-12-21 16:00:36 +01:00
|
|
|
this->emitOutputSetup();
|
2017-12-21 12:37:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 17:27:40 +01:00
|
|
|
void DxbcCompiler::emitCsFinalize() {
|
|
|
|
m_module.opFunctionCall(
|
|
|
|
m_module.defVoidType(),
|
|
|
|
m_cs.functionId, 0, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
void DxbcCompiler::emitDclInputArray(uint32_t vertexCount) {
|
|
|
|
DxbcArrayType info;
|
|
|
|
info.ctype = DxbcScalarType::Float32;
|
|
|
|
info.ccount = 4;
|
|
|
|
info.alength = DxbcMaxInterfaceRegs;
|
|
|
|
|
|
|
|
// Define the array type. This will be two-dimensional
|
|
|
|
// in some shaders, with the outer index representing
|
|
|
|
// the vertex ID within an invocation.
|
|
|
|
uint32_t arrayTypeId = getArrayTypeId(info);
|
|
|
|
|
|
|
|
if (vertexCount != 0) {
|
|
|
|
arrayTypeId = m_module.defArrayType(
|
|
|
|
arrayTypeId, m_module.constu32(vertexCount));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Define the actual variable. Note that this is private
|
|
|
|
// because we will copy input registers and some system
|
|
|
|
// variables to the array during the setup phase.
|
|
|
|
const uint32_t ptrTypeId = m_module.defPointerType(
|
|
|
|
arrayTypeId, spv::StorageClassPrivate);
|
|
|
|
|
|
|
|
const uint32_t varId = m_module.newVar(
|
|
|
|
ptrTypeId, spv::StorageClassPrivate);
|
|
|
|
|
|
|
|
m_module.setDebugName(varId, "shader_in");
|
|
|
|
m_vArray = varId;
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
void DxbcCompiler::emitDclInputPerVertex(
|
|
|
|
uint32_t vertexCount,
|
|
|
|
const char* varName) {
|
|
|
|
uint32_t typeId = getPerVertexBlockId();
|
|
|
|
|
|
|
|
if (vertexCount != 0) {
|
|
|
|
typeId = m_module.defArrayType(typeId,
|
|
|
|
m_module.constu32(vertexCount));
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint32_t ptrTypeId = m_module.defPointerType(
|
|
|
|
typeId, spv::StorageClassInput);
|
|
|
|
|
|
|
|
m_perVertexIn = m_module.newVar(
|
|
|
|
ptrTypeId, spv::StorageClassInput);
|
|
|
|
m_module.setDebugName(m_perVertexIn, varName);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
uint32_t DxbcCompiler::emitNewVariable(const DxbcRegisterInfo& info) {
|
|
|
|
const uint32_t ptrTypeId = this->getPointerTypeId(info);
|
|
|
|
return m_module.newVar(ptrTypeId, info.sclass);
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-21 12:37:20 +01:00
|
|
|
uint32_t DxbcCompiler::emitNewBuiltinVariable(
|
|
|
|
const DxbcRegisterInfo& info,
|
|
|
|
spv::BuiltIn builtIn,
|
|
|
|
const char* name) {
|
|
|
|
const uint32_t varId = emitNewVariable(info);
|
|
|
|
|
|
|
|
m_module.decorateBuiltIn(varId, builtIn);
|
|
|
|
m_module.setDebugName(varId, name);
|
|
|
|
|
|
|
|
m_entryPointInterfaces.push_back(varId);
|
|
|
|
return varId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 11:53:28 +01:00
|
|
|
DxbcCfgBlock* DxbcCompiler::cfgFindLoopBlock() {
|
|
|
|
for (auto cur = m_controlFlowBlocks.rbegin();
|
|
|
|
cur != m_controlFlowBlocks.rend(); cur++) {
|
|
|
|
if (cur->type == DxbcCfgBlockType::Loop)
|
|
|
|
return &(*cur);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-28 16:03:17 +01:00
|
|
|
DxbcBufferInfo DxbcCompiler::getBufferInfo(const DxbcRegister& reg) {
|
|
|
|
const uint32_t registerId = reg.idx[0].offset;
|
|
|
|
|
|
|
|
switch (reg.type) {
|
|
|
|
case DxbcOperandType::Resource: {
|
|
|
|
DxbcBufferInfo result;
|
|
|
|
result.type = m_textures.at(registerId).type;
|
|
|
|
result.typeId = m_textures.at(registerId).colorTypeId;
|
|
|
|
result.varId = m_textures.at(registerId).varId;
|
|
|
|
result.stride = m_textures.at(registerId).structStride;
|
|
|
|
return result;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case DxbcOperandType::UnorderedAccessView: {
|
|
|
|
DxbcBufferInfo result;
|
|
|
|
result.type = m_uavs.at(registerId).type;
|
|
|
|
result.typeId = m_uavs.at(registerId).imageTypeId;
|
|
|
|
result.varId = m_uavs.at(registerId).varId;
|
|
|
|
result.stride = m_uavs.at(registerId).structStride;
|
|
|
|
return result;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
// TODO implement
|
|
|
|
// case DxbcOperandType::ThreadGroupSharedMemory: {
|
|
|
|
// } break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw DxvkError(str::format("DxbcCompiler: Invalid operand type for buffer: ", reg.type));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
uint32_t DxbcCompiler::getScalarTypeId(DxbcScalarType type) {
|
|
|
|
switch (type) {
|
2017-12-18 11:53:28 +01:00
|
|
|
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);
|
2017-12-18 00:46:44 +01:00
|
|
|
case DxbcScalarType::Float32: return m_module.defFloatType(32);
|
|
|
|
case DxbcScalarType::Float64: return m_module.defFloatType(64);
|
2017-12-18 11:53:28 +01:00
|
|
|
case DxbcScalarType::Bool: return m_module.defBoolType();
|
2017-12-18 00:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
throw DxvkError("DxbcCompiler: Invalid scalar type");
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
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;
|
2017-11-17 11:41:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 16:41:05 +01:00
|
|
|
uint32_t DxbcCompiler::getArrayTypeId(const DxbcArrayType& type) {
|
|
|
|
DxbcVectorType vtype;
|
|
|
|
vtype.ctype = type.ctype;
|
|
|
|
vtype.ccount = type.ccount;
|
|
|
|
|
|
|
|
uint32_t typeId = this->getVectorTypeId(vtype);
|
|
|
|
|
|
|
|
if (type.alength != 0) {
|
|
|
|
typeId = m_module.defArrayType(typeId,
|
|
|
|
m_module.constu32(type.alength));
|
|
|
|
}
|
|
|
|
|
|
|
|
return typeId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
uint32_t DxbcCompiler::getPointerTypeId(const DxbcRegisterInfo& type) {
|
|
|
|
return m_module.defPointerType(
|
2017-12-18 16:41:05 +01:00
|
|
|
this->getArrayTypeId(type.type),
|
2017-12-18 00:46:44 +01:00
|
|
|
type.sclass);
|
2017-12-13 15:32:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-18 00:46:44 +01:00
|
|
|
uint32_t DxbcCompiler::getPerVertexBlockId() {
|
2017-12-13 15:32:54 +01:00
|
|
|
uint32_t t_f32 = m_module.defFloatType(32);
|
|
|
|
uint32_t t_f32_v4 = m_module.defVectorType(t_f32, 4);
|
2017-12-21 16:28:42 +01:00
|
|
|
uint32_t t_f32_a4 = m_module.defArrayType(t_f32, m_module.constu32(4));
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-12-21 16:00:36 +01:00
|
|
|
std::array<uint32_t, 3> members;
|
2017-12-21 16:28:42 +01:00
|
|
|
members[PerVertex_Position] = t_f32_v4;
|
|
|
|
members[PerVertex_CullDist] = t_f32_a4;
|
|
|
|
members[PerVertex_ClipDist] = t_f32_a4;
|
2017-11-16 01:30:17 +01:00
|
|
|
|
2017-12-13 15:32:54 +01:00
|
|
|
uint32_t typeId = m_module.defStructTypeUnique(
|
|
|
|
members.size(), members.data());
|
|
|
|
|
|
|
|
m_module.memberDecorateBuiltIn(typeId, PerVertex_Position, spv::BuiltInPosition);
|
|
|
|
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_CullDist, "cull_dist");
|
|
|
|
m_module.setDebugMemberName(typeId, PerVertex_ClipDist, "clip_dist");
|
|
|
|
return typeId;
|
|
|
|
}
|
|
|
|
|
2017-10-16 17:50:09 +02:00
|
|
|
}
|