SPV: Implement Vulkan version of GLSL (KHR_vulkan_glsl).
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
//
|
||||
//Copyright (C) 2014 LunarG, Inc.
|
||||
//Copyright (C) 2014-2015 LunarG, Inc.
|
||||
//Copyright (C) 2015-2016 Google, Inc.
|
||||
//
|
||||
//All rights reserved.
|
||||
//
|
||||
@@ -95,6 +96,7 @@ protected:
|
||||
spv::Id getSampledType(const glslang::TSampler&);
|
||||
spv::Id convertGlslangToSpvType(const glslang::TType& type);
|
||||
spv::Id convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking, const glslang::TQualifier&);
|
||||
spv::Id makeArraySizeId(const glslang::TArraySizes&, int dim);
|
||||
spv::Id accessChainLoad(const glslang::TType& type);
|
||||
glslang::TLayoutPacking getExplicitLayout(const glslang::TType& type) const;
|
||||
int getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking, glslang::TLayoutMatrix);
|
||||
@@ -195,6 +197,8 @@ spv::StorageClass TranslateStorageClass(const glslang::TType& type)
|
||||
else if (type.getQualifier().isPipeOutput())
|
||||
return spv::StorageClassOutput;
|
||||
else if (type.getQualifier().isUniformOrBuffer()) {
|
||||
if (type.getQualifier().layoutPushConstant)
|
||||
return spv::StorageClassPushConstant;
|
||||
if (type.getBasicType() == glslang::EbtBlock)
|
||||
return spv::StorageClassUniform;
|
||||
else if (type.getBasicType() == glslang::EbtAtomicUint)
|
||||
@@ -225,6 +229,7 @@ spv::Dim TranslateDimensionality(const glslang::TSampler& sampler)
|
||||
case glslang::EsdCube: return spv::DimCube;
|
||||
case glslang::EsdRect: return spv::DimRect;
|
||||
case glslang::EsdBuffer: return spv::DimBuffer;
|
||||
case glslang::EsdSubpass: return spv::DimSubpassData;
|
||||
default:
|
||||
assert(0);
|
||||
return spv::Dim2D;
|
||||
@@ -376,6 +381,8 @@ spv::BuiltIn TGlslangToSpvTraverser::TranslateBuiltInDecoration(glslang::TBuiltI
|
||||
case glslang::EbvPosition: return spv::BuiltInPosition;
|
||||
case glslang::EbvVertexId: return spv::BuiltInVertexId;
|
||||
case glslang::EbvInstanceId: return spv::BuiltInInstanceId;
|
||||
case glslang::EbvVertexIndex: return spv::BuiltInVertexIndex;
|
||||
case glslang::EbvInstanceIndex: return spv::BuiltInInstanceIndex;
|
||||
case glslang::EbvBaseVertex:
|
||||
case glslang::EbvBaseInstance:
|
||||
case glslang::EbvDrawId:
|
||||
@@ -492,6 +499,25 @@ spv::ImageFormat TGlslangToSpvTraverser::TranslateImageFormat(const glslang::TTy
|
||||
}
|
||||
}
|
||||
|
||||
// Return whether or not the given type is something that should be tied to a
|
||||
// descriptor set.
|
||||
bool IsDescriptorResource(const glslang::TType& type)
|
||||
{
|
||||
// uniform and buffer blocks are included
|
||||
if (type.getBasicType() == glslang::EbtBlock)
|
||||
return type.getQualifier().isUniformOrBuffer();
|
||||
|
||||
// non block...
|
||||
// basically samplerXXX/subpass/sampler/texture are all included
|
||||
// if they are the global-scope-class, not the function parameter
|
||||
// (or local, if they ever exist) class.
|
||||
if (type.getBasicType() == glslang::EbtSampler)
|
||||
return type.getQualifier().isUniformOrBuffer();
|
||||
|
||||
// None of the above.
|
||||
return false;
|
||||
}
|
||||
|
||||
void InheritQualifiers(glslang::TQualifier& child, const glslang::TQualifier& parent)
|
||||
{
|
||||
if (child.layoutMatrix == glslang::ElmNone)
|
||||
@@ -708,7 +734,7 @@ void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol)
|
||||
}
|
||||
|
||||
// Only process non-linkage-only nodes for generating actual static uses
|
||||
if (! linkageOnly) {
|
||||
if (! linkageOnly || symbol->getQualifier().isSpecConstant()) {
|
||||
// Prepare to generate code for the access
|
||||
|
||||
// L-value chains will be computed left to right. We're on the symbol now,
|
||||
@@ -717,10 +743,14 @@ void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol)
|
||||
builder.clearAccessChain();
|
||||
|
||||
// For now, we consider all user variables as being in memory, so they are pointers,
|
||||
// except for "const in" arguments to a function, which are an intermediate object.
|
||||
// See comments in handleUserFunctionCall().
|
||||
glslang::TStorageQualifier qualifier = symbol->getQualifier().storage;
|
||||
if (qualifier == glslang::EvqConstReadOnly && constReadOnlyParameters.find(symbol->getId()) != constReadOnlyParameters.end())
|
||||
// except for
|
||||
// A) "const in" arguments to a function, which are an intermediate object.
|
||||
// See comments in handleUserFunctionCall().
|
||||
// B) Specialization constants (normal constant don't even come in as a variable),
|
||||
// These are also pure R-values.
|
||||
glslang::TQualifier qualifier = symbol->getQualifier();
|
||||
if ((qualifier.storage == glslang::EvqConstReadOnly && constReadOnlyParameters.find(symbol->getId()) != constReadOnlyParameters.end()) ||
|
||||
qualifier.isSpecConstant())
|
||||
builder.setAccessChainRValue(id);
|
||||
else
|
||||
builder.setAccessChainLValue(id);
|
||||
@@ -1110,9 +1140,12 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt
|
||||
{
|
||||
if (node->isUserDefined())
|
||||
result = handleUserFunctionCall(node);
|
||||
assert(result);
|
||||
builder.clearAccessChain();
|
||||
builder.setAccessChainRValue(result);
|
||||
//assert(result); // this can happen for bad shaders because the call graph completeness checking is not yet done
|
||||
if (result) {
|
||||
builder.clearAccessChain();
|
||||
builder.setAccessChainRValue(result);
|
||||
} else
|
||||
spv::MissingFunctionality("missing user function; linker needs to catch that");
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1157,12 +1190,15 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt
|
||||
case glslang::EOpConstructUVec3:
|
||||
case glslang::EOpConstructUVec4:
|
||||
case glslang::EOpConstructStruct:
|
||||
case glslang::EOpConstructTextureSampler:
|
||||
{
|
||||
std::vector<spv::Id> arguments;
|
||||
translateArguments(*node, arguments);
|
||||
spv::Id resultTypeId = convertGlslangToSpvType(node->getType());
|
||||
spv::Id constructed;
|
||||
if (node->getOp() == glslang::EOpConstructStruct || node->getType().isArray()) {
|
||||
if (node->getOp() == glslang::EOpConstructTextureSampler)
|
||||
constructed = builder.createOp(spv::OpSampledImage, resultTypeId, arguments);
|
||||
else if (node->getOp() == glslang::EOpConstructStruct || node->getType().isArray()) {
|
||||
std::vector<spv::Id> constituents;
|
||||
for (int c = 0; c < (int)arguments.size(); ++c)
|
||||
constituents.push_back(arguments[c]);
|
||||
@@ -1640,11 +1676,17 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty
|
||||
case glslang::EbtSampler:
|
||||
{
|
||||
const glslang::TSampler& sampler = type.getSampler();
|
||||
// an image is present, make its type
|
||||
spvType = builder.makeImageType(getSampledType(sampler), TranslateDimensionality(sampler), sampler.shadow, sampler.arrayed, sampler.ms,
|
||||
sampler.image ? 2 : 1, TranslateImageFormat(type));
|
||||
if (! sampler.image) {
|
||||
spvType = builder.makeSampledImageType(spvType);
|
||||
if (sampler.sampler) {
|
||||
// pure sampler
|
||||
spvType = builder.makeSamplerType();
|
||||
} else {
|
||||
// an image is present, make its type
|
||||
spvType = builder.makeImageType(getSampledType(sampler), TranslateDimensionality(sampler), sampler.shadow, sampler.arrayed, sampler.ms,
|
||||
sampler.image ? 2 : 1, TranslateImageFormat(type));
|
||||
if (sampler.combined) {
|
||||
// already has both image and sampler, make the combined type
|
||||
spvType = builder.makeSampledImageType(spvType);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1790,12 +1832,10 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty
|
||||
|
||||
// make the arrays
|
||||
for (int dim = type.getArraySizes()->getNumDims() - 1; dim > 0; --dim) {
|
||||
int size = type.getArraySizes()->getDimSize(dim);
|
||||
assert(size > 0);
|
||||
spvType = builder.makeArrayType(spvType, size, stride);
|
||||
spvType = builder.makeArrayType(spvType, makeArraySizeId(*type.getArraySizes(), dim), stride);
|
||||
if (stride > 0)
|
||||
builder.addDecoration(spvType, spv::DecorationArrayStride, stride);
|
||||
stride *= size;
|
||||
stride *= type.getArraySizes()->getDimSize(dim);
|
||||
}
|
||||
} else {
|
||||
// single-dimensional array, and don't yet have stride
|
||||
@@ -1810,7 +1850,7 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty
|
||||
spvType = builder.makeRuntimeArray(spvType);
|
||||
} else {
|
||||
assert(type.getOuterArraySize() > 0);
|
||||
spvType = builder.makeArrayType(spvType, type.getOuterArraySize(), stride);
|
||||
spvType = builder.makeArrayType(spvType, makeArraySizeId(*type.getArraySizes(), 0), stride);
|
||||
}
|
||||
if (stride > 0)
|
||||
builder.addDecoration(spvType, spv::DecorationArrayStride, stride);
|
||||
@@ -1819,6 +1859,26 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty
|
||||
return spvType;
|
||||
}
|
||||
|
||||
// Turn the expression forming the array size into an id.
|
||||
// This is not quite trivial, because of specialization constants.
|
||||
// Sometimes, a raw constant is turned into an Id, and sometimes
|
||||
// a specialization constant expression is.
|
||||
spv::Id TGlslangToSpvTraverser::makeArraySizeId(const glslang::TArraySizes& arraySizes, int dim)
|
||||
{
|
||||
// First, see if this is sized with a node, meaning a specialization constant:
|
||||
glslang::TIntermTyped* specNode = arraySizes.getDimNode(dim);
|
||||
if (specNode != nullptr) {
|
||||
builder.clearAccessChain();
|
||||
specNode->traverse(this);
|
||||
return accessChainLoad(specNode->getAsTyped()->getType());
|
||||
}
|
||||
|
||||
// Otherwise, need a compile-time (front end) size, get it:
|
||||
int size = arraySizes.getDimSize(dim);
|
||||
assert(size > 0);
|
||||
return builder.makeUintConstant(size);
|
||||
}
|
||||
|
||||
// Wrap the builder's accessChainLoad to:
|
||||
// - localize handling of RelaxedPrecision
|
||||
// - use the SPIR-V inferred type instead of another conversion of the glslang type
|
||||
@@ -1891,7 +1951,7 @@ int TGlslangToSpvTraverser::getMatrixStride(const glslang::TType& matrixType, gl
|
||||
// 'currentOffset' should be passed in already initialized, ready to modify, and reflecting
|
||||
// the migration of data from nextOffset -> currentOffset. It should be -1 on the first call.
|
||||
// -1 means a non-forced member offset (no decoration needed).
|
||||
void TGlslangToSpvTraverser::updateMemberOffset(const glslang::TType& structType, const glslang::TType& memberType, int& currentOffset, int& nextOffset,
|
||||
void TGlslangToSpvTraverser::updateMemberOffset(const glslang::TType& /*structType*/, const glslang::TType& memberType, int& currentOffset, int& nextOffset,
|
||||
glslang::TLayoutPacking explicitLayout, glslang::TLayoutMatrix matrixLayout)
|
||||
{
|
||||
// this will get a positive value when deemed necessary
|
||||
@@ -2169,6 +2229,24 @@ spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermO
|
||||
std::vector<spv::Id> operands;
|
||||
auto opIt = arguments.begin();
|
||||
operands.push_back(*(opIt++));
|
||||
|
||||
// Handle subpass operations
|
||||
// TODO: GLSL should change to have the "MS" only on the type rather than the
|
||||
// built-in function.
|
||||
if (cracked.subpass) {
|
||||
// add on the (0,0) coordinate
|
||||
spv::Id zero = builder.makeIntConstant(0);
|
||||
std::vector<spv::Id> comps;
|
||||
comps.push_back(zero);
|
||||
comps.push_back(zero);
|
||||
operands.push_back(builder.makeCompositeConstant(builder.makeVectorType(builder.makeIntType(32), 2), comps));
|
||||
if (sampler.ms) {
|
||||
operands.push_back(spv::ImageOperandsSampleMask);
|
||||
operands.push_back(*(opIt++));
|
||||
}
|
||||
return builder.createOp(spv::OpImageRead, convertGlslangToSpvType(node->getType()), operands);
|
||||
}
|
||||
|
||||
operands.push_back(*(opIt++));
|
||||
if (node->getOp() == glslang::EOpImageLoad) {
|
||||
if (sampler.ms) {
|
||||
@@ -3109,7 +3187,7 @@ spv::Id TGlslangToSpvTraverser::makeSmearedConstant(spv::Id constant, int vector
|
||||
}
|
||||
|
||||
// For glslang ops that map to SPV atomic opCodes
|
||||
spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy)
|
||||
spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv::Decoration /*precision*/, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy)
|
||||
{
|
||||
spv::Op opCode = spv::OpNop;
|
||||
|
||||
@@ -3244,8 +3322,10 @@ spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv::
|
||||
case glslang::EOpMix:
|
||||
if (isFloat)
|
||||
libCall = spv::GLSLstd450FMix;
|
||||
else
|
||||
libCall = spv::GLSLstd450IMix;
|
||||
else {
|
||||
opCode = spv::OpSelect;
|
||||
spv::MissingFunctionality("translating integer mix to OpSelect");
|
||||
}
|
||||
builder.promoteScalar(precision, operands.front(), operands.back());
|
||||
break;
|
||||
case glslang::EOpStep:
|
||||
@@ -3439,6 +3519,8 @@ spv::Id TGlslangToSpvTraverser::getSymbolId(const glslang::TIntermSymbol* symbol
|
||||
if (! symbol->getType().isStruct()) {
|
||||
addDecoration(id, TranslatePrecisionDecoration(symbol->getType()));
|
||||
addDecoration(id, TranslateInterpolationDecoration(symbol->getType().getQualifier()));
|
||||
if (symbol->getType().getQualifier().hasSpecConstantId())
|
||||
addDecoration(id, spv::DecorationSpecId, symbol->getType().getQualifier().layoutSpecConstantId);
|
||||
if (symbol->getQualifier().hasLocation())
|
||||
builder.addDecoration(id, spv::DecorationLocation, symbol->getQualifier().layoutLocation);
|
||||
if (symbol->getQualifier().hasIndex())
|
||||
@@ -3463,8 +3545,14 @@ spv::Id TGlslangToSpvTraverser::getSymbolId(const glslang::TIntermSymbol* symbol
|
||||
}
|
||||
if (symbol->getQualifier().hasSet())
|
||||
builder.addDecoration(id, spv::DecorationDescriptorSet, symbol->getQualifier().layoutSet);
|
||||
else if (IsDescriptorResource(symbol->getType())) {
|
||||
// default to 0
|
||||
builder.addDecoration(id, spv::DecorationDescriptorSet, 0);
|
||||
}
|
||||
if (symbol->getQualifier().hasBinding())
|
||||
builder.addDecoration(id, spv::DecorationBinding, symbol->getQualifier().layoutBinding);
|
||||
if (symbol->getQualifier().hasAttachment())
|
||||
builder.addDecoration(id, spv::DecorationInputAttachmentIndex, symbol->getQualifier().layoutAttachment);
|
||||
if (glslangIntermediate->getXfbMode()) {
|
||||
builder.addCapability(spv::CapabilityTransformFeedback);
|
||||
if (symbol->getQualifier().hasXfbStride())
|
||||
@@ -3510,7 +3598,7 @@ void TGlslangToSpvTraverser::addMemberDecoration(spv::Id id, int member, spv::De
|
||||
}
|
||||
|
||||
// Make a full tree of instructions to build a SPIR-V specialization constant,
|
||||
// or regularly constant if possible.
|
||||
// or regular constant if possible.
|
||||
//
|
||||
// TBD: this is not yet done, nor verified to be the best design, it does do the leaf symbols though
|
||||
//
|
||||
@@ -3523,10 +3611,38 @@ spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermType
|
||||
{
|
||||
assert(node.getQualifier().storage == glslang::EvqConst);
|
||||
|
||||
// hand off to the non-spec-constant path
|
||||
assert(node.getAsConstantUnion() != nullptr || node.getAsSymbolNode() != nullptr);
|
||||
int nextConst = 0;
|
||||
return createSpvConstant(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(), nextConst, false);
|
||||
if (! node.getQualifier().specConstant) {
|
||||
// hand off to the non-spec-constant path
|
||||
assert(node.getAsConstantUnion() != nullptr || node.getAsSymbolNode() != nullptr);
|
||||
int nextConst = 0;
|
||||
return createSpvConstant(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(),
|
||||
nextConst, false);
|
||||
}
|
||||
|
||||
// We now know we have a specialization constant to build
|
||||
|
||||
if (node.getAsSymbolNode() && node.getQualifier().hasSpecConstantId()) {
|
||||
// this is a direct literal assigned to a layout(constant_id=) declaration
|
||||
int nextConst = 0;
|
||||
return createSpvConstant(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(),
|
||||
nextConst, true);
|
||||
} else {
|
||||
// gl_WorkgroupSize is a special case until the front-end handles hierarchical specialization constants,
|
||||
// even then, it's specialization ids are handled by special case syntax in GLSL: layout(local_size_x = ...
|
||||
if (node.getType().getQualifier().builtIn == glslang::EbvWorkGroupSize) {
|
||||
std::vector<spv::Id> dimConstId;
|
||||
for (int dim = 0; dim < 3; ++dim) {
|
||||
bool specConst = (glslangIntermediate->getLocalSizeSpecId(dim) != glslang::TQualifier::layoutNotSet);
|
||||
dimConstId.push_back(builder.makeUintConstant(glslangIntermediate->getLocalSize(dim), specConst));
|
||||
if (specConst)
|
||||
addDecoration(dimConstId.back(), spv::DecorationSpecId, glslangIntermediate->getLocalSizeSpecId(dim));
|
||||
}
|
||||
return builder.makeCompositeConstant(builder.makeVectorType(builder.makeUintType(32), 3), dimConstId, true);
|
||||
} else {
|
||||
spv::MissingFunctionality("specialization-constant expression trees");
|
||||
return spv::NoResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use 'consts' as the flattened glslang source of scalar constants to recursively
|
||||
|
||||
Reference in New Issue
Block a user