SPV: Implement Vulkan version of GLSL (KHR_vulkan_glsl).
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index ed86cc2..864902c 100755
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+// 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 @@
}
// 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 @@
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 @@
{
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
// '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 @@
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 @@
}
// 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 @@
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 @@
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 @@
}
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 @@
}
// 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 @@
{
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