/*------------------------------------------------------------------------- * OpenGL Conformance Test Suite * ----------------------------- * * Copyright (c) 2014-2016 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /*! * \file * \brief */ /*-------------------------------------------------------------------*/ #include "glwEnums.hpp" #include "gluContextInfo.hpp" #include "tcuRenderTarget.hpp" #include "tcuVectorUtil.hpp" #include #include #include "es31cExplicitUniformLocationTest.hpp" namespace glcts { using namespace glw; namespace { class Logger { public: Logger() : null_log_(0) { } Logger(const Logger& rhs) { null_log_ = rhs.null_log_; if (!null_log_) { str_ << rhs.str_.str(); } } ~Logger() { s_tcuLog->writeMessage(str_.str().c_str()); if (!str_.str().empty()) { s_tcuLog->writeMessage(NL); } } template Logger& operator<<(const T& t) { if (!null_log_) { str_ << t; } return *this; } static tcu::TestLog* Get() { return s_tcuLog; } static void setOutput(tcu::TestLog& log) { s_tcuLog = &log; } private: void operator=(const Logger&); bool null_log_; std::ostringstream str_; static tcu::TestLog* s_tcuLog; }; tcu::TestLog* Logger::s_tcuLog = NULL; class DefOccurence { public: enum DefOccurenceEnum { ALL_SH, VSH, FSH_OR_CSH, //"one shader" ALL_BUT_FSH, ALL_BUT_VSH, NONE_SH, } occurence; DefOccurence(DefOccurenceEnum _occurence) : occurence(_occurence) { } bool occurs(GLenum shader) const { if (occurence == NONE_SH) { return false; } if (occurence == ALL_SH) { return true; } if (occurence == FSH_OR_CSH) { return shader == GL_FRAGMENT_SHADER || shader == GL_COMPUTE_SHADER; } if (occurence == VSH) { return shader == GL_VERTEX_SHADER; } if (occurence == ALL_BUT_FSH) { return shader != GL_FRAGMENT_SHADER; } if (occurence == ALL_BUT_VSH) { return shader != GL_VERTEX_SHADER; } assert(0); return false; } }; class LocationSpecifier { }; class IndexSpecifier { }; class LayoutSpecifierBase { public: enum NumSys { Dec, Oct, Hex, }; LayoutSpecifierBase(int _val, NumSys _numSys, DefOccurence _occurence) : val(_val), numSys(_numSys), occurence(_occurence) { } bool isImplicit(const std::vector stages) const { bool implicit = true; for (size_t i = 0; i < stages.size(); i++) { implicit &= !occurence.occurs(stages[i]); } return implicit; } int val; NumSys numSys; DefOccurence occurence; }; template class LayoutSpecifier : public LayoutSpecifierBase { public: LayoutSpecifier(int _val, NumSys _numSys, DefOccurence _occurence) : LayoutSpecifierBase(_val, _numSys, _occurence) { } static LayoutSpecifier C(int _val, NumSys _sys = Dec) { return LayoutSpecifier(_val, _sys, DefOccurence::ALL_SH); } static LayoutSpecifier C(int _val, DefOccurence _occurence) { return LayoutSpecifier(_val, Dec, _occurence); } static LayoutSpecifier Implicit() { return LayoutSpecifier(1999999, Dec, DefOccurence::NONE_SH); } void streamDefinition(std::ostringstream& str, GLenum shader) const; }; typedef LayoutSpecifier Loc; typedef LayoutSpecifier Index; template <> void LayoutSpecifier::streamDefinition(std::ostringstream& str, GLenum shader) const { if (val < 0 || !occurence.occurs(shader)) { return; } str << "layout(location = "; if (numSys == Loc::Oct) { str << std::oct << "0"; } else if (numSys == Loc::Hex) { str << std::hex << "0x"; } str << val << std::dec << ") "; } template <> void LayoutSpecifier::streamDefinition(std::ostringstream& str, GLenum shader) const { if (val < 0 || !occurence.occurs(shader)) { return; } str << "layout(index = "; if (numSys == Loc::Oct) { str << std::oct << "0"; } else if (numSys == Loc::Hex) { str << std::hex << "0x"; } str << val << std::dec << ") "; } class UniformStructCounter { public: UniformStructCounter() : counter(0) { } GLint getNextCount() { return counter++; } private: UniformStructCounter(const UniformStructCounter&); GLint counter; }; class UniformType { public: UniformType(GLenum _enumType, int _arraySize = 0) : enumType(_enumType), arraySize(_arraySize), isArray(_arraySize > 0), signedType(true) { if (!arraySize) { arraySize = 1; } arraySizesSegmented.push_back(arraySize); fill(); } UniformType(GLenum _enumType, const std::vector& _arraySizesSegmented) : enumType(_enumType), arraySizesSegmented(_arraySizesSegmented), isArray(true), signedType(true) { arraySize = 1; for (size_t i = 0; i < arraySizesSegmented.size(); i++) { assert(arraySizesSegmented[i] > 0); arraySize *= arraySizesSegmented[i]; } fill(); } UniformType(UniformStructCounter& structCounter, std::vector _childTypes, int _arraySize = 0) : enumType(0), arraySize(_arraySize), childTypes(_childTypes), isArray(_arraySize > 0), signedType(true) { baseType = 0; std::ostringstream _str; _str << "S" << structCounter.getNextCount(); strType = _str.str(); if (!arraySize) { arraySize = 1; } arraySizesSegmented.push_back(arraySize); } inline const std::string& str() const { return strType; } inline const std::string& refStr() const { return refStrType; } bool isStruct() const { return (baseType == 0); } bool isSigned() const { return signedType; } const char* abs() const { switch (baseType) { case GL_FLOAT: case GL_SAMPLER: return "0.1"; case GL_UNSIGNED_INT: return "0u"; case GL_INT: return "0"; default: assert(0); return ""; } } std::pair getSize() const { return size; } GLenum getBaseType() const { return baseType; } void streamArrayStr(std::ostringstream& _str, int arrayElem = -1) const { if (!isArray) { return; } if (arrayElem < 0) { for (size_t segment = 0; segment < arraySizesSegmented.size(); segment++) { _str << "[" << arraySizesSegmented[segment] << "]"; } } else { int tailSize = arraySize; for (size_t segment = 0; segment < arraySizesSegmented.size(); segment++) { tailSize /= arraySizesSegmented[segment]; _str << "[" << arrayElem / tailSize << "]"; arrayElem %= tailSize; } } } GLenum enumType; //arrays-of-arrays size std::vector arraySizesSegmented; //premultiplied array size int arraySize; //child types for nested (struct) types; std::vector childTypes; private: void fill() { size = std::pair(1, 1); switch (enumType) { case GL_SAMPLER_2D: refStrType = "vec4"; strType = "sampler2D"; baseType = GL_SAMPLER; break; case GL_FLOAT: refStrType = strType = "float"; baseType = GL_FLOAT; break; case GL_INT: refStrType = strType = "int"; baseType = GL_INT; break; case GL_UNSIGNED_INT: refStrType = strType = "uint"; baseType = GL_UNSIGNED_INT; signedType = false; break; case GL_FLOAT_VEC2: refStrType = strType = "vec2"; baseType = GL_FLOAT; size.first = 2; break; case GL_FLOAT_VEC3: refStrType = strType = "vec3"; baseType = GL_FLOAT; size.first = 3; break; case GL_FLOAT_VEC4: refStrType = strType = "vec4"; baseType = GL_FLOAT; size.first = 4; break; case GL_FLOAT_MAT2: strType = "mat2"; refStrType = "vec2"; baseType = GL_FLOAT; size.first = size.second = 2; break; case GL_FLOAT_MAT3: strType = "mat3"; refStrType = "vec3"; baseType = GL_FLOAT; size.first = size.second = 3; break; case GL_FLOAT_MAT4: strType = "mat4"; refStrType = "vec4"; baseType = GL_FLOAT; size.first = size.second = 4; break; case GL_FLOAT_MAT2x3: strType = "mat2x3"; refStrType = "vec3"; baseType = GL_FLOAT; size.first = 3; size.second = 2; break; case GL_FLOAT_MAT4x3: strType = "mat4x3"; refStrType = "vec3"; baseType = GL_FLOAT; size.first = 3; size.second = 4; break; case GL_FLOAT_MAT2x4: strType = "mat2x4"; refStrType = "vec4"; baseType = GL_FLOAT; size.first = 4; size.second = 2; break; case GL_FLOAT_MAT3x4: strType = "mat3x4"; refStrType = "vec4"; baseType = GL_FLOAT; size.first = 4; size.second = 3; break; case GL_FLOAT_MAT3x2: strType = "mat3x2"; refStrType = "vec2"; baseType = GL_FLOAT; size.first = 2; size.second = 3; break; case GL_FLOAT_MAT4x2: strType = "mat4x2"; refStrType = "vec2"; baseType = GL_FLOAT; size.first = 2; size.second = 4; break; case GL_INT_VEC2: refStrType = strType = "ivec2"; baseType = GL_INT; size.first = 2; break; case GL_INT_VEC3: refStrType = strType = "ivec3"; baseType = GL_INT; size.first = 3; break; case GL_INT_VEC4: refStrType = strType = "ivec4"; baseType = GL_INT; size.first = 4; break; default: assert(0); } } std::string strType, refStrType; std::pair size; GLenum baseType; bool isArray; bool signedType; }; class UniformValueGenerator { public: UniformValueGenerator() : fValue(0.0f), iValue(0) { } GLfloat genF() { if (fValue > 99999.0f) { fValue = 0.0f; } return (fValue += 1.0f); } GLint genI() { return (iValue += 1); } private: UniformValueGenerator(const UniformValueGenerator&); GLfloat fValue; GLint iValue; }; class UniformValue { public: void streamValue(std::ostringstream& str, int arrayElement = 0, int column = 0) const { int arrayElementSize = type.getSize().first * type.getSize().second; str << type.refStr() << "("; if (type.getBaseType() == GL_SAMPLER) { for (size_t elem = 0; elem < 4; elem++) { if (elem) str << ", "; str << fValues[arrayElement * 4 + elem]; } str << ")"; return; } for (int elem = 0; fValues.size() && elem < type.getSize().first; elem++) { if (elem) str << ", "; str << fValues[arrayElement * arrayElementSize + column * type.getSize().first + elem] << ".0"; } for (int elem = 0; iValues.size() && elem < type.getSize().first; elem++) { if (elem) str << ", "; str << iValues[arrayElement * arrayElementSize + elem]; } for (int elem = 0; uValues.size() && elem < type.getSize().first; elem++) { if (elem) str << ", "; str << uValues[arrayElement * arrayElementSize + elem] << "u"; } str << ")"; } const void* getPtr(int arrayElement) const { int arrayElementSize = type.getSize().first * type.getSize().second; if (type.getBaseType() == GL_INT || type.getBaseType() == GL_SAMPLER) { return &iValues[arrayElement * arrayElementSize]; } else if (type.getBaseType() == GL_UNSIGNED_INT) { return &uValues[arrayElement * arrayElementSize]; } else if (type.getBaseType() == GL_FLOAT) { return &fValues[arrayElement * arrayElementSize]; } assert(0); return NULL; } UniformValue(const UniformType& _type, UniformValueGenerator& generator) : type(_type) { const int sizeRow = type.getSize().first; const int sizeColumn = type.getSize().second; if (type.isStruct()) { return; } if (type.getBaseType() == GL_INT) { assert(sizeColumn == 1); iValues.resize(sizeRow * type.arraySize); for (size_t elem = 0; elem < iValues.size(); elem++) { iValues[elem] = generator.genI(); } } else if (type.getBaseType() == GL_UNSIGNED_INT) { assert(sizeColumn == 1); uValues.resize(sizeRow * type.arraySize); for (size_t elem = 0; elem < uValues.size(); elem++) { uValues[elem] = static_cast(generator.genI()); } } else if (type.getBaseType() == GL_FLOAT) { fValues.resize(sizeColumn * sizeRow * type.arraySize); for (size_t elem = 0; elem < fValues.size(); elem++) { fValues[elem] = generator.genF(); } } else if (type.getBaseType() == GL_SAMPLER) { //color ref value fValues.resize(4 * type.arraySize); for (size_t elem = 0; elem < fValues.size(); elem++) { fValues[elem] = float(elem) / float(fValues.size()); } //uniform value iValues.resize(type.arraySize); for (size_t elem = 0; elem < iValues.size(); elem++) { iValues[elem] = generator.genI() % 16; } } else { assert(0); } } std::vector fValues; std::vector iValues; std::vector uValues; private: UniformType type; }; class Uniform { public: Uniform(UniformValueGenerator& generator, UniformType _type, Loc _location, DefOccurence _declOccurence = DefOccurence::ALL_SH, DefOccurence _usageOccurence = DefOccurence::ALL_SH) : type(_type) , location(_location) , declOccurence(_declOccurence) , usageOccurence(_usageOccurence) , value(_type, generator) { if (type.isStruct()) { int currentLocation = location.val; for (int arrayElem = 0; arrayElem < type.arraySize; arrayElem++) { for (size_t child = 0; child < type.childTypes.size(); child++) { Loc childLocation = Loc::Implicit(); if (currentLocation > 0) { childLocation = Loc::C(currentLocation); } childUniforms.push_back( Uniform(generator, type.childTypes[child], childLocation, declOccurence, usageOccurence)); currentLocation += type.childTypes[child].arraySize; } } } } void setName(const std::string& parentName, const std::string& _name) { shortName = _name; { std::ostringstream __name; __name << parentName << _name; name = __name.str(); } if (type.isStruct()) { for (size_t i = 0; i < childUniforms.size(); i++) { std::ostringstream childName; childName << "m" << (i % (childUniforms.size() / type.arraySize)); std::ostringstream childParentName; childParentName << name; type.streamArrayStr(childParentName, (int)(i / type.arraySize)); childParentName << "."; childUniforms[i].setName(childParentName.str(), childName.str()); } } } const std::string& getName() const { return name; } void streamDefinition(std::ostringstream& str) const { str << type.str() << " " << shortName; type.streamArrayStr(str); } UniformType type; Loc location; DefOccurence declOccurence, usageOccurence; UniformValue value; std::vector childUniforms; std::string name, shortName; }; class SubroutineFunction { public: SubroutineFunction(UniformValueGenerator& generator, Index _index = Index::Implicit()) : index(_index), embeddedRetVal(GL_FLOAT_VEC4, generator) { } const UniformValue& getRetVal() const { return embeddedRetVal; } inline const std::string& getName() const { return name; } void setName(int _name) { std::ostringstream __name; __name << "sf" << _name; name = __name.str(); } Index index; private: UniformValue embeddedRetVal; std::string name; }; class SubroutineFunctionSet { public: SubroutineFunctionSet(UniformValueGenerator& generator, size_t count = 0) : fn(count, SubroutineFunction(generator)) { } void push_back(const SubroutineFunction& _fn) { fn.push_back(_fn); } inline const std::string& getTypeName() const { return typeName; } void setTypeName(int _name) { std::ostringstream __name; __name << "st" << _name; typeName = __name.str(); } std::vector fn; std::string typeName; }; class SubroutineUniform { public: SubroutineUniform(UniformValueGenerator& generator, SubroutineFunctionSet& _functions, Loc _location, int _arraySize = 0, DefOccurence _defOccurence = DefOccurence::ALL_SH, bool _used = true) : functions(_functions) , location(_location) , arraySize(_arraySize) , defOccurence(_defOccurence) , used(_used) , embeddedUIntUniform(GL_UNSIGNED_INT, generator) { assert(arraySize >= 0); if (!arraySize) { arraySize = 1; isArray = false; } else { isArray = true; } arraySizesSegmented.push_back(arraySize); embeddedUIntUniform = UniformValue(UniformType(GL_UNSIGNED_INT, arraySize), generator); for (int i = 0; i < arraySize; i++) { embeddedUIntUniform.uValues[i] = static_cast(embeddedUIntUniform.uValues[i] % functions.fn.size()); } } SubroutineUniform(UniformValueGenerator& generator, SubroutineFunctionSet& _functions, Loc _location, std::vector _arraySizesSegmented, DefOccurence _defOccurence = DefOccurence::ALL_SH, bool _used = true) : functions(_functions) , location(_location) , defOccurence(_defOccurence) , used(_used) , arraySizesSegmented(_arraySizesSegmented) , isArray(true) , embeddedUIntUniform(GL_UNSIGNED_INT, generator) { arraySize = 1; for (size_t i = 0; i < arraySizesSegmented.size(); i++) { assert(arraySizesSegmented[i] > 0); arraySize *= arraySizesSegmented[i]; } embeddedUIntUniform = UniformValue(UniformType(GL_UNSIGNED_INT, arraySize), generator); for (int i = 0; i < arraySize; i++) { embeddedUIntUniform.uValues[i] = static_cast(embeddedUIntUniform.uValues[i] % functions.fn.size()); } } void setName(const std::string& _name) { name = _name; } const std::string& getName() const { return name; } void streamArrayStr(std::ostringstream& str, int arrayElem = -1) const { if (!isArray) { return; } if (arrayElem < 0) { for (size_t segment = 0; segment < arraySizesSegmented.size(); segment++) { str << "[" << arraySizesSegmented[segment] << "]"; } } else { int tailSize = arraySize; for (size_t segment = 0; segment < arraySizesSegmented.size(); segment++) { tailSize /= arraySizesSegmented[segment]; str << "[" << arrayElem / tailSize << "]"; arrayElem %= tailSize; } } } const SubroutineFunction& getSelectedFunction(int arrayElem) const { assert(arrayElem < arraySize); return functions.fn[embeddedUIntUniform.uValues[arrayElem]]; } SubroutineFunctionSet functions; Loc location; int arraySize; DefOccurence defOccurence; bool used; private: std::vector arraySizesSegmented; bool isArray; UniformValue embeddedUIntUniform; std::string name; }; class ShaderKey { public: ShaderKey() { } ShaderKey(GLenum _stage, const std::string& _input, const std::string& _output) : stage(_stage), input(_input), output(_output) { } GLenum stage; std::string input, output; bool operator<(const ShaderKey& rhs) const { if (stage == rhs.stage) { if (input == rhs.input) { return (output < rhs.output); } return input < rhs.input; } return stage < rhs.stage; } }; class CompiledProgram { public: GLuint name; std::vector stages; }; class ShaderSourceFactory { static void streamUniformDefinitions(const std::vector& uniforms, GLenum shader, std::ostringstream& ret) { for (size_t i = 0; i < uniforms.size(); i++) { if (uniforms[i].declOccurence.occurs(shader)) { if (uniforms[i].type.isStruct()) { ret << "struct " << uniforms[i].type.str() << " {" << std::endl; for (size_t child = 0; child < uniforms[i].childUniforms.size() / uniforms[i].type.arraySize; child++) { ret << " "; uniforms[i].childUniforms[child].streamDefinition(ret); ret << ";" << std::endl; } ret << "};" << std::endl; } uniforms[i].location.streamDefinition(ret, shader); ret << "uniform "; uniforms[i].streamDefinition(ret); ret << ";" << std::endl; } } } static void streamSubroutineDefinitions(const std::vector& subroutineUniforms, GLenum shader, std::ostringstream& ret) { if (subroutineUniforms.size()) { //add a "zero" uniform; ret << "uniform float zero;" << std::endl; } for (size_t i = 0; i < subroutineUniforms.size(); i++) { if (subroutineUniforms[i].defOccurence.occurs(shader)) { //subroutine vec4 st0(float param); ret << "subroutine vec4 " << subroutineUniforms[i].functions.getTypeName() << "(float param);" << std::endl; for (size_t fn = 0; fn < subroutineUniforms[i].functions.fn.size(); fn++) { //layout(index = X) subroutine(st0) vec4 sf0(float param) { .... }; subroutineUniforms[i].functions.fn[fn].index.streamDefinition(ret, shader); ret << "subroutine(" << subroutineUniforms[i].functions.getTypeName() << ") vec4 " << subroutineUniforms[i].functions.fn[fn].getName() << "(float param) { return zero + "; subroutineUniforms[i].functions.fn[fn].getRetVal().streamValue(ret); ret << "; }" << std::endl; } //layout(location = X) subroutine uniform stX uX[...]; subroutineUniforms[i].location.streamDefinition(ret, shader); ret << "subroutine uniform " << subroutineUniforms[i].functions.getTypeName() << " " << subroutineUniforms[i].getName(); subroutineUniforms[i].streamArrayStr(ret); ret << ";" << std::endl; } } } static void streamUniformValidator(std::ostringstream& ret, const Uniform& uniform, GLenum shader, const char* outTemporary) { if (uniform.declOccurence.occurs(shader) && uniform.usageOccurence.occurs(shader)) { if (uniform.type.isStruct()) { for (size_t child = 0; child < uniform.childUniforms.size(); child++) { streamUniformValidator(ret, uniform.childUniforms[child], shader, outTemporary); } } else { for (int arrayElement = 0; arrayElement < uniform.type.arraySize; arrayElement++) { for (int column = 0; column < uniform.type.getSize().second; column++) { std::string columnIndex; if (uniform.type.getSize().second > 1) { std::ostringstream str; str << "[" << column << "]"; columnIndex = str.str(); } std::string absoluteF; if (uniform.type.isSigned()) { absoluteF = "abs"; } if (uniform.type.getBaseType() == GL_SAMPLER) { ret << NL " if (any(greaterThan(" << absoluteF << "(texture(" << uniform.getName(); uniform.type.streamArrayStr(ret, arrayElement); ret << columnIndex << ", vec2(0.5)) - "; uniform.value.streamValue(ret, arrayElement, column); ret << " ), " << uniform.type.refStr() << "(" << uniform.type.abs() << ")))) {"; } else if (uniform.type.getSize().first > 1) { ret << NL " if (any(greaterThan(" << absoluteF << "(" << uniform.getName(); uniform.type.streamArrayStr(ret, arrayElement); ret << columnIndex << " - "; uniform.value.streamValue(ret, arrayElement, column); ret << "), " << uniform.type.refStr() << "(" << uniform.type.abs() << ")))) {"; } else { ret << NL " if (" << absoluteF << "(" << uniform.getName(); uniform.type.streamArrayStr(ret, arrayElement); ret << " - "; uniform.value.streamValue(ret, arrayElement); ret << ") >" << uniform.type.refStr() << "(" << uniform.type.abs() << ")) {"; } ret << NL " " << outTemporary << " = vec4 (1.0, 0.0, 0.0, 1.0);"; ret << NL " }"; } } } } } static void streamUniformValidators(std::ostringstream& ret, const std::vector& uniforms, GLenum shader, const char* outTemporary) { for (size_t i = 0; i < uniforms.size(); i++) { streamUniformValidator(ret, uniforms[i], shader, outTemporary); } } static void streamSubroutineValidator(std::ostringstream& ret, const SubroutineUniform& subroutineUniform, GLenum shader, const char* outTemporary) { if (subroutineUniform.defOccurence.occurs(shader) && subroutineUniform.used) { for (int arrayElem = 0; arrayElem < subroutineUniform.arraySize; arrayElem++) { ret << NL " if (any(greaterThan(abs(" << subroutineUniform.getName(); subroutineUniform.streamArrayStr(ret, arrayElem); ret << "(zero) - "; subroutineUniform.getSelectedFunction(arrayElem).getRetVal().streamValue(ret); ret << "), vec4(0.1)))) {"; ret << NL " " << outTemporary << " = vec4 (1.0, 0.0, 0.0, 1.0);"; ret << NL " }"; } } } static void streamSubroutineValidators(std::ostringstream& ret, const std::vector& subroutineUniforms, GLenum shader, const char* outTemporary) { for (size_t i = 0; i < subroutineUniforms.size(); i++) { streamSubroutineValidator(ret, subroutineUniforms[i], shader, outTemporary); } } static void streamShaderHeader(std::ostringstream& str, const glu::ContextType type) { if (glu::isContextTypeES(type)) { str << "#version 310 es" NL "precision highp float;" NL "precision highp int;"; } else { str << "#version 430 core" NL; } } static std::string generateFragmentShader(const ShaderKey& key, const std::vector& uniforms, const std::vector& subroutineUniforms, const std::string& additionalDef, const glu::ContextType type) { std::ostringstream ret; streamShaderHeader(ret, type); ret << NL; streamUniformDefinitions(uniforms, GL_FRAGMENT_SHADER, ret); ret << NL; streamSubroutineDefinitions(subroutineUniforms, GL_FRAGMENT_SHADER, ret); ret << NL << additionalDef << NL "in vec4 " << key.input << ";" << NL "out vec4 out_FragColor;" << NL "void main() {" << NL " vec4 validationResult = " << key.input << ";" << NL; streamUniformValidators(ret, uniforms, GL_FRAGMENT_SHADER, "validationResult"); ret << NL; streamSubroutineValidators(ret, subroutineUniforms, GL_FRAGMENT_SHADER, "validationResult"); ret << NL " out_FragColor = validationResult;" << NL "}"; return ret.str(); } static std::string generateVertexShader(const ShaderKey& key, const std::vector& uniforms, const std::vector& subroutineUniforms, const std::string& additionalDef, const glu::ContextType type) { std::ostringstream ret; streamShaderHeader(ret, type); ret << NL; streamUniformDefinitions(uniforms, GL_VERTEX_SHADER, ret); ret << NL; streamSubroutineDefinitions(subroutineUniforms, GL_VERTEX_SHADER, ret); ret << NL << additionalDef << NL "in vec4 in_Position;" << NL "out vec4 " << key.output << ";" << NL "void main() {" << NL " vec4 validationResult = vec4(0.0, 1.0, 0.0, 1.0);" << NL; streamUniformValidators(ret, uniforms, GL_VERTEX_SHADER, "validationResult"); ret << NL; streamSubroutineValidators(ret, subroutineUniforms, GL_VERTEX_SHADER, "validationResult"); ret << NL " " << key.output << " = validationResult;" << NL " gl_Position = in_Position;" << NL "}"; return ret.str(); } static std::string generateComputeShader(const ShaderKey&, const std::vector& uniforms, const std::vector& subroutineUniforms, const std::string& additionalDef, const glu::ContextType type) { std::ostringstream ret; streamShaderHeader(ret, type); ret << NL "layout (local_size_x = 1, local_size_y = 1) in;" << NL "layout (std430, binding = 1) buffer ResultBuffer {" << NL " vec4 cs_ValidationResult;" << NL "};" << NL; streamUniformDefinitions(uniforms, GL_COMPUTE_SHADER, ret); ret << NL; streamSubroutineDefinitions(subroutineUniforms, GL_COMPUTE_SHADER, ret); ret << NL << additionalDef << NL "void main() {" << NL " vec4 validationResult = vec4(0.0, 1.0, 0.0, 1.0);" << NL; streamUniformValidators(ret, uniforms, GL_COMPUTE_SHADER, "validationResult"); ret << NL; streamSubroutineValidators(ret, subroutineUniforms, GL_COMPUTE_SHADER, "validationResult"); ret << NL " cs_ValidationResult = validationResult;" << NL "}"; return ret.str(); } public: static std::string generateShader(const ShaderKey& key, const std::vector& uniforms, const std::vector& subroutineUniforms, const std::string& additionalDef, const glu::ContextType type) { switch (key.stage) { case GL_VERTEX_SHADER: return generateVertexShader(key, uniforms, subroutineUniforms, additionalDef, type); case GL_FRAGMENT_SHADER: return generateFragmentShader(key, uniforms, subroutineUniforms, additionalDef, type); case GL_COMPUTE_SHADER: return generateComputeShader(key, uniforms, subroutineUniforms, additionalDef, type); default: assert(0); return ""; } } }; class ExplicitUniformLocationCaseBase : public glcts::SubcaseBase { virtual std::string Title() { return ""; } virtual std::string Purpose() { return ""; } virtual std::string Method() { return ""; } virtual std::string PassCriteria() { return ""; } int getWindowWidth() { return m_context.getRenderContext().getRenderTarget().getWidth(); } int getWindowHeight() { return m_context.getRenderContext().getRenderTarget().getHeight(); } std::map CreateShaders(const std::vector >& programConfigs, const std::vector& uniforms, const std::vector& subroutineUniforms, const std::string& additionalDef) { std::map ret; //create shaders for (size_t config = 0; config < programConfigs.size(); config++) { for (size_t target = 0; target < programConfigs[config].size(); target++) { if (ret.find(programConfigs[config][target]) == ret.end()) { GLuint shader = glCreateShader(programConfigs[config][target].stage); std::string source = ShaderSourceFactory::generateShader(programConfigs[config][target], uniforms, subroutineUniforms, additionalDef, m_context.getRenderContext().getType()); const char* cSource[] = { source.c_str() }; glShaderSource(shader, 1, cSource, NULL); ret[programConfigs[config][target]] = shader; } } } //compile shaders for (std::map::iterator i = ret.begin(); i != ret.end(); i++) { glCompileShader(i->second); } return ret; } long CreatePrograms(std::vector& programs, const std::vector& uniforms, const std::vector& subroutineUniforms, const std::string& additionalDef, bool negativeCompile, bool negativeLink) { long ret = NO_ERROR; std::vector > programConfigs; { std::vector vsh_fsh(2); vsh_fsh[0] = ShaderKey(GL_VERTEX_SHADER, "", "vs_ValidationResult"); vsh_fsh[1] = ShaderKey(GL_FRAGMENT_SHADER, "vs_ValidationResult", ""); programConfigs.push_back(vsh_fsh); } { std::vector csh(1); csh[0] = ShaderKey(GL_COMPUTE_SHADER, "", ""); programConfigs.push_back(csh); } std::map shaders = CreateShaders(programConfigs, uniforms, subroutineUniforms, additionalDef); //query compilation results for (std::map::iterator it = shaders.begin(); it != shaders.end(); it++) { GLint status; glGetShaderiv(it->second, GL_COMPILE_STATUS, &status); GLchar infoLog[1000], source[4000]; glGetShaderSource(it->second, 4000, NULL, source); glGetShaderInfoLog(it->second, 1000, NULL, infoLog); Logger::Get()->writeKernelSource(source); Logger::Get()->writeCompileInfo("shader", "", status == GL_TRUE, infoLog); if (!negativeLink) { if (!negativeCompile) { if (status != GL_TRUE) { Logger() << "Shader compilation failed"; ret |= ERROR; } } else { if (status) { Logger() << "Negative compilation case failed: shader shoult not compile, but " "GL_COMPILE_STATUS != 0"; ret |= ERROR; } } } } if (negativeCompile) { //delete shaders for (std::map::iterator it = shaders.begin(); it != shaders.end(); it++) { glDeleteShader(it->second); } return ret; } //assemble programs and link for (size_t config = 0; config < programConfigs.size(); config++) { CompiledProgram program; program.name = glCreateProgram(); for (size_t target = 0; target < programConfigs[config].size(); target++) { GLuint shader = shaders.find(programConfigs[config][target])->second; glAttachShader(program.name, shader); program.stages.push_back(programConfigs[config][target].stage); } programs.push_back(program); glLinkProgram(programs[config].name); } for (size_t config = 0; config < programConfigs.size(); config++) { glLinkProgram(programs[config].name); } //delete shaders for (std::map::iterator it = shaders.begin(); it != shaders.end(); it++) { glDeleteShader(it->second); } //query link status: for (size_t config = 0; config < programConfigs.size(); config++) { GLint status; glGetProgramiv(programs[config].name, GL_LINK_STATUS, &status); GLchar infoLog[1000]; glGetProgramInfoLog(programs[config].name, 1000, NULL, infoLog); Logger::Get()->writeCompileInfo("program", "", status == GL_TRUE, infoLog); if (!negativeLink) { if (status != GL_TRUE) { Logger() << "Shader link failed"; ret |= ERROR; } } else { if (status) { Logger() << "Negative link case failed: program should not link, but GL_LINK_STATUS != 0"; ret |= ERROR; } } } return ret; } long DeletePrograms(std::vector& programs) { for (size_t i = 0; i < programs.size(); i++) { glDeleteProgram(programs[i].name); } programs.resize(0); return NO_ERROR; } void setUniform(const Uniform& uniform, const CompiledProgram& program) { bool used = false; for (size_t i = 0; i < program.stages.size(); i++) { used |= uniform.declOccurence.occurs(program.stages[i]) && uniform.usageOccurence.occurs(program.stages[i]); } if (!used) return; if (uniform.type.isStruct()) { for (size_t j = 0; j < uniform.childUniforms.size(); j++) { setUniform(uniform.childUniforms[j], program); } } else { GLint loc; if (uniform.location.isImplicit(program.stages)) { std::ostringstream name; name << uniform.getName(); uniform.type.streamArrayStr(name, 0); loc = glGetUniformLocation(program.name, name.str().c_str()); } else { loc = uniform.location.val; } for (int arrayElem = 0; arrayElem < uniform.type.arraySize; arrayElem++) { switch (uniform.type.enumType) { case GL_FLOAT: glUniform1f(loc, *(GLfloat*)uniform.value.getPtr(arrayElem)); break; case GL_FLOAT_VEC2: glUniform2fv(loc, 1, (GLfloat*)uniform.value.getPtr(arrayElem)); break; case GL_FLOAT_VEC3: glUniform3fv(loc, 1, (GLfloat*)uniform.value.getPtr(arrayElem)); break; case GL_FLOAT_VEC4: glUniform4fv(loc, 1, (GLfloat*)uniform.value.getPtr(arrayElem)); break; case GL_FLOAT_MAT2: glUniformMatrix2fv(loc, 1, GL_FALSE, (GLfloat*)uniform.value.getPtr(arrayElem)); break; case GL_FLOAT_MAT3: glUniformMatrix3fv(loc, 1, GL_FALSE, (GLfloat*)uniform.value.getPtr(arrayElem)); break; case GL_FLOAT_MAT4: glUniformMatrix4fv(loc, 1, GL_FALSE, (GLfloat*)uniform.value.getPtr(arrayElem)); break; case GL_FLOAT_MAT2x3: glUniformMatrix2x3fv(loc, 1, GL_FALSE, (GLfloat*)uniform.value.getPtr(arrayElem)); break; case GL_FLOAT_MAT4x3: glUniformMatrix4x3fv(loc, 1, GL_FALSE, (GLfloat*)uniform.value.getPtr(arrayElem)); break; case GL_FLOAT_MAT2x4: glUniformMatrix2x4fv(loc, 1, GL_FALSE, (GLfloat*)uniform.value.getPtr(arrayElem)); break; case GL_FLOAT_MAT3x4: glUniformMatrix3x4fv(loc, 1, GL_FALSE, (GLfloat*)uniform.value.getPtr(arrayElem)); break; case GL_FLOAT_MAT3x2: glUniformMatrix3x2fv(loc, 1, GL_FALSE, (GLfloat*)uniform.value.getPtr(arrayElem)); break; case GL_FLOAT_MAT4x2: glUniformMatrix4x2fv(loc, 1, GL_FALSE, (GLfloat*)uniform.value.getPtr(arrayElem)); break; case GL_INT: case GL_SAMPLER_2D: glUniform1i(loc, *(GLint*)uniform.value.getPtr(arrayElem)); break; case GL_INT_VEC2: glUniform2iv(loc, 1, (GLint*)uniform.value.getPtr(arrayElem)); break; case GL_INT_VEC3: glUniform3iv(loc, 1, (GLint*)uniform.value.getPtr(arrayElem)); break; case GL_INT_VEC4: glUniform4iv(loc, 1, (GLint*)uniform.value.getPtr(arrayElem)); break; case GL_UNSIGNED_INT: glUniform1ui(loc, *(GLuint*)uniform.value.getPtr(arrayElem)); break; default: assert(0); } loc++; } } } void setSubroutineUniform(const SubroutineUniform& subroutineUniform, const CompiledProgram& program, GLenum stage, std::vector& indicesOut) { bool used = subroutineUniform.defOccurence.occurs(stage) && subroutineUniform.used; if (used) { for (int arrayElem = 0; arrayElem < subroutineUniform.arraySize; arrayElem++) { GLint loc = -1; if (subroutineUniform.location.isImplicit(program.stages)) { std::ostringstream name; name << subroutineUniform.getName(); subroutineUniform.streamArrayStr(name, arrayElem); loc = glGetSubroutineUniformLocation(program.name, stage, name.str().c_str()); } else { loc = subroutineUniform.location.val + arrayElem; } if (loc >= 0) { const SubroutineFunction& selectedFunction = subroutineUniform.getSelectedFunction(arrayElem); int index = -1; if (selectedFunction.index.isImplicit(std::vector(1, stage))) { index = glGetSubroutineIndex(program.name, stage, selectedFunction.getName().c_str()); } else { index = selectedFunction.index.val; } if (loc < (int)indicesOut.size()) { indicesOut[loc] = index; } else { assert(0); } } else { assert(0); } } } } long runExecuteProgram(const CompiledProgram& program, const std::vector& uniforms, const std::vector& subroutineUniforms) { long ret = NO_ERROR; glUseProgram(program.name); for (size_t i = 0; i < uniforms.size(); i++) { setUniform(uniforms[i], program); } for (size_t stage = 0; stage < program.stages.size() && subroutineUniforms.size(); stage++) { glw::GLint numactive; glGetProgramStageiv(program.name, program.stages[stage], GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS, &numactive); if (numactive) { std::vector indices(numactive, 0); for (size_t i = 0; i < subroutineUniforms.size(); i++) { setSubroutineUniform(subroutineUniforms[i], program, program.stages[stage], indices); } glUniformSubroutinesuiv(program.stages[stage], numactive, &indices[0]); } } if (program.stages[0] != GL_COMPUTE_SHADER) { glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); std::vector pixels(getWindowWidth() * getWindowHeight() * 4); glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]); for (size_t i = 0; i < pixels.size(); i += 4) { if (pixels[i] != 0 || pixels[i + 1] != 255 || pixels[i + 2] != 0) { ret |= ERROR; Logger() << "Program " << program.name << ": Wrong color. Expected green, got (" << (int)pixels[i] << ", " << (int)pixels[i + 1] << ", " << (int)pixels[i + 2] << ", " << (int)pixels[i + 3] << ")."; break; } } Logger().Get()->writeImage("rendered image", "", QP_IMAGE_COMPRESSION_MODE_BEST, QP_IMAGE_FORMAT_RGBA8888, getWindowWidth(), getWindowHeight(), 0, &pixels[0]); } else { GLuint buffer; glGenBuffers(1, &buffer); glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer); glBufferData(GL_SHADER_STORAGE_BUFFER, 4 * sizeof(GLfloat), NULL, GL_DYNAMIC_READ); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, buffer); glDispatchCompute(1, 1, 1); glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); GLfloat* color = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 4 * sizeof(GLfloat), GL_MAP_READ_BIT)); if (color[0] != 0 || color[1] != 1.0 || color[2] != 0) { ret |= ERROR; Logger() << "Program " << program.name << ": Wrong color. Expected green, got (" << (int)color[0] << ", " << (int)color[1] << ", " << (int)color[2] << ", " << (int)color[3] << ")."; } glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); glDeleteBuffers(1, &buffer); } return ret; } long runQueryUniform(const CompiledProgram& program, const Uniform& uniform, std::set& usedLocations, GLint max) { long ret = NO_ERROR; /* glGetUniformLocation(program, name); Query passes if returned value is unique in current program, matches explicit location (if passed in GLSL code) and is less than value of GL_MAX_UNIFORM_LOCATIONS. glGetProgramResourceLocation(program, GL_UNIFIORM, name); Query passes if returned value matches value returned from glGetUniformLocation(). */ if (uniform.type.isStruct()) { for (size_t i = 0; i < uniform.childUniforms.size(); i++) { ret |= runQueryUniform(program, uniform.childUniforms[i], usedLocations, max); } } else { for (int arrayElem = 0; arrayElem < uniform.type.arraySize; arrayElem++) { /* Location that is taken by this uniform (even if not used).*/ GLint reservedLocation = -1; if (!uniform.location.isImplicit(program.stages)) { reservedLocation = uniform.location.val + arrayElem; } //optimization: for continuous arrays run queries at the beging and end only. bool runQueries = uniform.location.isImplicit(program.stages) || (arrayElem < 1000 || arrayElem > uniform.type.arraySize - 1000); if (runQueries) { std::ostringstream name; name << uniform.getName(); uniform.type.streamArrayStr(name, arrayElem); GLint returned = glGetUniformLocation(program.name, name.str().c_str()); GLint returnedPIQ = glGetProgramResourceLocation(program.name, GL_UNIFORM, name.str().c_str()); if (returned != returnedPIQ) { ret |= ERROR; Logger() << "Locations of uniform \"" << name.str() << "\" returned by glGetUniformLocation and differ glGetProgramResourceLocation differ: " << returned << " != " << returnedPIQ << "."; } bool used = false; for (size_t i = 0; i < program.stages.size(); i++) { used |= uniform.declOccurence.occurs(program.stages[i]) && uniform.usageOccurence.occurs(program.stages[i]); } if (!uniform.location.isImplicit(program.stages)) { //Validate uniform location against explicit value GLint expected = reservedLocation; if (!(expected == returned || (!used && returned == -1))) { ret |= ERROR; Logger() << "Unexpected uniform \"" << name.str() << "\" location: expected " << expected << ", got " << returned << "."; } } else { //Check if location > 0 if used; if (used) { if (returned < 0) { ret |= ERROR; Logger() << "Unexpected uniform \"" << name.str() << "\" location: expected positive value, got " << returned << "."; } else { reservedLocation = returned; } } } if (returned >= 0) { //check if location is less than max if (returned >= max) { ret |= ERROR; Logger() << "Uniform \"" << name.str() << "\" returned location (" << returned << ") is greater than implementation dependent limit (" << max << ")."; } } } //if (runQueries) //usedLocations is always checked (even if queries were not run. if (reservedLocation >= 0) { //check if location is unique if (usedLocations.find(reservedLocation) != usedLocations.end()) { ret |= ERROR; Logger() << "Uniform location (" << reservedLocation << ") is not unique."; } usedLocations.insert(reservedLocation); } } } return ret; } long runQueryUniformSubroutine(const CompiledProgram& program, GLenum stage, const SubroutineUniform& subroutineUniform, std::set& usedLocations, GLint max) { long ret = NO_ERROR; /* glGetSubroutineUniformLocation(program, shaderType, name) Query passes if returned value is unique in current program stage, matches explicit location (if passed in GLSL code) and is less than value of GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS. glGetProgramResourceLocation(program, GL_(VERTEX|FRAGMENT|COMPUTE|... ..._SUBROUTINE_UNIFORM, name) Query passes if returned value matches value returned from glGetUniformLocation(). */ for (int arrayElem = 0; arrayElem < subroutineUniform.arraySize; arrayElem++) { std::ostringstream name; name << subroutineUniform.getName(); subroutineUniform.streamArrayStr(name, arrayElem); GLint returned = glGetSubroutineUniformLocation(program.name, stage, name.str().c_str()); glw::GLenum piqStage = 0; switch (stage) { case GL_VERTEX_SHADER: piqStage = GL_VERTEX_SUBROUTINE_UNIFORM; break; case GL_FRAGMENT_SHADER: piqStage = GL_FRAGMENT_SUBROUTINE_UNIFORM; break; case GL_COMPUTE_SHADER: piqStage = GL_COMPUTE_SUBROUTINE_UNIFORM; break; default: assert(0); } GLint returnedPIQ = glGetProgramResourceLocation(program.name, piqStage, name.str().c_str()); if (returned != returnedPIQ) { ret |= ERROR; Logger() << "Locations of subrutine uniform \"" << name.str() << "\" returned by glGetUniformLocation and differ glGetProgramResourceLocation differ: " << returned << " != " << returnedPIQ << "."; } bool used = subroutineUniform.defOccurence.occurs(stage) && subroutineUniform.used; GLint reservedLocation = -1; if (!subroutineUniform.location.isImplicit(std::vector(1, stage))) { //Validate uniform location against explicit value GLint expected = subroutineUniform.location.val + arrayElem; if (!(expected == returned || (!used && returned == -1))) { ret |= ERROR; Logger() << "Unexpected subroutine uniform \"" << name.str() << "\" location: expected " << expected << ", got " << returned << "."; } reservedLocation = expected; } else { //Check if location > 0 if used; if (used) { if (returned < 0) { ret |= ERROR; Logger() << "Unexpected subroutine uniform \"" << name.str() << "\" location: expected positive value, got " << returned << "."; } else { reservedLocation = returned; } } } if (reservedLocation >= 0) { //check if location is unique if (usedLocations.find(reservedLocation) != usedLocations.end()) { ret |= ERROR; Logger() << "Subroutine uniform \"" << name.str() << "\" location (" << reservedLocation << ") is not unique."; } usedLocations.insert(reservedLocation); } if (returned >= 0) { //check if location is less than max if (returned >= max) { ret |= ERROR; Logger() << "Subroutine uniform \"" << name.str() << "\" returned location (" << returned << ") is greater than implementation dependent limit (" << max << ")."; } } } return ret; } long runQueryUniformSubroutineFunction(const CompiledProgram& program, GLenum stage, const SubroutineFunction& subroutineFunction, std::set& usedIndices, GLint max, bool used) { long ret = NO_ERROR; /* glGetSubroutineIndex(program, shaderType, name) Query passes if returned value is unique in current program stage, matches explicit index (if passed in GLSL code) and is less than value of GL_MAX_SUBROUTINES. glGetProgramResourceIndex(program, GL_(VERTEX|FRAGMENT|COMPUTE|... ..._SUBROUTINE, name) Query passes if returned value matches value returned from glGetSubroutineIndex(). */ std::string name = subroutineFunction.getName(); GLint returned = glGetSubroutineIndex(program.name, stage, name.c_str()); glw::GLenum piqStage = 0; switch (stage) { case GL_VERTEX_SHADER: piqStage = GL_VERTEX_SUBROUTINE; break; case GL_FRAGMENT_SHADER: piqStage = GL_FRAGMENT_SUBROUTINE; break; case GL_COMPUTE_SHADER: piqStage = GL_COMPUTE_SUBROUTINE; break; default: assert(0); } GLint returnedPIQ = glGetProgramResourceIndex(program.name, piqStage, name.c_str()); if (returned != returnedPIQ) { ret |= ERROR; Logger() << "Indices of subroutine function \"" << name << "\" returned by glGetSubroutineIndex and differ glGetProgramResourceIndex differ: " << returned << " != " << returnedPIQ << "."; } GLint reservedIndex = -1; if (!subroutineFunction.index.isImplicit(std::vector(1, stage))) { //Validate uniform location against explicit value GLint expected = subroutineFunction.index.val; if (!(expected == returned || (!used && returned == -1))) { ret |= ERROR; Logger() << "Unexpected subroutine function \"" << name << "\" index: expected " << expected << ", got " << returned << "."; } reservedIndex = expected; } else { //Check if location > 0 if used; if (used) { if (returned < 0) { ret |= ERROR; Logger() << "Unexpected subroutine function \"" << name << "\" index: expected positive value, got " << returned << "."; } else { reservedIndex = returned; } } } if (reservedIndex >= 0) { //check if location is unique if (usedIndices.find(reservedIndex) != usedIndices.end()) { ret |= ERROR; Logger() << "Subroutine function \"" << name << "\" index (" << reservedIndex << ") is not unique."; } usedIndices.insert(reservedIndex); } if (returned >= 0) { //check if location is less than max if (returned >= max) { ret |= ERROR; Logger() << "Subroutine function \"" << name << "\" returned index (" << returned << ") is greater than implementation dependent limit (" << max << ")."; } } return ret; } long runQueryProgram(const CompiledProgram& program, const std::vector& uniforms, const std::vector& subroutineUniforms) { long ret = NO_ERROR; { std::set usedLocations; GLint max; glGetIntegerv(GL_MAX_UNIFORM_LOCATIONS, &max); for (size_t i = 0; i < uniforms.size(); i++) { ret |= runQueryUniform(program, uniforms[i], usedLocations, max); } } if (subroutineUniforms.size()) { GLint maxLocation, maxIndex; glGetIntegerv(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, &maxLocation); glGetIntegerv(GL_MAX_SUBROUTINES, &maxIndex); for (size_t stage = 0; stage < program.stages.size(); stage++) { std::set usedLocations; std::set usedIndices; for (size_t i = 0; i < subroutineUniforms.size(); i++) { ret |= runQueryUniformSubroutine(program, program.stages[stage], subroutineUniforms[i], usedLocations, maxLocation); for (size_t fn = 0; fn < subroutineUniforms[i].functions.fn.size(); fn++) { ret |= runQueryUniformSubroutineFunction( program, program.stages[stage], subroutineUniforms[i].functions.fn[fn], usedIndices, maxIndex, subroutineUniforms[i].defOccurence.occurs(program.stages[stage]) && subroutineUniforms[i].used); } } } } return ret; } protected: UniformValueGenerator uniformValueGenerator; UniformStructCounter uniformStructCounter; long doRun(std::vector& subroutineUniforms) { assert(subroutineUniforms.size()); std::vector noUniforms; return doRun(noUniforms, subroutineUniforms); } long doRun(std::vector& uniforms) { assert(uniforms.size()); std::vector noSubroutineUniforms; return doRun(uniforms, noSubroutineUniforms); } long doRunNegativeCompile(const std::string additionalDef) { std::vector noUniforms; std::vector noSubroutineUniforms; return doRun(noUniforms, noSubroutineUniforms, additionalDef, true); } long doRunNegativeLink(std::vector& uniforms) { std::vector noSubroutineUniforms; return doRun(uniforms, noSubroutineUniforms, "", false, true); } long doRunNegativeLink(std::vector& subroutineUniforms) { std::vector noUniforms; return doRun(noUniforms, subroutineUniforms, "", false, true); } long doRun(std::vector& uniforms, std::vector& subroutineUniforms, std::string additionalDef = "", bool negativeCompile = false, bool negativeLink = false) { long ret = NO_ERROR; std::string parentUniformName = ""; for (size_t i = 0; i < uniforms.size(); i++) { std::ostringstream name; name << "u" << i; uniforms[i].setName(parentUniformName, name.str()); } int subroutineTypeCounter = 0; int subroutineFunctionCounter = 0; for (size_t i = 0; i < subroutineUniforms.size(); i++) { std::ostringstream name; name << "u" << i + uniforms.size(); subroutineUniforms[i].setName(name.str()); if (!subroutineUniforms[i].functions.getTypeName().size()) { subroutineUniforms[i].functions.setTypeName(subroutineTypeCounter++); for (size_t fn = 0; fn < subroutineUniforms[i].functions.fn.size(); fn++) { subroutineUniforms[i].functions.fn[fn].setName(subroutineFunctionCounter++); } } } GLfloat coords[] = { 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, }; GLuint vbo, vao; glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(coords), coords, GL_STATIC_DRAW); glGenVertexArrays(1, &vao); glBindVertexArray(vao); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(0); std::vector programs; ret |= CreatePrograms(programs, uniforms, subroutineUniforms, additionalDef, negativeCompile, negativeLink); for (size_t i = 0; i < programs.size() && ret == NO_ERROR && !negativeCompile && !negativeLink; i++) { ret |= runExecuteProgram(programs[i], uniforms, subroutineUniforms); ret |= runQueryProgram(programs[i], uniforms, subroutineUniforms); } glUseProgram(0); DeletePrograms(programs); glDeleteBuffers(1, &vbo); glDeleteVertexArrays(1, &vao); return ret; } }; class UniformLoc : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout (location = 2) uniform vec4 u0; std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_VEC4, Loc::C(2), DefOccurence::FSH_OR_CSH)); return doRun(uniforms); } }; class UniformLocNonDec : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout (location = 0x0a) uniform vec4 u0; //layout (location = 010) uniform vec4 u1; std::vector uniforms; uniforms.push_back( Uniform(uniformValueGenerator, GL_FLOAT_VEC4, Loc::C(0x0a, Loc::Hex), DefOccurence::FSH_OR_CSH)); uniforms.push_back( Uniform(uniformValueGenerator, GL_FLOAT_VEC4, Loc::C(010, Loc::Oct), DefOccurence::FSH_OR_CSH)); return doRun(uniforms); } }; class UniformLocMultipleStages : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout (location = 2) uniform vec4 u0; std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_VEC4, Loc::C(2))); return doRun(uniforms); } }; class UniformLocMultipleUniforms : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout (location = 2) uniform vec4 u0; //layout (location = 3) uniform vec4 u1; //layout (location = 5) uniform vec4 u2; std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_VEC4, Loc::C(2))); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_VEC4, Loc::C(3))); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_VEC4, Loc::C(5))); return doRun(uniforms); } }; class UniformLocTypesMix : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout (location = 2) uniform float u0; //layout (location = 3) uniform vec3 u1; //layout (location = 0) uniform uint u2; //layout (location = 1) uniform ivec3 u3; //layout (location = 4) uniform mat2 u4; //layout (location = 7) uniform mat2 u5; //layout (location = 5) uniform mat2 u6; //layout (location = 6) uniform mat3 u7; std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT, Loc::C(2))); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_VEC3, Loc::C(3))); uniforms.push_back(Uniform(uniformValueGenerator, GL_UNSIGNED_INT, Loc::C(0))); uniforms.push_back(Uniform(uniformValueGenerator, GL_INT_VEC3, Loc::C(1))); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_MAT2, Loc::C(4))); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_MAT2, Loc::C(7))); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_MAT2, Loc::C(5))); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_MAT3, Loc::C(6))); return doRun(uniforms); } }; class UniformLocTypesMat : public ExplicitUniformLocationCaseBase { virtual long Run() { std::vector uniforms; //layout (location = 1) uniform mat2x3 u0; //layout (location = 2) uniform mat3x2 u1; //layout (location = 0) uniform mat2 u2; //layout (location = 3) uniform imat3x4 u3; uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_MAT2x3, Loc::C(1))); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_MAT3x2, Loc::C(2))); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_MAT2, Loc::C(0))); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_MAT4x3, Loc::C(3))); return doRun(uniforms); } }; class UniformLocTypesSamplers : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout (location = 1) uniform sampler2D s0[3]; //layout (location = 13) uniform sampler2D s1; std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_SAMPLER_2D, 3), Loc::C(1))); uniforms.push_back(Uniform(uniformValueGenerator, GL_SAMPLER_2D, Loc::C(13))); std::vector texUnits; std::vector > colors; for (size_t i = 0; i < uniforms.size(); i++) { for (int elem = 0; elem < uniforms[i].type.arraySize; elem++) { texUnits.push_back(uniforms[i].value.iValues[elem]); std::vector color(4); color[0] = static_cast(255. * uniforms[i].value.fValues[4 * elem + 0] + 0.5); color[1] = static_cast(255. * uniforms[i].value.fValues[4 * elem + 1] + 0.5); color[2] = static_cast(255. * uniforms[i].value.fValues[4 * elem + 2] + 0.5); color[3] = static_cast(255. * uniforms[i].value.fValues[4 * elem + 3] + 0.5); colors.push_back(color); } } std::vector textures(texUnits.size()); glGenTextures((GLsizei)(textures.size()), &textures[0]); for (size_t i = 0; i < textures.size(); i++) { glActiveTexture(GL_TEXTURE0 + texUnits[i]); glBindTexture(GL_TEXTURE_2D, textures[i]); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &colors[i][0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } glActiveTexture(GL_TEXTURE0); long ret = doRun(uniforms); glDeleteTextures((GLsizei)(textures.size()), &textures[0]); return ret; } }; class UniformLocTypesStructs : public ExplicitUniformLocationCaseBase { virtual long Run() { /** * This test case uses following uniform declarations: * * struct S { * vec4 u0; * float u1[2]; * mat2 u2; * }; * layout (location = 1) uniform S s0[3]; * layout (location = 13) uniform S s1; */ std::vector members; members.push_back(GL_FLOAT_VEC4); members.push_back(UniformType(GL_FLOAT, 2)); members.push_back(GL_FLOAT_MAT2); std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, UniformType(uniformStructCounter, members, 3), Loc::C(1))); uniforms.push_back(Uniform(uniformValueGenerator, UniformType(uniformStructCounter, members), Loc::C(13))); return doRun(uniforms); } }; class UniformLocArraysNonSpaced : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout (location = 2) uniform float[3] u0; //layout (location = 5) uniform vec3[2] u1; //layout (location = 7) uniform int[3] u2; //layout (location = 10) uniform ivec4 u3; std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_FLOAT, 3), Loc::C(2))); uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_FLOAT_VEC3, 2), Loc::C(5))); uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_INT, 3), Loc::C(7))); uniforms.push_back(Uniform(uniformValueGenerator, GL_INT_VEC4, Loc::C(10))); return doRun(uniforms); } }; class UniformLocArraySpaced : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout (location = 2) uniform float u0; //layout (location = 5) uniform vec3[2] u1; //layout (location = 8) uniform int[3] u2; //layout (location = 12) uniform ivec4[1] u3; std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT, Loc::C(2))); uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_FLOAT_VEC3, 2), Loc::C(5))); uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_INT, 3), Loc::C(8))); uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_INT_VEC4, 1), Loc::C(12))); return doRun(uniforms); } }; class UniformLocArrayofArrays : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout (location = 2) uniform float[2][3] u0; //layout (location = 8) uniform vec3[2][2] u1; //layout (location = 12) uniform float u2; std::vector uniforms; { std::vector arraySizesSegmented(2); arraySizesSegmented[0] = 2; arraySizesSegmented[1] = 3; uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_FLOAT, arraySizesSegmented), Loc::C(2))); } { std::vector arraySizesSegmented(2); arraySizesSegmented[0] = arraySizesSegmented[1] = 2; uniforms.push_back( Uniform(uniformValueGenerator, UniformType(GL_FLOAT_VEC3, arraySizesSegmented), Loc::C(8))); } uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT, Loc::C(12))); return doRun(uniforms); } }; class UniformLocMixWithImplicit : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout (location = 0) uniform float u0; //layout (location = 2) uniform vec3 u1; //layout (location = 3) uniform int u2; //uniform float u0; //uniform vec3 u1; //uniform int u2; std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT, Loc::C(0, DefOccurence::FSH_OR_CSH))); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_VEC3, Loc::C(2, DefOccurence::FSH_OR_CSH))); uniforms.push_back(Uniform(uniformValueGenerator, GL_INT, Loc::C(3, DefOccurence::FSH_OR_CSH))); return doRun(uniforms); } }; class UniformLocMixWithImplicit2 : public ExplicitUniformLocationCaseBase { virtual long Run() { //uniform float[3] u0; //layout (location = 3) uniform vec3[2] u1; //uniform int[3] u2; //layout (location = 8) uniform ivec4 u3; std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_FLOAT, 3), Loc::Implicit())); uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_FLOAT_VEC3, 2), Loc::C(3))); uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_INT, 3), Loc::Implicit())); uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_INT_VEC4, 2), Loc::C(8))); return doRun(uniforms); } }; class UniformLocMixWithImplicit3 : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout (location = 0) uniform float u0; //unused //layout (location = 2) uniform vec3 u1; //unused //layout (location = 3) uniform int u2; //unused //uniform float u3; //uniform vec3 u4; //uniform int u5; std::vector uniforms; uniforms.push_back( Uniform(uniformValueGenerator, GL_FLOAT, Loc::C(0), DefOccurence::ALL_SH, DefOccurence::NONE_SH)); uniforms.push_back( Uniform(uniformValueGenerator, GL_FLOAT_VEC3, Loc::C(2), DefOccurence::ALL_SH, DefOccurence::NONE_SH)); uniforms.push_back( Uniform(uniformValueGenerator, GL_INT, Loc::C(3), DefOccurence::ALL_SH, DefOccurence::NONE_SH)); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT, Loc::Implicit())); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_VEC3, Loc::Implicit())); uniforms.push_back(Uniform(uniformValueGenerator, GL_INT, Loc::Implicit())); return doRun(uniforms); } }; class UniformLocMixWithImplicitMax : public ExplicitUniformLocationCaseBase { virtual long Run() { long ret = NO_ERROR; GLint max; glGetIntegerv(GL_MAX_UNIFORM_LOCATIONS, &max); const int implicitCount = 1; int tests[3] = { 0, 3, max - implicitCount }; for (int test = 0; test < 3; test++) { std::vector uniforms; //for performance reasons fill-up all avaliable locations with an unused arrays. if (tests[test] > 0) { //[0..test - 1] uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_FLOAT, tests[test]), Loc::C(0), DefOccurence::ALL_SH, DefOccurence::NONE_SH)); assert(uniforms[uniforms.size() - 1].location.val + uniforms[uniforms.size() - 1].type.arraySize == tests[test]); } if (tests[test] < max - implicitCount) { //[test + 1..max] uniforms.push_back( Uniform(uniformValueGenerator, UniformType(GL_FLOAT, max - implicitCount - tests[test]), Loc::C(tests[test] + implicitCount), DefOccurence::ALL_SH, DefOccurence::NONE_SH)); assert(uniforms[uniforms.size() - 1].location.val + uniforms[uniforms.size() - 1].type.arraySize == max); } uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT, Loc::Implicit())); ret |= doRun(uniforms); } return ret; } }; class UniformLocMixWithImplicitMaxArray : public ExplicitUniformLocationCaseBase { virtual long Run() { long ret = NO_ERROR; GLint max; glGetIntegerv(GL_MAX_UNIFORM_LOCATIONS, &max); const int implicitCount = 3; int tests[3] = { 0, 3, max - 4 }; for (int test = 0; test < 3; test++) { std::vector uniforms; //for performance reasons fill-up all avaliable locations with an unused arrays. if (tests[test] > 0) { //[0..test - 1] uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_FLOAT, tests[test]), Loc::C(0), DefOccurence::ALL_SH, DefOccurence::NONE_SH)); assert(uniforms[uniforms.size() - 1].location.val + uniforms[uniforms.size() - 1].type.arraySize == tests[test]); } if (tests[test] < max - implicitCount) { //[test + 3 ..max] uniforms.push_back( Uniform(uniformValueGenerator, UniformType(GL_FLOAT, max - implicitCount - tests[test]), Loc::C(tests[test] + implicitCount), DefOccurence::ALL_SH, DefOccurence::NONE_SH)); assert(uniforms[uniforms.size() - 1].location.val + uniforms[uniforms.size() - 1].type.arraySize == max); } uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_FLOAT, implicitCount), Loc::Implicit())); ret |= doRun(uniforms); } return ret; } }; class UniformLocImplicitInSomeStages : public ExplicitUniformLocationCaseBase { virtual long Run() { //One shader: uniform float u0; //Another shader: layout (location = 3) uniform float u0; std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT, Loc::C(3, DefOccurence::FSH_OR_CSH))); return doRun(uniforms); } }; class UniformLocImplicitInSomeStages2 : public ExplicitUniformLocationCaseBase { virtual long Run() { //One shader: uniform float u0; //Another shader: layout (location = 3) uniform float u0; //not used! std::vector uniforms; //location only in fsh, declaration in all shaders, usage in all shaders but fsh. uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT, Loc::C(3, DefOccurence::FSH_OR_CSH), DefOccurence::ALL_SH, DefOccurence::ALL_BUT_FSH)); return doRun(uniforms); } }; class UniformLocImplicitInSomeStages3 : public ExplicitUniformLocationCaseBase { virtual long Run() { std::vector uniforms; //location only in fsh, declaration in all shaders, usage in all shaders but fsh. uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT, Loc::C(3, DefOccurence::FSH_OR_CSH), DefOccurence::ALL_SH, DefOccurence::ALL_BUT_FSH)); //location in all but fsh, declaration in all shaders, usage in fsh. uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT, Loc::C(2, DefOccurence::ALL_BUT_FSH), DefOccurence::ALL_SH, DefOccurence::FSH_OR_CSH)); //location only in fsh, declaration in all shaders, usage in all shaders but fsh. uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_FLOAT, 3), Loc::C(7, DefOccurence::FSH_OR_CSH), DefOccurence::ALL_SH, DefOccurence::ALL_BUT_FSH)); //location in all but fsh, declaration in all shaders, usage in fsh. uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_FLOAT, 3), Loc::C(4, DefOccurence::ALL_BUT_FSH), DefOccurence::ALL_SH, DefOccurence::FSH_OR_CSH)); //location only in vsh, declaration in all shaders, usage in all shaders but vsh. uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT, Loc::C(0, DefOccurence::VSH), DefOccurence::ALL_SH, DefOccurence::ALL_BUT_VSH)); //location only in vsh, declaration in all shaders, usage in all shaders but vsh. uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT, Loc::C(1, DefOccurence::ALL_BUT_FSH), DefOccurence::ALL_SH, DefOccurence::ALL_BUT_VSH)); return doRun(uniforms); } }; class UniformLocNegativeCompileNonNumberLiteral : public ExplicitUniformLocationCaseBase { virtual long Run() { std::string def = "layout (location = x) uniform float u0;"; return doRunNegativeCompile(def); } }; class UniformLocNegativeCompileNonConstLoc : public ExplicitUniformLocationCaseBase { virtual long Run() { std::string def = NL "const int i = 1;" NL "layout (location = i) uniform float u0;"; return doRunNegativeCompile(def); } }; class UniformLocNegativeLinkLocationReused1 : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout (location = 2) uniform float u0; //layout (location = 2) uniform float u1; std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_VEC4, Loc::C(2), DefOccurence::FSH_OR_CSH)); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_VEC4, Loc::C(2), DefOccurence::FSH_OR_CSH)); return doRunNegativeLink(uniforms); } }; class UniformLocNegativeLinkLocationReused2 : public ExplicitUniformLocationCaseBase { virtual long Run() { ///layout (location = 2) uniform float u0; //layout (location = 2) uniform float u1; std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_VEC4, Loc::C(2), DefOccurence::FSH_OR_CSH)); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_VEC4, Loc::C(2), DefOccurence::ALL_BUT_FSH)); return doRunNegativeLink(uniforms); } }; class UniformLocNegativeLinkMaxLocation : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout (location = X) uniform float u0; //Where X is substituted with value of GL_MAX_UNIFORM_LOCATIONS. GLint max; glGetIntegerv(GL_MAX_UNIFORM_LOCATIONS, &max); std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT_VEC4, Loc::C(max), DefOccurence::FSH_OR_CSH)); return doRunNegativeLink(uniforms); } }; class UniformLocNegativeLinkMaxMaxNumOfLocation : public ExplicitUniformLocationCaseBase { virtual long Run() { GLint max; glGetIntegerv(GL_MAX_UNIFORM_LOCATIONS, &max); std::vector uniforms; uniforms.push_back(Uniform(uniformValueGenerator, UniformType(GL_FLOAT, max), Loc::C(0), DefOccurence::FSH_OR_CSH, DefOccurence::NONE_SH)); uniforms.push_back(Uniform(uniformValueGenerator, GL_FLOAT, Loc::Implicit(), DefOccurence::ALL_BUT_FSH)); return doRunNegativeLink(uniforms); } }; class SubRoutineLoc : public ExplicitUniformLocationCaseBase { virtual long Run() { //one shader: //subroutine vec4 st0(float param); //subroutine(st0) vec4 sf0(float param) { .... }; //subroutine(st0) vec4 sf1(float param) { .... }; SubroutineFunctionSet functions_st0(uniformValueGenerator, 2); std::vector subroutineUniforms; //layout(location = 2) subroutine uniform st0 u0; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(2), 0, DefOccurence::FSH_OR_CSH)); return doRun(subroutineUniforms); } }; class SubRoutineLocNonDecimal : public ExplicitUniformLocationCaseBase { virtual long Run() { //one shader: //subroutine vec4 st0(float param); //subroutine(st0) vec4 sf0(float param) { .... }; //subroutine(st0) vec4 sf1(float param) { .... }; SubroutineFunctionSet functions_st0(uniformValueGenerator, 2); std::vector subroutineUniforms; //layout(location = 0x0a) subroutine uniform st0 u0; subroutineUniforms.push_back(SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(0x0a, Loc::Hex), 0, DefOccurence::FSH_OR_CSH)); //layout(location = 010 ) subroutine uniform st0 u1; subroutineUniforms.push_back(SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(010, Loc::Oct), 0, DefOccurence::FSH_OR_CSH)); return doRun(subroutineUniforms); } }; class SubRoutineLocAllStages : public ExplicitUniformLocationCaseBase { virtual long Run() { //subroutine vec4 st0(float param); //subroutine(st0) vec4 sf0(float param) { .... }; //subroutine(st0) vec4 sf1(float param) { .... }; SubroutineFunctionSet functions_st0(uniformValueGenerator, 2); std::vector subroutineUniforms; //layout(location = 2) subroutine uniform st0 u0; subroutineUniforms.push_back(SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(2))); return doRun(subroutineUniforms); } }; class SubRoutineLocArrays : public ExplicitUniformLocationCaseBase { virtual long Run() { //subroutine vec4 st0(float param); //subroutine(st0) vec4 sf0(float param) { .... }; //subroutine(st0) vec4 sf1(float param) { .... }; SubroutineFunctionSet functions_st0(uniformValueGenerator, 2); std::vector subroutineUniforms; //layout(location = 1) subroutine uniform st0 u0[2]; subroutineUniforms.push_back(SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(1), 2)); return doRun(subroutineUniforms); } }; class SubRoutineLocArraysMix : public ExplicitUniformLocationCaseBase { virtual long Run() { //subroutine vec4 st0(float param); //subroutine(st0) vec4 sf0(float param) { .... }; //subroutine(st0) vec4 sf1(float param) { .... }; SubroutineFunctionSet functions_st0(uniformValueGenerator, 2); //subroutine vec4 st1(float param); //subroutine(st1) vec4 sf2(float param) { .... }; //subroutine(st1) vec4 sf3(float param) { .... }; SubroutineFunctionSet functions_st1(uniformValueGenerator, 2); std::vector subroutineUniforms; //layout(location = 1) subroutine uniform st0 u0[2]; subroutineUniforms.push_back(SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(1), 2)); ////layout(location = 3) subroutine uniform st0 u1[2][3]; std::vector arraySizesSegmented(2); arraySizesSegmented[0] = 2; arraySizesSegmented[1] = 3; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(3), arraySizesSegmented)); //layout(location = 9) subroutine uniform st1 u2; subroutineUniforms.push_back(SubroutineUniform(uniformValueGenerator, functions_st1, Loc::C(9))); return doRun(subroutineUniforms); } }; class SubRoutineLocMixWithImplicit : public ExplicitUniformLocationCaseBase { virtual long Run() { //subroutine vec4 st0(float param); //subroutine(st0) vec4 sf0(float param) { .... }; //subroutine(st0) vec4 sf1(float param) { .... }; SubroutineFunctionSet functions_st0(uniformValueGenerator, 2); std::vector subroutineUniforms; //subroutine uniform st0 u0; subroutineUniforms.push_back(SubroutineUniform(uniformValueGenerator, functions_st0, Loc::Implicit())); //layout(location = 1 ) subroutine uniform st0 u1; subroutineUniforms.push_back(SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(0))); //layout(location = 0 ) subroutine uniform st0 u2; subroutineUniforms.push_back(SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(1))); return doRun(subroutineUniforms); } }; class SubRoutineLocCompilationNonNumberLiteral : public ExplicitUniformLocationCaseBase { virtual long Run() { std::string def = NL "subroutine vec4 st0(float param);" NL "subroutine(st0) vec4 sf0(float param) { return param; }" NL "layout(location = x ) subroutine uniform st0 u0;"; return doRunNegativeCompile(def); } }; class SubRoutineLocCompilationNonConstLoc : public ExplicitUniformLocationCaseBase { virtual long Run() { std::string def = NL "const int i = 1;" NL "subroutine vec4 st0(float param);" NL "subroutine(st0) vec4 sf0(float param) { return param; }" NL "layout(location = i ) subroutine uniform st0 u0;"; return doRunNegativeCompile(def); } }; class SubRoutineLocLinkLocationReused1 : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout(location = 1) subroutine uniform st0 u0; //layout(location = 1) subroutine uniform st0 u0; SubroutineFunctionSet functions_st0(uniformValueGenerator, 2); std::vector subroutineUniforms; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(2), 0, DefOccurence::FSH_OR_CSH)); subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(2), 0, DefOccurence::FSH_OR_CSH)); return doRunNegativeLink(subroutineUniforms); } }; class SubRoutineLocLinkLocationMaxLocation : public ExplicitUniformLocationCaseBase { virtual long Run() { //layout(location = N) subroutine uniform st0 u0; //Where X is substituted with value of GL_MAX_UNIFORM_LOCATIONS. GLint max; glGetIntegerv(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, &max); SubroutineFunctionSet functions_st0(uniformValueGenerator, 2); std::vector subroutineUniforms; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(max), 0, DefOccurence::FSH_OR_CSH)); return doRunNegativeLink(subroutineUniforms); } }; class SubRoutineLocLinkMaxNumOfLocations : public ExplicitUniformLocationCaseBase { virtual long Run() { GLint max; glGetIntegerv(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, &max); SubroutineFunctionSet functions_st0(uniformValueGenerator, 2); std::vector subroutineUniforms; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(0), max, DefOccurence::FSH_OR_CSH, false)); subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::Implicit(), 0, DefOccurence::FSH_OR_CSH)); return doRunNegativeLink(subroutineUniforms); } }; class SubroutineIndex : public ExplicitUniformLocationCaseBase { virtual long Run() { //one shader: //subroutine vec4 st0(float param); SubroutineFunctionSet functions_st0(uniformValueGenerator); //layout(index = 1) subroutine(st0) vec4 sf0(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(1))); //layout(index = 2) subroutine(st0) vec4 sf1(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(2))); std::vector subroutineUniforms; //subroutine uniform st0 u0; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::Implicit(), 0, DefOccurence::FSH_OR_CSH)); return doRun(subroutineUniforms); } }; class SubroutineIndexNonDecimal : public ExplicitUniformLocationCaseBase { virtual long Run() { //one shader: //subroutine vec4 st0(float param); SubroutineFunctionSet functions_st0(uniformValueGenerator); //layout(index = 0x0a) subroutine(st0) vec4 sf0(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(0x0a, Index::Hex))); //layout(index = 010 ) subroutine(st0) vec4 sf1(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(010, Index::Oct))); std::vector subroutineUniforms; //subroutine uniform st0 u0; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::Implicit(), 0, DefOccurence::FSH_OR_CSH)); return doRun(subroutineUniforms); } }; class SubroutineIndexLoc : public ExplicitUniformLocationCaseBase { virtual long Run() { //one shader: //subroutine vec4 st0(float param); SubroutineFunctionSet functions_st0(uniformValueGenerator); //layout(index = 1) subroutine(st0) vec4 sf0(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(1))); //layout(index = 2) subroutine(st0) vec4 sf1(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(2))); std::vector subroutineUniforms; //layout(location = 3) subroutine uniform st0 u0; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(3), 0, DefOccurence::FSH_OR_CSH)); return doRun(subroutineUniforms); } }; class SubroutineIndexNonCont : public ExplicitUniformLocationCaseBase { virtual long Run() { //one shader: //subroutine vec4 st0(float param); SubroutineFunctionSet functions_st0(uniformValueGenerator); //layout(index = 0) subroutine(st0) vec4 sf0(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(0))); //layout(index = 2) subroutine(st0) vec4 sf1(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(2))); std::vector subroutineUniforms; //layout(location = 2) subroutine uniform st0 u0; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(2), 0, DefOccurence::FSH_OR_CSH)); return doRun(subroutineUniforms); } }; class SubroutineIndexMultUniforms : public ExplicitUniformLocationCaseBase { virtual long Run() { //one shader: //subroutine vec4 st0(float param); SubroutineFunctionSet functions_st0(uniformValueGenerator); //layout(index = 1) subroutine(st0) vec4 sf0(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(1))); //layout(index = 3) subroutine(st0) vec4 sf1(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(3))); //subroutine vec4 st1(float param); SubroutineFunctionSet functions_st1(uniformValueGenerator); //layout(index = 2) subroutine(st1) vec4 sf2(float param) { .... }; functions_st1.push_back(SubroutineFunction(uniformValueGenerator, Index::C(2))); //layout(index = 0) subroutine(st1) vec4 sf3(float param) { .... }; functions_st1.push_back(SubroutineFunction(uniformValueGenerator, Index::C(0))); std::vector subroutineUniforms; //layout(location = 1) subroutine uniform st0 u0; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(1), 0, DefOccurence::FSH_OR_CSH)); //layout(location = 9) subroutine uniform st1 u1; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st1, Loc::C(9), 0, DefOccurence::FSH_OR_CSH)); return doRun(subroutineUniforms); } }; class SubroutineIndexAllstages : public ExplicitUniformLocationCaseBase { virtual long Run() { //subroutine vec4 st0(float param); SubroutineFunctionSet functions_st0(uniformValueGenerator); //layout(index = 1) subroutine(st0) vec4 sf0(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(1))); //layout(index = 2) subroutine(st0) vec4 sf1(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(2))); std::vector subroutineUniforms; //subroutine uniform st0 u0; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::Implicit(), 0, DefOccurence::ALL_SH)); return doRun(subroutineUniforms); } }; class SubroutineIndexMixImplicit : public ExplicitUniformLocationCaseBase { virtual long Run() { //subroutine vec4 st0(float param); SubroutineFunctionSet functions_st0(uniformValueGenerator); //subroutine(st0) vec4 sf0(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::Implicit())); //layout(index = 1) subroutine(st0) vec4 sf1(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(1))); //layout(index = 0) subroutine(st0) vec4 sf1(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(0))); std::vector subroutineUniforms; //layout(location = 2) subroutine uniform st0 u0; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::C(2), 0, DefOccurence::FSH_OR_CSH)); return doRun(subroutineUniforms); } }; class SubroutineIndexNegativeCompilationNonNumberLiteral : public ExplicitUniformLocationCaseBase { virtual long Run() { std::string def = NL "subroutine vec4 st0(float param);" NL "layout(index = x) subroutine(st0) vec4 sf1(float param) { return param; };"; return doRunNegativeCompile(def); } }; class SubroutineIndexNegativeCompilationNonConstIndex : public ExplicitUniformLocationCaseBase { virtual long Run() { std::string def = NL "const int i = 1;" NL "layout(index = i) subroutine(st0) vec4 sf1(float param) { return param; };"; return doRunNegativeCompile(def); } }; class SubroutineIndexNegativeLinkIndexReused : public ExplicitUniformLocationCaseBase { virtual long Run() { //subroutine vec4 st0(float param); SubroutineFunctionSet functions_st0(uniformValueGenerator); //layout(index = 2) subroutine(st0) vec4 sf0(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(2))); //layout(index = 2) subroutine(st0) vec4 sf1(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(2))); std::vector subroutineUniforms; //subroutine uniform st0 u0; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::Implicit(), 0, DefOccurence::FSH_OR_CSH)); return doRunNegativeLink(subroutineUniforms); } }; class SubroutineIndexNegativeLinkMaxIndex : public ExplicitUniformLocationCaseBase { virtual long Run() { GLint max; glGetIntegerv(GL_MAX_SUBROUTINES, &max); //subroutine vec4 st0(float param); SubroutineFunctionSet functions_st0(uniformValueGenerator); //layout(index = N) subroutine(st0) vec4 sf0(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(max))); std::vector subroutineUniforms; //subroutine uniform st0 u0; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::Implicit(), 0, DefOccurence::FSH_OR_CSH)); return doRunNegativeLink(subroutineUniforms); } }; class SubroutineIndexNegativeLinkMaxNumOfIndices : public ExplicitUniformLocationCaseBase { virtual long Run() { //subroutine vec4 st0(float param); SubroutineFunctionSet functions_st0(uniformValueGenerator); glw::GLint max; glGetIntegerv(GL_MAX_SUBROUTINES, &max); for (int i = 0; i < max; i++) { //layout(index = N) subroutine(st0) vec4 sf0(float param) { .... }; functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::C(i))); } functions_st0.push_back(SubroutineFunction(uniformValueGenerator, Index::Implicit())); std::vector subroutineUniforms; //subroutine uniform st0 u0; subroutineUniforms.push_back( SubroutineUniform(uniformValueGenerator, functions_st0, Loc::Implicit(), 0, DefOccurence::FSH_OR_CSH)); return doRunNegativeLink(subroutineUniforms); } }; } ExplicitUniformLocationGLTests::ExplicitUniformLocationGLTests(glcts::Context& context) : TestCaseGroup(context, "explicit_uniform_location", "") { } ExplicitUniformLocationGLTests::~ExplicitUniformLocationGLTests(void) { } void ExplicitUniformLocationGLTests::init() { using namespace glcts; Logger::setOutput(m_context.getTestContext().getLog()); addChild(new TestSubcase(m_context, "uniform-loc", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-nondecimal", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-all-stages", TestSubcase::Create)); addChild( new TestSubcase(m_context, "uniform-loc-multiple-uniforms", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-types-mix", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-types-mat", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-types-structs", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-types-samplers", TestSubcase::Create)); addChild( new TestSubcase(m_context, "uniform-loc-arrays-nonspaced", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-arrays-spaced", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-arrays-of-arrays", TestSubcase::Create)); addChild( new TestSubcase(m_context, "uniform-loc-mix-with-implicit", TestSubcase::Create)); addChild( new TestSubcase(m_context, "uniform-loc-mix-with-implicit2", TestSubcase::Create)); addChild( new TestSubcase(m_context, "uniform-loc-mix-with-implicit3", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-mix-with-implicit-max", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-mix-with-implicit-max-array", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-implicit-in-some-stages", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-implicit-in-some-stages2", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-implicit-in-some-stages3", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-negative-compile-non-number-literal", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-negative-compile-nonconst-loc", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-negative-link-location-reused1", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-negative-link-location-reused2", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-negative-link-max-location", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-negative-link-max-num-of-locations", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-loc", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-loc-nondecimal", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-loc-all-stages", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-loc-arrays", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-loc-arrays-mix", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-loc-mix-with-implicit", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-loc-negative-compilation-non-number-literal", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-loc-negative-compilation-nonconst-loc", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-loc-negative-link-location-reused1", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-loc-negative-link-location-max-location", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-loc-negative-link-max-num-of-locations", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-index", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-index-nondecimal", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-index-loc", TestSubcase::Create)); addChild( new TestSubcase(m_context, "subroutine-index-non-continuous", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-index-multiple-uniforms", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-index-all-stages", TestSubcase::Create)); addChild( new TestSubcase(m_context, "subroutine-index-mix-implicit", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-index-negative-compilation-non-number-literal", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-index-negative-compilation-nonconst-index", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-index-negative-link-index-reused", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-index-negative-link-location-maxindex", TestSubcase::Create)); addChild(new TestSubcase(m_context, "subroutine-index-negative-link-max-num-of-indices", TestSubcase::Create)); } ExplicitUniformLocationES31Tests::ExplicitUniformLocationES31Tests(glcts::Context& context) : TestCaseGroup(context, "explicit_uniform_location", "") { } ExplicitUniformLocationES31Tests::~ExplicitUniformLocationES31Tests(void) { } void ExplicitUniformLocationES31Tests::init() { using namespace glcts; Logger::setOutput(m_context.getTestContext().getLog()); addChild(new TestSubcase(m_context, "uniform-loc", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-nondecimal", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-all-stages", TestSubcase::Create)); addChild( new TestSubcase(m_context, "uniform-loc-multiple-uniforms", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-types-mix", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-types-mat", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-types-structs", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-types-samplers", TestSubcase::Create)); addChild( new TestSubcase(m_context, "uniform-loc-arrays-nonspaced", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-arrays-spaced", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-arrays-of-arrays", TestSubcase::Create)); addChild( new TestSubcase(m_context, "uniform-loc-mix-with-implicit", TestSubcase::Create)); addChild( new TestSubcase(m_context, "uniform-loc-mix-with-implicit2", TestSubcase::Create)); addChild( new TestSubcase(m_context, "uniform-loc-mix-with-implicit3", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-mix-with-implicit-max", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-mix-with-implicit-max-array", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-implicit-in-some-stages", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-implicit-in-some-stages2", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-implicit-in-some-stages3", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-negative-compile-non-number-literal", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-negative-compile-nonconst-loc", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-negative-link-location-reused1", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-negative-link-location-reused2", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-negative-link-max-location", TestSubcase::Create)); addChild(new TestSubcase(m_context, "uniform-loc-negative-link-max-num-of-locations", TestSubcase::Create)); } }