Add support for pragma STDGL invariant(all)

Fixes #2689
diff --git a/Test/baseResults/spv.invariantAll.vert.out b/Test/baseResults/spv.invariantAll.vert.out
new file mode 100644
index 0000000..ec5ad30
--- /dev/null
+++ b/Test/baseResults/spv.invariantAll.vert.out
@@ -0,0 +1,55 @@
+spv.invariantAll.vert
+// Module Version 10000
+// Generated by (magic number): 8000a
+// Id's are bound by 25
+
+                              Capability Shader
+               1:             ExtInstImport  "GLSL.std.450"
+                              MemoryModel Logical GLSL450
+                              EntryPoint Vertex 4  "main" 13 17
+                              Source GLSL 450
+                              Name 4  "main"
+                              Name 11  "gl_PerVertex"
+                              MemberName 11(gl_PerVertex) 0  "gl_Position"
+                              MemberName 11(gl_PerVertex) 1  "gl_PointSize"
+                              MemberName 11(gl_PerVertex) 2  "gl_ClipDistance"
+                              MemberName 11(gl_PerVertex) 3  "gl_CullDistance"
+                              Name 13  ""
+                              Name 17  "v"
+                              MemberDecorate 11(gl_PerVertex) 0 Invariant
+                              MemberDecorate 11(gl_PerVertex) 0 BuiltIn Position
+                              MemberDecorate 11(gl_PerVertex) 1 Invariant
+                              MemberDecorate 11(gl_PerVertex) 1 BuiltIn PointSize
+                              MemberDecorate 11(gl_PerVertex) 2 Invariant
+                              MemberDecorate 11(gl_PerVertex) 2 BuiltIn ClipDistance
+                              MemberDecorate 11(gl_PerVertex) 3 Invariant
+                              MemberDecorate 11(gl_PerVertex) 3 BuiltIn CullDistance
+                              Decorate 11(gl_PerVertex) Block
+                              Decorate 17(v) Location 0
+                              Decorate 17(v) Invariant
+               2:             TypeVoid
+               3:             TypeFunction 2
+               6:             TypeFloat 32
+               7:             TypeVector 6(float) 4
+               8:             TypeInt 32 0
+               9:      8(int) Constant 1
+              10:             TypeArray 6(float) 9
+11(gl_PerVertex):             TypeStruct 7(fvec4) 6(float) 10 10
+              12:             TypePointer Output 11(gl_PerVertex)
+              13:     12(ptr) Variable Output
+              14:             TypeInt 32 1
+              15:     14(int) Constant 0
+              16:             TypePointer Output 6(float)
+           17(v):     16(ptr) Variable Output
+              20:    6(float) Constant 0
+              21:    6(float) Constant 1065353216
+              23:             TypePointer Output 7(fvec4)
+         4(main):           2 Function None 3
+               5:             Label
+              18:    6(float) Load 17(v)
+              19:    6(float) Load 17(v)
+              22:    7(fvec4) CompositeConstruct 18 19 20 21
+              24:     23(ptr) AccessChain 13 15
+                              Store 24 22
+                              Return
+                              FunctionEnd
diff --git a/Test/spv.invariantAll.vert b/Test/spv.invariantAll.vert
new file mode 100644
index 0000000..e094aa0
--- /dev/null
+++ b/Test/spv.invariantAll.vert
@@ -0,0 +1,10 @@
+#version 450 core
+#pragma STDGL invariant(all)
+
+layout(location=0) out highp float v;
+
+void main()
+{
+    gl_Position = vec4(v, v, 0, 1);
+}
+
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index c2b9edc..f1c7d84 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -327,6 +327,16 @@
     block.getQualifier().layoutMatrix = ElmRowMajor;
 }
 
+void TParseContext::setInvariant(const TSourceLoc& loc, const char* builtin) {
+    TSymbol* symbol = symbolTable.find(builtin);
+    if (symbol && symbol->getType().getQualifier().isPipeOutput()) {
+        if (intermediate.inIoAccessed(builtin))
+            warn(loc, "changing qualification after use", "invariant", builtin);
+        TSymbol* csymbol = symbolTable.copyUp(symbol);
+        csymbol->getWritableType().getQualifier().invariant = true;
+    }
+}
+
 void TParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens)
 {
 #ifndef GLSLANG_WEB
@@ -404,8 +414,33 @@
         intermediate.setUseVariablePointers();
     } else if (tokens[0].compare("once") == 0) {
         warn(loc, "not implemented", "#pragma once", "");
-    } else if (tokens[0].compare("glslang_binary_double_output") == 0)
+    } else if (tokens[0].compare("glslang_binary_double_output") == 0) {
         intermediate.setBinaryDoubleOutput();
+    } else if (spvVersion.spv > 0 && tokens[0].compare("STDGL") == 0 &&
+               tokens[1].compare("invariant") == 0 && tokens[3].compare("all") == 0) {
+        intermediate.setInvariantAll();
+        // Set all builtin out variables invariant if declared
+        setInvariant(loc, "gl_Position");
+        setInvariant(loc, "gl_PointSize");
+        setInvariant(loc, "gl_ClipDistance");
+        setInvariant(loc, "gl_CullDistance");
+        setInvariant(loc, "gl_TessLevelOuter");
+        setInvariant(loc, "gl_TessLevelInner");
+        setInvariant(loc, "gl_PrimitiveID");
+        setInvariant(loc, "gl_Layer");
+        setInvariant(loc, "gl_ViewportIndex");
+        setInvariant(loc, "gl_FragDepth");
+        setInvariant(loc, "gl_SampleMask");
+        setInvariant(loc, "gl_ClipVertex");
+        setInvariant(loc, "gl_FrontColor");
+        setInvariant(loc, "gl_BackColor");
+        setInvariant(loc, "gl_FrontSecondaryColor");
+        setInvariant(loc, "gl_BackSecondaryColor");
+        setInvariant(loc, "gl_TexCoord");
+        setInvariant(loc, "gl_FogFragCoord");
+        setInvariant(loc, "gl_FragColor");
+        setInvariant(loc, "gl_FragData");
+    }
 #endif
 }
 
