[dxbc] Implemented input mapping + sample controls

Input variables are now copied into a temporary array, which allows
dynamic indexing and which also allows us to use system values that
are mapped to input registers in DXBC. This breaks geometry shaders
for now, however.
This commit is contained in:
Philip Rebohle 2017-12-21 12:37:20 +01:00
parent 109ce0a695
commit 2e4275649e
3 changed files with 259 additions and 108 deletions

View File

@ -339,14 +339,14 @@ namespace dxvk {
// Avoid declaring the same variable multiple times.
// This may happen when multiple system values are
// mapped to different parts of the same register.
if (m_vRegs.at(regIdx) == 0) {
if (m_vRegs.at(regIdx) == 0 && sv == DxbcSystemValue::None) {
DxbcRegisterInfo info;
info.type.ctype = DxbcScalarType::Float32;
info.type.ccount = 4;
info.type.alength = regDim;
info.sclass = spv::StorageClassInput;
uint32_t varId = this->emitNewVariable(info);
const uint32_t varId = emitNewVariable(info);
m_module.decorateLocation(varId, regIdx);
m_module.setDebugName(varId, str::format("v", regIdx).c_str());
@ -370,12 +370,10 @@ namespace dxvk {
if (im == DxbcInterpolationMode::LinearSample
|| im == DxbcInterpolationMode::LinearNoPerspectiveSample)
m_module.decorate(varId, spv::DecorationSample);
}
// Add a new system value mapping if needed
// TODO declare SV if necessary
if (sv != DxbcSystemValue::None)
} else if (sv != DxbcSystemValue::None) {
// Add a new system value mapping if needed
m_vMappings.push_back({ regIdx, regMask, sv });
}
}
@ -406,7 +404,6 @@ namespace dxvk {
// Add a new system value mapping if needed
// TODO declare SV if necessary
if (sv != DxbcSystemValue::None)
m_oMappings.push_back({ regIdx, regMask, sv });
}
@ -533,14 +530,7 @@ namespace dxvk {
const uint32_t sampledTypeId = getScalarTypeId(sampledType);
// Declare the resource type
struct ImageTypeInfo {
spv::Dim dim;
uint32_t array;
uint32_t ms;
uint32_t sampled;
};
const ImageTypeInfo typeInfo = [resourceType] () -> ImageTypeInfo {
const DxbcImageInfo typeInfo = [resourceType] () -> DxbcImageInfo {
switch (resourceType) {
case DxbcResourceDim::Texture1D: return { spv::Dim1D, 0, 0, 1 };
case DxbcResourceDim::Texture1DArr: return { spv::Dim1D, 1, 0, 1 };
@ -574,6 +564,7 @@ namespace dxvk {
m_module.setDebugName(varId,
str::format("t", registerId).c_str());
m_textures.at(registerId).imageInfo = typeInfo;
m_textures.at(registerId).varId = varId;
m_textures.at(registerId).sampledType = sampledType;
m_textures.at(registerId).sampledTypeId = sampledTypeId;
@ -611,7 +602,14 @@ namespace dxvk {
}
}();
m_gs.inputPrimitive = ins.controls.primitive;
m_module.setExecutionMode(m_entryPointId, mode);
const uint32_t vertexCount
= primitiveVertexCount(m_gs.inputPrimitive);
emitDclInputArray(vertexCount);
emitGsInitBuiltins(vertexCount);
}
@ -1267,7 +1265,7 @@ namespace dxvk {
void DxbcCompiler::emitGeometryEmit(const DxbcShaderInstruction& ins) {
switch (ins.op) {
case DxbcOpcode::Emit: {
emitGsOutputSetup();
emitOutputSetup(0);
m_module.opEmitVertex();
} break;
@ -1299,9 +1297,26 @@ namespace dxvk {
const uint32_t textureId = textureReg.idx[0].offset;
const uint32_t samplerId = samplerReg.idx[0].offset;
// Image type, which stores the image dimensions etc.
const DxbcImageInfo imageType = m_textures.at(textureId).imageInfo;
// FIXME implement properly
DxbcRegMask coordArrayMask(true, true, true, true);
DxbcRegMask coordLayerMask(true, true, true, true);
const uint32_t imageLayerDim = [&] {
switch (imageType.dim) {
case spv::DimBuffer: return 1;
case spv::Dim1D: return 1;
case spv::Dim2D: return 2;
case spv::Dim3D: return 3;
case spv::DimCube: return 3;
default: throw DxvkError("DxbcCompiler: Unsupported image dim");
}
}();
const DxbcRegMask coordArrayMask =
DxbcRegMask::firstN(imageLayerDim + imageType.array);
const DxbcRegMask coordLayerMask =
DxbcRegMask::firstN(imageLayerDim);
// Load the texture coordinates. SPIR-V allows these
// to be float4 even if not all components are used.
@ -1346,7 +1361,19 @@ namespace dxvk {
// Accumulate additional image operands. These are
// not part of the actual operand token in SPIR-V.
SpirvImageOperands imageOperands;
// TODO implement sample controls
if (ins.sampleControls.u != 0 || ins.sampleControls.v != 0 || ins.sampleControls.w != 0) {
const std::array<uint32_t, 3> offsetIds = {
imageLayerDim >= 1 ? m_module.constu32(ins.sampleControls.u) : 0,
imageLayerDim >= 2 ? m_module.constu32(ins.sampleControls.v) : 0,
imageLayerDim >= 3 ? m_module.constu32(ins.sampleControls.w) : 0,
};
imageOperands.flags |= spv::ImageOperandsConstOffsetMask;
imageOperands.sConstOffset = m_module.constComposite(
getVectorTypeId({ DxbcScalarType::Sint32, imageLayerDim }),
imageLayerDim, offsetIds.data());
}
// Sampling an image always returns a four-component
// vector, whereas depth-compare ops return a scalar.
@ -1886,25 +1913,20 @@ namespace dxvk {
result.type.ctype = DxbcScalarType::Float32;
result.type.ccount = 4;
if (operand.idxDim == 1) {
// TODO add support for relative register index
result.id = m_vRegs.at(operand.idx[0].offset);
return result;
} else {
// TODO add support for relative register index
const DxbcRegisterValue vertexId = emitIndexLoad(operand.idx[0]);
std::array<uint32_t, 2> indices = { 0, 0 };
for (uint32_t i = 0; i < operand.idxDim; i++)
indices.at(i) = emitIndexLoad(operand.idx[i]).id;
DxbcRegisterInfo info;
info.type.ctype = result.type.ctype;
info.type.ccount = result.type.ccount;
info.type.alength = 0;
info.sclass = spv::StorageClassInput;
DxbcRegisterInfo info;
info.type.ctype = result.type.ctype;
info.type.ccount = result.type.ccount;
info.type.alength = 0;
info.sclass = spv::StorageClassPrivate;
result.id = m_module.opAccessChain(
getPointerTypeId(info),
m_vRegs.at(operand.idx[1].offset),
1, &vertexId.id);
}
result.id = m_module.opAccessChain(
getPointerTypeId(info), m_vArray,
operand.idxDim, indices.data());
return result;
}
@ -2148,22 +2170,80 @@ namespace dxvk {
}
void DxbcCompiler::emitVsInputSetup() {
void DxbcCompiler::emitInputSetup(uint32_t vertexCount) {
const uint32_t vecTypeId = m_module.defVectorType(
m_module.defFloatType(32), 4);
const uint32_t ptrTypeId = m_module.defPointerType(
vecTypeId, spv::StorageClassPrivate);
// Copy all defined user input registers into the array
for (uint32_t i = 0; i < m_vRegs.size(); i++) {
if (m_vRegs.at(i) != 0) {
const uint32_t registerId = m_module.constu32(i);
m_module.opStore(
m_module.opAccessChain(ptrTypeId, m_vArray, 1, &registerId),
m_module.opLoad(vecTypeId, m_vRegs.at(i)));
}
}
// Copy all system value registers into the array,
// preserving any previously written contents.
for (const DxbcSvMapping& svMapping : m_vMappings) {
const uint32_t registerId = m_module.constu32(svMapping.regId);
DxbcRegisterPointer ptrOut;
ptrOut.type.ctype = DxbcScalarType::Float32;
ptrOut.type.ccount = 4;
ptrOut.id = m_module.opAccessChain(ptrTypeId,
m_vArray, 1, &registerId);
switch (svMapping.sv) {
case DxbcSystemValue::Position: {
if (m_version.type() == DxbcProgramType::PixelShader) {
DxbcRegisterPointer ptrIn;
ptrIn.type.ctype = DxbcScalarType::Float32;
ptrIn.type.ccount = 4;
ptrIn.id = m_ps.builtinFragCoord;
emitValueStore(ptrOut,
emitRegisterExtract(
emitValueLoad(ptrIn),
svMapping.regMask),
svMapping.regMask);
} else {
Logger::warn(str::format(
"DxbcCompiler: SV_POSITION input not yet supported: ",
m_version.type()));
}
} break;
case DxbcSystemValue::VertexId: {
if (m_version.type() == DxbcProgramType::VertexShader) {
DxbcRegisterPointer ptrIn;
ptrIn.type.ctype = DxbcScalarType::Uint32;
ptrIn.type.ccount = 1;
ptrIn.id = m_vs.builtinVertexId;
emitValueStore(ptrOut,
emitValueLoad(ptrIn),
svMapping.regMask);
} else {
Logger::warn(str::format(
"DxbcCompiler: SV_VERTEXID input not yet supported: ",
m_version.type()));
}
} break;
default:
Logger::warn(str::format(
"DxbcCompiler: Unhandled sv input: ",
svMapping.sv));
}
}
}
void DxbcCompiler::emitGsInputSetup() {
}
void DxbcCompiler::emitPsInputSetup() {
}
void DxbcCompiler::emitVsOutputSetup() {
void DxbcCompiler::emitOutputSetup(uint32_t vertexCount) {
for (const DxbcSvMapping& svMapping : m_oMappings) {
switch (svMapping.sv) {
case DxbcSystemValue::Position: {
@ -2193,52 +2273,39 @@ namespace dxvk {
default:
Logger::warn(str::format(
"DxbcCompiler: Unhandled vertex sv output: ",
"DxbcCompiler: Unhandled sv output: ",
svMapping.sv));
}
}
}
void DxbcCompiler::emitGsOutputSetup() {
for (const DxbcSvMapping& svMapping : m_oMappings) {
switch (svMapping.sv) {
case DxbcSystemValue::Position: {
DxbcRegisterInfo info;
info.type.ctype = DxbcScalarType::Float32;
info.type.ccount = 4;
info.type.alength = 0;
info.sclass = spv::StorageClassOutput;
const uint32_t ptrTypeId = getPointerTypeId(info);
const uint32_t memberId = m_module.constu32(PerVertex_Position);
DxbcRegisterPointer dstPtr;
dstPtr.type.ctype = info.type.ctype;
dstPtr.type.ccount = info.type.ccount;
dstPtr.id = m_module.opAccessChain(
ptrTypeId, m_perVertexOut, 1, &memberId);
DxbcRegisterPointer srcPtr;
srcPtr.type.ctype = info.type.ctype;
srcPtr.type.ccount = info.type.ccount;
srcPtr.id = m_oRegs.at(svMapping.regId);
emitValueStore(dstPtr, emitValueLoad(srcPtr),
DxbcRegMask(true, true, true, true));
} break;
default:
Logger::warn(str::format(
"DxbcCompiler: Unhandled vertex sv output: ",
svMapping.sv));
}
}
}
void DxbcCompiler::emitPsOutputSetup() {
void DxbcCompiler::emitVsInitBuiltins() {
m_vs.builtinVertexId = emitNewBuiltinVariable({
{ DxbcScalarType::Uint32, 1, 0 },
spv::StorageClassInput },
spv::BuiltInVertexIndex, // TODO test
"vs_vertex_index");
m_vs.builtinInstanceId = emitNewBuiltinVariable({
{ DxbcScalarType::Uint32, 1, 0 },
spv::StorageClassInput },
spv::BuiltInInstanceIndex, // TODO test
"vs_instance_index");
}
void DxbcCompiler::emitGsInitBuiltins(uint32_t vertexCount) {
}
void DxbcCompiler::emitPsInitBuiltins() {
m_ps.builtinFragCoord = emitNewBuiltinVariable({
{ DxbcScalarType::Float32, 4, 0 },
spv::StorageClassInput },
spv::BuiltInFragCoord,
"ps_frag_coord");
}
@ -2258,6 +2325,10 @@ namespace dxvk {
m_entryPointInterfaces.push_back(m_perVertexOut);
m_module.setDebugName(m_perVertexOut, "vs_vertex_out");
// Standard input array
emitDclInputArray(0);
emitVsInitBuiltins();
// Main function of the vertex shader
m_vs.functionId = m_module.allocateId();
m_module.setDebugName(m_vs.functionId, "vs_main");
@ -2332,6 +2403,10 @@ namespace dxvk {
}
}
// Standard input array
emitDclInputArray(0);
emitPsInitBuiltins();
// Main function of the pixel shader
m_ps.functionId = m_module.allocateId();
m_module.setDebugName(m_ps.functionId, "ps_main");
@ -2347,16 +2422,17 @@ namespace dxvk {
void DxbcCompiler::emitVsFinalize() {
this->emitVsInputSetup();
this->emitInputSetup(0);
m_module.opFunctionCall(
m_module.defVoidType(),
m_vs.functionId, 0, nullptr);
this->emitVsOutputSetup();
this->emitOutputSetup(0);
}
void DxbcCompiler::emitGsFinalize() {
this->emitGsInputSetup();
this->emitInputSetup(
primitiveVertexCount(m_gs.inputPrimitive));
m_module.opFunctionCall(
m_module.defVoidType(),
m_gs.functionId, 0, nullptr);
@ -2366,11 +2442,41 @@ namespace dxvk {
void DxbcCompiler::emitPsFinalize() {
this->emitPsInputSetup();
this->emitInputSetup(0);
m_module.opFunctionCall(
m_module.defVoidType(),
m_ps.functionId, 0, nullptr);
this->emitPsOutputSetup();
this->emitOutputSetup(0);
}
void DxbcCompiler::emitDclInputArray(uint32_t vertexCount) {
DxbcArrayType info;
info.ctype = DxbcScalarType::Float32;
info.ccount = 4;
info.alength = DxbcMaxInterfaceRegs;
// Define the array type. This will be two-dimensional
// in some shaders, with the outer index representing
// the vertex ID within an invocation.
uint32_t arrayTypeId = getArrayTypeId(info);
if (vertexCount != 0) {
arrayTypeId = m_module.defArrayType(
arrayTypeId, m_module.constu32(vertexCount));
}
// Define the actual variable. Note that this is private
// because we will copy input registers and some system
// variables to the array during the setup phase.
const uint32_t ptrTypeId = m_module.defPointerType(
arrayTypeId, spv::StorageClassPrivate);
const uint32_t varId = m_module.newVar(
ptrTypeId, spv::StorageClassPrivate);
m_module.setDebugName(varId, "shader_in");
m_vArray = varId;
}
@ -2380,6 +2486,20 @@ namespace dxvk {
}
uint32_t DxbcCompiler::emitNewBuiltinVariable(
const DxbcRegisterInfo& info,
spv::BuiltIn builtIn,
const char* name) {
const uint32_t varId = emitNewVariable(info);
m_module.decorateBuiltIn(varId, builtIn);
m_module.setDebugName(varId, name);
m_entryPointInterfaces.push_back(varId);
return varId;
}
DxbcCfgBlock* DxbcCompiler::cfgFindLoopBlock() {
for (auto cur = m_controlFlowBlocks.rbegin();
cur != m_controlFlowBlocks.rend(); cur++) {

View File

@ -91,6 +91,9 @@ namespace dxvk {
*/
struct DxbcCompilerVsPart {
uint32_t functionId = 0;
uint32_t builtinVertexId = 0;
uint32_t builtinInstanceId = 0;
};
@ -111,6 +114,9 @@ namespace dxvk {
struct DxbcCompilerPsPart {
uint32_t functionId = 0;
uint32_t builtinFragCoord = 0;
uint32_t builtinDepth = 0;
std::array<DxbcVectorType, DxbcMaxInterfaceRegs> oTypes;
};
@ -455,17 +461,16 @@ namespace dxvk {
const DxbcRegister& reg,
DxbcRegisterValue value);
/////////////////////////////
// Input preparation methods
void emitVsInputSetup();
void emitGsInputSetup();
void emitPsInputSetup();
////////////////////////////
// Input/output preparation
void emitInputSetup(uint32_t vertexCount);
void emitOutputSetup(uint32_t vertexCount);
//////////////////////////////
// Output preparation methods
void emitVsOutputSetup();
void emitGsOutputSetup();
void emitPsOutputSetup();
////////////////////////////////////////
// Builtin variable declaration methods
void emitVsInitBuiltins();
void emitGsInitBuiltins(uint32_t vertexCount);
void emitPsInitBuiltins();
/////////////////////////////////
// Shader initialization methods
@ -479,11 +484,21 @@ namespace dxvk {
void emitGsFinalize();
void emitPsFinalize();
//////////////
// Misc stuff
void emitDclInputArray(
uint32_t vertexCount);
///////////////////////////////
// Variable definition methods
uint32_t emitNewVariable(
const DxbcRegisterInfo& info);
uint32_t emitNewBuiltinVariable(
const DxbcRegisterInfo& info,
spv::BuiltIn builtIn,
const char* name);
/////////////////////////////////////
// Control flow block search methods
DxbcCfgBlock* cfgFindLoopBlock();

View File

@ -51,6 +51,17 @@ namespace dxvk {
};
/**
* \brief Image type information
*/
struct DxbcImageInfo {
spv::Dim dim = spv::Dim1D;
uint32_t array = 0;
uint32_t ms = 0;
uint32_t sampled = 0;
};
/**
* \brief Shader resource binding
*
@ -58,11 +69,12 @@ namespace dxvk {
* and associated type IDs.
*/
struct DxbcShaderResource {
uint32_t varId = 0;
DxbcScalarType sampledType = DxbcScalarType::Float32;
uint32_t sampledTypeId = 0;
uint32_t colorTypeId = 0;
uint32_t depthTypeId = 0;
DxbcImageInfo imageInfo;
uint32_t varId = 0;
DxbcScalarType sampledType = DxbcScalarType::Float32;
uint32_t sampledTypeId = 0;
uint32_t colorTypeId = 0;
uint32_t depthTypeId = 0;
};
/**
@ -128,6 +140,10 @@ namespace dxvk {
bool operator == (const DxbcRegMask& other) const { return m_mask == other.m_mask; }
bool operator != (const DxbcRegMask& other) const { return m_mask != other.m_mask; }
static DxbcRegMask firstN(uint32_t n) {
return DxbcRegMask(n >= 1, n >= 2, n >= 3, n >= 4);
}
private:
uint8_t m_mask = 0;