diff --git a/Test/100.frag b/Test/100.frag index 08dc51b6..e07f75bc 100644 --- a/Test/100.frag +++ b/Test/100.frag @@ -88,4 +88,16 @@ void foo234() { texture3D(s3D2, vec3(0.2), 0.2); texture3DProj(s3D2, v[1], 0.4); + dFdx(v[0]); // ERROR + dFdy(3.2); // ERROR + fwidth(f13); // ERROR +} + +#extension GL_OES_standard_derivatives : enable + +void foo236() +{ + dFdx(v[0]); + dFdy(3.2); + fwidth(f13); } diff --git a/Test/baseResults/100.frag.out b/Test/baseResults/100.frag.out index d9a755a3..ae6b0143 100644 --- a/Test/baseResults/100.frag.out +++ b/Test/baseResults/100.frag.out @@ -42,7 +42,10 @@ ERROR: 0:76: 'h' : cannot use storage or interpolation qualifiers on structure m ERROR: 0:77: 'i' : cannot use invariant qualifier on structure members ERROR: 0:80: 'sampler3D' : Reserved word. ERROR: 0:80: 'sampler/image' : type requires declaration of default precision qualifier -ERROR: 37 compilation errors. No code generated. +ERROR: 0:91: 'dFdx' : required extension not requested: GL_OES_standard_derivatives +ERROR: 0:92: 'dFdy' : required extension not requested: GL_OES_standard_derivatives +ERROR: 0:93: 'fwidth' : required extension not requested: GL_OES_standard_derivatives +ERROR: 40 compilation errors. No code generated. ERROR: node is still EOpNull! 0:3 Sequence @@ -142,6 +145,27 @@ ERROR: node is still EOpNull! 0:90 1 (const int) 0:90 Constant: 0:90 0.400000 +0:91 dPdx (mediump 4-component vector of float) +0:91 direct index (smooth mediump 4-component vector of float) +0:91 'v' (smooth in 3-element array of mediump 4-component vector of float) +0:91 Constant: +0:91 0 (const int) +0:92 Constant: +0:92 0.000000 +0:93 fwidth (mediump float) +0:93 'f13' (invariant mediump float) +0:98 Function Definition: foo236( (void) +0:98 Function Parameters: +0:100 Sequence +0:100 dPdx (mediump 4-component vector of float) +0:100 direct index (smooth mediump 4-component vector of float) +0:100 'v' (smooth in 3-element array of mediump 4-component vector of float) +0:100 Constant: +0:100 0 (const int) +0:101 Constant: +0:101 0.000000 +0:102 fwidth (mediump float) +0:102 'f13' (invariant mediump float) 0:? Linker Objects 0:? 'a' (3-element array of mediump int) 0:? 'uint' (mediump int) diff --git a/glslang/MachineIndependent/Initialize.cpp b/glslang/MachineIndependent/Initialize.cpp index a6b64048..83eb4ccf 100644 --- a/glslang/MachineIndependent/Initialize.cpp +++ b/glslang/MachineIndependent/Initialize.cpp @@ -42,8 +42,10 @@ // Where to put a built-in: // TBuiltIns::initialize(version,profile) context-independent textual built-ins; add them to the right string // TBuiltIns::initialize(resources,...) context-dependent textual built-ins; add them to the right string -// IdentifyBuiltIns(...,symbolTable) context-independent programmatic additions/mappings to the symbol table -// IdentifyBuiltIns(...,symbolTable, resources) context-dependent programmatic additions/mappings to the symbol table +// IdentifyBuiltIns(...,symbolTable) context-independent programmatic additions/mappings to the symbol table, +// including identify what extensions are needed if a version does not allow a symbol +// IdentifyBuiltIns(...,symbolTable, resources) context-dependent programmatic additions/mappings to the symbol table, +// including identify what extensions are needed if a version does not allow a symbol // #include "../Include/intermediate.h" @@ -2120,8 +2122,8 @@ void SpecialQualifier(const char* name, TStorageQualifier qualifier, TSymbolTabl void IdentifyBuiltIns(int version, EProfile profile, EShLanguage language, TSymbolTable& symbolTable) { // - // First, insert some special built-in variables that are not in - // the built-in text strings. + // Tag built-in variables and functions with additional qualifier and extension information + // that cannot be declared with the text strings. // switch(language) { case EShLangVertex: @@ -2141,6 +2143,11 @@ void IdentifyBuiltIns(int version, EProfile profile, EShLanguage language, TSymb SpecialQualifier("gl_PointCoord", EvqPointCoord, symbolTable); SpecialQualifier("gl_FragColor", EvqFragColor, symbolTable); SpecialQualifier("gl_FragDepth", EvqFragDepth, symbolTable); + if (version == 100) { + symbolTable.setFunctionExtensions("dFdx", 1, &GL_OES_standard_derivatives); + symbolTable.setFunctionExtensions("dFdy", 1, &GL_OES_standard_derivatives); + symbolTable.setFunctionExtensions("fwidth", 1, &GL_OES_standard_derivatives); + } break; case EShLangCompute: diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 557981f0..e9dc7b17 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -385,6 +385,10 @@ TIntermTyped* TParseContext::handleVariable(TSourceLoc loc, TSymbol* symbol, TSt { TIntermTyped* node = 0; + // Error check for function requiring specific extensions present. + if (symbol && symbol->getNumExtensions()) + requireExtensions(loc, symbol->getNumExtensions(), symbol->getExtensions(), symbol->getName().c_str()); + const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : 0; if (anon) { // it was a member of an anonymous container, have to insert its dereference @@ -903,6 +907,10 @@ TIntermTyped* TParseContext::handleFunctionCall(TSourceLoc loc, TFunction* fnCal bool builtIn; fnCandidate = findFunction(loc, *fnCall, builtIn); if (fnCandidate) { + // Error check for function requiring specific extensions present. + if (builtIn && fnCandidate->getNumExtensions()) + requireExtensions(loc, fnCandidate->getNumExtensions(), fnCandidate->getExtensions(), fnCandidate->getName().c_str()); + // // A declared function. But, it might still map to a built-in // operation. diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index 7c5b8ab0..9ff76e77 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -185,6 +185,7 @@ public: 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 requireExtensions(TSourceLoc, int numExtensions, const char* const extensions[], const char *featureDesc); TExtensionBehavior getExtensionBehavior(const char*); bool extensionsTurnedOn(int numExtensions, const char* const extensions[]); void updateExtensionBehavior(const char* const extension, const char* behavior); diff --git a/glslang/MachineIndependent/SymbolTable.cpp b/glslang/MachineIndependent/SymbolTable.cpp index 4c6cbcde..e801a935 100644 --- a/glslang/MachineIndependent/SymbolTable.cpp +++ b/glslang/MachineIndependent/SymbolTable.cpp @@ -207,6 +207,23 @@ void TSymbolTableLevel::relateToOperator(const char* name, TOperator op) } } +// Make all function overloads of the given name require an extension(s). +// Should only be used for a version/profile that actually needs the extension(s). +void TSymbolTableLevel::setFunctionExtensions(const char* name, int num, const char* const extensions[]) +{ + tLevel::const_iterator candidate = level.lower_bound(name); + while (candidate != level.end()) { + const TString& candidateName = (*candidate).first; + TString::size_type parenAt = candidateName.find_first_of('('); + if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) { + TSymbol* symbol = candidate->second; + symbol->setExtensions(num, extensions); + } else + break; + ++candidate; + } +} + // // Make all symbols in this table level read only. // @@ -230,6 +247,8 @@ TVariable::TVariable(const TVariable& copyOf) : TSymbol(copyOf) { type.deepCopy(copyOf.type); userType = copyOf.userType; + extensions = 0; + setExtensions(copyOf.numExtensions, copyOf.extensions); if (! copyOf.unionArray.empty()) { assert(!copyOf.type.getStruct()); @@ -255,6 +274,8 @@ TFunction::TFunction(const TFunction& copyOf) : TSymbol(copyOf) parameters.back().copyParam(copyOf.parameters[i]); } + extensions = 0; + setExtensions(copyOf.numExtensions, copyOf.extensions); returnType.deepCopy(copyOf.returnType); mangledName = copyOf.mangledName; op = copyOf.op; diff --git a/glslang/MachineIndependent/SymbolTable.h b/glslang/MachineIndependent/SymbolTable.h index 52182dbb..d36a5968 100644 --- a/glslang/MachineIndependent/SymbolTable.h +++ b/glslang/MachineIndependent/SymbolTable.h @@ -81,9 +81,9 @@ class TAnonMember; class TSymbol { public: POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) - explicit TSymbol(const TString *n) : name(n), writable(true) { } + explicit TSymbol(const TString *n) : name(n), writable(true), numExtensions(0), extensions(0) { } virtual TSymbol* clone() const = 0; - virtual ~TSymbol() { } + virtual ~TSymbol() { delete [] extensions; } virtual const TString& getName() const { return *name; } virtual void changeName(const TString* newName) { name = newName; } @@ -97,6 +97,16 @@ public: virtual TType& getWritableType() = 0; virtual void setUniqueId(int id) { uniqueId = id; } virtual int getUniqueId() const { return uniqueId; } + virtual void setExtensions(int num, const char* const exts[]) + { + assert(extensions == 0); + numExtensions = num; + extensions = new const char*[numExtensions]; + for (int e = 0; e < num; ++e) + extensions[e] = exts[e]; + } + virtual int getNumExtensions() const { return numExtensions; } + virtual const char** getExtensions() const { return extensions; } virtual void dump(TInfoSink &infoSink) const = 0; virtual bool isReadOnly() const { return ! writable; } @@ -109,6 +119,11 @@ protected: const TString *name; unsigned int uniqueId; // For cross-scope comparing during code generation + // For tracking what extensions must be present + // (don't use if correct version/profile is present). + int numExtensions; + const char** extensions; // an array of pointers to existing constant char strings + // // N.B.: Non-const functions that will be generally used should assert an this, // to avoid overwriting shared symbol-table information. @@ -376,6 +391,7 @@ public: } void relateToOperator(const char* name, TOperator op); + void setFunctionExtensions(const char* name, int num, const char* const extensions[]); void dump(TInfoSink &infoSink) const; TSymbolTableLevel* clone() const; void readOnly(); @@ -551,6 +567,12 @@ public: for (unsigned int level = 0; level < table.size(); ++level) table[level]->relateToOperator(name, op); } + + void setFunctionExtensions(const char* name, int num, const char* const extensions[]) + { + for (unsigned int level = 0; level < table.size(); ++level) + table[level]->setFunctionExtensions(name, num, extensions); + } int getMaxSymbolId() { return uniqueId; } void dump(TInfoSink &infoSink) const; diff --git a/glslang/MachineIndependent/Versions.cpp b/glslang/MachineIndependent/Versions.cpp index 93e3c039..03b239e1 100644 --- a/glslang/MachineIndependent/Versions.cpp +++ b/glslang/MachineIndependent/Versions.cpp @@ -60,6 +60,7 @@ // requireStage() // checkDeprecated() // requireNotRemoved() +// requireExtensions() // // 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 @@ -129,8 +130,12 @@ // // ~EEsProfile // -// 6) If built-in symbols are added by the extension, add them in Initialize.cpp; see -// the comment at the top of that file for where to put them. +// 6) If built-in symbols are added by the extension, add them in Initialize.cpp: Their use +// will be automatically error checked against the extensions enabled at that moment. +// see the comment at the top of Initialize.cpp for where to put them. Establish them at +// the earliest release that supports the extension. Then, tag them with the +// set of extensions that both enable them and are necessary, given the version of the symbol +// table. (There is a different symbol table for each version.) // #include "ParseHelper.h" @@ -145,6 +150,7 @@ namespace glslang { void TParseContext::initializeExtensionBehavior() { extensionBehavior[GL_OES_texture_3D] = EBhDisable; + extensionBehavior[GL_OES_standard_derivatives] = EBhDisable; extensionBehavior[GL_ARB_texture_rectangle] = EBhDisable; extensionBehavior[GL_3DL_array_objects] = EBhDisable; @@ -159,8 +165,9 @@ const char* TParseContext::getPreamble() { if (profile == EEsProfile) { return + "#define GL_ES 1\n" "#define GL_OES_texture_3D 1\n" - "#define GL_ES 1\n"; + "#define GL_OES_standard_derivatives 1\n"; } else { return "#define GL_FRAGMENT_PRECISION_HIGH 1\n" @@ -316,6 +323,41 @@ void TParseContext::requireNotRemoved(TSourceLoc loc, int profileMask, int remov } } +// +// Use when there are no profile/version to check, it's just an error if one of the +// extensions is not present. +// +void TParseContext::requireExtensions(TSourceLoc loc, int numExtensions, const char* const extensions[], const char *featureDesc) +{ + // First, see if any of the extensions are enabled + for (int i = 0; i < numExtensions; ++i) { + TExtensionBehavior behavior = getExtensionBehavior(extensions[i]); + if (behavior == EBhEnable || behavior == EBhRequire) + return; + } + + // See if any extensions want to give a warning on use; give warnings for all such extensions + bool warned = false; + for (int i = 0; i < numExtensions; ++i) { + TExtensionBehavior behavior = getExtensionBehavior(extensions[i]); + if (behavior == EBhWarn) { + infoSink.info.message(EPrefixWarning, ("extension " + TString(extensions[i]) + " is being used for " + featureDesc).c_str(), loc); + warned = true; + } + } + if (warned) + return; + + // If we get this far, give errors explaining what extensions are needed + if (numExtensions == 1) + error(loc, "required extension not requested:", featureDesc, extensions[0]); + else { + error(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); + for (int i = 0; i < numExtensions; ++i) + infoSink.info.message(EPrefixNone, extensions[i]); + } +} + TExtensionBehavior TParseContext::getExtensionBehavior(const char* extension) { TMap::iterator iter = extensionBehavior.find(TString(extension)); @@ -375,12 +417,12 @@ void TParseContext::updateExtensionBehavior(const char* extension, const char* b if (iter == extensionBehavior.end()) { switch (behavior) { case EBhRequire: - error(getCurrentLoc(), "extension not supported", "#extension", extension); + error(getCurrentLoc(), "extension not supported:", "#extension", extension); break; case EBhEnable: case EBhWarn: case EBhDisable: - warn(getCurrentLoc(), "extension not supported", "#extension", extension); + warn(getCurrentLoc(), "extension not supported:", "#extension", extension); break; default: assert(0 && "unexpected behavior"); diff --git a/glslang/MachineIndependent/Versions.h b/glslang/MachineIndependent/Versions.h index 66622a86..393dbe4f 100644 --- a/glslang/MachineIndependent/Versions.h +++ b/glslang/MachineIndependent/Versions.h @@ -73,7 +73,7 @@ typedef enum { // functions, but better to have the compiler do spelling checks. // const char* const GL_OES_texture_3D = "GL_OES_texture_3D"; - +const char* const GL_OES_standard_derivatives = "GL_OES_standard_derivatives"; const char* const GL_ARB_texture_rectangle = "GL_ARB_texture_rectangle"; const char* const GL_3DL_array_objects = "GL_3DL_array_objects";