@@ -3651,6 +3686,8 @@
         profileRequires(loc, ENoProfile, 130, nullptr, "out for stage outputs");
         profileRequires(loc, EEsProfile, 300, nullptr, "out for stage outputs");
         qualifier.storage = EvqVaryingOut;
+        if (intermediate.isInvariantAll())
+            qualifier.invariant = true;
         break;
     case EvqInOut:
         qualifier.storage = EvqVaryingIn;
diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h
index c9e6334..de44884 100644
--- a/glslang/MachineIndependent/ParseHelper.h
+++ b/glslang/MachineIndependent/ParseHelper.h
@@ -241,6 +241,7 @@
     // override this to set the language-specific name
     virtual const char* getAtomicCounterBlockName() const { return ""; }
     virtual void setAtomicCounterBlockDefaults(TType&) const {}
+    virtual void setInvariant(const TSourceLoc& loc, const char* builtin) {}
     virtual void finalizeAtomicCounterBlockLayout(TVariable&) {}
     bool isAtomicCounterBlock(const TSymbol& symbol) {
         const TVariable* var = symbol.getAsVariable();
@@ -511,6 +512,7 @@
     virtual const char* getAtomicCounterBlockName() const override;
     virtual void finalizeAtomicCounterBlockLayout(TVariable&) override;
     virtual void setAtomicCounterBlockDefaults(TType& block) const override;
+    virtual void setInvariant(const TSourceLoc& loc, const char* builtin) override;
 
 public:
     //
diff --git a/glslang/MachineIndependent/linkValidate.cpp b/glslang/MachineIndependent/linkValidate.cpp
index 42b416d..9656e2e 100644
--- a/glslang/MachineIndependent/linkValidate.cpp
+++ b/glslang/MachineIndependent/linkValidate.cpp
@@ -316,6 +316,7 @@
     MERGE_TRUE(useUnknownFormat);
     MERGE_TRUE(hlslOffsets);
     MERGE_TRUE(useStorageBuffer);
+    MERGE_TRUE(invariantAll);
     MERGE_TRUE(hlslIoMapping);
 
     // TODO: sourceFile
diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h
index 5cc9930..9e8af38 100644
--- a/glslang/MachineIndependent/localintermediate.h
+++ b/glslang/MachineIndependent/localintermediate.h
@@ -291,6 +291,7 @@
         numEntryPoints(0), numErrors(0), numPushConstants(0), recursive(false),
         invertY(false),
         useStorageBuffer(false),
+        invariantAll(false),
         nanMinMaxClamp(false),
         depthReplacing(false),
         uniqueId(0),
@@ -560,6 +561,8 @@
 
     void setUseStorageBuffer() { useStorageBuffer = true; }
     bool usingStorageBuffer() const { return useStorageBuffer; }
+    void setInvariantAll() { invariantAll = true; }
+    bool isInvariantAll() const { return invariantAll; }
     void setDepthReplacing() { depthReplacing = true; }
     bool isDepthReplacing() const { return depthReplacing; }
     bool setLocalSize(int dim, int size)
@@ -1063,6 +1066,7 @@
     bool recursive;
     bool invertY;
     bool useStorageBuffer;
+    bool invariantAll;
     bool nanMinMaxClamp;            // true if desiring min/max/clamp to favor non-NaN over NaN
     bool depthReplacing;
     int localSize[3];
diff --git a/gtests/Spv.FromFile.cpp b/gtests/Spv.FromFile.cpp
index 20683a6..3746557 100644
--- a/gtests/Spv.FromFile.cpp
+++ b/gtests/Spv.FromFile.cpp
@@ -363,6 +363,7 @@
         "spv.intrinsicsSpirvLiteral.vert",
         "spv.intrinsicsSpirvStorageClass.rchit",
         "spv.intrinsicsSpirvType.rgen",
+        "spv.invariantAll.vert",
         "spv.layer.tese",
         "spv.layoutNested.vert",
         "spv.length.frag",