SPV: Generalize multiple struct-type instances for interpolation/invariant qualifiers.
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index afea3bc..654fe87 100755
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -91,7 +91,7 @@
     spv::Id createSpvVariable(const glslang::TIntermSymbol*);
     spv::Id getSampledType(const glslang::TSampler&);
     spv::Id convertGlslangToSpvType(const glslang::TType& type);
-    spv::Id convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking, glslang::TLayoutMatrix);
+    spv::Id convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking, const glslang::TQualifier&);
     glslang::TLayoutPacking getExplicitLayout(const glslang::TType& type) const;
     int getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking, glslang::TLayoutMatrix);
     int getMatrixStride(const glslang::TType& matrixType, glslang::TLayoutPacking, glslang::TLayoutMatrix);
@@ -299,30 +299,30 @@
 // Translate glslang type to SPIR-V interpolation decorations.
 // Returns spv::Decoration(spv::BadValue) when no decoration
 // should be applied.
-spv::Decoration TranslateInterpolationDecoration(const glslang::TType& type)
+spv::Decoration TranslateInterpolationDecoration(const glslang::TQualifier& qualifier)
 {
-    if (type.getQualifier().smooth) {
+    if (qualifier.smooth) {
         // Smooth decoration doesn't exist in SPIR-V 1.0
         return (spv::Decoration)spv::BadValue;
     }
-    if (type.getQualifier().nopersp)
+    if (qualifier.nopersp)
         return spv::DecorationNoPerspective;
-    else if (type.getQualifier().patch)
+    else if (qualifier.patch)
         return spv::DecorationPatch;
-    else if (type.getQualifier().flat)
+    else if (qualifier.flat)
         return spv::DecorationFlat;
-    else if (type.getQualifier().centroid)
+    else if (qualifier.centroid)
         return spv::DecorationCentroid;
-    else if (type.getQualifier().sample)
+    else if (qualifier.sample)
         return spv::DecorationSample;
     else
         return (spv::Decoration)spv::BadValue;
 }
 
 // If glslang type is invaraiant, return SPIR-V invariant decoration.
-spv::Decoration TranslateInvariantDecoration(const glslang::TType& type)
+spv::Decoration TranslateInvariantDecoration(const glslang::TQualifier& qualifier)
 {
-    if (type.getQualifier().invariant)
+    if (qualifier.invariant)
         return spv::DecorationInvariant;
     else
         return (spv::Decoration)spv::BadValue;
@@ -420,6 +420,34 @@
     }
 }
 
+void InheritQualifiers(glslang::TQualifier& child, const glslang::TQualifier& parent)
+{
+    if (child.layoutMatrix == glslang::ElmNone)
+        child.layoutMatrix = parent.layoutMatrix;
+
+    if (parent.invariant)
+        child.invariant = true;
+    if (parent.nopersp)
+        child.nopersp = true;
+    if (parent.flat)
+        child.flat = true;
+    if (parent.centroid)
+        child.centroid = true;
+    if (parent.patch)
+        child.patch = true;
+    if (parent.sample)
+        child.sample = true;
+}
+
+bool HasNonLayoutQualifiers(const glslang::TQualifier& qualifier)
+{
+    // This should list qualifiers that simultaneous satisify:
+    // - struct members can inherit from a struct declaration
+    // - effect decorations on the struct members (note smooth does not, and expecting something like volatile to effect the whole object)
+    // - are not part of the offset/st430/etc or row/column-major layout
+    return qualifier.invariant || qualifier.nopersp || qualifier.flat || qualifier.centroid || qualifier.patch || qualifier.sample;
+}
+
 //
 // Implement the TGlslangToSpvTraverser class.
 //
