From 2e4275649e9811c45e6702ce0ad9b46edd594360 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 21 Dec 2017 12:37:20 +0100 Subject: [PATCH] [dxbc] Implemented input mapping + sample controls Input variables are now copied into a temporary array, which allows dynamic indexing and which also allows us to use system values that are mapped to input registers in DXBC. This breaks geometry shaders for now, however. --- src/dxbc/dxbc_compiler.cpp | 306 ++++++++++++++++++++++++++----------- src/dxbc/dxbc_compiler.h | 35 +++-- src/dxbc/dxbc_decoder.h | 26 +++- 3 files changed, 259 insertions(+), 108 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 0127adca..e5e51b83 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -339,14 +339,14 @@ namespace dxvk { // 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) { + if (m_vRegs.at(regIdx) == 0 && sv == DxbcSystemValue::None) { DxbcRegisterInfo info; info.type.ctype = DxbcScalarType::Float32; info.type.ccount = 4; info.type.alength = regDim; info.sclass = spv::StorageClassInput; - uint32_t varId = this->emitNewVariable(info); + const uint32_t varId = emitNewVariable(info); m_module.decorateLocation(varId, regIdx); m_module.setDebugName(varId, str::format("v", regIdx).c_str()); @@ -370,12 +370,10 @@ namespace dxvk { if (im == DxbcInterpolationMode::LinearSample || im == DxbcInterpolationMode::LinearNoPerspectiveSample) m_module.decorate(varId, spv::DecorationSample); - } - - // Add a new system value mapping if needed - // TODO declare SV if necessary - if (sv != DxbcSystemValue::None) + } else if (sv != DxbcSystemValue::None) { + // Add a new system value mapping if needed m_vMappings.push_back({ regIdx, regMask, sv }); + } } @@ -406,7 +404,6 @@ namespace dxvk { // Add a new system value mapping if needed - // TODO declare SV if necessary if (sv != DxbcSystemValue::None) m_oMappings.push_back({ regIdx, regMask, sv }); } @@ -533,14 +530,7 @@ namespace dxvk { const uint32_t sampledTypeId = getScalarTypeId(sampledType); // Declare the resource type - struct ImageTypeInfo { - spv::Dim dim; - uint32_t array; - uint32_t ms; - uint32_t sampled; - }; - - const ImageTypeInfo typeInfo = [resourceType] () -> ImageTypeInfo { + const DxbcImageInfo typeInfo = [resourceType] () -> DxbcImageInfo { switch (resourceType) { case DxbcResourceDim::Texture1D: return { spv::Dim1D, 0, 0, 1 }; case DxbcResourceDim::Texture1DArr: return { spv::Dim1D, 1, 0, 1 }; @@ -574,6 +564,7 @@ namespace dxvk { m_module.setDebugName(varId, str::format("t", registerId).c_str()); + m_textures.at(registerId).imageInfo = typeInfo; m_textures.at(registerId).varId = varId; m_textures.at(registerId).sampledType = sampledType; m_textures.at(registerId).sampledTypeId = sampledTypeId; @@ -611,7 +602,14 @@ namespace dxvk { } }(); + m_gs.inputPrimitive = ins.controls.primitive; m_module.setExecutionMode(m_entryPointId, mode); + + const uint32_t vertexCount + = primitiveVertexCount(m_gs.inputPrimitive); + + emitDclInputArray(vertexCount); + emitGsInitBuiltins(vertexCount); } @@ -1267,7 +1265,7 @@ namespace dxvk { void DxbcCompiler::emitGeometryEmit(const DxbcShaderInstruction& ins) { switch (ins.op) { case DxbcOpcode::Emit: { - emitGsOutputSetup(); + emitOutputSetup(0); m_module.opEmitVertex(); } break; @@ -1299,9 +1297,26 @@ namespace dxvk { const uint32_t textureId = textureReg.idx[0].offset; const uint32_t samplerId = samplerReg.idx[0].offset; + // Image type, which stores the image dimensions etc. + const DxbcImageInfo imageType = m_textures.at(textureId).imageInfo; + // FIXME implement properly - DxbcRegMask coordArrayMask(true, true, true, true); - DxbcRegMask coordLayerMask(true, true, true, true); + 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); // Load the texture coordinates. SPIR-V allows these // to be float4 even if not all components are used. @@ -1346,7 +1361,19 @@ namespace dxvk { // Accumulate additional image operands. These are // not part of the actual operand token in SPIR-V. SpirvImageOperands imageOperands; - // TODO implement sample controls + + if (ins.sampleControls.u != 0 || ins.sampleControls.v != 0 || ins.sampleControls.w != 0) { + const std::array offsetIds = { + imageLayerDim >= 1 ? m_module.constu32(ins.sampleControls.u) : 0, + imageLayerDim >= 2 ? m_module.constu32(ins.sampleControls.v) : 0, + imageLayerDim >= 3 ? m_module.constu32(ins.sampleControls.w) : 0, + }; + + imageOperands.flags |= spv::ImageOperandsConstOffsetMask; + imageOperands.sConstOffset = m_module.constComposite( + getVectorTypeId({ DxbcScalarType::Sint32, imageLayerDim }), + imageLayerDim, offsetIds.data()); + } // Sampling an image always returns a four-component // vector, whereas depth-compare ops return a scalar. @@ -1886,25 +1913,20 @@ namespace dxvk { result.type.ctype = DxbcScalarType::Float32; result.type.ccount = 4; - 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]); + std::array indices = { 0, 0 }; + + for (uint32_t i = 0; i < operand.idxDim; i++) + indices.at(i) = emitIndexLoad(operand.idx[i]).id; - DxbcRegisterInfo info; - info.type.ctype = result.type.ctype; - info.type.ccount = result.type.ccount; - info.type.alength = 0; - info.sclass = spv::StorageClassInput; + DxbcRegisterInfo info; + info.type.ctype = result.type.ctype; + info.type.ccount = result.type.ccount; + info.type.alength = 0; + info.sclass = spv::StorageClassPrivate; - result.id = m_module.opAccessChain( - getPointerTypeId(info), - m_vRegs.at(operand.idx[1].offset), - 1, &vertexId.id); - } + result.id = m_module.opAccessChain( + getPointerTypeId(info), m_vArray, + operand.idxDim, indices.data()); return result; } @@ -2148,22 +2170,80 @@ namespace dxvk { } - void DxbcCompiler::emitVsInputSetup() { + void DxbcCompiler::emitInputSetup(uint32_t vertexCount) { + const uint32_t vecTypeId = m_module.defVectorType( + m_module.defFloatType(32), 4); + const uint32_t ptrTypeId = m_module.defPointerType( + vecTypeId, spv::StorageClassPrivate); + // Copy all defined user input registers into the array + for (uint32_t i = 0; i < m_vRegs.size(); i++) { + if (m_vRegs.at(i) != 0) { + const uint32_t registerId = m_module.constu32(i); + m_module.opStore( + m_module.opAccessChain(ptrTypeId, m_vArray, 1, ®isterId), + m_module.opLoad(vecTypeId, m_vRegs.at(i))); + } + } + + // Copy all system value registers into the array, + // preserving any previously written contents. + for (const DxbcSvMapping& svMapping : m_vMappings) { + const uint32_t registerId = m_module.constu32(svMapping.regId); + + DxbcRegisterPointer ptrOut; + ptrOut.type.ctype = DxbcScalarType::Float32; + ptrOut.type.ccount = 4; + ptrOut.id = m_module.opAccessChain(ptrTypeId, + m_vArray, 1, ®isterId); + + switch (svMapping.sv) { + case DxbcSystemValue::Position: { + if (m_version.type() == DxbcProgramType::PixelShader) { + DxbcRegisterPointer ptrIn; + ptrIn.type.ctype = DxbcScalarType::Float32; + ptrIn.type.ccount = 4; + ptrIn.id = m_ps.builtinFragCoord; + + emitValueStore(ptrOut, + emitRegisterExtract( + emitValueLoad(ptrIn), + svMapping.regMask), + svMapping.regMask); + } else { + Logger::warn(str::format( + "DxbcCompiler: SV_POSITION input not yet supported: ", + m_version.type())); + } + } break; + + case DxbcSystemValue::VertexId: { + if (m_version.type() == DxbcProgramType::VertexShader) { + DxbcRegisterPointer ptrIn; + ptrIn.type.ctype = DxbcScalarType::Uint32; + ptrIn.type.ccount = 1; + ptrIn.id = m_vs.builtinVertexId; + + emitValueStore(ptrOut, + emitValueLoad(ptrIn), + svMapping.regMask); + } else { + Logger::warn(str::format( + "DxbcCompiler: SV_VERTEXID input not yet supported: ", + m_version.type())); + } + } break; + + default: + Logger::warn(str::format( + "DxbcCompiler: Unhandled sv input: ", + svMapping.sv)); + } + } } - void DxbcCompiler::emitGsInputSetup() { - - } - - - void DxbcCompiler::emitPsInputSetup() { - - } - - - void DxbcCompiler::emitVsOutputSetup() { + void DxbcCompiler::emitOutputSetup(uint32_t vertexCount) { for (const DxbcSvMapping& svMapping : m_oMappings) { switch (svMapping.sv) { case DxbcSystemValue::Position: { @@ -2193,52 +2273,39 @@ namespace dxvk { default: Logger::warn(str::format( - "DxbcCompiler: Unhandled vertex sv output: ", + "DxbcCompiler: Unhandled 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)); - } break; - - default: - Logger::warn(str::format( - "DxbcCompiler: Unhandled vertex sv output: ", - svMapping.sv)); - } - } - } - - - void DxbcCompiler::emitPsOutputSetup() { + void DxbcCompiler::emitVsInitBuiltins() { + m_vs.builtinVertexId = emitNewBuiltinVariable({ + { DxbcScalarType::Uint32, 1, 0 }, + spv::StorageClassInput }, + spv::BuiltInVertexIndex, // TODO test + "vs_vertex_index"); + m_vs.builtinInstanceId = emitNewBuiltinVariable({ + { DxbcScalarType::Uint32, 1, 0 }, + spv::StorageClassInput }, + spv::BuiltInInstanceIndex, // TODO test + "vs_instance_index"); + } + + + void DxbcCompiler::emitGsInitBuiltins(uint32_t vertexCount) { + + } + + + void DxbcCompiler::emitPsInitBuiltins() { + m_ps.builtinFragCoord = emitNewBuiltinVariable({ + { DxbcScalarType::Float32, 4, 0 }, + spv::StorageClassInput }, + spv::BuiltInFragCoord, + "ps_frag_coord"); } @@ -2258,6 +2325,10 @@ namespace dxvk { m_entryPointInterfaces.push_back(m_perVertexOut); m_module.setDebugName(m_perVertexOut, "vs_vertex_out"); + // Standard input array + emitDclInputArray(0); + emitVsInitBuiltins(); + // Main function of the vertex shader m_vs.functionId = m_module.allocateId(); m_module.setDebugName(m_vs.functionId, "vs_main"); @@ -2332,6 +2403,10 @@ namespace dxvk { } } + // Standard input array + emitDclInputArray(0); + emitPsInitBuiltins(); + // Main function of the pixel shader m_ps.functionId = m_module.allocateId(); m_module.setDebugName(m_ps.functionId, "ps_main"); @@ -2347,16 +2422,17 @@ namespace dxvk { void DxbcCompiler::emitVsFinalize() { - this->emitVsInputSetup(); + this->emitInputSetup(0); m_module.opFunctionCall( m_module.defVoidType(), m_vs.functionId, 0, nullptr); - this->emitVsOutputSetup(); + this->emitOutputSetup(0); } void DxbcCompiler::emitGsFinalize() { - this->emitGsInputSetup(); + this->emitInputSetup( + primitiveVertexCount(m_gs.inputPrimitive)); m_module.opFunctionCall( m_module.defVoidType(), m_gs.functionId, 0, nullptr); @@ -2366,11 +2442,41 @@ namespace dxvk { void DxbcCompiler::emitPsFinalize() { - this->emitPsInputSetup(); + this->emitInputSetup(0); m_module.opFunctionCall( m_module.defVoidType(), m_ps.functionId, 0, nullptr); - this->emitPsOutputSetup(); + this->emitOutputSetup(0); + } + + + 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; } @@ -2380,6 +2486,20 @@ namespace dxvk { } + 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; + } + + DxbcCfgBlock* DxbcCompiler::cfgFindLoopBlock() { for (auto cur = m_controlFlowBlocks.rbegin(); cur != m_controlFlowBlocks.rend(); cur++) { diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index cc2e3c72..0d4bd048 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -91,6 +91,9 @@ namespace dxvk { */ struct DxbcCompilerVsPart { uint32_t functionId = 0; + + uint32_t builtinVertexId = 0; + uint32_t builtinInstanceId = 0; }; @@ -111,6 +114,9 @@ namespace dxvk { struct DxbcCompilerPsPart { uint32_t functionId = 0; + uint32_t builtinFragCoord = 0; + uint32_t builtinDepth = 0; + std::array oTypes; }; @@ -455,17 +461,16 @@ namespace dxvk { const DxbcRegister& reg, DxbcRegisterValue value); - ///////////////////////////// - // Input preparation methods - void emitVsInputSetup(); - void emitGsInputSetup(); - void emitPsInputSetup(); + //////////////////////////// + // Input/output preparation + void emitInputSetup(uint32_t vertexCount); + void emitOutputSetup(uint32_t vertexCount); - ////////////////////////////// - // Output preparation methods - void emitVsOutputSetup(); - void emitGsOutputSetup(); - void emitPsOutputSetup(); + //////////////////////////////////////// + // Builtin variable declaration methods + void emitVsInitBuiltins(); + void emitGsInitBuiltins(uint32_t vertexCount); + void emitPsInitBuiltins(); ///////////////////////////////// // Shader initialization methods @@ -479,11 +484,21 @@ namespace dxvk { void emitGsFinalize(); void emitPsFinalize(); + ////////////// + // Misc stuff + void emitDclInputArray( + uint32_t vertexCount); + /////////////////////////////// // Variable definition methods uint32_t emitNewVariable( const DxbcRegisterInfo& info); + uint32_t emitNewBuiltinVariable( + const DxbcRegisterInfo& info, + spv::BuiltIn builtIn, + const char* name); + ///////////////////////////////////// // Control flow block search methods DxbcCfgBlock* cfgFindLoopBlock(); diff --git a/src/dxbc/dxbc_decoder.h b/src/dxbc/dxbc_decoder.h index 1a16f17f..116b6d79 100644 --- a/src/dxbc/dxbc_decoder.h +++ b/src/dxbc/dxbc_decoder.h @@ -51,6 +51,17 @@ namespace dxvk { }; + /** + * \brief Image type information + */ + struct DxbcImageInfo { + spv::Dim dim = spv::Dim1D; + uint32_t array = 0; + uint32_t ms = 0; + uint32_t sampled = 0; + }; + + /** * \brief Shader resource binding * @@ -58,11 +69,12 @@ namespace dxvk { * and associated type IDs. */ struct DxbcShaderResource { - uint32_t varId = 0; - DxbcScalarType sampledType = DxbcScalarType::Float32; - uint32_t sampledTypeId = 0; - uint32_t colorTypeId = 0; - uint32_t depthTypeId = 0; + DxbcImageInfo imageInfo; + uint32_t varId = 0; + DxbcScalarType sampledType = DxbcScalarType::Float32; + uint32_t sampledTypeId = 0; + uint32_t colorTypeId = 0; + uint32_t depthTypeId = 0; }; /** @@ -128,6 +140,10 @@ namespace dxvk { bool operator == (const DxbcRegMask& other) const { return m_mask == other.m_mask; } bool operator != (const DxbcRegMask& other) const { return m_mask != other.m_mask; } + static DxbcRegMask firstN(uint32_t n) { + return DxbcRegMask(n >= 1, n >= 2, n >= 3, n >= 4); + } + private: uint8_t m_mask = 0;