GLSL/SPV: Implement SPV_EXT_descriptor_indexing and GL_EXT_nonuniform_qualifier
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index 74cb9e9..1434cba 100755
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -102,6 +102,7 @@
 struct OpDecorations {
     spv::Decoration precision;
     spv::Decoration noContraction;
+    spv::Decoration nonUniform;
 };
 
 } // namespace
@@ -136,12 +137,14 @@
 
     spv::Decoration TranslateInterpolationDecoration(const glslang::TQualifier& qualifier);
     spv::Decoration TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier);
+    spv::Decoration TranslateNonUniformDecoration(const glslang::TQualifier& qualifier);
     spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable, bool memberDeclaration);
     spv::ImageFormat TranslateImageFormat(const glslang::TType& type);
     spv::SelectionControlMask TranslateSelectionControl(const glslang::TIntermSelection&) const;
     spv::SelectionControlMask TranslateSwitchControl(const glslang::TIntermSwitch&) const;
     spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&, unsigned int& dependencyLength) const;
     spv::StorageClass TranslateStorageClass(const glslang::TType&);
+    void addIndirectionIndexCapabilities(const glslang::TType& baseType, const glslang::TType& indexType);
     spv::Id createSpvVariable(const glslang::TIntermSymbol*);
     spv::Id getSampledType(const glslang::TSampler&);
     spv::Id getInvertedSwizzleType(const glslang::TIntermTyped&);
@@ -443,6 +446,17 @@
         return spv::DecorationMax;
 }
 
+// If glslang type is nonUniform, return SPIR-V NonUniform decoration.
+spv::Decoration TGlslangToSpvTraverser::TranslateNonUniformDecoration(const glslang::TQualifier& qualifier)
+{
+    if (qualifier.isNonUniform()) {
+        builder.addExtension("SPV_EXT_descriptor_indexing");
+        builder.addCapability(spv::CapabilityShaderNonUniformEXT);
+        return spv::DecorationNonUniformEXT;
+    } else
+        return spv::DecorationMax;
+}
+
 // Translate a glslang built-in variable to a SPIR-V built in decoration.  Also generate
 // associated capabilities when required.  For some built-in variables, a capability
 // is generated only when using the variable in an executable instruction, but not when
@@ -889,6 +903,42 @@
     return spv::StorageClassFunction;
 }
 
+// Add capabilities pertaining to how an array is indexed.
+void TGlslangToSpvTraverser::addIndirectionIndexCapabilities(const glslang::TType& baseType,
+                                                             const glslang::TType& indexType)
+{
+    if (indexType.getQualifier().isNonUniform()) {
+        // deal with an asserted non-uniform index
+        if (baseType.getBasicType() == glslang::EbtSampler) {
+            if (baseType.getQualifier().hasAttachment())
+                builder.addCapability(spv::CapabilityInputAttachmentArrayNonUniformIndexingEXT);
+            else if (baseType.isImage() && baseType.getSampler().dim == glslang::EsdBuffer)
+                builder.addCapability(spv::CapabilityStorageTexelBufferArrayNonUniformIndexingEXT);
+            else if (baseType.isTexture() && baseType.getSampler().dim == glslang::EsdBuffer)
+                builder.addCapability(spv::CapabilityUniformTexelBufferArrayNonUniformIndexingEXT);
+            else if (baseType.isImage())
+                builder.addCapability(spv::CapabilityStorageImageArrayNonUniformIndexingEXT);
+            else if (baseType.isTexture())
+                builder.addCapability(spv::CapabilitySampledImageArrayNonUniformIndexingEXT);
+        } else if (baseType.getBasicType() == glslang::EbtBlock) {
+            if (baseType.getQualifier().storage == glslang::EvqBuffer)
+                builder.addCapability(spv::CapabilityStorageBufferArrayNonUniformIndexingEXT);
+            else if (baseType.getQualifier().storage == glslang::EvqUniform)
+                builder.addCapability(spv::CapabilityUniformBufferArrayNonUniformIndexingEXT);
+        }
+    } else {
+        // assume a dynamically uniform index
+        if (baseType.getBasicType() == glslang::EbtSampler) {
+            if (baseType.getQualifier().hasAttachment())
+                builder.addCapability(spv::CapabilityInputAttachmentArrayDynamicIndexingEXT);
+            else if (baseType.isImage() && baseType.getSampler().dim == glslang::EsdBuffer)
+                builder.addCapability(spv::CapabilityStorageTexelBufferArrayDynamicIndexingEXT);
+            else if (baseType.isTexture() && baseType.getSampler().dim == glslang::EsdBuffer)
+                builder.addCapability(spv::CapabilityUniformTexelBufferArrayDynamicIndexingEXT);
+        }
+    }
+}
+
 // Return whether or not the given type is something that should be tied to a
 // descriptor set.
 bool IsDescriptorResource(const glslang::TType& type)
