From 6cc3ff4ad8bb4657a468ff3bba05b401783629d9 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 18 Dec 2017 16:41:05 +0100 Subject: [PATCH] [dxbc] Basic geometry shader (sm4) support --- src/d3d11/d3d11_shader.cpp | 11 +- src/dxbc/dxbc_compiler.cpp | 375 ++++++++++++++++++++++++++++++++----- src/dxbc/dxbc_compiler.h | 61 +++++- src/dxbc/dxbc_decoder.cpp | 21 ++- src/dxbc/dxbc_decoder.h | 27 ++- src/dxbc/dxbc_defs.cpp | 30 ++- src/dxbc/dxbc_defs.h | 2 + src/dxbc/dxbc_enums.h | 73 ++++++++ src/dxbc/dxbc_util.cpp | 21 +++ src/dxbc/dxbc_util.h | 10 + src/spirv/spirv_module.cpp | 94 +++++++++- src/spirv/spirv_module.h | 36 +++- 12 files changed, 675 insertions(+), 86 deletions(-) diff --git a/src/d3d11/d3d11_shader.cpp b/src/d3d11/d3d11_shader.cpp index 98b9693f..7ca0edc2 100644 --- a/src/d3d11/d3d11_shader.cpp +++ b/src/d3d11/d3d11_shader.cpp @@ -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 diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 94110cd7..f48596f8 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -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 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 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 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); } diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index 58fae510..dd8d0dc9 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -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 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); diff --git a/src/dxbc/dxbc_decoder.cpp b/src/dxbc/dxbc_decoder.cpp index 5b33a064..3a4d01c5 100644 --- a/src/dxbc/dxbc_decoder.cpp +++ b/src/dxbc/dxbc_decoder.cpp @@ -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 (bit::extract(token, 18, 18)); - m_instruction.controls.syncFlags = static_cast (bit::extract(token, 11, 14)); - m_instruction.controls.resourceDim = static_cast (bit::extract(token, 11, 15)); - m_instruction.controls.resinfoType = static_cast (bit::extract(token, 11, 12)); - m_instruction.controls.interpolation = static_cast(bit::extract(token, 11, 14)); + m_instruction.controls.zeroTest = + static_cast(bit::extract(token, 18, 18)); + m_instruction.controls.syncFlags = + static_cast(bit::extract(token, 11, 14)); + m_instruction.controls.resourceDim = + static_cast(bit::extract(token, 11, 15)); + m_instruction.controls.resinfoType = + static_cast(bit::extract(token, 11, 12)); + m_instruction.controls.interpolation = + static_cast(bit::extract(token, 11, 14)); + m_instruction.controls.samplerMode = + static_cast(bit::extract(token, 11, 14)); + m_instruction.controls.primitiveTopology = + static_cast(bit::extract(token, 11, 17)); + m_instruction.controls.primitive = + static_cast(bit::extract(token, 11, 16)); // Process extended opcode tokens while (bit::extract(token, 31, 31)) { diff --git a/src/dxbc/dxbc_decoder.h b/src/dxbc/dxbc_decoder.h index ad5b871e..6ee1818b 100644 --- a/src/dxbc/dxbc_decoder.h +++ b/src/dxbc/dxbc_decoder.h @@ -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; }; diff --git a/src/dxbc/dxbc_defs.cpp b/src/dxbc/dxbc_defs.cpp index 21920917..3709cfa3 100644 --- a/src/dxbc/dxbc_defs.cpp +++ b/src/dxbc/dxbc_defs.cpp @@ -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 }, diff --git a/src/dxbc/dxbc_defs.h b/src/dxbc/dxbc_defs.h index 61205477..0fc56725 100644 --- a/src/dxbc/dxbc_defs.h +++ b/src/dxbc/dxbc_defs.h @@ -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 diff --git a/src/dxbc/dxbc_enums.h b/src/dxbc/dxbc_enums.h index 86cddcb3..0c00d7c2 100644 --- a/src/dxbc/dxbc_enums.h +++ b/src/dxbc/dxbc_enums.h @@ -456,6 +456,79 @@ namespace dxvk { using DxbcSyncFlags = Flags; + + /** + * \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 * diff --git a/src/dxbc/dxbc_util.cpp b/src/dxbc/dxbc_util.cpp index 067e2894..b9adab94 100644 --- a/src/dxbc/dxbc_util.cpp +++ b/src/dxbc/dxbc_util.cpp @@ -41,4 +41,25 @@ namespace dxvk { return 0; } + uint32_t primitiveVertexCount(DxbcPrimitive primitive) { + static const std::array 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(primitive) + - static_cast(DxbcPrimitive::Patch1); + } else { + return s_vertexCounts.at( + static_cast(primitive)); + } + } + } \ No newline at end of file diff --git a/src/dxbc/dxbc_util.h b/src/dxbc/dxbc_util.h index 64f2c055..f3750267 100644 --- a/src/dxbc/dxbc_util.h +++ b/src/dxbc/dxbc_util.h @@ -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); + } \ No newline at end of file diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp index ff33582f..f856642b 100644 --- a/src/spirv/spirv_module.cpp +++ b/src/spirv/spirv_module.cpp @@ -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, diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h index f99fb6ef..f87160df 100644 --- a/src/spirv/spirv_module.h +++ b/src/spirv/spirv_module.h @@ -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;