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
This commit is contained in:
John Kessenich 2013-10-03 20:23:57 +00:00
parent a5830dfc0e
commit 61c2d1410a
12 changed files with 336 additions and 197 deletions

View File

@ -7,10 +7,13 @@ There are two components:
2) A standalone wrapper, glslangValidator, that can be used as a shader 2) A standalone wrapper, glslangValidator, that can be used as a shader
validation tool. 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 Things left to do: See Todo.txt
Execution Execution of Standalone Wrapper
--------- -------------------------------
There are binaries in the Install/Windows and Install/Linux directories. 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 .frag for a fragment shader
.comp for a compute 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 A simple bash script "BuildLinux.sh" is provided at the root directory

View File

@ -1,7 +1,5 @@
Current functionality level: ESSL 3.0 Current functionality level: ESSL 3.0
- extension adding process
Link Validation Link Validation
+ provide input config file for setting limits + provide input config file for setting limits
- also consider spitting out measures of complexity - also consider spitting out measures of complexity
@ -166,22 +164,24 @@ Shader Functionality to Implement/Finish
- Allow swizzle operations on scalars. - 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. - 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. - 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,...). - Use vec2 instead of vec3 for coordinate in textureGather*(sampler2DRect,...).
- Clarify that textureGatherOffset() can take non-constants for the offsets. - Clarify that textureGatherOffset() can take non-constants for the offsets.
GLSL 4.3 GLSL 4.3
- Add shader storage bufferobjects, as per the ARB_shader_storage_buffer_object extension. - Add shader storage buffer objects, as per the ARB_shader_storage_buffer_object extension. This includes
This includes 1) allowing the last member of a storage buffer block to be an array that does not - allowing the last member of a storage buffer block to be an array that does not know its size until render time
know its size until render time, and 2) read/write memory shared with the application and other - read/write memory shared with the application and other shader invocations
shader invocations. It also adds the std430layout qualifier for shader storage blocks. - 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 - 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. 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. - 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 - Array clarifications:
shader storage buffer objects. 2) Arrays of shader storage buffer objects will be dereferenced - All arrays are inherently homogeneous, except for arrays of the new shader storage buffer objects
when the .length() method is used on an unsized array member, so that must a have valid index. - Arrays of shader storage buffer objects will be dereferenced when the .length() method is used on an unsized array
3) Arrays of other objects (uniform blocks) containing implicitly sized arrays will have the same member, so that must a have valid index.
implicit size for all elements of the array. - 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. - 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. - Compute shaders are now supported, as per the GL_ARB_compute_shader extension.
- Added imageSize() built-ins to query the dimensions of an image. - Added imageSize() built-ins to query the dimensions of an image.

View File

