Jelajahi Sumber

Updated glslang.

Бранимир Караџић 4 tahun lalu
induk
melakukan
0260fe6204

+ 4 - 6
3rdparty/glslang/SPIRV/GlslangToSpv.cpp

@@ -4400,7 +4400,6 @@ void TGlslangToSpvTraverser::decorateStructType(const glslang::TType& type,
 {
 {
     // Name and decorate the non-hidden members
     // Name and decorate the non-hidden members
     int offset = -1;
     int offset = -1;
-    int locationOffset = 0;  // for use within the members of this struct
     bool memberLocationInvalid = type.isArrayOfArrays() ||
     bool memberLocationInvalid = type.isArrayOfArrays() ||
         (type.isArray() && (type.getQualifier().isArrayedIo(glslangIntermediate->getStage()) == false));
         (type.isArray() && (type.getQualifier().isArrayedIo(glslangIntermediate->getStage()) == false));
     for (int i = 0; i < (int)glslangMembers->size(); i++) {
     for (int i = 0; i < (int)glslangMembers->size(); i++) {
@@ -4458,10 +4457,6 @@ void TGlslangToSpvTraverser::decorateStructType(const glslang::TType& type,
         if (!memberLocationInvalid && memberQualifier.hasLocation())
         if (!memberLocationInvalid && memberQualifier.hasLocation())
             builder.addMemberDecoration(spvType, member, spv::DecorationLocation, memberQualifier.layoutLocation);
             builder.addMemberDecoration(spvType, member, spv::DecorationLocation, memberQualifier.layoutLocation);
 
 
-        if (qualifier.hasLocation())      // track for upcoming inheritance
-            locationOffset += glslangIntermediate->computeTypeLocationSize(
-                                            glslangMember, glslangIntermediate->getStage());
-
         // component, XFB, others
         // component, XFB, others
         if (glslangMember.getQualifier().hasComponent())
         if (glslangMember.getQualifier().hasComponent())
             builder.addMemberDecoration(spvType, member, spv::DecorationComponent,
             builder.addMemberDecoration(spvType, member, spv::DecorationComponent,
@@ -5322,7 +5317,10 @@ spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermO
 
 
     int components = node->getType().getVectorSize();
     int components = node->getType().getVectorSize();
 
 
-    if (node->getOp() == glslang::EOpTextureFetch) {
+    if (node->getOp() == glslang::EOpImageLoad ||
+        node->getOp() == glslang::EOpImageLoadLod ||
+        node->getOp() == glslang::EOpTextureFetch ||
+        node->getOp() == glslang::EOpTextureFetchOffset) {
         // These must produce 4 components, per SPIR-V spec.  We'll add a conversion constructor if needed.
         // These must produce 4 components, per SPIR-V spec.  We'll add a conversion constructor if needed.
         // This will only happen through the HLSL path for operator[], so we do not have to handle e.g.
         // This will only happen through the HLSL path for operator[], so we do not have to handle e.g.
         // the EOpTexture/Proj/Lod/etc family.  It would be harmless to do so, but would need more logic
         // the EOpTexture/Proj/Lod/etc family.  It would be harmless to do so, but would need more logic

+ 67 - 26
3rdparty/glslang/SPIRV/SpvBuilder.cpp

@@ -743,6 +743,26 @@ Id Builder::getContainedTypeId(Id typeId, int member) const
     }
     }
 }
 }
 
 
+// Figure out the final resulting type of the access chain.
+Id Builder::getResultingAccessChainType() const
+{
+    assert(accessChain.base != NoResult);
+    Id typeId = getTypeId(accessChain.base);
+
+    assert(isPointerType(typeId));
+    typeId = getContainedTypeId(typeId);
+
+    for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {
+        if (isStructType(typeId)) {
+            assert(isConstantScalar(accessChain.indexChain[i]));
+            typeId = getContainedTypeId(typeId, getConstantScalar(accessChain.indexChain[i]));
+        } else
+            typeId = getContainedTypeId(typeId, accessChain.indexChain[i]);
+    }
+
+    return typeId;
+}
+
 // Return the immediately contained type of a given composite type.
 // Return the immediately contained type of a given composite type.
 Id Builder::getContainedTypeId(Id typeId) const
 Id Builder::getContainedTypeId(Id typeId) const
 {
 {
@@ -1585,16 +1605,7 @@ Id Builder::createLoad(Id lValue, spv::Decoration precision, spv::MemoryAccessMa
 Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets)
 Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets)
 {
 {
     // Figure out the final resulting type.
     // Figure out the final resulting type.
-    spv::Id typeId = getTypeId(base);
-    assert(isPointerType(typeId) && offsets.size() > 0);
-    typeId = getContainedTypeId(typeId);
-    for (int i = 0; i < (int)offsets.size(); ++i) {
-        if (isStructType(typeId)) {
-            assert(isConstantScalar(offsets[i]));
-            typeId = getContainedTypeId(typeId, getConstantScalar(offsets[i]));
-        } else
-            typeId = getContainedTypeId(typeId, offsets[i]);
-    }
+    Id typeId = getResultingAccessChainType();
     typeId = makePointer(storageClass, typeId);
     typeId = makePointer(storageClass, typeId);
 
 
     // Make the instruction
     // Make the instruction
@@ -2794,28 +2805,58 @@ void Builder::accessChainStore(Id rvalue, Decoration nonUniform, spv::MemoryAcce
     assert(accessChain.isRValue == false);
     assert(accessChain.isRValue == false);
 
 
     transferAccessChainSwizzle(true);
     transferAccessChainSwizzle(true);
-    Id base = collapseAccessChain();
-    addDecoration(base, nonUniform);
 
 
-    Id source = rvalue;
+    // If a swizzle exists and is not full and is not dynamic, then the swizzle will be broken into individual stores.
+    if (accessChain.swizzle.size() > 0 &&
+        getNumTypeComponents(getResultingAccessChainType()) != (int)accessChain.swizzle.size() &&
+        accessChain.component == NoResult) {
+        for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {
+            accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle[i]));
 
 
-    // dynamic component should be gone
-    assert(accessChain.component == NoResult);
+            Id base = collapseAccessChain();
+            addDecoration(base, nonUniform);
 
 
-    // If swizzle still exists, it is out-of-order or not full, we must load the target vector,
-    // extract and insert elements to perform writeMask and/or swizzle.
-    if (accessChain.swizzle.size() > 0) {
-        Id tempBaseId = createLoad(base, spv::NoPrecision);
-        source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle);
-    }
+            accessChain.indexChain.pop_back();
+            accessChain.instr = NoResult;
+
+            // dynamic component should be gone
+            assert(accessChain.component == NoResult);
+
+            Id source = createCompositeExtract(rvalue, getContainedTypeId(getTypeId(rvalue)), i);
+
+            // take LSB of alignment
+            alignment = alignment & ~(alignment & (alignment-1));
+            if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) {
+                memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
+            }
 
 
-    // take LSB of alignment
-    alignment = alignment & ~(alignment & (alignment-1));
-    if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) {
-        memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
+            createStore(source, base, memoryAccess, scope, alignment);
+        }
     }
     }
+    else {
+        Id base = collapseAccessChain();
+        addDecoration(base, nonUniform);
+
+        Id source = rvalue;
+
+        // dynamic component should be gone
+        assert(accessChain.component == NoResult);
 
 
-    createStore(source, base, memoryAccess, scope, alignment);
+        // If swizzle still exists, it may be out-of-order, we must load the target vector,
+        // extract and insert elements to perform writeMask and/or swizzle.
+        if (accessChain.swizzle.size() > 0) {
+            Id tempBaseId = createLoad(base, spv::NoPrecision);
+            source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle);
+        }
+
+        // take LSB of alignment
+        alignment = alignment & ~(alignment & (alignment-1));
+        if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) {
+            memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
+        }
+
+        createStore(source, base, memoryAccess, scope, alignment);
+    }
 }
 }
 
 
 // Comments in header
 // Comments in header

+ 1 - 0
3rdparty/glslang/SPIRV/SpvBuilder.h

@@ -202,6 +202,7 @@ public:
     StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); }
     StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); }
     ImageFormat getImageTypeFormat(Id typeId) const
     ImageFormat getImageTypeFormat(Id typeId) const
         { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); }
         { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); }
