Support a uniform block to hold global uniform variables.

Used initially just by HLSL, for $Global.  Could be an option
for GLSL -> Vulkan.
This commit is contained in:
John Kessenich 2016-09-27 19:13:05 -06:00
parent e82061de08
commit 6dbc0a7a33
6 changed files with 117 additions and 40 deletions

View 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"

View 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

View 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&) { }
};
//

View File

@ -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?

View File

@ -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."

View File

@ -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); }