Support a uniform block to hold global uniform variables.
authorJohn Kessenich <cepheus@frii.com>
Wed, 28 Sep 2016 01:13:05 +0000 (19:13 -0600)
committerJohn Kessenich <cepheus@frii.com>
Thu, 29 Sep 2016 16:25:15 +0000 (10:25 -0600)
Used initially just by HLSL, for $Global.  Could be an option
for GLSL -> Vulkan.

glslang/Include/revision.h
glslang/MachineIndependent/ParseContextBase.cpp
glslang/MachineIndependent/ParseHelper.h
hlsl/hlslGrammar.cpp
hlsl/hlslParseHelper.cpp
hlsl/hlslParseHelper.h

index 7806c77..fb515df 100644 (file)
@@ -2,5 +2,5 @@
 // For the version, it uses the latest git tag followed by the number of commits.
 // For the date, it uses the current date (when then script is run).
 
-#define GLSLANG_REVISION "Overload400-PrecQual.1523"
+#define GLSLANG_REVISION "Overload400-PrecQual.1524"
 #define GLSLANG_DATE "27-Sep-2016"
index 4c1d02a..54715ce 100644 (file)
@@ -181,4 +181,54 @@ const TFunction* TParseContextBase::selectFunction(
     return incumbent;
 }
 
+//
+// Make the passed-in variable information become a member of the
+// global uniform block.  If this doesn't exist yet, make it.
+//
+void TParseContextBase::growGlobalUniformBlock(TSourceLoc& loc, TType& memberType, TString& memberName)
+{
+    // make the global block, if not yet made
+    if (globalUniformBlock == nullptr) {
+        TString& blockName = *NewPoolTString(getGlobalUniformBlockName());
+        TQualifier blockQualifier;
+        blockQualifier.clear();
+        blockQualifier.storage = EvqUniform;
+        TType blockType(new TTypeList, blockName, blockQualifier);
+        TString* instanceName = NewPoolTString("");
+        globalUniformBlock = new TVariable(instanceName, blockType, true);
+        globalUniformBlockAdded = false;
+    }
+
+    // add the requested member as a member to the block
+    TType* type = new TType;
+    type->shallowCopy(memberType);
+    type->setFieldName(memberName);
+    TTypeLoc typeLoc = {type, loc};
+    globalUniformBlock->getType().getWritableStruct()->push_back(typeLoc);
+    globalUniformBlockChanged = true;
+}
+
+//
+// Insert into the symbol table the global uniform block created in
+// growGlobalUniformBlock(). The variables added as members won't be
+// found unless this is done.
+//
+bool TParseContextBase::insertGlobalUniformBlock()
+{
+    if (globalUniformBlock == nullptr)
+        return true;
+
+    if (globalUniformBlockAdded)
+        return ! globalUniformBlockChanged;
+
+    globalUniformBlockChanged = false;
+    globalUniformBlockAdded = symbolTable.insert(*globalUniformBlock);
+    if (globalUniformBlockAdded) {
+        intermediate.addSymbolLinkageNode(linkage, *globalUniformBlock);
+        finalizeGlobalUniformBlockLayout(*globalUniformBlock);
+    }
+
+    return globalUniformBlockAdded;
+}
+
 } // end namespace glslang
index aa92954..b007ebb 100644 (file)
@@ -78,7 +78,8 @@ public:
                       TInfoSink& infoSink, bool forwardCompatible, EShMessages messages)
           : TParseVersions(interm, version, profile, spvVersion, language, infoSink, forwardCompatible, messages),
             symbolTable(symbolTable),
-            linkage(nullptr), scanContext(nullptr), ppContext(nullptr) { }
+            linkage(nullptr), scanContext(nullptr), ppContext(nullptr),
+            globalUniformBlock(nullptr) { }
     virtual ~TParseContextBase() { }
 
     virtual void setLimits(const TBuiltInResource&) = 0;
@@ -126,6 +127,13 @@ public:
 
     TSymbolTable& symbolTable;   // symbol table that goes with the current language, version, and profile
 
+    // Manage the global uniform block (default uniforms in GLSL, $Global in HLSL)
+    // TODO: This could perhaps get its own object, but the current design doesn't work
+    // yet when new uniform variables are declared between function definitions, so
+    // this is pending getting a fully functional design.
+    virtual void growGlobalUniformBlock(TSourceLoc&, TType&, TString& memberName);
+    virtual bool insertGlobalUniformBlock();
+
 protected:
     TParseContextBase(TParseContextBase&);
     TParseContextBase& operator=(TParseContextBase&);