+    Id getResultingAccessChainType() const;
 
 
     bool isPointer(Id resultId)      const { return isPointerType(getTypeId(resultId)); }
     bool isPointer(Id resultId)      const { return isPointerType(getTypeId(resultId)); }
     bool isScalar(Id resultId)       const { return isScalarType(getTypeId(resultId)); }
     bool isScalar(Id resultId)       const { return isScalarType(getTypeId(resultId)); }

+ 3 - 3
3rdparty/glslang/StandAlone/StandAlone.cpp

@@ -347,13 +347,13 @@ void ProcessBindingBase(int& argc, char**& argv, glslang::TResourceType res)
         lang = FindLanguage(argv[arg++], false);
         lang = FindLanguage(argv[arg++], false);
     }
     }
 
 
-    if ((argc - arg) > 2 && isdigit(argv[arg+0][0]) && isdigit(argv[arg+1][0])) {
+    if ((argc - arg) >= 2 && isdigit(argv[arg+0][0]) && isdigit(argv[arg+1][0])) {
         // Parse a per-set binding base
         // Parse a per-set binding base
-        while ((argc - arg) > 2 && isdigit(argv[arg+0][0]) && isdigit(argv[arg+1][0])) {
+        do {
             const int baseNum = atoi(argv[arg++]);
             const int baseNum = atoi(argv[arg++]);
             const int setNum = atoi(argv[arg++]);
             const int setNum = atoi(argv[arg++]);
             perSetBase[setNum] = baseNum;
             perSetBase[setNum] = baseNum;
-        }
+        } while ((argc - arg) >= 2 && isdigit(argv[arg + 0][0]) && isdigit(argv[arg + 1][0]));
     } else {
     } else {
         // Parse single binding base
         // Parse single binding base
         singleBase = atoi(argv[arg++]);
         singleBase = atoi(argv[arg++]);

+ 1 - 8
3rdparty/glslang/glslang/HLSL/hlslParseHelper.cpp

@@ -6689,12 +6689,6 @@ void HlslParseContext::globalQualifierFix(const TSourceLoc&, TQualifier& qualifi
 
 
 //
 //
 // Merge characteristics of the 'src' qualifier into the 'dst'.
 // Merge characteristics of the 'src' qualifier into the 'dst'.
-// If there is duplication, issue error messages, unless 'force'
-// is specified, which means to just override default settings.
-//
-// Also, when force is false, it will be assumed that 'src' follows
-// 'dst', for the purpose of error checking order for versions
-// that require specific orderings of qualifiers.
 //
 //
 void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src)
 void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src)
 {
 {
@@ -6712,8 +6706,7 @@ void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src)
     mergeObjectLayoutQualifiers(dst, src, false);
     mergeObjectLayoutQualifiers(dst, src, false);
 
 
     // individual qualifiers
     // individual qualifiers
-    bool repeated = false;
-#define MERGE_SINGLETON(field) repeated |= dst.field && src.field; dst.field |= src.field;
+#define MERGE_SINGLETON(field) dst.field |= src.field;
     MERGE_SINGLETON(invariant);
     MERGE_SINGLETON(invariant);
     MERGE_SINGLETON(noContraction);
     MERGE_SINGLETON(noContraction);
     MERGE_SINGLETON(centroid);
     MERGE_SINGLETON(centroid);

+ 8 - 2
3rdparty/glslang/glslang/MachineIndependent/Intermediate.cpp

@@ -1739,7 +1739,7 @@ bool TIntermediate::canImplicitlyPromote(TBasicType from, TBasicType to, TOperat
         case EbtUint:
         case EbtUint:
             switch (from) {
             switch (from) {
             case EbtInt:
             case EbtInt:
-                return version >= 400 || getSource() == EShSourceHlsl;
+                return version >= 400 || getSource() == EShSourceHlsl || IsRequestedExtension(E_GL_ARB_gpu_shader5);
             case EbtBool:
             case EbtBool:
                 return getSource() == EShSourceHlsl;
                 return getSource() == EShSourceHlsl;
             case EbtInt16:
             case EbtInt16:
@@ -2676,7 +2676,11 @@ TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors<selectorType>& selecto
 // 'swizzleOkay' says whether or not it is okay to consider a swizzle
 // 'swizzleOkay' says whether or not it is okay to consider a swizzle
 // a valid part of the dereference chain.
 // a valid part of the dereference chain.
 //
 //
-const TIntermTyped* TIntermediate::findLValueBase(const TIntermTyped* node, bool swizzleOkay)
+// 'BufferReferenceOk' says if type is buffer_reference, the routine stop to find the most left node.
+//
+//
+
+const TIntermTyped* TIntermediate::findLValueBase(const TIntermTyped* node, bool swizzleOkay , bool bufferReferenceOk)
 {
 {
     do {
     do {
         const TIntermBinary* binary = node->getAsBinaryNode();
         const TIntermBinary* binary = node->getAsBinaryNode();
@@ -2694,6 +2698,8 @@ const TIntermTyped* TIntermediate::findLValueBase(const TIntermTyped* node, bool
                 return nullptr;
                 return nullptr;
         }
         }
         node = node->getAsBinaryNode()->getLeft();
         node = node->getAsBinaryNode()->getLeft();
+        if (bufferReferenceOk && node->isReference())
+            return node;
     } while (true);
     } while (true);
 }
 }
 
 

+ 47 - 2
3rdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp

@@ -327,6 +327,16 @@ void TParseContext::setAtomicCounterBlockDefaults(TType& block) const
     block.getQualifier().layoutMatrix = ElmRowMajor;
     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)
 void TParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens)
 {
 {
 #ifndef GLSLANG_WEB
 #ifndef GLSLANG_WEB
@@ -404,8 +414,33 @@ void TParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>&
         intermediate.setUseVariablePointers();
         intermediate.setUseVariablePointers();
     } else if (tokens[0].compare("once") == 0) {
     } else if (tokens[0].compare("once") == 0) {
         warn(loc, "not implemented", "#pragma once", "");
         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();
         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
 #endif
 }
 }
 
 
@@ -2409,6 +2444,14 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan
                    arg0->getType().isFloatingDomain()) {
                    arg0->getType().isFloatingDomain()) {
             requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_float2, fnCandidate.getName().c_str());
             requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_float2, fnCandidate.getName().c_str());
         }
         }