@@ -1467,14 +1495,14 @@
 // layout state rooted from the top-level type.
 spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type)
 {
-    return convertGlslangToSpvType(type, getExplicitLayout(type), type.getQualifier().layoutMatrix);
+    return convertGlslangToSpvType(type, getExplicitLayout(type), type.getQualifier());
 }
 
 // Do full recursive conversion of an arbitrary glslang type to a SPIR-V Id.
 // explicitLayout can be kept the same throughout the heirarchical recursive walk.
-spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking explicitLayout, glslang::TLayoutMatrix matrixLayout)
+spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking explicitLayout, const glslang::TQualifier& qualifier)
 {
-    spv::Id spvType = 0;
+    spv::Id spvType = spv::NoResult;
 
     switch (type.getBasicType()) {
     case glslang::EbtVoid:
@@ -1503,13 +1531,13 @@
     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));
+            // 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);
-                }
+                spvType = builder.makeSampledImageType(spvType);
             }
+        }
         break;
     case glslang::EbtStruct:
     case glslang::EbtBlock:
@@ -1517,8 +1545,12 @@
             // If we've seen this struct type, return it
             const glslang::TTypeList* glslangStruct = type.getStruct();
             std::vector<spv::Id> structFields;
-            spvType = structMap[explicitLayout][matrixLayout][glslangStruct];
-            if (spvType)
+
+            // Try to share structs for different layouts, but not yet for other
+            // kinds of qualification (primarily not yet including interpolant qualification).
+            if (! HasNonLayoutQualifiers(qualifier))
+                spvType = structMap[explicitLayout][qualifier.layoutMatrix][glslangStruct];
+            if (spvType != spv::NoResult)
                 break;
 
             // else, we haven't seen it...
@@ -1536,16 +1568,17 @@
                 } else {
                     if (type.getBasicType() == glslang::EbtBlock)
                         memberRemapper[glslangStruct][i] = i - memberDelta;
-                    // modify just the children's view of matrix layout, if there is one for this member
-                    glslang::TLayoutMatrix subMatrixLayout = glslangType.getQualifier().layoutMatrix;
-                    structFields.push_back(convertGlslangToSpvType(glslangType, explicitLayout,
-                                           subMatrixLayout != glslang::ElmNone ? subMatrixLayout : matrixLayout));
+                    // modify just this child's view of the qualifier
+                    glslang::TQualifier subQualifier = glslangType.getQualifier();
+                    InheritQualifiers(subQualifier, qualifier);
+                    structFields.push_back(convertGlslangToSpvType(glslangType, explicitLayout, subQualifier));
                 }
             }
 
             // Make the SPIR-V type
             spvType = builder.makeStructType(structFields, type.getTypeName().c_str());
-            structMap[explicitLayout][matrixLayout][glslangStruct] = spvType;
+            if (! HasNonLayoutQualifiers(qualifier))
+                structMap[explicitLayout][qualifier.layoutMatrix][glslangStruct] = spvType;
 
             // Name and decorate the non-hidden members
             int offset = -1;
@@ -1555,18 +1588,17 @@
                 if (type.getBasicType() == glslang::EbtBlock)
                     member = memberRemapper[glslangStruct][i];
 