@@ -1286,7 +1336,8 @@
 
                 // do the operation
                 OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()),
-                                              TranslateNoContractionDecoration(node->getType().getQualifier()) };
+                                              TranslateNoContractionDecoration(node->getType().getQualifier()),
+                                              TranslateNonUniformDecoration(node->getType().getQualifier()) };
                 rValue = createBinaryOperation(node->getOp(), decorations,
                                                convertGlslangToSpvType(node->getType()), leftRValue, rValue,
                                                node->getType().getBasicType());
@@ -1362,6 +1413,8 @@
             node->getRight()->traverse(this);
             spv::Id index = accessChainLoad(node->getRight()->getType());
 
+            addIndirectionIndexCapabilities(node->getLeft()->getType(), node->getRight()->getType());
+
             // restore the saved access chain
             builder.setAccessChain(partial);
 
@@ -1415,7 +1468,8 @@
 
     // get result
     OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()),
-                                  TranslateNoContractionDecoration(node->getType().getQualifier()) };
+                                  TranslateNoContractionDecoration(node->getType().getQualifier()),
+                                  TranslateNonUniformDecoration(node->getType().getQualifier()) };
     spv::Id result = createBinaryOperation(node->getOp(), decorations,
                                            convertGlslangToSpvType(node->getType()), left, right,
                                            node->getLeft()->getType().getBasicType());
@@ -1454,6 +1508,11 @@
     if (node->getOp() == glslang::EOpArrayLength) {
         // Quite special; won't want to evaluate the operand.
 
+        // Currently, the front-end does not allow .length() on an array until it is sized,
+        // except for the last block membeor of an SSBO.
+        // TODO: If this changes, link-time sized arrays might show up here, and need their
+        // size extracted.
+
         // Normal .length() would have been constant folded by the front-end.
         // So, this has to be block.lastMember.length().
         // SPV wants "block" and member number as the operands, go get them.
@@ -1495,7 +1554,8 @@
         operand = accessChainLoad(node->getOperand()->getType());
 
     OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()),
-                                  TranslateNoContractionDecoration(node->getType().getQualifier()) };
+                                  TranslateNoContractionDecoration(node->getType().getQualifier()),
+                                  TranslateNonUniformDecoration(node->getType().getQualifier()) };
 
     // it could be a conversion
     if (! result)
@@ -1506,8 +1566,10 @@
         result = createUnaryOperation(node->getOp(), decorations, resultType(), operand, node->getOperand()->getBasicType());
 
     if (result) {
-        if (invertedType)
+        if (invertedType) {
             result = createInvertedSwizzle(decorations.precision, *node->getOperand(), result);
+            builder.addDecoration(result, decorations.nonUniform);
+        }
 
         builder.clearAccessChain();
         builder.setAccessChainRValue(result);
@@ -1934,7 +1996,8 @@
 
         builder.setLine(node->getLoc().line);
         OpDecorations decorations = { precision,
-                                      TranslateNoContractionDecoration(node->getType().getQualifier()) };
+                                      TranslateNoContractionDecoration(node->getType().getQualifier()),
+                                      TranslateNonUniformDecoration(node->getType().getQualifier()) };
         result = createBinaryOperation(binOp, decorations,
                                        resultType(), leftId, rightId,
                                        left->getType().getBasicType(), reduceComparison);