@@ -147,6 +155,14 @@ protected:
         std::function<bool(const TType&, const TType&)>,
         std::function<bool(const TType&, const TType&, const TType&)>,
         /* output */ bool& tie);
+
+    // Manage the global uniform block (default uniforms in GLSL, $Global in HLSL)
+    TVariable* globalUniformBlock;   // the actual block, inserted into the symbol table
+    bool globalUniformBlockAdded;    // true once inserted into the symbol table
+    bool globalUniformBlockChanged;  // true if members have changed
+    // override this to set the language-specific name
+    virtual const char* getGlobalUniformBlockName() { return ""; }
+    virtual void finalizeGlobalUniformBlockLayout(TVariable&) { }
 };
 
 //
index 85c2c69..9652129 100755 (executable)
@@ -287,17 +287,6 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& node)
     if (! acceptFullySpecifiedType(declaredType))
         return false;
 
-    if (declaredType.getQualifier().storage == EvqTemporary && parseContext.symbolTable.atGlobalLevel()) {
-        if (declaredType.getBasicType() == EbtSampler) {
-            // Sampler/textures are uniform by default (if no explicit qualifier is present) in
-            // HLSL.  This line silently converts samplers *explicitly* declared static to uniform,
-            // which is incorrect but harmless.
-            declaredType.getQualifier().storage = EvqUniform; 
-        } else {
-            declaredType.getQualifier().storage = EvqGlobal;
-        }
-    }
-
     // identifier
     HlslToken idToken;
     while (acceptIdentifier(idToken)) {
@@ -320,7 +309,10 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& node)
                 parseContext.handleFunctionDeclarator(idToken.loc, function, true);
             }
         } else {
-            // A variable declaration.
+            // A variable declaration. Fix the storage qualifier if it's a global.
+            if (declaredType.getQualifier().storage == EvqTemporary && parseContext.symbolTable.atGlobalLevel())
+                declaredType.getQualifier().storage = EvqUniform;
+
             // We can handle multiple variables per type declaration, so 
             // the number of types can expand when arrayness is different.
             TType variableType;
@@ -364,18 +356,29 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& node)
                 }
             }
 
-            if (typedefDecl)
-                parseContext.declareTypedef(idToken.loc, *idToken.string, variableType, arraySizes);
-            else if (variableType.getBasicType() == EbtBlock)
-                parseContext.declareBlock(idToken.loc, variableType, idToken.string);
-            else {
-                // Declare the variable and add any initializer code to the AST.
-                // The top-level node is always made into an aggregate, as that's
-                // historically how the AST has been.
-                node = intermediate.growAggregate(node,
-                                                  parseContext.declareVariable(idToken.loc, *idToken.string, variableType,
-                                                                               expressionNode),
-                                                  idToken.loc);
+            // Hand off the actual declaration
+
+            // TODO: things scoped within an annotation need their own name space;
+            // TODO: strings are not yet handled.
+            if (variableType.getBasicType() != EbtString && parseContext.getAnnotationNestingLevel() == 0) {
+                if (typedefDecl)
+                    parseContext.declareTypedef(idToken.loc, *idToken.string, variableType);
+                else if (variableType.getBasicType() == EbtBlock)
+                    parseContext.declareBlock(idToken.loc, variableType, idToken.string);
+                else {
+                    if (variableType.getQualifier().storage == EvqUniform && variableType.getBasicType() != EbtSampler) {
+                        // this isn't really an individual variable, but a member of the $Global buffer
+                        parseContext.growGlobalUniformBlock(idToken.loc, variableType, *idToken.string);
+                    } else {
+                        // Declare the variable and add any initializer code to the AST.
+                        // The top-level node is always made into an aggregate, as that's
+                        // historically how the AST has been.
+                        node = intermediate.growAggregate(node,
+                                                          parseContext.declareVariable(idToken.loc, *idToken.string, variableType,
+                                                                                       expressionNode),
+                                                          idToken.loc);
+                    }
+                }
             }
         }
 