-                // modify just the children's view of matrix layout, if there is one for this member
-                glslang::TLayoutMatrix subMatrixLayout = glslangType.getQualifier().layoutMatrix;
-                if (subMatrixLayout == glslang::ElmNone)
-                    subMatrixLayout = matrixLayout;
+                // modify just this child's view of the qualifier
+                glslang::TQualifier subQualifier = glslangType.getQualifier();
+                InheritQualifiers(subQualifier, qualifier);
 
                 // using -1 above to indicate a hidden member
                 if (member >= 0) {
                     builder.addMemberName(spvType, member, glslangType.getFieldName().c_str());
-                    addMemberDecoration(spvType, member, TranslateLayoutDecoration(glslangType, subMatrixLayout));
+                    addMemberDecoration(spvType, member, TranslateLayoutDecoration(glslangType, subQualifier.layoutMatrix));
                     addMemberDecoration(spvType, member, TranslatePrecisionDecoration(glslangType));
-                    addMemberDecoration(spvType, member, TranslateInterpolationDecoration(glslangType));
-                    addMemberDecoration(spvType, member, TranslateInvariantDecoration(glslangType));
+                    addMemberDecoration(spvType, member, TranslateInterpolationDecoration(subQualifier));
+                    addMemberDecoration(spvType, member, TranslateInvariantDecoration(subQualifier));
                     if (glslangType.getQualifier().hasLocation())
                         builder.addMemberDecoration(spvType, member, spv::DecorationLocation, glslangType.getQualifier().layoutLocation);
                     if (glslangType.getQualifier().hasComponent())
@@ -1576,14 +1608,14 @@
                     else if (explicitLayout != glslang::ElpNone) {
                         // figure out what to do with offset, which is accumulating
                         int nextOffset;
-                        updateMemberOffset(type, glslangType, offset, nextOffset, explicitLayout, subMatrixLayout);
+                        updateMemberOffset(type, glslangType, offset, nextOffset, explicitLayout, subQualifier.layoutMatrix);
                         if (offset >= 0)
                             builder.addMemberDecoration(spvType, member, spv::DecorationOffset, offset);
                         offset = nextOffset;
                     }
 
                     if (glslangType.isMatrix() && explicitLayout != glslang::ElpNone)
-                        builder.addMemberDecoration(spvType, member, spv::DecorationMatrixStride, getMatrixStride(glslangType, explicitLayout, subMatrixLayout));
+                        builder.addMemberDecoration(spvType, member, spv::DecorationMatrixStride, getMatrixStride(glslangType, explicitLayout, subQualifier.layoutMatrix));
 
                     // built-in variable decorations
                     spv::BuiltIn builtIn = TranslateBuiltInDecoration(glslangType.getQualifier().builtIn);
@@ -1593,7 +1625,7 @@
             }
 
             // Decorate the structure
-            addDecoration(spvType, TranslateLayoutDecoration(type, matrixLayout));
+            addDecoration(spvType, TranslateLayoutDecoration(type, qualifier.layoutMatrix));
             addDecoration(spvType, TranslateBlockDecoration(type));
             if (type.getQualifier().hasStream())
                 builder.addDecoration(spvType, spv::DecorationStream, type.getQualifier().layoutStream);
@@ -1641,7 +1673,7 @@
         // except for the very top if it is an array of blocks; that array is
         // not laid out in memory in a way needing a stride.
         if (explicitLayout && type.getBasicType() != glslang::EbtBlock)
-            builder.addDecoration(spvType, spv::DecorationArrayStride, getArrayStride(type, explicitLayout, matrixLayout));
+            builder.addDecoration(spvType, spv::DecorationArrayStride, getArrayStride(type, explicitLayout, qualifier.layoutMatrix));
     }
 
     return spvType;
@@ -3146,7 +3178,7 @@
 
     if (! symbol->getType().isStruct()) {
         addDecoration(id, TranslatePrecisionDecoration(symbol->getType()));
-        addDecoration(id, TranslateInterpolationDecoration(symbol->getType()));
+        addDecoration(id, TranslateInterpolationDecoration(symbol->getType().getQualifier()));
         if (symbol->getQualifier().hasLocation())
             builder.addDecoration(id, spv::DecorationLocation, symbol->getQualifier().layoutLocation);
         if (symbol->getQualifier().hasIndex())
@@ -3163,7 +3195,7 @@
         }
     }
 
-    addDecoration(id, TranslateInvariantDecoration(symbol->getType()));
+    addDecoration(id, TranslateInvariantDecoration(symbol->getType().getQualifier()));
     if (symbol->getQualifier().hasStream())
         builder.addDecoration(id, spv::DecorationStream, symbol->getQualifier().layoutStream);
     if (symbol->getQualifier().hasSet())