From 6dbc0a7a33eac420c0b9ce3b6c53140fb13122a8 Mon Sep 17 00:00:00 2001 From: John Kessenich <cepheus@frii.com> Date: Tue, 27 Sep 2016 19:13:05 -0600 Subject: [PATCH] Support a uniform block to hold global uniform variables. Used initially just by HLSL, for $Global. Could be an option for GLSL -> Vulkan. --- glslang/Include/revision.h | 2 +- .../MachineIndependent/ParseContextBase.cpp | 50 +++++++++++++++++ glslang/MachineIndependent/ParseHelper.h | 18 ++++++- hlsl/hlslGrammar.cpp | 53 ++++++++++--------- hlsl/hlslParseHelper.cpp | 29 +++++----- hlsl/hlslParseHelper.h | 5 +- 6 files changed, 117 insertions(+), 40 deletions(-) diff --git a/glslang/Include/revision.h b/glslang/Include/revision.h index 7806c77c2..fb515df0c 100644 --- a/glslang/Include/revision.h +++ b/glslang/Include/revision.h @@ -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" diff --git a/glslang/MachineIndependent/ParseContextBase.cpp b/glslang/MachineIndependent/ParseContextBase.cpp index 4c1d02a88..54715ce18 100644 --- a/glslang/MachineIndependent/ParseContextBase.cpp +++ b/glslang/MachineIndependent/ParseContextBase.cpp @@ -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 diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index aa92954be..b007ebbb1 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -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&) { } }; // diff --git a/hlsl/hlslGrammar.cpp b/hlsl/hlslGrammar.cpp index 85c2c6972..9652129ca 100755 --- a/hlsl/hlslGrammar.cpp +++ b/hlsl/hlslGrammar.cpp @@ -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? diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp index 34fd2025e..65f7aa263 100755 --- a/hlsl/hlslParseHelper.cpp +++ b/hlsl/hlslParseHelper.cpp @@ -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." diff --git a/hlsl/hlslParseHelper.h b/hlsl/hlslParseHelper.h index 7b7b95dc7..657858a6a 100755 --- a/hlsl/hlslParseHelper.h +++ b/hlsl/hlslParseHelper.h @@ -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); } -- GitLab