@ -584,7 +584,7 @@ void TBuiltIns::initialize(int version, EProfile profile)
s.append("\n"); s.append("\n");
// //
// Original-style texture Functions existing in both stages. // Original-style texture functions existing in both stages.
// (Per-stage functions below.) // (Per-stage functions below.)
// //
if (profile == EEsProfile && version == 100 || if (profile == EEsProfile && version == 100 ||

View File

@ -1098,7 +1098,7 @@ void TIntermediate::checkCallGraphCycles(TInfoSink& infoSink)
void TIntermediate::error(TInfoSink& infoSink, const char* message) void TIntermediate::error(TInfoSink& infoSink, const char* message)
{ {
infoSink.info.prefix(EPrefixError); infoSink.info.prefix(EPrefixError);
infoSink.info << "Linking " << StageName[language] << " stage: " << message << "\n"; infoSink.info << "Linking " << StageName(language) << " stage: " << message << "\n";
++numErrors; ++numErrors;
} }

View File

@ -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<TString, TBehavior>:: 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 // Sub- vector and matrix fields
@ -538,12 +485,11 @@ TIntermTyped* TParseContext::handleBracketDereference(TSourceLoc loc, TIntermTyp
if (base->isArray() && base->getType().getArraySize() == 0) if (base->isArray() && base->getType().getArraySize() == 0)
error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable"); error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable");
if (base->getBasicType() == EbtBlock) if (base->getBasicType() == EbtBlock)
requireProfile(base->getLoc(), static_cast<EProfileMask>(~EEsProfileMask), "variable indexing block array"); requireProfile(base->getLoc(), ~EEsProfile, "variable indexing block array");
if (base->getBasicType() == EbtSampler && version >= 130) { if (base->getBasicType() == EbtSampler && version >= 130) {
const char* explanation = "variable indexing sampler array"; const char* explanation = "variable indexing sampler array";
requireProfile(base->getLoc(), static_cast<EProfileMask>(ECoreProfileMask | ECompatibilityProfileMask), explanation); requireProfile(base->getLoc(), ECoreProfile | ECompatibilityProfile, explanation);
profileRequires(base->getLoc(), ECoreProfile, 400, 0, explanation); profileRequires(base->getLoc(), ECoreProfile | ECompatibilityProfile, 400, 0, explanation);
profileRequires(base->getLoc(), ECompatibilityProfile, 400, 0, explanation);
} }
result = intermediate.addIndex(EOpIndexIndirect, base, index, loc); 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 // ES can't declare prototypes inside functions
if (! symbolTable.atGlobalLevel()) if (! symbolTable.atGlobalLevel())
requireProfile(loc, static_cast<EProfileMask>(~EEsProfileMask), "local function declaration"); requireProfile(loc, ~EEsProfile, "local function declaration");
// //
// Multiple declarations of the same function are allowed. // Multiple declarations of the same function are allowed.
@ -1479,7 +1425,7 @@ void TParseContext::globalQualifierFix(TSourceLoc loc, TQualifier& qualifier, co
return; return;
} }
if (publicType.arraySizes) { if (publicType.arraySizes) {
requireProfile(loc, (EProfileMask)~EEsProfileMask, "vertex input arrays"); requireProfile(loc, ~EEsProfile, "vertex input arrays");
profileRequires(loc, ENoProfile, 150, 0, "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"); profileRequires(loc, ENoProfile, 120, "GL_3DL_array_objects", "const array");
if (qualifier.storage == EvqVaryingIn && language == EShLangVertex) { 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"); profileRequires(loc, ENoProfile, 150, 0, "vertex input arrays");
} }
@ -1726,9 +1672,8 @@ void TParseContext::arraySizeRequiredCheck(TSourceLoc loc, int size)
void TParseContext::arrayDimError(TSourceLoc loc) void TParseContext::arrayDimError(TSourceLoc loc)
{ {
requireProfile(loc, (EProfileMask)(ECoreProfileMask | ECompatibilityProfileMask), "arrays of arrays"); requireProfile(loc, ECoreProfile | ECompatibilityProfile, "arrays of arrays");
profileRequires(loc, ECoreProfile, 430, 0, "arrays of arrays"); profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, 0, "arrays of arrays");
profileRequires(loc, ECompatibilityProfile, 430, 0, "arrays of arrays");
} }
void TParseContext::arrayDimCheck(TSourceLoc loc, TArraySizes* sizes1, TArraySizes* sizes2) 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()); arraySizeRequiredCheck(loc, arraySizes->getSize());
if (currentBlockDefaults.storage == EvqUniform) { if (currentBlockDefaults.storage == EvqUniform) {
requireProfile(loc, (EProfileMask)(~ENoProfileMask), "uniform block"); requireProfile(loc, ~ENoProfile, "uniform block");
profileRequires(loc, EEsProfile, 300, 0, "uniform block"); profileRequires(loc, EEsProfile, 300, 0, "uniform block");
} else { } else {
error(loc, "only uniform interface blocks are supported", blockName->c_str(), ""); error(loc, "only uniform interface blocks are supported", blockName->c_str(), "");
@ -2788,17 +2733,4 @@ TIntermTyped* TParseContext::addConstStruct(TString& identifier, TIntermTyped* n
return typedNode; 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 } // end namespace glslang

View File

@ -43,13 +43,6 @@
namespace glslang { namespace glslang {
typedef enum {
EBhRequire,
EBhEnable,
EBhWarn,
EBhDisable
} TBehavior;
struct TPragma { struct TPragma {
TPragma(bool o, bool d) : optimize(o), debug(d) { } TPragma(bool o, bool d) : optimize(o), debug(d) { }
bool optimize; bool optimize;
@ -71,7 +64,6 @@ public:
public: public:
bool parseShaderStrings(TPpContext&, char* strings[], int strLen[], int numStrings); bool parseShaderStrings(TPpContext&, char* strings[], int strLen[], int numStrings);
void initializeExtensionBehavior();
void parserError(const char *s); // for bison's yyerror void parserError(const char *s); // for bison's yyerror
void C_DECL error(TSourceLoc, const char *szReason, const char *szToken, void C_DECL error(TSourceLoc, const char *szReason, const char *szToken,
@ -148,15 +140,6 @@ public:
bool arraySetMaxSize(TSourceLoc, TIntermSymbol*, int); 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; } void setScanContext(TScanContext* c) { scanContext = c; }
TScanContext* getScanContext() const { return scanContext; } TScanContext* getScanContext() const { return scanContext; }
void setPpContext(TPpContext* c) { ppContext = c; } void setPpContext(TPpContext* c) { ppContext = c; }
@ -164,9 +147,21 @@ public:
void addError() { ++numErrors; } void addError() { ++numErrors; }
int getNumErrors() const { return 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: protected:
const char* getPreamble(); const char* getPreamble();
TBehavior getExtensionBehavior(const char* behavior); TExtensionBehavior getExtensionBehavior(const char* behavior);
void nonInitConstCheck(TSourceLoc, TString& identifier, TType& type); void nonInitConstCheck(TSourceLoc, TString& identifier, TType& type);
TVariable* declareNonArray(TSourceLoc, TString& identifier, TType&, bool& newDeclaration); TVariable* declareNonArray(TSourceLoc, TString& identifier, TType&, bool& newDeclaration);
void declareArray(TSourceLoc, TString& identifier, const TType&, TVariable*&, bool& newDeclaration); void declareArray(TSourceLoc, TString& identifier, const TType&, TVariable*&, bool& newDeclaration);
@ -208,7 +203,7 @@ protected:
TPpContext* ppContext; TPpContext* ppContext;
int numErrors; // number of compile-time errors encountered int numErrors; // number of compile-time errors encountered
bool parsingBuiltins; // true if parsing built-in symbols/functions bool parsingBuiltins; // true if parsing built-in symbols/functions
TMap<TString, TBehavior> extensionBehavior; // for each extension string, what it's current enablement is TMap<TString, TExtensionBehavior> extensionBehavior; // for each extension string, what it's current behavior is set to
static const int maxSamplerIndex = EsdNumDims * (EbtNumTypes * (2 * 2)); // see computeSamplerTypeIndex() static const int maxSamplerIndex = EsdNumDims * (EbtNumTypes * (2 * 2)); // see computeSamplerTypeIndex()
TPrecisionQualifier defaultSamplerPrecision[maxSamplerIndex]; TPrecisionQualifier defaultSamplerPrecision[maxSamplerIndex];
bool afterEOF; bool afterEOF;

View File

@ -59,9 +59,11 @@ namespace { // anonymous namespace for file-local functions and symbols
using namespace glslang; using namespace glslang;
// Local mapping functions for making arrays of symbol tables....
int MapVersionToIndex(int version) int MapVersionToIndex(int version)
{ {
switch(version) { switch (version) {
case 100: return 0; case 100: return 0;
case 110: return 1; case 110: return 1;
case 120: return 2; case 120: return 2;
@ -81,6 +83,19 @@ int MapVersionToIndex(int version)
} // V } // V
const int VersionCount = 13; // number of case statements above 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 // only one of these needed for non-ES; ES needs 2 for different precision defaults of built-ins
enum EPrecisionClass { enum EPrecisionClass {
EPcGeneral, EPcGeneral,
@ -96,8 +111,8 @@ enum EPrecisionClass {
// Each has a different set of built-ins, and we want to preserve that from // Each has a different set of built-ins, and we want to preserve that from
// compile to compile. // compile to compile.
// //
TSymbolTable* CommonSymbolTable[VersionCount][EProfileCount][EPcCount] = {}; TSymbolTable* CommonSymbolTable[VersionCount][ProfileCount][EPcCount] = {};
TSymbolTable* SharedSymbolTables[VersionCount][EProfileCount][EShLangCount] = {}; TSymbolTable* SharedSymbolTables[VersionCount][ProfileCount][EShLangCount] = {};
TPoolAllocator* PerProcessGPA = 0; 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 // See if it's already been done for this version/profile combination
int versionIndex = MapVersionToIndex(version); int versionIndex = MapVersionToIndex(version);
if (CommonSymbolTable[versionIndex][profile][EPcGeneral]) { int profileIndex = MapProfileToIndex(profile);
if (CommonSymbolTable[versionIndex][profileIndex][EPcGeneral]) {
glslang::ReleaseGlobalLock(); glslang::ReleaseGlobalLock();
return; 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 // 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) { for (int precClass = 0; precClass < EPcCount; ++precClass) {
if (! commonTable[precClass]->isEmpty()) { if (! commonTable[precClass]->isEmpty()) {
CommonSymbolTable[versionIndex][profile][precClass] = new TSymbolTable; CommonSymbolTable[versionIndex][profileIndex][precClass] = new TSymbolTable;
CommonSymbolTable[versionIndex][profile][precClass]->copyTable(*commonTable[precClass]); CommonSymbolTable[versionIndex][profileIndex][precClass]->copyTable(*commonTable[precClass]);
CommonSymbolTable[versionIndex][profile][precClass]->readOnly(); CommonSymbolTable[versionIndex][profileIndex][precClass]->readOnly();
} }
} }
for (int stage = 0; stage < EShLangCount; ++stage) { for (int stage = 0; stage < EShLangCount; ++stage) {
if (! stageTables[stage]->isEmpty()) { if (! stageTables[stage]->isEmpty()) {
SharedSymbolTables[versionIndex][profile][stage] = new TSymbolTable; SharedSymbolTables[versionIndex][profileIndex][stage] = new TSymbolTable;
SharedSymbolTables[versionIndex][profile][stage]->adoptLevels(*CommonSymbolTable[versionIndex][profile][CommonIndex(profile, (EShLanguage)stage)]); SharedSymbolTables[versionIndex][profileIndex][stage]->adoptLevels(*CommonSymbolTable[versionIndex][profileIndex][CommonIndex(profile, (EShLanguage)stage)]);
SharedSymbolTables[versionIndex][profile][stage]->copyTable(*stageTables[stage]); SharedSymbolTables[versionIndex][profileIndex][stage]->copyTable(*stageTables[stage]);
SharedSymbolTables[versionIndex][profile][stage]->readOnly(); SharedSymbolTables[versionIndex][profileIndex][stage]->readOnly();
} }
} }
@ -433,7 +449,7 @@ bool CompileDeferred(
SetupBuiltinSymbolTable(version, profile); SetupBuiltinSymbolTable(version, profile);
TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)] TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)]
[profile] [MapProfileToIndex(profile)]
[compiler->getLanguage()]; [compiler->getLanguage()];
// Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool. // 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() int __fastcall ShFinalize()
{ {
for (int version = 0; version < VersionCount; ++version) 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) for (int lang = 0; lang < EShLangCount; ++lang)
delete SharedSymbolTables[version][p][lang]; delete SharedSymbolTables[version][p][lang];
@ -964,7 +980,7 @@ bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
merged = intermediate[stage]; merged = intermediate[stage];
} }
infoSink->info << "\nLinked " << StageName[stage] << " stage:\n\n"; infoSink->info << "\nLinked " << StageName(stage) << " stage:\n\n";
if (stages[stage].size() > 1) { if (stages[stage].size() > 1) {
std::list<TShader*>::const_iterator it; std::list<TShader*>::const_iterator it;

View File

@ -37,68 +37,166 @@
// //
// Help manage multiple profiles, versions, extensions etc. // Help manage multiple profiles, versions, extensions etc.
// //
// These don't return error codes, as the presumption is parsing // These don't return error codes, as the presumption is parsing will
// will always continue as if the feature were present, and there // always continue as if the tested feature were enabled, and thus there
// is no error recovery needed to enable that. // 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" #include "ParseHelper.h"
namespace glslang { 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 // Initialize all extensions, almost always to 'disable', as once their features
// which subset allows the feature. If the current profile is not present, // are incorporated into a core version, their features are supported through allowing that
// give an error message. // core version, not through a pseudo-enablement of the extension.
// //
void TParseContext::requireProfile(TSourceLoc loc, EProfileMask profileMask, const char *featureDesc) void TParseContext::initializeExtensionBehavior()
{ {
if (((1 << profile) & profileMask) == 0) extensionBehavior["GL_ARB_texture_rectangle"] = EBhDisable;
error(loc, "not supported with this profile:", featureDesc, ProfileName[profile]); 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";
}
} }
// //
// If only some stages support a feature, use requireStage() to specify // When to use requireProfile():
// which subset allows the feature. If the current stage is not present, //
// 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().
//
// Operation: If the current profile is not one of the profileMask,
// give an error message. // 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) if (! (profile & profileMask))
error(loc, "not supported in this stage:", featureDesc, StageName[language]); 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 // When to use profileRequires():
// ProfileRequires(). This only checks if the current profile matches //
// the passed-in profile. // 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 // entry point that takes multiple extensions
void TParseContext::profileRequires(TSourceLoc loc, EProfile callingProfile, int minVersion, int numExtensions, const char* extensions[], const char *featureDesc) 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; bool okay = false;
if (version >= minVersion) if (minVersion > 0 && version >= minVersion)
okay = true; okay = true;
for (int i = 0; i < numExtensions; ++i) { for (int i = 0; i < numExtensions; ++i) {
TBehavior behavior = extensionBehavior[extensions[i]]; TExtensionBehavior behavior = extensionBehavior[extensions[i]];
switch (behavior) { switch (behavior) {
case EBhWarn: case EBhWarn:
infoSink.info.message(EPrefixWarning, ("extension " + TString(extensions[i]) + " is being used for " + featureDesc).c_str(), loc); 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 // entry point for the above that takes a single extension
void TParseContext::profileRequires(TSourceLoc loc, EProfile callingProfile, int minVersion, const char* extension, const char *featureDesc) void TParseContext::profileRequires(TSourceLoc loc, int profileMask, int minVersion, const char* extension, const char *featureDesc)
{ {
profileRequires(loc, callingProfile, minVersion, extension ? 1 : 0, &extension, featureDesc); profileRequires(loc, profileMask, minVersion, extension ? 1 : 0, &extension, featureDesc);
} }
// //
// Within a profile, see if a feature is deprecated and error or warn based on whether // 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)
{
requireStage(loc, static_cast<EShLanguageMask>(1 << stage), featureDesc);
}
//
// 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. // 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 (version >= depVersion) {
if (forwardCompatible) if (forwardCompatible)
error(loc, "deprecated, may be removed in future release", featureDesc, ""); 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. // 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) { if (version >= removedVersion) {
const int maxSize = 60; const int maxSize = 60;
char buf[maxSize]; 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); 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<TString, TExtensionBehavior>:: 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) void TParseContext::fullIntegerCheck(TSourceLoc loc, const char* op)
{ {
profileRequires(loc, ENoProfile, 130, 0, op); profileRequires(loc, ENoProfile, 130, 0, op);
profileRequires(loc, EEsProfile, 300, 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) 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, ECoreProfile, 400, 0, op);
profileRequires(loc, ECompatibilityProfile, 400, 0, op); profileRequires(loc, ECompatibilityProfile, 400, 0, op);
} }

View File

@ -40,19 +40,29 @@
// Help manage multiple profiles, versions, extensions etc. // Help manage multiple profiles, versions, extensions etc.
// //
//
// The behaviors from "#extension extension_name : behavior"
//
typedef enum { typedef enum {
ENoProfile, // only for desktop, before profiles showed up EBhRequire,
ECoreProfile, EBhEnable,
ECompatibilityProfile, EBhWarn,
EEsProfile, EBhDisable
EProfileCount, } 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 {
EBadProfile = 0,
ENoProfile = (1 << 0), // only for desktop, before profiles showed up
ECoreProfile = (1 << 1),
ECompatibilityProfile = (1 << 2),
EEsProfile = (1 << 3)
} EProfile; } EProfile;
typedef enum {
ENoProfileMask = (1 << ENoProfile),
ECoreProfileMask = (1 << ECoreProfile),
ECompatibilityProfileMask = (1 << ECompatibilityProfile),
EEsProfileMask = (1 << EEsProfile)
} EProfileMask;
#endif // _VERSIONS_INCLUDED_ #endif // _VERSIONS_INCLUDED_

View File

@ -1047,7 +1047,7 @@ interpolation_qualifier
} }
| NOPERSPECTIVE { | NOPERSPECTIVE {
parseContext.globalCheck($1.loc, parseContext.symbolTable.atGlobalLevel(), "noperspective"); parseContext.globalCheck($1.loc, parseContext.symbolTable.atGlobalLevel(), "noperspective");
parseContext.requireProfile($1.loc, static_cast<EProfileMask>(~EEsProfileMask), "noperspective"); parseContext.requireProfile($1.loc, ~EEsProfile, "noperspective");
parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "noperspective"); parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "noperspective");
$$.init($1.loc); $$.init($1.loc);
$$.qualifier.nopersp = true; $$.qualifier.nopersp = true;
@ -1138,7 +1138,7 @@ storage_qualifier
$$.qualifier.storage = EvqConst; $$.qualifier.storage = EvqConst;
} }
| ATTRIBUTE { | ATTRIBUTE {
parseContext.requireStage($1.loc, EShLangVertexMask, "attribute"); parseContext.requireStage($1.loc, EShLangVertex, "attribute");
parseContext.checkDeprecated($1.loc, ECoreProfile, 130, "attribute"); parseContext.checkDeprecated($1.loc, ECoreProfile, 130, "attribute");
parseContext.checkDeprecated($1.loc, ENoProfile, 130, "attribute"); parseContext.checkDeprecated($1.loc, ENoProfile, 130, "attribute");
parseContext.requireNotRemoved($1.loc, ECoreProfile, 420, "attribute"); parseContext.requireNotRemoved($1.loc, ECoreProfile, 420, "attribute");
@ -1206,9 +1206,9 @@ storage_qualifier
$$.qualifier.storage = EvqUniform; // TODO: 4.0 functionality: implement BUFFER $$.qualifier.storage = EvqUniform; // TODO: 4.0 functionality: implement BUFFER
} }
| SHARED { | SHARED {
parseContext.requireProfile($1.loc, static_cast<EProfileMask>(~EEsProfileMask), "shared"); parseContext.requireProfile($1.loc, ~EEsProfile, "shared");
parseContext.profileRequires($1.loc, ECoreProfile, 430, 0, "shared"); parseContext.profileRequires($1.loc, ECoreProfile, 430, 0, "shared");
parseContext.requireStage($1.loc, EShLangComputeMask, "shared"); parseContext.requireStage($1.loc, EShLangCompute, "shared");
$$.init($1.loc); $$.init($1.loc);
$$.qualifier.shared = true; $$.qualifier.shared = true;
} }
@ -2315,7 +2315,7 @@ jump_statement
parseContext.error($1.loc, "function return is not matching type:", "return", ""); parseContext.error($1.loc, "function return is not matching type:", "return", "");
} }
| DISCARD SEMICOLON { | DISCARD SEMICOLON {
parseContext.requireStage($1.loc, EShLangFragmentMask, "discard"); parseContext.requireStage($1.loc, EShLangFragment, "discard");
$$ = parseContext.intermediate.addBranch(EOpKill, $1.loc); $$ = parseContext.intermediate.addBranch(EOpKill, $1.loc);
} }
; ;

View File

@ -529,7 +529,7 @@ int TPpContext::CPPifdef(int defined, TPpToken * yylvalpp)
int token = currentInput->scan(this, currentInput, yylvalpp); int token = currentInput->scan(this, currentInput, yylvalpp);
int name = yylvalpp->atom; int name = yylvalpp->atom;
if (++ifdepth > maxIfNesting) { 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; return 0;
} }
elsetracker++; elsetracker++;

View File

@ -96,7 +96,7 @@ typedef enum {
namespace glslang { namespace glslang {
extern const char* StageName[EShLangCount]; const char* StageName(EShLanguage);
} // end namespace glslang } // end namespace glslang