@@ -2035,7 +2098,8 @@
         case 1:
             {
                 OpDecorations decorations = { precision, 
-                                              TranslateNoContractionDecoration(node->getType().getQualifier()) };
+                                              TranslateNoContractionDecoration(node->getType().getQualifier()),
+                                              TranslateNonUniformDecoration(node->getType().getQualifier()) };
                 result = createUnaryOperation(
                     node->getOp(), decorations,
                     resultType(), operands.front(),
@@ -2651,8 +2715,13 @@
         // (Unsized arrays that survive through linking will be runtime-sized arrays)
         if (type.isSizedArray())
             spvType = builder.makeArrayType(spvType, makeArraySizeId(*type.getArraySizes(), 0), stride);
-        else
+        else {
+            if (!lastBufferBlockMember) {
+                builder.addExtension("SPV_EXT_descriptor_indexing");
+                builder.addCapability(spv::CapabilityRuntimeDescriptorArrayEXT);
+            }
             spvType = builder.makeRuntimeArray(spvType);
+        }
         if (stride > 0)
             builder.addDecoration(spvType, spv::DecorationArrayStride, stride);
     }
@@ -2824,6 +2893,9 @@
         if (builtIn != spv::BuiltInMax)
             builder.addMemberDecoration(spvType, member, spv::DecorationBuiltIn, (int)builtIn);
 
+        // nonuniform
+        builder.addMemberDecoration(spvType, member, TranslateNonUniformDecoration(glslangMember.getQualifier()));
+
         if (glslangIntermediate->getHlslFunctionality1() && memberQualifier.semanticName != nullptr) {
             builder.addExtension("SPV_GOOGLE_hlsl_functionality1");
             builder.addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationHlslSemanticGOOGLE,
@@ -2891,7 +2963,8 @@
 spv::Id TGlslangToSpvTraverser::accessChainLoad(const glslang::TType& type)
 {
     spv::Id nominalTypeId = builder.accessChainGetInferredType();
-    spv::Id loadedId = builder.accessChainLoad(TranslatePrecisionDecoration(type), nominalTypeId);
+    spv::Id loadedId = builder.accessChainLoad(TranslatePrecisionDecoration(type),
+                                               TranslateNonUniformDecoration(type.getQualifier()), nominalTypeId);
 
     // Need to convert to abstract types when necessary
     if (type.getBasicType() == glslang::EbtBool) {
@@ -4102,6 +4175,7 @@
 
         spv::Id result = builder.createBinOp(binOp, typeId, left, right);
         builder.addDecoration(result, decorations.noContraction);
+        builder.addDecoration(result, decorations.nonUniform);
         return builder.setPrecision(result, decorations.precision);
     }
 
@@ -4113,6 +4187,7 @@
     if (reduceComparison && (op == glslang::EOpEqual || op == glslang::EOpNotEqual)
                          && (builder.isVector(left) || builder.isMatrix(left) || builder.isAggregate(left))) {
         spv::Id result = builder.createCompositeCompare(decorations.precision, left, right, op == glslang::EOpEqual);
+        builder.addDecoration(result, decorations.nonUniform);
         return result;
     }
 
@@ -4174,6 +4249,7 @@
     if (binOp != spv::OpNop) {
         spv::Id result = builder.createBinOp(binOp, typeId, left, right);
         builder.addDecoration(result, decorations.noContraction);
+        builder.addDecoration(result, decorations.nonUniform);
         return builder.setPrecision(result, decorations.precision);
     }
 
@@ -4235,6 +4311,7 @@
     if (firstClass) {
         spv::Id result = builder.createBinOp(op, typeId, left, right);
         builder.addDecoration(result, decorations.noContraction);
+        builder.addDecoration(result, decorations.nonUniform);
         return builder.setPrecision(result, decorations.precision);
     }
 
@@ -4274,11 +4351,13 @@
             spv::Id rightVec = rightMat ? builder.createCompositeExtract(right, vecType, indexes) : smearVec;
             spv::Id result = builder.createBinOp(op, vecType, leftVec, rightVec);
             builder.addDecoration(result, decorations.noContraction);
+            builder.addDecoration(result, decorations.nonUniform);
             results.push_back(builder.setPrecision(result, decorations.precision));
         }
 
         // put the pieces together
         spv::Id result = builder.setPrecision(builder.createCompositeConstruct(typeId, results), decorations.precision);
+        builder.addDecoration(result, decorations.nonUniform);
         return result;
     }
     default:
