Add geometry-shader stream, invocations, max_vertices, lines, triangles, etc. layout qualifiers, and their default/inheritance behaviors, and some other misc. geometry shader features. (Geometry shaders are not yet done though.)

git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@23679 e7fa87d3-cd2b-0410-9028-fcbf551c1848
This commit is contained in:
John Kessenich 2013-10-23 19:34:05 +00:00
parent 284231c9c5
commit 94fdd1117b
24 changed files with 639 additions and 225 deletions

View file

@ -176,14 +176,8 @@ protected:
};
//
// 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.
//
// Not as bad as it looks, there is no actual assumption that the fields
// match up or are named the same or anything like that.
// Following are a series of helper enums for managing layouts and qualifiers,
// used for TPublicType, TType, others.
//
enum TLayoutPacking {
@ -191,14 +185,32 @@ enum TLayoutPacking {
ElpShared, // default, but different than saying nothing
ElpStd140,
ElpStd430,
ElpPacked // see bitfield width below
ElpPacked
// If expanding, see bitfield width below
};
enum TLayoutMatrix {
ElmNone,
ElmRowMajor,
ElmColumnMajor // default, but different than saying nothing
}; // see bitfield width below
// If expanding, see bitfield width below
};
// Union of geometry shader and tessellation shader geometry types.
// They don't go into TType, but rather have current state per shader or
// active parser type (TPublicType).
enum TLayoutGeometry {
ElgNone,
ElgPoints,
ElgLines,
ElgLinesAdjacency,
ElgLineStrip,
ElgTriangles,
ElgTrianglesAdjacency,
ElgTriangleStrip,
ElgQuads,
ElgIsolines,
};
class TQualifier {
public:
@ -298,13 +310,15 @@ public:
layoutPacking = ElpNone;
layoutSlotLocation = layoutLocationEnd;
layoutBinding = layoutBindingEnd;
layoutStream = layoutStreamEnd;
}
bool hasLayout() const
{
return layoutMatrix != ElmNone ||
layoutPacking != ElpNone ||
hasLocation() ||
hasBinding();
hasBinding() ||
hasStream();
}
TLayoutMatrix layoutMatrix : 3;
TLayoutPacking layoutPacking : 4;
@ -312,6 +326,8 @@ public:
static const unsigned int layoutLocationEnd = 0x3F;
unsigned int layoutBinding : 8;
static const unsigned int layoutBindingEnd = 0xFF;
unsigned int layoutStream : 8;
static const unsigned int layoutStreamEnd = 0xFF;
bool hasLocation() const
{
return layoutSlotLocation != layoutLocationEnd;
@ -320,6 +336,10 @@ public:
{
return layoutBinding != layoutBindingEnd;
}
bool hasStream() const
{
return layoutStream != layoutStreamEnd;
}
static const char* getLayoutPackingString(TLayoutPacking packing)
{
switch (packing) {
@ -338,13 +358,50 @@ public:
default: return "none";
}
}
static const char* getGeometryString(TLayoutGeometry geometry)
{
switch (geometry) {
case ElgPoints: return "points";
case ElgLines: return "lines";
case ElgLinesAdjacency: return "lines_adjancency";
case ElgLineStrip: return "line_strip";
case ElgTriangles: return "triangles";
case ElgTrianglesAdjacency: return "triangles_adjacency";
case ElgTriangleStrip: return "triangle_strip";
case ElgQuads: return "quads";
case ElgIsolines: return "isolines";
default: return "none";
}
}
static int mapGeometryToSize(TLayoutGeometry geometry)
{
switch (geometry) {
case ElgPoints: return 1;
case ElgLines: return 2;
case ElgLinesAdjacency: return 4;
case ElgTriangles: return 3;
case ElgTrianglesAdjacency: return 6;
default: return 0;
}
}
};
//
// TPublicType is just temporarily used while parsing and not quite the same
// information kept per node in TType. Due to the bison 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.
// Once enough is known about the situation, the proper information
// moved into a TType, or the parse context, etc.
//
class TPublicType {
public:
TBasicType basicType;
TSampler sampler;
TQualifier qualifier;
TLayoutGeometry geometry; // don't keep this in the qualifier; it's more a per shader than per type
int invocations; // 0 means no declaration
int maxVertices;
int vectorSize : 4;
int matrixCols : 4;
int matrixRows : 4;
@ -375,6 +432,9 @@ public:
initType(loc);
sampler.clear();
initQualifiers(global);
geometry = ElgNone;
invocations = 0; // 0 means no declaration
maxVertices = 0;
}
void setVector(int s)
@ -641,6 +701,8 @@ public:
p += snprintf(p, end - p, "location=%d ", qualifier.layoutSlotLocation);
if (qualifier.hasBinding())
p += snprintf(p, end - p, "binding=%d ", qualifier.layoutBinding);
if (qualifier.hasStream())
p += snprintf(p, end - p, "stream=%d ", qualifier.layoutStream);
if (qualifier.layoutMatrix != ElmNone)
p += snprintf(p, end - p, "%s ", TQualifier::getLayoutMatrixString(qualifier.layoutMatrix));
if (qualifier.layoutPacking != ElpNone)