@@ -473,7 +476,7 @@ bool HlslGrammar::acceptQualifier(TQualifier& qualifier)
     do {
         switch (peek()) {
         case EHTokStatic:
-            // normal glslang default
+            qualifier.storage = parseContext.symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
             break;
         case EHTokExtern:
             // TODO: no meaning in glslang?
index 34fd202..65f7aa2 100755 (executable)
@@ -957,6 +957,11 @@ TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& l
     } else
         remapNonEntryPointIO(function);
 
+    // Insert the $Global constant buffer.
+    // TODO: this design fails if new members are declared between function definitions.
+    if (! insertGlobalUniformBlock())
+        error(loc, "failed to insert the global constant buffer", "uniform", "");
+
     //
     // New symbol table scope for body of function plus its arguments
     //
@@ -4178,15 +4183,6 @@ void HlslParseContext::declareTypedef(const TSourceLoc& loc, TString& identifier
 //
 TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, TString& identifier, TType& type, TIntermTyped* initializer)
 {
-    // TODO: things scoped within an annotation need their own name space;
-    // haven't done that yet
-    if (annotationNestingLevel > 0)
-        return nullptr;
-
-    // TODO: strings are not yet handled
-    if (type.getBasicType() == EbtString)
-        return nullptr;
-
     if (voidErrorCheck(loc, identifier, type.getBasicType()))
         return nullptr;
 
@@ -4832,6 +4828,13 @@ void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TS
     intermediate.addSymbolLinkageNode(linkage, variable);
 }
 
+void HlslParseContext::finalizeGlobalUniformBlockLayout(TVariable& block)
+{
+    block.getWritableType().getQualifier().layoutPacking = ElpStd140;
+    block.getWritableType().getQualifier().layoutMatrix = ElmRowMajor;
+    fixBlockUniformOffsets(block.getType().getQualifier(), *block.getWritableType().getWritableStruct());
+}
+
 //
 // "For a block, this process applies to the entire block, or until the first member 
 // is reached that has a location layout qualifier. When a block member is declared with a location 
@@ -4912,7 +4915,7 @@ void HlslParseContext::fixBlockXfbOffsets(TQualifier& qualifier, TTypeList& type
 // Also, compute and save the total size of the block. For the block's size, arrayness 
 // is not taken into account, as each element is backed by a separate buffer.
 //
-void HlslParseContext::fixBlockUniformOffsets(TQualifier& qualifier, TTypeList& typeList)
+void HlslParseContext::fixBlockUniformOffsets(const TQualifier& qualifier, TTypeList& typeList)
 {
     if (! qualifier.isUniformOrBuffer())
         return;
@@ -4930,8 +4933,10 @@ void HlslParseContext::fixBlockUniformOffsets(TQualifier& qualifier, TTypeList&
         // modify just the children's view of matrix layout, if there is one for this member
         TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix;
         int dummyStride;
-        int memberAlignment = intermediate.getBaseAlignment(*typeList[member].type, memberSize, dummyStride, qualifier.layoutPacking == ElpStd140,
-            subMatrixLayout != ElmNone ? subMatrixLayout == ElmRowMajor : qualifier.layoutMatrix == ElmRowMajor);
+        int memberAlignment = intermediate.getBaseAlignment(*typeList[member].type, memberSize, dummyStride,
+                                                            qualifier.layoutPacking == ElpStd140,
+                                                            subMatrixLayout != ElmNone ? subMatrixLayout == ElmRowMajor
+                                                                                       : qualifier.layoutMatrix == ElmRowMajor);
         if (memberQualifier.hasOffset()) {
             // "The specified offset must be a multiple 
             // of the base alignment of the type of the block member it qualifies, or a compile-time error results."
index 7b7b95d..657858a 100755 (executable)
@@ -51,6 +51,7 @@ public:
 
     void setLimits(const TBuiltInResource&);
     bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false);
+    virtual const char* getGlobalUniformBlockName() { return "$Global"; }
 
     void C_DECL error(const TSourceLoc&, const char* szReason, const char* szToken,
         const char* szExtraInfoFormat, ...);
@@ -145,9 +146,10 @@ public:
     TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&);
     TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset);
     void declareBlock(const TSourceLoc&, TType&, const TString* instanceName = 0, TArraySizes* arraySizes = 0);
+    void finalizeGlobalUniformBlockLayout(TVariable& block);
     void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation);
     void fixBlockXfbOffsets(TQualifier&, TTypeList&);
-    void fixBlockUniformOffsets(TQualifier&, TTypeList&);
+    void fixBlockUniformOffsets(const TQualifier&, TTypeList&);
     void addQualifierToExisting(const TSourceLoc&, TQualifier, const TString& identifier);
     void addQualifierToExisting(const TSourceLoc&, TQualifier, TIdentifierList&);
     void updateStandaloneQualifierDefaults(const TSourceLoc&, const TPublicType&);
@@ -160,6 +162,7 @@ public:
     void unnestLooping()     { --loopNestingLevel; }
     void nestAnnotations()   { ++annotationNestingLevel; }
     void unnestAnnotations() { --annotationNestingLevel; }
+    int getAnnotationNestingLevel() { return annotationNestingLevel; }
     void pushScope()         { symbolTable.push(); }
     void popScope()          { symbolTable.pop(0); }