@@ -4687,6 +4766,7 @@
     }
 
     builder.addDecoration(id, decorations.noContraction);
+    builder.addDecoration(id, decorations.nonUniform);
     return builder.setPrecision(id, decorations.precision);
 }
 
@@ -4715,11 +4795,13 @@
         spv::Id srcVec  = builder.createCompositeExtract(operand, srcVecType, indexes);
         spv::Id destVec = builder.createUnaryOp(op, destVecType, srcVec);
         builder.addDecoration(destVec, decorations.noContraction);
+        builder.addDecoration(destVec, decorations.nonUniform);
         results.push_back(builder.setPrecision(destVec, decorations.precision));
     }
 
     // put the pieces together
     spv::Id result = builder.setPrecision(builder.createCompositeConstruct(typeId, results), decorations.precision);
+    builder.addDecoration(result, decorations.nonUniform);
     return result;
 }
 
@@ -5177,6 +5259,7 @@
         result = builder.createUnaryOp(convOp, destType, operand);
 
     result = builder.setPrecision(result, decorations.precision);
+    builder.addDecoration(result, decorations.nonUniform);
     return result;
 }
 
@@ -6396,6 +6479,9 @@
     if (builtIn != spv::BuiltInMax)
         builder.addDecoration(id, spv::DecorationBuiltIn, (int)builtIn);
 
+    // nonuniform
+    builder.addDecoration(id, TranslateNonUniformDecoration(symbol->getType().getQualifier()));
+
 #ifdef NV_EXTENSIONS
     if (builtIn == spv::BuiltInSampleMask) {
           spv::Decoration decoration;
diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp
index 1660787..27ce71c 100644
--- a/SPIRV/SpvBuilder.cpp
+++ b/SPIRV/SpvBuilder.cpp
@@ -2331,7 +2331,7 @@
 }
 
 // Comments in header
-Id Builder::accessChainLoad(Decoration precision, Id resultType)
+Id Builder::accessChainLoad(Decoration precision, Decoration nonUniform, Id resultType)
 {
     Id id;
 
@@ -2377,6 +2377,7 @@
         // load through the access chain
         id = createLoad(collapseAccessChain());
         setPrecision(id, precision);
+        addDecoration(id, nonUniform);
     }
 
     // Done, unless there are swizzles to do
@@ -2397,6 +2398,7 @@
     if (accessChain.component != NoResult)
         id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision);
 
+    addDecoration(id, nonUniform);
     return id;
 }
 
diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h
index dc76dae..099b1d9 100755
--- a/SPIRV/SpvBuilder.h
+++ b/SPIRV/SpvBuilder.h
@@ -554,7 +554,7 @@
     void accessChainStore(Id rvalue);
 
     // use accessChain and swizzle to load an r-value
-    Id accessChainLoad(Decoration precision, Id ResultType);
+    Id accessChainLoad(Decoration precision, Decoration nonUniform, Id ResultType);
 
     // get the direct pointer for an l-value
     Id accessChainGetLValue();
diff --git a/SPIRV/doc.cpp b/SPIRV/doc.cpp
index 9317d50..a905968 100755
--- a/SPIRV/doc.cpp
+++ b/SPIRV/doc.cpp
@@ -255,6 +255,7 @@
     case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV";
 #endif
 