+
+        const TIntermTyped* base = TIntermediate::findLValueBase(arg0, true , true);
+        const TType* refType = (base->getType().isReference()) ? base->getType().getReferentType() : nullptr;
+        const TQualifier& qualifier = (refType != nullptr) ? refType->getQualifier() : base->getType().getQualifier();
+        if (qualifier.storage != EvqShared && qualifier.storage != EvqBuffer)
+            error(loc,"Atomic memory function can only be used for shader storage block member or shared variable.",
+            fnCandidate.getName().c_str(), "");
+
         break;
         break;
     }
     }
 
 
@@ -3651,6 +3694,8 @@ void TParseContext::globalQualifierFixCheck(const TSourceLoc& loc, TQualifier& q
         profileRequires(loc, ENoProfile, 130, nullptr, "out for stage outputs");
         profileRequires(loc, ENoProfile, 130, nullptr, "out for stage outputs");
         profileRequires(loc, EEsProfile, 300, nullptr, "out for stage outputs");
         profileRequires(loc, EEsProfile, 300, nullptr, "out for stage outputs");
         qualifier.storage = EvqVaryingOut;
         qualifier.storage = EvqVaryingOut;
+        if (intermediate.isInvariantAll())
+            qualifier.invariant = true;
         break;
         break;
     case EvqInOut:
     case EvqInOut:
         qualifier.storage = EvqVaryingIn;
         qualifier.storage = EvqVaryingIn;
@@ -3667,7 +3712,7 @@ void TParseContext::globalQualifierFixCheck(const TSourceLoc& loc, TQualifier& q
         if (blockName == nullptr &&
         if (blockName == nullptr &&
             qualifier.layoutPacking == ElpStd430)
             qualifier.layoutPacking == ElpStd430)
         {
         {
-            error(loc, "it is invalid to declare std430 qualifier on uniform", "", "");
+            requireExtensions(loc, 1, &E_GL_EXT_scalar_block_layout, "default std430 layout for uniform");
         }
         }
         break;
         break;
     default:
     default:

+ 2 - 0
3rdparty/glslang/glslang/MachineIndependent/ParseHelper.h

@@ -241,6 +241,7 @@ protected:
     // override this to set the language-specific name
     // override this to set the language-specific name
     virtual const char* getAtomicCounterBlockName() const { return ""; }
     virtual const char* getAtomicCounterBlockName() const { return ""; }
     virtual void setAtomicCounterBlockDefaults(TType&) const {}
     virtual void setAtomicCounterBlockDefaults(TType&) const {}
+    virtual void setInvariant(const TSourceLoc& loc, const char* builtin) {}
     virtual void finalizeAtomicCounterBlockLayout(TVariable&) {}
     virtual void finalizeAtomicCounterBlockLayout(TVariable&) {}
     bool isAtomicCounterBlock(const TSymbol& symbol) {
     bool isAtomicCounterBlock(const TSymbol& symbol) {
         const TVariable* var = symbol.getAsVariable();
         const TVariable* var = symbol.getAsVariable();
@@ -511,6 +512,7 @@ protected:
     virtual const char* getAtomicCounterBlockName() const override;
     virtual const char* getAtomicCounterBlockName() const override;
     virtual void finalizeAtomicCounterBlockLayout(TVariable&) override;
     virtual void finalizeAtomicCounterBlockLayout(TVariable&) override;
     virtual void setAtomicCounterBlockDefaults(TType& block) const override;
     virtual void setAtomicCounterBlockDefaults(TType& block) const override;
+    virtual void setInvariant(const TSourceLoc& loc, const char* builtin) override;
 
 
 public:
 public:
     //
     //

+ 1 - 0
3rdparty/glslang/glslang/MachineIndependent/linkValidate.cpp

@@ -316,6 +316,7 @@ void TIntermediate::mergeModes(TInfoSink& infoSink, TIntermediate& unit)
     MERGE_TRUE(useUnknownFormat);
     MERGE_TRUE(useUnknownFormat);
     MERGE_TRUE(hlslOffsets);
     MERGE_TRUE(hlslOffsets);
     MERGE_TRUE(useStorageBuffer);
     MERGE_TRUE(useStorageBuffer);
+    MERGE_TRUE(invariantAll);
     MERGE_TRUE(hlslIoMapping);
     MERGE_TRUE(hlslIoMapping);
 
 
     // TODO: sourceFile
     // TODO: sourceFile

+ 10 - 1
3rdparty/glslang/glslang/MachineIndependent/localintermediate.h

@@ -291,6 +291,7 @@ public:
         numEntryPoints(0), numErrors(0), numPushConstants(0), recursive(false),
         numEntryPoints(0), numErrors(0), numPushConstants(0), recursive(false),
         invertY(false),
         invertY(false),
         useStorageBuffer(false),
         useStorageBuffer(false),
+        invariantAll(false),
         nanMinMaxClamp(false),
         nanMinMaxClamp(false),
         depthReplacing(false),
         depthReplacing(false),
         uniqueId(0),
         uniqueId(0),
@@ -538,7 +539,7 @@ public:
     TIntermTyped* foldSwizzle(TIntermTyped* node, TSwizzleSelectors<TVectorSelector>& fields, const TSourceLoc&);
     TIntermTyped* foldSwizzle(TIntermTyped* node, TSwizzleSelectors<TVectorSelector>& fields, const TSourceLoc&);
 
 
     // Tree ops
     // Tree ops
-    static const TIntermTyped* findLValueBase(const TIntermTyped*, bool swizzleOkay);
+    static const TIntermTyped* findLValueBase(const TIntermTyped*, bool swizzleOkay , bool BufferReferenceOk = false);
 
 
     // Linkage related
     // Linkage related
     void addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage, TSymbolTable&);
     void addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage, TSymbolTable&);
@@ -560,6 +561,8 @@ public:
 
 
     void setUseStorageBuffer() { useStorageBuffer = true; }
     void setUseStorageBuffer() { useStorageBuffer = true; }
     bool usingStorageBuffer() const { return useStorageBuffer; }
     bool usingStorageBuffer() const { return useStorageBuffer; }
+    void setInvariantAll() { invariantAll = true; }
+    bool isInvariantAll() const { return invariantAll; }
     void setDepthReplacing() { depthReplacing = true; }
     void setDepthReplacing() { depthReplacing = true; }
     bool isDepthReplacing() const { return depthReplacing; }
     bool isDepthReplacing() const { return depthReplacing; }
     bool setLocalSize(int dim, int size)
     bool setLocalSize(int dim, int size)
@@ -926,6 +929,11 @@ public:
         return false;
         return false;
     }
     }
 
 
+    bool IsRequestedExtension(const char* extension) const
+    {
+        return (requestedExtensions.find(extension) != requestedExtensions.end());
+    }
+
     void addToCallGraph(TInfoSink&, const TString& caller, const TString& callee);
     void addToCallGraph(TInfoSink&, const TString& caller, const TString& callee);
     void merge(TInfoSink&, TIntermediate&);
     void merge(TInfoSink&, TIntermediate&);
     void finalCheck(TInfoSink&, bool keepUncalled);
     void finalCheck(TInfoSink&, bool keepUncalled);
@@ -1063,6 +1071,7 @@ protected:
     bool recursive;
     bool recursive;
     bool invertY;
     bool invertY;
     bool useStorageBuffer;
     bool useStorageBuffer;
+    bool invariantAll;
     bool nanMinMaxClamp;            // true if desiring min/max/clamp to favor non-NaN over NaN
     bool nanMinMaxClamp;            // true if desiring min/max/clamp to favor non-NaN over NaN
     bool depthReplacing;
     bool depthReplacing;
     int localSize[3];
     int localSize[3];