[dxbc] Basic geometry shader (sm4) support

This commit is contained in:
Philip Rebohle 2017-12-18 16:41:05 +01:00
parent c44b50ae4d
commit 6cc3ff4ad8
12 changed files with 675 additions and 86 deletions

View File

@ -17,7 +17,6 @@ namespace dxvk {
BytecodeLength);
DxbcModule module(reader);
m_shader = module.compile();
// If requested by the user, dump both the raw DXBC
// shader and the compiled SPIR-V module to a file.
@ -31,12 +30,20 @@ namespace dxvk {
reader.store(std::ofstream(str::format(baseName, ".dxbc"),
std::ios_base::binary | std::ios_base::trunc));
}
m_shader = module.compile();
if (dumpPath.size() != 0) {
const std::string baseName = str::format(dumpPath, "/",
ConstructFileName(ComputeShaderHash(pShaderBytecode, BytecodeLength),
module.version().type()));
m_shader->dump(std::ofstream(str::format(baseName, ".spv"),
std::ios_base::binary | std::ios_base::trunc));
}
// If requested by the user, replace
// the shader with another file.
const std::string readPath

View File

@ -35,8 +35,9 @@ namespace dxvk {
// Initialize the shader module with capabilities
// etc. Each shader type has its own peculiarities.
switch (m_version.type()) {
case DxbcProgramType::VertexShader: this->emitVsInit(); break;
case DxbcProgramType::PixelShader: this->emitPsInit(); break;
case DxbcProgramType::VertexShader: this->emitVsInit(); break;
case DxbcProgramType::GeometryShader: this->emitGsInit(); break;
case DxbcProgramType::PixelShader: this->emitPsInit(); break;
default: throw DxvkError("DxbcCompiler: Unsupported program type");
}
}
@ -55,6 +56,9 @@ namespace dxvk {
case DxbcInstClass::ControlFlow:
return this->emitControlFlow(ins);
case DxbcInstClass::GeometryEmit:
return this->emitGeometryEmit(ins);
case DxbcInstClass::TextureSample:
return this->emitSample(ins);
@ -70,6 +74,9 @@ namespace dxvk {
case DxbcInstClass::VectorDot:
return this->emitVectorDot(ins);
case DxbcInstClass::VectorIdiv:
return this->emitVectorIdiv(ins);
case DxbcInstClass::VectorImul:
return this->emitVectorImul(ins);
@ -98,8 +105,9 @@ namespace dxvk {
// input registers, call various shader functions
// and write back the output registers.
switch (m_version.type()) {
case DxbcProgramType::VertexShader: this->emitVsFinalize(); break;
case DxbcProgramType::PixelShader: this->emitPsFinalize(); break;
case DxbcProgramType::VertexShader: this->emitVsFinalize(); break;
case DxbcProgramType::GeometryShader: this->emitGsFinalize(); break;
case DxbcProgramType::PixelShader: this->emitPsFinalize(); break;
default: throw DxvkError("DxbcCompiler: Unsupported program type");
}
@ -152,6 +160,15 @@ namespace dxvk {
case DxbcOpcode::DclResource:
return this->emitDclResource(ins);
case DxbcOpcode::DclGsInputPrimitive:
return this->emitDclGsInputPrimitive(ins);
case DxbcOpcode::DclGsOutputPrimitiveTopology:
return this->emitDclGsOutputTopology(ins);
case DxbcOpcode::DclMaxOutputVertexCount:
return this->emitDclMaxOutputVertexCount(ins);
default:
Logger::warn(
str::format("DxbcCompiler: Unhandled opcode: ",
@ -175,9 +192,10 @@ namespace dxvk {
m_rRegs.resize(newCount);
DxbcRegisterInfo info;
info.type.ctype = DxbcScalarType::Float32;
info.type.ccount = 4;
info.sclass = spv::StorageClassPrivate;
info.type.ctype = DxbcScalarType::Float32;
info.type.ccount = 4;
info.type.alength = 0;
info.sclass = spv::StorageClassPrivate;
for (uint32_t i = oldCount; i < newCount; i++) {
const uint32_t varId = this->emitNewVariable(info);
@ -274,21 +292,17 @@ namespace dxvk {
DxbcRegMask regMask,
DxbcSystemValue sv,
DxbcInterpolationMode im) {
if (regDim != 0) {
Logger::err("DxbcCompiler: Input arrays not yet supported");
return;
}
// Avoid declaring the same variable multiple times.
// This may happen when multiple system values are
// mapped to different parts of the same register.
if (m_vRegs.at(regIdx) == 0) {
DxbcRegisterInfo info;
info.type.ctype = DxbcScalarType::Float32;
info.type.ccount = 4;
info.type.ctype = DxbcScalarType::Float32;
info.type.ccount = 4;
info.type.alength = regDim;
info.sclass = spv::StorageClassInput;
const uint32_t varId = this->emitNewVariable(info);
uint32_t varId = this->emitNewVariable(info);
m_module.decorateLocation(varId, regIdx);
m_module.setDebugName(varId, str::format("v", regIdx).c_str());
@ -327,18 +341,14 @@ namespace dxvk {
DxbcRegMask regMask,
DxbcSystemValue sv,
DxbcInterpolationMode im) {
if (regDim != 0) {
Logger::err("DxbcCompiler: Output arrays not yet supported");
return;
}
// Avoid declaring the same variable multiple times.
// This may happen when multiple system values are
// mapped to different parts of the same register.
if (m_oRegs.at(regIdx) == 0) {
DxbcRegisterInfo info;
info.type.ctype = DxbcScalarType::Float32;
info.type.ccount = 4;
info.type.ctype = DxbcScalarType::Float32;
info.type.ccount = 4;
info.type.alength = regDim;
info.sclass = spv::StorageClassOutput;
const uint32_t varId = this->emitNewVariable(info);
@ -555,6 +565,50 @@ namespace dxvk {
}
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");
}
}();
m_module.setExecutionMode(m_entryPointId, mode);
}
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);
}
void DxbcCompiler::emitVectorAlu(const DxbcShaderInstruction& ins) {
std::array<DxbcRegisterValue, DxbcMaxOperandCount> src;
@ -824,6 +878,66 @@ namespace dxvk {
}
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);
}
}
void DxbcCompiler::emitVectorImul(const DxbcShaderInstruction& ins) {
// imul and umul have four operands:
// (dst0) High destination register
@ -835,7 +949,7 @@ namespace dxvk {
return;
// If dst0 is NULL, this instruction behaves just
// like any other three -operand ALU instruction
// like any other three-operand ALU instruction
const std::array<DxbcRegisterValue, 2> src = {
emitRegisterLoad(ins.src[0], ins.dst[1].mask),
emitRegisterLoad(ins.src[1], ins.dst[1].mask),
@ -897,8 +1011,26 @@ namespace dxvk {
}
void DxbcCompiler::emitSample(
const DxbcShaderInstruction& ins) {
void DxbcCompiler::emitGeometryEmit(const DxbcShaderInstruction& ins) {
switch (ins.op) {
case DxbcOpcode::Emit: {
emitGsOutputSetup();
m_module.opEmitVertex();
} break;
case DxbcOpcode::Cut: {
m_module.opEndPrimitive();
} break;
default:
Logger::warn(str::format(
"DxbcCompiler: Unhandled instruction: ",
ins.op));
}
}
void DxbcCompiler::emitSample(const DxbcShaderInstruction& ins) {
// TODO support address offset
// TODO support more sample ops
@ -1106,6 +1238,33 @@ namespace dxvk {
}
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);
}
void DxbcCompiler::emitControlFlow(const DxbcShaderInstruction& ins) {
switch (ins.op) {
case DxbcOpcode::If:
@ -1128,6 +1287,9 @@ namespace dxvk {
case DxbcOpcode::Ret:
return this->emitControlFlowRet(ins);
case DxbcOpcode::Discard:
return this->emitControlFlowDiscard(ins);
default:
Logger::warn(str::format(
@ -1368,16 +1530,30 @@ namespace dxvk {
// stages, the index has two dimensions:
// (0) vertex index (relative)
// (1) register index (relative)
if (operand.idxDim != 1)
throw DxvkError("DxbcCompiler: 2D index for v# not yet supported");
// We don't support two-dimensional indices yet
const uint32_t registerId = operand.idx[0].offset;
DxbcRegisterPointer result;
result.type.ctype = DxbcScalarType::Float32;
result.type.ccount = 4;
result.id = m_vRegs.at(registerId);
if (operand.idxDim == 1) {
// TODO add support for relative register index
result.id = m_vRegs.at(operand.idx[0].offset);
return result;
} else {
// TODO add support for relative register index
const DxbcRegisterValue vertexId = emitIndexLoad(operand.idx[0]);
DxbcRegisterInfo info;
info.type.ctype = result.type.ctype;
info.type.ccount = result.type.ccount;
info.type.alength = 0;
info.sclass = spv::StorageClassInput;
result.id = m_module.opAccessChain(
getPointerTypeId(info),
m_vRegs.at(operand.idx[1].offset),
1, &vertexId.id);
}
return result;
}
@ -1415,8 +1591,9 @@ namespace dxvk {
// (0) register index (immediate)
// (1) constant offset (relative)
DxbcRegisterInfo info;
info.type.ctype = DxbcScalarType::Float32;
info.type.ccount = 4;
info.type.ctype = DxbcScalarType::Float32;
info.type.ccount = 4;
info.type.alength = 0;
info.sclass = spv::StorageClassUniform;
const uint32_t regId = operand.idx[0].offset;
@ -1429,7 +1606,8 @@ namespace dxvk {
};
DxbcRegisterPointer result;
result.type = info.type;
result.type.ctype = info.type.ctype;
result.type.ccount = info.type.ccount;
result.id = m_module.opAccessChain(ptrTypeId,
m_constantBuffers.at(regId).varId,
indices.size(), indices.data());
@ -1585,6 +1763,11 @@ namespace dxvk {
}
void DxbcCompiler::emitGsInputSetup() {
}
void DxbcCompiler::emitPsInputSetup() {
}
@ -1595,21 +1778,61 @@ namespace dxvk {
switch (svMapping.sv) {
case DxbcSystemValue::Position: {
DxbcRegisterInfo info;
info.type.ctype = DxbcScalarType::Float32;
info.type.ccount = 4;
info.type.ctype = DxbcScalarType::Float32;
info.type.ccount = 4;
info.type.alength = 0;
info.sclass = spv::StorageClassOutput;
const uint32_t ptrTypeId = getPointerTypeId(info);
const uint32_t memberId = m_module.constu32(PerVertex_Position);
DxbcRegisterPointer dstPtr;
dstPtr.type = info.type;
dstPtr.id = m_module.opAccessChain(
dstPtr.type.ctype = info.type.ctype;
dstPtr.type.ccount = info.type.ccount;
dstPtr.id = m_module.opAccessChain(
ptrTypeId, m_perVertexOut, 1, &memberId);
DxbcRegisterPointer srcPtr;
srcPtr.type = info.type;
srcPtr.id = m_oRegs.at(svMapping.regId);
srcPtr.type.ctype = info.type.ctype;
srcPtr.type.ccount = info.type.ccount;
srcPtr.id = m_oRegs.at(svMapping.regId);
emitValueStore(dstPtr, emitValueLoad(srcPtr),
DxbcRegMask(true, true, true, true));
} break;
default:
Logger::warn(str::format(
"DxbcCompiler: Unhandled vertex sv output: ",
svMapping.sv));
}
}
}
void DxbcCompiler::emitGsOutputSetup() {
for (const DxbcSvMapping& svMapping : m_oMappings) {
switch (svMapping.sv) {
case DxbcSystemValue::Position: {
DxbcRegisterInfo info;
info.type.ctype = DxbcScalarType::Float32;
info.type.ccount = 4;
info.type.alength = 0;
info.sclass = spv::StorageClassOutput;
const uint32_t ptrTypeId = getPointerTypeId(info);
const uint32_t memberId = m_module.constu32(PerVertex_Position);
DxbcRegisterPointer dstPtr;
dstPtr.type.ctype = info.type.ctype;
dstPtr.type.ccount = info.type.ccount;
dstPtr.id = m_module.opAccessChain(
ptrTypeId, m_perVertexOut, 1, &memberId);
DxbcRegisterPointer srcPtr;
srcPtr.type.ctype = info.type.ctype;
srcPtr.type.ccount = info.type.ccount;
srcPtr.id = m_oRegs.at(svMapping.regId);
emitValueStore(dstPtr, emitValueLoad(srcPtr),
DxbcRegMask(true, true, true, true));
@ -1659,9 +1882,41 @@ namespace dxvk {
}
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());
}
void DxbcCompiler::emitPsInit() {
m_module.enableCapability(spv::CapabilityShader);
m_module.setOriginUpperLeft(m_entryPointId);
m_module.setExecutionMode(m_entryPointId,
spv::ExecutionModeOriginUpperLeft);
// Declare pixel shader outputs. According to the Vulkan
// documentation, they are required to match the type of
@ -1669,8 +1924,9 @@ namespace dxvk {
for (auto e = m_osgn->begin(); e != m_osgn->end(); e++) {
if (e->systemValue == DxbcSystemValue::None) {
DxbcRegisterInfo info;
info.type.ctype = e->componentType;
info.type.ccount = e->componentMask.setCount();
info.type.ctype = e->componentType;
info.type.ccount = e->componentMask.setCount();
info.type.alength = 0;
info.sclass = spv::StorageClassOutput;
const uint32_t varId = emitNewVariable(info);
@ -1680,7 +1936,8 @@ namespace dxvk {
m_entryPointInterfaces.push_back(varId);
m_oRegs.at(e->registerId) = varId;
m_ps.oTypes.at(e->registerId) = info.type;
m_ps.oTypes.at(e->registerId).ctype = info.type.ctype;
m_ps.oTypes.at(e->registerId).ccount = info.type.ccount;
}
}
@ -1707,6 +1964,16 @@ namespace dxvk {
}
void DxbcCompiler::emitGsFinalize() {
this->emitGsInputSetup();
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
}
void DxbcCompiler::emitPsFinalize() {
this->emitPsInputSetup();
m_module.opFunctionCall(
@ -1758,9 +2025,25 @@ namespace dxvk {
}
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;
}
uint32_t DxbcCompiler::getPointerTypeId(const DxbcRegisterInfo& type) {
return m_module.defPointerType(
this->getVectorTypeId(type.type),
this->getArrayTypeId(type.type),
type.sclass);
}

View File

@ -26,15 +26,30 @@ namespace dxvk {
};
/**
* \brief Array type
*
* Convenience struct that stores a scalar type, a
* component count and an array size. An array of
* length 0 will be evaluated to a vector type. The
* compiler can use this to generate SPIR-V types.
*/
struct DxbcArrayType {
DxbcScalarType ctype;
uint32_t ccount;
uint32_t alength;
};
/**
* \brief Register info
*
* Stores the vector type of a register and
* Stores the array type of a register and
* its storage class. The compiler can use
* this to generate SPIR-V pointer types.
*/
struct DxbcRegisterInfo {
DxbcVectorType type;
DxbcArrayType type;
spv::StorageClass sclass;
};
@ -69,7 +84,18 @@ namespace dxvk {
* \brief Vertex shader-specific structure
*/
struct DxbcCompilerVsPart {
uint32_t functionId;
uint32_t functionId = 0;
};
/**
* \brief Geometry shader-specific structure
*/
struct DxbcCompilerGsPart {
DxbcPrimitive inputPrimitive = DxbcPrimitive::Undefined;
DxbcPrimitiveTopology outputTopology = DxbcPrimitiveTopology::Undefined;
uint32_t outputVertexCount = 0;
uint32_t functionId = 0;
};
@ -77,7 +103,8 @@ namespace dxvk {
* \brief Pixel shader-specific structure
*/
struct DxbcCompilerPsPart {
uint32_t functionId;
uint32_t functionId = 0;
std::array<DxbcVectorType, DxbcMaxInterfaceRegs> oTypes;
};
@ -205,6 +232,7 @@ namespace dxvk {
///////////////////////////////////
// Shader-specific data structures
DxbcCompilerVsPart m_vs;
DxbcCompilerGsPart m_gs;
DxbcCompilerPsPart m_ps;
/////////////////////////////////////////////////////
@ -244,6 +272,15 @@ namespace dxvk {
void emitDclResource(
const DxbcShaderInstruction& ins);
void emitDclGsInputPrimitive(
const DxbcShaderInstruction& ins);
void emitDclGsOutputTopology(
const DxbcShaderInstruction& ins);
void emitDclMaxOutputVertexCount(
const DxbcShaderInstruction& ins);
//////////////////////////////
// Instruction class handlers
void emitVectorAlu(
@ -258,12 +295,18 @@ namespace dxvk {
void emitVectorDot(
const DxbcShaderInstruction& ins);
void emitVectorIdiv(
const DxbcShaderInstruction& ins);
void emitVectorImul(
const DxbcShaderInstruction& ins);
void emitVectorSinCos(
const DxbcShaderInstruction& ins);
void emitGeometryEmit(
const DxbcShaderInstruction& ins);
void emitSample(
const DxbcShaderInstruction& ins);
@ -290,6 +333,9 @@ namespace dxvk {
void emitControlFlowRet(
const DxbcShaderInstruction& ins);
void emitControlFlowDiscard(
const DxbcShaderInstruction& ins);
void emitControlFlow(
const DxbcShaderInstruction& ins);
@ -376,21 +422,25 @@ namespace dxvk {
/////////////////////////////
// Input preparation methods
void emitVsInputSetup();
void emitGsInputSetup();
void emitPsInputSetup();
//////////////////////////////
// Output preparation methods
void emitVsOutputSetup();
void emitGsOutputSetup();
void emitPsOutputSetup();
/////////////////////////////////
// Shader initialization methods
void emitVsInit();
void emitGsInit();
void emitPsInit();
///////////////////////////////
// Shader finalization methods
void emitVsFinalize();
void emitGsFinalize();
void emitPsFinalize();
///////////////////////////////
@ -410,6 +460,9 @@ namespace dxvk {
uint32_t getVectorTypeId(
const DxbcVectorType& type);
uint32_t getArrayTypeId(
const DxbcArrayType& type);
uint32_t getPointerTypeId(
const DxbcRegisterInfo& type);

View File

@ -80,11 +80,22 @@ namespace dxvk {
m_instruction.modifiers.precise = !!bit::extract(token, 19, 22);
// Opcode controls. It will depend on the opcode itself which ones are valid.
m_instruction.controls.zeroTest = static_cast<DxbcZeroTest> (bit::extract(token, 18, 18));
m_instruction.controls.syncFlags = static_cast<DxbcSyncFlags> (bit::extract(token, 11, 14));
m_instruction.controls.resourceDim = static_cast<DxbcResourceDim> (bit::extract(token, 11, 15));
m_instruction.controls.resinfoType = static_cast<DxbcResinfoType> (bit::extract(token, 11, 12));
m_instruction.controls.interpolation = static_cast<DxbcInterpolationMode>(bit::extract(token, 11, 14));
m_instruction.controls.zeroTest =
static_cast<DxbcZeroTest>(bit::extract(token, 18, 18));
m_instruction.controls.syncFlags =
static_cast<DxbcSyncFlags>(bit::extract(token, 11, 14));
m_instruction.controls.resourceDim =
static_cast<DxbcResourceDim>(bit::extract(token, 11, 15));
m_instruction.controls.resinfoType =
static_cast<DxbcResinfoType>(bit::extract(token, 11, 12));
m_instruction.controls.interpolation =
static_cast<DxbcInterpolationMode>(bit::extract(token, 11, 14));
m_instruction.controls.samplerMode =
static_cast<DxbcSamplerMode>(bit::extract(token, 11, 14));
m_instruction.controls.primitiveTopology =
static_cast<DxbcPrimitiveTopology>(bit::extract(token, 11, 17));
m_instruction.controls.primitive =
static_cast<DxbcPrimitive>(bit::extract(token, 11, 16));
// Process extended opcode tokens
while (bit::extract(token, 31, 31)) {

View File

@ -75,15 +75,18 @@ namespace dxvk {
DxbcRegSwizzle() { }
DxbcRegSwizzle(uint32_t x, uint32_t y, uint32_t z, uint32_t w)
: m_data((x << 0) | (y << 2) | (z << 4) | (w << 6)) { }
: m_mask((x << 0) | (y << 2) | (z << 4) | (w << 6)) { }
uint32_t operator [] (uint32_t id) const {
return (m_data >> (id + id)) & 0x3;
return (m_mask >> (id + id)) & 0x3;
}
bool operator == (const DxbcRegSwizzle& other) const { return m_mask == other.m_mask; }
bool operator != (const DxbcRegSwizzle& other) const { return m_mask != other.m_mask; }
private:
uint8_t m_data = 0;
uint8_t m_mask = 0;
};
@ -99,30 +102,33 @@ namespace dxvk {
public:
DxbcRegMask() { }
DxbcRegMask(uint32_t mask) : m_data(mask) { }
DxbcRegMask(uint32_t mask) : m_mask(mask) { }
DxbcRegMask(bool x, bool y, bool z, bool w)
: m_data((x ? 0x1 : 0) | (y ? 0x2 : 0)
: m_mask((x ? 0x1 : 0) | (y ? 0x2 : 0)
| (z ? 0x4 : 0) | (w ? 0x8 : 0)) { }
bool operator [] (uint32_t id) const {
return (m_data >> id) & 1;
return (m_mask >> id) & 1;
}
uint32_t setCount() const {
const uint8_t n[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
1, 2, 2, 3, 2, 3, 3, 4 };
return n[m_data & 0xF];
return n[m_mask & 0xF];
}
uint32_t firstSet() const {
const uint8_t n[16] = { 4, 0, 1, 0, 2, 0, 1, 0,
3, 0, 1, 0, 2, 0, 1, 0 };
return n[m_data & 0xF];
return n[m_mask & 0xF];
}
bool operator == (const DxbcRegMask& other) const { return m_mask == other.m_mask; }
bool operator != (const DxbcRegMask& other) const { return m_mask != other.m_mask; }
private:
uint8_t m_data = 0;
uint8_t m_mask = 0;
};
@ -194,6 +200,9 @@ namespace dxvk {
DxbcResourceDim resourceDim;
DxbcResinfoType resinfoType;
DxbcInterpolationMode interpolation;
DxbcSamplerMode samplerMode;
DxbcPrimitiveTopology primitiveTopology;
DxbcPrimitive primitive;
};

View File

@ -34,7 +34,7 @@ namespace dxvk {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Uint32 },
} },
/* Cut */
{ },
{ 0, DxbcInstClass::GeometryEmit },
/* Default */
{ },
/* DerivRtx */
@ -42,7 +42,9 @@ namespace dxvk {
/* DerivRty */
{ },
/* Discard */
{ },
{ 1, DxbcInstClass::ControlFlow, {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
} },
/* Div */
{ 3, DxbcInstClass::VectorAlu, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Float32 },
@ -70,7 +72,7 @@ namespace dxvk {
/* Else */
{ 0, DxbcInstClass::ControlFlow },
/* Emit */
{ },
{ 0, DxbcInstClass::GeometryEmit },
/* EmitThenCut */
{ },
/* EndIf */
@ -295,13 +297,23 @@ namespace dxvk {
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
} },
/* UDiv */
{ },
{ 4, DxbcInstClass::VectorIdiv, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Uint32 },
{ DxbcOperandKind::DstReg, DxbcScalarType::Uint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Uint32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Uint32 },
} },
/* ULt */
{ },
/* UGe */
{ },
/* UMul */
{ },
{ 4, DxbcInstClass::VectorImul, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Float32 },
{ DxbcOperandKind::DstReg, DxbcScalarType::Float32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
{ DxbcOperandKind::SrcReg, DxbcScalarType::Float32 },
} },
/* UMad */
{ },
/* UMax */
@ -330,11 +342,13 @@ namespace dxvk {
/* DclIndexRange */
{ },
/* DclGsOutputPrimitiveTopology */
{ },
{ 0, DxbcInstClass::Declaration },
/* DclGsInputPrimitive */
{ },
{ 0, DxbcInstClass::Declaration },
/* DclMaxOutputVertexCount */
{ },
{ 1, DxbcInstClass::Declaration, {
{ DxbcOperandKind::Imm32, DxbcScalarType::Uint32 },
} },
/* DclInput */
{ 1, DxbcInstClass::Declaration, {
{ DxbcOperandKind::DstReg, DxbcScalarType::Float32 },

View File

@ -30,11 +30,13 @@ namespace dxvk {
enum class DxbcInstClass {
Declaration, ///< Interface or resource declaration
ControlFlow, ///< Control flow instructions
GeometryEmit, ///< Special geometry shader instructions
TextureSample, ///< Texture sampling instruction
VectorAlu, ///< Component-wise vector instructions
VectorCmov, ///< Component-wise conditional move
VectorCmp, ///< Component-wise vector comparison
VectorDot, ///< Dot product instruction
VectorIdiv, ///< Component-wise integer division
VectorImul, ///< Component-wise integer multiplication
VectorSinCos, ///< Sine and Cosine instruction
Undefined, ///< Instruction code not defined

View File

@ -456,6 +456,79 @@ namespace dxvk {
using DxbcSyncFlags = Flags<DxbcSyncFlag>;
/**
* \brief Geometry shader input primitive
*/
enum class DxbcPrimitive : uint32_t {
Undefined = 0,
Point = 1,
Line = 2,
Triangle = 3,
LineAdj = 6,
TriangleAdj = 7,
Patch1 = 8,
Patch2 = 9,
Patch3 = 10,
Patch4 = 11,
Patch5 = 12,
Patch6 = 13,
Patch7 = 14,
Patch8 = 15,
Patch9 = 16,
Patch10 = 17,
Patch11 = 18,
Patch12 = 19,
Patch13 = 20,
Patch14 = 21,
Patch15 = 22,
Patch16 = 23,
Patch17 = 24,
Patch18 = 25,
Patch19 = 26,
Patch20 = 27,
Patch21 = 28,
Patch22 = 29,
Patch23 = 30,
Patch24 = 31,
Patch25 = 32,
Patch26 = 33,
Patch27 = 34,
Patch28 = 35,
Patch29 = 36,
Patch30 = 37,
Patch31 = 38,
Patch32 = 39,
};
/**
* \brief Geometry shader output topology
*/
enum class DxbcPrimitiveTopology : uint32_t {
Undefined = 0,
PointList = 1,
LineList = 2,
LineStrip = 3,
TriangleList = 4,
TriangleStrip = 5,
LineListAdj = 10,
LineStripAdj = 11,
TriangleListAdj = 12,
TriangleStripAdj = 13,
};
/**
* \brief Sampler operation mode
*/
enum class DxbcSamplerMode : uint32_t {
Default = 0,
Comparison = 1,
Mono = 2,
};
/**
* \brief Scalar value type
*

View File

@ -41,4 +41,25 @@ namespace dxvk {
return 0;
}
uint32_t primitiveVertexCount(DxbcPrimitive primitive) {
static const std::array<uint32_t, 8> s_vertexCounts = {
0, // Undefined
1, // Point
2, // Line
3, // Triangle
0, // Undefined
0, // Undefined
4, // Line with adjacency
6, // Triangle with adjacency
};
if (primitive >= DxbcPrimitive::Patch1) {
return static_cast<uint32_t>(primitive)
- static_cast<uint32_t>(DxbcPrimitive::Patch1);
} else {
return s_vertexCounts.at(
static_cast<uint32_t>(primitive));
}
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include "dxbc_common.h"
#include "dxbc_enums.h"
namespace dxvk {
@ -32,4 +33,13 @@ namespace dxvk {
DxbcBindingType bindingType,
uint32_t bindingIndex);
/**
* \brief Primitive vertex count
*
* Calculates the number of vertices
* for a given primitive type.
*/
uint32_t primitiveVertexCount(
DxbcPrimitive primitive);
}

View File

@ -72,12 +72,13 @@ namespace dxvk {
m_memoryModel.putWord (memoryModel);
}
void SpirvModule::enableEarlyFragmentTests(
uint32_t entryPointId) {
void SpirvModule::setExecutionMode(
uint32_t entryPointId,
spv::ExecutionMode executionMode) {
m_execModeInfo.putIns (spv::OpExecutionMode, 3);
m_execModeInfo.putWord(entryPointId);
m_execModeInfo.putWord(spv::ExecutionModeEarlyFragmentTests);
m_execModeInfo.putWord(executionMode);
}
@ -95,11 +96,13 @@ namespace dxvk {
}
void SpirvModule::setOriginUpperLeft(
uint32_t entryPointId) {
m_execModeInfo.putIns (spv::OpExecutionMode, 3);
void SpirvModule::setOutputVertices(
uint32_t entryPointId,
uint32_t vertexCount) {
m_execModeInfo.putIns (spv::OpExecutionMode, 4);
m_execModeInfo.putWord(entryPointId);
m_execModeInfo.putWord(spv::ExecutionModeOriginUpperLeft);
m_execModeInfo.putWord(spv::ExecutionModeOutputVertices);
m_execModeInfo.putWord(vertexCount);
}
@ -738,6 +741,66 @@ namespace dxvk {
}
uint32_t SpirvModule::opSDiv(
uint32_t resultType,
uint32_t a,
uint32_t b) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpSDiv, 5);
m_code.putWord(resultType);
m_code.putWord(resultId);
m_code.putWord(a);
m_code.putWord(b);
return resultId;
}
uint32_t SpirvModule::opUDiv(
uint32_t resultType,
uint32_t a,
uint32_t b) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpUDiv, 5);
m_code.putWord(resultType);
m_code.putWord(resultId);
m_code.putWord(a);
m_code.putWord(b);
return resultId;
}
uint32_t SpirvModule::opSRem(
uint32_t resultType,
uint32_t a,
uint32_t b) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpSRem, 5);
m_code.putWord(resultType);
m_code.putWord(resultId);
m_code.putWord(a);
m_code.putWord(b);
return resultId;
}
uint32_t SpirvModule::opUMod(
uint32_t resultType,
uint32_t a,
uint32_t b) {
uint32_t resultId = this->allocateId();
m_code.putIns (spv::OpUMod, 5);
m_code.putWord(resultType);
m_code.putWord(resultId);
m_code.putWord(a);
m_code.putWord(b);
return resultId;
}
uint32_t SpirvModule::opFDiv(
uint32_t resultType,
uint32_t a,
@ -1310,6 +1373,21 @@ namespace dxvk {
}
void SpirvModule::opKill() {
m_code.putIns (spv::OpKill, 1);
}
void SpirvModule::opEmitVertex() {
m_code.putIns (spv::OpEmitVertex, 1);
}
void SpirvModule::opEndPrimitive() {
m_code.putIns (spv::OpEndPrimitive, 1);
}
uint32_t SpirvModule::defType(
spv::Op op,
uint32_t argCount,

View File

@ -37,8 +37,9 @@ namespace dxvk {
spv::AddressingModel addressModel,
spv::MemoryModel memoryModel);
void enableEarlyFragmentTests(
uint32_t entryPointId);
void setExecutionMode(
uint32_t entryPointId,
spv::ExecutionMode executionMode);
void setLocalSize(
uint32_t entryPointId,
@ -46,8 +47,9 @@ namespace dxvk {
uint32_t y,
uint32_t z);
void setOriginUpperLeft(
uint32_t entryPointId);
void setOutputVertices(
uint32_t entryPointId,
uint32_t vertexCount);
void setDebugName(
uint32_t expressionId,
@ -265,6 +267,26 @@ namespace dxvk {
uint32_t a,
uint32_t b);
uint32_t opSDiv(
uint32_t resultType,
uint32_t a,
uint32_t b);
uint32_t opUDiv(
uint32_t resultType,
uint32_t a,
uint32_t b);
uint32_t opSRem(
uint32_t resultType,
uint32_t a,
uint32_t b);
uint32_t opUMod(
uint32_t resultType,
uint32_t a,
uint32_t b);
uint32_t opFDiv(
uint32_t resultType,
uint32_t a,
@ -453,6 +475,12 @@ namespace dxvk {
void opReturn();
void opKill();
void opEmitVertex();
void opEndPrimitive();
private:
uint32_t m_id = 1;