+    case DecorationNonUniformEXT:           return "DecorationNonUniformEXT";
     case DecorationHlslCounterBufferGOOGLE: return "DecorationHlslCounterBufferGOOGLE";
     case DecorationHlslSemanticGOOGLE:      return "DecorationHlslSemanticGOOGLE";
     }
@@ -815,6 +816,19 @@
 
     case CapabilityFragmentFullyCoveredEXT: return "FragmentFullyCoveredEXT";
 
+    case CapabilityShaderNonUniformEXT:                          return "CapabilityShaderNonUniformEXT";
+    case CapabilityRuntimeDescriptorArrayEXT:                    return "CapabilityRuntimeDescriptorArrayEXT";
+    case CapabilityInputAttachmentArrayDynamicIndexingEXT:       return "CapabilityInputAttachmentArrayDynamicIndexingEXT";
+    case CapabilityUniformTexelBufferArrayDynamicIndexingEXT:    return "CapabilityUniformTexelBufferArrayDynamicIndexingEXT";
+    case CapabilityStorageTexelBufferArrayDynamicIndexingEXT:    return "CapabilityStorageTexelBufferArrayDynamicIndexingEXT";
+    case CapabilityUniformBufferArrayNonUniformIndexingEXT:      return "CapabilityUniformBufferArrayNonUniformIndexingEXT";
+    case CapabilitySampledImageArrayNonUniformIndexingEXT:       return "CapabilitySampledImageArrayNonUniformIndexingEXT";
+    case CapabilityStorageBufferArrayNonUniformIndexingEXT:      return "CapabilityStorageBufferArrayNonUniformIndexingEXT";
+    case CapabilityStorageImageArrayNonUniformIndexingEXT:       return "CapabilityStorageImageArrayNonUniformIndexingEXT";
+    case CapabilityInputAttachmentArrayNonUniformIndexingEXT:    return "CapabilityInputAttachmentArrayNonUniformIndexingEXT";
+    case CapabilityUniformTexelBufferArrayNonUniformIndexingEXT: return "CapabilityUniformTexelBufferArrayNonUniformIndexingEXT";
+    case CapabilityStorageTexelBufferArrayNonUniformIndexingEXT: return "CapabilityStorageTexelBufferArrayNonUniformIndexingEXT";
+
     default: return "Bad";
     }
 }
diff --git a/SPIRV/spirv.hpp b/SPIRV/spirv.hpp
old mode 100755
new mode 100644
index 3c599f1..e21762d
--- a/SPIRV/spirv.hpp
+++ b/SPIRV/spirv.hpp
@@ -393,6 +393,7 @@
     DecorationPassthroughNV = 5250,
     DecorationViewportRelativeNV = 5252,
     DecorationSecondaryViewportRelativeNV = 5256,
+    DecorationNonUniformEXT = 5300,
     DecorationHlslCounterBufferGOOGLE = 5634,
     DecorationHlslSemanticGOOGLE = 5635,
     DecorationMax = 0x7fffffff,
@@ -692,6 +693,18 @@
     CapabilityPerViewAttributesNV = 5260,
     CapabilityFragmentFullyCoveredEXT = 5265,
     CapabilityGroupNonUniformPartitionedNV = 5297,
+    CapabilityShaderNonUniformEXT = 5301,
+    CapabilityRuntimeDescriptorArrayEXT = 5302,
+    CapabilityInputAttachmentArrayDynamicIndexingEXT = 5303,
+    CapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304,
+    CapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305,
+    CapabilityUniformBufferArrayNonUniformIndexingEXT = 5306,
+    CapabilitySampledImageArrayNonUniformIndexingEXT = 5307,
+    CapabilityStorageBufferArrayNonUniformIndexingEXT = 5308,
+    CapabilityStorageImageArrayNonUniformIndexingEXT = 5309,
+    CapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310,
+    CapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311,
+    CapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312,
     CapabilitySubgroupShuffleINTEL = 5568,
     CapabilitySubgroupBufferBlockIOINTEL = 5569,
     CapabilitySubgroupImageBlockIOINTEL = 5570,