mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-01-29 17:52:18 +01:00
[dxbc] Shader compiler rewrite (1/2)
Rewrote most parts of the shader compiler and removed the old one. The next step is to improve documentation and remove the remaining traces of the old shader compiler.
This commit is contained in:
parent
a0db9198e3
commit
464a3e7d4e
@ -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<DxbcIsgn>& isgn,
|
||||
const Rc<DxbcIsgn>& 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<DxvkShader> 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<DxvkShader> 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<DxbcSystemValue>(
|
||||
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<DxbcInterpolationMode>(
|
||||
// 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<DxbcResourceDim>(
|
||||
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<DxbcResourceReturnType>(
|
||||
bit::extract(ins.arg(ofs), 0, 3));
|
||||
bit::extract(ins.operands[1].immediates[0], 0, 3));
|
||||
auto yType = static_cast<DxbcResourceReturnType>(
|
||||
bit::extract(ins.arg(ofs), 4, 7));
|
||||
bit::extract(ins.operands[1].immediates[0], 4, 7));
|
||||
auto zType = static_cast<DxbcResourceReturnType>(
|
||||
bit::extract(ins.arg(ofs), 8, 11));
|
||||
bit::extract(ins.operands[1].immediates[0], 8, 11));
|
||||
auto wType = static_cast<DxbcResourceReturnType>(
|
||||
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<DxbcSystemValue>(op.length());
|
||||
|
||||
const bool hasInterpolationMode =
|
||||
opcode == DxbcOpcode::DclInputPs
|
||||
|| opcode == DxbcOpcode::DclInputPsSiv;
|
||||
|
||||
DxbcInterpolationMode im = DxbcInterpolationMode::Undefined;
|
||||
|
||||
if (hasInterpolationMode) {
|
||||
im = static_cast<DxbcInterpolationMode>(
|
||||
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<uint32_t, 4> 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<uint32_t, 4> 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<int32_t>(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<uint32_t, 2> 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<DxbcIsgn>& 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<DxbcIsgn>& 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<uint32_t, 4> 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<DxbcPointer2, DxbcMaxInterfaceRegs> 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<DxbcIsgn>& isgn,
|
||||
const Rc<DxbcIsgn>& 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<DxvkShader> finalize();
|
||||
|
||||
private:
|
||||
|
||||
Rc<DxbcCodeGen> m_gen;
|
||||
DxbcProgramVersion m_version;
|
||||
SpirvModule m_module;
|
||||
|
||||
void dclGlobalFlags(
|
||||
const DxbcInstruction& ins);
|
||||
Rc<DxbcIsgn> m_isgn;
|
||||
Rc<DxbcIsgn> 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<DxvkResourceSlot> m_resourceSlots;
|
||||
|
||||
void dclResource(
|
||||
const DxbcInstruction& ins);
|
||||
///////////////////////////////
|
||||
// r# registers of type float4
|
||||
std::vector<uint32_t> 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<uint32_t, DxbcMaxInterfaceRegs> 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<uint32_t, DxbcMaxInterfaceRegs> m_oRegs;
|
||||
|
||||
void dclTemps(
|
||||
const DxbcInstruction& ins);
|
||||
//////////////////////////////////////////////////////
|
||||
// Shader resource variables. These provide access to
|
||||
// constant buffers, samplers, textures, and UAVs.
|
||||
std::array<DxbcConstantBuffer2, 16> m_constantBuffers;
|
||||
std::array<DxbcSampler2, 16> m_samplers;
|
||||
std::array<DxbcShaderResource2, 128> 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<DxbcSvMapping2> m_vSvs;
|
||||
std::vector<DxbcSvMapping2> 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<uint32_t> 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<DxbcIsgn>& isgn);
|
||||
void beginPixelShader (const Rc<DxbcIsgn>& 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);
|
||||
|
||||
};
|
||||
|
||||
|
@ -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<DxbcResourceDim>(
|
||||
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<uint32_t, 3> count = { 0, 1, 4 };
|
||||
std::array<uint32_t, 4> 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
|
||||
*
|
||||
|
500
src/dxbc/dxbc_defs.cpp
Normal file
500
src/dxbc/dxbc_defs.cpp
Normal file
@ -0,0 +1,500 @@
|
||||
#include "dxbc_defs.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
const std::array<DxbcInstFormat, 207> 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<uint32_t>(opcode);
|
||||
|
||||
return (idx < g_instructionFormats.size())
|
||||
? g_instructionFormats.at(idx)
|
||||
: DxbcInstFormat();
|
||||
}
|
||||
|
||||
}
|
40
src/dxbc/dxbc_defs.h
Normal file
40
src/dxbc/dxbc_defs.h
Normal file
@ -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);
|
||||
|
||||
}
|
@ -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<uint32_t>(error)));
|
||||
}
|
||||
}
|
||||
|
||||
return compiler.finalize();
|
||||
}
|
||||
|
@ -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<uint32_t, 4> 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<uint32_t, 2> 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<uint32_t, 4> const0;
|
||||
std::array<uint32_t, 4> 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<uint32_t, 4> 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<uint32_t, 4> 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<uint32_t, 4> 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> DxbcCodeGen::create(
|
||||
const DxbcProgramVersion& version,
|
||||
const Rc<DxbcIsgn>& isgn,
|
||||
const Rc<DxbcIsgn>& 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<uint32_t, 4> 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<DxvkShader> finalize() = 0;
|
||||
|
||||
static Rc<DxbcCodeGen> create(
|
||||
const DxbcProgramVersion& version,
|
||||
const Rc<DxbcIsgn>& isgn,
|
||||
const Rc<DxbcIsgn>& 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<uint32_t> m_entryPointInterfaces;
|
||||
uint32_t m_entryPointId = 0;
|
||||
|
||||
std::vector<DxbcPointer> m_rRegs;
|
||||
|
||||
std::array<DxbcConstantBuffer, 16> m_constantBuffers;
|
||||
std::array<DxbcSampler, 16> m_samplers;
|
||||
std::array<DxbcShaderResource, 128> m_textures;
|
||||
|
||||
std::vector<DxvkResourceSlot> 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);
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,217 +0,0 @@
|
||||
#include "dxbc_gen_pixel.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
DxbcPsCodeGen::DxbcPsCodeGen(const Rc<DxbcIsgn>& 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<DxvkShader> 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<uint32_t, 5> 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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<DxbcIsgn>& 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<DxvkShader> finalize() final;
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_function = 0;
|
||||
uint32_t m_psIn = 0;
|
||||
|
||||
DxbcPointer m_svPosition;
|
||||
|
||||
std::array<DxbcPointer, 32> m_vRegs;
|
||||
std::array<DxbcPointer, 8> m_oRegs;
|
||||
std::array<DxbcPointer, 8> m_psOut;
|
||||
|
||||
void dclSvInputReg(DxbcSystemValue sv);
|
||||
|
||||
void prepareSvInputs();
|
||||
void prepareSvOutputs();
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
#include "dxbc_gen_vertex.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
DxbcVsCodeGen::DxbcVsCodeGen(const Rc<DxbcIsgn>& 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<DxvkShader> 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<DxbcIsgn>& 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<DxvkShader> finalize() final;
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_function = 0;
|
||||
uint32_t m_vsPerVertex = 0;
|
||||
|
||||
std::array<DxbcPointer, 32> m_vRegs;
|
||||
std::array<DxbcPointer, 32> m_oRegs;
|
||||
|
||||
std::vector<DxbcSvMapping> m_svIn;
|
||||
std::vector<DxbcSvMapping> m_svOut;
|
||||
|
||||
void dclSvInputReg(DxbcSystemValue sv);
|
||||
|
||||
void prepareSvInputs();
|
||||
void prepareSvOutputs();
|
||||
|
||||
DxbcPointer ptrBuiltInPosition();
|
||||
|
||||
DxbcPointer getVsOutPtr(uint32_t id);
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user