diff --git a/Test/420.vert b/Test/420.vert index 6561b795968ed8cc8a2d4c28f5aa726440bc0ac1..ebe7d0ed0d2e0a854a8000eab61dbabb021dd571 100644 --- a/Test/420.vert +++ b/Test/420.vert @@ -62,3 +62,12 @@ void bar(in highp volatile vec4 v) f.xxxxx; // ERROR f.xxy; // ERROR } + +layout(binding = 3) uniform; // ERROR +layout(binding = 3) uniform boundblock { int aoeu; } boundInst; +layout(binding = 7) uniform anonblock { int aoeu; } ; +layout(location = 1) in; // ERROR +layout(binding = 1) in inblock { int aoeua; }; // ERROR +layout(binding = 100000) uniform anonblock2 { int aooeu; } ; +layout(binding = 4) uniform sampler2D sampb1; +layout(binding = 5) uniform sampler2D sampb2[10]; diff --git a/Test/baseResults/300layout.frag.out b/Test/baseResults/300layout.frag.out index 58d870dd2b76b034e1950cf047dd661f3eda41f2..ae7d9ab0097ba38474a30b5d9413188a323eb16d 100644 --- a/Test/baseResults/300layout.frag.out +++ b/Test/baseResults/300layout.frag.out @@ -1,4 +1,4 @@ -ERROR: 0:4: 'color' : can only use location layout qualifier on a vertex input or fragment output +ERROR: 0:4: 'input location layout qualifier' : not supported in this stage: fragment ERROR: 1 compilation errors. No code generated. ERROR: node is still EOpNull! diff --git a/Test/baseResults/300layout.vert.out b/Test/baseResults/300layout.vert.out index fb1e04c5461139615dd8d491cfc351fcad1e1f8b..d0f8e43f14b1e526499ec50af1d94ee90d34bbf4 100644 --- a/Test/baseResults/300layout.vert.out +++ b/Test/baseResults/300layout.vert.out @@ -7,7 +7,7 @@ ERROR: 0:19: 'badf' : member of uniform block cannot have an auxiliary or interp ERROR: 0:20: 'badg' : member storage qualifier cannot contradict block storage qualifier ERROR: 0:28: 'T3' : nameless block contains a member that already has a name at global scope ERROR: 0:35: 'output block' : not supported with this profile: es -ERROR: 0:39: 'badoutA' : can only use location layout qualifier on a vertex input or fragment output +ERROR: 0:39: 'output location layout qualifier' : not supported in this stage: vertex ERROR: 0:47: 'shared' : not supported with this profile: es ERROR: 0:47: 'shared' : not supported in this stage: vertex ERROR: 12 compilation errors. No code generated. @@ -33,11 +33,11 @@ ERROR: node is still EOpNull! 0:43 Constant: 0:43 1 (const int) 0:43 M4: direct index for structure (layout(row_major shared ) highp 4X4 matrix of float) -0:43 '__anon__1' (layout(shared ) uniform block) +0:43 '__anon__1' (layout(column_major shared ) uniform block) 0:43 Constant: 0:43 1 (const uint) 0:43 M3: direct index for structure (layout(column_major shared ) highp 4X4 matrix of float) -0:43 '__anon__1' (layout(shared ) uniform block) +0:43 '__anon__1' (layout(column_major shared ) uniform block) 0:43 Constant: 0:43 0 (const uint) 0:43 t2m: direct index for structure (layout(row_major shared ) highp 4X4 matrix of float) diff --git a/Test/baseResults/420.vert.out b/Test/baseResults/420.vert.out index 7c38c76467eada29232107f2f879d78f3e975fc2..263651d7809f0acace16d563eb718f94f55ff0ed 100644 --- a/Test/baseResults/420.vert.out +++ b/Test/baseResults/420.vert.out @@ -20,7 +20,11 @@ ERROR: 0:44: '=' : cannot convert from 'float' to 'int' ERROR: 0:54: 'y' : vector field selection out of range ERROR: 0:62: 'xxxxx' : illegal vector field selection ERROR: 0:63: 'xxy' : vector field selection out of range -ERROR: 20 compilation errors. No code generated. +ERROR: 0:66: 'binding' : cannot declare a default, include a type or full declaration +ERROR: 0:69: 'location' : cannot declare a default, use a full declaration +ERROR: 0:70: 'binding' : requires uniform or buffer storage qualifier +ERROR: 0:71: 'binding' : binding is too large +ERROR: 24 compilation errors. No code generated. ERROR: node is still EOpNull! 0:20 Function Definition: foo( (const int) @@ -112,6 +116,12 @@ ERROR: node is still EOpNull! 0:? 4.200000 0:? 'dx' (const float) 0:? 4.200000 +0:? 'boundInst' (layout(binding=3 shared ) uniform block) +0:? '__anon__0' (layout(binding=7 shared ) uniform block) +0:? '__anon__1' (layout(binding=1 ) in block) +0:? '__anon__2' (layout(shared ) uniform block) +0:? 'sampb1' (layout(binding=4 ) uniform sampler2D) +0:? 'sampb2' (layout(binding=5 ) uniform 10-element array of sampler2D) 0:? 'gl_VertexID' (gl_VertexId int) 0:? 'gl_InstanceID' (gl_InstanceId int) diff --git a/Test/baseResults/specExamples.vert.out b/Test/baseResults/specExamples.vert.out index d4bfe6aa008b9a3581551e506a17bb57897bcbae..65625732a6a95cf93c326d4338cd3c8212d317d2 100644 --- a/Test/baseResults/specExamples.vert.out +++ b/Test/baseResults/specExamples.vert.out @@ -15,23 +15,20 @@ ERROR: 0:46: 'stream' : there is no such layout identifier taking an assigned va ERROR: 0:47: 'stream' : there is no such layout identifier taking an assigned value ERROR: 0:50: 'stream' : there is no such layout identifier taking an assigned value ERROR: 0:55: 'stream' : there is no such layout identifier taking an assigned value -ERROR: 0:77: 'binding' : not supported ERROR: 0:80: 's17' : redefinition -ERROR: 0:85: 'binding' : not supported ERROR: 0:85: 'offset' : there is no such layout identifier taking an assigned value -ERROR: 0:87: 'binding' : not supported -ERROR: 0:89: 'binding' : not supported +ERROR: 0:85: 'binding' : requires block, or sampler/image, or atomic-counter type +ERROR: 0:87: 'binding' : requires block, or sampler/image, or atomic-counter type ERROR: 0:89: 'offset' : there is no such layout identifier taking an assigned value -ERROR: 0:91: 'binding' : not supported +WARNING: 0:89: '' : cannot set qualifier defaults when using a type and no identifier ERROR: 0:91: 'bar' : redefinition ERROR: 0:92: 'offset' : there is no such layout identifier taking an assigned value ERROR: 0:92: 'bar' : redefinition -ERROR: 0:94: 'binding' : not supported ERROR: 0:94: 'offset' : there is no such layout identifier taking an assigned value ERROR: 0:94: 'a2' : redefinition -ERROR: 0:95: 'binding' : not supported -ERROR: 0:96: 'binding' : not supported -ERROR: 0:97: 'binding' : not supported +ERROR: 0:95: 'binding' : requires block, or sampler/image, or atomic-counter type +ERROR: 0:96: 'binding' : requires block, or sampler/image, or atomic-counter type +ERROR: 0:97: 'binding' : requires block, or sampler/image, or atomic-counter type ERROR: 0:106: '' : vertex input cannot be further qualified ERROR: 0:112: 'ColorIvn' : identifier not previously declared ERROR: 0:132: 'shared' : not supported in this stage: vertex @@ -43,7 +40,7 @@ ERROR: 0:153: '' : function does not return a value: func3 ERROR: 0:192: 'constructor' : constructing from a non-dereferenced array ERROR: 0:193: 'constructor' : constructing from a non-dereferenced array ERROR: 0:194: 'constructor' : constructing from a non-dereferenced array -ERROR: 44 compilation errors. No code generated. +ERROR: 40 compilation errors. No code generated. ERROR: node is still EOpNull! 0:134 Function Definition: funcA(I21; (4-component vector of float) @@ -289,13 +286,13 @@ ERROR: node is still EOpNull! 0:? '__anon__2' (out block) 0:? 'var7' (smooth out 4-component vector of float) 0:? '__anon__3' (layout(std140 ) uniform block) -0:? '__anon__4' (layout(shared ) uniform block) -0:? 's17' (uniform sampler2D) -0:? 'a2' (uniform int) -0:? 'bar' (uniform int) -0:? 'b2' (uniform int) -0:? 'c2' (uniform int) -0:? 'd2' (uniform int) +0:? '__anon__4' (layout(column_major shared ) uniform block) +0:? 's17' (layout(binding=3 ) uniform sampler2D) +0:? 'a2' (layout(binding=2 ) uniform int) +0:? 'bar' (layout(binding=2 ) uniform int) +0:? 'b2' (layout(binding=2 ) uniform int) +0:? 'c2' (layout(binding=3 ) uniform int) +0:? 'd2' (layout(binding=2 ) uniform int) 0:? '__anon__5' (out block) 0:? 'ColorInv' (smooth out 3-component vector of float) 0:? 'Color4' (invariant centroid smooth out 3-component vector of float) @@ -306,7 +303,7 @@ ERROR: node is still EOpNull! 0:? 'c' (in 4-component vector of float) 0:? 'd' (in 4-component vector of float) 0:? 'v' (smooth out 4-component vector of float) -0:? '__anon__6' (layout(shared ) uniform block) +0:? '__anon__6' (layout(shared ) coherent uniform block) 0:? '__anon__7' (layout(shared ) uniform block) 0:? 'shv' (shared 4-component vector of float) 0:? 'img1' (uniform image2D) diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h index d4d9fc8b206002c274b78d3b44ef9c584528c14d..442e3ae49e95b2be5c04ec5c6051b0c99e8012a3 100644 --- a/glslang/Include/Types.h +++ b/glslang/Include/Types.h @@ -297,21 +297,29 @@ public: layoutMatrix = ElmNone; layoutPacking = ElpNone; layoutSlotLocation = layoutLocationEnd; + layoutBinding = layoutBindingEnd; } bool hasLayout() const { return layoutMatrix != ElmNone || layoutPacking != ElpNone || - layoutSlotLocation != layoutLocationEnd; + hasLocation() || + hasBinding(); } TLayoutMatrix layoutMatrix : 3; TLayoutPacking layoutPacking : 4; unsigned int layoutSlotLocation : 7; // ins/outs should have small numbers, buffer offsets could be large static const unsigned int layoutLocationEnd = 0x3F; + unsigned int layoutBinding : 8; + static const unsigned int layoutBindingEnd = 0xFF; bool hasLocation() const { return layoutSlotLocation != layoutLocationEnd; } + bool hasBinding() const + { + return layoutBinding != layoutBindingEnd; + } static const char* getLayoutPackingString(TLayoutPacking packing) { switch (packing) { @@ -430,17 +438,20 @@ public: typeName = NewPoolTString(p.userDef->getTypeName().c_str()); } } - TType(TTypeList* userDef, const TString& n, TStorageQualifier blockQualifier = EvqGlobal) : - basicType(EbtStruct), vectorSize(1), matrixCols(0), matrixRows(0), arraySizes(0), - structure(userDef), fieldName(0) + TType(TTypeList* userDef, const TString& n) : + basicType(EbtStruct), vectorSize(1), matrixCols(0), matrixRows(0), + arraySizes(0), structure(userDef), fieldName(0) { sampler.clear(); qualifier.clear(); - // is it an interface block? - if (blockQualifier != EvqGlobal) { - qualifier.storage = blockQualifier; - basicType = EbtBlock; - } + typeName = NewPoolTString(n.c_str()); + } + // For interface blocks + TType(TTypeList* userDef, const TString& n, const TQualifier& q) : + basicType(EbtBlock), vectorSize(1), matrixCols(0), matrixRows(0), + qualifier(q), arraySizes(0), structure(userDef), fieldName(0) + { + sampler.clear(); typeName = NewPoolTString(n.c_str()); } virtual ~TType() {} @@ -628,6 +639,8 @@ public: p += snprintf(p, end - p, "layout("); if (qualifier.hasLocation()) p += snprintf(p, end - p, "location=%d ", qualifier.layoutSlotLocation); + if (qualifier.hasBinding()) + p += snprintf(p, end - p, "binding=%d ", qualifier.layoutBinding); if (qualifier.layoutMatrix != ElmNone) p += snprintf(p, end - p, "%s ", TQualifier::getLayoutMatrixString(qualifier.layoutMatrix)); if (qualifier.layoutPacking != ElpNone) diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 2e3d769f5ab72cc957705e5182c79d5dcb3e047a..de9c859dadc62da4961abd4263d30e396ea9209c 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -2086,7 +2086,8 @@ void TParseContext::finalize() // Layout qualifier stuff. // -// Put the id's layout qualification into the public type. +// Put the id's layout qualification into the public type. This is before we know any +// type information for error checking. void TParseContext::setLayoutQualifier(TSourceLoc loc, TPublicType& publicType, TString& id) { std::transform(id.begin(), id.end(), id.begin(), ::tolower); @@ -2100,28 +2101,38 @@ void TParseContext::setLayoutQualifier(TSourceLoc loc, TPublicType& publicType, publicType.qualifier.layoutPacking = ElpShared; else if (id == TQualifier::getLayoutPackingString(ElpStd140)) publicType.qualifier.layoutPacking = ElpStd140; - else if (id == TQualifier::getLayoutPackingString(ElpStd430)) + else if (id == TQualifier::getLayoutPackingString(ElpStd430)) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "std430"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, 0, "std430"); publicType.qualifier.layoutPacking = ElpStd430; - else if (id == "location") + } else if (id == "location") error(loc, "requires an integer assignment (e.g., location = 4)", "location", ""); - else if (id == "binding") + else if (id == "binding") { error(loc, "requires an integer assignment (e.g., binding = 4)", "binding", ""); - else + } else error(loc, "unrecognized layout identifier", id.c_str(), ""); } -// Put the id's layout qualifier value into the public type. +// Put the id's layout qualifier value into the public type. This is before we know any +// type information for error checking. void TParseContext::setLayoutQualifier(TSourceLoc loc, TPublicType& publicType, TString& id, int value) { std::transform(id.begin(), id.end(), id.begin(), ::tolower); if (id == "location") { + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, "location"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 330, 0, "location"); if ((unsigned int)value >= TQualifier::layoutLocationEnd) - error(loc, "value is too large", id.c_str(), ""); + error(loc, "location is too large", id.c_str(), ""); else publicType.qualifier.layoutSlotLocation = value; - } else if (id == "binding") - error(loc, "not supported", "binding", ""); - else + } else if (id == "binding") { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "binding"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 420, GL_ARB_shading_language_420pack, "binding"); + if ((unsigned int)value >= TQualifier::layoutBindingEnd) + error(loc, "binding is too large", id.c_str(), ""); + else + publicType.qualifier.layoutBinding = value; + } else error(loc, "there is no such layout identifier taking an assigned value", id.c_str(), ""); // TODO: semantics: error check: make sure locations are non-overlapping across the whole stage @@ -2139,6 +2150,37 @@ void TParseContext::mergeLayoutQualifiers(TSourceLoc loc, TQualifier& dst, const if (src.hasLocation()) dst.layoutSlotLocation = src.layoutSlotLocation; + + if (src.hasBinding()) + dst.layoutBinding = src.layoutBinding; +} + +// Do error layout error checking given a full variable/block declaration. +void TParseContext::layoutCheck(TSourceLoc loc, const TSymbol& symbol) +{ + const TType& type = symbol.getType(); + const TQualifier& qualifier = type.getQualifier(); + + if (qualifier.hasLocation()) { + // TODO: location = functionality, when is it allowed? + } + if (qualifier.hasBinding()) { + // Binding checking, from the spec: + // + // "If the binding point for any uniform or shader storage block instance is less than zero, or greater than or + // equal to the implementation-dependent maximum number of uniform buffer bindings, a compile-time + // error will occur. When the binding identifier is used with a uniform or shader storage block instanced as + // an array of size N, all elements of the array from binding through binding + N – 1 must be within this + // range." + // + // TODO: binding error checking against limits, arrays + // + if (qualifier.storage != EvqUniform && qualifier.storage != EvqBuffer) + error(loc, "requires uniform or buffer storage qualifier", "binding", ""); + if (type.getBasicType() != EbtSampler && type.getBasicType() != EbtBlock) + error(loc, "requires block, or sampler/image, or atomic-counter type", "binding", ""); + // TODO: atomic counter functionality: include in test above + } } ///////////////////////////////////////////////////////////////////////////////// @@ -2229,6 +2271,10 @@ TIntermNode* TParseContext::declareVariable(TSourceLoc loc, TString& identifier, initNode = executeInitializer(loc, identifier, initializer, variable); } + // look for errors in layout qualifier use + if (symbol) + layoutCheck(loc, *symbol); + // see if it's a linker-level object to track if (symbol && newDeclaration && symbolTable.atGlobalLevel()) intermediate.addSymbolLinkageNode(linkage, *symbol); @@ -2648,7 +2694,7 @@ void TParseContext::addBlock(TSourceLoc loc, TTypeList& typeList, const TString* arrayDimCheck(loc, arraySizes, 0); - // fix and check for qualifiers and types that don't belong within a block + // fix and check for member qualifiers and types that don't belong within a block for (unsigned int member = 0; member < typeList.size(); ++member) { TQualifier& memberQualifier = typeList[member].type->getQualifier(); TSourceLoc memberLoc = typeList[member].loc; @@ -2683,7 +2729,7 @@ void TParseContext::addBlock(TSourceLoc loc, TTypeList& typeList, const TString* // Build and add the interface block as a new type named blockName - TType blockType(&typeList, *blockName, currentBlockDefaults.storage); + TType blockType(&typeList, *blockName, currentBlockDefaults); if (arraySizes) blockType.setArraySizes(arraySizes); blockType.getQualifier().layoutPacking = defaultQualification.layoutPacking; @@ -2717,18 +2763,21 @@ void TParseContext::addBlock(TSourceLoc loc, TTypeList& typeList, const TString* if (! instanceName) instanceName = NewPoolTString(""); - TVariable* variable = new TVariable(instanceName, blockType); - if (! symbolTable.insert(*variable)) { + TVariable& variable = *new TVariable(instanceName, blockType); + if (! symbolTable.insert(variable)) { if (*instanceName == "") error(loc, "nameless block contains a member that already has a name at global scope", blockName->c_str(), ""); else - error(loc, "block instance name redefinition", variable->getName().c_str(), ""); + error(loc, "block instance name redefinition", variable.getName().c_str(), ""); return; } + // Check for general layout qualifier errors + layoutCheck(loc, variable); + // Save it in the AST for linker use. - intermediate.addSymbolLinkageNode(linkage, *variable); + intermediate.addSymbolLinkageNode(linkage, variable); } // For an identifier that is already declared, add more qualification to it. @@ -2769,6 +2818,10 @@ void TParseContext::addQualifierToExisting(TSourceLoc loc, TQualifier qualifier, addQualifierToExisting(loc, qualifier, *identifiers[i]); } +// +// Update qualifier defaults for all forms of declarations, which +// must error check for their form before calling here. +// void TParseContext::updateQualifierDefaults(TQualifier qualifier) { switch (qualifier.storage) { @@ -2798,27 +2851,41 @@ void TParseContext::updateQualifierDefaults(TQualifier qualifier) } } +// +// Update defaults for qualifiers. This is called directly for the case +// of a declaration with just a qualifier. +// void TParseContext::updateQualifierDefaults(TSourceLoc loc, TQualifier qualifier) { if (qualifier.isAuxiliary() || qualifier.isMemory() || qualifier.isInterpolation() || qualifier.precision != EpqNone) - error(loc, "cannot use auxiliary, memory, interpolation, or precision qualifier in a standalone qualifier", "", ""); + error(loc, "cannot use auxiliary, memory, interpolation, or precision qualifier in a default qualifier declaration (declaration with no type)", "", ""); switch (qualifier.storage) { case EvqUniform: + case EvqBuffer: case EvqVaryingIn: case EvqVaryingOut: break; default: - error(loc, "standalone qualifier requires 'uniform', 'in', or 'out' storage qualification", "", ""); + error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", ""); return; } + + if (qualifier.hasBinding()) + error(loc, "cannot declare a default, include a type or full declaration", "binding", ""); + if (qualifier.hasLocation()) + error(loc, "cannot declare a default, use a full declaration", "location", ""); updateQualifierDefaults(qualifier); } +// +// Update defaults for qualifiers when declared with a type, and optionally an id. +// (But, not the case of just a qualifier; this is called when a type is present.) +// void TParseContext::updateTypedDefaults(TSourceLoc loc, TQualifier qualifier, const TString* id) { bool cantHaveId = false; @@ -2836,11 +2903,15 @@ void TParseContext::updateTypedDefaults(TSourceLoc loc, TQualifier qualifier, co if (qualifier.layoutPacking != ElpNone) error(loc, "cannot specify packing on a variable declaration", id->c_str(), ""); } else if (qualifier.storage == EvqVaryingIn) { - if (qualifier.hasLayout() && language != EShLangVertex) - error(loc, "can only use location layout qualifier on a vertex input or fragment output", id->c_str(), ""); + if (qualifier.hasLocation()) { + if (profile == EEsProfile) + requireStage(loc, EShLangVertex, "input location layout qualifier"); + } } else if (qualifier.storage == EvqVaryingOut) { - if (qualifier.hasLayout() && language != EShLangFragment) - error(loc, "can only use location layout qualifier on a vertex input or fragment output", id->c_str(), ""); + if (qualifier.hasLocation()) { + if (profile == EEsProfile) + requireStage(loc, EShLangFragment, "output location layout qualifier"); + } } else { if (qualifier.layoutMatrix != ElmNone || qualifier.layoutPacking != ElpNone) diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index a7de01ac277fa38e8a0d3bab6103a9eefd815215..cc6378467f2894cc93d0183420e754b01fc2d60d 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -126,6 +126,7 @@ public: void setLayoutQualifier(TSourceLoc, TPublicType&, TString&); void setLayoutQualifier(TSourceLoc, TPublicType&, TString&, int); void mergeLayoutQualifiers(TSourceLoc, TQualifier& dest, const TQualifier& src); + void layoutCheck(TSourceLoc, const TSymbol&); const TFunction* findFunction(TSourceLoc, TFunction* pfnCall, bool *builtIn = 0); TIntermNode* declareVariable(TSourceLoc, TString& identifier, TPublicType&, TArraySizes* typeArray = 0, TIntermTyped* initializer = 0);