diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index f460a11d3..656f56770 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -33,6 +33,9 @@ namespace dxvk { case DxbcOpcode::DclTemps: return this->dclTemps(ins); + case DxbcOpcode::Add: + return this->opAdd(ins); + case DxbcOpcode::Mov: return this->opMov(ins); @@ -109,13 +112,27 @@ namespace dxvk { } + void DxbcCompiler::opAdd(const DxbcInstruction& ins) { + auto dstOp = ins.operand(0); + auto srcOp1 = ins.operand(dstOp.length()); + auto srcOp2 = ins.operand(dstOp.length() + srcOp1.length()); + DxbcComponentMask mask = this->getDstOperandMask(dstOp); + + DxbcValue src1 = this->loadOperand(srcOp1, mask, DxbcScalarType::Float32); + DxbcValue src2 = this->loadOperand(srcOp2, mask, DxbcScalarType::Float32); + DxbcValue val = m_gen->opAdd(src1, src2); + val = this->applyResultModifiers(val, ins.token().control()); + this->storeOperand(dstOp, val, mask); + } + + void DxbcCompiler::opMov(const DxbcInstruction& ins) { auto dstOp = ins.operand(0); auto srcOp = ins.operand(dstOp.length()); DxbcComponentMask mask = this->getDstOperandMask(dstOp); DxbcValue value = this->loadOperand(srcOp, mask, DxbcScalarType::Float32); - + value = this->applyResultModifiers(value, ins.token().control()); this->storeOperand(dstOp, value, mask); } @@ -252,6 +269,15 @@ namespace dxvk { } + DxbcValue DxbcCompiler::applyResultModifiers( + DxbcValue value, + DxbcOpcodeControl control) { + if (control.saturateBit()) + value = m_gen->opSaturate(value); + return value; + } + + DxbcValue DxbcCompiler::loadOperand( const DxbcOperand& operand, DxbcComponentMask dstMask, @@ -267,16 +293,22 @@ namespace dxvk { result = m_gen->defConstVector( operand.imm32(0), operand.imm32(1), operand.imm32(2), operand.imm32(3)); + result = m_gen->regExtract(result, dstMask); } else { throw DxvkError(str::format( "DxbcCompiler::loadOperand [imm32]: Invalid number of components: ", token.numComponents())); } - result = m_gen->regExtract(result, dstMask); + result = m_gen->regCast(result, DxbcValueType( + dstType, result.type.componentCount)); } else { result = m_gen->regLoad(this->getOperandPtr(operand)); + // Cast register to requested type + result = m_gen->regCast(result, DxbcValueType( + dstType, result.type.componentCount)); + // Apply the source operand swizzle if (token.numComponents() == 4) result = this->selectOperandComponents(token, result, dstMask); diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index 7ae8dddcb..4edfcc9b8 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -36,6 +36,9 @@ namespace dxvk { void dclTemps( const DxbcInstruction& ins); + void opAdd( + const DxbcInstruction& ins); + void opMov( const DxbcInstruction& ins); @@ -63,6 +66,10 @@ namespace dxvk { DxbcValue value, DxbcOperandModifiers modifiers); + DxbcValue applyResultModifiers( + DxbcValue value, + DxbcOpcodeControl control); + DxbcValue loadOperand( const DxbcOperand& operand, DxbcComponentMask dstMask, diff --git a/src/dxbc/dxbc_decoder.h b/src/dxbc/dxbc_decoder.h index 9d88cea68..3bafe3428 100644 --- a/src/dxbc/dxbc_decoder.h +++ b/src/dxbc/dxbc_decoder.h @@ -11,6 +11,77 @@ namespace dxvk { class DxbcOperand; + /** + * \brief Basic control info + * + * Parses instruction-specific control bits. Whether + * these are well defined depends on the instruction. + */ + class DxbcOpcodeControl { + + public: + + DxbcOpcodeControl() { } + DxbcOpcodeControl(uint32_t control) + : m_control(control) { } + + /** + * \brief Saturation hint + * + * If set, the result of the given instruction + * is clamped to the [0..1] range. + */ + bool saturateBit() const { + return bit::extract(m_control, 2, 2) != 0; + } + + /** + * \brief Precision hint + */ + bool preciseBit() const { + return bit::extract(m_control, 8, 11) != 0; + } + + /** + * \brief Zero test + * + * For conditional instructions, this defines + * whether the test shall pass when the given + * operand is zero or non-zero. + */ + DxbcZeroTest zeroTest() const { + return static_cast( + bit::extract(m_control, 7, 7)); + } + + /** + * \brief Resinfo return type + * + * Control bits specifically for + * the \c resinfo instruction. + */ + DxbcResinfoType resinfoType() const { + return static_cast( + bit::extract(m_control, 0, 1)); + } + + /** + * \brief Sync flags + * + * Defines the exact operation of sync + * instructions in compute shaders. + */ + DxbcSyncFlags syncFlags() const { + return bit::extract(m_control, 0, 3); + } + + private: + + uint32_t m_control = 0; + + }; + + /** * \brief DXBC instruction token * diff --git a/src/dxbc/dxbc_enums.h b/src/dxbc/dxbc_enums.h index c135aa5fe..0f8060862 100644 --- a/src/dxbc/dxbc_enums.h +++ b/src/dxbc/dxbc_enums.h @@ -432,4 +432,24 @@ namespace dxvk { using DxbcOperandModifiers = Flags; + enum class DxbcZeroTest : uint32_t { + TestZ = 0, + TestNz = 1, + }; + + enum class DxbcResinfoType : uint32_t { + Float = 0, + RcpFloat = 1, // ? + Uint = 2, + }; + + enum class DxbcSyncFlag : uint32_t { + ThreadsInGroup = 0, + ThreadGroupSharedMemory = 1, + UavMemoryGroup = 2, + UavMemoryGlobal = 3, + }; + + using DxbcSyncFlags = Flags; + } \ No newline at end of file diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp index a63c24752..b489dac32 100644 --- a/src/spirv/spirv_module.cpp +++ b/src/spirv/spirv_module.cpp @@ -565,6 +565,55 @@ namespace dxvk { } + uint32_t SpirvModule::opIAdd( + uint32_t resultType, + uint32_t a, + uint32_t b) { + uint32_t resultId = this->allocateId(); + + m_code.putIns (spv::OpIAdd, 5); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(a); + m_code.putWord(b); + return resultId; + } + + + uint32_t SpirvModule::opFAdd( + uint32_t resultType, + uint32_t a, + uint32_t b) { + uint32_t resultId = this->allocateId(); + + m_code.putIns (spv::OpFAdd, 5); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(a); + m_code.putWord(b); + return resultId; + } + + + uint32_t SpirvModule::opFClamp( + uint32_t resultType, + uint32_t x, + uint32_t minVal, + uint32_t maxVal) { + uint32_t resultId = this->allocateId(); + + m_code.putIns (spv::OpExtInst, 8); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(m_instExtGlsl450); + m_code.putWord(spv::GLSLstd450FClamp); + m_code.putWord(x); + m_code.putWord(minVal); + m_code.putWord(maxVal); + return resultId; + } + + uint32_t SpirvModule::opFunctionCall( uint32_t resultType, uint32_t functionId, diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h index a3cbbc3ee..97d7f395e 100644 --- a/src/spirv/spirv_module.h +++ b/src/spirv/spirv_module.h @@ -201,6 +201,22 @@ namespace dxvk { uint32_t resultType, uint32_t operand); + uint32_t opIAdd( + uint32_t resultType, + uint32_t a, + uint32_t b); + + uint32_t opFAdd( + uint32_t resultType, + uint32_t a, + uint32_t b); + + uint32_t opFClamp( + uint32_t resultType, + uint32_t x, + uint32_t minVal, + uint32_t maxVal); + uint32_t opFunctionCall( uint32_t resultType, uint32_t functionId,