diff --git a/Test/300layout.frag b/Test/300layout.frag new file mode 100644 index 0000000000000000000000000000000000000000..618582d3cff7b692ebeb5ac020883a3ee6cb260e --- /dev/null +++ b/Test/300layout.frag @@ -0,0 +1,29 @@ +#version 300 es + +in vec4 pos; +in vec4 color; + +layout(location = 7) out vec4 c; +layout(location = 3) out vec4 p; + +//layout(std140) uniform Transform { // layout of this block is std140 +// mat4 M1; // row_major +// layout(column_major) mat4 M2; // column major +// mat3 N1; // row_major +//}; +// +//uniform T2 { // layout of this block is shared +//... +//}; +// +//layout(column_major) uniform T3 { // shared and column_major +// mat4 M3; // column_major +// layout(row_major) mat4 m4; // row major +// mat3 N2; // column_major +//}; + +void main() +{ + c = color; + p = pos; +} diff --git a/Test/300layout.vert b/Test/300layout.vert new file mode 100644 index 0000000000000000000000000000000000000000..fd177ddb24bda939bdcf1d5e3cca72a0cabbb580 --- /dev/null +++ b/Test/300layout.vert @@ -0,0 +1,30 @@ +#version 300 es + +layout(location = 7) in vec4 c; +layout(LocatioN = 3) in vec4 p; +out vec4 pos; +out vec4 color; + +layout(shared, column_major, row_major) uniform mat4 m4; // default is now shared and row_major + +//layout(std140) uniform Transform { // layout of this block is std140 +// mat4 M1; // row_major +// layout(column_major) mat4 M2; // column major +// mat3 N1; // row_major +//}; +// +//uniform T2 { // layout of this block is shared +//... +//}; +// +//layout(column_major) uniform T3 { // shared and column_major +// mat4 M3; // column_major +// layout(row_major) mat4 m4; // row major +// mat3 N2; // column_major +//}; + +void main() +{ + pos = p * m4; + color = c; +} diff --git a/Test/testlist b/Test/testlist index 7ae5e7bd49f2704c88fa26899b697bdfd6831064..36c3bc9f537a66ba7b5d347abdbe2b31867a8220 100644 --- a/Test/testlist +++ b/Test/testlist @@ -24,6 +24,8 @@ comment.frag 300.vert 300.frag 300BuiltIns.frag +300layout.vert +300layout.frag 330.frag 330comp.frag constErrors.frag diff --git a/glslang/Include/BaseTypes.h b/glslang/Include/BaseTypes.h index 613a0ba290c8b96db17862fd76049bd0607945e6..367ce36e7b519c09b8ce00ccb3ac084954716cdd 100644 --- a/glslang/Include/BaseTypes.h +++ b/glslang/Include/BaseTypes.h @@ -64,7 +64,8 @@ enum TStorageQualifier { EvqConst, // User defined constants and non-output parameters in functions EvqVaryingIn, // pipeline input, read only EvqVaryingOut, // pipeline ouput, read/write - EvqUniform, // Readonly, vertex and fragment + EvqUniform, // read only, shader with app + EVqBuffer, // read only, shader with app // parameters EvqIn, @@ -102,8 +103,8 @@ __inline const char* getStorageQualifierString(TStorageQualifier q) case EvqGlobal: return "global"; break; case EvqConst: return "const"; break; case EvqConstReadOnly: return "const (read only)"; break; - case EvqVaryingIn: return "shader in"; break; - case EvqVaryingOut: return "shader out"; break; + case EvqVaryingIn: return "in"; break; + case EvqVaryingOut: return "out"; break; case EvqUniform: return "uniform"; break; case EvqIn: return "in"; break; case EvqOut: return "out"; break; @@ -116,7 +117,7 @@ __inline const char* getStorageQualifierString(TStorageQualifier q) case EvqFace: return "gl_FrontFacing"; break; case EvqFragCoord: return "gl_FragCoord"; break; case EvqPointCoord: return "gl_PointCoord"; break; - case EvqFragColor: return "fragment out"; break; + case EvqFragColor: return "fragColor"; break; case EvqFragDepth: return "gl_FragDepth"; break; default: return "unknown qualifier"; } diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h index e1214ced6108e839f9aef8d19c73f37f197eaf80..b13f331489ed5d6a53ea195ad0123c0514be8a9c 100644 --- a/glslang/Include/Types.h +++ b/glslang/Include/Types.h @@ -171,7 +171,8 @@ inline TArraySizes NewPoolTArraySizes() } // -// TPublicType is a workaround for a problem with the yacc stack, It can't have +// TPublicType (coming up after some dependent declarations) +// is a workaround for a problem with the yacc stack, It can't have // types that it thinks have non-trivial constructors. It should // just be used while recognizing the grammar, not anything else. Pointers // could be used, but also trying to avoid lots of memory management overhead. @@ -180,13 +181,26 @@ inline TArraySizes NewPoolTArraySizes() // match up or are named the same or anything like that. // +enum TLayoutPacking { + ElpNone, + ElpShared, // default, but different than saying nothing + ElpStd140, + ElpStd430, + ElpPacked // see bitfield width below +}; + +enum TLayoutMatrix { + ElmNone, + ElmRowMajor, + ElmColumnMajor // default, but different than saying nothing +}; // see bitfield width below + class TQualifier { public: void clear() { storage = EvqTemporary; precision = EpqNone; - buffer = false; invariant = false; centroid = false; smooth = false; @@ -200,10 +214,10 @@ public: restrict = false; readonly = false; writeonly = false; + clearLayout(); } - TStorageQualifier storage : 7; + TStorageQualifier storage : 6; TPrecisionQualifier precision : 3; - bool buffer : 1; bool invariant : 1; bool centroid : 1; bool smooth : 1; @@ -217,6 +231,7 @@ public: bool restrict : 1; bool readonly : 1; bool writeonly : 1; + bool isMemory() { return coherent || volatil || restrict || readonly || writeonly; @@ -229,6 +244,46 @@ public: { return centroid || patch || sample; } + + // Implementing an embedded layout-qualifier class here, since C++ can't have a real class bitfield + void clearLayout() + { + layoutMatrix = ElmNone; + layoutPacking = ElpNone; + layoutSlotLocation = layoutLocationEnd; + } + bool hasLayout() const + { + return layoutMatrix != ElmNone || + layoutPacking != ElpNone || + layoutSlotLocation != layoutLocationEnd; + } + 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; + bool hasLocation() const + { + return layoutSlotLocation != layoutLocationEnd; + } + static const char* getLayoutPackingString(TLayoutPacking packing) + { + switch (packing) { + case ElpPacked: return "packed"; + case ElpShared: return "shared"; + case ElpStd140: return "std140"; + case ElpStd430: return "std430"; + default: return "none"; + } + } + static const char* getLayoutMatrixString(TLayoutMatrix m) + { + switch (m) { + case ElmColumnMajor: return "column_major"; + case ElmRowMajor: return "row_major"; + default: return "none"; + } + } }; class TPublicType { @@ -479,8 +534,17 @@ public: char *p = &buf[0]; char *end = &buf[maxSize]; - if (qualifier.buffer) - p += snprintf(p, end - p, "buffer "); + if (qualifier.hasLayout()) { + p += snprintf(p, end - p, "layout("); + if (qualifier.hasLocation()) + p += snprintf(p, end - p, "location=%d ", qualifier.layoutSlotLocation); + if (qualifier.layoutMatrix != ElmNone) + p += snprintf(p, end - p, "%s ", TQualifier::getLayoutMatrixString(qualifier.layoutMatrix)); + if (qualifier.layoutPacking != ElpNone) + p += snprintf(p, end - p, "%s ", TQualifier::getLayoutPackingString(qualifier.layoutPacking)); + p += snprintf(p, end - p, ") "); + } + if (qualifier.invariant) p += snprintf(p, end - p, "invariant "); if (qualifier.centroid) diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 2b35ae31667641c8d93c53214f5b9307affb1a6e..363af6016525fcf28b5dfc5a713adaae4ff97192 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -38,6 +38,7 @@ #include "Include/InitializeParseContext.h" #include "osinclude.h" #include <stdarg.h> +#include <algorithm> TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, int v, EProfile p, EShLanguage L, TInfoSink& is, bool fc, EShMessages m) : @@ -706,7 +707,7 @@ bool TParseContext::globalQualifierFixAndErrorCheck(int line, TQualifier& qualif } if (language == EShLangVertex && qualifier.storage == EvqVaryingIn && - (qualifier.isAuxillary() || qualifier.isInterpolation() || qualifier.isMemory() || qualifier.buffer || qualifier.invariant)) { + (qualifier.isAuxillary() || qualifier.isInterpolation() || qualifier.isMemory() || qualifier.invariant)) { error(line, "vertex input cannot be further qualified", "", ""); return true; @@ -748,9 +749,11 @@ bool TParseContext::mergeQualifiersErrorCheck(int line, TPublicType& left, const bad = true; } + // Layout qualifiers + mergeLayoutQualifiers(line, left, right); + // other qualifiers #define MERGE_SINGLETON(field) bad |= left.qualifier.field && right.qualifier.field; left.qualifier.field |= right.qualifier.field; - MERGE_SINGLETON(buffer); MERGE_SINGLETON(invariant); MERGE_SINGLETON(centroid); MERGE_SINGLETON(smooth); @@ -1086,6 +1089,62 @@ bool TParseContext::paramErrorCheck(int line, TStorageQualifier qualifier, TType } } +// +// Layout qualifier stuff. +// + +// Put the id's layout qualification into the public type. +void TParseContext::setLayoutQualifier(int line, TPublicType& publicType, TString& id) +{ + std::transform(id.begin(), id.end(), id.begin(), ::tolower); + if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) + publicType.qualifier.layoutMatrix = ElmColumnMajor; + else if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) + publicType.qualifier.layoutMatrix = ElmRowMajor; + else if (id == TQualifier::getLayoutPackingString(ElpPacked)) + publicType.qualifier.layoutPacking = ElpPacked; + else if (id == TQualifier::getLayoutPackingString(ElpShared)) + publicType.qualifier.layoutPacking = ElpShared; + else if (id == TQualifier::getLayoutPackingString(ElpStd140)) + publicType.qualifier.layoutPacking = ElpStd140; + else if (id == TQualifier::getLayoutPackingString(ElpStd430)) + publicType.qualifier.layoutPacking = ElpStd430; + else if (id == "location") + error(line, "requires an integer assignment (e.g., location = 4)", "location", ""); + else if (id == "binding") + error(line, "requires an integer assignment (e.g., binding = 4)", "binding", ""); + else + error(line, "unrecognized layout identifier", id.c_str(), ""); +} + +// Put the id's layout qualifier value into the public type. +void TParseContext::setLayoutQualifier(int line, TPublicType& publicType, TString& id, int value) +{ + std::transform(id.begin(), id.end(), id.begin(), ::tolower); + if (id == "location") { + if ((unsigned int)value >= TQualifier::layoutLocationEnd) + error(line, "value is too large", id.c_str(), ""); + else + publicType.qualifier.layoutSlotLocation = value; + } else if (id == "binding") + error(line, "not supported", "binding", ""); + else + error(line, "there is no such layout identifier taking an assigned value", id.c_str(), ""); +} + +// Merge any layout qualifier information from src into dst, leaving everything else in dst alone +void TParseContext::mergeLayoutQualifiers(int line, TPublicType& dst, const TPublicType& src) +{ + if (src.qualifier.layoutMatrix != ElmNone) + dst.qualifier.layoutMatrix = src.qualifier.layoutMatrix; + + if (src.qualifier.layoutPacking != ElpNone) + dst.qualifier.layoutPacking = src.qualifier.layoutPacking; + + if (src.qualifier.hasLocation()) + dst.qualifier.layoutSlotLocation = src.qualifier.layoutSlotLocation; +} + ///////////////////////////////////////////////////////////////////////////////// // // Non-Errors. diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index a5de17ac3c2fb76644e76a4e29fab1b4321ad3f2..13e334513d3537a9037935f09c0d9416f51ce0b5 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -129,6 +129,11 @@ struct TParseContext { bool nonInitConstErrorCheck(int line, TString& identifier, TPublicType& type); bool nonInitErrorCheck(int line, TString& identifier, TPublicType& type); bool paramErrorCheck(int line, TStorageQualifier qualifier, TType* type); + + void setLayoutQualifier(int line, TPublicType&, TString&); + void setLayoutQualifier(int line, TPublicType&, TString&, int); + void mergeLayoutQualifiers(int line, TPublicType& dest, const TPublicType& src); + const TFunction* findFunction(int line, TFunction* pfnCall, bool *builtIn = 0); bool executeInitializer(TSourceLoc line, TString& identifier, TPublicType& pType, TIntermTyped* initializer, TIntermNode*& intermNode, TVariable* variable = 0); diff --git a/glslang/MachineIndependent/glslang.y b/glslang/MachineIndependent/glslang.y index 67aa3d031241403e33c34aa99eb624e6f88bd7d0..6c84725ff882b29f7886d46f6a4917187864a50a 100644 --- a/glslang/MachineIndependent/glslang.y +++ b/glslang/MachineIndependent/glslang.y @@ -201,7 +201,7 @@ extern void yyerror(const char*); %type <interm> array_specifier %type <interm.type> precise_qualifier invariant_qualifier interpolation_qualifier storage_qualifier precision_qualifier -%type <interm.type> layout_qualifier layout_qualifier_id_list +%type <interm.type> layout_qualifier layout_qualifier_id_list layout_qualifier_id %type <interm.type> type_qualifier fully_specified_type type_specifier %type <interm.type> single_type_qualifier @@ -1622,22 +1622,31 @@ interpolation_qualifier layout_qualifier : LAYOUT LEFT_PAREN layout_qualifier_id_list RIGHT_PAREN { - $$.init($1.line); + $$ = $3; } ; layout_qualifier_id_list : layout_qualifier_id { + $$ = $1; } | layout_qualifier_id_list COMMA layout_qualifier_id { + $$ = $1; + parseContext.mergeLayoutQualifiers($2.line, $$, $3); } layout_qualifier_id : IDENTIFIER { + $$.init($1.line); + parseContext.setLayoutQualifier($1.line, $$, *$1.string); } | IDENTIFIER EQUAL INTCONSTANT { + $$.init($1.line); + parseContext.setLayoutQualifier($1.line, $$, *$1.string, $3.i); } - | SHARED { + | SHARED { // because "shared" is both an identifier and a keyword + $$.init($1.line); + parseContext.setLayoutQualifier($1.line, $$, TString("shared")); } ; @@ -1766,8 +1775,7 @@ storage_qualifier if (parseContext.globalErrorCheck($1.line, parseContext.symbolTable.atGlobalLevel(), "buffer")) parseContext.recover(); $$.init($1.line); - $$.qualifier.storage = EvqUniform; - $$.qualifier.buffer = true; + $$.qualifier.storage = EvqUniform; // TODO: functionality: implement BUFFER } | SHARED { $$.init($1.line);