diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 194d11640..aef83ca26 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -1,35 +1,118 @@ #include "dxbc_compiler.h" +#include "dxbc_names.h" +#include "dxbc_util.h" namespace dxvk { - DxbcCompiler::DxbcCompiler( + constexpr uint32_t PerVertex_Position = 0; + constexpr uint32_t PerVertex_PointSize = 1; + constexpr uint32_t PerVertex_CullDist = 2; + constexpr uint32_t PerVertex_ClipDist = 3; + + + DxbcCompiler2::DxbcCompiler2( const DxbcProgramVersion& version, const Rc& isgn, const Rc& osgn) - : m_gen(DxbcCodeGen::create(version, isgn, osgn)) { } + : m_version (version), + m_isgn (isgn), + m_osgn (osgn) { + // Declare an entry point ID. We'll need it during the + // initialization phase where the execution mode is set. + m_entryPointId = m_module.allocateId(); + + // Set the memory model. This is the same for all shaders. + m_module.setMemoryModel( + spv::AddressingModelLogical, + spv::MemoryModelGLSL450); + + // Make sure our interface registers don't + // contain any valid IDs at the moment. + for (size_t i = 0; i < DxbcMaxInterfaceRegs; i++) { + m_vRegs[i] = 0; + m_oRegs[i] = 0; + } + + // Initialize the shader module with capabilities + // etc. Each shader type has its own peculiarities. + switch (m_version.type()) { + case DxbcProgramType::PixelShader: this->beginPixelShader (osgn); break; + case DxbcProgramType::VertexShader: this->beginVertexShader(isgn); break; + default: Logger::err("dxbc: Unsupported shader type"); + } + } - DxbcCompiler::~DxbcCompiler() { + DxbcCompiler2::~DxbcCompiler2() { } - void DxbcCompiler::processInstruction(const DxbcInstruction& ins) { - const DxbcOpcodeToken token = ins.token(); + DxbcError DxbcCompiler2::processInstruction(const DxbcInstruction& ins) { + DxbcInst parsedInst; + DxbcError parseError = this->parseInstruction(ins, parsedInst); - switch (token.opcode()) { + if (parseError != DxbcError::sOk) + return parseError; + + switch (parsedInst.format.instructionClass) { + case DxbcInstClass::Declaration: return this->handleDeclaration (parsedInst); + case DxbcInstClass::ControlFlow: return this->handleControlFlow (parsedInst); + case DxbcInstClass::TextureSample: return this->handleTextureSample(parsedInst); + case DxbcInstClass::VectorAlu: return this->handleVectorAlu (parsedInst); + case DxbcInstClass::VectorDot: return this->handleVectorDot (parsedInst); + default: return DxbcError::eUnhandledOpcode; + } + } + + + Rc DxbcCompiler2::finalize() { + // Define the actual 'main' function of the shader + m_module.functionBegin( + m_module.defVoidType(), + m_entryPointId, + m_module.defFunctionType( + m_module.defVoidType(), 0, nullptr), + spv::FunctionControlMaskNone); + m_module.opLabel(m_module.allocateId()); + + // Depending on the shader type, this will prepare input registers, + // call various shader functions and write back the output registers. + switch (m_version.type()) { + case DxbcProgramType::PixelShader: this->endPixelShader (); break; + case DxbcProgramType::VertexShader: this->endVertexShader(); break; + default: Logger::err("dxbc: Unsupported shader type"); + } + + // End main function + m_module.opReturn(); + m_module.functionEnd(); + + // Declare the entry point, we now have all the + // information we need, including the interfaces + m_module.addEntryPoint(m_entryPointId, + m_version.executionModel(), "main", + m_entryPointInterfaces.size(), + m_entryPointInterfaces.data()); + m_module.setDebugName(m_entryPointId, "main"); + + // Create the shader module object + return new DxvkShader( + m_version.shaderStage(), + m_resourceSlots.size(), + m_resourceSlots.data(), + m_module.compile()); + } + + + DxbcError DxbcCompiler2::handleDeclaration(const DxbcInst& ins) { + switch (ins.opcode) { case DxbcOpcode::DclGlobalFlags: - return this->dclGlobalFlags(ins); + return this->declareGlobalFlags(ins); + + case DxbcOpcode::DclTemps: + return this->declareTemps(ins); - case DxbcOpcode::DclConstantBuffer: - return this->dclConstantBuffer(ins); - - case DxbcOpcode::DclResource: - return this->dclResource(ins); - - case DxbcOpcode::DclSampler: - return this->dclSampler(ins); - case DxbcOpcode::DclInput: case DxbcOpcode::DclInputSiv: case DxbcOpcode::DclInputSgv: @@ -39,543 +122,1375 @@ namespace dxvk { case DxbcOpcode::DclOutput: case DxbcOpcode::DclOutputSiv: case DxbcOpcode::DclOutputSgv: - return this->dclInterfaceVar(ins); + return this->declareInterfaceVar(ins); - case DxbcOpcode::DclTemps: - return this->dclTemps(ins); + case DxbcOpcode::DclConstantBuffer: + return this->declareConstantBuffer(ins); - case DxbcOpcode::Add: - return this->opAdd(ins); + case DxbcOpcode::DclSampler: + return this->declareSampler(ins); - case DxbcOpcode::Mad: - return this->opMad(ins); - - case DxbcOpcode::Mul: - return this->opMul(ins); - - case DxbcOpcode::Mov: - return this->opMov(ins); - - case DxbcOpcode::Dp2: - return this->opDpx(ins, 2); - - case DxbcOpcode::Dp3: - return this->opDpx(ins, 3); - - case DxbcOpcode::Dp4: - return this->opDpx(ins, 4); - - case DxbcOpcode::Rsq: - return this->opRsq(ins); - - case DxbcOpcode::Ret: - return this->opRet(ins); - - case DxbcOpcode::Sample: - return this->opSample(ins); + case DxbcOpcode::DclResource: + return this->declareResource(ins); default: - Logger::err(str::format( - "DxbcCompiler::processInstruction: Unhandled opcode: ", - token.opcode())); + return DxbcError::eUnhandledOpcode; } } - Rc DxbcCompiler::finalize() { - return m_gen->finalize(); + DxbcError DxbcCompiler2::declareGlobalFlags(const DxbcInst& ins) { + // TODO add support for double-precision floats + // TODO add support for early depth-stencil + return DxbcError::sOk; } - void DxbcCompiler::dclGlobalFlags(const DxbcInstruction& ins) { - // TODO fill with life + DxbcError DxbcCompiler2::declareTemps(const DxbcInst& ins) { + if (ins.operands[0].type != DxbcOperandType::Imm32) { + Logger::err("dxbc: Number of temps not a contant"); + return DxbcError::eInvalidOperand; + } + + // Some shader program types use multiple sets of temps, + // so we'll just check if we need to create new ones. + const uint32_t newSize = ins.operands[0].immediates[0]; + const uint32_t oldSize = m_rRegs.size(); + + if (newSize > oldSize) { + m_rRegs.resize(newSize); + + // r# registers are always 4x32-bit float vectors + const uint32_t regTypeId = this->defineVectorType( + DxbcScalarType::Float32, 4); + + const uint32_t ptrTypeId = m_module.defPointerType( + regTypeId, spv::StorageClassPrivate); + + for (uint32_t i = oldSize; i < newSize; i++) { + m_rRegs.at(i) = m_module.newVar( + ptrTypeId, spv::StorageClassPrivate); + + m_module.setDebugName(m_rRegs.at(i), + str::format("r", i).c_str()); + } + } + + return DxbcError::sOk; } - void DxbcCompiler::dclConstantBuffer(const DxbcInstruction& ins) { - // dclConstantBuffer takes two operands: - // 1. The buffer register ID - // 2. The number of 4x32-bit constants - auto op = ins.operand(0); + DxbcError DxbcCompiler2::declareInterfaceVar(const DxbcInst& ins) { + const DxbcInstOp& op = ins.operands[0]; - if (op.token().indexDimension() != 2) - throw DxvkError("DxbcCompiler::dclConstantBuffer: Invalid index dimension"); + // In the vertex and fragment shader stage, the + // operand indices will have the following format: + // (0) Register index + // + // In other stages, the input and output registers + // are declared as arrays of a fixed size: + // (0) Array size + // (1) Register index + uint32_t regId = 0; + uint32_t regDim = 0; - const uint32_t index = op.index(0).immPart(); - const uint32_t size = op.index(1).immPart(); + if (op.indexDim == 1) { + if (op.index[0].type != DxbcIndexType::Immediate) + return DxbcError::eInvalidOperandIndex; + + regId = op.index[0].immediate; + } else if (op.indexDim == 2) { + if (op.index[0].type != DxbcIndexType::Immediate + || op.index[1].type != DxbcIndexType::Immediate) + return DxbcError::eInvalidOperandIndex; + + regDim = op.index[0].immediate; + regId = op.index[1].immediate; + } else { + Logger::err("dxbc: Invalid index dimension for v#/o# declaration"); + return DxbcError::eInvalidOperandIndex; + } - m_gen->dclConstantBuffer(index, size); + // This declaration may map an output register to a system + // value. If that is the case, the system value type will + // be stored in the second operand. + const bool hasSv = + ins.opcode == DxbcOpcode::DclInputSgv + || ins.opcode == DxbcOpcode::DclInputSiv + || ins.opcode == DxbcOpcode::DclInputPsSgv + || ins.opcode == DxbcOpcode::DclInputPsSiv + || ins.opcode == DxbcOpcode::DclOutputSgv + || ins.opcode == DxbcOpcode::DclOutputSiv; + + DxbcSystemValue sv = DxbcSystemValue::None; + + if (hasSv) { + if (ins.operands[1].type != DxbcOperandType::Imm32) { + Logger::err("dxbc: Invalid system value in v#/o# declaration"); + return DxbcError::eInstructionFormat; + } + + sv = static_cast( + ins.operands[1].immediates[0]); + } + + // In the pixel shader, inputs are declared with an + // interpolation mode that is part of the op token. +// const bool hasInterpolationMode = +// ins.opcode == DxbcOpcode::DclInputPs +// || ins.opcode == DxbcOpcode::DclInputPsSiv; + + DxbcInterpolationMode im = DxbcInterpolationMode::Undefined; + + // TODO implement this +// if (hasInterpolationMode) { +// im = static_cast( +// bit::extract(ins.token().control(), 0, 3)); +// } + + // Declare the actual variable + switch (op.type) { + case DxbcOperandType::Input: + return this->declareInputVar( + regId, regDim, op.mask, sv, im); + + case DxbcOperandType::Output: + return this->declareOutputVar( + regId, regDim, op.mask, sv, im); + + default: + // We shouldn't ever be here + return DxbcError::eInternal; + } } - void DxbcCompiler::dclResource(const DxbcInstruction& ins) { + DxbcError DxbcCompiler2::declareConstantBuffer(const DxbcInst& ins) { + const DxbcInstOp& op = ins.operands[0]; + + // This instruction has one operand with two indices: + // (1) Constant buffer register ID (cb#) + // (2) Number of constants in the buffer + if (op.indexDim != 2) { + Logger::err("dxbc: Constant buffer declaration requires two indices"); + return DxbcError::eInvalidOperandIndex; + } + + const uint32_t bufferId = op.index[0].immediate; + const uint32_t elementCount = op.index[1].immediate; + + // Uniform buffer data is stored as a fixed-size array + // of 4x32-bit vectors. SPIR-V requires explicit strides. + uint32_t arrayType = m_module.defArrayTypeUnique( + this->defineVectorType(DxbcScalarType::Float32, 4), + m_module.constu32(elementCount)); + m_module.decorateArrayStride(arrayType, 16); + + // SPIR-V requires us to put that array into a + // struct and decorate that struct as a block. + uint32_t structType = m_module.defStructTypeUnique(1, &arrayType); + m_module.memberDecorateOffset(structType, 0, 0); + m_module.decorateBlock(structType); + + // Variable that we'll use to access the buffer + uint32_t varId = m_module.newVar( + m_module.defPointerType(structType, spv::StorageClassUniform), + spv::StorageClassUniform); + + m_module.setDebugName(varId, + str::format("cb", bufferId).c_str()); + + m_constantBuffers.at(bufferId).varId = varId; + m_constantBuffers.at(bufferId).size = elementCount; + + // Compute the DXVK binding slot index for the buffer. + // D3D11 needs to bind the actual buffers to this slot. + uint32_t bindingId = computeResourceSlotId( + m_version.type(), DxbcBindingType::ConstantBuffer, + bufferId); + + m_module.decorateDescriptorSet(varId, 0); + m_module.decorateBinding(varId, bindingId); + + // Store descriptor info for the shader interface + DxvkResourceSlot resource; + resource.slot = bindingId; + resource.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + m_resourceSlots.push_back(resource); + return DxbcError::sOk; + } + + + DxbcError DxbcCompiler2::declareSampler(const DxbcInst& ins) { + // dclSampler takes one operand: + // (1) The sampler register ID + // TODO implement sampler mode (default / comparison / mono) + if (ins.operands[0].indexDim != 1) { + Logger::err("dxbc: Invalid sampler index dimension"); + return DxbcError::eInvalidOperandIndex; + } + + uint32_t samplerId = ins.operands[0].index[0].immediate; + + // The sampler type is opaque, but we still have to + // define a pointer and a variable in oder to use it + uint32_t samplerType = m_module.defSamplerType(); + uint32_t samplerPtrType = m_module.defPointerType( + samplerType, spv::StorageClassUniformConstant); + + // Define the sampler variable + uint32_t varId = m_module.newVar(samplerPtrType, + spv::StorageClassUniformConstant); + + m_module.setDebugName(varId, + str::format("s", samplerId).c_str()); + + m_samplers.at(samplerId).varId = varId; + m_samplers.at(samplerId).typeId = samplerType; + + // Compute binding slot index for the sampler + uint32_t bindingId = computeResourceSlotId( + m_version.type(), DxbcBindingType::ImageSampler, samplerId); + + m_module.decorateDescriptorSet(varId, 0); + m_module.decorateBinding(varId, bindingId); + + // Store descriptor info for the shader interface + DxvkResourceSlot resource; + resource.slot = bindingId; + resource.type = VK_DESCRIPTOR_TYPE_SAMPLER; + m_resourceSlots.push_back(resource); + return DxbcError::sOk; + } + + + DxbcError DxbcCompiler2::declareResource(const DxbcInst& ins) { // dclResource takes two operands: - // 1. The resource register ID - // 2. The resource return type - auto op = ins.operand(0); + // (1) The resource register ID + // (2) The resource return type + const DxbcInstOp op = ins.operands[0]; - if (op.token().indexDimension() != 1) - throw DxvkError("DXBC: dclResource: Invalid index dimension"); + if (op.indexDim != 1) { + Logger::err("dxbc: dclResource: Invalid index dimension"); + return DxbcError::eInvalidOperandIndex; + } - const uint32_t index = op.index(0).immPart(); + const uint32_t registerId = op.index[0].immediate; // Defines the type of the resource (texture2D, ...) - auto resourceDim = static_cast( - bit::extract(ins.token().control(), 0, 4)); + const DxbcResourceDim resourceType = ins.control.resourceDim(); // Defines the type of a read operation. DXBC has the ability // to define four different types whereas SPIR-V only allows // one, but in practice this should not be much of a problem. - const uint32_t ofs = op.length(); - auto xType = static_cast( - bit::extract(ins.arg(ofs), 0, 3)); + bit::extract(ins.operands[1].immediates[0], 0, 3)); auto yType = static_cast( - bit::extract(ins.arg(ofs), 4, 7)); + bit::extract(ins.operands[1].immediates[0], 4, 7)); auto zType = static_cast( - bit::extract(ins.arg(ofs), 8, 11)); + bit::extract(ins.operands[1].immediates[0], 8, 11)); auto wType = static_cast( - bit::extract(ins.arg(ofs), 12, 15)); + bit::extract(ins.operands[1].immediates[0], 12, 15)); if ((xType != yType) || (xType != zType) || (xType != wType)) Logger::warn("DXBC: dclResource: Ignoring resource return types"); - m_gen->dclResource(index, resourceDim, xType); + // Declare the actual sampled type + uint32_t sampledTypeId = 0; + + switch (xType) { + case DxbcResourceReturnType::Float: sampledTypeId = m_module.defFloatType(32); break; + case DxbcResourceReturnType::Sint: sampledTypeId = m_module.defIntType (32, 1); break; + case DxbcResourceReturnType::Uint: sampledTypeId = m_module.defIntType (32, 0); break; + default: + Logger::err(str::format("dxbc: Invalid sampled type: ", xType)); + return DxbcError::eInvalidOperand; + } + + // Declare the resource type + uint32_t textureTypeId = 0; + + switch (resourceType) { + case DxbcResourceDim::Texture1D: + textureTypeId = m_module.defImageType( + sampledTypeId, spv::Dim1D, 0, 0, 0, 1, + spv::ImageFormatUnknown); + break; + + case DxbcResourceDim::Texture1DArr: + textureTypeId = m_module.defImageType( + sampledTypeId, spv::Dim1D, 0, 1, 0, 1, + spv::ImageFormatUnknown); + break; + + case DxbcResourceDim::Texture2D: + textureTypeId = m_module.defImageType( + sampledTypeId, spv::Dim2D, 0, 0, 0, 1, + spv::ImageFormatUnknown); + break; + + case DxbcResourceDim::Texture2DArr: + textureTypeId = m_module.defImageType( + sampledTypeId, spv::Dim2D, 0, 1, 0, 1, + spv::ImageFormatUnknown); + break; + + case DxbcResourceDim::Texture3D: + textureTypeId = m_module.defImageType( + sampledTypeId, spv::Dim3D, 0, 0, 0, 1, + spv::ImageFormatUnknown); + break; + + case DxbcResourceDim::TextureCube: + textureTypeId = m_module.defImageType( + sampledTypeId, spv::DimCube, 0, 0, 0, 1, + spv::ImageFormatUnknown); + break; + + case DxbcResourceDim::TextureCubeArr: + textureTypeId = m_module.defImageType( + sampledTypeId, spv::DimCube, 0, 1, 0, 1, + spv::ImageFormatUnknown); + break; + + default: + Logger::err(str::format("dxbc: Unsupported resource type: ", resourceType)); + return DxbcError::eUnsupported; + } + + uint32_t resourcePtrType = m_module.defPointerType( + textureTypeId, spv::StorageClassUniformConstant); + + uint32_t varId = m_module.newVar(resourcePtrType, + spv::StorageClassUniformConstant); + + m_module.setDebugName(varId, + str::format("t", registerId).c_str()); + + m_textures.at(registerId).varId = varId; + m_textures.at(registerId).sampledTypeId = sampledTypeId; + m_textures.at(registerId).textureTypeId = textureTypeId; + + // Compute the DXVK binding slot index for the resource. + // D3D11 needs to bind the actual resource to this slot. + uint32_t bindingId = computeResourceSlotId(m_version.type(), + DxbcBindingType::ShaderResource, registerId); + + m_module.decorateDescriptorSet(varId, 0); + m_module.decorateBinding(varId, bindingId); + + // Store descriptor info for the shader interface + DxvkResourceSlot resource; + resource.slot = bindingId; + resource.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + m_resourceSlots.push_back(resource); + return DxbcError::sOk; } - void DxbcCompiler::dclSampler(const DxbcInstruction& ins) { - // dclSampler takes one operand: - // 1. The sampler register ID - // TODO implement sampler mode (default / comparison / mono) - auto op = ins.operand(0); + DxbcError DxbcCompiler2::declareInputVar( + uint32_t regId, + uint32_t regDim, + DxbcRegMask regMask, + DxbcSystemValue sv, + DxbcInterpolationMode im) { + if (regDim != 0) { + Logger::err("dxbc: Input arrays not yet supported"); + return DxbcError::eUnsupported; + } - if (op.token().indexDimension() != 1) - throw DxvkError("DxbcCompiler::dclSampler: Invalid index dimension"); + // A variable may be declared multiple + // times when system values are involved + if (m_vRegs.at(regId) == 0) { + uint32_t regTypeId = m_module.defVectorType( + m_module.defFloatType(32), 4); + + uint32_t ptrTypeId = m_module.defPointerType( + regTypeId, spv::StorageClassInput); + + uint32_t varId = m_module.newVar( + ptrTypeId, spv::StorageClassInput); + + m_module.decorateLocation(varId, regId); + m_module.setDebugName(varId, str::format("v", regId).c_str()); + m_entryPointInterfaces.push_back(varId); + + m_vRegs.at(regId) = varId; + } - const uint32_t index = op.index(0).immPart(); - m_gen->dclSampler(index); + return DxbcError::sOk; } - void DxbcCompiler::dclInterfaceVar(const DxbcInstruction& ins) { - auto op = ins.operand(0); - auto opcode = ins.token().opcode(); + DxbcError DxbcCompiler2::declareOutputVar( + uint32_t regId, + uint32_t regDim, + DxbcRegMask regMask, + DxbcSystemValue sv, + DxbcInterpolationMode im) { + if (regDim != 0) { + Logger::err("dxbc: Output arrays not yet supported"); + return DxbcError::eUnsupported; + } - 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()); - - const bool hasInterpolationMode = - opcode == DxbcOpcode::DclInputPs - || opcode == DxbcOpcode::DclInputPsSiv; - - DxbcInterpolationMode im = DxbcInterpolationMode::Undefined; - - if (hasInterpolationMode) { - im = static_cast( - bit::extract(ins.token().control(), 0, 3)); - } - - m_gen->dclInterfaceVar( - op.token().type(), regId, regDim, - op.token().componentMask(), sv, im); - } break; + // Fragment shader outputs were defined earlier, based + // on their signature. We cannot add new output registers. + if (m_version.type() == DxbcProgramType::PixelShader) + return DxbcError::sOk; + + // Output variables may also be defined multiple times since + // multiple vector components may be mapped to system values. + if (m_oRegs.at(regId) == 0) { + uint32_t regTypeId = m_module.defVectorType( + m_module.defFloatType(32), 4); + + uint32_t ptrTypeId = m_module.defPointerType( + regTypeId, spv::StorageClassOutput); + + uint32_t varId = m_module.newVar( + ptrTypeId, spv::StorageClassOutput); + + m_module.decorateLocation(varId, regId); + m_module.setDebugName(varId, str::format("o", regId).c_str()); + m_entryPointInterfaces.push_back(varId); + + m_oRegs.at(regId) = varId; + } + + // Add a new system value mapping if needed + // TODO declare SV if necessary + if (sv != DxbcSystemValue::None) + m_oSvs.push_back({ regId, regMask, sv }); + + return DxbcError::sOk; + } + + + DxbcError DxbcCompiler2::handleControlFlow(const DxbcInst& ins) { + switch (ins.opcode) { + case DxbcOpcode::Ret: + m_module.opReturn(); + m_module.functionEnd(); + return DxbcError::sOk; default: - throw DxvkError(str::format( - "DxbcCompiler::dclInterfaceVar: Unhandled operand type: ", - op.token().type())); + return DxbcError::eUnhandledOpcode; } } - void DxbcCompiler::dclTemps(const DxbcInstruction& ins) { - // dclTemps takes one operand: - // 1. The number of temporary registers to - // declare, as an immediate 32-bit integer. - m_gen->dclTemps(ins.arg(0)); - } - - - 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::opMad(const DxbcInstruction& ins) { - auto dstOp = ins.operand(0); - auto srcOp1 = ins.operand(dstOp.length()); - auto srcOp2 = ins.operand(dstOp.length() + srcOp1.length()); - auto srcOp3 = ins.operand(dstOp.length() + srcOp1.length() + srcOp2.length()); - DxbcComponentMask mask = this->getDstOperandMask(dstOp); - - DxbcValue src1 = this->loadOperand(srcOp1, mask, DxbcScalarType::Float32); - DxbcValue src2 = this->loadOperand(srcOp2, mask, DxbcScalarType::Float32); - DxbcValue src3 = this->loadOperand(srcOp3, mask, DxbcScalarType::Float32); - // TODO implement with native FMA instruction - DxbcValue val = m_gen->opMul(src1, src2); - val = m_gen->opAdd(val, src3); - val = this->applyResultModifiers(val, ins.token().control()); - this->storeOperand(dstOp, val, mask); - } - - - void DxbcCompiler::opMul(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->opMul(src1, src2); - val = this->applyResultModifiers(val, ins.token().control()); - this->storeOperand(dstOp, val, mask); - } - - - void DxbcCompiler::opDpx(const DxbcInstruction& ins, uint32_t n) { - auto dstOp = ins.operand(0); - auto srcOp1 = ins.operand(dstOp.length()); - auto srcOp2 = ins.operand(dstOp.length() + srcOp1.length()); - - DxbcComponentMask dstMask = this->getDstOperandMask(dstOp); - DxbcComponentMask srcMask(n >= 1, n >= 2, n >= 3, n == 4); - - DxbcValue src1 = this->loadOperand(srcOp1, srcMask, DxbcScalarType::Float32); - DxbcValue src2 = this->loadOperand(srcOp2, srcMask, DxbcScalarType::Float32); - DxbcValue val = m_gen->opDot(src1, src2); - val = this->applyResultModifiers(val, ins.token().control()); - this->storeOperand(dstOp, val, dstMask); - } - - - void DxbcCompiler::opRsq(const DxbcInstruction& ins) { - auto dstOp = ins.operand(0); - auto srcOp = ins.operand(dstOp.length()); - DxbcComponentMask mask = this->getDstOperandMask(dstOp); - - DxbcValue src = this->loadOperand(srcOp, mask, DxbcScalarType::Float32); - DxbcValue val = m_gen->opRsqrt(src); - 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); - } - - - void DxbcCompiler::opRet(const DxbcInstruction& ins) { - m_gen->fnReturn(); - } - - - void DxbcCompiler::opSample(const DxbcInstruction& ins) { + DxbcError DxbcCompiler2::handleTextureSample(const DxbcInst& ins) { // TODO support address offset // TODO support more sample ops - auto dstOp = ins.operand(0); - auto coordOp = ins.operand(dstOp.length()); - auto texture = ins.operand(dstOp.length() + coordOp.length()); - auto sampler = ins.operand(dstOp.length() + coordOp.length() + texture.length()); - if ((texture.token().indexDimension() != 1) - || (sampler.token().indexDimension() != 1)) - throw DxvkError("DXBC: opSample: Invalid operand index dimensions"); + // sample has four operands: + // (1) The destination register + // (2) Texture coordinates + // (3) The texture itself + // (4) The sampler object + const DxbcInstOp destOp = ins.operands[0]; + const DxbcInstOp coordOp = ins.operands[1]; + const DxbcInstOp textureOp = ins.operands[2]; + const DxbcInstOp samplerOp = ins.operands[3]; - uint32_t textureId = texture.index(0).immPart(); - uint32_t samplerId = sampler.index(0).immPart(); + if (textureOp.indexDim != 1 || samplerOp.indexDim != 1) { + Logger::err("dxbc: Texture and Sampler registers require one index"); + return DxbcError::eInvalidOperandIndex; + } - DxbcValue coord = this->loadOperand(coordOp, - DxbcComponentMask(true, true, true, true), + // Texture and sampler register IDs + const uint32_t textureId = textureOp.index[0].immediate; + const uint32_t samplerId = samplerOp.index[0].immediate; + + // Load the texture coordinates. SPIR-V allows these + // to be float4 even if not all components are used. + const DxbcValue2 coord = this->loadOp(coordOp, + DxbcRegMask(true, true, true, true), DxbcScalarType::Float32); - DxbcComponentMask mask = this->getDstOperandMask(dstOp); + // Combine the texture and the sampler into a sampled image + uint32_t sampledImageType = m_module.defSampledImageType( + m_textures.at(textureId).textureTypeId); - DxbcValue value = m_gen->texSample(textureId, samplerId, coord); - value = this->selectOperandComponents(texture.token(), value, mask); - this->storeOperand(dstOp, value, mask); + uint32_t sampledImageId = m_module.opSampledImage( + sampledImageType, + m_module.opLoad( + m_textures.at(textureId).textureTypeId, + m_textures.at(textureId).varId), + m_module.opLoad( + m_samplers.at(samplerId).typeId, + m_samplers.at(samplerId).varId)); + + // Sampling an image in SPIR-V always returns a four-component + // vector, so we need to declare the corresponding type here + // TODO infer sampled type properly + DxbcValue2 result; + result.componentType = DxbcScalarType::Float32; + result.componentCount = 4; + result.valueId = m_module.opImageSampleImplicitLod( + this->defineVectorType(result.componentType, result.componentCount), + sampledImageId, coord.valueId); + + // Swizzle components using the texture swizzle + // and the destination operand's write mask + result = this->swizzleReg(result, textureOp.swizzle, destOp.mask); + + this->storeOp(destOp, result); + return DxbcError::sOk; } - DxbcValue DxbcCompiler::getDynamicIndexValue(const DxbcOperandIndex& index) { - DxbcValue immPart; - DxbcValue relPart; + DxbcError DxbcCompiler2::handleVectorAlu(const DxbcInst& ins) { + // Load input operands. Operands that are floating + // point types will be affected by modifiers. + DxbcValue2 arguments[DxbcMaxOperandCount - 1]; - if (index.hasImmPart()) - immPart = m_gen->defConstScalar(index.immPart()); - - if (index.hasRelPart()) { - relPart = this->loadOperand(index.relPart(), - DxbcComponentMask(true, false, false, false), - DxbcScalarType::Uint32); + for (uint32_t i = 1; i < ins.format.operandCount; i++) { + arguments[i - 1] = this->loadOp( + ins.operands[i], + ins.operands[0].mask, + ins.format.operands[i].type); } - if (immPart.valueId == 0) - return relPart; - else if (relPart.valueId == 0) - return immPart; - else - return m_gen->opAdd(relPart, immPart); - } - - - DxbcComponentMask DxbcCompiler::getDstOperandMask(const DxbcOperand& operand) { - const DxbcOperandToken token = operand.token(); + // Result that we will write to the destination operand + DxbcValue2 result; + result.componentType = arguments[0].componentType; + result.componentCount = arguments[0].componentCount; - 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())); - } + uint32_t resultTypeId = this->defineVectorType( + result.componentType, result.componentCount); - 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::getConstantBufferPtr(const DxbcOperand& operand) { - if (operand.token().indexDimension() != 2) - throw DxvkError("DxbcCompiler::getConstantBufferPtr: Invalid index dimension"); - - return m_gen->ptrConstantBuffer( - operand.index(0).immPart(), - this->getDynamicIndexValue(operand.index(1))); - } - - - DxbcPointer DxbcCompiler::getOperandPtr(const DxbcOperand& operand) { - switch (operand.token().type()) { - case DxbcOperandType::Temp: - return this->getTempOperandPtr(operand); + switch (ins.opcode) { + case DxbcOpcode::Add: + result.valueId = m_module.opFAdd( + resultTypeId, + arguments[0].valueId, + arguments[1].valueId); + break; - case DxbcOperandType::Input: - case DxbcOperandType::Output: - return this->getInterfaceOperandPtr(operand); + case DxbcOpcode::Mad: + result.valueId = m_module.opFFma( + resultTypeId, + arguments[0].valueId, + arguments[1].valueId, + arguments[2].valueId); + break; - case DxbcOperandType::ConstantBuffer: - return this->getConstantBufferPtr(operand); + case DxbcOpcode::Mov: + result.valueId = arguments[0].valueId; + break; + + case DxbcOpcode::Mul: + result.valueId = m_module.opFMul( + resultTypeId, + arguments[0].valueId, + arguments[1].valueId); + break; + + case DxbcOpcode::Rsq: + result.valueId = m_module.opInverseSqrt( + resultTypeId, arguments[0].valueId); + break; default: - throw DxvkError(str::format( - "DxbcCompiler::getOperandPtr: Unhandled operand type: ", - operand.token().type())); + return DxbcError::eUnhandledOpcode; } + + // Apply result modifiers to floating-point results + result = this->applyResultModifiers(result, ins.control); + this->storeOp(ins.operands[0], result); + return DxbcError::sOk; } - 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()); - - case DxbcComponentSelectionMode::Mask: - return m_gen->regExtract(opValue, - opToken.componentMask()); - - default: - throw DxvkError(str::format( - "DxbcCompiler::selectOperandComponents: Invalid selection mode: ", - opToken.selectionMode())); + DxbcError DxbcCompiler2::handleVectorDot(const DxbcInst& ins) { + // Determine the component count and the source + // operand mask. Since the result is scalar, we + // cannot use the destination register mask. + uint32_t numComponents = 0; + + switch (ins.opcode) { + case DxbcOpcode::Dp2: numComponents = 2; break; + case DxbcOpcode::Dp3: numComponents = 3; break; + case DxbcOpcode::Dp4: numComponents = 4; break; + default: return DxbcError::eUnhandledOpcode; } + + // We'll use xyz for dp3, xy for dp2 + const DxbcRegMask srcMask( + numComponents >= 1, numComponents >= 2, + numComponents >= 3, numComponents >= 4); + + // Load input operands as floatig point numbers + DxbcValue2 arguments[2]; + + for (uint32_t i = 1; i <= 2; i++) { + arguments[i - 1] = this->loadOp( + ins.operands[i], srcMask, + DxbcScalarType::Float32); + } + + DxbcValue2 result; + result.componentType = DxbcScalarType::Float32; + result.componentCount = 1; + result.valueId = m_module.opDot( + this->defineVectorType( + result.componentType, + result.componentCount), + arguments[0].valueId, + arguments[1].valueId); + + // Apply result modifiers to floating-point results + result = this->applyResultModifiers(result, ins.control); + this->storeOp(ins.operands[0], result); + return DxbcError::sOk; } - DxbcValue DxbcCompiler::applyOperandModifiers( - DxbcValue value, - DxbcOperandModifiers modifiers) { - if (modifiers.test(DxbcOperandModifier::Abs)) - value = m_gen->opAbs(value); + DxbcValue2 DxbcCompiler2::bitcastReg( + const DxbcValue2& src, + DxbcScalarType type) { + if (src.componentType == type) + return src; - if (modifiers.test(DxbcOperandModifier::Neg)) - value = m_gen->opNeg(value); - return value; + // TODO support 64-bit types by adjusting the component count + uint32_t typeId = this->defineVectorType(type, src.componentCount); + + DxbcValue2 result; + result.componentType = type; + result.componentCount = src.componentCount; + result.valueId = m_module.opBitcast(typeId, src.valueId); + return result; } - 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, - DxbcScalarType dstType) { - const DxbcOperandToken token = operand.token(); + DxbcValue2 DxbcCompiler2::insertReg( + const DxbcValue2& dst, + const DxbcValue2& src, + DxbcRegMask mask) { + DxbcValue2 result; + result.componentType = dst.componentType; + result.componentCount = dst.componentCount; - DxbcValue result; + const uint32_t resultTypeId = this->defineVectorType( + result.componentType, result.componentCount); - if (token.type() == 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)); - result = m_gen->regExtract(result, dstMask); - } else { - throw DxvkError(str::format( - "DxbcCompiler::loadOperand [imm32]: Invalid number of components: ", - token.numComponents())); - } + if (dst.componentCount == 1) { + // Both values are scalar, so the first component + // of the write mask decides which one to take. + result.valueId = mask[0] ? src.valueId : dst.valueId; + } else if (src.componentCount == 1) { + // The source value is scalar. Since OpVectorShuffle + // requires both arguments to be vectors, we have to + // use OpCompositeInsert to modify the vector instead. + const uint32_t componentId = mask.firstSet(); - result = m_gen->regCast(result, DxbcValueType( - dstType, result.type.componentCount)); + result.valueId = m_module.opCompositeInsert( + resultTypeId, src.valueId, dst.valueId, + 1, &componentId); } else { - result = m_gen->regLoad(this->getOperandPtr(operand)); + // Both arguments are vectors. We can determine which + // components to take from which vector and use the + // OpVectorShuffle instruction. + uint32_t components[4]; + uint32_t srcComponentId = dst.componentCount; - // Cast register to requested type - result = m_gen->regCast(result, DxbcValueType( - dstType, result.type.componentCount)); + for (uint32_t i = 0; i < dst.componentCount; i++) + components[i] = mask[i] ? srcComponentId++ : i; - // Apply the source operand swizzle - if (token.numComponents() == 4) - result = this->selectOperandComponents(token, result, dstMask); - - // Apply source operand modifiers, if any - DxbcOperandTokenExt token; - - if (operand.queryOperandExt(DxbcOperandExt::OperandModifier, token)) - result = this->applyOperandModifiers(result, token.data()); + result.valueId = m_module.opVectorShuffle( + resultTypeId, dst.valueId, src.valueId, + dst.componentCount, components); } return result; } - void DxbcCompiler::storeOperand( - const DxbcOperand& operand, - DxbcValue value, - DxbcComponentMask mask) { - const DxbcPointer ptr = this->getOperandPtr(operand); + DxbcValue2 DxbcCompiler2::extractReg( + const DxbcValue2& src, + DxbcRegMask mask) { + return this->swizzleReg(src, + DxbcRegSwizzle(0, 1, 2, 3), mask); + } + + + DxbcValue2 DxbcCompiler2::swizzleReg( + const DxbcValue2& src, + const DxbcRegSwizzle& swizzle, + DxbcRegMask mask) { + std::array indices; - // The value to store is actually allowed to be scalar, - // so we might need to create a vector from it. - if (value.type.componentCount == 1) - value = m_gen->regVector(value, mask.componentCount()); + uint32_t dstIndex = 0; + for (uint32_t i = 0; i < src.componentCount; i++) { + if (mask[i]) + indices[dstIndex++] = swizzle[i]; + } - // 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 = value.type.componentCount; - value = m_gen->regCast(value, dstType); + // If the swizzle combined with the mask can be reduced + // to a no-op, we don't need to insert any instructions. + bool isIdentitySwizzle = dstIndex == src.componentCount; - m_gen->regStore(ptr, value, mask); + for (uint32_t i = 0; i < dstIndex && isIdentitySwizzle; i++) + isIdentitySwizzle &= indices[i] == i; + + if (isIdentitySwizzle) + return src; + + // Use OpCompositeExtract if the resulting vector contains + // only one component, and OpVectorShuffle if it is a vector. + DxbcValue2 result; + result.componentType = src.componentType; + result.componentCount = dstIndex; + + const uint32_t resultTypeId = this->defineVectorType( + result.componentType, result.componentCount); + + if (dstIndex == 1) { + result.valueId = m_module.opCompositeExtract( + resultTypeId, src.valueId, 1, indices.data()); + } else { + result.valueId = m_module.opVectorShuffle( + resultTypeId, src.valueId, src.valueId, + dstIndex, indices.data()); + } + + return result; + } + + + DxbcValue2 DxbcCompiler2::extendReg( + const DxbcValue2& src, + uint32_t size) { + if (size == 1) + return src; + + std::array ids = { + src.valueId, src.valueId, + src.valueId, src.valueId, + }; + + uint32_t typeId = this->defineVectorType( + src.componentType, size); + + DxbcValue2 result; + result.componentType = src.componentType; + result.componentCount = size; + result.valueId = m_module.opCompositeConstruct( + typeId, size, ids.data()); + return result; + } + + + DxbcValue2 DxbcCompiler2::applyOperandModifiers( + DxbcValue2 value, + DxbcOperandModifiers modifiers) { + uint32_t typeId = this->defineVectorType( + value.componentType, value.componentCount); + + // Both modifiers can be applied to the same value. + // In that case, the absolute value is negated. + if (modifiers.test(DxbcOperandModifier::Abs)) + value.valueId = m_module.opFAbs(typeId, value.valueId); + + if (modifiers.test(DxbcOperandModifier::Neg)) + value.valueId = m_module.opFNegate(typeId, value.valueId); + + return value; + } + + + DxbcValue2 DxbcCompiler2::applyResultModifiers( + DxbcValue2 value, + DxbcOpcodeControl control) { + uint32_t typeId = this->defineVectorType( + value.componentType, value.componentCount); + + if (control.saturateBit()) { + value.valueId = m_module.opFClamp( + typeId, value.valueId, + m_module.constf32(0.0f), + m_module.constf32(1.0f)); + } + + return value; + } + + + DxbcValue2 DxbcCompiler2::loadOp( + const DxbcInstOp& srcOp, + DxbcRegMask srcMask, + DxbcScalarType dstType) { + if (srcOp.type == DxbcOperandType::Imm32) { + return this->loadImm32(srcOp, srcMask, dstType); + } else { + // Load operand value from the operand pointer + DxbcValue2 result = this->loadRegister(srcOp, srcMask, dstType); + + // Apply the component swizzle or the selection, + // depending on which mode the operand is in. + if (srcOp.componentCount == 4) { + switch (srcOp.componentMode) { + case DxbcRegMode::Swizzle: + result = this->swizzleReg(result, srcOp.swizzle, srcMask); + break; + + case DxbcRegMode::Select1: + result = this->extractReg(result, 1u << srcOp.select1); + break; + + default: + Logger::err("dxbc: Invalid component selection mode"); + } + } + + // Cast it to the requested type. We need to do + // this after the swizzling for 64-bit types. + if (result.componentType != dstType) + result = this->bitcastReg(result, dstType); + + // Apply operand modifiers + return this->applyOperandModifiers(result, srcOp.modifiers); + } + } + + + DxbcValue2 DxbcCompiler2::loadImm32( + const DxbcInstOp& srcOp, + DxbcRegMask srcMask, + DxbcScalarType dstType) { + // We will generate Uint32 constants because at this + // point we don't know how they are going to be used. + DxbcValue2 result; + result.componentType = DxbcScalarType::Uint32; + result.componentCount = srcMask.setCount(); + + uint32_t resultTypeId = this->defineVectorType( + result.componentType, result.componentCount); + + uint32_t constIds[4]; + uint32_t constIdx = 0; + + // Generate scalar constants for each component + // and pack them tightly in an array, so that we + // can use them to generate a constant vector. + if (srcOp.componentCount == 4) { + for (uint32_t i = 0; i < 4; i++) { + if (srcMask[i]) { + constIds[constIdx++] = m_module.constu32( + srcOp.immediates[i]); + } + } + } else if (srcOp.componentCount == 1) { + constIds[0] = m_module.constu32(srcOp.immediates[0]); + } else { + Logger::err("dxbc: Invalid imm32 component count"); + } + + // If the result is a vector, emit the final constant + if (result.componentCount == 1) { + result.valueId = constIds[0]; + } else { + result.valueId = m_module.constComposite( + resultTypeId, result.componentCount, constIds); + } + + // If necessary, cast the constant to the desired type + if (result.componentType != dstType) + result = this->bitcastReg(result, dstType); + + return result; + } + + + DxbcValue2 DxbcCompiler2::loadRegister( + const DxbcInstOp& srcOp, + DxbcRegMask srcMask, + DxbcScalarType dstType) { + return this->loadPtr( + this->getOperandPtr(srcOp)); + } + + + void DxbcCompiler2::storeOp( + const DxbcInstOp& dstOp, + const DxbcValue2& srcValue) { + this->storePtr( + this->getOperandPtr(dstOp), + srcValue, dstOp.mask); + } + + + DxbcValue2 DxbcCompiler2::loadPtr(const DxbcPointer2& ptr) { + const uint32_t typeId = this->defineVectorType( + ptr.componentType, ptr.componentCount); + + DxbcValue2 result; + result.componentType = ptr.componentType; + result.componentCount = ptr.componentCount; + result.valueId = m_module.opLoad(typeId, ptr.pointerId); + return result; + } + + + void DxbcCompiler2::storePtr( + const DxbcPointer2& ptr, + const DxbcValue2& value, + DxbcRegMask mask) { + DxbcValue2 srcValue = value; + + // If the source value consists of only one component, + // it is stored in all destination register components. + if (srcValue.componentCount == 1) + srcValue = this->extendReg(srcValue, mask.setCount()); + + // If the component types are not compatible, + // we need to bit-cast the source variable. + if (ptr.componentType != srcValue.componentType) + srcValue = this->bitcastReg(srcValue, ptr.componentType); + + if (mask.setCount() == ptr.componentCount) { + // Simple case: We write to the entire register + m_module.opStore(ptr.pointerId, srcValue.valueId); + } else { + // We only write to part of the destination + // register, so we need to load and modify it + DxbcValue2 tmp = this->loadPtr(ptr); + tmp = this->insertReg(tmp, srcValue, mask); + + m_module.opStore(ptr.pointerId, tmp.valueId); + } + + } + + + DxbcValue2 DxbcCompiler2::loadIndex(const DxbcInstOpIndex& idx) { + DxbcValue2 constantPart; + DxbcValue2 relativePart; + + if ((idx.type == DxbcIndexType::Immediate) || (idx.immediate != 0)) { + constantPart.componentType = DxbcScalarType::Sint32; + constantPart.componentCount = 1; + constantPart.valueId = m_module.consti32( + static_cast(idx.immediate)); + } + + if (idx.type == DxbcIndexType::Relative) { + DxbcInstOp offsetOp; + offsetOp.type = DxbcOperandType::Temp; + offsetOp.indexDim = 1; + offsetOp.index[0].type = DxbcIndexType::Immediate; + offsetOp.index[0].immediate = idx.tempRegId; + offsetOp.componentCount = 4; + offsetOp.componentMode = DxbcRegMode::Select1; + offsetOp.select1 = idx.tempRegComponent; + + relativePart = this->loadOp(offsetOp, + DxbcRegMask(true, false, false, false), + DxbcScalarType::Sint32); + } + + if (relativePart.valueId == 0) return constantPart; + if (constantPart.valueId == 0) return relativePart; + + DxbcValue2 result; + result.componentType = DxbcScalarType::Sint32; + result.componentCount = 1; + result.valueId = m_module.opIAdd( + this->defineScalarType(result.componentType), + relativePart.valueId, constantPart.valueId); + return result; + } + + + DxbcPointer2 DxbcCompiler2::getOperandPtr(const DxbcInstOp& op) { + DxbcPointer2 result; + + switch (op.type) { + case DxbcOperandType::Temp: + result.componentType = DxbcScalarType::Float32; + result.componentCount = 4; + result.pointerId = m_rRegs.at(op.index[0].immediate); + break; + + // TODO implement properly + case DxbcOperandType::Input: + result.componentType = DxbcScalarType::Float32; + result.componentCount = 4; + result.pointerId = m_vRegs.at(op.index[0].immediate); + break; + + // TODO implement properly + case DxbcOperandType::Output: + if (m_version.type() == DxbcProgramType::PixelShader) + return m_ps.oregs.at(op.index[0].immediate); + + result.componentType = DxbcScalarType::Float32; + result.componentCount = 4; + result.pointerId = m_oRegs.at(op.index[0].immediate); + break; + + case DxbcOperandType::ConstantBuffer: + return this->getConstantBufferPtr(op); + + default: + Logger::err("dxbc: Unhandled operand type"); + } + + return result; + } + + + DxbcPointer2 DxbcCompiler2::getConstantBufferPtr(const DxbcInstOp& op) { + if (op.indexDim != 2) { + Logger::err("dxbc: Constant buffer reference needs two indices"); + return DxbcPointer2(); + } + + // The operand itself has two indices: + // (1) The constant buffer ID (immediate) + // (2) The constant offset (relative) + const uint32_t bufferId = op.index[0].immediate; + const DxbcValue2 offset = this->loadIndex(op.index[1]); + + // The first index selects the struct member, + // the second one selects the array element. + std::array indices = { + m_module.constu32(0), offset.valueId }; + + DxbcPointer2 result; + result.componentType = DxbcScalarType::Float32; + result.componentCount = 4; + result.pointerId = m_module.opAccessChain( + this->definePointerType( + result.componentType, + result.componentCount, + spv::StorageClassUniform), + m_constantBuffers.at(bufferId).varId, + 2, indices.data()); + return result; + } + + + void DxbcCompiler2::beginVertexShader(const Rc& isgn) { + m_module.enableCapability(spv::CapabilityShader); + m_module.enableCapability(spv::CapabilityCullDistance); + m_module.enableCapability(spv::CapabilityClipDistance); + + // Declare the per-vertex output block. This is where + // the vertex shader will write the vertex position. + uint32_t perVertexStruct = this->definePerVertexBlock(); + 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, "vs_block"); + + // Main function of the vertex shader + m_vs.functionId = m_module.allocateId(); + m_module.setDebugName(m_vs.functionId, "vs_main"); + + m_module.functionBegin( + m_module.defVoidType(), + m_vs.functionId, + m_module.defFunctionType( + m_module.defVoidType(), 0, nullptr), + spv::FunctionControlMaskNone); + m_module.opLabel(m_module.allocateId()); + } + + + void DxbcCompiler2::beginPixelShader(const Rc& osgn) { + m_module.enableCapability(spv::CapabilityShader); + m_module.setOriginUpperLeft(m_entryPointId); + + // Declare pixel shader outputs. According to the Vulkan + // documentation, they are required to match the type of + // the render target. + for (auto e = m_osgn->begin(); e != m_osgn->end(); e++) { + if (e->systemValue == DxbcSystemValue::None) { + uint32_t regTypeId = this->defineVectorType( + e->componentType, e->componentMask.componentCount()); + + uint32_t ptrTypeId = m_module.defPointerType( + regTypeId, spv::StorageClassOutput); + + uint32_t varId = m_module.newVar( + ptrTypeId, spv::StorageClassOutput); + + m_module.decorateLocation(varId, e->registerId); + m_module.setDebugName(varId, str::format("o", e->registerId).c_str()); + m_entryPointInterfaces.push_back(varId); + + m_ps.oregs.at(e->registerId).componentType = e->componentType; + m_ps.oregs.at(e->registerId).componentCount = e->componentMask.componentCount(); + m_ps.oregs.at(e->registerId).pointerId = varId; + } + } + + // Main function of the pixel shader + m_ps.functionId = m_module.allocateId(); + m_module.setDebugName(m_ps.functionId, "ps_main"); + + m_module.functionBegin( + m_module.defVoidType(), + m_ps.functionId, + m_module.defFunctionType( + m_module.defVoidType(), 0, nullptr), + spv::FunctionControlMaskNone); + m_module.opLabel(m_module.allocateId()); + } + + + void DxbcCompiler2::prepareVertexInputs() { + // TODO implement + } + + + void DxbcCompiler2::preparePixelInputs() { + // TODO implement + } + + + void DxbcCompiler2::prepareVertexOutputs() { + for (const DxbcSvMapping2& svMapping : m_oSvs) { + switch (svMapping.sv) { + case DxbcSystemValue::Position: { + DxbcPointer2 dstPtr; + dstPtr.componentType = DxbcScalarType::Float32; + dstPtr.componentCount = 4; + + uint32_t regTypeId = this->defineVectorType( + dstPtr.componentType, dstPtr.componentCount); + + uint32_t ptrTypeId = m_module.defPointerType( + regTypeId, spv::StorageClassOutput); + + uint32_t memberId = m_module.constu32(PerVertex_Position); + + dstPtr.pointerId = m_module.opAccessChain( + ptrTypeId, m_perVertexOut, 1, &memberId); + + DxbcPointer2 srcPtr; + srcPtr.componentType = DxbcScalarType::Float32; + srcPtr.componentCount = 4; + srcPtr.pointerId = m_oRegs.at(svMapping.regId); + + this->storePtr(dstPtr, this->loadPtr(srcPtr), + DxbcRegMask(true, true, true, true)); + } break; + + default: + Logger::err(str::format( + "dxbc: Unsupported vertex sv output: ", + svMapping.sv)); + } + } + } + + + void DxbcCompiler2::preparePixelOutputs() { + // TODO implement + } + + + void DxbcCompiler2::endVertexShader() { + this->prepareVertexInputs(); + m_module.opFunctionCall( + m_module.defVoidType(), + m_vs.functionId, 0, nullptr); + this->prepareVertexOutputs(); + } + + + void DxbcCompiler2::endPixelShader() { + this->preparePixelInputs(); + m_module.opFunctionCall( + m_module.defVoidType(), + m_ps.functionId, 0, nullptr); + this->preparePixelOutputs(); + } + + + uint32_t DxbcCompiler2::definePerVertexBlock() { + uint32_t t_f32 = m_module.defFloatType(32); + uint32_t t_f32_v4 = m_module.defVectorType(t_f32, 4); + uint32_t t_f32_a2 = m_module.defArrayType(t_f32, m_module.constu32(2)); + + std::array members; + members[PerVertex_Position] = t_f32_v4; + members[PerVertex_PointSize] = t_f32; + members[PerVertex_CullDist] = t_f32_a2; + members[PerVertex_ClipDist] = t_f32_a2; + + uint32_t typeId = m_module.defStructTypeUnique( + members.size(), members.data()); + + m_module.memberDecorateBuiltIn(typeId, PerVertex_Position, spv::BuiltInPosition); + m_module.memberDecorateBuiltIn(typeId, PerVertex_PointSize, spv::BuiltInPointSize); + m_module.memberDecorateBuiltIn(typeId, PerVertex_CullDist, spv::BuiltInCullDistance); + m_module.memberDecorateBuiltIn(typeId, PerVertex_ClipDist, spv::BuiltInClipDistance); + m_module.decorateBlock(typeId); + + m_module.setDebugName(typeId, "per_vertex"); + m_module.setDebugMemberName(typeId, PerVertex_Position, "position"); + m_module.setDebugMemberName(typeId, PerVertex_PointSize, "point_size"); + m_module.setDebugMemberName(typeId, PerVertex_CullDist, "cull_dist"); + m_module.setDebugMemberName(typeId, PerVertex_ClipDist, "clip_dist"); + return typeId; + } + + + uint32_t DxbcCompiler2::defineScalarType( + DxbcScalarType componentType) { + switch (componentType) { + case DxbcScalarType::Float32: return m_module.defFloatType(32); + case DxbcScalarType::Float64: return m_module.defFloatType(64); + case DxbcScalarType::Uint32: return m_module.defIntType(32, 0); + case DxbcScalarType::Uint64: return m_module.defIntType(64, 0); + case DxbcScalarType::Sint32: return m_module.defIntType(32, 1); + case DxbcScalarType::Sint64: return m_module.defIntType(64, 1); + } + + Logger::err("dxbc: Invalid scalar type"); + return 0; + } + + + uint32_t DxbcCompiler2::defineVectorType( + DxbcScalarType componentType, + uint32_t componentCount) { + uint32_t typeId = this->defineScalarType(componentType); + + if (componentCount > 1) { + typeId = m_module.defVectorType( + typeId, componentCount); + } + + return typeId; + } + + + uint32_t DxbcCompiler2::definePointerType( + DxbcScalarType componentType, + uint32_t componentCount, + spv::StorageClass storageClass) { + return m_module.defPointerType( + this->defineVectorType( + componentType, componentCount), + storageClass); + } + + + DxbcError DxbcCompiler2::parseInstruction(const DxbcInstruction& ins, DxbcInst& out) { + out.opcode = ins.token().opcode(); + out.control = ins.token().control(); + out.format = dxbcInstructionFormat(out.opcode); + + // TODO implement extended opcodes + + uint32_t argOffset = 0; + + for (uint32_t i = 0; i < out.format.operandCount; i++) { + + if (out.format.operands[i].kind == DxbcOperandKind::Imm32) { + // Some declarations use immediate DWORDs rather than the + // immediate operand tokens, but we'll treat them the same. + out.operands[i].type = DxbcOperandType::Imm32; + out.operands[i].immediates[0] = ins.arg(argOffset); + argOffset += 1; + } else { + // Most instructions use proper operand tokens, which either + // store an immediate value or references a register from one + // of the indexable register files. + const DxbcOperand op = ins.operand(argOffset); + out.operands[i].type = op.token().type(); + + // Parse operand modifiers + DxbcOperandTokenExt token; + + if (op.queryOperandExt(DxbcOperandExt::OperandModifier, token)) + out.operands[i].modifiers = DxbcOperandModifiers(token.data()); + + // Parse immediate values if applicable + if (op.token().type() == DxbcOperandType::Imm32) { + for (uint32_t j = 0; j < op.token().numComponents(); j++) + out.operands[i].immediates[j] = op.imm32(j); + } + + if (op.token().type() == DxbcOperandType::Imm64) { + Logger::err("dxbc: 64-bit immediates not supported"); + return DxbcError::eInstructionFormat; + } + + // Parse the indices. Each index is either a constant value + // or the sum of a constant and a temporary register value. + out.operands[i].indexDim = op.token().indexDimension(); + + for (uint32_t j = 0; j < op.token().indexDimension(); j++) { + const DxbcOperandIndex index = op.index(j); + + DxbcInstOpIndex& opIndex = out.operands[i].index[j]; + opIndex.type = index.hasRelPart() + ? DxbcIndexType::Relative + : DxbcIndexType::Immediate; + opIndex.immediate = index.immPart(); + + // If the index is relative, we have to parse another + // operand token which must reference a r# register. + if (index.hasRelPart()) { + const DxbcOperand relPart = index.relPart(); + + if (relPart.token().type() != DxbcOperandType::Temp) { + Logger::err("dxbc: Invalid index register type"); + return DxbcError::eInstructionFormat; + } + + if ((relPart.token().indexDimension() != 1) + || (relPart.index(0).hasRelPart())) { + Logger::err("dxbc: Invalid index register index"); + return DxbcError::eInstructionFormat; + } + + if (relPart.token().selectionMode() != DxbcRegMode::Select1) { + Logger::err("dxbc: Invalid index component selection mode"); + return DxbcError::eInstructionFormat; + } + + // Assign the index and the component selection + opIndex.tempRegId = relPart.index(0).immPart(); + opIndex.tempRegComponent = relPart.token().select1(); + } + } + + // Parse component mask, swizzle or selection + out.operands[i].componentCount = op.token().numComponents(); + out.operands[i].componentMode = op.token().selectionMode(); + + if (op.token().numComponents() == 4) { + switch (op.token().selectionMode()) { + case DxbcRegMode::Mask: + out.operands[i].mask = op.token().mask(); + break; + + case DxbcRegMode::Swizzle: + out.operands[i].swizzle = op.token().swizzle(); + break; + + case DxbcRegMode::Select1: + out.operands[i].select1 = op.token().select1(); + break; + } + } + + // Move on to next operand + argOffset += op.length(); + } + } + + return DxbcError::sOk; } } \ No newline at end of file diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index afce1f419..8f9dc8bb5 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -1,117 +1,438 @@ #pragma once -#include "./gen/dxbc_gen_common.h" +#include "../spirv/spirv_module.h" + +#include "dxbc_chunk_isgn.h" +#include "dxbc_decoder.h" +#include "dxbc_defs.h" namespace dxvk { + // TODO deprecate DxbcComponentSelectionMode + using DxbcRegMode = DxbcComponentSelectionMode; + + struct DxbcValue2 { + DxbcScalarType componentType = DxbcScalarType::Float32; + uint32_t componentCount = 0; + uint32_t valueId = 0; + }; + + struct DxbcPointer2 { + DxbcScalarType componentType = DxbcScalarType::Float32; + uint32_t componentCount = 0; + uint32_t pointerId = 0; + }; + /** - * \brief DXBC compiler + * \brief Constant buffer binding * - * Interprets DXBC instructions and generates - * SPIR-V code for the appropriate shader type. + * Stores information required to + * access a constant buffer. */ - class DxbcCompiler { + struct DxbcConstantBuffer2 { + uint32_t varId = 0; + uint32_t size = 0; + }; + + /** + * \brief Sampler binding + * + * Stores a sampler variable that can be + * used together with a texture resource. + */ + struct DxbcSampler2 { + uint32_t varId = 0; + uint32_t typeId = 0; + }; + + + /** + * \brief Shader resource binding + * + * Stores a resource variable + * and associated type IDs. + */ + struct DxbcShaderResource2 { + uint32_t varId = 0; + uint32_t sampledTypeId = 0; + uint32_t textureTypeId = 0; + }; + + /** + * \brief System value mapping + * + * Maps a system value to a given set of + * components of an input or output register. + */ + struct DxbcSvMapping2 { + uint32_t regId; + DxbcRegMask regMask; + DxbcSystemValue sv; + }; + + /** + * \brief Compiler error code + * + * Helps identify the type of error + * that may occur during compilation. + */ + enum class DxbcError { + sOk, + eInternal, + eInstructionFormat, + eInvalidOperand, + eInvalidOperandIndex, + eTypeMismatch, + eUnhandledOpcode, + eUnsupported, + }; + + + /** + * \brief Operand index type + * + * Defines whether a register index + * is relative or constant. + */ + enum class DxbcIndexType { + Immediate, ///< Index is a constant value + Relative, ///< Index depends on a r# register + }; + + + /** + * \brief Instruction operand index + * + * Stores the type of the index as well as the + * register (if relative) and the constant offset. + */ + struct DxbcInstOpIndex { + DxbcIndexType type = DxbcIndexType::Immediate; + uint32_t immediate = 0; + uint32_t tempRegId = 0; + uint32_t tempRegComponent = 0; + }; + + /** + * \brief Instruction operand + * + * Stores all information about a single + * operand, including the register index. + */ + struct DxbcInstOp { + DxbcOperandType type = DxbcOperandType::Temp; + DxbcOperandModifiers modifiers = 0; + uint32_t immediates[4] = { 0u, 0u, 0u, 0u }; + + uint32_t indexDim = 0; + DxbcInstOpIndex index[3]; + + uint32_t componentCount = 0; + DxbcRegMode componentMode = DxbcRegMode::Mask; + + DxbcRegMask mask = { false, false, false, false }; + DxbcRegSwizzle swizzle = { 0, 0, 0, 0 }; + uint32_t select1 = 0; + }; + + + /** + * \brief Decoded instruction + * + * Stores all information about a single + * instruction, including its operands. + */ + struct DxbcInst { + DxbcOpcode opcode = DxbcOpcode::Nop; + DxbcOpcodeControl control = 0; + DxbcInstFormat format; + DxbcInstOp operands[DxbcMaxOperandCount]; + }; + + + /** + * \brief Vertex shader-specific data + */ + struct DxbcVsSpecifics { + uint32_t functionId = 0; + }; + + + /** + * \brief Pixel shader-specific data + */ + struct DxbcPsSpecifics { + uint32_t functionId = 0; + + std::array oregs; + }; + + + /** + * \brief DXBC to SPIR-V shader compiler + * + * Processes instructions from a DXBC shader and creates + * a DXVK shader object, which contains the SPIR-V module + * and information about the shader resource bindings. + */ + class DxbcCompiler2 { public: - DxbcCompiler( + DxbcCompiler2( const DxbcProgramVersion& version, const Rc& isgn, const Rc& osgn); - ~DxbcCompiler(); + ~DxbcCompiler2(); - void processInstruction( + /** + * \brief Processes a single instruction + * + * \param [in] ins The instruction + * \returns An error code, or \c sOK + */ + DxbcError processInstruction( const DxbcInstruction& ins); + /** + * \brief Finalizes the shader + * \returns The final shader object + */ Rc finalize(); private: - Rc m_gen; + DxbcProgramVersion m_version; + SpirvModule m_module; - void dclGlobalFlags( - const DxbcInstruction& ins); + Rc m_isgn; + Rc m_osgn; - void dclConstantBuffer( - const DxbcInstruction& ins); + /////////////////////////////////////////////////////// + // Resource slot description for the shader. This will + // be used to map D3D11 bindings to DXVK bindings. + std::vector m_resourceSlots; - void dclResource( - const DxbcInstruction& ins); + /////////////////////////////// + // r# registers of type float4 + std::vector m_rRegs; - void dclSampler( - const DxbcInstruction& ins); + /////////////////////////////////////////////////////////// + // v# registers as defined by the shader. The type of each + // of these inputs is either float4 or an array of float4. + std::array m_vRegs; - void dclInterfaceVar( - const DxbcInstruction& ins); + ////////////////////////////////////////////////////////// + // o# registers as defined by the shader. In the fragment + // shader stage, these registers are typed by the signature, + // in all other stages, they are float4 registers or arrays. + std::array m_oRegs; - void dclTemps( - const DxbcInstruction& ins); + ////////////////////////////////////////////////////// + // Shader resource variables. These provide access to + // constant buffers, samplers, textures, and UAVs. + std::array m_constantBuffers; + std::array m_samplers; + std::array m_textures; - void opAdd( - const DxbcInstruction& ins); + //////////////////////////////////////////////////////// + // Input/Output system value mappings. These will need + // to be set up before or after the main function runs. + std::vector m_vSvs; + std::vector m_oSvs; - void opMad( - const DxbcInstruction& ins); + /////////////////////////////////////////////////////////// + // Array of input values. Since v# registers are indexable + // in DXBC, we need to copy them into an array first. + uint32_t m_vArray = 0; - void opMul( - const DxbcInstruction& ins); + //////////////////////////////////////////////////// + // Per-vertex input and output blocks. Depending on + // the shader stage, these may be declared as arrays. + uint32_t m_perVertexIn = 0; + uint32_t m_perVertexOut = 0; - void opDpx( - const DxbcInstruction& ins, - uint32_t n); + /////////////////////////////////////////////////// + // Entry point description - we'll need to declare + // the function ID and all input/output variables. + std::vector m_entryPointInterfaces; + uint32_t m_entryPointId = 0; - void opRsq( - const DxbcInstruction& ins); + //////////////////////////////////////// + // Data structures for each shader type + DxbcVsSpecifics m_vs; + DxbcPsSpecifics m_ps; - void opMov( - const DxbcInstruction& ins); + ////////////////////////////// + // Instruction class handlers + DxbcError handleDeclaration( + const DxbcInst& ins); - void opRet( - const DxbcInstruction& ins); + DxbcError handleControlFlow( + const DxbcInst& ins); - void opSample( - const DxbcInstruction& ins); + DxbcError handleTextureSample( + const DxbcInst& ins); - DxbcValue getDynamicIndexValue( - const DxbcOperandIndex& index); + DxbcError handleVectorAlu( + const DxbcInst& ins); - DxbcComponentMask getDstOperandMask( - const DxbcOperand& operand); + DxbcError handleVectorDot( + const DxbcInst& ins); - DxbcPointer getTempOperandPtr( - const DxbcOperand& operand); + /////////////////////// + // Declaration methods + DxbcError declareGlobalFlags( + const DxbcInst& ins); - DxbcPointer getInterfaceOperandPtr( - const DxbcOperand& operand); + DxbcError declareTemps( + const DxbcInst& ins); - DxbcPointer getConstantBufferPtr( - const DxbcOperand& operand); + DxbcError declareInterfaceVar( + const DxbcInst& ins); - DxbcPointer getOperandPtr( - const DxbcOperand& operand); + DxbcError declareConstantBuffer( + const DxbcInst& ins); - DxbcValue selectOperandComponents( - const DxbcOperandToken& opToken, - const DxbcValue& opValue, - DxbcComponentMask dstMask); + DxbcError declareSampler( + const DxbcInst& ins); - DxbcValue applyOperandModifiers( - DxbcValue value, - DxbcOperandModifiers modifiers); + DxbcError declareResource( + const DxbcInst& ins); - DxbcValue applyResultModifiers( - DxbcValue value, - DxbcOpcodeControl control); - - DxbcValue loadOperand( - const DxbcOperand& operand, - DxbcComponentMask dstMask, - DxbcScalarType dstType); + DxbcError declareInputVar( + uint32_t regId, + uint32_t regDim, + DxbcRegMask regMask, + DxbcSystemValue sv, + DxbcInterpolationMode im); - void storeOperand( - const DxbcOperand& operand, - DxbcValue value, - DxbcComponentMask mask); + DxbcError declareOutputVar( + uint32_t regId, + uint32_t regDim, + DxbcRegMask regMask, + DxbcSystemValue sv, + DxbcInterpolationMode im); + + //////////////////////////////////// + // Register manipulation operations + DxbcValue2 bitcastReg( + const DxbcValue2& src, + DxbcScalarType type); + + DxbcValue2 insertReg( + const DxbcValue2& dst, + const DxbcValue2& src, + DxbcRegMask mask); + + DxbcValue2 extractReg( + const DxbcValue2& src, + DxbcRegMask mask); + + DxbcValue2 swizzleReg( + const DxbcValue2& src, + const DxbcRegSwizzle& swizzle, + DxbcRegMask mask); + + DxbcValue2 regVector( + const DxbcValue2& src, + uint32_t size); + + DxbcValue2 extendReg( + const DxbcValue2& src, + uint32_t size); + + //////////////////////////// + // Operand modifier methods + DxbcValue2 applyOperandModifiers( + DxbcValue2 value, + DxbcOperandModifiers modifiers); + + DxbcValue2 applyResultModifiers( + DxbcValue2 value, + DxbcOpcodeControl control); + + ///////////////////////// + // Load/Store operations + DxbcValue2 loadOp( + const DxbcInstOp& srcOp, + DxbcRegMask srcMask, + DxbcScalarType dstType); + + DxbcValue2 loadImm32( + const DxbcInstOp& srcOp, + DxbcRegMask srcMask, + DxbcScalarType dstType); + + DxbcValue2 loadRegister( + const DxbcInstOp& srcOp, + DxbcRegMask srcMask, + DxbcScalarType dstType); + + void storeOp( + const DxbcInstOp& dstOp, + const DxbcValue2& srcValue); + + DxbcValue2 loadPtr( + const DxbcPointer2& ptr); + + void storePtr( + const DxbcPointer2& ptr, + const DxbcValue2& value, + DxbcRegMask mask); + + DxbcValue2 loadIndex( + const DxbcInstOpIndex& idx); + + /////////////////////////// + // Operand pointer methods + DxbcPointer2 getOperandPtr( + const DxbcInstOp& op); + + DxbcPointer2 getConstantBufferPtr( + const DxbcInstOp& op); + + ///////////////////////////////// + // Shader initialization methods + void beginVertexShader(const Rc& isgn); + void beginPixelShader (const Rc& osgn); + + ///////////////////////////// + // Input preparation methods + void prepareVertexInputs(); + void preparePixelInputs(); + + ////////////////////////////// + // Output preparation methods + void prepareVertexOutputs(); + void preparePixelOutputs(); + + /////////////////////////////// + // Shader finalization methods + void endVertexShader(); + void endPixelShader(); + + /////////////////////////// + // Type definition methods + uint32_t definePerVertexBlock(); + + uint32_t defineScalarType( + DxbcScalarType componentType); + + uint32_t defineVectorType( + DxbcScalarType componentType, + uint32_t componentCount); + + uint32_t definePointerType( + DxbcScalarType componentType, + uint32_t componentCount, + spv::StorageClass storageClass); + + ///////////////////////// + // DXBC decoding methods + DxbcError parseInstruction( + const DxbcInstruction& ins, + DxbcInst& out); }; diff --git a/src/dxbc/dxbc_decoder.h b/src/dxbc/dxbc_decoder.h index 7b02d4674..3f580138c 100644 --- a/src/dxbc/dxbc_decoder.h +++ b/src/dxbc/dxbc_decoder.h @@ -10,6 +10,72 @@ namespace dxvk { class DxbcOperand; + /** + * \brief Component swizzle + * + * Maps vector components to + * other vector components. + * TODO remove old class + */ + class DxbcRegSwizzle { + + public: + + DxbcRegSwizzle() { } + DxbcRegSwizzle(uint32_t x, uint32_t y, uint32_t z, uint32_t w) + : m_data((x << 0) | (y << 2) | (z << 4) | (w << 6)) { } + + uint32_t operator [] (uint32_t id) const { + return (m_data >> (id + id)) & 0x3; + } + + private: + + uint8_t m_data = 0; + + }; + + + /** + * \brief Component mask + * + * Enables access to certain + * subset of vector components. + * TODO remove old class + */ + class DxbcRegMask { + + public: + + DxbcRegMask() { } + DxbcRegMask(uint32_t mask) : m_data(mask) { } + DxbcRegMask(bool x, bool y, bool z, bool w) + : m_data((x ? 0x1 : 0) | (y ? 0x2 : 0) + | (z ? 0x4 : 0) | (w ? 0x8 : 0)) { } + + bool operator [] (uint32_t id) const { + return (m_data >> 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]; + } + + 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]; + } + + private: + + uint8_t m_data = 0; + + }; + + /** * \brief Basic control info * @@ -74,6 +140,17 @@ namespace dxvk { return bit::extract(m_control, 0, 3); } + /** + * \brief Resource dimension + * + * Defines the type of a resource. + * Only valid for dcl_resource etc. + */ + DxbcResourceDim resourceDim() const { + return static_cast( + bit::extract(m_control, 0, 4)); + } + private: uint32_t m_control = 0; @@ -233,7 +310,7 @@ namespace dxvk { * \returns Number of components */ uint32_t numComponents() const { - std::array count = { 0, 1, 4 }; + std::array count = { 0, 1, 4, 0 }; return count.at(bit::extract(m_token, 0, 1)); } @@ -261,6 +338,17 @@ namespace dxvk { return DxbcComponentMask(bit::extract(m_token, 4, 7)); } + /** + * \brief Component mask + * + * Used when the component selection mode is + * \c DxbcComponentSelectionMode::Mask. + * \returns The component mask + */ + DxbcRegMask mask() const { + return DxbcRegMask(bit::extract(m_token, 4, 7)); + } + /** * \brief Component swizzle * @@ -276,6 +364,29 @@ namespace dxvk { bit::extract(m_token, 10, 11)); } + /** + * \brief Component swizzle + * + * Used when the component selection mode is + * \c DxbcComponentSelectionMode::Swizzle. + * \returns The component swizzle + */ + DxbcRegSwizzle swizzle() const { + return DxbcRegSwizzle( + bit::extract(m_token, 4, 5), + bit::extract(m_token, 6, 7), + bit::extract(m_token, 8, 9), + bit::extract(m_token, 10, 11)); + } + + /** + * \brief Single component selection + * \returns The component index + */ + uint32_t select1() const { + return bit::extract(m_token, 4, 5); + } + /** * \brief Component selection * diff --git a/src/dxbc/dxbc_defs.cpp b/src/dxbc/dxbc_defs.cpp new file mode 100644 index 000000000..1549939eb --- /dev/null +++ b/src/dxbc/dxbc_defs.cpp @@ -0,0 +1,500 @@ +#include "dxbc_defs.h" + +namespace dxvk { + + const std::array g_instructionFormats = {{ + /* Add */ + { 3, DxbcInstClass::VectorAlu, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + } }, + /* And */ + { }, + /* Break */ + { }, + /* Breakc */ + { }, + /* Call */ + { }, + /* Callc */ + { }, + /* Case */ + { }, + /* Continue */ + { }, + /* Continuec */ + { }, + /* Cut */ + { }, + /* Default */ + { }, + /* DerivRtx */ + { }, + /* DerivRty */ + { }, + /* Discard */ + { }, + /* Div */ + { }, + /* Dp2 */ + { 3, DxbcInstClass::VectorDot, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + } }, + /* Dp3 */ + { 3, DxbcInstClass::VectorDot, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + } }, + /* Dp4 */ + { 3, DxbcInstClass::VectorDot, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + } }, + /* Else */ + { }, + /* Emit */ + { }, + /* EmitThenCut */ + { }, + /* EndIf */ + { }, + /* EndLoop */ + { }, + /* EndSwitch */ + { }, + /* Eq */ + { }, + /* Exp */ + { }, + /* Frc */ + { }, + /* FtoI */ + { }, + /* FtoU */ + { }, + /* Ge */ + { }, + /* IAdd */ + { }, + /* If */ + { }, + /* IEq */ + { }, + /* IGe */ + { }, + /* ILt */ + { }, + /* IMad */ + { }, + /* IMax */ + { }, + /* IMin */ + { }, + /* IMul */ + { }, + /* INe */ + { }, + /* INeg */ + { }, + /* IShl */ + { }, + /* IShr */ + { }, + /* ItoF */ + { }, + /* Label */ + { }, + /* Ld */ + { }, + /* LdMs */ + { }, + /* Log */ + { }, + /* Loop */ + { }, + /* Lt */ + { }, + /* Mad */ + { 4, DxbcInstClass::VectorAlu, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + } }, + /* Min */ + { }, + /* Max */ + { }, + /* CustomData */ + { }, + /* Mov */ + { 2, DxbcInstClass::VectorAlu, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + } }, + /* Movc */ + { }, + /* Mul */ + { 3, DxbcInstClass::VectorAlu, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + } }, + /* Ne */ + { }, + /* Nop */ + { }, + /* Not */ + { }, + /* Or */ + { }, + /* ResInfo */ + { }, + /* Ret */ + { 0, DxbcInstClass::ControlFlow }, + /* Retc */ + { }, + /* RoundNe */ + { }, + /* RoundNi */ + { }, + /* RoundPi */ + { }, + /* RoundZ */ + { }, + /* Rsq */ + { 2, DxbcInstClass::VectorAlu, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + } }, + /* Sample */ + { 4, DxbcInstClass::TextureSample, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::SrcReg, DxbcScalarType::Float32 }, + } }, + /* SampleC */ + { }, + /* SampleClz */ + { }, + /* SampleL */ + { }, + /* SampleD */ + { }, + /* SampleB */ + { }, + /* Sqrt */ + { }, + /* Switch */ + { }, + /* SinCos */ + { }, + /* UDiv */ + { }, + /* ULt */ + { }, + /* UGe */ + { }, + /* UMul */ + { }, + /* UMad */ + { }, + /* UMax */ + { }, + /* UMin */ + { }, + /* UShr */ + { }, + /* UtoF */ + { }, + /* Xor */ + { }, + /* DclResource */ + { 2, DxbcInstClass::Declaration, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::Imm32, DxbcScalarType::Uint32 }, + } }, + /* DclConstantBuffer */ + { 1, DxbcInstClass::Declaration, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + } }, + /* DclSampler */ + { 1, DxbcInstClass::Declaration, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + } }, + /* DclIndexRange */ + { }, + /* DclGsOutputPrimitiveTopology */ + { }, + /* DclGsInputPrimitive */ + { }, + /* DclMaxOutputVertexCount */ + { }, + /* DclInput */ + { 1, DxbcInstClass::Declaration, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + } }, + /* DclInputSgv */ + { 2, DxbcInstClass::Declaration, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::Imm32, DxbcScalarType::Uint32 }, + } }, + /* DclInputSiv */ + { 2, DxbcInstClass::Declaration, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::Imm32, DxbcScalarType::Uint32 }, + } }, + /* DclInputPs */ + { 1, DxbcInstClass::Declaration, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + } }, + /* DclInputPsSgv */ + { 2, DxbcInstClass::Declaration, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::Imm32, DxbcScalarType::Uint32 }, + } }, + /* DclInputPsSiv */ + { 2, DxbcInstClass::Declaration, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::Imm32, DxbcScalarType::Uint32 }, + } }, + /* DclOutput */ + { 1, DxbcInstClass::Declaration, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + } }, + /* DclOutputSgv */ + { 2, DxbcInstClass::Declaration, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::Imm32, DxbcScalarType::Uint32 }, + } }, + /* DclOutputSiv */ + { 2, DxbcInstClass::Declaration, { + { DxbcOperandKind::DstReg, DxbcScalarType::Float32 }, + { DxbcOperandKind::Imm32, DxbcScalarType::Uint32 }, + } }, + /* DclTemps */ + { 1, DxbcInstClass::Declaration, { + { DxbcOperandKind::Imm32, DxbcScalarType::Uint32 }, + } }, + /* DclIndexableTemp */ + { }, + /* DclGlobalFlags */ + { 0, DxbcInstClass::Declaration }, + /* Reserved0 */ + { 0, DxbcInstClass::Undefined }, + /* Lod */ + { }, + /* Gather4 */ + { }, + /* SamplePos */ + { }, + /* SampleInfo */ + { }, + /* Reserved1 */ + { }, + /* HsDecls */ + { }, + /* HsControlPointPhase */ + { }, + /* HsForkPhase */ + { }, + /* HsJoinPhase */ + { }, + /* EmitStream */ + { }, + /* CutStream */ + { }, + /* EmitThenCutStream */ + { }, + /* InterfaceCall */ + { }, + /* BufInfo */ + { }, + /* DerivRtxCoarse */ + { }, + /* DerivRtxFine */ + { }, + /* DerivRtyCoarse */ + { }, + /* DerivRtyFine */ + { }, + /* Gather4C */ + { }, + /* Gather4Po */ + { }, + /* Gather4PoC */ + { }, + /* Rcp */ + { }, + /* F32toF16 */ + { }, + /* F16toF32 */ + { }, + /* UAddc */ + { }, + /* USubb */ + { }, + /* CountBits */ + { }, + /* FirstBitHi */ + { }, + /* FirstBitLo */ + { }, + /* FirstBitShi */ + { }, + /* UBfe */ + { }, + /* IBfe */ + { }, + /* Bfi */ + { }, + /* BfRev */ + { }, + /* Swapc */ + { }, + /* DclStream */ + { }, + /* DclFunctionBody */ + { }, + /* DclFunctionTable */ + { }, + /* DclInterface */ + { }, + /* DclInputControlPointCount */ + { }, + /* DclOutputControlPointCount */ + { }, + /* DclTessDomain */ + { }, + /* DclTessPartitioning */ + { }, + /* DclTessOutputPrimitive */ + { }, + /* DclHsMaxTessFactor */ + { }, + /* DclHsForkPhaseInstanceCount */ + { }, + /* DclHsJoinPhaseInstanceCount */ + { }, + /* DclThreadGroup */ + { }, + /* DclUavTyped */ + { }, + /* DclUavRaw */ + { }, + /* DclUavStructured */ + { }, + /* DclThreadGroupSharedMemoryRaw */ + { }, + /* DclThreadGroupSharedMemoryStructured */ + { }, + /* DclResourceRaw */ + { }, + /* DclResourceStructured */ + { }, + /* LdUavTyped */ + { }, + /* StoreUavTyped */ + { }, + /* LdRaw */ + { }, + /* StoreRaw */ + { }, + /* LdStructured */ + { }, + /* StoreStructured */ + { }, + /* AtomicAnd */ + { }, + /* AtomicOr */ + { }, + /* AtomicXor */ + { }, + /* AtomicCmpStore */ + { }, + /* AtomicIAdd */ + { }, + /* AtomicIMax */ + { }, + /* AtomicIMin */ + { }, + /* AtomicUMax */ + { }, + /* AtomicUMin */ + { }, + /* ImmAtomicAlloc */ + { }, + /* ImmAtomicConsume */ + { }, + /* ImmAtomicIAdd */ + { }, + /* ImmAtomicAnd */ + { }, + /* ImmAtomicOr */ + { }, + /* ImmAtomicXor */ + { }, + /* ImmAtomicExch */ + { }, + /* ImmAtomicCmpExch */ + { }, + /* ImmAtomicImax */ + { }, + /* ImmAtomicImin */ + { }, + /* ImmAtomicUmax */ + { }, + /* ImmAtomicUmin */ + { }, + /* Sync */ + { }, + /* DAdd */ + { }, + /* DMax */ + { }, + /* DMin */ + { }, + /* DMul */ + { }, + /* DEq */ + { }, + /* DGe */ + { }, + /* DLt */ + { }, + /* DNe */ + { }, + /* DMov */ + { }, + /* DMovc */ + { }, + /* DtoF */ + { }, + /* FtoD */ + { }, + /* EvalSnapped */ + { }, + /* EvalSampleIndex */ + { }, + /* EvalCentroid */ + { }, + /* DclGsInstanceCount */ + { }, + }}; + + + DxbcInstFormat dxbcInstructionFormat(DxbcOpcode opcode) { + const uint32_t idx = static_cast(opcode); + + return (idx < g_instructionFormats.size()) + ? g_instructionFormats.at(idx) + : DxbcInstFormat(); + } + +} \ No newline at end of file diff --git a/src/dxbc/dxbc_defs.h b/src/dxbc/dxbc_defs.h new file mode 100644 index 000000000..57fede352 --- /dev/null +++ b/src/dxbc/dxbc_defs.h @@ -0,0 +1,40 @@ +#pragma once + +#include "dxbc_enums.h" +#include "dxbc_type.h" + +namespace dxvk { + + constexpr size_t DxbcMaxInterfaceRegs = 32; + constexpr size_t DxbcMaxOperandCount = 8; + + enum class DxbcOperandKind { + DstReg, ///< Destination register + SrcReg, ///< Source register + Imm32, ///< Constant number + }; + + enum class DxbcInstClass { + Declaration, ///< Interface or resource declaration + TextureSample, ///< Texture sampling instruction + VectorAlu, ///< Component-wise vector instructions + VectorCmp, ///< Component-wise vector comparison + VectorDot, ///< Dot product instruction + ControlFlow, ///< Control flow instructions + Undefined, ///< Instruction code not defined + }; + + struct DxbcInstOperandFormat { + DxbcOperandKind kind; + DxbcScalarType type; + }; + + struct DxbcInstFormat { + uint32_t operandCount = 0; + DxbcInstClass instructionClass = DxbcInstClass::Undefined; + DxbcInstOperandFormat operands[DxbcMaxOperandCount]; + }; + + DxbcInstFormat dxbcInstructionFormat(DxbcOpcode opcode); + +} \ No newline at end of file diff --git a/src/dxbc/dxbc_module.cpp b/src/dxbc/dxbc_module.cpp index 8bfd2250e..dd87295d5 100644 --- a/src/dxbc/dxbc_module.cpp +++ b/src/dxbc/dxbc_module.cpp @@ -44,12 +44,20 @@ namespace dxvk { if (m_shexChunk == nullptr) throw DxvkError("DxbcModule::compile: No SHDR/SHEX chunk"); - DxbcCompiler compiler( + DxbcCompiler2 compiler( m_shexChunk->version(), m_isgnChunk, m_osgnChunk); - for (auto ins : *m_shexChunk) - compiler.processInstruction(ins); + for (auto ins : *m_shexChunk) { + const DxbcError error = compiler.processInstruction(ins); + + if (error != DxbcError::sOk) { + Logger::err(str::format( + "dxbc: Error while processing ", + ins.token().opcode(), ": Error ", + static_cast(error))); + } + } return compiler.finalize(); } diff --git a/src/dxbc/gen/dxbc_gen_common.cpp b/src/dxbc/gen/dxbc_gen_common.cpp deleted file mode 100644 index b60e08d96..000000000 --- a/src/dxbc/gen/dxbc_gen_common.cpp +++ /dev/null @@ -1,707 +0,0 @@ -#include "dxbc_gen_common.h" -#include "dxbc_gen_pixel.h" -#include "dxbc_gen_vertex.h" - -#include "../dxbc_names.h" - -namespace dxvk { - - DxbcCodeGen::DxbcCodeGen(DxbcProgramType shaderStage) - : m_shaderStage(shaderStage) { - m_module.setMemoryModel( - spv::AddressingModelLogical, - spv::MemoryModelGLSL450); - m_entryPointId = m_module.allocateId(); - } - - - DxbcCodeGen::~DxbcCodeGen() { - - } - - - void DxbcCodeGen::dclTemps(uint32_t n) { - const uint32_t oldSize = m_rRegs.size(); - - if (n > oldSize) { - m_rRegs.resize(n); - - for (uint32_t i = oldSize; i < n; i++) { - 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()); - } - } - } - - - void DxbcCodeGen::dclConstantBuffer( - uint32_t bufferId, - uint32_t elementCount) { - // Uniform buffer data is stored as a fixed-size array - // of 4x32-bit vectors. SPIR-V requires explicit strides. - uint32_t arrayType = m_module.defArrayTypeUnique( - this->defValueType(DxbcValueType(DxbcScalarType::Float32, 4)), - m_module.constu32(elementCount)); - m_module.decorateArrayStride(arrayType, 16); - - // SPIR-V requires us to put that array into a - // struct and decorate that struct as a block. - uint32_t structType = m_module.defStructTypeUnique(1, &arrayType); - m_module.memberDecorateOffset(structType, 0, 0); - m_module.decorateBlock(structType); - - // Variable that we'll use to access the buffer - uint32_t varId = m_module.newVar( - m_module.defPointerType(structType, spv::StorageClassUniform), - spv::StorageClassUniform); - - m_module.setDebugName(varId, - str::format("cb", bufferId).c_str()); - - m_constantBuffers.at(bufferId).varId = varId; - m_constantBuffers.at(bufferId).size = elementCount; - - // Compute the DXVK binding slot index for the buffer. - // D3D11 needs to bind the actual buffers to this slot. - uint32_t bindingId = computeResourceSlotId(m_shaderStage, - DxbcBindingType::ConstantBuffer, bufferId); - - m_module.decorateDescriptorSet(varId, 0); - m_module.decorateBinding(varId, bindingId); - - // Store descriptor info for the shader interface - DxvkResourceSlot resource; - resource.slot = bindingId; - resource.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - m_resourceSlots.push_back(resource); - } - - - void DxbcCodeGen::dclResource( - uint32_t registerId, - DxbcResourceDim resourceType, - DxbcResourceReturnType returnType) { - uint32_t sampledTypeId = 0; - - switch (returnType) { - default: Logger::err(str::format("DXBC: Invalid sampled type: ", returnType)); - case DxbcResourceReturnType::Float: sampledTypeId = m_module.defFloatType(32); break; - case DxbcResourceReturnType::Sint: sampledTypeId = m_module.defIntType (32, 1); break; - case DxbcResourceReturnType::Uint: sampledTypeId = m_module.defIntType (32, 0); break; - } - - uint32_t textureTypeId = 0; - - switch (resourceType) { - default: - Logger::err(str::format( - "DXBC: Invalid resource type: ", - resourceType)); - - case DxbcResourceDim::Texture1D: - textureTypeId = m_module.defImageType( - sampledTypeId, spv::Dim1D, 0, 0, 0, 1, - spv::ImageFormatUnknown); - break; - - case DxbcResourceDim::Texture1DArr: - textureTypeId = m_module.defImageType( - sampledTypeId, spv::Dim1D, 0, 1, 0, 1, - spv::ImageFormatUnknown); - break; - - case DxbcResourceDim::Texture2D: - textureTypeId = m_module.defImageType( - sampledTypeId, spv::Dim2D, 0, 0, 0, 1, - spv::ImageFormatUnknown); - break; - - case DxbcResourceDim::Texture2DArr: - textureTypeId = m_module.defImageType( - sampledTypeId, spv::Dim2D, 0, 1, 0, 1, - spv::ImageFormatUnknown); - break; - - case DxbcResourceDim::Texture3D: - textureTypeId = m_module.defImageType( - sampledTypeId, spv::Dim3D, 0, 0, 0, 1, - spv::ImageFormatUnknown); - break; - - case DxbcResourceDim::TextureCube: - textureTypeId = m_module.defImageType( - sampledTypeId, spv::DimCube, 0, 0, 0, 1, - spv::ImageFormatUnknown); - break; - - case DxbcResourceDim::TextureCubeArr: - textureTypeId = m_module.defImageType( - sampledTypeId, spv::DimCube, 0, 1, 0, 1, - spv::ImageFormatUnknown); - break; - } - - uint32_t resourcePtrType = m_module.defPointerType( - textureTypeId, spv::StorageClassUniformConstant); - - uint32_t varId = m_module.newVar(resourcePtrType, - spv::StorageClassUniformConstant); - - m_module.setDebugName(varId, - str::format("t", registerId).c_str()); - - m_textures.at(registerId).varId = varId; - m_textures.at(registerId).sampledTypeId = sampledTypeId; - m_textures.at(registerId).textureTypeId = textureTypeId; - - // Compute the DXVK binding slot index for the resource. - // D3D11 needs to bind the actual resource to this slot. - uint32_t bindingId = computeResourceSlotId(m_shaderStage, - DxbcBindingType::ShaderResource, registerId); - - m_module.decorateDescriptorSet(varId, 0); - m_module.decorateBinding(varId, bindingId); - - // Store descriptor info for the shader interface - DxvkResourceSlot resource; - resource.slot = bindingId; - resource.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - m_resourceSlots.push_back(resource); - } - - - void DxbcCodeGen::dclSampler(uint32_t samplerId) { - // The sampler type is opaque, but we still have to - // define a pointer and a variable in oder to use it - uint32_t samplerType = m_module.defSamplerType(); - uint32_t samplerPtrType = m_module.defPointerType( - samplerType, spv::StorageClassUniformConstant); - - // Define the sampler variable - uint32_t varId = m_module.newVar(samplerPtrType, - spv::StorageClassUniformConstant); - - m_module.setDebugName(varId, - str::format("s", samplerId).c_str()); - - m_samplers.at(samplerId).varId = varId; - m_samplers.at(samplerId).typeId = samplerType; - - // Compute binding slot index for the sampler - uint32_t bindingId = computeResourceSlotId(m_shaderStage, - DxbcBindingType::ImageSampler, samplerId); - - m_module.decorateDescriptorSet(varId, 0); - m_module.decorateBinding(varId, bindingId); - - // Store descriptor info for the shader interface - DxvkResourceSlot resource; - resource.slot = bindingId; - resource.type = VK_DESCRIPTOR_TYPE_SAMPLER; - m_resourceSlots.push_back(resource); - } - - - 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); - } - - - DxbcPointer DxbcCodeGen::ptrConstantBuffer( - uint32_t regId, - const DxbcValue& index) { - // The first index selects the struct member, - // the second one selects the array element. - std::array indices = { - m_module.constu32(0), index.valueId }; - - DxbcPointer result; - result.type = DxbcPointerType( - DxbcValueType(DxbcScalarType::Float32, 4), - spv::StorageClassUniform); - result.valueId = m_module.opAccessChain( - this->defPointerType(result.type), - m_constantBuffers.at(regId).varId, - 2, indices.data()); - return result; - } - - - 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::opAdd(const DxbcValue& a, const DxbcValue& b) { - DxbcValue result; - result.type = a.type; - - switch (result.type.componentType) { - case DxbcScalarType::Sint32: - case DxbcScalarType::Sint64: - case DxbcScalarType::Uint32: - case DxbcScalarType::Uint64: - result.valueId = m_module.opIAdd( - this->defValueType(result.type), - a.valueId, b.valueId); - break; - - case DxbcScalarType::Float32: - case DxbcScalarType::Float64: - result.valueId = m_module.opFAdd( - this->defValueType(result.type), - a.valueId, b.valueId); - break; - } - - return result; - } - - - DxbcValue DxbcCodeGen::opMul(const DxbcValue& a, const DxbcValue& b) { - DxbcValue result; - result.type = a.type; - - switch (result.type.componentType) { - case DxbcScalarType::Sint32: - case DxbcScalarType::Sint64: - case DxbcScalarType::Uint32: - case DxbcScalarType::Uint64: - result.valueId = m_module.opIMul( - this->defValueType(result.type), - a.valueId, b.valueId); - break; - - case DxbcScalarType::Float32: - case DxbcScalarType::Float64: - result.valueId = m_module.opFMul( - this->defValueType(result.type), - a.valueId, b.valueId); - break; - } - - return result; - } - - - DxbcValue DxbcCodeGen::opDot(const DxbcValue& a, const DxbcValue& b) { - DxbcValue result; - result.type = DxbcValueType(a.type.componentType, 1); - result.valueId = m_module.opDot( - this->defValueType(result.type), - a.valueId, b.valueId); - return result; - } - - - DxbcValue DxbcCodeGen::opRsqrt(const DxbcValue& src) { - DxbcValue result; - result.type = src.type; - result.valueId = m_module.opInverseSqrt( - this->defValueType(result.type), - src.valueId); - 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::opSaturate(const DxbcValue& src) { - const uint32_t typeId = this->defValueType(src.type); - - std::array const0; - std::array const1; - - uint32_t const0Id = 0; - uint32_t const1Id = 0; - - if (src.type.componentType == DxbcScalarType::Float32) { - const0Id = m_module.constf32(0.0f); - const1Id = m_module.constf32(1.0f); - } else if (src.type.componentType == DxbcScalarType::Float64) { - const0Id = m_module.constf64(0.0); - const1Id = m_module.constf64(1.0); - } - - for (uint32_t i = 0; i < src.type.componentCount; i++) { - const0.at(i) = const0Id; - const1.at(i) = const1Id; - } - - if (src.type.componentCount > 1) { - const0Id = m_module.constComposite(typeId, src.type.componentCount, const0.data()); - const1Id = m_module.constComposite(typeId, src.type.componentCount, const1.data()); - } - - DxbcValue result; - result.type = src.type; - result.valueId = m_module.opFClamp( - typeId, src.valueId, const0Id, const1Id); - 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) { - return this->regSwizzle(src, - DxbcComponentSwizzle(), mask); - } - - - 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]; - } - - // If the swizzle combined with the mask can be reduced - // to a no-op, we don't need to insert any instructions. - bool isIdentitySwizzle = dstIndex == src.type.componentCount; - - for (uint32_t i = 0; i < dstIndex && isIdentitySwizzle; i++) - isIdentitySwizzle &= indices[i] == i; - - if (isIdentitySwizzle) - return src; - - // Use OpCompositeExtract if the resulting vector contains - // only one component, and OpVectorShuffle if it is a vector. - 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; - - if (dst.type.componentCount == 1) { - // Both values are scalar, so the first component - // of the write mask decides which one to take. - result.valueId = mask.test(0) - ? src.valueId : dst.valueId; - } else if (src.type.componentCount == 1) { - // The source value is scalar. Since OpVectorShuffle - // requires both arguments to be vectors, we have to - // use OpCompositeInsert to modify the vector instead. - const uint32_t componentId = mask.firstComponent(); - - result.valueId = m_module.opCompositeInsert( - this->defValueType(result.type), - src.valueId, dst.valueId, - 1, &componentId); - } else { - // Both arguments are vectors. We can determine which - // components to take from which vector and use the - // OpVectorShuffle instruction. - std::array components; - uint32_t srcComponentId = dst.type.componentCount; - - for (uint32_t i = 0; i < dst.type.componentCount; i++) - components[i] = mask.test(i) ? srcComponentId++ : i; - - result.valueId = m_module.opVectorShuffle( - this->defValueType(result.type), - dst.valueId, src.valueId, - dst.type.componentCount, - components.data()); - } - - return result; - } - - - DxbcValue DxbcCodeGen::regVector( - const DxbcValue& src, - uint32_t size) { - if (size == 1) - return src; - - std::array ids = { - src.valueId, src.valueId, src.valueId, src.valueId, - }; - - DxbcValue result; - result.type = DxbcValueType(src.type.componentType, size); - result.valueId = m_module.opCompositeConstruct( - this->defValueType(result.type), size, ids.data()); - return result; - } - - - DxbcValue DxbcCodeGen::regLoad(const DxbcPointer& ptr) { - DxbcValue result; - result.type = ptr.type.valueType; - result.valueId = m_module.opLoad( - this->defValueType(result.type), - ptr.valueId); - return result; - } - - - void DxbcCodeGen::regStore( - 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->regInsert(tmp, val, mask); - - m_module.opStore(ptr.valueId, tmp.valueId); - } else { - // All destination components get written, so we don't - // need to load and modify the target register first. - m_module.opStore(ptr.valueId, val.valueId); - } - } - - - DxbcValue DxbcCodeGen::texSample( - const uint32_t textureId, - const uint32_t samplerId, - const DxbcValue& coordinates) { - // Combine the texture and the sampler into a sampled image - uint32_t sampledImageType = m_module.defSampledImageType( - m_textures.at(textureId).textureTypeId); - - uint32_t sampledImageId = m_module.opSampledImage( - sampledImageType, - m_module.opLoad( - m_textures.at(textureId).textureTypeId, - m_textures.at(textureId).varId), - m_module.opLoad( - m_samplers.at(samplerId).typeId, - m_samplers.at(samplerId).varId)); - - // Sampling an image in SPIR-V always returns a four-component - // vector, so we need to declare the corresponding type here - // TODO infer sampled type properly - DxbcValue result; - result.type = DxbcValueType(DxbcScalarType::Float32, 4); - result.valueId = m_module.opImageSampleImplicitLod( - this->defValueType(result.type), - sampledImageId, - this->regExtract(coordinates, DxbcComponentMask(true, true, false, false)).valueId); - return result; - } - - - Rc DxbcCodeGen::create( - const DxbcProgramVersion& version, - const Rc& isgn, - const Rc& osgn) { - switch (version.type()) { - case DxbcProgramType::PixelShader: - return new DxbcPsCodeGen(osgn); - - case DxbcProgramType::VertexShader: - return new DxbcVsCodeGen(isgn); - - default: - throw DxvkError(str::format( - "DxbcCodeGen::create: Unsupported program type: ", - version.type())); - } - } - - - uint32_t DxbcCodeGen::defScalarType(DxbcScalarType type) { - switch (type) { - case DxbcScalarType::Uint32 : return m_module.defIntType(32, 0); - case DxbcScalarType::Uint64 : return m_module.defIntType(64, 0); - case DxbcScalarType::Sint32 : return m_module.defIntType(32, 1); - case DxbcScalarType::Sint64 : return m_module.defIntType(64, 1); - case DxbcScalarType::Float32: return m_module.defFloatType(32); - case DxbcScalarType::Float64: return m_module.defFloatType(64); - - default: - throw DxvkError("DxbcCodeGen::defScalarType: Invalid scalar type"); - } - } - - - uint32_t DxbcCodeGen::defValueType(const DxbcValueType& type) { - uint32_t typeId = this->defScalarType(type.componentType); - - if (type.componentCount > 1) - typeId = m_module.defVectorType(typeId, type.componentCount); - - if (type.elementCount > 0) - typeId = m_module.defArrayType(typeId, m_module.constu32(type.elementCount)); - - return typeId; - } - - - uint32_t DxbcCodeGen::defPointerType(const DxbcPointerType& type) { - uint32_t valueTypeId = this->defValueType(type.valueType); - return m_module.defPointerType(valueTypeId, type.storageClass); - } - - - uint32_t DxbcCodeGen::defPerVertexBlock() { - uint32_t s1f32 = this->defScalarType(DxbcScalarType::Float32); - uint32_t v4f32 = this->defValueType(DxbcValueType(DxbcScalarType::Float32, 4, 0)); - uint32_t a2f32 = this->defValueType(DxbcValueType(DxbcScalarType::Float32, 1, 2)); - - std::array members; - members[PerVertex_Position] = v4f32; - members[PerVertex_PointSize] = s1f32; - members[PerVertex_CullDist] = a2f32; - members[PerVertex_ClipDist] = a2f32; - - uint32_t typeId = m_module.defStructTypeUnique( - members.size(), members.data()); - - m_module.memberDecorateBuiltIn(typeId, PerVertex_Position, spv::BuiltInPosition); - m_module.memberDecorateBuiltIn(typeId, PerVertex_PointSize, spv::BuiltInPointSize); - m_module.memberDecorateBuiltIn(typeId, PerVertex_CullDist, spv::BuiltInCullDistance); - m_module.memberDecorateBuiltIn(typeId, PerVertex_ClipDist, spv::BuiltInClipDistance); - m_module.decorateBlock(typeId); - - m_module.setDebugName(typeId, "per_vertex"); - m_module.setDebugMemberName(typeId, PerVertex_Position, "position"); - m_module.setDebugMemberName(typeId, PerVertex_PointSize, "point_size"); - m_module.setDebugMemberName(typeId, PerVertex_CullDist, "cull_dist"); - m_module.setDebugMemberName(typeId, PerVertex_ClipDist, "clip_dist"); - 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 deleted file mode 100644 index 26377a814..000000000 --- a/src/dxbc/gen/dxbc_gen_common.h +++ /dev/null @@ -1,228 +0,0 @@ -#pragma once - -#include "../dxbc_chunk_isgn.h" -#include "../dxbc_common.h" -#include "../dxbc_decoder.h" -#include "../dxbc_type.h" -#include "../dxbc_util.h" - -#include "../../spirv/spirv_module.h" - -namespace dxvk { - - /** - * \brief System value mapping - * - * Maps a system value to a given set of - * components of an input or output register. - */ - struct DxbcSvMapping { - uint32_t regId; - DxbcComponentMask regMask; - DxbcSystemValue sv; - }; - - - /** - * \brief Constant buffer binding - * - * Stores information required to - * access a constant buffer. - */ - struct DxbcConstantBuffer { - uint32_t varId = 0; - uint32_t size = 0; - }; - - - /** - * \brief Sampler binding - * - * Stores a sampler variable. - */ - struct DxbcSampler { - uint32_t varId = 0; - uint32_t typeId = 0; - }; - - - /** - * \brief Shader resource binding - * - * Stores the sampler variable as well as - */ - struct DxbcShaderResource { - uint32_t varId = 0; - uint32_t sampledTypeId = 0; - uint32_t textureTypeId = 0; - }; - - - /** - * \brief DXBC code generator - * - * SPIR-V code generator. Implements simple micro ops that are - * generated when parsing the DXBC shader code. Some of these - * may require different implementations for each shader stage - * and are therefore implemented in a sub class. - */ - class DxbcCodeGen : public RcObject { - - public: - - DxbcCodeGen(DxbcProgramType shaderStage); - - virtual ~DxbcCodeGen(); - - void dclTemps(uint32_t n); - - void dclConstantBuffer( - uint32_t registerId, - uint32_t elementCount); - - void dclResource( - uint32_t registerId, - DxbcResourceDim resourceType, - DxbcResourceReturnType returnType); - - void dclSampler( - uint32_t registerId); - - DxbcValue defConstScalar(uint32_t v); - - DxbcValue defConstVector( - uint32_t x, uint32_t y, - uint32_t z, uint32_t w); - - void fnReturn(); - - DxbcPointer ptrTempReg( - uint32_t regId); - - DxbcPointer ptrConstantBuffer( - uint32_t regId, - const DxbcValue& index); - - DxbcValue opAbs( - const DxbcValue& src); - - DxbcValue opAdd( - const DxbcValue& a, - const DxbcValue& b); - - DxbcValue opMul( - const DxbcValue& a, - const DxbcValue& b); - - DxbcValue opDot( - const DxbcValue& a, - const DxbcValue& b); - - DxbcValue opRsqrt( - const DxbcValue& src); - - DxbcValue opNeg( - const DxbcValue& src); - - DxbcValue opSaturate( - const DxbcValue& src); - - DxbcValue regCast( - const DxbcValue& src, - const DxbcValueType& type); - - DxbcValue regExtract( - const DxbcValue& src, - DxbcComponentMask mask); - - DxbcValue regSwizzle( - const DxbcValue& src, - const DxbcComponentSwizzle& swizzle, - DxbcComponentMask mask); - - DxbcValue regInsert( - const DxbcValue& dst, - const DxbcValue& src, - DxbcComponentMask mask); - - DxbcValue regVector( - const DxbcValue& src, - uint32_t size); - - DxbcValue regLoad( - const DxbcPointer& ptr); - - void regStore( - const DxbcPointer& ptr, - const DxbcValue& val, - DxbcComponentMask mask); - - DxbcValue texSample( - const uint32_t textureId, - const uint32_t samplerId, - const DxbcValue& coordinates); - - virtual void dclInterfaceVar( - DxbcOperandType regType, - uint32_t regId, - uint32_t regDim, - DxbcComponentMask regMask, - DxbcSystemValue sv, - DxbcInterpolationMode im) = 0; - - virtual DxbcPointer ptrInterfaceVar( - DxbcOperandType regType, - uint32_t regId) = 0; - - virtual DxbcPointer ptrInterfaceVarIndexed( - DxbcOperandType regType, - uint32_t regId, - const DxbcValue& index) = 0; - - virtual Rc finalize() = 0; - - static Rc create( - const DxbcProgramVersion& version, - const Rc& isgn, - const Rc& osgn); - - protected: - - constexpr static uint32_t PerVertex_Position = 0; - constexpr static uint32_t PerVertex_PointSize = 1; - constexpr static uint32_t PerVertex_CullDist = 2; - constexpr static uint32_t PerVertex_ClipDist = 3; - - const DxbcProgramType m_shaderStage; - - SpirvModule m_module; - - std::vector m_entryPointInterfaces; - uint32_t m_entryPointId = 0; - - std::vector m_rRegs; - - std::array m_constantBuffers; - std::array m_samplers; - std::array m_textures; - - std::vector m_resourceSlots; - - uint32_t defScalarType( - DxbcScalarType type); - - uint32_t defValueType( - const DxbcValueType& type); - - uint32_t defPointerType( - const DxbcPointerType& type); - - 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_pixel.cpp b/src/dxbc/gen/dxbc_gen_pixel.cpp deleted file mode 100644 index d8411ac07..000000000 --- a/src/dxbc/gen/dxbc_gen_pixel.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include "dxbc_gen_pixel.h" - -namespace dxvk { - - DxbcPsCodeGen::DxbcPsCodeGen(const Rc& osgn) - : DxbcCodeGen(DxbcProgramType::PixelShader) { - 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, "ps_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()); - - // Declare outputs based on the input signature - for (auto e = osgn->begin(); e != osgn->end(); e++) { - if (e->systemValue == DxbcSystemValue::None) { - const DxbcPointer var = this->defVar( - DxbcValueType(e->componentType, e->componentMask.componentCount()), - spv::StorageClassOutput); - - m_psOut.at(e->registerId) = var; - - m_module.decorateLocation(var.valueId, e->registerId); - m_module.setDebugName(var.valueId, - str::format("ps_out", e->registerId).c_str()); - m_entryPointInterfaces.push_back(var.valueId); - } - } - } - - - DxbcPsCodeGen::~DxbcPsCodeGen() { - - } - - - void DxbcPsCodeGen::dclInterfaceVar( - DxbcOperandType regType, - uint32_t regId, - uint32_t regDim, - DxbcComponentMask regMask, - DxbcSystemValue sv, - DxbcInterpolationMode im) { - switch (regType) { - case DxbcOperandType::Input: { - if (m_vRegs.at(regId).valueId == 0) { - const DxbcPointer var = this->defVar( - DxbcValueType(DxbcScalarType::Float32, 4), - spv::StorageClassInput); - - m_vRegs.at(regId) = var; - m_module.decorateLocation(var.valueId, regId); - m_module.setDebugName(var.valueId, - str::format("v", regId).c_str()); - m_entryPointInterfaces.push_back(var.valueId); - - switch (im) { - case DxbcInterpolationMode::Undefined: - case DxbcInterpolationMode::Linear: - break; - - case DxbcInterpolationMode::Constant: - m_module.decorate(var.valueId, spv::DecorationFlat); - break; - - case DxbcInterpolationMode::LinearCentroid: - m_module.decorate(var.valueId, spv::DecorationCentroid); - break; - - case DxbcInterpolationMode::LinearNoPerspective: - m_module.decorate(var.valueId, spv::DecorationNoPerspective); - break; - - case DxbcInterpolationMode::LinearNoPerspectiveCentroid: - m_module.decorate(var.valueId, spv::DecorationNoPerspective); - m_module.decorate(var.valueId, spv::DecorationCentroid); - break; - - case DxbcInterpolationMode::LinearSample: - m_module.decorate(var.valueId, spv::DecorationSample); - break; - - case DxbcInterpolationMode::LinearNoPerspectiveSample: - m_module.decorate(var.valueId, spv::DecorationNoPerspective); - m_module.decorate(var.valueId, spv::DecorationSample); - break; - } - } - } break; - - case DxbcOperandType::Output: { - if (m_oRegs.at(regId).valueId == 0) { - m_oRegs.at(regId) = this->defVar( - DxbcValueType(DxbcScalarType::Float32, 4), - spv::StorageClassPrivate); - m_module.setDebugName(m_oRegs.at(regId).valueId, - str::format("o", regId).c_str()); - } - } break; - - default: - throw DxvkError(str::format( - "DxbcPsCodeGen::dclInterfaceVar: Unhandled operand type: ", - regType)); - } - } - - - DxbcPointer DxbcPsCodeGen::ptrInterfaceVar( - DxbcOperandType regType, - uint32_t regId) { - switch (regType) { - case DxbcOperandType::Input: - return m_vRegs.at(regId); - - case DxbcOperandType::Output: - return m_oRegs.at(regId); - - default: - throw DxvkError(str::format( - "DxbcPsCodeGen::ptrInterfaceVar: Unhandled operand type: ", - regType)); - } - } - - - DxbcPointer DxbcPsCodeGen::ptrInterfaceVarIndexed( - DxbcOperandType regType, - uint32_t regId, - const DxbcValue& index) { - throw DxvkError(str::format( - "DxbcPsCodeGen::ptrInterfaceVarIndexed:\n", - "Pixel shaders do not support indexed interface variables")); - } - - - Rc DxbcPsCodeGen::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::ExecutionModelFragment, "main", - m_entryPointInterfaces.size(), - m_entryPointInterfaces.data()); - m_module.setOriginUpperLeft(m_entryPointId); - m_module.setDebugName(m_entryPointId, "main"); - - return new DxvkShader( - VK_SHADER_STAGE_FRAGMENT_BIT, - m_resourceSlots.size(), - m_resourceSlots.data(), - m_module.compile()); - } - - - void DxbcPsCodeGen::dclSvInputReg(DxbcSystemValue sv) { - switch (sv) { - case DxbcSystemValue::Position: { - m_svPosition = this->defVar( - DxbcValueType(DxbcScalarType::Float32, 4), - spv::StorageClassInput); - m_entryPointInterfaces.push_back( - m_svPosition.valueId); - - m_module.setDebugName(m_svPosition.valueId, "sv_position"); - m_module.decorateBuiltIn(m_svPosition.valueId, spv::BuiltInFragCoord); - } break; - - default: - throw DxvkError(str::format( - "DxbcPsCodeGen::dclSvInputReg: Unhandled SV: ", sv)); - } - } - - - void DxbcPsCodeGen::prepareSvInputs() { - - } - - - void DxbcPsCodeGen::prepareSvOutputs() { - // TODO properly re-implement this - std::array masks = { 0x0, 0x1, 0x3, 0x7, 0xF }; - - for (uint32_t i = 0; i < m_psOut.size(); i++) { - if ((m_psOut.at(i).valueId != 0) && (m_oRegs.at(i).valueId != 0)) { - DxbcValue srcValue = this->regLoad(m_oRegs.at(i)); - srcValue = this->regCast(srcValue, m_psOut.at(i).type.valueType); - this->regStore(m_psOut.at(i), srcValue, DxbcComponentMask( - masks.at(m_psOut.at(i).type.valueType.componentCount))); - } - } - } - -} \ No newline at end of file diff --git a/src/dxbc/gen/dxbc_gen_pixel.h b/src/dxbc/gen/dxbc_gen_pixel.h deleted file mode 100644 index 70156ab65..000000000 --- a/src/dxbc/gen/dxbc_gen_pixel.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "dxbc_gen_common.h" - -namespace dxvk { - - /** - * \brief Pixel shader code generator - */ - class DxbcPsCodeGen : public DxbcCodeGen { - - public: - - DxbcPsCodeGen( - const Rc& osgn); - ~DxbcPsCodeGen(); - - void dclInterfaceVar( - DxbcOperandType regType, - uint32_t regId, - uint32_t regDim, - DxbcComponentMask regMask, - DxbcSystemValue sv, - DxbcInterpolationMode im); - - DxbcPointer ptrInterfaceVar( - DxbcOperandType regType, - uint32_t regId); - - DxbcPointer ptrInterfaceVarIndexed( - DxbcOperandType regType, - uint32_t regId, - const DxbcValue& index); - - Rc finalize() final; - - private: - - uint32_t m_function = 0; - uint32_t m_psIn = 0; - - DxbcPointer m_svPosition; - - std::array m_vRegs; - std::array m_oRegs; - std::array m_psOut; - - void dclSvInputReg(DxbcSystemValue sv); - - void prepareSvInputs(); - void prepareSvOutputs(); - - }; - -} \ No newline at end of file diff --git a/src/dxbc/gen/dxbc_gen_vertex.cpp b/src/dxbc/gen/dxbc_gen_vertex.cpp deleted file mode 100644 index 5d0fbcd8f..000000000 --- a/src/dxbc/gen/dxbc_gen_vertex.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include "dxbc_gen_vertex.h" - -namespace dxvk { - - DxbcVsCodeGen::DxbcVsCodeGen(const Rc& isgn) - : DxbcCodeGen(DxbcProgramType::VertexShader) { - 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()); - - // Declare per-vertex builtin output block - m_vsPerVertex = m_module.newVar( - m_module.defPointerType(this->defPerVertexBlock(), spv::StorageClassOutput), - spv::StorageClassOutput); - m_entryPointInterfaces.push_back(m_vsPerVertex); - m_module.setDebugName(m_vsPerVertex, "vs_per_vertex"); - } - - - DxbcVsCodeGen::~DxbcVsCodeGen() { - - } - - - void DxbcVsCodeGen::dclInterfaceVar( - DxbcOperandType regType, - uint32_t regId, - uint32_t regDim, - DxbcComponentMask regMask, - DxbcSystemValue sv, - DxbcInterpolationMode im) { - switch (regType) { - case DxbcOperandType::Input: { - if (m_vRegs.at(regId).valueId == 0) { - m_vRegs.at(regId) = this->defVar( - DxbcValueType(DxbcScalarType::Float32, 4), - spv::StorageClassInput); - m_module.decorateLocation(m_vRegs.at(regId).valueId, regId); - m_module.setDebugName(m_vRegs.at(regId).valueId, - str::format("v", regId).c_str()); - m_entryPointInterfaces.push_back(m_vRegs.at(regId).valueId); - } - - if (sv != DxbcSystemValue::None) { - m_svIn.push_back(DxbcSvMapping { - regId, regMask, sv }); - } - } 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.decorateLocation(m_oRegs.at(regId).valueId, regId); - m_module.setDebugName(m_oRegs.at(regId).valueId, - str::format("o", regId).c_str()); - m_entryPointInterfaces.push_back(m_oRegs.at(regId).valueId); - } - - if (sv != DxbcSystemValue::None) { - m_svOut.push_back(DxbcSvMapping { - regId, regMask, sv }); - } - } break; - - default: - throw DxvkError(str::format( - "DxbcVsCodeGen::dclInterfaceVar: Unhandled operand type: ", - regType)); - } - } - - - DxbcPointer DxbcVsCodeGen::ptrInterfaceVar( - DxbcOperandType regType, - uint32_t regId) { - switch (regType) { - case DxbcOperandType::Input: - return m_vRegs.at(regId); - - case DxbcOperandType::Output: - return m_oRegs.at(regId); - - default: - throw DxvkError(str::format( - "DxbcVsCodeGen::ptrInterfaceVar: Unhandled operand type: ", - regType)); - } - } - - - DxbcPointer DxbcVsCodeGen::ptrInterfaceVarIndexed( - DxbcOperandType regType, - uint32_t regId, - const DxbcValue& index) { - throw DxvkError(str::format( - "DxbcVsCodeGen::ptrInterfaceVarIndexed:\n", - "Vertex shaders do not support indexed interface variables")); - } - - - 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(), - m_entryPointInterfaces.data()); - m_module.setDebugName(m_entryPointId, "main"); - - return new DxvkShader( - VK_SHADER_STAGE_VERTEX_BIT, - m_resourceSlots.size(), - m_resourceSlots.data(), - m_module.compile()); - } - - - void DxbcVsCodeGen::dclSvInputReg(DxbcSystemValue sv) { - - } - - - void DxbcVsCodeGen::prepareSvInputs() { - DxbcValueType targetType(DxbcScalarType::Float32, 4); - - // TODO system values - } - - - void DxbcVsCodeGen::prepareSvOutputs() { - for (const auto& mapping : m_svOut) { - DxbcValue srcValue = this->regLoad(m_oRegs.at(mapping.regId)); - - switch (mapping.sv) { - case DxbcSystemValue::Position: { - this->regStore(this->ptrBuiltInPosition(), srcValue, - DxbcComponentMask(true, true, true, true)); - } break; - default: ; - } - } - } - - - 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_vsPerVertex, 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 deleted file mode 100644 index 62338384e..000000000 --- a/src/dxbc/gen/dxbc_gen_vertex.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include "dxbc_gen_common.h" - -namespace dxvk { - - /** - * \brief Vertex shader code generator - */ - class DxbcVsCodeGen : public DxbcCodeGen { - - public: - - DxbcVsCodeGen( - const Rc& isgn); - ~DxbcVsCodeGen(); - - void dclInterfaceVar( - DxbcOperandType regType, - uint32_t regId, - uint32_t regDim, - DxbcComponentMask regMask, - DxbcSystemValue sv, - DxbcInterpolationMode im); - - DxbcPointer ptrInterfaceVar( - DxbcOperandType regType, - uint32_t regId); - - DxbcPointer ptrInterfaceVarIndexed( - DxbcOperandType regType, - uint32_t regId, - const DxbcValue& index); - - Rc finalize() final; - - private: - - uint32_t m_function = 0; - uint32_t m_vsPerVertex = 0; - - std::array m_vRegs; - std::array m_oRegs; - - std::vector m_svIn; - std::vector m_svOut; - - void dclSvInputReg(DxbcSystemValue sv); - - void prepareSvInputs(); - void prepareSvOutputs(); - - DxbcPointer ptrBuiltInPosition(); - - DxbcPointer getVsOutPtr(uint32_t id); - - }; - -} \ No newline at end of file diff --git a/src/dxbc/meson.build b/src/dxbc/meson.build index 3fb7dda84..91f124950 100644 --- a/src/dxbc/meson.build +++ b/src/dxbc/meson.build @@ -3,6 +3,7 @@ dxbc_src = files([ 'dxbc_chunk_shex.cpp', 'dxbc_common.cpp', 'dxbc_compiler.cpp', + 'dxbc_defs.cpp', 'dxbc_decoder.cpp', 'dxbc_header.cpp', 'dxbc_module.cpp', @@ -10,10 +11,6 @@ dxbc_src = files([ 'dxbc_reader.cpp', 'dxbc_type.cpp', 'dxbc_util.cpp', - - 'gen/dxbc_gen_common.cpp', - 'gen/dxbc_gen_pixel.cpp', - 'gen/dxbc_gen_vertex.cpp', ]) dxbc_lib = static_library('dxbc', dxbc_src, diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp index a03f98ab0..4a10e807f 100644 --- a/src/spirv/spirv_module.cpp +++ b/src/spirv/spirv_module.cpp @@ -768,6 +768,25 @@ namespace dxvk { } + uint32_t SpirvModule::opFFma( + uint32_t resultType, + uint32_t a, + uint32_t b, + uint32_t c) { + 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::GLSLstd450Fma); + m_code.putWord(a); + m_code.putWord(b); + m_code.putWord(c); + return resultId; + } + + uint32_t SpirvModule::opFClamp( uint32_t resultType, uint32_t x, diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h index 02bb07508..0d83c5a7a 100644 --- a/src/spirv/spirv_module.h +++ b/src/spirv/spirv_module.h @@ -275,6 +275,12 @@ namespace dxvk { uint32_t a, uint32_t b); + uint32_t opFFma( + uint32_t resultType, + uint32_t a, + uint32_t b, + uint32_t c); + uint32_t opFClamp( uint32_t resultType, uint32_t x,