1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-01-07 07:46:19 +01:00
dxvk/src/dxbc/dxbc_compiler.cpp

1703 lines
56 KiB
C++
Raw Normal View History

#include "dxbc_compiler.h"
#include "dxbc_names.h"
#include "dxbc_util.h"
namespace dxvk {
constexpr uint32_t PerVertex_Position = 0;
constexpr uint32_t PerVertex_PointSize = 1;
constexpr uint32_t PerVertex_CullDist = 2;
constexpr uint32_t PerVertex_ClipDist = 3;
2017-12-14 12:53:53 +01:00
DxbcCompiler::DxbcCompiler(
const DxbcProgramVersion& version,
const Rc<DxbcIsgn>& isgn,
const Rc<DxbcIsgn>& 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");
}
}
2017-12-14 12:53:53 +01:00
DxbcCompiler::~DxbcCompiler() {
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::processInstruction(const DxbcInstruction& ins) {
DxbcInst parsedInst;
DxbcError parseError = this->parseInstruction(ins, parsedInst);
if (parseError != DxbcError::sOk)
return parseError;
2017-10-22 23:13:29 +02:00
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::VectorCmov: return this->handleVectorCmov (parsedInst);
case DxbcInstClass::VectorCmp: return this->handleVectorCmp (parsedInst);
case DxbcInstClass::VectorDot: return this->handleVectorDot (parsedInst);
case DxbcInstClass::VectorSinCos: return this->handleVectorSinCos (parsedInst);
default: return DxbcError::eUnhandledOpcode;
}
}
2017-12-14 12:53:53 +01:00
Rc<DxvkShader> DxbcCompiler::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());
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::handleDeclaration(const DxbcInst& ins) {
switch (ins.opcode) {
case DxbcOpcode::DclGlobalFlags:
return this->declareGlobalFlags(ins);
2017-12-10 03:39:35 +01:00
case DxbcOpcode::DclTemps:
return this->declareTemps(ins);
case DxbcOpcode::DclInput:
case DxbcOpcode::DclInputSiv:
case DxbcOpcode::DclInputSgv:
case DxbcOpcode::DclInputPs:
case DxbcOpcode::DclInputPsSiv:
case DxbcOpcode::DclInputPsSgv:
case DxbcOpcode::DclOutput:
case DxbcOpcode::DclOutputSiv:
case DxbcOpcode::DclOutputSgv:
return this->declareInterfaceVar(ins);
case DxbcOpcode::DclConstantBuffer:
return this->declareConstantBuffer(ins);
2017-12-11 14:36:35 +01:00
case DxbcOpcode::DclSampler:
return this->declareSampler(ins);
case DxbcOpcode::DclResource:
return this->declareResource(ins);
default:
return DxbcError::eUnhandledOpcode;
}
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::declareGlobalFlags(const DxbcInst& ins) {
// TODO add support for double-precision floats
// TODO add support for early depth-stencil
return DxbcError::sOk;
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::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);
2017-12-08 17:08:26 +01:00
// r# registers are always 4x32-bit float vectors
const uint32_t regTypeId = this->defineVectorType(
DxbcScalarType::Float32, 4);
2017-12-08 17:08:26 +01:00
const uint32_t ptrTypeId = m_module.defPointerType(
regTypeId, spv::StorageClassPrivate);
2017-12-08 17:08:26 +01:00
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;
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::declareInterfaceVar(const DxbcInst& ins) {
const DxbcInstOp& op = ins.operands[0];
// 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;
if (op.indexDim == 1) {
if (op.index[0].type != DxbcIndexType::Immediate)
return DxbcError::eInvalidOperandIndex;
2017-12-10 20:01:38 +01:00
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;
}
// 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;
}
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::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;
2017-12-08 17:08:26 +01:00
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::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);
2017-12-08 17:08:26 +01:00
m_module.setDebugName(varId,
str::format("s", samplerId).c_str());
2017-12-08 17:08:26 +01:00
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;
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::declareResource(const DxbcInst& ins) {
// dclResource takes two operands:
// (1) The resource register ID
// (2) The resource return type
const DxbcInstOp op = ins.operands[0];
if (op.indexDim != 1) {
Logger::err("dxbc: dclResource: Invalid index dimension");
return DxbcError::eInvalidOperandIndex;
}
const uint32_t registerId = op.index[0].immediate;
// Defines the type of the resource (texture2D, ...)
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.
auto xType = static_cast<DxbcResourceReturnType>(
bit::extract(ins.operands[1].immediates[0], 0, 3));
auto yType = static_cast<DxbcResourceReturnType>(
bit::extract(ins.operands[1].immediates[0], 4, 7));
auto zType = static_cast<DxbcResourceReturnType>(
bit::extract(ins.operands[1].immediates[0], 8, 11));
auto wType = static_cast<DxbcResourceReturnType>(
bit::extract(ins.operands[1].immediates[0], 12, 15));
if ((xType != yType) || (xType != zType) || (xType != wType))
Logger::warn("DXBC: dclResource: Ignoring resource return types");
// 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;
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::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;
}
2017-12-10 03:39:35 +01:00
// 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;
}
2017-12-10 03:39:35 +01:00
return DxbcError::sOk;
2017-12-10 03:39:35 +01:00
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::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;
}
// 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;
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::handleControlFlow(const DxbcInst& ins) {
switch (ins.opcode) {
case DxbcOpcode::Ret:
m_module.opReturn();
m_module.functionEnd();
return DxbcError::sOk;
default:
return DxbcError::eUnhandledOpcode;
}
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::handleTextureSample(const DxbcInst& ins) {
// TODO support address offset
// TODO support more sample ops
// 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];
if (textureOp.indexDim != 1 || samplerOp.indexDim != 1) {
Logger::err("dxbc: Texture and Sampler registers require one index");
return DxbcError::eInvalidOperandIndex;
}
// 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.
2017-12-14 12:53:53 +01:00
const DxbcValue coord = this->loadOp(coordOp,
DxbcRegMask(true, true, true, true),
DxbcScalarType::Float32);
// 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
2017-12-14 12:53:53 +01:00
DxbcValue 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;
2017-12-11 14:36:35 +01:00
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::handleVectorAlu(const DxbcInst& ins) {
// Load input operands. Operands that are floating
// point types will be affected by modifiers.
2017-12-14 12:53:53 +01:00
DxbcValue arguments[DxbcMaxOperandCount - 1];
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);
}
// Result that we will write to the destination operand
2017-12-14 12:53:53 +01:00
DxbcValue result;
result.componentType = arguments[0].componentType;
result.componentCount = arguments[0].componentCount;
uint32_t resultTypeId = this->defineVectorType(
result.componentType, result.componentCount);
switch (ins.opcode) {
case DxbcOpcode::Add:
result.valueId = m_module.opFAdd(
resultTypeId,
arguments[0].valueId,
arguments[1].valueId);
break;
case DxbcOpcode::Div:
result.valueId = m_module.opFDiv(
resultTypeId,
arguments[0].valueId,
arguments[1].valueId);
break;
case DxbcOpcode::Mad:
result.valueId = m_module.opFFma(
resultTypeId,
arguments[0].valueId,
arguments[1].valueId,
arguments[2].valueId);
break;
case DxbcOpcode::Max:
result.valueId = m_module.opFMax(
resultTypeId,
arguments[0].valueId,
arguments[1].valueId);
break;
case DxbcOpcode::Min:
result.valueId = m_module.opFMin(
resultTypeId,
arguments[0].valueId,
arguments[1].valueId);
break;
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:
return DxbcError::eUnhandledOpcode;
}
2017-12-11 14:36:35 +01:00
// Apply result modifiers to floating-point results
result = this->applyResultModifiers(result, ins.control);
this->storeOp(ins.operands[0], result);
return DxbcError::sOk;
}
DxbcError DxbcCompiler::handleVectorCmov(const DxbcInst& ins) {
// movc has four operands:
// (1) The destination register
// (2) The condition vector
// (3) Vector to select from if the condition is not 0
// (4) Vector to select from if the condition is 0
const DxbcValue condition = this->loadOp(ins.operands[1], ins.operands[0].mask, ins.format.operands[1].type);
const DxbcValue selectTrue = this->loadOp(ins.operands[2], ins.operands[0].mask, ins.format.operands[2].type);
const DxbcValue selectFalse = this->loadOp(ins.operands[3], ins.operands[0].mask, ins.format.operands[3].type);
const uint32_t componentCount = ins.operands[0].mask.setCount();
// We'll compare against a vector of zeroes to generate a
// boolean vector, which in turn will be used by OpSelect
uint32_t zeroType = m_module.defIntType(32, 0);
uint32_t boolType = m_module.defBoolType();
uint32_t zero = m_module.constu32(0);
if (componentCount > 1) {
zeroType = m_module.defVectorType(zeroType, componentCount);
boolType = m_module.defVectorType(boolType, componentCount);
const std::array<uint32_t, 4> zeroVec = { zero, zero, zero, zero };
zero = m_module.constComposite(zeroType, componentCount, zeroVec.data());
}
// Use the component mask to select the vector components
DxbcValue result;
result.componentType = ins.format.operands[0].type;
result.componentCount = componentCount;
result.valueId = m_module.opSelect(
this->defineVectorType(result.componentType, result.componentCount),
m_module.opINotEqual(boolType, condition.valueId, zero),
selectTrue.valueId, selectFalse.valueId);
// Apply result modifiers to floating-point results
result = this->applyResultModifiers(result, ins.control);
this->storeOp(ins.operands[0], result);
return DxbcError::sOk;
}
DxbcError DxbcCompiler::handleVectorCmp(const DxbcInst& ins) {
// Compare instructions have three operands:
// (1) The destination register
// (2) The first vector to compare
// (3) The second vector to compare
DxbcValue arguments[2];
if (ins.format.operandCount != 3)
return DxbcError::eInvalidOperand;
for (uint32_t i = 0; i < 2; i++) {
arguments[i] = this->loadOp(
ins.operands[i + 1],
ins.operands[0].mask,
ins.format.operands[i + 1].type);
}
const uint32_t componentCount = ins.operands[0].mask.setCount();
// Condition, which is a boolean vector used
// to select between the ~0u and 0u vectors.
uint32_t condition = 0;
uint32_t conditionType = m_module.defBoolType();
if (componentCount > 1)
conditionType = m_module.defVectorType(conditionType, componentCount);
switch (ins.opcode) {
case DxbcOpcode::Eq:
condition = m_module.opFOrdEqual(
conditionType,
arguments[0].valueId,
arguments[1].valueId);
break;
case DxbcOpcode::Ge:
condition = m_module.opFOrdGreaterThanEqual(
conditionType,
arguments[0].valueId,
arguments[1].valueId);
break;
case DxbcOpcode::Lt:
condition = m_module.opFOrdLessThan(
conditionType,
arguments[0].valueId,
arguments[1].valueId);
break;
case DxbcOpcode::Ne:
condition = m_module.opFOrdNotEqual(
conditionType,
arguments[0].valueId,
arguments[1].valueId);
break;
default:
return DxbcError::eUnhandledOpcode;
}
// Generate constant vectors for selection
uint32_t sFalse = m_module.constu32( 0u);
uint32_t sTrue = m_module.constu32(~0u);
const uint32_t maskType = this->defineVectorType(
DxbcScalarType::Uint32, componentCount);
if (componentCount > 1) {
const std::array<uint32_t, 4> vFalse = { sFalse, sFalse, sFalse, sFalse };
const std::array<uint32_t, 4> vTrue = { sTrue, sTrue, sTrue, sTrue };
sFalse = m_module.constComposite(maskType, componentCount, vFalse.data());
sTrue = m_module.constComposite(maskType, componentCount, vTrue .data());
}
// Perform component-wise mask selection
// based on the condition evaluated above.
DxbcValue result;
result.componentType = DxbcScalarType::Uint32;
result.componentCount = componentCount;
result.valueId = m_module.opSelect(
maskType, condition, sTrue, sFalse);
this->storeOp(ins.operands[0], result);
return DxbcError::sOk;
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::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
2017-12-14 12:53:53 +01:00
DxbcValue arguments[2];
for (uint32_t i = 1; i <= 2; i++) {
arguments[i - 1] = this->loadOp(
ins.operands[i], srcMask,
DxbcScalarType::Float32);
}
2017-12-14 12:53:53 +01:00
DxbcValue 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;
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::handleVectorSinCos(const DxbcInst& ins) {
// sincos has three operands:
// (1) Destination register for sin(x)
// (2) Destination register for cos(x)
// (3) Source operand x
const DxbcInstOp dstSinOp = ins.operands[0];
const DxbcInstOp dstCosOp = ins.operands[1];
const DxbcInstOp srcOp = ins.operands[2];
// Load source operand as 32-bit float vector
2017-12-14 12:53:53 +01:00
const DxbcValue srcValue = this->loadOp(srcOp,
DxbcRegMask(true, true, true, true),
DxbcScalarType::Float32);
// Either output may be DxbcOperandType::Null, in
// which case we don't have to generate any code
if (dstSinOp.type != DxbcOperandType::Null) {
2017-12-14 12:53:53 +01:00
const DxbcValue sinInput = this->extractReg(
srcValue, dstSinOp.mask);
2017-12-14 12:53:53 +01:00
DxbcValue sinValue;
sinValue.componentType = srcValue.componentType;
sinValue.componentCount = srcValue.componentCount;
sinValue.valueId = m_module.opSin(
this->defineVectorType(
sinInput.componentType,
sinInput.componentCount),
sinInput.valueId);
this->storeOp(dstSinOp, sinValue);
}
if (dstCosOp.type != DxbcOperandType::Null) {
2017-12-14 12:53:53 +01:00
const DxbcValue cosInput = this->extractReg(
srcValue, dstCosOp.mask);
2017-12-14 12:53:53 +01:00
DxbcValue cosValue;
cosValue.componentType = srcValue.componentType;
cosValue.componentCount = srcValue.componentCount;
cosValue.valueId = m_module.opCos(
this->defineVectorType(
cosInput.componentType,
cosInput.componentCount),
cosInput.valueId);
this->storeOp(dstCosOp, cosValue);
}
return DxbcError::sOk;
}
2017-12-14 12:53:53 +01:00
DxbcValue DxbcCompiler::bitcastReg(
const DxbcValue& src,
DxbcScalarType type) {
if (src.componentType == type)
return src;
2017-12-08 17:08:26 +01:00
// TODO support 64-bit types by adjusting the component count
uint32_t typeId = this->defineVectorType(type, src.componentCount);
2017-12-08 17:08:26 +01:00
2017-12-14 12:53:53 +01:00
DxbcValue result;
result.componentType = type;
result.componentCount = src.componentCount;
result.valueId = m_module.opBitcast(typeId, src.valueId);
return result;
2017-12-08 17:08:26 +01:00
}
2017-12-14 12:53:53 +01:00
DxbcValue DxbcCompiler::insertReg(
const DxbcValue& dst,
const DxbcValue& src,
DxbcRegMask mask) {
2017-12-14 12:53:53 +01:00
DxbcValue result;
result.componentType = dst.componentType;
result.componentCount = dst.componentCount;
2017-12-10 20:01:38 +01:00
const uint32_t resultTypeId = this->defineVectorType(
result.componentType, result.componentCount);
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.valueId = m_module.opCompositeInsert(
resultTypeId, 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.
uint32_t components[4];
uint32_t srcComponentId = dst.componentCount;
for (uint32_t i = 0; i < dst.componentCount; i++)
components[i] = mask[i] ? srcComponentId++ : i;
result.valueId = m_module.opVectorShuffle(
resultTypeId, dst.valueId, src.valueId,
dst.componentCount, components);
}
return result;
}
2017-12-14 12:53:53 +01:00
DxbcValue DxbcCompiler::extractReg(
const DxbcValue& src,
DxbcRegMask mask) {
return this->swizzleReg(src,
DxbcRegSwizzle(0, 1, 2, 3), mask);
}
2017-12-14 12:53:53 +01:00
DxbcValue DxbcCompiler::swizzleReg(
const DxbcValue& src,
const DxbcRegSwizzle& swizzle,
DxbcRegMask mask) {
std::array<uint32_t, 4> indices;
uint32_t dstIndex = 0;
for (uint32_t i = 0; i < src.componentCount; i++) {
if (mask[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.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.
2017-12-14 12:53:53 +01:00
DxbcValue 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;
}
2017-12-14 12:53:53 +01:00
DxbcValue DxbcCompiler::extendReg(
const DxbcValue& src,
uint32_t size) {
if (size == 1)
return src;
2017-12-08 17:08:26 +01:00
std::array<uint32_t, 4> ids = {
src.valueId, src.valueId,
src.valueId, src.valueId,
};
2017-12-08 17:08:26 +01:00
uint32_t typeId = this->defineVectorType(
src.componentType, size);
2017-12-08 17:08:26 +01:00
2017-12-14 12:53:53 +01:00
DxbcValue result;
result.componentType = src.componentType;
result.componentCount = size;
result.valueId = m_module.opCompositeConstruct(
typeId, size, ids.data());
return result;
2017-12-08 17:08:26 +01:00
}
2017-12-14 12:53:53 +01:00
DxbcValue DxbcCompiler::applyOperandModifiers(
DxbcValue 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;
}
2017-12-14 12:53:53 +01:00
DxbcValue DxbcCompiler::applyResultModifiers(
DxbcValue 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;
}
2017-12-14 12:53:53 +01:00
DxbcValue DxbcCompiler::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
2017-12-14 12:53:53 +01:00
DxbcValue 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);
}
}
2017-12-14 12:53:53 +01:00
DxbcValue DxbcCompiler::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.
2017-12-14 12:53:53 +01:00
DxbcValue 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;
}
2017-12-14 12:53:53 +01:00
DxbcValue DxbcCompiler::loadRegister(
const DxbcInstOp& srcOp,
DxbcRegMask srcMask,
DxbcScalarType dstType) {
return this->loadPtr(
this->getOperandPtr(srcOp));
}
2017-12-14 12:53:53 +01:00
void DxbcCompiler::storeOp(
const DxbcInstOp& dstOp,
2017-12-14 12:53:53 +01:00
const DxbcValue& srcValue) {
this->storePtr(
this->getOperandPtr(dstOp),
srcValue, dstOp.mask);
}
2017-12-14 12:53:53 +01:00
DxbcValue DxbcCompiler::loadPtr(const DxbcPointer& ptr) {
const uint32_t typeId = this->defineVectorType(
ptr.componentType, ptr.componentCount);
2017-12-14 12:53:53 +01:00
DxbcValue result;
result.componentType = ptr.componentType;
result.componentCount = ptr.componentCount;
result.valueId = m_module.opLoad(typeId, ptr.pointerId);
return result;
}
2017-12-14 12:53:53 +01:00
void DxbcCompiler::storePtr(
const DxbcPointer& ptr,
const DxbcValue& value,
DxbcRegMask mask) {
2017-12-14 12:53:53 +01:00
DxbcValue 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
2017-12-14 12:53:53 +01:00
DxbcValue tmp = this->loadPtr(ptr);
tmp = this->insertReg(tmp, srcValue, mask);
m_module.opStore(ptr.pointerId, tmp.valueId);
}
}
2017-12-14 12:53:53 +01:00
DxbcValue DxbcCompiler::loadIndex(const DxbcInstOpIndex& idx) {
DxbcValue constantPart;
DxbcValue 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);
}
2017-12-08 17:08:26 +01:00
if (relativePart.valueId == 0) return constantPart;
if (constantPart.valueId == 0) return relativePart;
2017-12-14 12:53:53 +01:00
DxbcValue result;
result.componentType = DxbcScalarType::Sint32;
result.componentCount = 1;
result.valueId = m_module.opIAdd(
this->defineScalarType(result.componentType),
relativePart.valueId, constantPart.valueId);
return result;
2017-12-08 17:08:26 +01:00
}
2017-12-14 12:53:53 +01:00
DxbcPointer DxbcCompiler::getOperandPtr(const DxbcInstOp& op) {
DxbcPointer 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;
2017-12-08 17:08:26 +01:00
case DxbcOperandType::ConstantBuffer:
return this->getConstantBufferPtr(op);
2017-12-08 17:08:26 +01:00
default:
Logger::err("dxbc: Unhandled operand type");
}
return result;
}
2017-12-14 12:53:53 +01:00
DxbcPointer DxbcCompiler::getConstantBufferPtr(const DxbcInstOp& op) {
if (op.indexDim != 2) {
Logger::err("dxbc: Constant buffer reference needs two indices");
2017-12-14 12:53:53 +01:00
return DxbcPointer();
}
// 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;
2017-12-14 12:53:53 +01:00
const DxbcValue 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 };
2017-12-14 12:53:53 +01:00
DxbcPointer 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;
}
2017-12-14 12:53:53 +01:00
void DxbcCompiler::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());
}
2017-12-14 12:53:53 +01:00
void DxbcCompiler::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(
2017-12-14 12:53:53 +01:00
e->componentType, e->componentMask.setCount());
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;
2017-12-14 12:53:53 +01:00
m_ps.oregs.at(e->registerId).componentCount = e->componentMask.setCount();
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());
}
2017-12-14 12:53:53 +01:00
void DxbcCompiler::prepareVertexInputs() {
// TODO implement
}
2017-12-14 12:53:53 +01:00
void DxbcCompiler::preparePixelInputs() {
// TODO implement
}
2017-12-14 12:53:53 +01:00
void DxbcCompiler::prepareVertexOutputs() {
for (const DxbcSvMapping& svMapping : m_oSvs) {
switch (svMapping.sv) {
case DxbcSystemValue::Position: {
2017-12-14 12:53:53 +01:00
DxbcPointer 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);
2017-12-14 12:53:53 +01:00
DxbcPointer 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));
}
}
}
2017-12-14 12:53:53 +01:00
void DxbcCompiler::preparePixelOutputs() {
// TODO implement
}
2017-12-14 12:53:53 +01:00
void DxbcCompiler::endVertexShader() {
this->prepareVertexInputs();
m_module.opFunctionCall(
m_module.defVoidType(),
m_vs.functionId, 0, nullptr);
this->prepareVertexOutputs();
}
2017-12-14 12:53:53 +01:00
void DxbcCompiler::endPixelShader() {
this->preparePixelInputs();
m_module.opFunctionCall(
m_module.defVoidType(),
m_ps.functionId, 0, nullptr);
this->preparePixelOutputs();
}
2017-12-14 12:53:53 +01:00
uint32_t DxbcCompiler::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;
}
2017-12-14 12:53:53 +01:00
uint32_t DxbcCompiler::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;
}
2017-12-14 12:53:53 +01:00
uint32_t DxbcCompiler::defineVectorType(
DxbcScalarType componentType,
uint32_t componentCount) {
uint32_t typeId = this->defineScalarType(componentType);
if (componentCount > 1) {
typeId = m_module.defVectorType(
typeId, componentCount);
}
return typeId;
}
2017-12-14 12:53:53 +01:00
uint32_t DxbcCompiler::definePointerType(
DxbcScalarType componentType,
uint32_t componentCount,
spv::StorageClass storageClass) {
return m_module.defPointerType(
this->defineVectorType(
componentType, componentCount),
storageClass);
}
2017-12-14 12:53:53 +01:00
DxbcError DxbcCompiler::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;
}
}