/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 2.0 Module * ------------------------------------------------- * * Copyright 2014 The Android Open Source Project * * 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 Uniform API tests. * * \todo [2013-02-26 nuutti] Much duplication between this and ES3. * Utilities to glshared? *//*--------------------------------------------------------------------*/ #include "es2fUniformApiTests.hpp" #include "gluCallLogWrapper.hpp" #include "gluShaderProgram.hpp" #include "gluVarType.hpp" #include "gluPixelTransfer.hpp" #include "gluTextureUtil.hpp" #include "gluTexture.hpp" #include "tcuRenderTarget.hpp" #include "tcuTestLog.hpp" #include "tcuSurface.hpp" #include "tcuCommandLine.hpp" #include "deRandom.hpp" #include "deStringUtil.hpp" #include "deSharedPtr.hpp" #include "deString.h" #include "deMemory.h" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include #include using namespace glw; namespace deqp { namespace gles2 { namespace Functional { using std::vector; using std::string; using tcu::TestLog; using tcu::ScopedLogSection; using glu::ShaderProgram; using glu::StructType; using de::Random; using de::SharedPtr; typedef bool (* dataTypePredicate)(glu::DataType); static const int MAX_RENDER_WIDTH = 32; static const int MAX_RENDER_HEIGHT = 32; static const int MAX_NUM_SAMPLER_UNIFORMS = 16; static const glu::DataType s_testDataTypes[] = { glu::TYPE_FLOAT, glu::TYPE_FLOAT_VEC2, glu::TYPE_FLOAT_VEC3, glu::TYPE_FLOAT_VEC4, glu::TYPE_FLOAT_MAT2, glu::TYPE_FLOAT_MAT3, glu::TYPE_FLOAT_MAT4, glu::TYPE_INT, glu::TYPE_INT_VEC2, glu::TYPE_INT_VEC3, glu::TYPE_INT_VEC4, glu::TYPE_BOOL, glu::TYPE_BOOL_VEC2, glu::TYPE_BOOL_VEC3, glu::TYPE_BOOL_VEC4, glu::TYPE_SAMPLER_2D, glu::TYPE_SAMPLER_CUBE }; static inline int getGLInt (const glw::Functions& funcs, const deUint32 name) { int val = -1; funcs.getIntegerv(name, &val); return val; } static inline tcu::Vec4 vec4FromPtr (const float* const ptr) { tcu::Vec4 result; for (int i = 0; i < 4; i++) result[i] = ptr[i]; return result; } static inline string beforeLast (const string& str, const char c) { return str.substr(0, str.find_last_of(c)); } static inline void fillWithColor (const tcu::PixelBufferAccess& access, const tcu::Vec4& color) { for (int z = 0; z < access.getDepth(); z++) for (int y = 0; y < access.getHeight(); y++) for (int x = 0; x < access.getWidth(); x++) access.setPixel(color, x, y, z); } static inline int getSamplerNumLookupDimensions (const glu::DataType type) { switch (type) { case glu::TYPE_SAMPLER_2D: return 2; case glu::TYPE_SAMPLER_CUBE: return 3; default: // \note All others than 2d and cube are gles3-only types. DE_ASSERT(false); return 0; } } template static bool dataTypeEquals (const glu::DataType t) { return t == T; } template static bool dataTypeIsMatrixWithNRows (const glu::DataType t) { return glu::isDataTypeMatrix(t) && glu::getDataTypeMatrixNumRows(t) == N; } static bool typeContainsMatchingBasicType (const glu::VarType& type, const dataTypePredicate predicate) { if (type.isBasicType()) return predicate(type.getBasicType()); else if (type.isArrayType()) return typeContainsMatchingBasicType(type.getElementType(), predicate); else { DE_ASSERT(type.isStructType()); const StructType& structType = *type.getStructPtr(); for (int i = 0; i < structType.getNumMembers(); i++) if (typeContainsMatchingBasicType(structType.getMember(i).getType(), predicate)) return true; return false; } } static void getDistinctSamplerTypes (vector& dst, const glu::VarType& type) { if (type.isBasicType()) { const glu::DataType basicType = type.getBasicType(); if (glu::isDataTypeSampler(basicType) && std::find(dst.begin(), dst.end(), basicType) == dst.end()) dst.push_back(basicType); } else if (type.isArrayType()) getDistinctSamplerTypes(dst, type.getElementType()); else { DE_ASSERT(type.isStructType()); const StructType& structType = *type.getStructPtr(); for (int i = 0; i < structType.getNumMembers(); i++) getDistinctSamplerTypes(dst, structType.getMember(i).getType()); } } static int getNumSamplersInType (const glu::VarType& type) { if (type.isBasicType()) return glu::isDataTypeSampler(type.getBasicType()) ? 1 : 0; else if (type.isArrayType()) return getNumSamplersInType(type.getElementType()) * type.getArraySize(); else { DE_ASSERT(type.isStructType()); const StructType& structType = *type.getStructPtr(); int sum = 0; for (int i = 0; i < structType.getNumMembers(); i++) sum += getNumSamplersInType(structType.getMember(i).getType()); return sum; } } static glu::VarType generateRandomType (const int maxDepth, int& curStructIdx, vector& structTypesDst, Random& rnd) { const bool isStruct = maxDepth > 0 && rnd.getFloat() < 0.2f; const bool isArray = rnd.getFloat() < 0.3f; if (isStruct) { const int numMembers = rnd.getInt(1, 5); StructType* const structType = new StructType(("structType" + de::toString(curStructIdx++)).c_str()); for (int i = 0; i < numMembers; i++) structType->addMember(("m" + de::toString(i)).c_str(), generateRandomType(maxDepth-1, curStructIdx, structTypesDst, rnd)); structTypesDst.push_back(structType); return isArray ? glu::VarType(glu::VarType(structType), rnd.getInt(1, 5)) : glu::VarType(structType); } else { const glu::DataType basicType = (glu::DataType)s_testDataTypes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testDataTypes)-1)]; const glu::Precision precision = glu::isDataTypeBoolOrBVec(basicType) ? glu::PRECISION_LAST : glu::PRECISION_MEDIUMP; return isArray ? glu::VarType(glu::VarType(basicType, precision), rnd.getInt(1, 5)) : glu::VarType(basicType, precision); } } namespace { struct VarValue { glu::DataType type; union { float floatV[4*4]; // At most mat4. \note Matrices here are column-major. deInt32 intV[4]; bool boolV[4]; struct { int unit; float fillColor[4]; } samplerV; } val; }; enum CaseShaderType { CASESHADERTYPE_VERTEX = 0, CASESHADERTYPE_FRAGMENT, CASESHADERTYPE_BOTH, CASESHADERTYPE_LAST }; struct Uniform { string name; glu::VarType type; Uniform (const char* const name_, const glu::VarType& type_) : name(name_), type(type_) {} }; // A set of uniforms, along with related struct types. class UniformCollection { public: int getNumUniforms (void) const { return (int)m_uniforms.size(); } int getNumStructTypes (void) const { return (int)m_structTypes.size(); } Uniform& getUniform (const int ndx) { return m_uniforms[ndx]; } const Uniform& getUniform (const int ndx) const { return m_uniforms[ndx]; } const StructType* getStructType (const int ndx) const { return m_structTypes[ndx]; } void addUniform (const Uniform& uniform) { m_uniforms.push_back(uniform); } void addStructType (const StructType* const type) { m_structTypes.push_back(type); } UniformCollection (void) {} ~UniformCollection (void) { for (int i = 0; i < (int)m_structTypes.size(); i++) delete m_structTypes[i]; } // Add the contents of m_uniforms and m_structTypes to receiver, and remove them from this one. // \note receiver takes ownership of the struct types. void moveContents (UniformCollection& receiver) { for (int i = 0; i < (int)m_uniforms.size(); i++) receiver.addUniform(m_uniforms[i]); m_uniforms.clear(); for (int i = 0; i < (int)m_structTypes.size(); i++) receiver.addStructType(m_structTypes[i]); m_structTypes.clear(); } bool containsMatchingBasicType (const dataTypePredicate predicate) const { for (int i = 0; i < (int)m_uniforms.size(); i++) if (typeContainsMatchingBasicType(m_uniforms[i].type, predicate)) return true; return false; } vector getSamplerTypes (void) const { vector samplerTypes; for (int i = 0; i < (int)m_uniforms.size(); i++) getDistinctSamplerTypes(samplerTypes, m_uniforms[i].type); return samplerTypes; } bool containsSeveralSamplerTypes (void) const { return getSamplerTypes().size() > 1; } int getNumSamplers (void) const { int sum = 0; for (int i = 0; i < (int)m_uniforms.size(); i++) sum += getNumSamplersInType(m_uniforms[i].type); return sum; } static UniformCollection* basic (const glu::DataType type, const char* const nameSuffix = "") { UniformCollection* const res = new UniformCollection; const glu::Precision prec = glu::isDataTypeBoolOrBVec(type) ? glu::PRECISION_LAST : glu::PRECISION_MEDIUMP; res->m_uniforms.push_back(Uniform((string("u_var") + nameSuffix).c_str(), glu::VarType(type, prec))); return res; } static UniformCollection* basicArray (const glu::DataType type, const char* const nameSuffix = "") { UniformCollection* const res = new UniformCollection; const glu::Precision prec = glu::isDataTypeBoolOrBVec(type) ? glu::PRECISION_LAST : glu::PRECISION_MEDIUMP; res->m_uniforms.push_back(Uniform((string("u_var") + nameSuffix).c_str(), glu::VarType(glu::VarType(type, prec), 3))); return res; } static UniformCollection* basicStruct (const glu::DataType type0, const glu::DataType type1, const bool containsArrays, const char* const nameSuffix = "") { UniformCollection* const res = new UniformCollection; const glu::Precision prec0 = glu::isDataTypeBoolOrBVec(type0) ? glu::PRECISION_LAST : glu::PRECISION_MEDIUMP; const glu::Precision prec1 = glu::isDataTypeBoolOrBVec(type1) ? glu::PRECISION_LAST : glu::PRECISION_MEDIUMP; StructType* const structType = new StructType((string("structType") + nameSuffix).c_str()); structType->addMember("m0", glu::VarType(type0, prec0)); structType->addMember("m1", glu::VarType(type1, prec1)); if (containsArrays) { structType->addMember("m2", glu::VarType(glu::VarType(type0, prec0), 3)); structType->addMember("m3", glu::VarType(glu::VarType(type1, prec1), 3)); } res->addStructType(structType); res->addUniform(Uniform((string("u_var") + nameSuffix).c_str(), glu::VarType(structType))); return res; } static UniformCollection* structInArray (const glu::DataType type0, const glu::DataType type1, const bool containsArrays, const char* const nameSuffix = "") { UniformCollection* const res = basicStruct(type0, type1, containsArrays, nameSuffix); res->getUniform(0).type = glu::VarType(res->getUniform(0).type, 3); return res; } static UniformCollection* nestedArraysStructs (const glu::DataType type0, const glu::DataType type1, const char* const nameSuffix = "") { UniformCollection* const res = new UniformCollection; const glu::Precision prec0 = glu::isDataTypeBoolOrBVec(type0) ? glu::PRECISION_LAST : glu::PRECISION_MEDIUMP; const glu::Precision prec1 = glu::isDataTypeBoolOrBVec(type1) ? glu::PRECISION_LAST : glu::PRECISION_MEDIUMP; StructType* const structType = new StructType((string("structType") + nameSuffix).c_str()); StructType* const subStructType = new StructType((string("subStructType") + nameSuffix).c_str()); StructType* const subSubStructType = new StructType((string("subSubStructType") + nameSuffix).c_str()); subSubStructType->addMember("mss0", glu::VarType(type0, prec0)); subSubStructType->addMember("mss1", glu::VarType(type1, prec1)); subStructType->addMember("ms0", glu::VarType(type1, prec1)); subStructType->addMember("ms1", glu::VarType(glu::VarType(type0, prec0), 2)); subStructType->addMember("ms2", glu::VarType(glu::VarType(subSubStructType), 2)); structType->addMember("m0", glu::VarType(type0, prec0)); structType->addMember("m1", glu::VarType(subStructType)); structType->addMember("m2", glu::VarType(type1, prec1)); res->addStructType(subSubStructType); res->addStructType(subStructType); res->addStructType(structType); res->addUniform(Uniform((string("u_var") + nameSuffix).c_str(), glu::VarType(structType))); return res; } static UniformCollection* multipleBasic (const char* const nameSuffix = "") { static const glu::DataType types[] = { glu::TYPE_FLOAT, glu::TYPE_INT_VEC3, glu::TYPE_FLOAT_MAT3, glu::TYPE_BOOL_VEC2 }; UniformCollection* const res = new UniformCollection; for (int i = 0; i < DE_LENGTH_OF_ARRAY(types); i++) { UniformCollection* const sub = basic(types[i], ("_" + de::toString(i) + nameSuffix).c_str()); sub->moveContents(*res); delete sub; } return res; } static UniformCollection* multipleBasicArray (const char* const nameSuffix = "") { static const glu::DataType types[] = { glu::TYPE_FLOAT, glu::TYPE_INT_VEC3, glu::TYPE_BOOL_VEC2 }; UniformCollection* const res = new UniformCollection; for (int i = 0; i < DE_LENGTH_OF_ARRAY(types); i++) { UniformCollection* const sub = basicArray(types[i], ("_" + de::toString(i) + nameSuffix).c_str()); sub->moveContents(*res); delete sub; } return res; } static UniformCollection* multipleNestedArraysStructs (const char* const nameSuffix = "") { static const glu::DataType types0[] = { glu::TYPE_FLOAT, glu::TYPE_INT, glu::TYPE_BOOL_VEC4 }; static const glu::DataType types1[] = { glu::TYPE_FLOAT_VEC4, glu::TYPE_INT_VEC4, glu::TYPE_BOOL }; UniformCollection* const res = new UniformCollection; DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(types0) == DE_LENGTH_OF_ARRAY(types1)); for (int i = 0; i < DE_LENGTH_OF_ARRAY(types0); i++) { UniformCollection* const sub = nestedArraysStructs(types0[i], types1[i], ("_" + de::toString(i) + nameSuffix).c_str()); sub->moveContents(*res); delete sub; } return res; } static UniformCollection* random (const deUint32 seed) { Random rnd (seed); const int numUniforms = rnd.getInt(1, 5); int structIdx = 0; UniformCollection* const res = new UniformCollection; for (int i = 0; i < numUniforms; i++) { vector structTypes; Uniform uniform(("u_var" + de::toString(i)).c_str(), glu::VarType()); // \note Discard uniforms that would cause number of samplers to exceed MAX_NUM_SAMPLER_UNIFORMS. do { for (int j = 0; j < (int)structTypes.size(); j++) delete structTypes[j]; structTypes.clear(); uniform.type = (("u_var" + de::toString(i)).c_str(), generateRandomType(3, structIdx, structTypes, rnd)); } while (res->getNumSamplers() + getNumSamplersInType(uniform.type) > MAX_NUM_SAMPLER_UNIFORMS); res->addUniform(uniform); for (int j = 0; j < (int)structTypes.size(); j++) res->addStructType(structTypes[j]); } return res; } private: // \note Copying these would be cumbersome, since deep-copying both m_uniforms and m_structTypes // would mean that we'd need to update pointers from uniforms to point to the new structTypes. // When the same UniformCollection is needed in several places, a SharedPtr is used instead. UniformCollection (const UniformCollection&); // Not allowed. UniformCollection& operator= (const UniformCollection&); // Not allowed. vector m_uniforms; vector m_structTypes; }; }; // anonymous static VarValue getSamplerFillValue (const VarValue& sampler) { DE_ASSERT(glu::isDataTypeSampler(sampler.type)); VarValue result; result.type = glu::TYPE_FLOAT_VEC4; for (int i = 0; i < 4; i++) result.val.floatV[i] = sampler.val.samplerV.fillColor[i]; return result; } static VarValue getSamplerUnitValue (const VarValue& sampler) { DE_ASSERT(glu::isDataTypeSampler(sampler.type)); VarValue result; result.type = glu::TYPE_INT; result.val.intV[0] = sampler.val.samplerV.unit; return result; } static string shaderVarValueStr (const VarValue& value) { const int numElems = glu::getDataTypeScalarSize(value.type); std::ostringstream result; if (numElems > 1) result << glu::getDataTypeName(value.type) << "("; for (int i = 0; i < numElems; i++) { if (i > 0) result << ", "; if (glu::isDataTypeFloatOrVec(value.type) || glu::isDataTypeMatrix(value.type)) result << de::floatToString(value.val.floatV[i], 2); else if (glu::isDataTypeIntOrIVec((value.type))) result << de::toString(value.val.intV[i]); else if (glu::isDataTypeBoolOrBVec((value.type))) result << (value.val.boolV[i] ? "true" : "false"); else if (glu::isDataTypeSampler((value.type))) result << shaderVarValueStr(getSamplerFillValue(value)); else DE_ASSERT(false); } if (numElems > 1) result << ")"; return result.str(); } static string apiVarValueStr (const VarValue& value) { const int numElems = glu::getDataTypeScalarSize(value.type); std::ostringstream result; if (numElems > 1) result << "("; for (int i = 0; i < numElems; i++) { if (i > 0) result << ", "; if (glu::isDataTypeFloatOrVec(value.type) || glu::isDataTypeMatrix(value.type)) result << de::floatToString(value.val.floatV[i], 2); else if (glu::isDataTypeIntOrIVec((value.type))) result << de::toString(value.val.intV[i]); else if (glu::isDataTypeBoolOrBVec((value.type))) result << (value.val.boolV[i] ? "true" : "false"); else if (glu::isDataTypeSampler((value.type))) result << value.val.samplerV.unit; else DE_ASSERT(false); } if (numElems > 1) result << ")"; return result.str(); } static VarValue generateRandomVarValue (const glu::DataType type, Random& rnd, int samplerUnit = -1 /* Used if type is a sampler type. \note Samplers' unit numbers are not randomized. */) { const int numElems = glu::getDataTypeScalarSize(type); VarValue result; result.type = type; DE_ASSERT((samplerUnit >= 0) == (glu::isDataTypeSampler(type))); if (glu::isDataTypeFloatOrVec(type) || glu::isDataTypeMatrix(type)) { for (int i = 0; i < numElems; i++) result.val.floatV[i] = rnd.getFloat(-10.0f, 10.0f); } else if (glu::isDataTypeIntOrIVec(type)) { for (int i = 0; i < numElems; i++) result.val.intV[i] = rnd.getInt(-10, 10); } else if (glu::isDataTypeBoolOrBVec(type)) { for (int i = 0; i < numElems; i++) result.val.boolV[i] = rnd.getBool(); } else if (glu::isDataTypeSampler(type)) { result.val.samplerV.unit = samplerUnit; for (int i = 0; i < 4; i++) result.val.samplerV.fillColor[i] = rnd.getFloat(0.0f, 1.0f); } else DE_ASSERT(false); return result; } static VarValue generateZeroVarValue (const glu::DataType type) { const int numElems = glu::getDataTypeScalarSize(type); VarValue result; result.type = type; if (glu::isDataTypeFloatOrVec(type) || glu::isDataTypeMatrix(type)) { for (int i = 0; i < numElems; i++) result.val.floatV[i] = 0.0f; } else if (glu::isDataTypeIntOrIVec(type)) { for (int i = 0; i < numElems; i++) result.val.intV[i] = 0; } else if (glu::isDataTypeBoolOrBVec(type)) { for (int i = 0; i < numElems; i++) result.val.boolV[i] = false; } else if (glu::isDataTypeSampler(type)) { result.val.samplerV.unit = 0; for (int i = 0; i < 4; i++) result.val.samplerV.fillColor[i] = 0.12f * (float)i; } else DE_ASSERT(false); return result; } static bool apiVarValueEquals (const VarValue& a, const VarValue& b) { const int size = glu::getDataTypeScalarSize(a.type); const float floatThreshold = 0.05f; DE_ASSERT(a.type == b.type); if (glu::isDataTypeFloatOrVec(a.type) || glu::isDataTypeMatrix(a.type)) { for (int i = 0; i < size; i++) if (de::abs(a.val.floatV[i] - b.val.floatV[i]) >= floatThreshold) return false; } else if (glu::isDataTypeIntOrIVec(a.type)) { for (int i = 0; i < size; i++) if (a.val.intV[i] != b.val.intV[i]) return false; } else if (glu::isDataTypeBoolOrBVec(a.type)) { for (int i = 0; i < size; i++) if (a.val.boolV[i] != b.val.boolV[i]) return false; } else if (glu::isDataTypeSampler(a.type)) { if (a.val.samplerV.unit != b.val.samplerV.unit) return false; } else DE_ASSERT(false); return true; } static VarValue getRandomBoolRepresentation (const VarValue& boolValue, const glu::DataType targetScalarType, Random& rnd) { DE_ASSERT(glu::isDataTypeBoolOrBVec(boolValue.type)); const int size = glu::getDataTypeScalarSize(boolValue.type); const glu::DataType targetType = size == 1 ? targetScalarType : glu::getDataTypeVector(targetScalarType, size); VarValue result; result.type = targetType; switch (targetScalarType) { case glu::TYPE_INT: for (int i = 0; i < size; i++) { if (boolValue.val.boolV[i]) { result.val.intV[i] = rnd.getInt(-10, 10); if (result.val.intV[i] == 0) result.val.intV[i] = 1; } else result.val.intV[i] = 0; } break; case glu::TYPE_FLOAT: for (int i = 0; i < size; i++) { if (boolValue.val.boolV[i]) { result.val.floatV[i] = rnd.getFloat(-10.0f, 10.0f); if (result.val.floatV[i] == 0.0f) result.val.floatV[i] = 1.0f; } else result.val.floatV[i] = 0; } break; default: DE_ASSERT(false); } return result; } static const char* getCaseShaderTypeName (const CaseShaderType type) { switch (type) { case CASESHADERTYPE_VERTEX: return "vertex"; case CASESHADERTYPE_FRAGMENT: return "fragment"; case CASESHADERTYPE_BOTH: return "both"; default: DE_ASSERT(false); return DE_NULL; } } static CaseShaderType randomCaseShaderType (const deUint32 seed) { return (CaseShaderType)Random(seed).getInt(0, CASESHADERTYPE_LAST-1); } class UniformCase : public TestCase, protected glu::CallLogWrapper { public: enum Feature { // ARRAYUSAGE_ONLY_MIDDLE_INDEX: only middle index of each array is used in shader. If not given, use all indices. FEATURE_ARRAYUSAGE_ONLY_MIDDLE_INDEX = 1<<0, // UNIFORMFUNC_VALUE: use pass-by-value versions of uniform assignment funcs, e.g. glUniform1f(), where possible. If not given, use pass-by-pointer versions. FEATURE_UNIFORMFUNC_VALUE = 1<<1, // ARRAYASSIGN: how basic-type arrays are assigned with glUniform*(). If none given, assign each element of an array separately. FEATURE_ARRAYASSIGN_FULL = 1<<2, //!< Assign all elements of an array with one glUniform*(). FEATURE_ARRAYASSIGN_BLOCKS_OF_TWO = 1<<3, //!< Assign two elements per one glUniform*(). // UNIFORMUSAGE_EVERY_OTHER: use about half of the uniforms. If not given, use all uniforms (except that some array indices may be omitted according to ARRAYUSAGE). FEATURE_UNIFORMUSAGE_EVERY_OTHER = 1<<4, // BOOLEANAPITYPE: type used to pass booleans to and from GL api. If none given, use float. FEATURE_BOOLEANAPITYPE_INT = 1<<5, // UNIFORMVALUE_ZERO: use zero-valued uniforms. If not given, use random uniform values. FEATURE_UNIFORMVALUE_ZERO = 1<<6, // ARRAY_FIRST_ELEM_NAME_NO_INDEX: in certain API functions, when referring to the first element of an array, use just the array name without [0] at the end. FEATURE_ARRAY_FIRST_ELEM_NAME_NO_INDEX = 1<<7 }; UniformCase (Context& context, const char* name, const char* description, CaseShaderType caseType, const SharedPtr& uniformCollection, deUint32 features); UniformCase (Context& context, const char* name, const char* description, deUint32 seed); // \note Randomizes caseType, uniformCollection and features. virtual ~UniformCase (void); virtual void init (void); virtual void deinit (void); IterateResult iterate (void); protected: // A basic uniform is a uniform (possibly struct or array member) whose type is a basic type (e.g. float, ivec4, sampler2d). struct BasicUniform { string name; glu::DataType type; bool isUsedInShader; VarValue finalValue; //!< The value we ultimately want to set for this uniform. string rootName; //!< If this is a member of a basic-typed array, rootName is the name of that array with "[0]" appended. Otherwise it equals name. int elemNdx; //!< If this is a member of a basic-typed array, elemNdx is the index in that array. Otherwise -1. int rootSize; //!< If this is a member of a basic-typed array, rootSize is the size of that array. Otherwise 1. BasicUniform (const char* const name_, const glu::DataType type_, const bool isUsedInShader_, const VarValue& finalValue_, const char* const rootName_ = DE_NULL, const int elemNdx_ = -1, const int rootSize_ = 1) : name (name_) , type (type_) , isUsedInShader (isUsedInShader_) , finalValue (finalValue_) , rootName (rootName_ == DE_NULL ? name_ : rootName_) , elemNdx (elemNdx_) , rootSize (rootSize_) { } static vector::const_iterator findWithName (const vector& vec, const char* const name) { for (vector::const_iterator it = vec.begin(); it != vec.end(); it++) { if (it->name == name) return it; } return vec.end(); } }; // Reference values for info that is expected to be reported by glGetActiveUniform(). struct BasicUniformReportRef { string name; // \note minSize and maxSize are for arrays and can be distinct since implementations are allowed, but not required, to trim the inactive end indices of arrays. int minSize; int maxSize; glu::DataType type; bool isUsedInShader; BasicUniformReportRef (const char* const name_, const int minS, const int maxS, const glu::DataType type_, const bool used) : name(name_), minSize(minS), maxSize(maxS), type(type_), isUsedInShader(used) { DE_ASSERT(minSize <= maxSize); } BasicUniformReportRef (const char* const name_, const glu::DataType type_, const bool used) : name(name_), minSize(1), maxSize(1), type(type_), isUsedInShader(used) {} }; // Info that is actually reported by glGetActiveUniform(). struct BasicUniformReportGL { string name; int nameLength; int size; glu::DataType type; int index; BasicUniformReportGL (const char* const name_, const int nameLength_, const int size_, const glu::DataType type_, const int index_) : name(name_), nameLength(nameLength_), size(size_), type(type_), index(index_) {} static vector::const_iterator findWithName (const vector& vec, const char* const name) { for (vector::const_iterator it = vec.begin(); it != vec.end(); it++) { if (it->name == name) return it; } return vec.end(); } }; // Query info with glGetActiveUniform() and check validity. bool getActiveUniforms (vector& dst, const vector& ref, deUint32 programGL); // Get uniform values with glGetUniform*() and put to valuesDst. Uniforms that get -1 from glGetUniformLocation() get glu::TYPE_INVALID. bool getUniforms (vector& valuesDst, const vector& basicUniforms, deUint32 programGL); // Check that every uniform has the default (zero) value. bool checkUniformDefaultValues (const vector& values, const vector& basicUniforms); // Assign the basicUniforms[].finalValue values for uniforms. \note rnd parameter is for booleans (true can be any nonzero value). void assignUniforms (const vector& basicUniforms, deUint32 programGL, Random& rnd); // Compare the uniform values given in values (obtained with glGetUniform*()) with the basicUniform.finalValue values. bool compareUniformValues (const vector& values, const vector& basicUniforms); // Render and check that all pixels are white (i.e. all uniform comparisons passed). bool renderTest (const vector& basicUniforms, const ShaderProgram& program, Random& rnd); virtual bool test (const vector& basicUniforms, const vector& basicUniformReportsRef, const ShaderProgram& program, Random& rnd) = 0; const deUint32 m_features; const SharedPtr m_uniformCollection; private: static deUint32 randomFeatures (deUint32 seed); // Generates the basic uniforms, based on the uniform with name varName and type varType, in the same manner as are expected // to be returned by glGetActiveUniform(), e.g. generates a name like var[0] for arrays, and recursively generates struct member names. void generateBasicUniforms (vector& basicUniformsDst, vector& basicUniformReportsDst, const glu::VarType& varType, const char* varName, bool isParentActive, int& samplerUnitCounter, Random& rnd) const; void writeUniformDefinitions (std::ostringstream& dst) const; void writeUniformCompareExpr (std::ostringstream& dst, const BasicUniform& uniform) const; void writeUniformComparisons (std::ostringstream& dst, const vector& basicUniforms, const char* variableName) const; string generateVertexSource (const vector& basicUniforms) const; string generateFragmentSource (const vector& basicUniforms) const; void setupTexture (const VarValue& value); const CaseShaderType m_caseShaderType; vector m_textures2d; vector m_texturesCube; vector m_filledTextureUnits; }; deUint32 UniformCase::randomFeatures (const deUint32 seed) { static const deUint32 arrayUsageChoices[] = { 0, FEATURE_ARRAYUSAGE_ONLY_MIDDLE_INDEX }; static const deUint32 uniformFuncChoices[] = { 0, FEATURE_UNIFORMFUNC_VALUE }; static const deUint32 arrayAssignChoices[] = { 0, FEATURE_ARRAYASSIGN_FULL, FEATURE_ARRAYASSIGN_BLOCKS_OF_TWO }; static const deUint32 uniformUsageChoices[] = { 0, FEATURE_UNIFORMUSAGE_EVERY_OTHER }; static const deUint32 booleanApiTypeChoices[] = { 0, FEATURE_BOOLEANAPITYPE_INT }; static const deUint32 uniformValueChoices[] = { 0, FEATURE_UNIFORMVALUE_ZERO }; Random rnd(seed); deUint32 result = 0; #define ARRAY_CHOICE(ARR) ((ARR)[rnd.getInt(0, DE_LENGTH_OF_ARRAY(ARR)-1)]) result |= ARRAY_CHOICE(arrayUsageChoices); result |= ARRAY_CHOICE(uniformFuncChoices); result |= ARRAY_CHOICE(arrayAssignChoices); result |= ARRAY_CHOICE(uniformUsageChoices); result |= ARRAY_CHOICE(booleanApiTypeChoices); result |= ARRAY_CHOICE(uniformValueChoices); #undef ARRAY_CHOICE return result; } UniformCase::UniformCase (Context& context, const char* const name, const char* const description, const CaseShaderType caseShaderType, const SharedPtr& uniformCollection, const deUint32 features) : TestCase (context, name, description) , CallLogWrapper (context.getRenderContext().getFunctions(), m_testCtx.getLog()) , m_features (features) , m_uniformCollection (uniformCollection) , m_caseShaderType (caseShaderType) { } UniformCase::UniformCase (Context& context, const char* name, const char* description, const deUint32 seed) : TestCase (context, name, description) , CallLogWrapper (context.getRenderContext().getFunctions(), m_testCtx.getLog()) , m_features (randomFeatures(seed)) , m_uniformCollection (UniformCollection::random(seed)) , m_caseShaderType (randomCaseShaderType(seed)) { } void UniformCase::init (void) { { const glw::Functions& funcs = m_context.getRenderContext().getFunctions(); const int numSamplerUniforms = m_uniformCollection->getNumSamplers(); const int vertexTexUnitsRequired = m_caseShaderType != CASESHADERTYPE_FRAGMENT ? numSamplerUniforms : 0; const int fragmentTexUnitsRequired = m_caseShaderType != CASESHADERTYPE_VERTEX ? numSamplerUniforms : 0; const int combinedTexUnitsRequired = vertexTexUnitsRequired + fragmentTexUnitsRequired; const int vertexTexUnitsSupported = getGLInt(funcs, GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS); const int fragmentTexUnitsSupported = getGLInt(funcs, GL_MAX_TEXTURE_IMAGE_UNITS); const int combinedTexUnitsSupported = getGLInt(funcs, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS); DE_ASSERT(numSamplerUniforms <= MAX_NUM_SAMPLER_UNIFORMS); if (vertexTexUnitsRequired > vertexTexUnitsSupported) throw tcu::NotSupportedError(de::toString(vertexTexUnitsRequired) + " vertex texture units required, " + de::toString(vertexTexUnitsSupported) + " supported"); if (fragmentTexUnitsRequired > fragmentTexUnitsSupported) throw tcu::NotSupportedError(de::toString(fragmentTexUnitsRequired) + " fragment texture units required, " + de::toString(fragmentTexUnitsSupported) + " supported"); if (combinedTexUnitsRequired > combinedTexUnitsSupported) throw tcu::NotSupportedError(de::toString(combinedTexUnitsRequired) + " combined texture units required, " + de::toString(combinedTexUnitsSupported) + " supported"); } enableLogging(true); } void UniformCase::deinit (void) { for (int i = 0; i < (int)m_textures2d.size(); i++) delete m_textures2d[i]; m_textures2d.clear(); for (int i = 0; i < (int)m_texturesCube.size(); i++) delete m_texturesCube[i]; m_texturesCube.clear(); m_filledTextureUnits.clear(); } UniformCase::~UniformCase (void) { UniformCase::deinit(); } void UniformCase::generateBasicUniforms (vector& basicUniformsDst, vector& basicUniformReportsDst, const glu::VarType& varType, const char* const varName, const bool isParentActive, int& samplerUnitCounter, Random& rnd) const { if (varType.isBasicType()) { const bool isActive = isParentActive && (m_features & FEATURE_UNIFORMUSAGE_EVERY_OTHER ? basicUniformsDst.size() % 2 == 0 : true); const glu::DataType type = varType.getBasicType(); const VarValue value = m_features & FEATURE_UNIFORMVALUE_ZERO ? generateZeroVarValue(type) : glu::isDataTypeSampler(type) ? generateRandomVarValue(type, rnd, samplerUnitCounter++) : generateRandomVarValue(varType.getBasicType(), rnd); basicUniformsDst.push_back(BasicUniform(varName, varType.getBasicType(), isActive, value)); basicUniformReportsDst.push_back(BasicUniformReportRef(varName, varType.getBasicType(), isActive)); } else if (varType.isArrayType()) { const int size = varType.getArraySize(); const string arrayRootName = string("") + varName + "[0]"; vector isElemActive; for (int elemNdx = 0; elemNdx < varType.getArraySize(); elemNdx++) { const string indexedName = string("") + varName + "[" + de::toString(elemNdx) + "]"; const bool isCurElemActive = isParentActive && (m_features & FEATURE_UNIFORMUSAGE_EVERY_OTHER ? basicUniformsDst.size() % 2 == 0 : true) && (m_features & FEATURE_ARRAYUSAGE_ONLY_MIDDLE_INDEX ? elemNdx == size/2 : true); isElemActive.push_back(isCurElemActive); if (varType.getElementType().isBasicType()) { // \note We don't want separate entries in basicUniformReportsDst for elements of basic-type arrays. const glu::DataType elemBasicType = varType.getElementType().getBasicType(); const VarValue value = m_features & FEATURE_UNIFORMVALUE_ZERO ? generateZeroVarValue(elemBasicType) : glu::isDataTypeSampler(elemBasicType) ? generateRandomVarValue(elemBasicType, rnd, samplerUnitCounter++) : generateRandomVarValue(elemBasicType, rnd); basicUniformsDst.push_back(BasicUniform(indexedName.c_str(), elemBasicType, isCurElemActive, value, arrayRootName.c_str(), elemNdx, size)); } else generateBasicUniforms(basicUniformsDst, basicUniformReportsDst, varType.getElementType(), indexedName.c_str(), isCurElemActive, samplerUnitCounter, rnd); } if (varType.getElementType().isBasicType()) { int minSize; for (minSize = varType.getArraySize(); minSize > 0 && !isElemActive[minSize-1]; minSize--); basicUniformReportsDst.push_back(BasicUniformReportRef(arrayRootName.c_str(), minSize, size, varType.getElementType().getBasicType(), isParentActive && minSize > 0)); } } else { DE_ASSERT(varType.isStructType()); const StructType& structType = *varType.getStructPtr(); for (int i = 0; i < structType.getNumMembers(); i++) { const glu::StructMember& member = structType.getMember(i); const string memberFullName = string("") + varName + "." + member.getName(); generateBasicUniforms(basicUniformsDst, basicUniformReportsDst, member.getType(), memberFullName.c_str(), isParentActive, samplerUnitCounter, rnd); } } } void UniformCase::writeUniformDefinitions (std::ostringstream& dst) const { for (int i = 0; i < (int)m_uniformCollection->getNumStructTypes(); i++) dst << glu::declare(m_uniformCollection->getStructType(i)) << ";\n"; for (int i = 0; i < (int)m_uniformCollection->getNumUniforms(); i++) dst << "uniform " << glu::declare(m_uniformCollection->getUniform(i).type, m_uniformCollection->getUniform(i).name.c_str()) << ";\n"; dst << "\n"; { static const struct { dataTypePredicate requiringTypes[2]; const char* definition; } compareFuncs[] = { { { glu::isDataTypeFloatOrVec, glu::isDataTypeMatrix }, "mediump float compare_float (mediump float a, mediump float b) { return abs(a - b) < 0.05 ? 1.0 : 0.0; }" }, { { dataTypeEquals, dataTypeIsMatrixWithNRows<2> }, "mediump float compare_vec2 (mediump vec2 a, mediump vec2 b) { return compare_float(a.x, b.x)*compare_float(a.y, b.y); }" }, { { dataTypeEquals, dataTypeIsMatrixWithNRows<3> }, "mediump float compare_vec3 (mediump vec3 a, mediump vec3 b) { return compare_float(a.x, b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z); }" }, { { dataTypeEquals, dataTypeIsMatrixWithNRows<4> }, "mediump float compare_vec4 (mediump vec4 a, mediump vec4 b) { return compare_float(a.x, b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z)*compare_float(a.w, b.w); }" }, { { dataTypeEquals, dataTypeEquals }, "mediump float compare_mat2 (mediump mat2 a, mediump mat2 b) { return compare_vec2(a[0], b[0])*compare_vec2(a[1], b[1]); }" }, { { dataTypeEquals, dataTypeEquals }, "mediump float compare_mat3 (mediump mat3 a, mediump mat3 b) { return compare_vec3(a[0], b[0])*compare_vec3(a[1], b[1])*compare_vec3(a[2], b[2]); }" }, { { dataTypeEquals, dataTypeEquals }, "mediump float compare_mat4 (mediump mat4 a, mediump mat4 b) { return compare_vec4(a[0], b[0])*compare_vec4(a[1], b[1])*compare_vec4(a[2], b[2])*compare_vec4(a[3], b[3]); }" }, { { dataTypeEquals, dataTypeEquals }, "mediump float compare_int (mediump int a, mediump int b) { return a == b ? 1.0 : 0.0; }" }, { { dataTypeEquals, dataTypeEquals }, "mediump float compare_ivec2 (mediump ivec2 a, mediump ivec2 b) { return a == b ? 1.0 : 0.0; }" }, { { dataTypeEquals, dataTypeEquals }, "mediump float compare_ivec3 (mediump ivec3 a, mediump ivec3 b) { return a == b ? 1.0 : 0.0; }" }, { { dataTypeEquals, dataTypeEquals }, "mediump float compare_ivec4 (mediump ivec4 a, mediump ivec4 b) { return a == b ? 1.0 : 0.0; }" }, { { dataTypeEquals, dataTypeEquals }, "mediump float compare_bool (bool a, bool b) { return a == b ? 1.0 : 0.0; }" }, { { dataTypeEquals, dataTypeEquals }, "mediump float compare_bvec2 (bvec2 a, bvec2 b) { return a == b ? 1.0 : 0.0; }" }, { { dataTypeEquals, dataTypeEquals }, "mediump float compare_bvec3 (bvec3 a, bvec3 b) { return a == b ? 1.0 : 0.0; }" }, { { dataTypeEquals, dataTypeEquals }, "mediump float compare_bvec4 (bvec4 a, bvec4 b) { return a == b ? 1.0 : 0.0; }" } }; const bool containsSamplers = !m_uniformCollection->getSamplerTypes().empty(); for (int compFuncNdx = 0; compFuncNdx < DE_LENGTH_OF_ARRAY(compareFuncs); compFuncNdx++) { const dataTypePredicate (&typeReq)[2] = compareFuncs[compFuncNdx].requiringTypes; const bool containsTypeSampler = containsSamplers && (typeReq[0](glu::TYPE_FLOAT_VEC4) || typeReq[1](glu::TYPE_FLOAT_VEC4)); if (containsTypeSampler || m_uniformCollection->containsMatchingBasicType(typeReq[0]) || m_uniformCollection->containsMatchingBasicType(typeReq[1])) dst << compareFuncs[compFuncNdx].definition << "\n"; } } } void UniformCase::writeUniformCompareExpr (std::ostringstream& dst, const BasicUniform& uniform) const { if (glu::isDataTypeSampler(uniform.type)) { dst << "compare_vec4(" << (uniform.type == glu::TYPE_SAMPLER_2D ? "texture2D" : "textureCube") << "(" << uniform.name << ", vec" << getSamplerNumLookupDimensions(uniform.type) << "(0.0))"; } else dst << "compare_" << glu::getDataTypeName(uniform.type) << "(" << uniform.name; dst << ", " << shaderVarValueStr(uniform.finalValue) << ")"; } void UniformCase::writeUniformComparisons (std::ostringstream& dst, const vector& basicUniforms, const char* const variableName) const { for (int i = 0; i < (int)basicUniforms.size(); i++) { const BasicUniform& unif = basicUniforms[i]; if (unif.isUsedInShader) { dst << "\t" << variableName << " *= "; writeUniformCompareExpr(dst, basicUniforms[i]); dst << ";\n"; } else dst << "\t// UNUSED: " << basicUniforms[i].name << "\n"; } } string UniformCase::generateVertexSource (const vector& basicUniforms) const { const bool isVertexCase = m_caseShaderType == CASESHADERTYPE_VERTEX || m_caseShaderType == CASESHADERTYPE_BOTH; std::ostringstream result; result << "attribute highp vec4 a_position;\n" "varying mediump float v_vtxOut;\n" "\n"; if (isVertexCase) writeUniformDefinitions(result); result << "\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_vtxOut = 1.0;\n"; if (isVertexCase) writeUniformComparisons(result, basicUniforms, "v_vtxOut"); result << "}\n"; return result.str(); } string UniformCase::generateFragmentSource (const vector& basicUniforms) const { const bool isFragmentCase = m_caseShaderType == CASESHADERTYPE_FRAGMENT || m_caseShaderType == CASESHADERTYPE_BOTH; std::ostringstream result; result << "varying mediump float v_vtxOut;\n" "\n"; if (isFragmentCase) writeUniformDefinitions(result); result << "\n" "void main (void)\n" "{\n" " mediump float result = v_vtxOut;\n"; if (isFragmentCase) writeUniformComparisons(result, basicUniforms, "result"); result << " gl_FragColor = vec4(result, result, result, 1.0);\n" "}\n"; return result.str(); } void UniformCase::setupTexture (const VarValue& value) { enableLogging(false); const int width = 32; const int height = 32; const tcu::Vec4 color = vec4FromPtr(&value.val.samplerV.fillColor[0]); if (value.type == glu::TYPE_SAMPLER_2D) { glu::Texture2D* texture = new glu::Texture2D(m_context.getRenderContext(), GL_RGBA, GL_UNSIGNED_BYTE, width, height); tcu::Texture2D& refTexture = texture->getRefTexture(); m_textures2d.push_back(texture); refTexture.allocLevel(0); fillWithColor(refTexture.getLevel(0), color); GLU_CHECK_CALL(glActiveTexture(GL_TEXTURE0 + value.val.samplerV.unit)); m_filledTextureUnits.push_back(value.val.samplerV.unit); texture->upload(); GLU_CHECK_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GLU_CHECK_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); GLU_CHECK_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); GLU_CHECK_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); } else if (value.type == glu::TYPE_SAMPLER_CUBE) { DE_ASSERT(width == height); glu::TextureCube* texture = new glu::TextureCube(m_context.getRenderContext(), GL_RGBA, GL_UNSIGNED_BYTE, width); tcu::TextureCube& refTexture = texture->getRefTexture(); m_texturesCube.push_back(texture); for (int face = 0; face < (int)tcu::CUBEFACE_LAST; face++) { refTexture.allocLevel((tcu::CubeFace)face, 0); fillWithColor(refTexture.getLevelFace(0, (tcu::CubeFace)face), color); } GLU_CHECK_CALL(glActiveTexture(GL_TEXTURE0 + value.val.samplerV.unit)); m_filledTextureUnits.push_back(value.val.samplerV.unit); texture->upload(); GLU_CHECK_CALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GLU_CHECK_CALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); GLU_CHECK_CALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); GLU_CHECK_CALL(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); } else DE_ASSERT(false); enableLogging(true); } bool UniformCase::getActiveUniforms (vector& basicUniformReportsDst, const vector& basicUniformReportsRef, const deUint32 programGL) { TestLog& log = m_testCtx.getLog(); GLint numActiveUniforms = 0; GLint uniformMaxNameLength = 0; vector nameBuffer; bool success = true; GLU_CHECK_CALL(glGetProgramiv(programGL, GL_ACTIVE_UNIFORMS, &numActiveUniforms)); log << TestLog::Message << "// Number of active uniforms reported: " << numActiveUniforms << TestLog::EndMessage; GLU_CHECK_CALL(glGetProgramiv(programGL, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformMaxNameLength)); log << TestLog::Message << "// Maximum uniform name length reported: " << uniformMaxNameLength << TestLog::EndMessage; nameBuffer.resize(uniformMaxNameLength); for (int unifNdx = 0; unifNdx < numActiveUniforms; unifNdx++) { GLsizei reportedNameLength = 0; GLint reportedSize = -1; GLenum reportedTypeGL = GL_NONE; GLU_CHECK_CALL(glGetActiveUniform(programGL, (GLuint)unifNdx, (GLsizei)uniformMaxNameLength, &reportedNameLength, &reportedSize, &reportedTypeGL, &nameBuffer[0])); const glu::DataType reportedType = glu::getDataTypeFromGLType(reportedTypeGL); const string reportedNameStr (&nameBuffer[0]); TCU_CHECK_MSG(reportedType != glu::TYPE_LAST, "Invalid uniform type"); log << TestLog::Message << "// Got name = " << reportedNameStr << ", name length = " << reportedNameLength << ", size = " << reportedSize << ", type = " << glu::getDataTypeName(reportedType) << TestLog::EndMessage; if ((GLsizei)reportedNameStr.length() != reportedNameLength) { log << TestLog::Message << "// FAILURE: wrong name length reported, should be " << reportedNameStr.length() << TestLog::EndMessage; success = false; } if (!deStringBeginsWith(reportedNameStr.c_str(), "gl_")) // Ignore built-in uniforms. { int referenceNdx; for (referenceNdx = 0; referenceNdx < (int)basicUniformReportsRef.size(); referenceNdx++) { if (basicUniformReportsRef[referenceNdx].name == reportedNameStr) break; } if (referenceNdx >= (int)basicUniformReportsRef.size()) { log << TestLog::Message << "// FAILURE: invalid non-built-in uniform name reported" << TestLog::EndMessage; success = false; } else { const BasicUniformReportRef& reference = basicUniformReportsRef[referenceNdx]; DE_ASSERT(reference.type != glu::TYPE_LAST); DE_ASSERT(reference.minSize >= 1 || (reference.minSize == 0 && !reference.isUsedInShader)); DE_ASSERT(reference.minSize <= reference.maxSize); if (BasicUniformReportGL::findWithName(basicUniformReportsDst, reportedNameStr.c_str()) != basicUniformReportsDst.end()) { log << TestLog::Message << "// FAILURE: same uniform name reported twice" << TestLog::EndMessage; success = false; } basicUniformReportsDst.push_back(BasicUniformReportGL(reportedNameStr.c_str(), reportedNameLength, reportedSize, reportedType, unifNdx)); if (reportedType != reference.type) { log << TestLog::Message << "// FAILURE: wrong type reported, should be " << glu::getDataTypeName(reference.type) << TestLog::EndMessage; success = false; } if (reportedSize < reference.minSize || reportedSize > reference.maxSize) { log << TestLog::Message << "// FAILURE: wrong size reported, should be " << (reference.minSize == reference.maxSize ? de::toString(reference.minSize) : "in the range [" + de::toString(reference.minSize) + ", " + de::toString(reference.maxSize) + "]") << TestLog::EndMessage; success = false; } } } } for (int i = 0; i < (int)basicUniformReportsRef.size(); i++) { const BasicUniformReportRef& expected = basicUniformReportsRef[i]; if (expected.isUsedInShader && BasicUniformReportGL::findWithName(basicUniformReportsDst, expected.name.c_str()) == basicUniformReportsDst.end()) { log << TestLog::Message << "// FAILURE: uniform with name " << expected.name << " was not reported by GL" << TestLog::EndMessage; success = false; } } return success; } bool UniformCase::getUniforms (vector& valuesDst, const vector& basicUniforms, const deUint32 programGL) { TestLog& log = m_testCtx.getLog(); bool success = true; for (int unifNdx = 0; unifNdx < (int)basicUniforms.size(); unifNdx++) { const BasicUniform& uniform = basicUniforms[unifNdx]; const string queryName = m_features & FEATURE_ARRAY_FIRST_ELEM_NAME_NO_INDEX && uniform.elemNdx == 0 ? beforeLast(uniform.name, '[') : uniform.name; const int location = glGetUniformLocation(programGL, queryName.c_str()); const int size = glu::getDataTypeScalarSize(uniform.type); VarValue value; deMemset(&value, 0xcd, sizeof(value)); // Initialize to known garbage. if (location == -1) { value.type = glu::TYPE_INVALID; valuesDst.push_back(value); if (uniform.isUsedInShader) { log << TestLog::Message << "// FAILURE: " << uniform.name << " was used in shader, but has location -1" << TestLog::EndMessage; success = false; } continue; } value.type = uniform.type; DE_STATIC_ASSERT(sizeof(GLint) == sizeof(value.val.intV[0])); DE_STATIC_ASSERT(sizeof(GLfloat) == sizeof(value.val.floatV[0])); if (glu::isDataTypeFloatOrVec(uniform.type) || glu::isDataTypeMatrix(uniform.type)) GLU_CHECK_CALL(glGetUniformfv(programGL, location, &value.val.floatV[0])); else if (glu::isDataTypeIntOrIVec(uniform.type)) GLU_CHECK_CALL(glGetUniformiv(programGL, location, &value.val.intV[0])); else if (glu::isDataTypeBoolOrBVec(uniform.type)) { if (m_features & FEATURE_BOOLEANAPITYPE_INT) { GLU_CHECK_CALL(glGetUniformiv(programGL, location, &value.val.intV[0])); for (int i = 0; i < size; i++) value.val.boolV[i] = value.val.intV[i] != 0; } else // Default: use float. { GLU_CHECK_CALL(glGetUniformfv(programGL, location, &value.val.floatV[0])); for (int i = 0; i < size; i++) value.val.boolV[i] = value.val.floatV[i] != 0.0f; } } else if (glu::isDataTypeSampler(uniform.type)) { GLint unit = -1; GLU_CHECK_CALL(glGetUniformiv(programGL, location, &unit)); value.val.samplerV.unit = unit; } else DE_ASSERT(false); valuesDst.push_back(value); log << TestLog::Message << "// Got " << uniform.name << " value " << apiVarValueStr(value) << TestLog::EndMessage; } return success; } bool UniformCase::checkUniformDefaultValues (const vector& values, const vector& basicUniforms) { TestLog& log = m_testCtx.getLog(); bool success = true; DE_ASSERT(values.size() == basicUniforms.size()); for (int unifNdx = 0; unifNdx < (int)basicUniforms.size(); unifNdx++) { const BasicUniform& uniform = basicUniforms[unifNdx]; const VarValue& unifValue = values[unifNdx]; const int valSize = glu::getDataTypeScalarSize(uniform.type); log << TestLog::Message << "// Checking uniform " << uniform.name << TestLog::EndMessage; if (unifValue.type == glu::TYPE_INVALID) // This happens when glGetUniformLocation() returned -1. continue; #define CHECK_UNIFORM(VAR_VALUE_MEMBER, ZERO) \ do \ { \ for (int i = 0; i < valSize; i++) \ { \ if (unifValue.val.VAR_VALUE_MEMBER[i] != (ZERO)) \ { \ log << TestLog::Message << "// FAILURE: uniform " << uniform.name << " has non-zero initial value" << TestLog::EndMessage; \ success = false; \ } \ } \ } while (false) if (glu::isDataTypeFloatOrVec(uniform.type) || glu::isDataTypeMatrix(uniform.type)) CHECK_UNIFORM(floatV, 0.0f); else if (glu::isDataTypeIntOrIVec(uniform.type)) CHECK_UNIFORM(intV, 0); else if (glu::isDataTypeBoolOrBVec(uniform.type)) CHECK_UNIFORM(boolV, false); else if (glu::isDataTypeSampler(uniform.type)) { if (unifValue.val.samplerV.unit != 0) { log << TestLog::Message << "// FAILURE: uniform " << uniform.name << " has non-zero initial value" << TestLog::EndMessage; success = false; } } else DE_ASSERT(false); #undef CHECK_UNIFORM } return success; } void UniformCase::assignUniforms (const vector& basicUniforms, deUint32 programGL, Random& rnd) { TestLog& log = m_testCtx.getLog(); const glu::DataType boolApiType = m_features & FEATURE_BOOLEANAPITYPE_INT ? glu::TYPE_INT : glu::TYPE_FLOAT; for (int unifNdx = 0; unifNdx < (int)basicUniforms.size(); unifNdx++) { const BasicUniform& uniform = basicUniforms[unifNdx]; const bool isArrayMember = uniform.elemNdx >= 0; const string queryName = m_features & FEATURE_ARRAY_FIRST_ELEM_NAME_NO_INDEX && uniform.elemNdx == 0 ? beforeLast(uniform.name, '[') : uniform.name; const int numValuesToAssign = !isArrayMember ? 1 : m_features & FEATURE_ARRAYASSIGN_FULL ? (uniform.elemNdx == 0 ? uniform.rootSize : 0) : m_features & FEATURE_ARRAYASSIGN_BLOCKS_OF_TWO ? (uniform.elemNdx % 2 == 0 ? 2 : 0) : /* Default: assign array elements separately */ 1; DE_ASSERT(numValuesToAssign >= 0); DE_ASSERT(numValuesToAssign == 1 || isArrayMember); if (numValuesToAssign == 0) { log << TestLog::Message << "// Uniform " << uniform.name << " is covered by another glUniform*v() call to the same array" << TestLog::EndMessage; continue; } const int location = glGetUniformLocation(programGL, queryName.c_str()); const int typeSize = glu::getDataTypeScalarSize(uniform.type); const bool assignByValue = m_features & FEATURE_UNIFORMFUNC_VALUE && !glu::isDataTypeMatrix(uniform.type) && numValuesToAssign == 1; vector valuesToAssign; for (int i = 0; i < numValuesToAssign; i++) { const string curName = isArrayMember ? beforeLast(uniform.rootName, '[') + "[" + de::toString(uniform.elemNdx+i) + "]" : uniform.name; VarValue unifValue; if (isArrayMember) { const vector::const_iterator elemUnif = BasicUniform::findWithName(basicUniforms, curName.c_str()); if (elemUnif == basicUniforms.end()) continue; unifValue = elemUnif->finalValue; } else unifValue = uniform.finalValue; const VarValue apiValue = glu::isDataTypeBoolOrBVec(unifValue.type) ? getRandomBoolRepresentation(unifValue, boolApiType, rnd) : glu::isDataTypeSampler(unifValue.type) ? getSamplerUnitValue(unifValue) : unifValue; valuesToAssign.push_back(apiValue); if (glu::isDataTypeBoolOrBVec(uniform.type)) log << TestLog::Message << "// Using type " << glu::getDataTypeName(boolApiType) << " to set boolean value " << apiVarValueStr(unifValue) << " for " << curName << TestLog::EndMessage; else if (glu::isDataTypeSampler(uniform.type)) log << TestLog::Message << "// Texture for the sampler uniform " << curName << " will be filled with color " << apiVarValueStr(getSamplerFillValue(uniform.finalValue)) << TestLog::EndMessage; } DE_ASSERT(!valuesToAssign.empty()); if (glu::isDataTypeFloatOrVec(valuesToAssign[0].type)) { if (assignByValue) { const float* const ptr = &valuesToAssign[0].val.floatV[0]; switch (typeSize) { case 1: GLU_CHECK_CALL(glUniform1f(location, ptr[0])); break; case 2: GLU_CHECK_CALL(glUniform2f(location, ptr[0], ptr[1])); break; case 3: GLU_CHECK_CALL(glUniform3f(location, ptr[0], ptr[1], ptr[2])); break; case 4: GLU_CHECK_CALL(glUniform4f(location, ptr[0], ptr[1], ptr[2], ptr[3])); break; default: DE_ASSERT(false); } } else { vector buffer(valuesToAssign.size() * typeSize); for (int i = 0; i < (int)buffer.size(); i++) buffer[i] = valuesToAssign[i / typeSize].val.floatV[i % typeSize]; DE_STATIC_ASSERT(sizeof(GLfloat) == sizeof(buffer[0])); switch (typeSize) { case 1: GLU_CHECK_CALL(glUniform1fv(location, (GLsizei)valuesToAssign.size(), &buffer[0])); break; case 2: GLU_CHECK_CALL(glUniform2fv(location, (GLsizei)valuesToAssign.size(), &buffer[0])); break; case 3: GLU_CHECK_CALL(glUniform3fv(location, (GLsizei)valuesToAssign.size(), &buffer[0])); break; case 4: GLU_CHECK_CALL(glUniform4fv(location, (GLsizei)valuesToAssign.size(), &buffer[0])); break; default: DE_ASSERT(false); } } } else if (glu::isDataTypeMatrix(valuesToAssign[0].type)) { DE_ASSERT(!assignByValue); vector buffer(valuesToAssign.size() * typeSize); for (int i = 0; i < (int)buffer.size(); i++) buffer[i] = valuesToAssign[i / typeSize].val.floatV[i % typeSize]; switch (uniform.type) { case glu::TYPE_FLOAT_MAT2: GLU_CHECK_CALL(glUniformMatrix2fv(location, (GLsizei)valuesToAssign.size(), GL_FALSE, &buffer[0])); break; case glu::TYPE_FLOAT_MAT3: GLU_CHECK_CALL(glUniformMatrix3fv(location, (GLsizei)valuesToAssign.size(), GL_FALSE, &buffer[0])); break; case glu::TYPE_FLOAT_MAT4: GLU_CHECK_CALL(glUniformMatrix4fv(location, (GLsizei)valuesToAssign.size(), GL_FALSE, &buffer[0])); break; default: DE_ASSERT(false); } } else if (glu::isDataTypeIntOrIVec(valuesToAssign[0].type)) { if (assignByValue) { const deInt32* const ptr = &valuesToAssign[0].val.intV[0]; switch (typeSize) { case 1: GLU_CHECK_CALL(glUniform1i(location, ptr[0])); break; case 2: GLU_CHECK_CALL(glUniform2i(location, ptr[0], ptr[1])); break; case 3: GLU_CHECK_CALL(glUniform3i(location, ptr[0], ptr[1], ptr[2])); break; case 4: GLU_CHECK_CALL(glUniform4i(location, ptr[0], ptr[1], ptr[2], ptr[3])); break; default: DE_ASSERT(false); } } else { vector buffer(valuesToAssign.size() * typeSize); for (int i = 0; i < (int)buffer.size(); i++) buffer[i] = valuesToAssign[i / typeSize].val.intV[i % typeSize]; DE_STATIC_ASSERT(sizeof(GLint) == sizeof(buffer[0])); switch (typeSize) { case 1: GLU_CHECK_CALL(glUniform1iv(location, (GLsizei)valuesToAssign.size(), &buffer[0])); break; case 2: GLU_CHECK_CALL(glUniform2iv(location, (GLsizei)valuesToAssign.size(), &buffer[0])); break; case 3: GLU_CHECK_CALL(glUniform3iv(location, (GLsizei)valuesToAssign.size(), &buffer[0])); break; case 4: GLU_CHECK_CALL(glUniform4iv(location, (GLsizei)valuesToAssign.size(), &buffer[0])); break; default: DE_ASSERT(false); } } } else if (glu::isDataTypeSampler(valuesToAssign[0].type)) { if (assignByValue) GLU_CHECK_CALL(glUniform1i(location, uniform.finalValue.val.samplerV.unit)); else { const GLint unit = uniform.finalValue.val.samplerV.unit; GLU_CHECK_CALL(glUniform1iv(location, (GLsizei)valuesToAssign.size(), &unit)); } } else DE_ASSERT(false); } } bool UniformCase::compareUniformValues (const vector& values, const vector& basicUniforms) { TestLog& log = m_testCtx.getLog(); bool success = true; for (int unifNdx = 0; unifNdx < (int)basicUniforms.size(); unifNdx++) { const BasicUniform& uniform = basicUniforms[unifNdx]; const VarValue& unifValue = values[unifNdx]; log << TestLog::Message << "// Checking uniform " << uniform.name << TestLog::EndMessage; if (unifValue.type == glu::TYPE_INVALID) // This happens when glGetUniformLocation() returned -1. continue; if (!apiVarValueEquals(unifValue, uniform.finalValue)) { log << TestLog::Message << "// FAILURE: value obtained with glGetUniform*() for uniform " << uniform.name << " differs from value set with glUniform*()" << TestLog::EndMessage; success = false; } } return success; } bool UniformCase::renderTest (const vector& basicUniforms, const ShaderProgram& program, Random& rnd) { TestLog& log = m_testCtx.getLog(); const tcu::RenderTarget& renderTarget = m_context.getRenderTarget(); const int viewportW = de::min(renderTarget.getWidth(), MAX_RENDER_WIDTH); const int viewportH = de::min(renderTarget.getHeight(), MAX_RENDER_HEIGHT); const int viewportX = rnd.getInt(0, renderTarget.getWidth() - viewportW); const int viewportY = rnd.getInt(0, renderTarget.getHeight() - viewportH); tcu::Surface renderedImg (viewportW, viewportH); // Assert that no two samplers of different types have the same texture unit - this is an error in GL. for (int i = 0; i < (int)basicUniforms.size(); i++) { if (glu::isDataTypeSampler(basicUniforms[i].type)) { for (int j = 0; j < i; j++) { if (glu::isDataTypeSampler(basicUniforms[j].type) && basicUniforms[i].type != basicUniforms[j].type) DE_ASSERT(basicUniforms[i].finalValue.val.samplerV.unit != basicUniforms[j].finalValue.val.samplerV.unit); } } } for (int i = 0; i < (int)basicUniforms.size(); i++) { if (glu::isDataTypeSampler(basicUniforms[i].type) && std::find(m_filledTextureUnits.begin(), m_filledTextureUnits.end(), basicUniforms[i].finalValue.val.samplerV.unit) == m_filledTextureUnits.end()) { log << TestLog::Message << "// Filling texture at unit " << apiVarValueStr(basicUniforms[i].finalValue) << " with color " << shaderVarValueStr(basicUniforms[i].finalValue) << TestLog::EndMessage; setupTexture(basicUniforms[i].finalValue); } } GLU_CHECK_CALL(glViewport(viewportX, viewportY, viewportW, viewportH)); { static const float position[] = { -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 }; static const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 }; const int posLoc = glGetAttribLocation(program.getProgram(), "a_position"); glEnableVertexAttribArray(posLoc); glVertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, &position[0]); GLU_CHECK_CALL(glDrawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(indices), GL_UNSIGNED_SHORT, &indices[0])); } glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, renderedImg.getAccess()); int numFailedPixels = 0; for (int y = 0; y < renderedImg.getHeight(); y++) { for (int x = 0; x < renderedImg.getWidth(); x++) { if (renderedImg.getPixel(x, y) != tcu::RGBA::white()) numFailedPixels += 1; } } if (numFailedPixels > 0) { log << TestLog::Image("RenderedImage", "Rendered image", renderedImg); log << TestLog::Message << "FAILURE: image comparison failed, got " << numFailedPixels << " non-white pixels" << TestLog::EndMessage; return false; } else { log << TestLog::Message << "Success: got all-white pixels (all uniforms have correct values)" << TestLog::EndMessage; return true; } } UniformCase::IterateResult UniformCase::iterate (void) { Random rnd (deStringHash(getName()) ^ (deUint32)m_context.getTestContext().getCommandLine().getBaseSeed()); TestLog& log = m_testCtx.getLog(); vector basicUniforms; vector basicUniformReportsRef; { int samplerUnitCounter = 0; for (int i = 0; i < (int)m_uniformCollection->getNumUniforms(); i++) generateBasicUniforms(basicUniforms, basicUniformReportsRef, m_uniformCollection->getUniform(i).type, m_uniformCollection->getUniform(i).name.c_str(), true, samplerUnitCounter, rnd); } const string vertexSource = generateVertexSource(basicUniforms); const string fragmentSource = generateFragmentSource(basicUniforms); const ShaderProgram program (m_context.getRenderContext(), glu::makeVtxFragSources(vertexSource, fragmentSource)); log << program; if (!program.isOk()) { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed"); return STOP; } GLU_CHECK_CALL(glUseProgram(program.getProgram())); const bool success = test(basicUniforms, basicUniformReportsRef, program, rnd); m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Passed" : "Failed"); return STOP; } class UniformInfoQueryCase : public UniformCase { public: UniformInfoQueryCase (Context& context, const char* name, const char* description, CaseShaderType shaderType, const SharedPtr& uniformCollection, deUint32 additionalFeatures = 0); bool test (const vector& basicUniforms, const vector& basicUniformReportsRef, const ShaderProgram& program, Random& rnd); }; UniformInfoQueryCase::UniformInfoQueryCase (Context& context, const char* const name, const char* const description, const CaseShaderType shaderType, const SharedPtr& uniformCollection, const deUint32 additionalFeatures) : UniformCase (context, name, description, shaderType, uniformCollection, additionalFeatures) { } bool UniformInfoQueryCase::test (const vector& basicUniforms, const vector& basicUniformReportsRef, const ShaderProgram& program, Random& rnd) { DE_UNREF(basicUniforms); DE_UNREF(rnd); const deUint32 programGL = program.getProgram(); TestLog& log = m_testCtx.getLog(); vector basicUniformReportsUniform; const ScopedLogSection section(log, "InfoGetActiveUniform", "Uniform information queries with glGetActiveUniform()"); const bool success = getActiveUniforms(basicUniformReportsUniform, basicUniformReportsRef, programGL); if (!success) return false; return true; } class UniformValueCase : public UniformCase { public: enum ValueToCheck { VALUETOCHECK_INITIAL = 0, //!< Verify the initial values of the uniforms (i.e. check that they're zero). VALUETOCHECK_ASSIGNED, //!< Assign values to uniforms with glUniform*(), and check those. VALUETOCHECK_LAST }; enum CheckMethod { CHECKMETHOD_GET_UNIFORM = 0, //!< Check values with glGetUniform*(). CHECKMETHOD_RENDER, //!< Check values by rendering with the value-checking shader. CHECKMETHOD_LAST }; enum AssignMethod { ASSIGNMETHOD_POINTER = 0, ASSIGNMETHOD_VALUE, ASSIGNMETHOD_LAST }; UniformValueCase (Context& context, const char* name, const char* description, CaseShaderType shaderType, const SharedPtr& uniformCollection, ValueToCheck valueToCheck, CheckMethod checkMethod, AssignMethod assignMethod, deUint32 additionalFeatures = 0); bool test (const vector& basicUniforms, const vector& basicUniformReportsRef, const ShaderProgram& program, Random& rnd); static const char* getValueToCheckName (ValueToCheck valueToCheck); static const char* getValueToCheckDescription (ValueToCheck valueToCheck); static const char* getCheckMethodName (CheckMethod checkMethod); static const char* getCheckMethodDescription (CheckMethod checkMethod); static const char* getAssignMethodName (AssignMethod checkMethod); static const char* getAssignMethodDescription (AssignMethod checkMethod); private: const ValueToCheck m_valueToCheck; const CheckMethod m_checkMethod; }; const char* UniformValueCase::getValueToCheckName (const ValueToCheck valueToCheck) { switch (valueToCheck) { case VALUETOCHECK_INITIAL: return "initial"; case VALUETOCHECK_ASSIGNED: return "assigned"; default: DE_ASSERT(false); return DE_NULL; } } const char* UniformValueCase::getValueToCheckDescription (const ValueToCheck valueToCheck) { switch (valueToCheck) { case VALUETOCHECK_INITIAL: return "Check initial uniform values (zeros)"; case VALUETOCHECK_ASSIGNED: return "Check assigned uniform values"; default: DE_ASSERT(false); return DE_NULL; } } const char* UniformValueCase::getCheckMethodName (const CheckMethod checkMethod) { switch (checkMethod) { case CHECKMETHOD_GET_UNIFORM: return "get_uniform"; case CHECKMETHOD_RENDER: return "render"; default: DE_ASSERT(false); return DE_NULL; } } const char* UniformValueCase::getCheckMethodDescription (const CheckMethod checkMethod) { switch (checkMethod) { case CHECKMETHOD_GET_UNIFORM: return "Verify values with glGetUniform*()"; case CHECKMETHOD_RENDER: return "Verify values by rendering"; default: DE_ASSERT(false); return DE_NULL; } } const char* UniformValueCase::getAssignMethodName (const AssignMethod assignMethod) { switch (assignMethod) { case ASSIGNMETHOD_POINTER: return "by_pointer"; case ASSIGNMETHOD_VALUE: return "by_value"; default: DE_ASSERT(false); return DE_NULL; } } const char* UniformValueCase::getAssignMethodDescription (const AssignMethod assignMethod) { switch (assignMethod) { case ASSIGNMETHOD_POINTER: return "Assign values by-pointer"; case ASSIGNMETHOD_VALUE: return "Assign values by-value"; default: DE_ASSERT(false); return DE_NULL; } } UniformValueCase::UniformValueCase (Context& context, const char* const name, const char* const description, const CaseShaderType shaderType, const SharedPtr& uniformCollection, const ValueToCheck valueToCheck, const CheckMethod checkMethod, const AssignMethod assignMethod, const deUint32 additionalFeatures) : UniformCase (context, name, description, shaderType, uniformCollection, (valueToCheck == VALUETOCHECK_INITIAL ? FEATURE_UNIFORMVALUE_ZERO : 0) | (assignMethod == ASSIGNMETHOD_VALUE ? FEATURE_UNIFORMFUNC_VALUE : 0) | additionalFeatures) , m_valueToCheck (valueToCheck) , m_checkMethod (checkMethod) { DE_ASSERT(!(assignMethod == ASSIGNMETHOD_LAST && valueToCheck == VALUETOCHECK_ASSIGNED)); } bool UniformValueCase::test (const vector& basicUniforms, const vector& basicUniformReportsRef, const ShaderProgram& program, Random& rnd) { DE_UNREF(basicUniformReportsRef); const deUint32 programGL = program.getProgram(); TestLog& log = m_testCtx.getLog(); if (m_valueToCheck == VALUETOCHECK_ASSIGNED) { const ScopedLogSection section(log, "UniformAssign", "Uniform value assignments"); assignUniforms(basicUniforms, programGL, rnd); } else DE_ASSERT(m_valueToCheck == VALUETOCHECK_INITIAL); if (m_checkMethod == CHECKMETHOD_GET_UNIFORM) { vector values; { const ScopedLogSection section(log, "GetUniforms", "Uniform value query"); const bool success = getUniforms(values, basicUniforms, program.getProgram()); if (!success) return false; } if (m_valueToCheck == VALUETOCHECK_ASSIGNED) { const ScopedLogSection section(log, "ValueCheck", "Verify that the reported values match the assigned values"); const bool success = compareUniformValues(values, basicUniforms); if (!success) return false; } else { DE_ASSERT(m_valueToCheck == VALUETOCHECK_INITIAL); const ScopedLogSection section(log, "ValueCheck", "Verify that the uniforms have correct initial values (zeros)"); const bool success = checkUniformDefaultValues(values, basicUniforms); if (!success) return false; } } else { DE_ASSERT(m_checkMethod == CHECKMETHOD_RENDER); const ScopedLogSection section(log, "RenderTest", "Render test"); const bool success = renderTest(basicUniforms, program, rnd); if (!success) return false; } return true; } class RandomUniformCase : public UniformCase { public: RandomUniformCase (Context& m_context, const char* name, const char* description, deUint32 seed); bool test (const vector& basicUniforms, const vector& basicUniformReportsRef, const ShaderProgram& program, Random& rnd); }; RandomUniformCase::RandomUniformCase (Context& context, const char* const name, const char* const description, const deUint32 seed) : UniformCase (context, name, description, seed ^ (deUint32)context.getTestContext().getCommandLine().getBaseSeed()) { } bool RandomUniformCase::test (const vector& basicUniforms, const vector& basicUniformReportsRef, const ShaderProgram& program, Random& rnd) { // \note Different sampler types may not be bound to same unit when rendering. const bool renderingPossible = (m_features & FEATURE_UNIFORMVALUE_ZERO) == 0 || !m_uniformCollection->containsSeveralSamplerTypes(); bool performGetActiveUniforms = rnd.getBool(); const bool performGetUniforms = rnd.getBool(); const bool performCheckUniformDefaultValues = performGetUniforms && rnd.getBool(); const bool performAssignUniforms = rnd.getBool(); const bool performCompareUniformValues = performGetUniforms && performAssignUniforms && rnd.getBool(); const bool performRenderTest = renderingPossible && performAssignUniforms && rnd.getBool(); const deUint32 programGL = program.getProgram(); TestLog& log = m_testCtx.getLog(); if (!(performGetActiveUniforms || performGetUniforms || performCheckUniformDefaultValues || performAssignUniforms || performCompareUniformValues || performRenderTest)) performGetActiveUniforms = true; // Do something at least. #define PERFORM_AND_CHECK(CALL, SECTION_NAME, SECTION_DESCRIPTION) \ do \ { \ const ScopedLogSection section(log, (SECTION_NAME), (SECTION_DESCRIPTION)); \ const bool success = (CALL); \ if (!success) \ return false; \ } while (false) if (performGetActiveUniforms) { vector reportsUniform; PERFORM_AND_CHECK(getActiveUniforms(reportsUniform, basicUniformReportsRef, programGL), "InfoGetActiveUniform", "Uniform information queries with glGetActiveUniform()"); } { vector uniformDefaultValues; if (performGetUniforms) PERFORM_AND_CHECK(getUniforms(uniformDefaultValues, basicUniforms, programGL), "GetUniformDefaults", "Uniform default value query"); if (performCheckUniformDefaultValues) PERFORM_AND_CHECK(checkUniformDefaultValues(uniformDefaultValues, basicUniforms), "DefaultValueCheck", "Verify that the uniforms have correct initial values (zeros)"); } { vector uniformValues; if (performAssignUniforms) { const ScopedLogSection section(log, "UniformAssign", "Uniform value assignments"); assignUniforms(basicUniforms, programGL, rnd); } if (performCompareUniformValues) { PERFORM_AND_CHECK(getUniforms(uniformValues, basicUniforms, programGL), "GetUniforms", "Uniform value query"); PERFORM_AND_CHECK(compareUniformValues(uniformValues, basicUniforms), "ValueCheck", "Verify that the reported values match the assigned values"); } } if (performRenderTest) PERFORM_AND_CHECK(renderTest(basicUniforms, program, rnd), "RenderTest", "Render test"); #undef PERFORM_AND_CHECK return true; } UniformApiTests::UniformApiTests (Context& context) : TestCaseGroup(context, "uniform_api", "Uniform API Tests") { } UniformApiTests::~UniformApiTests (void) { } namespace { // \note Although this is only used in UniformApiTest::init, it needs to be defined here as it's used as a template argument. struct UniformCollectionCase { string namePrefix; SharedPtr uniformCollection; UniformCollectionCase (const char* const name, const UniformCollection* uniformCollection_) : namePrefix (name ? name + string("_") : "") , uniformCollection (uniformCollection_) { } }; } // anonymous void UniformApiTests::init (void) { // Generate sets of UniformCollections that are used by several cases. enum { UNIFORMCOLLECTIONS_BASIC = 0, UNIFORMCOLLECTIONS_BASIC_ARRAY, UNIFORMCOLLECTIONS_BASIC_STRUCT, UNIFORMCOLLECTIONS_STRUCT_IN_ARRAY, UNIFORMCOLLECTIONS_ARRAY_IN_STRUCT, UNIFORMCOLLECTIONS_NESTED_STRUCTS_ARRAYS, UNIFORMCOLLECTIONS_MULTIPLE_BASIC, UNIFORMCOLLECTIONS_MULTIPLE_BASIC_ARRAY, UNIFORMCOLLECTIONS_MULTIPLE_NESTED_STRUCTS_ARRAYS, UNIFORMCOLLECTIONS_LAST }; struct UniformCollectionGroup { string name; vector cases; } defaultUniformCollections[UNIFORMCOLLECTIONS_LAST]; defaultUniformCollections[UNIFORMCOLLECTIONS_BASIC].name = "basic"; defaultUniformCollections[UNIFORMCOLLECTIONS_BASIC_ARRAY].name = "basic_array"; defaultUniformCollections[UNIFORMCOLLECTIONS_BASIC_STRUCT].name = "basic_struct"; defaultUniformCollections[UNIFORMCOLLECTIONS_STRUCT_IN_ARRAY].name = "struct_in_array"; defaultUniformCollections[UNIFORMCOLLECTIONS_ARRAY_IN_STRUCT].name = "array_in_struct"; defaultUniformCollections[UNIFORMCOLLECTIONS_NESTED_STRUCTS_ARRAYS].name = "nested_structs_arrays"; defaultUniformCollections[UNIFORMCOLLECTIONS_MULTIPLE_BASIC].name = "multiple_basic"; defaultUniformCollections[UNIFORMCOLLECTIONS_MULTIPLE_BASIC_ARRAY].name = "multiple_basic_array"; defaultUniformCollections[UNIFORMCOLLECTIONS_MULTIPLE_NESTED_STRUCTS_ARRAYS].name = "multiple_nested_structs_arrays"; for (int dataTypeNdx = 0; dataTypeNdx < DE_LENGTH_OF_ARRAY(s_testDataTypes); dataTypeNdx++) { const glu::DataType dataType = s_testDataTypes[dataTypeNdx]; const char* const typeName = glu::getDataTypeName(dataType); defaultUniformCollections[UNIFORMCOLLECTIONS_BASIC].cases.push_back(UniformCollectionCase(typeName, UniformCollection::basic(dataType))); if (glu::isDataTypeScalar(dataType) || (glu::isDataTypeVector(dataType) && glu::getDataTypeScalarSize(dataType) == 4) || dataType == glu::TYPE_FLOAT_MAT4 || dataType == glu::TYPE_SAMPLER_2D) defaultUniformCollections[UNIFORMCOLLECTIONS_BASIC_ARRAY].cases.push_back(UniformCollectionCase(typeName, UniformCollection::basicArray(dataType))); if (glu::isDataTypeScalar(dataType) || dataType == glu::TYPE_FLOAT_MAT4 || dataType == glu::TYPE_SAMPLER_2D) { const glu::DataType secondDataType = glu::isDataTypeScalar(dataType) ? glu::getDataTypeVector(dataType, 4) : dataType == glu::TYPE_FLOAT_MAT4 ? glu::TYPE_FLOAT_MAT2 : dataType == glu::TYPE_SAMPLER_2D ? glu::TYPE_SAMPLER_CUBE : glu::TYPE_LAST; DE_ASSERT(secondDataType != glu::TYPE_LAST); const char* const secondTypeName = glu::getDataTypeName(secondDataType); const string name = string("") + typeName + "_" + secondTypeName; defaultUniformCollections[UNIFORMCOLLECTIONS_BASIC_STRUCT].cases.push_back (UniformCollectionCase(name.c_str(), UniformCollection::basicStruct(dataType, secondDataType, false))); defaultUniformCollections[UNIFORMCOLLECTIONS_ARRAY_IN_STRUCT].cases.push_back (UniformCollectionCase(name.c_str(), UniformCollection::basicStruct(dataType, secondDataType, true))); defaultUniformCollections[UNIFORMCOLLECTIONS_STRUCT_IN_ARRAY].cases.push_back (UniformCollectionCase(name.c_str(), UniformCollection::structInArray(dataType, secondDataType, false))); defaultUniformCollections[UNIFORMCOLLECTIONS_NESTED_STRUCTS_ARRAYS].cases.push_back (UniformCollectionCase(name.c_str(), UniformCollection::nestedArraysStructs(dataType, secondDataType))); } } defaultUniformCollections[UNIFORMCOLLECTIONS_MULTIPLE_BASIC].cases.push_back (UniformCollectionCase(DE_NULL, UniformCollection::multipleBasic())); defaultUniformCollections[UNIFORMCOLLECTIONS_MULTIPLE_BASIC_ARRAY].cases.push_back (UniformCollectionCase(DE_NULL, UniformCollection::multipleBasicArray())); defaultUniformCollections[UNIFORMCOLLECTIONS_MULTIPLE_NESTED_STRUCTS_ARRAYS].cases.push_back (UniformCollectionCase(DE_NULL, UniformCollection::multipleNestedArraysStructs())); // Info-query cases (check info returned by e.g. glGetActiveUniforms()). { TestCaseGroup* const infoQueryGroup = new TestCaseGroup(m_context, "info_query", "Test glGetActiveUniform()"); addChild(infoQueryGroup); for (int collectionGroupNdx = 0; collectionGroupNdx < (int)UNIFORMCOLLECTIONS_LAST; collectionGroupNdx++) { const UniformCollectionGroup& collectionGroup = defaultUniformCollections[collectionGroupNdx]; TestCaseGroup* const collectionTestGroup = new TestCaseGroup(m_context, collectionGroup.name.c_str(), ""); infoQueryGroup->addChild(collectionTestGroup); for (int collectionNdx = 0; collectionNdx < (int)collectionGroup.cases.size(); collectionNdx++) { const UniformCollectionCase& collectionCase = collectionGroup.cases[collectionNdx]; for (int shaderType = 0; shaderType < (int)CASESHADERTYPE_LAST; shaderType++) { const string name = collectionCase.namePrefix + getCaseShaderTypeName((CaseShaderType)shaderType); const SharedPtr& uniformCollection = collectionCase.uniformCollection; collectionTestGroup->addChild(new UniformInfoQueryCase(m_context, name.c_str(), "", (CaseShaderType)shaderType, uniformCollection)); } } } // Info-querying cases when unused uniforms are present. { TestCaseGroup* const unusedUniformsGroup = new TestCaseGroup(m_context, "unused_uniforms", "Test with unused uniforms"); infoQueryGroup->addChild(unusedUniformsGroup); const UniformCollectionGroup& collectionGroup = defaultUniformCollections[UNIFORMCOLLECTIONS_ARRAY_IN_STRUCT]; for (int collectionNdx = 0; collectionNdx < (int)collectionGroup.cases.size(); collectionNdx++) { const UniformCollectionCase& collectionCase = collectionGroup.cases[collectionNdx]; const string collName = collectionCase.namePrefix; const SharedPtr& uniformCollection = collectionCase.uniformCollection; for (int shaderType = 0; shaderType < (int)CASESHADERTYPE_LAST; shaderType++) { const string name = collName + getCaseShaderTypeName((CaseShaderType)shaderType); unusedUniformsGroup->addChild(new UniformInfoQueryCase(m_context, name.c_str(), "", (CaseShaderType)shaderType, uniformCollection, UniformCase::FEATURE_UNIFORMUSAGE_EVERY_OTHER | UniformCase::FEATURE_ARRAYUSAGE_ONLY_MIDDLE_INDEX)); } } } } // Cases testing uniform values. { TestCaseGroup* const valueGroup = new TestCaseGroup(m_context, "value", "Uniform value tests"); addChild(valueGroup); // Cases checking uniforms' initial values (all must be zeros), with glGetUniform*() or by rendering. { TestCaseGroup* const initialValuesGroup = new TestCaseGroup(m_context, UniformValueCase::getValueToCheckName(UniformValueCase::VALUETOCHECK_INITIAL), UniformValueCase::getValueToCheckDescription(UniformValueCase::VALUETOCHECK_INITIAL)); valueGroup->addChild(initialValuesGroup); for (int checkMethodI = 0; checkMethodI < (int)UniformValueCase::CHECKMETHOD_LAST; checkMethodI++) { const UniformValueCase::CheckMethod checkMethod = (UniformValueCase::CheckMethod)checkMethodI; TestCaseGroup* const checkMethodGroup = new TestCaseGroup(m_context, UniformValueCase::getCheckMethodName(checkMethod), UniformValueCase::getCheckMethodDescription(checkMethod)); initialValuesGroup->addChild(checkMethodGroup); for (int collectionGroupNdx = 0; collectionGroupNdx < (int)UNIFORMCOLLECTIONS_LAST; collectionGroupNdx++) { const UniformCollectionGroup& collectionGroup = defaultUniformCollections[collectionGroupNdx]; TestCaseGroup* const collectionTestGroup = new TestCaseGroup(m_context, collectionGroup.name.c_str(), ""); checkMethodGroup->addChild(collectionTestGroup); for (int collectionNdx = 0; collectionNdx < (int)collectionGroup.cases.size(); collectionNdx++) { const UniformCollectionCase& collectionCase = collectionGroup.cases[collectionNdx]; const string collName = collectionCase.namePrefix; const SharedPtr& uniformCollection = collectionCase.uniformCollection; const bool containsBooleans = uniformCollection->containsMatchingBasicType(glu::isDataTypeBoolOrBVec); const bool varyBoolApiType = checkMethod == UniformValueCase::CHECKMETHOD_GET_UNIFORM && containsBooleans && (collectionGroupNdx == UNIFORMCOLLECTIONS_BASIC || collectionGroupNdx == UNIFORMCOLLECTIONS_BASIC_ARRAY); const int numBoolVariations = varyBoolApiType ? 2 : 1; if (checkMethod == UniformValueCase::CHECKMETHOD_RENDER && uniformCollection->containsSeveralSamplerTypes()) continue; // \note Samplers' initial API values (i.e. their texture units) are 0, and no two samplers of different types shall have same unit when rendering. for (int booleanTypeI = 0; booleanTypeI < numBoolVariations; booleanTypeI++) { const deUint32 booleanTypeFeat = booleanTypeI == 1 ? UniformCase::FEATURE_BOOLEANAPITYPE_INT : 0; const char* const booleanTypeName = booleanTypeI == 1 ? "int" : "float"; const string nameWithApiType = varyBoolApiType ? collName + "api_" + booleanTypeName + "_" : collName; for (int shaderType = 0; shaderType < (int)CASESHADERTYPE_LAST; shaderType++) { const string name = nameWithApiType + getCaseShaderTypeName((CaseShaderType)shaderType); collectionTestGroup->addChild(new UniformValueCase(m_context, name.c_str(), "", (CaseShaderType)shaderType, uniformCollection, UniformValueCase::VALUETOCHECK_INITIAL, checkMethod, UniformValueCase::ASSIGNMETHOD_LAST, booleanTypeFeat)); } } } } } } // Cases that first assign values to each uniform, then check the values with glGetUniform*() or by rendering. { TestCaseGroup* const assignedValuesGroup = new TestCaseGroup(m_context, UniformValueCase::getValueToCheckName(UniformValueCase::VALUETOCHECK_ASSIGNED), UniformValueCase::getValueToCheckDescription(UniformValueCase::VALUETOCHECK_ASSIGNED)); valueGroup->addChild(assignedValuesGroup); for (int assignMethodI = 0; assignMethodI < (int)UniformValueCase::ASSIGNMETHOD_LAST; assignMethodI++) { const UniformValueCase::AssignMethod assignMethod = (UniformValueCase::AssignMethod)assignMethodI; TestCaseGroup* const assignMethodGroup = new TestCaseGroup(m_context, UniformValueCase::getAssignMethodName(assignMethod), UniformValueCase::getAssignMethodDescription(assignMethod)); assignedValuesGroup->addChild(assignMethodGroup); for (int checkMethodI = 0; checkMethodI < (int)UniformValueCase::CHECKMETHOD_LAST; checkMethodI++) { const UniformValueCase::CheckMethod checkMethod = (UniformValueCase::CheckMethod)checkMethodI; TestCaseGroup* const checkMethodGroup = new TestCaseGroup(m_context, UniformValueCase::getCheckMethodName(checkMethod), UniformValueCase::getCheckMethodDescription(checkMethod)); assignMethodGroup->addChild(checkMethodGroup); for (int collectionGroupNdx = 0; collectionGroupNdx < (int)UNIFORMCOLLECTIONS_LAST; collectionGroupNdx++) { const int numArrayFirstElemNameCases = checkMethod == UniformValueCase::CHECKMETHOD_GET_UNIFORM && collectionGroupNdx == UNIFORMCOLLECTIONS_BASIC_ARRAY ? 2 : 1; for (int referToFirstArrayElemWithoutIndexI = 0; referToFirstArrayElemWithoutIndexI < numArrayFirstElemNameCases; referToFirstArrayElemWithoutIndexI++) { const UniformCollectionGroup& collectionGroup = defaultUniformCollections[collectionGroupNdx]; const string collectionGroupName = collectionGroup.name + (referToFirstArrayElemWithoutIndexI == 0 ? "" : "_first_elem_without_brackets"); TestCaseGroup* collectionTestGroup = DE_NULL; for (int collectionNdx = 0; collectionNdx < (int)collectionGroup.cases.size(); collectionNdx++) { const UniformCollectionCase& collectionCase = collectionGroup.cases[collectionNdx]; const string collName = collectionCase.namePrefix; const SharedPtr& uniformCollection = collectionCase.uniformCollection; const bool containsBooleans = uniformCollection->containsMatchingBasicType(glu::isDataTypeBoolOrBVec); const bool varyBoolApiType = checkMethod == UniformValueCase::CHECKMETHOD_GET_UNIFORM && containsBooleans && (collectionGroupNdx == UNIFORMCOLLECTIONS_BASIC || collectionGroupNdx == UNIFORMCOLLECTIONS_BASIC_ARRAY); const int numBoolVariations = varyBoolApiType ? 2 : 1; const bool containsMatrices = uniformCollection->containsMatchingBasicType(glu::isDataTypeMatrix); if (containsMatrices && assignMethod != UniformValueCase::ASSIGNMETHOD_POINTER) continue; for (int booleanTypeI = 0; booleanTypeI < numBoolVariations; booleanTypeI++) { const deUint32 booleanTypeFeat = booleanTypeI == 1 ? UniformCase::FEATURE_BOOLEANAPITYPE_INT : 0; const char* const booleanTypeName = booleanTypeI == 1 ? "int" : "float"; const string nameWithBoolType = varyBoolApiType ? collName + "api_" + booleanTypeName + "_" : collName; const string nameWithMatrixType = nameWithBoolType; for (int shaderType = 0; shaderType < (int)CASESHADERTYPE_LAST; shaderType++) { const string name = nameWithMatrixType + getCaseShaderTypeName((CaseShaderType)shaderType); const deUint32 arrayFirstElemNameNoIndexFeat = referToFirstArrayElemWithoutIndexI == 0 ? 0 : UniformCase::FEATURE_ARRAY_FIRST_ELEM_NAME_NO_INDEX; // skip empty groups by creating groups on demand if (!collectionTestGroup) { collectionTestGroup = new TestCaseGroup(m_context, collectionGroupName.c_str(), ""); checkMethodGroup->addChild(collectionTestGroup); } collectionTestGroup->addChild(new UniformValueCase(m_context, name.c_str(), "", (CaseShaderType)shaderType, uniformCollection, UniformValueCase::VALUETOCHECK_ASSIGNED, checkMethod, assignMethod, booleanTypeFeat | arrayFirstElemNameNoIndexFeat)); } } } } } } } // Cases assign multiple basic-array elements with one glUniform*v() (i.e. the count parameter is bigger than 1). { static const struct { UniformCase::Feature arrayAssignMode; const char* name; const char* description; } arrayAssignGroups[] = { { UniformCase::FEATURE_ARRAYASSIGN_FULL, "basic_array_assign_full", "Assign entire basic-type arrays per glUniform*v() call" }, { UniformCase::FEATURE_ARRAYASSIGN_BLOCKS_OF_TWO, "basic_array_assign_partial", "Assign two elements of a basic-type array per glUniform*v() call" } }; for (int arrayAssignGroupNdx = 0; arrayAssignGroupNdx < DE_LENGTH_OF_ARRAY(arrayAssignGroups); arrayAssignGroupNdx++) { UniformCase::Feature arrayAssignMode = arrayAssignGroups[arrayAssignGroupNdx].arrayAssignMode; const char* const groupName = arrayAssignGroups[arrayAssignGroupNdx].name; const char* const groupDesc = arrayAssignGroups[arrayAssignGroupNdx].description; TestCaseGroup* const curArrayAssignGroup = new TestCaseGroup(m_context, groupName, groupDesc); assignedValuesGroup->addChild(curArrayAssignGroup); static const int basicArrayCollectionGroups[] = { UNIFORMCOLLECTIONS_BASIC_ARRAY, UNIFORMCOLLECTIONS_ARRAY_IN_STRUCT, UNIFORMCOLLECTIONS_MULTIPLE_BASIC_ARRAY }; for (int collectionGroupNdx = 0; collectionGroupNdx < DE_LENGTH_OF_ARRAY(basicArrayCollectionGroups); collectionGroupNdx++) { const UniformCollectionGroup& collectionGroup = defaultUniformCollections[basicArrayCollectionGroups[collectionGroupNdx]]; TestCaseGroup* const collectionTestGroup = new TestCaseGroup(m_context, collectionGroup.name.c_str(), ""); curArrayAssignGroup->addChild(collectionTestGroup); for (int collectionNdx = 0; collectionNdx < (int)collectionGroup.cases.size(); collectionNdx++) { const UniformCollectionCase& collectionCase = collectionGroup.cases[collectionNdx]; const string collName = collectionCase.namePrefix; const SharedPtr& uniformCollection = collectionCase.uniformCollection; for (int shaderType = 0; shaderType < (int)CASESHADERTYPE_LAST; shaderType++) { const string name = collName + getCaseShaderTypeName((CaseShaderType)shaderType); collectionTestGroup->addChild(new UniformValueCase(m_context, name.c_str(), "", (CaseShaderType)shaderType, uniformCollection, UniformValueCase::VALUETOCHECK_ASSIGNED, UniformValueCase::CHECKMETHOD_GET_UNIFORM, UniformValueCase::ASSIGNMETHOD_POINTER, arrayAssignMode)); } } } } } // Value checking cases when unused uniforms are present. { TestCaseGroup* const unusedUniformsGroup = new TestCaseGroup(m_context, "unused_uniforms", "Test with unused uniforms"); assignedValuesGroup->addChild(unusedUniformsGroup); const UniformCollectionGroup& collectionGroup = defaultUniformCollections[UNIFORMCOLLECTIONS_ARRAY_IN_STRUCT]; for (int collectionNdx = 0; collectionNdx < (int)collectionGroup.cases.size(); collectionNdx++) { const UniformCollectionCase& collectionCase = collectionGroup.cases[collectionNdx]; const string collName = collectionCase.namePrefix; const SharedPtr& uniformCollection = collectionCase.uniformCollection; for (int shaderType = 0; shaderType < (int)CASESHADERTYPE_LAST; shaderType++) { const string name = collName + getCaseShaderTypeName((CaseShaderType)shaderType); unusedUniformsGroup->addChild(new UniformValueCase(m_context, name.c_str(), "", (CaseShaderType)shaderType, uniformCollection, UniformValueCase::VALUETOCHECK_ASSIGNED, UniformValueCase::CHECKMETHOD_GET_UNIFORM, UniformValueCase::ASSIGNMETHOD_POINTER, UniformCase::FEATURE_ARRAYUSAGE_ONLY_MIDDLE_INDEX | UniformCase::FEATURE_UNIFORMUSAGE_EVERY_OTHER)); } } } } } // Random cases. { const int numRandomCases = 100; TestCaseGroup* const randomGroup = new TestCaseGroup(m_context, "random", "Random cases"); addChild(randomGroup); for (int ndx = 0; ndx < numRandomCases; ndx++) randomGroup->addChild(new RandomUniformCase(m_context, de::toString(ndx).c_str(), "", (deUint32)ndx)); } } } // Functional } // gles2 } // deqp