From 5d26f0fb0cf38a85e8b7c40fb57efc3c08faf9a3 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 16 Nov 2017 01:30:17 +0100 Subject: [PATCH] [dxbc] Implemented operand modifiers and load/store stuff --- include/spirv/GLSL.std.450.hpp | 135 +++++++++++++++++ src/dxbc/dxbc_compiler.cpp | 251 +++++++++++++++++++++++++++++++ src/dxbc/dxbc_compiler.h | 43 +++++- src/dxbc/dxbc_enums.h | 8 + src/dxbc/dxbc_names.cpp | 8 + src/dxbc/dxbc_names.h | 1 + src/dxbc/gen/dxbc_gen_common.cpp | 191 ++++++++++++++++++++--- src/dxbc/gen/dxbc_gen_common.h | 175 +++++++++++++++------ src/dxbc/gen/dxbc_gen_vertex.cpp | 147 +++++++++++++++++- src/dxbc/gen/dxbc_gen_vertex.h | 11 +- src/spirv/spirv_instruction.h | 1 + src/spirv/spirv_module.cpp | 95 +++++++++++- src/spirv/spirv_module.h | 28 +++- 13 files changed, 1017 insertions(+), 77 deletions(-) create mode 100644 include/spirv/GLSL.std.450.hpp diff --git a/include/spirv/GLSL.std.450.hpp b/include/spirv/GLSL.std.450.hpp new file mode 100644 index 000000000..186436680 --- /dev/null +++ b/include/spirv/GLSL.std.450.hpp @@ -0,0 +1,135 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLstd450_HPP +#define GLSLstd450_HPP + +namespace spv { + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 3; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +} + +#endif // #ifndef GLSLstd450_HPP \ No newline at end of file diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index e2b97430b..ec61b178e 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -33,6 +33,12 @@ namespace dxvk { case DxbcOpcode::DclTemps: return this->dclTemps(ins); + case DxbcOpcode::Mov: + return this->opMov(ins); + + case DxbcOpcode::Ret: + return this->opRet(ins); + default: Logger::err(str::format( "DxbcCompiler::processInstruction: Unhandled opcode: ", @@ -52,7 +58,49 @@ namespace dxvk { void DxbcCompiler::dclInterfaceVar(const DxbcInstruction& ins) { + auto op = ins.operand(0); + auto opcode = ins.token().opcode(); + switch (op.token().type()) { + case DxbcOperandType::Input: + case DxbcOperandType::Output: { + uint32_t regId = 0; + uint32_t regDim = 0; + + if (op.token().indexDimension() == 1) { + regId = op.index(0).immPart(); + } else if (op.token().indexDimension() == 2) { + regDim = op.index(0).immPart(); + regId = op.index(1).immPart(); + } else { + throw DxvkError(str::format( + "DxbcCompiler::dclInterfaceVar: Invalid index dimension: ", + op.token().indexDimension())); + } + + const bool hasSv = + opcode == DxbcOpcode::DclInputSgv + || opcode == DxbcOpcode::DclInputSiv + || opcode == DxbcOpcode::DclInputPsSgv + || opcode == DxbcOpcode::DclInputPsSiv + || opcode == DxbcOpcode::DclOutputSgv + || opcode == DxbcOpcode::DclOutputSiv; + + DxbcSystemValue sv = DxbcSystemValue::None; + + if (hasSv) + sv = ins.readEnum(op.length()); + + m_gen->dclInterfaceVar( + op.token().type(), regId, regDim, + op.token().componentMask(), sv); + } break; + + default: + throw DxvkError(str::format( + "DxbcCompiler::dclInterfaceVar: Unhandled operand type: ", + op.token().type())); + } } @@ -60,4 +108,207 @@ namespace dxvk { m_gen->dclTemps(ins.arg(0)); } + + 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); + + this->storeOperand(dstOp, value, mask); + } + + + void DxbcCompiler::opRet(const DxbcInstruction& ins) { + m_gen->fnReturn(); + } + + + DxbcComponentMask DxbcCompiler::getDstOperandMask(const DxbcOperand& operand) { + const DxbcOperandToken token = operand.token(); + + if (token.numComponents() == 1) { + return DxbcComponentMask(true, false, false, false); + } else if (token.numComponents() == 4) { + switch (token.selectionMode()) { + case DxbcComponentSelectionMode::Mask: + return token.componentMask(); + + case DxbcComponentSelectionMode::Select1: + return token.componentSelection(); + + default: + throw DxvkError(str::format( + "DxbcCompiler::getDstOperandMask: Invalid component selection mode: ", + token.selectionMode())); + } + } else { + throw DxvkError(str::format( + "DxbcCompiler::getDstOperandMask: Invalid component count: ", + token.numComponents())); + } + } + + + DxbcPointer DxbcCompiler::getTempOperandPtr(const DxbcOperand& operand) { + if (operand.token().indexDimension() != 1) { + throw DxvkError(str::format( + "DxbcCompiler::getTempOperandPtr: Invalid index dimension: ", + operand.token().indexDimension())); + } + + if (operand.token().indexRepresentation(0) != DxbcOperandIndexRepresentation::Imm32) { + throw DxvkError(str::format( + "DxbcCompiler::getTempOperandPtr: Invalid index representation: ", + operand.token().indexRepresentation(0))); + } + + return m_gen->ptrTempReg(operand.index(0).immPart()); + } + + + DxbcPointer DxbcCompiler::getInterfaceOperandPtr(const DxbcOperand& operand) { + const uint32_t indexDim = operand.token().indexDimension(); + + // Vertex index ID is unused if the index dimension + // is 1. The element index is always the last index. +// const uint32_t vIndexId = 0; + const uint32_t eIndexId = indexDim - 1; + + if (operand.token().indexRepresentation(eIndexId) != DxbcOperandIndexRepresentation::Imm32) { + throw DxvkError(str::format( + "DxbcCompiler::getInterfaceOperandPtr: Invalid element index representation: ", + operand.token().indexRepresentation(eIndexId))); + } + + if (indexDim == 1) { + return m_gen->ptrInterfaceVar( + operand.token().type(), + operand.index(eIndexId).immPart()); + } else { + // TODO implement index dimension 2 + throw DxvkError(str::format( + "DxbcCompiler::getInterfaceOperandPtr: Invalid index dimension: ", + indexDim)); + } + } + + + DxbcPointer DxbcCompiler::getOperandPtr(const DxbcOperand& operand) { + switch (operand.token().type()) { + case DxbcOperandType::Temp: + return this->getTempOperandPtr(operand); + + case DxbcOperandType::Input: + case DxbcOperandType::Output: + return this->getInterfaceOperandPtr(operand); + + default: + throw DxvkError(str::format( + "DxbcCompiler::getOperandPtr: Unhandled operand type: ", + operand.token().type())); + } + } + + + DxbcValue DxbcCompiler::selectOperandComponents( + const DxbcOperandToken& opToken, + const DxbcValue& opValue, + DxbcComponentMask dstMask) { + // Four-component source operands can provide either a + // swizzle to select multiple components, or a component + // index that is used to select one single component. + switch (opToken.selectionMode()) { + case DxbcComponentSelectionMode::Swizzle: + return m_gen->regSwizzle(opValue, + opToken.componentSwizzle(), dstMask); + + case DxbcComponentSelectionMode::Select1: + return m_gen->regExtract(opValue, + opToken.componentSelection()); + + default: + throw DxvkError(str::format( + "DxbcCompiler::loadOperand: Invalid component selection mode: ", + opToken.selectionMode())); + } + } + + + DxbcValue DxbcCompiler::applyOperandModifiers( + DxbcValue value, + DxbcOperandModifiers modifiers) { + if (modifiers.test(DxbcOperandModifier::Abs)) + value = m_gen->opAbs(value); + + if (modifiers.test(DxbcOperandModifier::Neg)) + value = m_gen->opNeg(value); + return value; + } + + + DxbcValue DxbcCompiler::loadOperand( + const DxbcOperand& operand, + DxbcComponentMask dstMask, + DxbcScalarType dstType) { + const DxbcOperandToken token = operand.token(); + + DxbcValue result; + + switch (token.type()) { + case DxbcOperandType::Imm32: { + if (token.numComponents() == 1) { + result = m_gen->defConstScalar(operand.imm32(0)); + } else if (token.numComponents() == 4) { + result = m_gen->defConstVector( + operand.imm32(0), operand.imm32(1), + operand.imm32(2), operand.imm32(3)); + } else { + throw DxvkError(str::format( + "DxbcCompiler::loadOperand [imm32]: Invalid number of components: ", + token.numComponents())); + } + } break; + + default: { + result = m_gen->regLoad( + this->getOperandPtr(operand)); + }; + } + + // Apply the source operand swizzle + if (token.numComponents() == 4) + result = this->selectOperandComponents(token, result, dstMask); + + // Apply source operand modifiers, if any + auto operandModifiers = operand.queryOperandExt( + DxbcOperandExt::OperandModifier); + + if (operandModifiers) { + result = this->applyOperandModifiers( + result, operandModifiers->data()); + } + + return result; + } + + + void DxbcCompiler::storeOperand( + const DxbcOperand& operand, + DxbcValue value, + DxbcComponentMask mask) { + const DxbcPointer ptr = this->getOperandPtr(operand); + + // Cast source value to destination register type. + // TODO verify that this actually works as intended. + DxbcValueType dstType; + dstType.componentType = ptr.type.valueType.componentType; + dstType.componentCount = mask.componentCount(); + value = m_gen->regCast(value, dstType); + + m_gen->regStore(ptr, value, mask); + } + } \ No newline at end of file diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index cb67bcb73..7ae8dddcb 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -28,13 +28,50 @@ namespace dxvk { Rc m_gen; void dclGlobalFlags( - const DxbcInstruction& ins); + const DxbcInstruction& ins); void dclInterfaceVar( - const DxbcInstruction& ins); + const DxbcInstruction& ins); void dclTemps( - const DxbcInstruction& ins); + const DxbcInstruction& ins); + + void opMov( + const DxbcInstruction& ins); + + void opRet( + const DxbcInstruction& ins); + + DxbcComponentMask getDstOperandMask( + const DxbcOperand& operand); + + DxbcPointer getTempOperandPtr( + const DxbcOperand& operand); + + DxbcPointer getInterfaceOperandPtr( + const DxbcOperand& operand); + + DxbcPointer getOperandPtr( + const DxbcOperand& operand); + + DxbcValue selectOperandComponents( + const DxbcOperandToken& opToken, + const DxbcValue& opValue, + DxbcComponentMask dstMask); + + DxbcValue applyOperandModifiers( + DxbcValue value, + DxbcOperandModifiers modifiers); + + DxbcValue loadOperand( + const DxbcOperand& operand, + DxbcComponentMask dstMask, + DxbcScalarType dstType); + + void storeOperand( + const DxbcOperand& operand, + DxbcValue value, + DxbcComponentMask mask); }; diff --git a/src/dxbc/dxbc_enums.h b/src/dxbc/dxbc_enums.h index f57ccbd12..c135aa5fe 100644 --- a/src/dxbc/dxbc_enums.h +++ b/src/dxbc/dxbc_enums.h @@ -424,4 +424,12 @@ namespace dxvk { using DxbcGlobalFlags = Flags; + + enum class DxbcOperandModifier : uint32_t { + Neg = 0, + Abs = 1, + }; + + using DxbcOperandModifiers = Flags; + } \ No newline at end of file diff --git a/src/dxbc/dxbc_names.cpp b/src/dxbc/dxbc_names.cpp index 56b7fa2ae..61fc44009 100644 --- a/src/dxbc/dxbc_names.cpp +++ b/src/dxbc/dxbc_names.cpp @@ -275,6 +275,14 @@ std::ostream& operator << (std::ostream& os, DxbcOperandType e) { } +std::ostream& operator << (std::ostream& os, dxvk::DxbcOperandExt e) { + switch (e) { + ENUM_NAME(DxbcOperandExt::OperandModifier); + ENUM_DEFAULT(e); + } +} + + std::ostream& operator << (std::ostream& os, DxbcComponentCount e) { switch (e) { ENUM_NAME(DxbcComponentCount::Component0); diff --git a/src/dxbc/dxbc_names.h b/src/dxbc/dxbc_names.h index 1b2719a44..f3e568a2e 100644 --- a/src/dxbc/dxbc_names.h +++ b/src/dxbc/dxbc_names.h @@ -8,6 +8,7 @@ std::ostream& operator << (std::ostream& os, dxvk::DxbcOpcode e); std::ostream& operator << (std::ostream& os, dxvk::DxbcExtOpcode e); std::ostream& operator << (std::ostream& os, dxvk::DxbcOperandType e); +std::ostream& operator << (std::ostream& os, dxvk::DxbcOperandExt e); std::ostream& operator << (std::ostream& os, dxvk::DxbcComponentCount e); std::ostream& operator << (std::ostream& os, dxvk::DxbcComponentSelectionMode e); std::ostream& operator << (std::ostream& os, dxvk::DxbcOperandIndexRepresentation e); diff --git a/src/dxbc/gen/dxbc_gen_common.cpp b/src/dxbc/gen/dxbc_gen_common.cpp index a88660cd2..5b770d9c3 100644 --- a/src/dxbc/gen/dxbc_gen_common.cpp +++ b/src/dxbc/gen/dxbc_gen_common.cpp @@ -6,7 +6,6 @@ namespace dxvk { DxbcCodeGen::DxbcCodeGen() { - m_module.enableCapability(spv::CapabilityShader); m_module.setMemoryModel( spv::AddressingModelLogical, spv::MemoryModelGLSL450); @@ -25,31 +24,173 @@ namespace dxvk { if (n > oldSize) { m_rRegs.resize(n); - DxbcPointer reg; - reg.type = DxbcPointerType( - DxbcValueType(DxbcScalarType::Float32, 4), - spv::StorageClassPrivate); - - const uint32_t typeId = this->defPointerType(reg.type); - for (uint32_t i = oldSize; i < n; i++) { - reg.valueId = m_module.newVar(typeId, spv::StorageClassPrivate); - m_module.setDebugName(reg.valueId, str::format("r", i).c_str()); - m_rRegs.at(i) = reg; + m_rRegs.at(i) = this->defVar( + DxbcValueType(DxbcScalarType::Float32, 4), + spv::StorageClassPrivate); + m_module.setDebugName(m_rRegs.at(i).valueId, + str::format("r", i).c_str()); } } } + DxbcValue DxbcCodeGen::defConstScalar(uint32_t v) { + DxbcValue result; + result.type = DxbcValueType(DxbcScalarType::Uint32, 1); + result.valueId = m_module.constu32(v); + return result; + } + + + DxbcValue DxbcCodeGen::defConstVector( + uint32_t x, uint32_t y, + uint32_t z, uint32_t w) { + std::array ids = { + m_module.constu32(x), + m_module.constu32(y), + m_module.constu32(z), + m_module.constu32(w) }; + + DxbcValue result; + result.type = DxbcValueType(DxbcScalarType::Uint32, 4); + result.valueId = m_module.constComposite( + this->defValueType(result.type), + ids.size(), ids.data()); + return result; + } + + + void DxbcCodeGen::fnReturn() { + // TODO implement control flow + m_module.opReturn(); + m_module.functionEnd(); + } + + DxbcPointer DxbcCodeGen::ptrTempReg(uint32_t regId) { return m_rRegs.at(regId); } - DxbcValue DxbcCodeGen::vecStore( - const DxbcValue& dst, - const DxbcValue& src, - DxbcComponentMask mask) { + DxbcValue DxbcCodeGen::opAbs(const DxbcValue& src) { + DxbcValue result; + result.type = src.type; + + switch (src.type.componentType) { + case DxbcScalarType::Sint32: + case DxbcScalarType::Sint64: + result.valueId = m_module.opSAbs( + this->defValueType(result.type), + src.valueId); + break; + + case DxbcScalarType::Uint32: + case DxbcScalarType::Uint64: + result.valueId = src.valueId; + break; + + case DxbcScalarType::Float32: + case DxbcScalarType::Float64: + result.valueId = m_module.opFAbs( + this->defValueType(result.type), + src.valueId); + break; + } + + return result; + } + + DxbcValue DxbcCodeGen::opNeg(const DxbcValue& src) { + DxbcValue result; + result.type = src.type; + + switch (src.type.componentType) { + case DxbcScalarType::Sint32: + case DxbcScalarType::Sint64: + case DxbcScalarType::Uint32: + case DxbcScalarType::Uint64: + result.valueId = m_module.opSNegate( + this->defValueType(result.type), + src.valueId); + break; + + case DxbcScalarType::Float32: + case DxbcScalarType::Float64: + result.valueId = m_module.opFNegate( + this->defValueType(result.type), + src.valueId); + break; + } + + return result; + } + + + DxbcValue DxbcCodeGen::regCast( + const DxbcValue& src, + const DxbcValueType& type) { + if (src.type.componentType == type.componentType) + return src; + + DxbcValue result; + result.type = type; + result.valueId = m_module.opBitcast( + this->defValueType(result.type), + src.valueId); + return result; + } + + + DxbcValue DxbcCodeGen::regExtract( + const DxbcValue& src, + DxbcComponentMask mask) { + // TODO implement + } + + + DxbcValue DxbcCodeGen::regSwizzle( + const DxbcValue& src, + const DxbcComponentSwizzle& swizzle, + DxbcComponentMask mask) { + std::array indices; + + uint32_t dstIndex = 0; + for (uint32_t i = 0; i < src.type.componentCount; i++) { + if (mask.test(i)) + indices[dstIndex++] = swizzle[i]; + } + + bool isIdentitySwizzle = dstIndex == src.type.componentCount; + + for (uint32_t i = 0; i < dstIndex && isIdentitySwizzle; i++) + isIdentitySwizzle &= indices[i] == i; + + if (isIdentitySwizzle) + return src; + + DxbcValue result; + result.type = DxbcValueType(src.type.componentType, dstIndex); + + if (dstIndex == 1) { + result.valueId = m_module.opCompositeExtract( + this->defValueType(result.type), + src.valueId, 1, indices.data()); + } else { + result.valueId = m_module.opVectorShuffle( + this->defValueType(result.type), + src.valueId, src.valueId, + dstIndex, indices.data()); + } + + return result; + } + + + DxbcValue DxbcCodeGen::regInsert( + const DxbcValue& dst, + const DxbcValue& src, + DxbcComponentMask mask) { DxbcValue result; result.type = dst.type; @@ -100,15 +241,15 @@ namespace dxvk { void DxbcCodeGen::regStore( - const DxbcPointer& ptr, - const DxbcValue& val, - DxbcComponentMask mask) { + const DxbcPointer& ptr, + const DxbcValue& val, + DxbcComponentMask mask) { if (ptr.type.valueType.componentCount != val.type.componentCount) { // In case we write to only a part of the destination // register, we need to load the previous value first // and then update the given components. DxbcValue tmp = this->regLoad(ptr); - tmp = this->vecStore(tmp, val, mask); + tmp = this->regInsert(tmp, val, mask); m_module.opStore(ptr.valueId, tmp.valueId); } else { @@ -195,4 +336,16 @@ namespace dxvk { return typeId; } + + DxbcPointer DxbcCodeGen::defVar( + const DxbcValueType& type, + spv::StorageClass storageClass) { + DxbcPointer result; + result.type = DxbcPointerType(type, storageClass); + result.valueId = m_module.newVar( + this->defPointerType(result.type), + storageClass); + return result; + } + } \ No newline at end of file diff --git a/src/dxbc/gen/dxbc_gen_common.h b/src/dxbc/gen/dxbc_gen_common.h index e7a099d4e..6d7c9a15a 100644 --- a/src/dxbc/gen/dxbc_gen_common.h +++ b/src/dxbc/gen/dxbc_gen_common.h @@ -37,6 +37,12 @@ namespace dxvk { virtual ~DxbcCodeGen(); + /** + * \brief Declares temporary registers + * \param [in] n Number of temp registers + */ + void dclTemps(uint32_t n); + /** * \brief Declares an interface variable * @@ -54,10 +60,36 @@ namespace dxvk { DxbcSystemValue sv) = 0; /** - * \brief Declares temporary registers - * \param [in] n Number of temp registers + * \brief Defines 32-bit constant + * + * The constant will be declared as a 32-bit + * unsigned integer. Cast the resulting value + * to the required type. + * \param [in] v Constant value + * \returns The constant value ID */ - void dclTemps(uint32_t n); + DxbcValue defConstScalar(uint32_t v); + + /** + * \brief Defines 32-bit constant vector + * + * Defines a four-component vector of 32-bit + * unsigned integer values. Cast the resulting + * value to the required type as needed. + * \param [in] x First vector component + * \param [in] y Second vector component + * \param [in] z Third vector component + * \param [in] w Fourth vector component + * \returns The constant value ID + */ + DxbcValue defConstVector( + uint32_t x, uint32_t y, + uint32_t z, uint32_t w); + + /** + * \brief Returns from function + */ + void fnReturn(); /** * \brief Retrieves temporary register pointer @@ -69,42 +101,6 @@ namespace dxvk { DxbcPointer ptrTempReg( uint32_t regId); - /** - * \brief Writes to parts of a vector register - * - * Note that the source value must not have the - * same number of components as the write mask. - * \param [in] dst Destination value ID - * \param [in] src Source value ID - * \param [in] mask Write mask - * \returns New destination value ID - */ - DxbcValue vecStore( - const DxbcValue& dst, - const DxbcValue& src, - DxbcComponentMask mask); - - /** - * \brief Loads register - * - * \param [in] ptr Register pointer - * \returns The register value ID - */ - DxbcValue regLoad( - const DxbcPointer& ptr); - - /** - * \brief Stores register - * - * \param [in] ptr Register pointer - * \param [in] val Value ID to store - * \param [in] mask Write mask - */ - void regStore( - const DxbcPointer& ptr, - const DxbcValue& val, - DxbcComponentMask mask); - /** * \brief Pointer to an interface variable * @@ -113,9 +109,9 @@ namespace dxvk { * \param [in] regId Register index * \returns Register pointer */ - virtual void ptrInterfaceVar( - DxbcOperandType regType, - uint32_t regId) = 0; + virtual DxbcPointer ptrInterfaceVar( + DxbcOperandType regType, + uint32_t regId) = 0; /** * \brief Pointer to an interface variable @@ -128,10 +124,93 @@ namespace dxvk { * \param [in] index Array index * \returns Register pointer */ - virtual void ptrInterfaceVarIndexed( - DxbcOperandType regType, - uint32_t regId, - const DxbcValue& index) = 0; + virtual DxbcPointer ptrInterfaceVarIndexed( + DxbcOperandType regType, + uint32_t regId, + const DxbcValue& index) = 0; + + DxbcValue opAbs( + const DxbcValue& src); + + DxbcValue opNeg( + const DxbcValue& src); + + /** + * \brief Casts register value to another type + * + * Type cast that does not change the bit pattern + * of the value. This is required as DXBC values + * are not statically typed, but SPIR-V is. + * \param [in] src Source value + * \param [in] type Destination type + * \returns Resulting register value + */ + DxbcValue regCast( + const DxbcValue& src, + const DxbcValueType& type); + + /** + * \brief Extracts vector components + * + * Extracts the given set of components. + * \param [in] src Source vector + * \param [in] mask Component mask + * \returns Resulting register value + */ + DxbcValue regExtract( + const DxbcValue& src, + DxbcComponentMask mask); + + /** + * \brief Swizzles a vector register + * + * Swizzles the vector and extracts + * the given set of vector components. + * \param [in] src Source vector to swizzle + * \param [in] swizzle The component swizzle + * \param [in] mask Components to extract + * \returns Resulting register value + */ + DxbcValue regSwizzle( + const DxbcValue& src, + const DxbcComponentSwizzle& swizzle, + DxbcComponentMask mask); + + /** + * \brief Writes to parts of a vector register + * + * Note that the source value must have the same + * number of components as the write mask. + * \param [in] dst Destination value ID + * \param [in] src Source value ID + * \param [in] mask Write mask + * \returns New destination value ID + */ + DxbcValue regInsert( + const DxbcValue& dst, + const DxbcValue& src, + DxbcComponentMask mask); + + /** + * \brief Loads register + * + * \param [in] ptr Register pointer + * \returns The register value ID + */ + DxbcValue regLoad( + const DxbcPointer& ptr); + + /** + * \brief Stores register + * + * \param [in] ptr Register pointer + * \param [in] val Value ID to store + * \param [in] mask Write mask + */ + void regStore( + const DxbcPointer& ptr, + const DxbcValue& val, + DxbcComponentMask mask); /** * \brief Finalizes shader @@ -177,6 +256,10 @@ namespace dxvk { uint32_t defPerVertexBlock(); + DxbcPointer defVar( + const DxbcValueType& type, + spv::StorageClass storageClass); + }; } \ No newline at end of file diff --git a/src/dxbc/gen/dxbc_gen_vertex.cpp b/src/dxbc/gen/dxbc_gen_vertex.cpp index e40731131..262d454b4 100644 --- a/src/dxbc/gen/dxbc_gen_vertex.cpp +++ b/src/dxbc/gen/dxbc_gen_vertex.cpp @@ -3,9 +3,25 @@ namespace dxvk { DxbcVsCodeGen::DxbcVsCodeGen() { + m_module.enableCapability(spv::CapabilityShader); + m_module.enableCapability(spv::CapabilityCullDistance); + m_module.enableCapability(spv::CapabilityClipDistance); + + m_function = m_module.allocateId(); + m_module.setDebugName(m_function, "vs_main"); + + m_module.functionBegin( + m_module.defVoidType(), + m_function, + m_module.defFunctionType( + m_module.defVoidType(), 0, nullptr), + spv::FunctionControlMaskNone); + m_module.opLabel(m_module.allocateId()); + m_outPerVertex = m_module.newVar( m_module.defPointerType(this->defPerVertexBlock(), spv::StorageClassOutput), spv::StorageClassOutput); + m_entryPointInterfaces.push_back(m_outPerVertex); m_module.setDebugName(m_outPerVertex, "vs_out"); } @@ -21,18 +37,79 @@ namespace dxvk { uint32_t regDim, DxbcComponentMask regMask, DxbcSystemValue sv) { - + switch (regType) { + case DxbcOperandType::Input: { + if (sv == DxbcSystemValue::None) { + if (m_vRegs.at(regId).valueId == 0) { + m_vRegs.at(regId) = this->defVar( + DxbcValueType(DxbcScalarType::Float32, 4), + spv::StorageClassInput); + m_module.setDebugName(m_vRegs.at(regId).valueId, + str::format("v", regId).c_str()); + m_module.decorateLocation( + m_vRegs.at(regId).valueId, regId); + m_entryPointInterfaces.push_back( + m_vRegs.at(regId).valueId); + } + } else { + if (m_vRegsSv.at(regId).valueId == 0) { + m_vRegsSv.at(regId) = this->defVar( + DxbcValueType(DxbcScalarType::Float32, 4), + spv::StorageClassPrivate); + m_module.setDebugName(m_vRegsSv.at(regId).valueId, + str::format("sv", regId).c_str()); + } + } + } break; + + case DxbcOperandType::Output: { + if (m_oRegs.at(regId).valueId == 0) { + m_oRegs.at(regId) = this->defVar( + DxbcValueType(DxbcScalarType::Float32, 4), + spv::StorageClassOutput); + m_module.setDebugName(m_oRegs.at(regId).valueId, + str::format("o", regId).c_str()); + m_module.decorateLocation( + m_oRegs.at(regId).valueId, regId); + m_entryPointInterfaces.push_back( + m_oRegs.at(regId).valueId); + } + + if (sv != DxbcSystemValue::None) { + m_svOutputs.push_back(DxbcSvMapping { + regId, regMask, sv }); + } + } break; + + default: + throw DxvkError(str::format( + "DxbcVsCodeGen::dclInterfaceVar: Unhandled operand type: ", + regType)); + } } - void DxbcVsCodeGen::ptrInterfaceVar( + DxbcPointer DxbcVsCodeGen::ptrInterfaceVar( DxbcOperandType regType, uint32_t regId) { - + switch (regType) { + case DxbcOperandType::Input: + return m_vRegsSv.at(regId).valueId != 0 + ? m_vRegsSv.at(regId) + : m_vRegs .at(regId); + + case DxbcOperandType::Output: + return m_oRegs.at(regId); + + default: + throw DxvkError(str::format( + "DxbcVsCodeGen::ptrInterfaceVar: Unhandled operand type: ", + regType)); + } } - void DxbcVsCodeGen::ptrInterfaceVarIndexed( + DxbcPointer DxbcVsCodeGen::ptrInterfaceVarIndexed( DxbcOperandType regType, uint32_t regId, const DxbcValue& index) { @@ -43,6 +120,23 @@ namespace dxvk { Rc DxbcVsCodeGen::finalize() { + m_module.functionBegin( + m_module.defVoidType(), + m_entryPointId, + m_module.defFunctionType( + m_module.defVoidType(), 0, nullptr), + spv::FunctionControlMaskNone); + m_module.opLabel(m_module.allocateId()); + + this->prepareSvInputs(); + m_module.opFunctionCall( + m_module.defVoidType(), + m_function, 0, nullptr); + this->prepareSvOutputs(); + + m_module.opReturn(); + m_module.functionEnd(); + m_module.addEntryPoint(m_entryPointId, spv::ExecutionModelVertex, "main", m_entryPointInterfaces.size(), @@ -53,4 +147,49 @@ namespace dxvk { m_module.compile(), 0, nullptr); } + + void DxbcVsCodeGen::prepareSvInputs() { + // TODO implement + } + + + void DxbcVsCodeGen::prepareSvOutputs() { + for (const auto& sv : m_svOutputs) { + DxbcValue val = this->regLoad(m_oRegs.at(sv.regId)); +// val = this->regExtract(val, sv.regMask); + + DxbcPointer dst; + + switch (sv.sv) { + case DxbcSystemValue::Position: + dst = this->ptrBuiltInPosition(); + break; + + default: + Logger::err(str::format( + "DxbcVsCodeGen::prepareSvOutputs: Unsupported system value: ", + sv.sv)); + } + + if (dst.valueId != 0) { +// val = this->regCast(val, dst.type.valueType); + this->regStore(dst, val, DxbcComponentMask()); + } + } + } + + + DxbcPointer DxbcVsCodeGen::ptrBuiltInPosition() { + const uint32_t memberId = m_module.constu32(PerVertex_Position); + + DxbcPointer result; + result.type = DxbcPointerType( + DxbcValueType(DxbcScalarType::Float32, 4), + spv::StorageClassOutput); + result.valueId = m_module.opAccessChain( + this->defPointerType(result.type), + m_outPerVertex, 1, &memberId); + return result; + } + } \ No newline at end of file diff --git a/src/dxbc/gen/dxbc_gen_vertex.h b/src/dxbc/gen/dxbc_gen_vertex.h index 2422be05b..9c66c5264 100644 --- a/src/dxbc/gen/dxbc_gen_vertex.h +++ b/src/dxbc/gen/dxbc_gen_vertex.h @@ -21,11 +21,11 @@ namespace dxvk { DxbcComponentMask regMask, DxbcSystemValue sv); - void ptrInterfaceVar( + DxbcPointer ptrInterfaceVar( DxbcOperandType regType, uint32_t regId); - void ptrInterfaceVarIndexed( + DxbcPointer ptrInterfaceVarIndexed( DxbcOperandType regType, uint32_t regId, const DxbcValue& index); @@ -34,14 +34,21 @@ namespace dxvk { private: + uint32_t m_function = 0; uint32_t m_outPerVertex = 0; std::array m_vRegs; + std::array m_vRegsSv; std::array m_oRegs; std::vector m_svInputs; std::vector m_svOutputs; + void prepareSvInputs(); + void prepareSvOutputs(); + + DxbcPointer ptrBuiltInPosition(); + }; } \ No newline at end of file diff --git a/src/spirv/spirv_instruction.h b/src/spirv/spirv_instruction.h index b3f341f37..b22f07524 100644 --- a/src/spirv/spirv_instruction.h +++ b/src/spirv/spirv_instruction.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "spirv_include.h" diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp index 6a18fe1dd..a63c24752 100644 --- a/src/spirv/spirv_module.cpp +++ b/src/spirv/spirv_module.cpp @@ -2,14 +2,21 @@ namespace dxvk { - SpirvModule:: SpirvModule() { } - SpirvModule::~SpirvModule() { } + SpirvModule:: SpirvModule() { + this->instImportGlsl450(); + } + + + SpirvModule::~SpirvModule() { + + } SpirvCodeBuffer SpirvModule::compile() const { SpirvCodeBuffer result; result.putHeader(m_id); result.append(m_capabilities); + result.append(m_instExt); result.append(m_memoryModel); result.append(m_entryPoints); result.append(m_execModeInfo); @@ -413,6 +420,24 @@ namespace dxvk { } + uint32_t SpirvModule::opAccessChain( + uint32_t resultType, + uint32_t composite, + uint32_t indexCount, + const uint32_t* indexArray) { + uint32_t resultId = this->allocateId(); + + m_code.putIns (spv::OpAccessChain, 4 + indexCount); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(composite); + + for (uint32_t i = 0; i < indexCount; i++) + m_code.putInt32(indexArray[i]); + return resultId; + } + + uint32_t SpirvModule::opBitcast( uint32_t resultType, uint32_t operand) { @@ -484,6 +509,62 @@ namespace dxvk { } + uint32_t SpirvModule::opSNegate( + uint32_t resultType, + uint32_t operand) { + uint32_t resultId = this->allocateId(); + + m_code.putIns (spv::OpSNegate, 4); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(operand); + return resultId; + } + + + uint32_t SpirvModule::opFNegate( + uint32_t resultType, + uint32_t operand) { + uint32_t resultId = this->allocateId(); + + m_code.putIns (spv::OpFNegate, 4); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(operand); + return resultId; + } + + + uint32_t SpirvModule::opSAbs( + uint32_t resultType, + uint32_t operand) { + uint32_t resultId = this->allocateId(); + + m_code.putIns (spv::OpExtInst, 6); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(m_instExtGlsl450); + m_code.putWord(spv::GLSLstd450SAbs); + m_code.putWord(operand); + return resultId; + } + + + uint32_t SpirvModule::opFAbs( + uint32_t resultType, + uint32_t operand) { + uint32_t resultId = this->allocateId(); + + m_code.putIns (spv::OpExtInst, 6); + m_code.putWord(resultType); + m_code.putWord(resultId); + m_code.putWord(m_instExtGlsl450); + m_code.putWord(spv::GLSLstd450FAbs); + m_code.putWord(operand); + return resultId; + } + + uint32_t SpirvModule::opFunctionCall( uint32_t resultType, uint32_t functionId, @@ -562,4 +643,14 @@ namespace dxvk { return resultId; } + + void SpirvModule::instImportGlsl450() { + m_instExtGlsl450 = this->allocateId(); + const char* name = "GLSL.std.450"; + + m_instExt.putIns (spv::OpExtInstImport, 2 + m_instExt.strLen(name)); + m_instExt.putWord(m_instExtGlsl450); + m_instExt.putStr (name); + } + } \ No newline at end of file diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h index 2284395e7..a3cbbc3ee 100644 --- a/src/spirv/spirv_module.h +++ b/src/spirv/spirv_module.h @@ -155,6 +155,12 @@ namespace dxvk { void functionEnd(); + uint32_t opAccessChain( + uint32_t resultType, + uint32_t composite, + uint32_t indexCount, + const uint32_t* indexArray); + uint32_t opBitcast( uint32_t resultType, uint32_t operand); @@ -179,6 +185,22 @@ namespace dxvk { uint32_t indexCount, const uint32_t* indexArray); + uint32_t opSNegate( + uint32_t resultType, + uint32_t operand); + + uint32_t opFNegate( + uint32_t resultType, + uint32_t operand); + + uint32_t opSAbs( + uint32_t resultType, + uint32_t operand); + + uint32_t opFAbs( + uint32_t resultType, + uint32_t operand); + uint32_t opFunctionCall( uint32_t resultType, uint32_t functionId, @@ -200,9 +222,11 @@ namespace dxvk { private: - uint32_t m_id = 1; + uint32_t m_id = 1; + uint32_t m_instExtGlsl450 = 0; SpirvCodeBuffer m_capabilities; + SpirvCodeBuffer m_instExt; SpirvCodeBuffer m_memoryModel; SpirvCodeBuffer m_entryPoints; SpirvCodeBuffer m_execModeInfo; @@ -217,6 +241,8 @@ namespace dxvk { uint32_t argCount, const uint32_t* argIds); + void instImportGlsl450(); + }; } \ No newline at end of file