View file

@ -77,19 +77,17 @@ TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, bool pb,
defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow;
switch (language) {
case EShLangVertex:
defaultPrecision[EbtInt] = EpqHigh;
defaultPrecision[EbtUint] = EpqHigh;
defaultPrecision[EbtFloat] = EpqHigh;
defaultPrecision[EbtSampler] = EpqLow;
break;
case EShLangFragment:
defaultPrecision[EbtInt] = EpqMedium;
defaultPrecision[EbtUint] = EpqMedium;
defaultPrecision[EbtSampler] = EpqLow;
break;
default:
infoSink.info.message(EPrefixError, "unexpected es-profile stage");
defaultPrecision[EbtInt] = EpqHigh;
defaultPrecision[EbtUint] = EpqHigh;
defaultPrecision[EbtFloat] = EpqHigh;
defaultPrecision[EbtSampler] = EpqLow;
break;
}
}
@ -104,6 +102,8 @@ TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, bool pb,
globalInputDefaults.clear();
globalOutputDefaults.clear();
if (language == EShLangGeometry)
globalOutputDefaults.layoutStream = 0;
}
//
@ -1587,7 +1587,7 @@ void TParseContext::mergeQualifiers(TSourceLoc loc, TQualifier& dst, const TQual
dst.precision = src.precision;
// Layout qualifiers
mergeLayoutQualifiers(loc, dst, src);
mergeObjectLayoutQualifiers(loc, dst, src);
// individual qualifiers
bool repeated = false;
@ -2109,26 +2109,68 @@ void TParseContext::finalize()
void TParseContext::setLayoutQualifier(TSourceLoc loc, TPublicType& publicType, TString& id)
{
std::transform(id.begin(), id.end(), id.begin(), ::tolower);
if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor))
if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) {
publicType.qualifier.layoutMatrix = ElmColumnMajor;
else if (id == TQualifier::getLayoutMatrixString(ElmRowMajor))
return;
}
if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) {
publicType.qualifier.layoutMatrix = ElmRowMajor;
else if (id == TQualifier::getLayoutPackingString(ElpPacked))
return;
}
if (id == TQualifier::getLayoutPackingString(ElpPacked)) {
publicType.qualifier.layoutPacking = ElpPacked;
else if (id == TQualifier::getLayoutPackingString(ElpShared))
return;
}
if (id == TQualifier::getLayoutPackingString(ElpShared)) {
publicType.qualifier.layoutPacking = ElpShared;
else if (id == TQualifier::getLayoutPackingString(ElpStd140))
return;
}
if (id == TQualifier::getLayoutPackingString(ElpStd140)) {
publicType.qualifier.layoutPacking = ElpStd140;
else if (id == TQualifier::getLayoutPackingString(ElpStd430)) {
return;
}
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")
error(loc, "requires an integer assignment (e.g., location = 4)", "location", "");
else if (id == "binding") {
error(loc, "requires an integer assignment (e.g., binding = 4)", "binding", "");
} else
error(loc, "unrecognized layout identifier", id.c_str(), "");
return;
}
if (language == EShLangGeometry || language == EShLangTessEvaluation) {
if (id == TQualifier::getGeometryString(ElgTriangles)) {
publicType.geometry = ElgTriangles;
return;
}
if (language == EShLangGeometry) {
if (id == TQualifier::getGeometryString(ElgPoints)) {
publicType.geometry = ElgPoints;
return;
}
if (id == TQualifier::getGeometryString(ElgLineStrip)) {
publicType.geometry = ElgLineStrip;
return;
}
if (id == TQualifier::getGeometryString(ElgLines)) {
publicType.geometry = ElgLines;
return;
}
if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) {
publicType.geometry = ElgLinesAdjacency;
return;
}
if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) {
publicType.geometry = ElgTrianglesAdjacency;
return;
}
if (id == TQualifier::getGeometryString(ElgTriangleStrip)) {
publicType.geometry = ElgTriangleStrip;
return;
}
} else {
// TODO: tessellation evaluation
}
}
error(loc, "unrecognized layout identifier, or qualifier requires assignemnt (e.g., binding = 4)", id.c_str(), "");
}
// Put the id's layout qualifier value into the public type. This is before we know any
@ -2143,22 +2185,54 @@ void TParseContext::setLayoutQualifier(TSourceLoc loc, TPublicType& publicType,
error(loc, "location is too large", id.c_str(), "");
else
publicType.qualifier.layoutSlotLocation = value;
} else if (id == "binding") {
return;
}
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(), "");
return;
}
if (language == EShLangGeometry) {
if (id == "invocations") {
profileRequires(loc, ECompatibilityProfile | ECoreProfile, 400, 0, "invocations");
publicType.invocations = value;
return;
}
if (id == "max_vertices") {
publicType.maxVertices = value;
return;
}
if (id == "stream") {
publicType.qualifier.layoutStream = value;
return;
}
}
error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), "");
// TODO: semantics: error check: make sure locations are non-overlapping across the whole stage
// TODO: semantics: error check: output arrays can only be indexed with a constant (es 300)
}
//
// Merge characteristics of the 'src' qualifier into the 'dst', at the TPublicType level,
// which means for layout-qualifier information not kept per qualifier.
//
void TParseContext::mergeShaderLayoutQualifiers(TSourceLoc loc, TPublicType& dst, const TPublicType& src)
{
if (src.geometry != ElgNone)
dst.geometry = src.geometry;
if (src.invocations != 0)
dst.invocations = src.invocations;
if (src.maxVertices != 0)
dst.maxVertices = src.maxVertices;
}
// Merge any layout qualifier information from src into dst, leaving everything else in dst alone
void TParseContext::mergeLayoutQualifiers(TSourceLoc loc, TQualifier& dst, const TQualifier& src)
void TParseContext::mergeObjectLayoutQualifiers(TSourceLoc loc, TQualifier& dst, const TQualifier& src)
{
if (src.layoutMatrix != ElmNone)
dst.layoutMatrix = src.layoutMatrix;
@ -2171,40 +2245,31 @@ void TParseContext::mergeLayoutQualifiers(TSourceLoc loc, TQualifier& dst, const
if (src.hasBinding())
dst.layoutBinding = src.layoutBinding;
if (src.hasStream())
dst.layoutStream = src.layoutStream;
}
// Do error layout error checking given a full variable/block declaration.
void TParseContext::layoutCheck(TSourceLoc loc, const TSymbol& symbol)
void TParseContext::layoutTypeCheck(TSourceLoc loc, const TSymbol& symbol)
{
const TType& type = symbol.getType();
const TQualifier& qualifier = type.getQualifier();
// first, qualifier only error checking
layoutQualifierCheck(loc, qualifier);
// now, error checking combining type and qualifier
if (qualifier.hasLocation()) {
switch (qualifier.storage) {
case EvqVaryingIn:
{
const char* feature = "location qualifier on input";
if (profile == EEsProfile)
requireStage(loc, EShLangVertex, feature);
requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature);
if (language == EShLangVertex)
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 330, 0, feature);
else
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 410, GL_ARB_separate_shader_objects, feature);
if (type.getBasicType() == EbtBlock)
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, 0 /* TODO ARB_enhanced_layouts*/, "location qualifier on input block");
break;
}
case EvqVaryingOut:
{
const char* feature = "location qualifier on output";
if (profile == EEsProfile)
requireStage(loc, EShLangFragment, feature);
requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature);
if (language == EShLangFragment)
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 330, 0, feature);
else
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 410, GL_ARB_separate_shader_objects, feature);
if (type.getBasicType() == EbtBlock)
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, 0 /* TODO ARB_enhanced_layouts*/, "location qualifier on output block");
break;
@ -2213,8 +2278,6 @@ void TParseContext::layoutCheck(TSourceLoc loc, const TSymbol& symbol)
case EvqBuffer:
{
const char* feature = "location qualifier on uniform or buffer";
requireProfile(loc, ECoreProfile | ECompatibilityProfile, feature);
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, 0, feature);
if (symbol.getAsVariable() == 0)
error(loc, "can only be used on variable declaration", feature, "");
break;
@ -2235,19 +2298,78 @@ void TParseContext::layoutCheck(TSourceLoc loc, const TSymbol& symbol)
//
// 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
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// Non-Errors.
//
/////////////////////////////////////////////////////////////////////////////////
// Do layout error checking that can be done within a qualifier proper, not needing to know
// if there are blocks, atomic counters, variables, etc.
void TParseContext::layoutQualifierCheck(TSourceLoc loc, const TQualifier& qualifier)
{
if (qualifier.hasLocation()) {
switch (qualifier.storage) {
case EvqVaryingIn:
{
const char* feature = "location qualifier on input";
if (profile == EEsProfile)
requireStage(loc, EShLangVertex, feature);
requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature);
if (language == EShLangVertex)
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 330, 0, feature);
else
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 410, GL_ARB_separate_shader_objects, feature);
break;
}
case EvqVaryingOut:
{
const char* feature = "location qualifier on output";
if (profile == EEsProfile)
requireStage(loc, EShLangFragment, feature);
requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature);
if (language == EShLangFragment)
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 330, 0, feature);
else
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 410, GL_ARB_separate_shader_objects, feature);
break;
}
case EvqUniform:
case EvqBuffer:
{
const char* feature = "location qualifier on uniform or buffer";
requireProfile(loc, ECoreProfile | ECompatibilityProfile, feature);
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, 0, feature);
break;
}
default:
break;
}
}
if (qualifier.hasBinding()) {
if (qualifier.storage != EvqUniform && qualifier.storage != EvqBuffer)
error(loc, "requires uniform or buffer storage qualifier", "binding", "");
}
if (qualifier.hasStream()) {
if (qualifier.storage != EvqVaryingOut)
error(loc, "can only be used on an output", "stream", "");
}
}
// For places that can't have shader-level layout qualifiers
void TParseContext::checkNoShaderLayouts(TSourceLoc loc, const TPublicType& publicType)
{
const char* message = "can only apply to a standalone qualifier";
if (publicType.geometry != ElgNone)
error(loc, message, TQualifier::getGeometryString(publicType.geometry), "");
if (publicType.invocations > 0)
error(loc, message, "invocations", "");
if (publicType.maxVertices > 0)
error(loc, message, "max_vertices", "");
}
//
// Look up a function name in the symbol table, and make sure it is a function.
@ -2292,6 +2414,13 @@ TIntermNode* TParseContext::declareVariable(TSourceLoc loc, TString& identifier,
if (! initializer)
nonInitConstCheck(loc, identifier, type);
// Pick up defaults
if (! type.getQualifier().hasStream() && language == EShLangGeometry && type.getQualifier().storage == EvqVaryingOut)
type.getQualifier().layoutStream = globalOutputDefaults.layoutStream;
if (publicType.geometry != ElgNone)
error(loc, "geometry primitive qualifier cannot be applied to a variable declaration", TQualifier::getGeometryString(publicType.geometry), "");
// Check for redeclaration of built-ins and/or attempting to declare a reserved name
bool newDeclaration = false; // true if a new entry gets added to the symbol table
TSymbol* symbol = redeclareBuiltin(loc, identifier, newDeclaration);
@ -2331,9 +2460,9 @@ TIntermNode* TParseContext::declareVariable(TSourceLoc loc, TString& identifier,
initNode = executeInitializer(loc, identifier, initializer, variable);
}
// look for errors in layout qualifier use
// look for errors/adjustments in layout qualifier use
if (symbol)
layoutCheck(loc, *symbol);
layoutTypeCheck(loc, *symbol);
// see if it's a linker-level object to track
if (symbol && newDeclaration && symbolTable.atGlobalLevel())
@ -2732,7 +2861,7 @@ void TParseContext::addBlock(TSourceLoc loc, TTypeList& typeList, const TString*
if (profile == EEsProfile && arraySizes)
arraySizeRequiredCheck(loc, arraySizes->getSize());
switch (currentBlockDefaults.storage) {
switch (currentBlockQualifier.storage) {
case EvqBuffer:
requireProfile(loc, ECoreProfile | ECompatibilityProfile, "buffer block");
profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, 0, "buffer block");
@ -2754,14 +2883,14 @@ void TParseContext::addBlock(TSourceLoc loc, TTypeList& typeList, const TString*
arrayDimCheck(loc, arraySizes, 0);
// fix and check for member qualifiers and types that don't belong within a block
// fix and check for member storage 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;
pipeInOutFix(memberLoc, memberQualifier);
if (memberQualifier.storage != EvqTemporary && memberQualifier.storage != EvqGlobal && memberQualifier.storage != currentBlockDefaults.storage)
if (memberQualifier.storage != EvqTemporary && memberQualifier.storage != EvqGlobal && memberQualifier.storage != currentBlockQualifier.storage)
error(memberLoc, "member storage qualifier cannot contradict block storage qualifier", typeList[member].type->getFieldName().c_str(), "");
if ((currentBlockDefaults.storage == EvqUniform && memberQualifier.isInterpolation()) || memberQualifier.isAuxiliary())
if ((currentBlockQualifier.storage == EvqUniform && memberQualifier.isInterpolation()) || memberQualifier.isAuxiliary())
error(memberLoc, "member of uniform block cannot have an auxiliary or interpolation qualifier", typeList[member].type->getFieldName().c_str(), "");
TBasicType basicType = typeList[member].type->getBasicType();
@ -2772,7 +2901,7 @@ void TParseContext::addBlock(TSourceLoc loc, TTypeList& typeList, const TString*
// Make default block qualification, and adjust the member qualifications
TQualifier defaultQualification;
switch (currentBlockDefaults.storage) {
switch (currentBlockQualifier.storage) {
case EvqBuffer: defaultQualification = globalBufferDefaults; break;
case EvqUniform: defaultQualification = globalUniformDefaults; break;
case EvqVaryingIn: defaultQualification = globalInputDefaults; break;
@ -2780,19 +2909,31 @@ void TParseContext::addBlock(TSourceLoc loc, TTypeList& typeList, const TString*
default: defaultQualification.clear(); break;
}
mergeLayoutQualifiers(loc, defaultQualification, currentBlockDefaults);
// fix and check for member layout qualifiers
mergeObjectLayoutQualifiers(loc, defaultQualification, currentBlockQualifier);
for (unsigned int member = 0; member < typeList.size(); ++member) {
TQualifier memberQualification = defaultQualification;
mergeQualifiers(loc, memberQualification, typeList[member].type->getQualifier(), false);
typeList[member].type->getQualifier() = memberQualification;
TQualifier& memberQualifier = typeList[member].type->getQualifier();
TSourceLoc memberLoc = typeList[member].loc;
if (memberQualifier.hasStream()) {
if (defaultQualification.layoutStream != memberQualifier.layoutStream)
error(memberLoc, "member cannot contradict block", "stream", "");
}
TQualifier newMemberQualification = defaultQualification;
mergeQualifiers(memberLoc, newMemberQualification, memberQualifier, false);
memberQualifier = newMemberQualification;
}
//
// Build and add the interface block as a new type named blockName
//
TType blockType(&typeList, *blockName, currentBlockDefaults);
// reverse merge, so that currentBlockQualifier now has all layout information
// (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers)
mergeObjectLayoutQualifiers(loc, currentBlockQualifier, defaultQualification);
TType blockType(&typeList, *blockName, currentBlockQualifier);
if (arraySizes)
blockType.setArraySizes(arraySizes);
blockType.getQualifier().layoutPacking = defaultQualification.layoutPacking;
//
// Don't make a user-defined type out of block name; that will cause an error
@ -2834,7 +2975,7 @@ void TParseContext::addBlock(TSourceLoc loc, TTypeList& typeList, const TString*
}
// Check for general layout qualifier errors
layoutCheck(loc, variable);
layoutTypeCheck(loc, variable);
// Save it in the AST for linker use.
intermediate.addSymbolLinkageNode(linkage, variable);
@ -2879,55 +3020,76 @@ void TParseContext::addQualifierToExisting(TSourceLoc loc, TQualifier qualifier,
}
//
// Update qualifier defaults for all forms of declarations, which
// must error check for their form before calling here.
// Updating default qualifier for the case of a declaration with just a qualifier,
// no type, block, or identifier.
//
void TParseContext::updateQualifierDefaults(TQualifier qualifier)
void TParseContext::updateStandaloneQualifierDefaults(TSourceLoc loc, const TPublicType& publicType)
{
switch (qualifier.storage) {
case EvqBuffer:
if (qualifier.layoutMatrix != ElmNone)
globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix;
if (qualifier.layoutPacking != ElpNone)
globalBufferDefaults.layoutPacking = qualifier.layoutPacking;
break;
case EvqUniform:
if (qualifier.layoutMatrix != ElmNone)
globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix;
if (qualifier.layoutPacking != ElpNone)
globalUniformDefaults.layoutPacking = qualifier.layoutPacking;
break;
case EvqVaryingIn:
if (qualifier.hasLocation())
globalInputDefaults.layoutSlotLocation = qualifier.layoutSlotLocation;
break;
case EvqVaryingOut:
if (qualifier.hasLocation())
globalOutputDefaults.layoutSlotLocation = qualifier.layoutSlotLocation;
break;
default:
// error handling should be done by callers of this function
break;
if (publicType.maxVertices) {
if (! intermediate.setMaxVertices(publicType.maxVertices))
error(loc, "cannot change previously set layout value", "max_vertices", "");
}
if (publicType.invocations) {
if (! intermediate.setInvocations(publicType.invocations))
error(loc, "cannot change previously set layout value", "invocations", "");
}
if (publicType.geometry != ElgNone) {
if (publicType.qualifier.storage == EvqVaryingIn) {
switch (publicType.geometry) {
case ElgPoints:
case ElgLines:
case ElgLinesAdjacency:
case ElgTriangles:
case ElgTrianglesAdjacency:
if (! intermediate.setInputPrimitive(publicType.geometry))
error(loc, "cannot change previously set input primitive", TQualifier::getGeometryString(publicType.geometry), "");
break;
default:
error(loc, "does not apply to input", TQualifier::getGeometryString(publicType.geometry), "");
}
} else if (publicType.qualifier.storage == EvqVaryingOut) {
switch (publicType.geometry) {
case ElgPoints:
case ElgLineStrip:
case ElgTriangleStrip:
if (! intermediate.setOutputPrimitive(publicType.geometry))
error(loc, "cannot change previously set output primitive", TQualifier::getGeometryString(publicType.geometry), "");
break;
default:
error(loc, "does not only apply to output", TQualifier::getGeometryString(publicType.geometry), "");
}
} else
error(loc, "cannot be used here", TQualifier::getGeometryString(publicType.geometry), "");
}
}
//
// 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)
{
const TQualifier& qualifier = publicType.qualifier;
if (qualifier.isAuxiliary() ||
qualifier.isMemory() ||
qualifier.isInterpolation() ||
qualifier.precision != EpqNone)
error(loc, "cannot use auxiliary, memory, interpolation, or precision qualifier in a default qualifier declaration (declaration with no type)", "", "");
layoutQualifierCheck(loc, qualifier);
switch (qualifier.storage) {
case EvqUniform:
if (qualifier.layoutMatrix != ElmNone)
globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix;
if (qualifier.layoutPacking != ElpNone)
globalUniformDefaults.layoutPacking = qualifier.layoutPacking;
break;
case EvqBuffer:
if (qualifier.layoutMatrix != ElmNone)
globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix;
if (qualifier.layoutPacking != ElpNone)
globalBufferDefaults.layoutPacking = qualifier.layoutPacking;
break;
case EvqVaryingIn:
break;
case EvqVaryingOut:
if (qualifier.hasStream())
globalOutputDefaults.layoutStream = qualifier.layoutStream;
break;
default:
error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", "");
@ -2938,18 +3100,14 @@ void TParseContext::updateQualifierDefaults(TSourceLoc loc, TQualifier qualifier
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 identifier.
// (But, not the case of just a qualifier; only when a type is present.)
// (But, not the case of just a qualifier.)
//
void TParseContext::updateTypedDefaults(TSourceLoc loc, TQualifier qualifier, const TString* id)
void TParseContext::updateTypedDefaults(TSourceLoc loc, const TQualifier& qualifier, const TString* id)
{
bool cantHaveId = false;
if (! id) {
if (qualifier.hasLayout())
warn(loc, "cannot set qualifier defaults when using a type and no identifier", "", "");
@ -2966,8 +3124,12 @@ void TParseContext::updateTypedDefaults(TSourceLoc loc, TQualifier qualifier, co
error(loc, "cannot specify packing on a variable declaration", id->c_str(), "");
break;
case EvqVaryingIn:
if (qualifier.hasLocation())
globalInputDefaults.layoutSlotLocation = qualifier.layoutSlotLocation;
break;
case EvqVaryingOut:
if (qualifier.hasLocation())
globalOutputDefaults.layoutSlotLocation = qualifier.layoutSlotLocation;
break;
default:
if (qualifier.layoutMatrix != ElmNone ||
@ -2976,11 +3138,6 @@ void TParseContext::updateTypedDefaults(TSourceLoc loc, TQualifier qualifier, co
else if (qualifier.hasLocation())
error(loc, "location qualifiers only appy to uniform, buffer, in, or out storage qualifiers", id->c_str(), "");
}
if (cantHaveId)
error(loc, "cannot set global layout qualifiers on uniform variable, use just 'uniform' or a block", id->c_str(), "");
updateQualifierDefaults(qualifier);
}
//

View file

@ -126,8 +126,11 @@ 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&);
void mergeShaderLayoutQualifiers(TSourceLoc, TPublicType& dst, const TPublicType& src);
void mergeObjectLayoutQualifiers(TSourceLoc, TQualifier& dest, const TQualifier& src);
void layoutTypeCheck(TSourceLoc, const TSymbol&);
void layoutQualifierCheck(TSourceLoc, const TQualifier&);
void checkNoShaderLayouts(TSourceLoc, const TPublicType&);
const TFunction* findFunction(TSourceLoc, TFunction* pfnCall, bool *builtIn = 0);
TIntermNode* declareVariable(TSourceLoc, TString& identifier, TPublicType&, TArraySizes* typeArray = 0, TIntermTyped* initializer = 0);
@ -137,9 +140,8 @@ public:
void addBlock(TSourceLoc, TTypeList& typeList, const TString* instanceName = 0, TArraySizes* arraySizes = 0);
void addQualifierToExisting(TSourceLoc, TQualifier, const TString& identifier);
void addQualifierToExisting(TSourceLoc, TQualifier, TIdentifierList&);
void updateQualifierDefaults(TQualifier);
void updateQualifierDefaults(TSourceLoc, TQualifier);
void updateTypedDefaults(TSourceLoc, TQualifier, const TString* id);
void updateStandaloneQualifierDefaults(TSourceLoc, const TPublicType&);
void updateTypedDefaults(TSourceLoc, const TQualifier&, const TString* id);
void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode);
TIntermNode* addSwitch(TSourceLoc, TIntermTyped* expression, TIntermAggregate* body);
TIntermTyped* addConstVectorNode(TVectorFields&, TIntermTyped*, TSourceLoc);
@ -202,7 +204,7 @@ public:
const TType* currentFunctionType; // the return type of the function that's currently being parsed
bool functionReturnsValue; // true if a non-void function has a return
const TString* blockName;
TQualifier currentBlockDefaults;
TQualifier currentBlockQualifier;
TIntermAggregate *linkage; // aggregate node of objects the linker may need, if not referenced by the rest of the AST
TPrecisionQualifier defaultPrecision[EbtNumTypes];
TSourceLoc currentLoc;
@ -214,7 +216,7 @@ protected:
TPpContext* ppContext;
int numErrors; // number of compile-time errors encountered
bool parsingBuiltins; // true if parsing built-in symbols/functions
TMap<TString, TExtensionBehavior> extensionBehavior; // for each extension string, what it's current behavior is set to
TMap<TString, TExtensionBehavior> extensionBehavior; // for each extension string, what its current behavior is set to
static const int maxSamplerIndex = EsdNumDims * (EbtNumTypes * (2 * 2)); // see computeSamplerTypeIndex()
TPrecisionQualifier defaultSamplerPrecision[maxSamplerIndex];
bool afterEOF;

View file

@ -159,7 +159,8 @@ const char* TParseContext::getPreamble()
return
"#define GL_ES 1\n";
} else {
return
return
"#define GL_FRAGMENT_PRECISION_HIGH 1\n"
"#define GL_ARB_texture_rectangle 1\n"
"#define GL_ARB_shading_language_420pack 1\n"
"#define GL_ARB_texture_gather 1\n"

View file

@ -774,16 +774,18 @@ declaration
}
| type_qualifier SEMICOLON {
parseContext.pipeInOutFix($1.loc, $1.qualifier);
parseContext.updateQualifierDefaults($1.loc, $1.qualifier);
parseContext.updateStandaloneQualifierDefaults($1.loc, $1);
$$ = 0;
}
| type_qualifier IDENTIFIER SEMICOLON {
parseContext.pipeInOutFix($1.loc, $1.qualifier);
parseContext.checkNoShaderLayouts($1.loc, $1);
parseContext.addQualifierToExisting($1.loc, $1.qualifier, *$2.string);
$$ = 0;
}
| type_qualifier IDENTIFIER identifier_list SEMICOLON {
parseContext.pipeInOutFix($1.loc, $1.qualifier);
parseContext.checkNoShaderLayouts($1.loc, $1);
$3->push_back($2.string);
parseContext.addQualifierToExisting($1.loc, $1.qualifier, *$3);
$$ = 0;
@ -795,7 +797,8 @@ block_structure
--parseContext.structNestingLevel;
parseContext.blockName = $2.string;
parseContext.pipeInOutFix($1.loc, $1.qualifier);
parseContext.currentBlockDefaults = $1.qualifier;
parseContext.checkNoShaderLayouts($1.loc, $1);
parseContext.currentBlockQualifier = $1.qualifier;
$$.loc = $1.loc;
$$.typeList = $5;
}
@ -915,7 +918,8 @@ parameter_declaration
$$ = $2;
if ($1.qualifier.precision != EpqNone)
$$.param.type->getQualifier().precision = $1.qualifier.precision;
parseContext.checkNoShaderLayouts($1.loc, $1);
parseContext.parameterSamplerCheck($2.loc, $1.qualifier.storage, *$$.param.type);
parseContext.paramCheck($1.loc, $1.qualifier.storage, $$.param.type);
}
@ -932,7 +936,8 @@ parameter_declaration
$$ = $2;
if ($1.qualifier.precision != EpqNone)
$$.param.type->getQualifier().precision = $1.qualifier.precision;
parseContext.checkNoShaderLayouts($1.loc, $1);
parseContext.parameterSamplerCheck($2.loc, $1.qualifier.storage, *$$.param.type);
parseContext.paramCheck($1.loc, $1.qualifier.storage, $$.param.type);
}
@ -1033,6 +1038,7 @@ fully_specified_type
if ($2.arraySizes && parseContext.arrayQualifierError($2.loc, $1.qualifier))
$2.arraySizes = 0;
parseContext.checkNoShaderLayouts($2.loc, $1);
parseContext.mergeQualifiers($2.loc, $2.qualifier, $1.qualifier, true);
parseContext.precisionQualifierCheck($2.loc, $2);
@ -1089,7 +1095,8 @@ layout_qualifier_id_list
}
| layout_qualifier_id_list COMMA layout_qualifier_id {
$$ = $1;
parseContext.mergeLayoutQualifiers($2.loc, $$.qualifier, $3.qualifier);
parseContext.mergeShaderLayoutQualifiers($2.loc, $$, $3);
parseContext.mergeObjectLayoutQualifiers($2.loc, $$.qualifier, $3.qualifier);
}
layout_qualifier_id
@ -1127,6 +1134,7 @@ type_qualifier
if ($$.basicType == EbtVoid)
$$.basicType = $2.basicType;
parseContext.mergeShaderLayoutQualifiers($$.loc, $$, $2);
parseContext.mergeQualifiers($$.loc, $$.qualifier, $2.qualifier, false);
}
;
@ -2043,6 +2051,7 @@ struct_declaration
$$ = $3;
parseContext.checkNoShaderLayouts($1.loc, $1);
parseContext.voidErrorCheck($2.loc, (*$3)[0].type->getFieldName(), $2.basicType);
parseContext.mergeQualifiers($2.loc, $2.qualifier, $1.qualifier, true);
parseContext.precisionQualifierCheck($2.loc, $2);

View file

@ -579,6 +579,13 @@ bool OutputSwitch(bool /* preVisit */, TIntermSwitch* node, TIntermTraverser* it
//
void TIntermediate::outputTree(TInfoSink& infoSink)
{
if (language == EShLangGeometry) {
infoSink.debug << "invocations = " << invocations << "\n";
infoSink.debug << "max_vertices = " << maxVertices << "\n";
infoSink.debug << "input primitive = " << TQualifier::getGeometryString(inputPrimitive) << "\n";
infoSink.debug << "output primitive = " << TQualifier::getGeometryString(outputPrimitive) << "\n";
}
if (treeRoot == 0)
return;

View file

@ -56,7 +56,8 @@ class TSymbol;
//
class TIntermediate {
public:
explicit TIntermediate(EShLanguage l, int v = 0, EProfile p = ENoProfile) : language(l), treeRoot(0), profile(p), version(v), numMains(0), numErrors(0) { }
explicit TIntermediate(EShLanguage l, int v = 0, EProfile p = ENoProfile) : language(l), treeRoot(0), profile(p), version(v), numMains(0), numErrors(0),
invocations(0), maxVertices(0), inputPrimitive(ElgNone), outputPrimitive(ElgNone) { }
void setVersion(int v) { version = v; }
int getVersion() const { return version; }
@ -99,6 +100,36 @@ public:
void addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable&, const TString&);
void addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol&);
bool setInvocations(int i)
{
if (invocations > 0)
return false;
invocations = i;
return true;
}
bool setMaxVertices(int m)
{
if (maxVertices > 0)
return maxVertices == m;
maxVertices = m;
return true;
}
bool setInputPrimitive(TLayoutGeometry p)
{
if (inputPrimitive != ElgNone)
return inputPrimitive == p;
inputPrimitive = p;
return true;
}
TLayoutGeometry getInputPrimitive() { return inputPrimitive; }
bool setOutputPrimitive(TLayoutGeometry p)
{
if (outputPrimitive != ElgNone)
return outputPrimitive == p;
outputPrimitive = p;
return true;
}
void addToCallGraph(TInfoSink&, const TString& caller, const TString& callee);
void merge(TInfoSink&, TIntermediate&);
void errorCheck(TInfoSink&);
@ -122,6 +153,10 @@ protected:
int version;
int numMains;
int numErrors;
int invocations;
int maxVertices;
TLayoutGeometry inputPrimitive;
TLayoutGeometry outputPrimitive;
// for detecting recursion: pair is <caller, callee>
struct TCall {