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

@ -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.
// 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::requireProfile(TSourceLoc loc, EProfileMask profileMask, const char *featureDesc)
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";
}
}
//
// If only some stages support a feature, use requireStage() to specify
// which subset allows the feature. If the current stage is not present,
// 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().
//
// 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, 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.
//
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<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)
{
{
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);
}