diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index cda39035a..3678125cf 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -52,6 +52,9 @@ namespace dxvk { switch (ins.opClass) { case DxbcInstClass::Declaration: return this->emitDcl(ins); + + case DxbcInstClass::CustomData: + return this->emitCustomData(ins); case DxbcInstClass::ControlFlow: return this->emitControlFlow(ins); @@ -620,6 +623,70 @@ namespace dxvk { } + 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 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 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)); + } + } + + void DxbcCompiler::emitVectorAlu(const DxbcShaderInstruction& ins) { std::array src; @@ -1654,6 +1721,30 @@ namespace dxvk { } + 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; + } + + DxbcRegisterPointer DxbcCompiler::emitGetOperandPtr( const DxbcRegister& operand) { switch (operand.type) { @@ -1669,6 +1760,9 @@ namespace dxvk { case DxbcOperandType::ConstantBuffer: return emitGetConstBufPtr(operand); + case DxbcOperandType::ImmediateConstantBuffer: + return emitGetImmConstBufPtr(operand); + default: throw DxvkError(str::format( "DxbcCompiler: Unhandled operand type: ", diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index dd8d0dc92..a7793587b 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -223,6 +223,11 @@ namespace dxvk { uint32_t m_perVertexIn = 0; uint32_t m_perVertexOut = 0; + ////////////////////////////////////////////////// + // Immediate constant buffer. If defined, this is + // an array of four-component uint32 vectors. + uint32_t m_immConstBuf = 0; + /////////////////////////////////////////////////// // Entry point description - we'll need to declare // the function ID and all input/output variables. @@ -281,6 +286,14 @@ namespace dxvk { void emitDclMaxOutputVertexCount( const DxbcShaderInstruction& ins); + //////////////////////// + // Custom data handlers + void emitDclImmediateConstantBuffer( + const DxbcShaderInstruction& ins); + + void emitCustomData( + const DxbcShaderInstruction& ins); + ////////////////////////////// // Instruction class handlers void emitVectorAlu( @@ -395,6 +408,9 @@ namespace dxvk { DxbcRegisterPointer emitGetConstBufPtr( const DxbcRegister& operand); + DxbcRegisterPointer emitGetImmConstBufPtr( + const DxbcRegister& operand); + DxbcRegisterPointer emitGetOperandPtr( const DxbcRegister& operand); diff --git a/src/dxbc/dxbc_decoder.cpp b/src/dxbc/dxbc_decoder.cpp index 3a4d01c54..40270c761 100644 --- a/src/dxbc/dxbc_decoder.cpp +++ b/src/dxbc/dxbc_decoder.cpp @@ -2,6 +2,13 @@ namespace dxvk { + const uint32_t* DxbcCodeSlice::ptrAt(uint32_t id) const { + if (m_ptr + id >= m_end) + throw DxvkError("DxbcCodeSlice: End of stream"); + return m_ptr + id; + } + + uint32_t DxbcCodeSlice::at(uint32_t id) const { if (m_ptr + id >= m_end) throw DxvkError("DxbcCodeSlice: End of stream"); @@ -37,6 +44,7 @@ namespace dxvk { // Initialize the instruction structure. Some of these values // may not get written otherwise while decoding the instruction. m_instruction.op = static_cast(bit::extract(token0, 0, 10)); + m_instruction.opClass = DxbcInstClass::Undefined; m_instruction.sampleControls = { 0, 0, 0 }; m_instruction.dstCount = 0; m_instruction.srcCount = 0; @@ -44,6 +52,9 @@ namespace dxvk { m_instruction.dst = m_dstOperands.data(); m_instruction.src = m_srcOperands.data(); m_instruction.imm = m_immOperands.data(); + m_instruction.customDataType = DxbcCustomDataClass::Comment; + m_instruction.customDataSize = 0; + m_instruction.customData = nullptr; // Reset the index pointer, which may still contain // a non-zero value from the previous iteration @@ -68,7 +79,22 @@ namespace dxvk { void DxbcDecodeContext::decodeCustomData(DxbcCodeSlice code) { - Logger::warn("DxbcDecodeContext::decodeCustomData: Not implemented"); + const uint32_t blockLength = code.at(1); + + if (blockLength < 2) { + Logger::err("DxbcDecodeContext: Invalid custom data block"); + return; + } + + // Custom data blocks have their own instruction class + m_instruction.op = DxbcOpcode::CustomData; + m_instruction.opClass = DxbcInstClass::CustomData; + + // We'll point into the code buffer rather than making a copy + m_instruction.customDataType = static_cast( + bit::extract(code.at(0), 11, 31)); + m_instruction.customDataSize = blockLength - 2; + m_instruction.customData = code.ptrAt(2); } diff --git a/src/dxbc/dxbc_decoder.h b/src/dxbc/dxbc_decoder.h index 6ee1818b6..b5d6b9f5c 100644 --- a/src/dxbc/dxbc_decoder.h +++ b/src/dxbc/dxbc_decoder.h @@ -231,6 +231,14 @@ namespace dxvk { /** * \brief Shader instruction + * + * Note that this structure may store pointer to + * external structures, such as the original code + * buffer. This is safe to use if and only if: + * - The \ref DxbcDecodeContext that created it + * still exists and was not moved + * - The code buffer that was being decoded + * still exists and was not moved. */ struct DxbcShaderInstruction { DxbcOpcode op; @@ -246,6 +254,10 @@ namespace dxvk { const DxbcRegister* dst; const DxbcRegister* src; const DxbcImmediate* imm; + + DxbcCustomDataClass customDataType; + uint32_t customDataSize; + const uint32_t* customData; }; @@ -264,6 +276,8 @@ namespace dxvk { const uint32_t* end) : m_ptr(ptr), m_end(end) { } + const uint32_t* ptrAt(uint32_t id) const; + uint32_t at(uint32_t id) const; uint32_t read(); diff --git a/src/dxbc/dxbc_defs.cpp b/src/dxbc/dxbc_defs.cpp index 9009bce46..ef4052089 100644 --- a/src/dxbc/dxbc_defs.cpp +++ b/src/dxbc/dxbc_defs.cpp @@ -218,7 +218,7 @@ namespace dxvk { { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, } }, /* CustomData */ - { }, + { 0, DxbcInstClass::CustomData }, /* Mov */ { 2, DxbcInstClass::VectorAlu, { { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, diff --git a/src/dxbc/dxbc_defs.h b/src/dxbc/dxbc_defs.h index 0fc56725a..c14064336 100644 --- a/src/dxbc/dxbc_defs.h +++ b/src/dxbc/dxbc_defs.h @@ -29,6 +29,7 @@ namespace dxvk { */ enum class DxbcInstClass { Declaration, ///< Interface or resource declaration + CustomData, ///< Immediate constant buffer ControlFlow, ///< Control flow instructions GeometryEmit, ///< Special geometry shader instructions TextureSample, ///< Texture sampling instruction diff --git a/src/dxbc/dxbc_enums.h b/src/dxbc/dxbc_enums.h index 0c00d7c2c..02aee0375 100644 --- a/src/dxbc/dxbc_enums.h +++ b/src/dxbc/dxbc_enums.h @@ -536,7 +536,7 @@ namespace dxvk { * types. Scalar types are represented as * a one-component vector type. */ - enum class DxbcScalarType { + enum class DxbcScalarType : uint32_t { Uint32 = 0, Uint64 = 1, Sint32 = 2, @@ -546,4 +546,18 @@ namespace dxvk { Bool = 6, }; + + /** + * \brief Custom data class + * + * Stores which type of custom data is + * referenced by the instruction. + */ + enum class DxbcCustomDataClass : uint32_t { + Comment = 0, + DebugInfo = 1, + Opaque = 2, + ImmConstBuf = 3, + }; + } \ No newline at end of file diff --git a/src/dxbc/dxbc_names.cpp b/src/dxbc/dxbc_names.cpp index 457376da0..d8509b3ad 100644 --- a/src/dxbc/dxbc_names.cpp +++ b/src/dxbc/dxbc_names.cpp @@ -411,3 +411,13 @@ std::ostream& operator << (std::ostream& os, dxvk::DxbcProgramType e) { ENUM_DEFAULT(e); } } + +std::ostream& operator << (std::ostream& os, dxvk::DxbcCustomDataClass e) { + switch (e) { + ENUM_NAME(DxbcCustomDataClass::Comment); + ENUM_NAME(DxbcCustomDataClass::DebugInfo); + ENUM_NAME(DxbcCustomDataClass::Opaque); + ENUM_NAME(DxbcCustomDataClass::ImmConstBuf); + ENUM_DEFAULT(e); + } +} diff --git a/src/dxbc/dxbc_names.h b/src/dxbc/dxbc_names.h index 4c377eaf9..c8599f662 100644 --- a/src/dxbc/dxbc_names.h +++ b/src/dxbc/dxbc_names.h @@ -18,3 +18,4 @@ std::ostream& operator << (std::ostream& os, dxvk::DxbcRegisterComponentType e); std::ostream& operator << (std::ostream& os, dxvk::DxbcInstructionReturnType e); std::ostream& operator << (std::ostream& os, dxvk::DxbcSystemValue e); std::ostream& operator << (std::ostream& os, dxvk::DxbcProgramType e); +std::ostream& operator << (std::ostream& os, dxvk::DxbcCustomDataClass e); diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp index 163a6c494..495c1c934 100644 --- a/src/spirv/spirv_module.cpp +++ b/src/spirv/spirv_module.cpp @@ -521,6 +521,21 @@ namespace dxvk { } + uint32_t SpirvModule::newVarInit( + uint32_t pointerType, + spv::StorageClass storageClass, + uint32_t initialValue) { + uint32_t resultId = this->allocateId(); + + m_variables.putIns (spv::OpVariable, 5); + m_variables.putWord (pointerType); + m_variables.putWord (resultId); + m_variables.putWord (storageClass); + m_variables.putWord (initialValue); + return resultId; + } + + void SpirvModule::functionBegin( uint32_t returnType, uint32_t functionId, diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h index 64866651b..fed925d35 100644 --- a/src/spirv/spirv_module.h +++ b/src/spirv/spirv_module.h @@ -195,6 +195,11 @@ namespace dxvk { uint32_t pointerType, spv::StorageClass storageClass); + uint32_t newVarInit( + uint32_t pointerType, + spv::StorageClass storageClass, + uint32_t initialValue); + void functionBegin( uint32_t returnType, uint32_t functionId,