From 61c2d1410a89c9e1a492621190560f69f5ab36dd Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Thu, 3 Oct 2013 20:23:57 +0000 Subject: [PATCH] Document how to add a new feature enabled by an extension in Versions.cpp. Also reorganized slightly to localize related functions. git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@23376 e7fa87d3-cd2b-0410-9028-fcbf551c1848 --- README.txt | 12 +- Todo.txt | 24 +-- glslang/MachineIndependent/Initialize.cpp | 2 +- glslang/MachineIndependent/Intermediate.cpp | 2 +- glslang/MachineIndependent/ParseHelper.cpp | 86 +------- glslang/MachineIndependent/ParseHelper.h | 33 ++- glslang/MachineIndependent/ShaderLang.cpp | 44 ++-- glslang/MachineIndependent/Versions.cpp | 284 ++++++++++++++++++++----- glslang/MachineIndependent/Versions.h | 32 ++- glslang/MachineIndependent/glslang.y | 10 +- glslang/MachineIndependent/preprocessor/Pp.cpp | 2 +- glslang/Public/ShaderLang.h | 2 +- 12 files changed, 336 insertions(+), 197 deletions(-) diff --git a/README.txt b/README.txt index 3c61ae5..b9650ec 100644 --- a/README.txt +++ b/README.txt @@ -7,10 +7,13 @@ There are two components: 2) A standalone wrapper, glslangValidator, that can be used as a shader validation tool. +How to add a feature protected by a version/extension/stage/profile: See the +comment in glslang/MachineIndependent/Versions.cpp. + Things left to do: See Todo.txt -Execution ---------- +Execution of Standalone Wrapper +------------------------------- There are binaries in the Install/Windows and Install/Linux directories. @@ -26,7 +29,10 @@ The applied stage-specific rules are based on the file extension: .frag for a fragment shader .comp for a compute shader -Source: Build and run on linux +There is also a non-shader extension + .conf for a configuration file of limits, see usage statement for example + +Source: Build and run on Linux ------------------------------- A simple bash script "BuildLinux.sh" is provided at the root directory diff --git a/Todo.txt b/Todo.txt index a5da567..68b1212 100644 --- a/Todo.txt +++ b/Todo.txt @@ -1,7 +1,5 @@ Current functionality level: ESSL 3.0 -- extension adding process - Link Validation + provide input config file for setting limits - also consider spitting out measures of complexity @@ -166,22 +164,24 @@ Shader Functionality to Implement/Finish - Allow swizzle operations on scalars. - Positive signed decimal literals, as well as octal and hexadecimal, can set all 32 bits. This includes setting the sign bit to create a negative value. - Make GLSL consistent with the API regarding user clipping, by no longer referring to gl_Positionwhen gl_ClipVertex is not written. Rather, user clipping becomes undefined. - - Clarified that a comma sequence-operator expression cannot be a constant expression. E.g., “(2,3)” is not allowed, semantically, as a valid constant expression 3, even though it is an expression that will evaluate to 3. + - Clarified that a comma sequence-operator expression cannot be a constant expression. E.g., “(2,3)” is not allowed, semantically, + as a valid constant expression 3, even though it is an expression that will evaluate to 3. - Use vec2 instead of vec3 for coordinate in textureGather*(sampler2DRect,...). - Clarify that textureGatherOffset() can take non-constants for the offsets. GLSL 4.3 - - Add shader storage bufferobjects, as per the ARB_shader_storage_buffer_object extension. - This includes 1) allowing the last member of a storage buffer block to be an array that does not - know its size until render time, and 2) read/write memory shared with the application and other - shader invocations. It also adds the std430layout qualifier for shader storage blocks. + - Add shader storage buffer objects, as per the ARB_shader_storage_buffer_object extension. This includes + - allowing the last member of a storage buffer block to be an array that does not know its size until render time + - read/write memory shared with the application and other shader invocations + - adding the std430 layout qualifier for shader storage blocks - Allow .length() on all arrays; returning a compile-time constant or not, depending on how the array is sized, as per the ARB_shader_storage_buffer_object extension. - Be clear that implicit array sizing is only within a stage, not cross stage. - - Array clarifications: 1) All arrays are inherently homogeneous, except for arrays of the new - shader storage buffer objects. 2) Arrays of shader storage buffer objects will be dereferenced - when the .length() method is used on an unsized array member, so that must a have valid index. - 3) Arrays of other objects (uniform blocks) containing implicitly sized arrays will have the same - implicit size for all elements of the array. + - Array clarifications: + - All arrays are inherently homogeneous, except for arrays of the new shader storage buffer objects + - Arrays of shader storage buffer objects will be dereferenced when the .length() method is used on an unsized array + member, so that must a have valid index. + - Arrays of other objects (uniform blocks) containing implicitly sized arrays will have the same implicit size for all + elements of the array. - Arrays of arrays are now supported, as per the GL_ARB_arrays_of_arraysextension. - Compute shaders are now supported, as per the GL_ARB_compute_shader extension. - Added imageSize() built-ins to query the dimensions of an image. diff --git a/glslang/MachineIndependent/Initialize.cpp b/glslang/MachineIndependent/Initialize.cpp index 0080c77..d39f3e6 100644 --- a/glslang/MachineIndependent/Initialize.cpp +++ b/glslang/MachineIndependent/Initialize.cpp @@ -584,7 +584,7 @@ void TBuiltIns::initialize(int version, EProfile profile) s.append("\n"); // - // Original-style texture Functions existing in both stages. + // Original-style texture functions existing in both stages. // (Per-stage functions below.) // if (profile == EEsProfile && version == 100 || diff --git a/glslang/MachineIndependent/Intermediate.cpp b/glslang/MachineIndependent/Intermediate.cpp index 7a63f26..f876777 100644 --- a/glslang/MachineIndependent/Intermediate.cpp +++ b/glslang/MachineIndependent/Intermediate.cpp @@ -1098,7 +1098,7 @@ void TIntermediate::checkCallGraphCycles(TInfoSink& infoSink) void TIntermediate::error(TInfoSink& infoSink, const char* message) { infoSink.info.prefix(EPrefixError); - infoSink.info << "Linking " << StageName[language] << " stage: " << message << "\n"; + infoSink.info << "Linking " << StageName(language) << " stage: " << message << "\n"; ++numErrors; } diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 14ab9e7..c4b63d4 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -249,59 +249,6 @@ void TParseContext::handlePragma(const char **tokens, int numTokens) } } -TBehavior TParseContext::getExtensionBehavior(const char* behavior) -{ - if (!strcmp("require", behavior)) - return EBhRequire; - else if (!strcmp("enable", behavior)) - return EBhEnable; - else if (!strcmp("disable", behavior)) - return EBhDisable; - else if (!strcmp("warn", behavior)) - return EBhWarn; - else { - error(currentLoc, "behavior not supported", "#extension", behavior); - return EBhDisable; - } -} - -void TParseContext::updateExtensionBehavior(const char* extName, const char* behavior) -{ - TBehavior behaviorVal = getExtensionBehavior(behavior); - TMap:: iterator iter; - TString msg; - - // special cased for all extension - if (!strcmp(extName, "all")) { - if (behaviorVal == EBhRequire || behaviorVal == EBhEnable) { - error(currentLoc, "extension 'all' cannot have 'require' or 'enable' behavior", "#extension", ""); - return; - } else { - for (iter = extensionBehavior.begin(); iter != extensionBehavior.end(); ++iter) - iter->second = behaviorVal; - } - } else { - iter = extensionBehavior.find(TString(extName)); - if (iter == extensionBehavior.end()) { - switch (behaviorVal) { - case EBhRequire: - error(currentLoc, "extension not supported", "#extension", extName); - break; - case EBhEnable: - case EBhWarn: - case EBhDisable: - warn(currentLoc, "extension not supported", "#extension", extName); - break; - default: - assert(0 && "unexpected behaviorVal"); - } - - return; - } else - iter->second = behaviorVal; - } -} - /////////////////////////////////////////////////////////////////////// // // Sub- vector and matrix fields @@ -538,12 +485,11 @@ TIntermTyped* TParseContext::handleBracketDereference(TSourceLoc loc, TIntermTyp if (base->isArray() && base->getType().getArraySize() == 0) error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable"); if (base->getBasicType() == EbtBlock) - requireProfile(base->getLoc(), static_cast(~EEsProfileMask), "variable indexing block array"); + requireProfile(base->getLoc(), ~EEsProfile, "variable indexing block array"); if (base->getBasicType() == EbtSampler && version >= 130) { const char* explanation = "variable indexing sampler array"; - requireProfile(base->getLoc(), static_cast(ECoreProfileMask | ECompatibilityProfileMask), explanation); - profileRequires(base->getLoc(), ECoreProfile, 400, 0, explanation); - profileRequires(base->getLoc(), ECompatibilityProfile, 400, 0, explanation); + requireProfile(base->getLoc(), ECoreProfile | ECompatibilityProfile, explanation); + profileRequires(base->getLoc(), ECoreProfile | ECompatibilityProfile, 400, 0, explanation); } result = intermediate.addIndex(EOpIndexIndirect, base, index, loc); @@ -662,7 +608,7 @@ TFunction* TParseContext::handleFunctionDeclarator(TSourceLoc loc, TFunction& fu { // ES can't declare prototypes inside functions if (! symbolTable.atGlobalLevel()) - requireProfile(loc, static_cast(~EEsProfileMask), "local function declaration"); + requireProfile(loc, ~EEsProfile, "local function declaration"); // // Multiple declarations of the same function are allowed. @@ -1479,7 +1425,7 @@ void TParseContext::globalQualifierFix(TSourceLoc loc, TQualifier& qualifier, co return; } if (publicType.arraySizes) { - requireProfile(loc, (EProfileMask)~EEsProfileMask, "vertex input arrays"); + requireProfile(loc, ~EEsProfile, "vertex input arrays"); profileRequires(loc, ENoProfile, 150, 0, "vertex input arrays"); } } @@ -1706,7 +1652,7 @@ bool TParseContext::arrayQualifierError(TSourceLoc loc, const TQualifier& qualif profileRequires(loc, ENoProfile, 120, "GL_3DL_array_objects", "const array"); if (qualifier.storage == EvqVaryingIn && language == EShLangVertex) { - requireProfile(loc, (EProfileMask)~EEsProfileMask, "vertex input arrays"); + requireProfile(loc, ~EEsProfile, "vertex input arrays"); profileRequires(loc, ENoProfile, 150, 0, "vertex input arrays"); } @@ -1726,9 +1672,8 @@ void TParseContext::arraySizeRequiredCheck(TSourceLoc loc, int size) void TParseContext::arrayDimError(TSourceLoc loc) { - requireProfile(loc, (EProfileMask)(ECoreProfileMask | ECompatibilityProfileMask), "arrays of arrays"); - profileRequires(loc, ECoreProfile, 430, 0, "arrays of arrays"); - profileRequires(loc, ECompatibilityProfile, 430, 0, "arrays of arrays"); + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "arrays of arrays"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, 0, "arrays of arrays"); } void TParseContext::arrayDimCheck(TSourceLoc loc, TArraySizes* sizes1, TArraySizes* sizes2) @@ -2387,7 +2332,7 @@ void TParseContext::addBlock(TSourceLoc loc, TTypeList& typeList, const TString* arraySizeRequiredCheck(loc, arraySizes->getSize()); if (currentBlockDefaults.storage == EvqUniform) { - requireProfile(loc, (EProfileMask)(~ENoProfileMask), "uniform block"); + requireProfile(loc, ~ENoProfile, "uniform block"); profileRequires(loc, EEsProfile, 300, 0, "uniform block"); } else { error(loc, "only uniform interface blocks are supported", blockName->c_str(), ""); @@ -2788,17 +2733,4 @@ TIntermTyped* TParseContext::addConstStruct(TString& identifier, TIntermTyped* n return typedNode; } -// -// Initialize all supported extensions to disable -// -void TParseContext::initializeExtensionBehavior() -{ - // - // example code: extensionBehavior["test"] = EBhDisable; // where "test" is the name of - // supported extension - // - extensionBehavior["GL_ARB_texture_rectangle"] = EBhDisable; - extensionBehavior["GL_3DL_array_objects"] = EBhDisable; -} - } // end namespace glslang diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index 669bd82..c252d77 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -43,13 +43,6 @@ namespace glslang { -typedef enum { - EBhRequire, - EBhEnable, - EBhWarn, - EBhDisable -} TBehavior; - struct TPragma { TPragma(bool o, bool d) : optimize(o), debug(d) { } bool optimize; @@ -71,7 +64,6 @@ public: public: bool parseShaderStrings(TPpContext&, char* strings[], int strLen[], int numStrings); - void initializeExtensionBehavior(); void parserError(const char *s); // for bison's yyerror void C_DECL error(TSourceLoc, const char *szReason, const char *szToken, @@ -148,15 +140,6 @@ public: bool arraySetMaxSize(TSourceLoc, TIntermSymbol*, int); - void requireProfile(TSourceLoc, EProfileMask profileMask, const char *featureDesc); - void requireStage(TSourceLoc, EShLanguageMask languageMask, const char *featureDesc); - void profileRequires(TSourceLoc, EProfile callingProfile, int minVersion, int numExtensions, const char* extensions[], const char *featureDesc); - void profileRequires(TSourceLoc, EProfile callingProfile, int minVersion, const char* extension, const char *featureDesc); - void checkDeprecated(TSourceLoc, EProfile callingProfile, int depVersion, const char *featureDesc); - void requireNotRemoved(TSourceLoc, EProfile callingProfile, int removedVersion, const char *featureDesc); - void fullIntegerCheck(TSourceLoc, const char* op); - void doubleCheck(TSourceLoc, const char* op); - void setScanContext(TScanContext* c) { scanContext = c; } TScanContext* getScanContext() const { return scanContext; } void setPpContext(TPpContext* c) { ppContext = c; } @@ -164,9 +147,21 @@ public: void addError() { ++numErrors; } int getNumErrors() const { return numErrors; } + // The following are implemented in Versions.cpp to localize version/profile/stage/extensions control + void initializeExtensionBehavior(); + void requireProfile(TSourceLoc, int queryProfiles, const char *featureDesc); + void profileRequires(TSourceLoc, int queryProfiles, int minVersion, int numExtensions, const char* extensions[], const char *featureDesc); + void profileRequires(TSourceLoc, int queryProfiles, int minVersion, const char* extension, const char *featureDesc); + void requireStage(TSourceLoc, EShLanguageMask, const char *featureDesc); + void requireStage(TSourceLoc, EShLanguage, const char *featureDesc); + void checkDeprecated(TSourceLoc, int queryProfiles, int depVersion, const char *featureDesc); + void requireNotRemoved(TSourceLoc, int queryProfiles, int removedVersion, const char *featureDesc); + void fullIntegerCheck(TSourceLoc, const char* op); + void doubleCheck(TSourceLoc, const char* op); + protected: const char* getPreamble(); - TBehavior getExtensionBehavior(const char* behavior); + TExtensionBehavior getExtensionBehavior(const char* behavior); void nonInitConstCheck(TSourceLoc, TString& identifier, TType& type); TVariable* declareNonArray(TSourceLoc, TString& identifier, TType&, bool& newDeclaration); void declareArray(TSourceLoc, TString& identifier, const TType&, TVariable*&, bool& newDeclaration); @@ -208,7 +203,7 @@ protected: TPpContext* ppContext; int numErrors; // number of compile-time errors encountered bool parsingBuiltins; // true if parsing built-in symbols/functions - TMap extensionBehavior; // for each extension string, what it's current enablement is + TMap extensionBehavior; // for each extension string, what it's current behavior is set to static const int maxSamplerIndex = EsdNumDims * (EbtNumTypes * (2 * 2)); // see computeSamplerTypeIndex() TPrecisionQualifier defaultSamplerPrecision[maxSamplerIndex]; bool afterEOF; diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index c99fe3b..78af81f 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -59,9 +59,11 @@ namespace { // anonymous namespace for file-local functions and symbols using namespace glslang; +// Local mapping functions for making arrays of symbol tables.... + int MapVersionToIndex(int version) { - switch(version) { + switch (version) { case 100: return 0; case 110: return 1; case 120: return 2; @@ -81,6 +83,19 @@ int MapVersionToIndex(int version) } // V const int VersionCount = 13; // number of case statements above +int MapProfileToIndex(EProfile profile) +{ + switch (profile) { + case ENoProfile: return 0; + case ECoreProfile: return 1; + case ECompatibilityProfile: return 2; + case EEsProfile: return 3; + default: // | + return 0; // | + } // | +} // V +const int ProfileCount = 4; // number of case statements above + // only one of these needed for non-ES; ES needs 2 for different precision defaults of built-ins enum EPrecisionClass { EPcGeneral, @@ -96,8 +111,8 @@ enum EPrecisionClass { // Each has a different set of built-ins, and we want to preserve that from // compile to compile. // -TSymbolTable* CommonSymbolTable[VersionCount][EProfileCount][EPcCount] = {}; -TSymbolTable* SharedSymbolTables[VersionCount][EProfileCount][EShLangCount] = {}; +TSymbolTable* CommonSymbolTable[VersionCount][ProfileCount][EPcCount] = {}; +TSymbolTable* SharedSymbolTables[VersionCount][ProfileCount][EShLangCount] = {}; TPoolAllocator* PerProcessGPA = 0; @@ -216,7 +231,8 @@ void SetupBuiltinSymbolTable(int version, EProfile profile) // See if it's already been done for this version/profile combination int versionIndex = MapVersionToIndex(version); - if (CommonSymbolTable[versionIndex][profile][EPcGeneral]) { + int profileIndex = MapProfileToIndex(profile); + if (CommonSymbolTable[versionIndex][profileIndex][EPcGeneral]) { glslang::ReleaseGlobalLock(); return; @@ -244,17 +260,17 @@ void SetupBuiltinSymbolTable(int version, EProfile profile) // Copy the local symbol tables from the new pool to the global tables using the process-global pool for (int precClass = 0; precClass < EPcCount; ++precClass) { if (! commonTable[precClass]->isEmpty()) { - CommonSymbolTable[versionIndex][profile][precClass] = new TSymbolTable; - CommonSymbolTable[versionIndex][profile][precClass]->copyTable(*commonTable[precClass]); - CommonSymbolTable[versionIndex][profile][precClass]->readOnly(); + CommonSymbolTable[versionIndex][profileIndex][precClass] = new TSymbolTable; + CommonSymbolTable[versionIndex][profileIndex][precClass]->copyTable(*commonTable[precClass]); + CommonSymbolTable[versionIndex][profileIndex][precClass]->readOnly(); } } for (int stage = 0; stage < EShLangCount; ++stage) { if (! stageTables[stage]->isEmpty()) { - SharedSymbolTables[versionIndex][profile][stage] = new TSymbolTable; - SharedSymbolTables[versionIndex][profile][stage]->adoptLevels(*CommonSymbolTable[versionIndex][profile][CommonIndex(profile, (EShLanguage)stage)]); - SharedSymbolTables[versionIndex][profile][stage]->copyTable(*stageTables[stage]); - SharedSymbolTables[versionIndex][profile][stage]->readOnly(); + SharedSymbolTables[versionIndex][profileIndex][stage] = new TSymbolTable; + SharedSymbolTables[versionIndex][profileIndex][stage]->adoptLevels(*CommonSymbolTable[versionIndex][profileIndex][CommonIndex(profile, (EShLanguage)stage)]); + SharedSymbolTables[versionIndex][profileIndex][stage]->copyTable(*stageTables[stage]); + SharedSymbolTables[versionIndex][profileIndex][stage]->readOnly(); } } @@ -433,7 +449,7 @@ bool CompileDeferred( SetupBuiltinSymbolTable(version, profile); TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)] - [profile] + [MapProfileToIndex(profile)] [compiler->getLanguage()]; // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool. @@ -569,7 +585,7 @@ void ShDestruct(ShHandle handle) int __fastcall ShFinalize() { for (int version = 0; version < VersionCount; ++version) - for (int p = 0; p < EProfileCount; ++p) + for (int p = 0; p < ProfileCount; ++p) for (int lang = 0; lang < EShLangCount; ++lang) delete SharedSymbolTables[version][p][lang]; @@ -964,7 +980,7 @@ bool TProgram::linkStage(EShLanguage stage, EShMessages messages) merged = intermediate[stage]; } - infoSink->info << "\nLinked " << StageName[stage] << " stage:\n\n"; + infoSink->info << "\nLinked " << StageName(stage) << " stage:\n\n"; if (stages[stage].size() > 1) { std::list::const_iterator it; diff --git a/glslang/MachineIndependent/Versions.cpp b/glslang/MachineIndependent/Versions.cpp index 3a4a767..0afd7fd 100644 --- a/glslang/MachineIndependent/Versions.cpp +++ b/glslang/MachineIndependent/Versions.cpp @@ -37,68 +37,166 @@ // // Help manage multiple profiles, versions, extensions etc. // -// These don't return error codes, as the presumption is parsing -// will always continue as if the feature were present, and there -// is no error recovery needed to enable that. +// These don't return error codes, as the presumption is parsing will +// always continue as if the tested feature were enabled, and thus there +// is no error recovery needed. +// + +// +// HOW TO add a feature enabled by an extension. +// +// To add a new hypothetical "Feature F" to the front end, where an extension +// "XXX_extension_X" can be used to enable the feature, do the following. +// +// 1) Understand that specific features are what are error-checked for, not +// extensions: A specific Feature F might be enabled by an extension, or a +// particular version in a particular profile, or a stage, or combinations, etc. +// +// The basic mechanism is to use the following to "declare" all the things that +// enable/disable Feature F, in a code path that implements Feature F: +// +// requireProfile() +// profileRequires() +// requireStage() +// checkDeprecated() +// requireNotRemoved() +// +// Typically, only the first two calls are needed. They go into a code path that +// implements Feature F, and will log the proper error/warning messages. Parsing +// will then always continue as if the tested feature was enabled. +// +// There is typically no if-testing or conditional parsing, just insertion of requirements. +// +// 2) Add extension initialization to TParseContext::initializeExtensionBehavior(), +// the first function below: +// +// extensionBehavior["XXX_extension_X"] = EBhDisable; +// +// 3) Insert a profile check in the feature's path (unless all profiles support the feature, +// for some version level). That is, call requireProfile() to constrain the profiles, e.g.: +// +// // ... in a path specific to Feature F... +// requireProfile(loc, +// ECoreProfile | ECompatibilityProfile, +// "Feature F"); +// +// 4) For each profile that supports the feature, insert version/extension checks: +// +// The mostly likely scenario is that Feature F can only be used with a +// particular profile if XXX_extension_X is present or the version is +// high enough that the core specification already incorporated it. +// +// // following the requireProfile() call... +// profileRequires(loc, +// ECoreProfile | ECompatibilityProfile, +// 420, // 0 if no version incorporated the feature into the core spec. +// "XXX_extension_X", // can be a list of extensions that all add the feature +// "Feature F"); +// +// This allows the feature if either A) one of the extensions is enabled or +// B) the version is high enough. If no version yet incorporates the feature +// into core, pass in 0. +// +// This can be called multiple times, if different profiles support the +// feature starting at different version numbers or with different +// extensions. +// +// This must be called for each profile allowed by the initial call to requireProfile(). +// +// Profiles are all masks, which can be "or"-ed together. +// +// ENoProfile +// ECoreProfile +// ECompatibilityProfile +// EEsProfile +// +// The ENoProfile profile is only for desktop, before profiles showed up in version 150; +// All other #version with no profile default to either es or core, and so have profiles. +// +// You can select all but a particular profile using ~. The following basically means "desktop": +// +// ~EEsProfile // #include "ParseHelper.h" namespace glslang { -const char* StageName[EShLangCount] = { - "vertex", - "tessellation control", - "tessellation evaluation", - "geometry", - "fragment", - "compute" -}; - -const char* ProfileName[EProfileCount] = { - "none", - "core", - "compatibility", - "es" -}; - -// -// If only some profiles support a feature, use requireProfile() to specify -// which subset allows the feature. If the current profile is not present, -// give an error message. // -void TParseContext::requireProfile(TSourceLoc loc, EProfileMask profileMask, const char *featureDesc) +// Initialize all extensions, almost always to 'disable', as once their features +// are incorporated into a core version, their features are supported through allowing that +// core version, not through a pseudo-enablement of the extension. +// +void TParseContext::initializeExtensionBehavior() { - if (((1 << profile) & profileMask) == 0) - error(loc, "not supported with this profile:", featureDesc, ProfileName[profile]); + extensionBehavior["GL_ARB_texture_rectangle"] = EBhDisable; + extensionBehavior["GL_3DL_array_objects"] = EBhDisable; } +const char* ProfileName(EProfile profile) +{ + switch (profile) { + case ENoProfile: return "none"; + case ECoreProfile: return "core"; + case ECompatibilityProfile: return "compatibility"; + case EEsProfile: return "es"; + default: return "unknown profile"; + } +} + +// +// When to use requireProfile(): +// +// If only some profiles support a feature. However, if within a profile the feature +// is version or extension specific, follow this call with calls to profileRequires(). // -// If only some stages support a feature, use requireStage() to specify -// which subset allows the feature. If the current stage is not present, +// Operation: If the current profile is not one of the profileMask, // give an error message. // -void TParseContext::requireStage(TSourceLoc loc, EShLanguageMask languageMask, const char *featureDesc) +void TParseContext::requireProfile(TSourceLoc loc, int profileMask, const char *featureDesc) { - if (((1 << language) & languageMask) == 0) - error(loc, "not supported in this stage:", featureDesc, StageName[language]); + if (! (profile & profileMask)) + error(loc, "not supported with this profile:", featureDesc, ProfileName(profile)); +} + +const char* StageName(EShLanguage stage) +{ + switch(stage) { + case EShLangVertex: return "vertex"; + case EShLangTessControl: return "tessellation control"; + case EShLangTessEvaluation: return "tessellation evaluation"; + case EShLangGeometry: return "geometry"; + case EShLangFragment: return "fragment"; + case EShLangCompute: return "compute"; + default: return "unknown stage"; + } } // -// Within a profile, if a feature requires a version level or extension, use -// ProfileRequires(). This only checks if the current profile matches -// the passed-in profile. +// When to use profileRequires(): +// +// If a set of profiles have the same requirements for what version or extensions +// are needed to support a feature. +// +// It must be called for each profile that needs protection. Use requireProfile() first +// to reduce that set of profiles. +// +// Operation: Will issue warnings/errors based on the current profile, version, and extension +// behaviors. It only checks extensions when the current profile is one of the profileMask. +// +// A minVersion of 0 means no version of the profileMask support this in core, +// the extension must be present. // -// one that takes multiple extensions -void TParseContext::profileRequires(TSourceLoc loc, EProfile callingProfile, int minVersion, int numExtensions, const char* extensions[], const char *featureDesc) +// entry point that takes multiple extensions +void TParseContext::profileRequires(TSourceLoc loc, int profileMask, int minVersion, int numExtensions, const char* extensions[], const char *featureDesc) { - if (profile == callingProfile) { + if (profile & profileMask) { bool okay = false; - if (version >= minVersion) + if (minVersion > 0 && version >= minVersion) okay = true; for (int i = 0; i < numExtensions; ++i) { - TBehavior behavior = extensionBehavior[extensions[i]]; + TExtensionBehavior behavior = extensionBehavior[extensions[i]]; switch (behavior) { case EBhWarn: infoSink.info.message(EPrefixWarning, ("extension " + TString(extensions[i]) + " is being used for " + featureDesc).c_str(), loc); @@ -116,19 +214,39 @@ void TParseContext::profileRequires(TSourceLoc loc, EProfile callingProfile, int } } -// one that takes a single extension -void TParseContext::profileRequires(TSourceLoc loc, EProfile callingProfile, int minVersion, const char* extension, const char *featureDesc) +// entry point for the above that takes a single extension +void TParseContext::profileRequires(TSourceLoc loc, int profileMask, int minVersion, const char* extension, const char *featureDesc) +{ + profileRequires(loc, profileMask, minVersion, extension ? 1 : 0, &extension, featureDesc); +} + +// +// When to use requireStage() +// +// If only some stages support a feature. +// +// Operation: If the current stage is not present, give an error message. +// +void TParseContext::requireStage(TSourceLoc loc, EShLanguageMask languageMask, const char *featureDesc) +{ + if (((1 << language) & languageMask) == 0) + error(loc, "not supported in this stage:", featureDesc, StageName(language)); +} + +// If only one stage supports a feature, this can be called. But, all supporting stages +// must be specified with one call. +void TParseContext::requireStage(TSourceLoc loc, EShLanguage stage, const char *featureDesc) { - profileRequires(loc, callingProfile, minVersion, extension ? 1 : 0, &extension, featureDesc); + requireStage(loc, static_cast(1 << stage), featureDesc); } // -// Within a profile, see if a feature is deprecated and error or warn based on whether +// Within a set of profiles, see if a feature is deprecated and give an error or warning based on whether // a future compatibility context is being use. // -void TParseContext::checkDeprecated(TSourceLoc loc, EProfile callingProfile, int depVersion, const char *featureDesc) +void TParseContext::checkDeprecated(TSourceLoc loc, int profileMask, int depVersion, const char *featureDesc) { - if (profile == callingProfile) { + if (profile & profileMask) { if (version >= depVersion) { if (forwardCompatible) error(loc, "deprecated, may be removed in future release", featureDesc, ""); @@ -140,30 +258,92 @@ void TParseContext::checkDeprecated(TSourceLoc loc, EProfile callingProfile, int } // -// Within a profile, see if a feature has now been removed and if so, give an error. +// Within a set of profiles, see if a feature has now been removed and if so, give an error. // The version argument is the first version no longer having the feature. // -void TParseContext::requireNotRemoved(TSourceLoc loc, EProfile callingProfile, int removedVersion, const char *featureDesc) +void TParseContext::requireNotRemoved(TSourceLoc loc, int profileMask, int removedVersion, const char *featureDesc) { - if (profile == callingProfile) { + if (profile & profileMask) { if (version >= removedVersion) { const int maxSize = 60; char buf[maxSize]; - snprintf(buf, maxSize, "%s profile; removed in version %d", ProfileName[profile], removedVersion); + snprintf(buf, maxSize, "%s profile; removed in version %d", ProfileName(profile), removedVersion); error(loc, "no longer supported in", featureDesc, buf); } } } +// +// Translate from text string of extension's behavior to enum. +// +TExtensionBehavior TParseContext::getExtensionBehavior(const char* behavior) +{ + if (! strcmp("require", behavior)) + return EBhRequire; + else if (! strcmp("enable", behavior)) + return EBhEnable; + else if (! strcmp("disable", behavior)) + return EBhDisable; + else if (! strcmp("warn", behavior)) + return EBhWarn; + else { + error(currentLoc, "behavior not supported", "#extension", behavior); + return EBhDisable; + } +} + +void TParseContext::updateExtensionBehavior(const char* extName, const char* behaviorString) +{ + TExtensionBehavior behavior = getExtensionBehavior(behaviorString); + TMap:: iterator iter; + TString msg; + + // special case for the 'all' extension + if (! strcmp(extName, "all")) { + if (behavior == EBhRequire || behavior == EBhEnable) { + error(currentLoc, "extension 'all' cannot have 'require' or 'enable' behavior", "#extension", ""); + return; + } else { + for (iter = extensionBehavior.begin(); iter != extensionBehavior.end(); ++iter) + iter->second = behavior; + } + } else { + iter = extensionBehavior.find(TString(extName)); + if (iter == extensionBehavior.end()) { + switch (behavior) { + case EBhRequire: + error(currentLoc, "extension not supported", "#extension", extName); + break; + case EBhEnable: + case EBhWarn: + case EBhDisable: + warn(currentLoc, "extension not supported", "#extension", extName); + break; + default: + assert(0 && "unexpected behavior"); + } + + return; + } else + iter->second = behavior; + } +} + +// +// Call for any operation needing full GLSL integer data-type support. +// void TParseContext::fullIntegerCheck(TSourceLoc loc, const char* op) -{ +{ profileRequires(loc, ENoProfile, 130, 0, op); profileRequires(loc, EEsProfile, 300, 0, op); } +// +// Call for any operation needing GLSL double data-type support. +// void TParseContext::doubleCheck(TSourceLoc loc, const char* op) -{ - requireProfile(loc, (EProfileMask)(ECoreProfileMask | ECompatibilityProfileMask), op); +{ + requireProfile(loc, ECoreProfile | ECompatibilityProfile, op); profileRequires(loc, ECoreProfile, 400, 0, op); profileRequires(loc, ECompatibilityProfile, 400, 0, op); } diff --git a/glslang/MachineIndependent/Versions.h b/glslang/MachineIndependent/Versions.h index 2569172..d917b5d 100644 --- a/glslang/MachineIndependent/Versions.h +++ b/glslang/MachineIndependent/Versions.h @@ -40,19 +40,29 @@ // Help manage multiple profiles, versions, extensions etc. // +// +// The behaviors from "#extension extension_name : behavior" +// typedef enum { - ENoProfile, // only for desktop, before profiles showed up - ECoreProfile, - ECompatibilityProfile, - EEsProfile, - EProfileCount, -} EProfile; + EBhRequire, + EBhEnable, + EBhWarn, + EBhDisable +} TExtensionBehavior; +// +// Profiles are set up for masking operations, so queries can be done on multiple +// profiles at the same time. +// +// Don't maintain an ordinal set of enums (0,1,2,3...) to avoid all possible +// defects from mixing the two different forms. +// typedef enum { - ENoProfileMask = (1 << ENoProfile), - ECoreProfileMask = (1 << ECoreProfile), - ECompatibilityProfileMask = (1 << ECompatibilityProfile), - EEsProfileMask = (1 << EEsProfile) -} EProfileMask; + EBadProfile = 0, + ENoProfile = (1 << 0), // only for desktop, before profiles showed up + ECoreProfile = (1 << 1), + ECompatibilityProfile = (1 << 2), + EEsProfile = (1 << 3) +} EProfile; #endif // _VERSIONS_INCLUDED_ diff --git a/glslang/MachineIndependent/glslang.y b/glslang/MachineIndependent/glslang.y index c99e255..0d33552 100644 --- a/glslang/MachineIndependent/glslang.y +++ b/glslang/MachineIndependent/glslang.y @@ -1047,7 +1047,7 @@ interpolation_qualifier } | NOPERSPECTIVE { parseContext.globalCheck($1.loc, parseContext.symbolTable.atGlobalLevel(), "noperspective"); - parseContext.requireProfile($1.loc, static_cast(~EEsProfileMask), "noperspective"); + parseContext.requireProfile($1.loc, ~EEsProfile, "noperspective"); parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "noperspective"); $$.init($1.loc); $$.qualifier.nopersp = true; @@ -1138,7 +1138,7 @@ storage_qualifier $$.qualifier.storage = EvqConst; } | ATTRIBUTE { - parseContext.requireStage($1.loc, EShLangVertexMask, "attribute"); + parseContext.requireStage($1.loc, EShLangVertex, "attribute"); parseContext.checkDeprecated($1.loc, ECoreProfile, 130, "attribute"); parseContext.checkDeprecated($1.loc, ENoProfile, 130, "attribute"); parseContext.requireNotRemoved($1.loc, ECoreProfile, 420, "attribute"); @@ -1206,9 +1206,9 @@ storage_qualifier $$.qualifier.storage = EvqUniform; // TODO: 4.0 functionality: implement BUFFER } | SHARED { - parseContext.requireProfile($1.loc, static_cast(~EEsProfileMask), "shared"); + parseContext.requireProfile($1.loc, ~EEsProfile, "shared"); parseContext.profileRequires($1.loc, ECoreProfile, 430, 0, "shared"); - parseContext.requireStage($1.loc, EShLangComputeMask, "shared"); + parseContext.requireStage($1.loc, EShLangCompute, "shared"); $$.init($1.loc); $$.qualifier.shared = true; } @@ -2315,7 +2315,7 @@ jump_statement parseContext.error($1.loc, "function return is not matching type:", "return", ""); } | DISCARD SEMICOLON { - parseContext.requireStage($1.loc, EShLangFragmentMask, "discard"); + parseContext.requireStage($1.loc, EShLangFragment, "discard"); $$ = parseContext.intermediate.addBranch(EOpKill, $1.loc); } ; diff --git a/glslang/MachineIndependent/preprocessor/Pp.cpp b/glslang/MachineIndependent/preprocessor/Pp.cpp index 98406c5..355b308 100644 --- a/glslang/MachineIndependent/preprocessor/Pp.cpp +++ b/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -529,7 +529,7 @@ int TPpContext::CPPifdef(int defined, TPpToken * yylvalpp) int token = currentInput->scan(this, currentInput, yylvalpp); int name = yylvalpp->atom; if (++ifdepth > maxIfNesting) { - parseContext.error(yylvalpp->loc, "maximum nesting depth exceededextension name not specified", "#ifdef", ""); + parseContext.error(yylvalpp->loc, "maximum nesting depth exceeded", "#ifdef", ""); return 0; } elsetracker++; diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index 0e41f1e..801d551 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -96,7 +96,7 @@ typedef enum { namespace glslang { -extern const char* StageName[EShLangCount]; +const char* StageName(EShLanguage); } // end namespace glslang -- 2.7.4