| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES 3.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 ES2. |
| * Utilities to glshared? |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es3fUniformApiTests.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 "deString.h" |
| #include "deSharedPtr.hpp" |
| #include "deMemory.h" |
| |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| |
| #include <set> |
| #include <cstring> |
| |
| using namespace glw; |
| |
| namespace deqp |
| { |
| namespace gles3 |
| { |
| namespace Functional |
| { |
| |
| using de::Random; |
| using de::SharedPtr; |
| using glu::ShaderProgram; |
| using glu::StructType; |
| using std::string; |
| using std::vector; |
| using tcu::ScopedLogSection; |
| using tcu::TestLog; |
| |
| 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_MAT2X3, glu::TYPE_FLOAT_MAT2X4, glu::TYPE_FLOAT_MAT3X2, |
| glu::TYPE_FLOAT_MAT3, glu::TYPE_FLOAT_MAT3X4, glu::TYPE_FLOAT_MAT4X2, glu::TYPE_FLOAT_MAT4X3, |
| glu::TYPE_FLOAT_MAT4, |
| |
| glu::TYPE_INT, glu::TYPE_INT_VEC2, glu::TYPE_INT_VEC3, glu::TYPE_INT_VEC4, |
| |
| glu::TYPE_UINT, glu::TYPE_UINT_VEC2, glu::TYPE_UINT_VEC3, glu::TYPE_UINT_VEC4, |
| |
| glu::TYPE_BOOL, glu::TYPE_BOOL_VEC2, glu::TYPE_BOOL_VEC3, glu::TYPE_BOOL_VEC4, |
| |
| glu::TYPE_SAMPLER_2D, glu::TYPE_SAMPLER_CUBE |
| // \note We don't test all sampler types here. |
| }; |
| |
| static inline int getGLInt(const glw::Functions &funcs, const uint32_t 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: |
| case glu::TYPE_INT_SAMPLER_2D: |
| case glu::TYPE_UINT_SAMPLER_2D: |
| return 2; |
| |
| case glu::TYPE_SAMPLER_3D: |
| case glu::TYPE_INT_SAMPLER_3D: |
| case glu::TYPE_UINT_SAMPLER_3D: |
| case glu::TYPE_SAMPLER_2D_SHADOW: |
| case glu::TYPE_SAMPLER_2D_ARRAY: |
| case glu::TYPE_INT_SAMPLER_2D_ARRAY: |
| case glu::TYPE_UINT_SAMPLER_2D_ARRAY: |
| case glu::TYPE_SAMPLER_CUBE: |
| case glu::TYPE_INT_SAMPLER_CUBE: |
| case glu::TYPE_UINT_SAMPLER_CUBE: |
| return 3; |
| |
| case glu::TYPE_SAMPLER_CUBE_SHADOW: |
| case glu::TYPE_SAMPLER_2D_ARRAY_SHADOW: |
| return 4; |
| |
| default: |
| DE_ASSERT(false); |
| return 0; |
| } |
| } |
| |
| static inline glu::DataType getSamplerLookupReturnType(const glu::DataType type) |
| { |
| switch (type) |
| { |
| case glu::TYPE_SAMPLER_2D: |
| case glu::TYPE_SAMPLER_CUBE: |
| case glu::TYPE_SAMPLER_2D_ARRAY: |
| case glu::TYPE_SAMPLER_3D: |
| return glu::TYPE_FLOAT_VEC4; |
| |
| case glu::TYPE_UINT_SAMPLER_2D: |
| case glu::TYPE_UINT_SAMPLER_CUBE: |
| case glu::TYPE_UINT_SAMPLER_2D_ARRAY: |
| case glu::TYPE_UINT_SAMPLER_3D: |
| return glu::TYPE_UINT_VEC4; |
| |
| case glu::TYPE_INT_SAMPLER_2D: |
| case glu::TYPE_INT_SAMPLER_CUBE: |
| case glu::TYPE_INT_SAMPLER_2D_ARRAY: |
| case glu::TYPE_INT_SAMPLER_3D: |
| return glu::TYPE_INT_VEC4; |
| |
| case glu::TYPE_SAMPLER_2D_SHADOW: |
| case glu::TYPE_SAMPLER_CUBE_SHADOW: |
| case glu::TYPE_SAMPLER_2D_ARRAY_SHADOW: |
| return glu::TYPE_FLOAT; |
| |
| default: |
| DE_ASSERT(false); |
| return glu::TYPE_LAST; |
| } |
| } |
| |
| template <glu::DataType T> |
| static bool dataTypeEquals(const glu::DataType t) |
| { |
| return t == T; |
| } |
| |
| template <int N> |
| 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<glu::DataType> &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<const StructType *> &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. |
| int32_t intV[4]; |
| uint32_t uintV[4]; |
| bool boolV[4]; |
| struct |
| { |
| int unit; |
| union |
| { |
| float floatV[4]; |
| int32_t intV[4]; |
| uint32_t uintV[4]; |
| } fillColor; |
| } 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<glu::DataType> getSamplerTypes(void) const |
| { |
| vector<glu::DataType> 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_UINT_VEC4, |
| 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 uint32_t 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<const StructType *> 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 = 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<Uniform> m_uniforms; |
| vector<const StructType *> m_structTypes; |
| }; |
| |
| } // namespace |
| |
| static VarValue getSamplerFillValue(const VarValue &sampler) |
| { |
| DE_ASSERT(glu::isDataTypeSampler(sampler.type)); |
| |
| VarValue result; |
| result.type = getSamplerLookupReturnType(sampler.type); |
| |
| switch (result.type) |
| { |
| case glu::TYPE_FLOAT_VEC4: |
| for (int i = 0; i < 4; i++) |
| result.val.floatV[i] = sampler.val.samplerV.fillColor.floatV[i]; |
| break; |
| case glu::TYPE_UINT_VEC4: |
| for (int i = 0; i < 4; i++) |
| result.val.uintV[i] = sampler.val.samplerV.fillColor.uintV[i]; |
| break; |
| case glu::TYPE_INT_VEC4: |
| for (int i = 0; i < 4; i++) |
| result.val.intV[i] = sampler.val.samplerV.fillColor.intV[i]; |
| break; |
| case glu::TYPE_FLOAT: |
| result.val.floatV[0] = sampler.val.samplerV.fillColor.floatV[0]; |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| |
| 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 glu::DataType getDataTypeTransposedMatrix(const glu::DataType original) |
| { |
| return glu::getDataTypeMatrix(glu::getDataTypeMatrixNumRows(original), glu::getDataTypeMatrixNumColumns(original)); |
| } |
| |
| static VarValue getTransposeMatrix(const VarValue &original) |
| { |
| DE_ASSERT(glu::isDataTypeMatrix(original.type)); |
| |
| const int rows = glu::getDataTypeMatrixNumRows(original.type); |
| const int cols = glu::getDataTypeMatrixNumColumns(original.type); |
| VarValue result; |
| result.type = getDataTypeTransposedMatrix(original.type); |
| |
| for (int i = 0; i < rows; i++) |
| for (int j = 0; j < cols; j++) |
| result.val.floatV[i * cols + j] = original.val.floatV[j * rows + i]; |
| |
| 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::isDataTypeUintOrUVec((value.type))) |
| result << de::toString(value.val.uintV[i]) << "u"; |
| 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::isDataTypeUintOrUVec((value.type))) |
| result << de::toString(value.val.uintV[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::isDataTypeUintOrUVec(type)) |
| { |
| for (int i = 0; i < numElems; i++) |
| result.val.uintV[i] = (uint32_t)rnd.getInt(0, 10); |
| } |
| else if (glu::isDataTypeBoolOrBVec(type)) |
| { |
| for (int i = 0; i < numElems; i++) |
| result.val.boolV[i] = rnd.getBool(); |
| } |
| else if (glu::isDataTypeSampler(type)) |
| { |
| const glu::DataType texResultType = getSamplerLookupReturnType(type); |
| const glu::DataType texResultScalarType = glu::getDataTypeScalarType(texResultType); |
| const int texResultNumDims = glu::getDataTypeScalarSize(texResultType); |
| |
| result.val.samplerV.unit = samplerUnit; |
| |
| for (int i = 0; i < texResultNumDims; i++) |
| { |
| switch (texResultScalarType) |
| { |
| case glu::TYPE_FLOAT: |
| result.val.samplerV.fillColor.floatV[i] = rnd.getFloat(0.0f, 1.0f); |
| break; |
| case glu::TYPE_INT: |
| result.val.samplerV.fillColor.intV[i] = rnd.getInt(-10, 10); |
| break; |
| case glu::TYPE_UINT: |
| result.val.samplerV.fillColor.uintV[i] = (uint32_t)rnd.getInt(0, 10); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| } |
| 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::isDataTypeUintOrUVec(type)) |
| { |
| for (int i = 0; i < numElems; i++) |
| result.val.uintV[i] = 0u; |
| } |
| else if (glu::isDataTypeBoolOrBVec(type)) |
| { |
| for (int i = 0; i < numElems; i++) |
| result.val.boolV[i] = false; |
| } |
| else if (glu::isDataTypeSampler(type)) |
| { |
| const glu::DataType texResultType = getSamplerLookupReturnType(type); |
| const glu::DataType texResultScalarType = glu::getDataTypeScalarType(texResultType); |
| const int texResultNumDims = glu::getDataTypeScalarSize(texResultType); |
| |
| result.val.samplerV.unit = 0; |
| |
| for (int i = 0; i < texResultNumDims; i++) |
| { |
| switch (texResultScalarType) |
| { |
| case glu::TYPE_FLOAT: |
| result.val.samplerV.fillColor.floatV[i] = 0.12f * (float)i; |
| break; |
| case glu::TYPE_INT: |
| result.val.samplerV.fillColor.intV[i] = -2 + i; |
| break; |
| case glu::TYPE_UINT: |
| result.val.samplerV.fillColor.uintV[i] = 4 + i; |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| } |
| 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::isDataTypeUintOrUVec(a.type)) |
| { |
| for (int i = 0; i < size; i++) |
| if (a.val.uintV[i] != b.val.uintV[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_UINT: |
| for (int i = 0; i < size; i++) |
| { |
| if (boolValue.val.boolV[i]) |
| result.val.uintV[i] = rnd.getInt(1, 10); |
| else |
| result.val.uintV[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 uint32_t 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, |
| |
| // MATRIXMODE_ROWMAJOR: pass matrices to GL in row major form. If not given, use column major. |
| FEATURE_MATRIXMODE_ROWMAJOR = 1 << 2, |
| |
| // ARRAYASSIGN: how basic-type arrays are assigned with glUniform*(). If none given, assign each element of an array separately. |
| FEATURE_ARRAYASSIGN_FULL = 1 << 3, //!< Assign all elements of an array with one glUniform*(). |
| FEATURE_ARRAYASSIGN_BLOCKS_OF_TWO = 1 << 4, //!< 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 << 5, |
| |
| // BOOLEANAPITYPE: type used to pass booleans to and from GL api. If none given, use float. |
| FEATURE_BOOLEANAPITYPE_INT = 1 << 6, |
| FEATURE_BOOLEANAPITYPE_UINT = 1 << 7, |
| |
| // UNIFORMVALUE_ZERO: use zero-valued uniforms. If not given, use random uniform values. |
| FEATURE_UNIFORMVALUE_ZERO = 1 << 8, |
| |
| // 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 << 9 |
| }; |
| |
| UniformCase(Context &context, const char *name, const char *description, CaseShaderType caseType, |
| const SharedPtr<const UniformCollection> &uniformCollection, uint32_t features); |
| UniformCase(Context &context, const char *name, const char *description, |
| uint32_t 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<BasicUniform>::const_iterator findWithName(const vector<BasicUniform> &vec, |
| const char *const name) |
| { |
| for (vector<BasicUniform>::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() or glGetActiveUniformsiv(). |
| 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() or glGetActiveUniformsiv(). |
| struct BasicUniformReportGL |
| { |
| string name; |
| int nameLength; // \note Whether this includes the null byte depends on whether it was queried with glGetActiveUniform() or glGetActiveUniformsiv(). |
| 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<BasicUniformReportGL>::const_iterator findWithName(const vector<BasicUniformReportGL> &vec, |
| const char *const name) |
| { |
| for (vector<BasicUniformReportGL>::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<BasicUniformReportGL> &dst, const vector<BasicUniformReportRef> &ref, |
| uint32_t programGL); |
| // Query info with glGetUniformIndices() + glGetActiveUniformsiv() and check validity. |
| bool getActiveUniformsiv(vector<BasicUniformReportGL> &dst, const vector<BasicUniformReportRef> &ref, |
| uint32_t programGL); |
| // Compare infos returned by glGetActiveUniform() and glGetUniformIndices() + glGetActiveUniformsiv(). |
| bool uniformVsUniformsivComparison(const vector<BasicUniformReportGL> &uniformsResult, |
| const vector<BasicUniformReportGL> &uniformsivResult); |
| // Get uniform values with glGetUniform*() and put to valuesDst. Uniforms that get -1 from glGetUniformLocation() get glu::TYPE_INVALID. |
| bool getUniforms(vector<VarValue> &valuesDst, const vector<BasicUniform> &basicUniforms, uint32_t programGL); |
| // Check that every uniform has the default (zero) value. |
| bool checkUniformDefaultValues(const vector<VarValue> &values, const vector<BasicUniform> &basicUniforms); |
| // Assign the basicUniforms[].finalValue values for uniforms. \note rnd parameter is for booleans (true can be any nonzero value). |
| void assignUniforms(const vector<BasicUniform> &basicUniforms, uint32_t programGL, Random &rnd); |
| // Compare the uniform values given in values (obtained with glGetUniform*()) with the basicUniform.finalValue values. |
| bool compareUniformValues(const vector<VarValue> &values, const vector<BasicUniform> &basicUniforms); |
| // Render and check that all pixels are white (i.e. all uniform comparisons passed). |
| bool renderTest(const vector<BasicUniform> &basicUniforms, const ShaderProgram &program, Random &rnd); |
| |
| virtual bool test(const vector<BasicUniform> &basicUniforms, |
| const vector<BasicUniformReportRef> &basicUniformReportsRef, const ShaderProgram &program, |
| Random &rnd) = 0; |
| |
| const uint32_t m_features; |
| const SharedPtr<const UniformCollection> m_uniformCollection; |
| |
| private: |
| static uint32_t randomFeatures(uint32_t 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<BasicUniform> &basicUniformsDst, |
| vector<BasicUniformReportRef> &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<BasicUniform> &basicUniforms, |
| const char *variableName) const; |
| |
| string generateVertexSource(const vector<BasicUniform> &basicUniforms) const; |
| string generateFragmentSource(const vector<BasicUniform> &basicUniforms) const; |
| |
| void setupTexture(const VarValue &value); |
| |
| const CaseShaderType m_caseShaderType; |
| |
| vector<glu::Texture2D *> m_textures2d; |
| vector<glu::TextureCube *> m_texturesCube; |
| vector<uint32_t> m_filledTextureUnits; |
| }; |
| |
| uint32_t UniformCase::randomFeatures(const uint32_t seed) |
| { |
| static const uint32_t arrayUsageChoices[] = {0, FEATURE_ARRAYUSAGE_ONLY_MIDDLE_INDEX}; |
| static const uint32_t uniformFuncChoices[] = {0, FEATURE_UNIFORMFUNC_VALUE}; |
| static const uint32_t matrixModeChoices[] = {0, FEATURE_MATRIXMODE_ROWMAJOR}; |
| static const uint32_t arrayAssignChoices[] = {0, FEATURE_ARRAYASSIGN_FULL, FEATURE_ARRAYASSIGN_BLOCKS_OF_TWO}; |
| static const uint32_t uniformUsageChoices[] = {0, FEATURE_UNIFORMUSAGE_EVERY_OTHER}; |
| static const uint32_t booleanApiTypeChoices[] = {0, FEATURE_BOOLEANAPITYPE_INT, FEATURE_BOOLEANAPITYPE_UINT}; |
| static const uint32_t uniformValueChoices[] = {0, FEATURE_UNIFORMVALUE_ZERO}; |
| |
| Random rnd(seed); |
| |
| uint32_t 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(matrixModeChoices); |
| 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<const UniformCollection> &uniformCollection, const uint32_t 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 uint32_t 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<BasicUniform> &basicUniformsDst, |
| vector<BasicUniformReportRef> &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<bool> 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<glu::TYPE_FLOAT_VEC2>, 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<glu::TYPE_FLOAT_VEC3>, 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<glu::TYPE_FLOAT_VEC4>, 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<glu::TYPE_FLOAT_MAT2>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_mat2 (mediump mat2 a, mediump mat2 b) { return compare_vec2(a[0], " |
| "b[0])*compare_vec2(a[1], b[1]); }"}, |
| {{dataTypeEquals<glu::TYPE_FLOAT_MAT2X3>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_mat2x3 (mediump mat2x3 a, mediump mat2x3 b){ return compare_vec3(a[0], " |
| "b[0])*compare_vec3(a[1], b[1]); }"}, |
| {{dataTypeEquals<glu::TYPE_FLOAT_MAT2X4>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_mat2x4 (mediump mat2x4 a, mediump mat2x4 b){ return compare_vec4(a[0], " |
| "b[0])*compare_vec4(a[1], b[1]); }"}, |
| {{dataTypeEquals<glu::TYPE_FLOAT_MAT3X2>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_mat3x2 (mediump mat3x2 a, mediump mat3x2 b){ return compare_vec2(a[0], " |
| "b[0])*compare_vec2(a[1], b[1])*compare_vec2(a[2], b[2]); }"}, |
| {{dataTypeEquals<glu::TYPE_FLOAT_MAT3>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "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<glu::TYPE_FLOAT_MAT3X4>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_mat3x4 (mediump mat3x4 a, mediump mat3x4 b){ return compare_vec4(a[0], " |
| "b[0])*compare_vec4(a[1], b[1])*compare_vec4(a[2], b[2]); }"}, |
| {{dataTypeEquals<glu::TYPE_FLOAT_MAT4X2>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_mat4x2 (mediump mat4x2 a, mediump mat4x2 b){ return compare_vec2(a[0], " |
| "b[0])*compare_vec2(a[1], b[1])*compare_vec2(a[2], b[2])*compare_vec2(a[3], b[3]); }"}, |
| {{dataTypeEquals<glu::TYPE_FLOAT_MAT4X3>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_mat4x3 (mediump mat4x3 a, mediump mat4x3 b){ return compare_vec3(a[0], " |
| "b[0])*compare_vec3(a[1], b[1])*compare_vec3(a[2], b[2])*compare_vec3(a[3], b[3]); }"}, |
| {{dataTypeEquals<glu::TYPE_FLOAT_MAT4>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "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<glu::TYPE_INT>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_int (mediump int a, mediump int b) { return a == b ? 1.0 : 0.0; }"}, |
| {{dataTypeEquals<glu::TYPE_INT_VEC2>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_ivec2 (mediump ivec2 a, mediump ivec2 b) { return a == b ? 1.0 : 0.0; }"}, |
| {{dataTypeEquals<glu::TYPE_INT_VEC3>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_ivec3 (mediump ivec3 a, mediump ivec3 b) { return a == b ? 1.0 : 0.0; }"}, |
| {{dataTypeEquals<glu::TYPE_INT_VEC4>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_ivec4 (mediump ivec4 a, mediump ivec4 b) { return a == b ? 1.0 : 0.0; }"}, |
| {{dataTypeEquals<glu::TYPE_UINT>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_uint (mediump uint a, mediump uint b) { return a == b ? 1.0 : 0.0; }"}, |
| {{dataTypeEquals<glu::TYPE_UINT_VEC2>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_uvec2 (mediump uvec2 a, mediump uvec2 b) { return a == b ? 1.0 : 0.0; }"}, |
| {{dataTypeEquals<glu::TYPE_UINT_VEC3>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_uvec3 (mediump uvec3 a, mediump uvec3 b) { return a == b ? 1.0 : 0.0; }"}, |
| {{dataTypeEquals<glu::TYPE_UINT_VEC4>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_uvec4 (mediump uvec4 a, mediump uvec4 b) { return a == b ? 1.0 : 0.0; }"}, |
| {{dataTypeEquals<glu::TYPE_BOOL>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_bool (bool a, bool b) { return a == b ? 1.0 : 0.0; }"}, |
| {{dataTypeEquals<glu::TYPE_BOOL_VEC2>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_bvec2 (bvec2 a, bvec2 b) { return a == b ? 1.0 : 0.0; }"}, |
| {{dataTypeEquals<glu::TYPE_BOOL_VEC3>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_bvec3 (bvec3 a, bvec3 b) { return a == b ? 1.0 : 0.0; }"}, |
| {{dataTypeEquals<glu::TYPE_BOOL_VEC4>, dataTypeEquals<glu::TYPE_INVALID>}, |
| "mediump float compare_bvec4 (bvec4 a, bvec4 b) { return a == b ? 1.0 : 0.0; }"}}; |
| |
| const vector<glu::DataType> samplerTypes = m_uniformCollection->getSamplerTypes(); |
| |
| for (int compFuncNdx = 0; compFuncNdx < DE_LENGTH_OF_ARRAY(compareFuncs); compFuncNdx++) |
| { |
| const dataTypePredicate(&typeReq)[2] = compareFuncs[compFuncNdx].requiringTypes; |
| bool containsTypeSampler = false; |
| |
| for (int i = 0; i < (int)samplerTypes.size(); i++) |
| { |
| if (glu::isDataTypeSampler(samplerTypes[i])) |
| { |
| const glu::DataType retType = getSamplerLookupReturnType(samplerTypes[i]); |
| if (typeReq[0](retType) || typeReq[1](retType)) |
| { |
| containsTypeSampler = true; |
| break; |
| } |
| } |
| } |
| |
| 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_" << glu::getDataTypeName(getSamplerLookupReturnType(uniform.type)) << "(texture(" |
| << 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<BasicUniform> &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<BasicUniform> &basicUniforms) const |
| { |
| const bool isVertexCase = m_caseShaderType == CASESHADERTYPE_VERTEX || m_caseShaderType == CASESHADERTYPE_BOTH; |
| std::ostringstream result; |
| |
| result << "#version 300 es\n" |
| "in highp vec4 a_position;\n" |
| "out 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<BasicUniform> &basicUniforms) const |
| { |
| const bool isFragmentCase = m_caseShaderType == CASESHADERTYPE_FRAGMENT || m_caseShaderType == CASESHADERTYPE_BOTH; |
| std::ostringstream result; |
| |
| result << "#version 300 es\n" |
| "in mediump float v_vtxOut;\n" |
| "\n"; |
| |
| if (isFragmentCase) |
| writeUniformDefinitions(result); |
| |
| result << "\n" |
| "layout(location = 0) out mediump vec4 dEQP_FragColor;\n" |
| "\n" |
| "void main (void)\n" |
| "{\n" |
| " mediump float result = v_vtxOut;\n"; |
| |
| if (isFragmentCase) |
| writeUniformComparisons(result, basicUniforms, "result"); |
| |
| result << " dEQP_FragColor = vec4(result, result, result, 1.0);\n" |
| "}\n"; |
| |
| return result.str(); |
| } |
| |
| void UniformCase::setupTexture(const VarValue &value) |
| { |
| // \note No handling for samplers other than 2D or cube. |
| |
| enableLogging(false); |
| |
| DE_ASSERT(getSamplerLookupReturnType(value.type) == glu::TYPE_FLOAT_VEC4); |
| |
| const int width = 32; |
| const int height = 32; |
| const tcu::Vec4 color = vec4FromPtr(&value.val.samplerV.fillColor.floatV[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<BasicUniformReportGL> &basicUniformReportsDst, |
| const vector<BasicUniformReportRef> &basicUniformReportsRef, |
| const uint32_t programGL) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| GLint numActiveUniforms = 0; |
| GLint uniformMaxNameLength = 0; |
| vector<char> 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::getActiveUniformsiv(vector<BasicUniformReportGL> &basicUniformReportsDst, |
| const vector<BasicUniformReportRef> &basicUniformReportsRef, |
| const uint32_t programGL) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| vector<string> queryNames(basicUniformReportsRef.size()); |
| vector<const char *> queryNamesC(basicUniformReportsRef.size()); |
| vector<GLuint> uniformIndices(basicUniformReportsRef.size()); |
| vector<uint32_t> |
| validUniformIndices; // This shall have the same contents, and in same order, as uniformIndices, but with GL_INVALID_INDEX entries removed. |
| bool success = true; |
| |
| for (int i = 0; i < (int)basicUniformReportsRef.size(); i++) |
| { |
| const string &name = basicUniformReportsRef[i].name; |
| queryNames[i] = m_features & FEATURE_ARRAY_FIRST_ELEM_NAME_NO_INDEX && name[name.size() - 1] == ']' ? |
| beforeLast(name, '[') : |
| name; |
| queryNamesC[i] = queryNames[i].c_str(); |
| } |
| |
| GLU_CHECK_CALL( |
| glGetUniformIndices(programGL, (GLsizei)basicUniformReportsRef.size(), &queryNamesC[0], &uniformIndices[0])); |
| |
| for (int i = 0; i < (int)uniformIndices.size(); i++) |
| { |
| if (uniformIndices[i] != GL_INVALID_INDEX) |
| validUniformIndices.push_back(uniformIndices[i]); |
| else |
| { |
| if (basicUniformReportsRef[i].isUsedInShader) |
| { |
| log << TestLog::Message << "// FAILURE: uniform with name " << basicUniformReportsRef[i].name |
| << " received GL_INVALID_INDEX" << TestLog::EndMessage; |
| success = false; |
| } |
| } |
| } |
| |
| if (!validUniformIndices.empty()) |
| { |
| vector<GLint> uniformNameLengthBuf(validUniformIndices.size()); |
| vector<GLint> uniformSizeBuf(validUniformIndices.size()); |
| vector<GLint> uniformTypeBuf(validUniformIndices.size()); |
| |
| GLU_CHECK_CALL(glGetActiveUniformsiv(programGL, (GLsizei)validUniformIndices.size(), &validUniformIndices[0], |
| GL_UNIFORM_NAME_LENGTH, &uniformNameLengthBuf[0])); |
| GLU_CHECK_CALL(glGetActiveUniformsiv(programGL, (GLsizei)validUniformIndices.size(), &validUniformIndices[0], |
| GL_UNIFORM_SIZE, &uniformSizeBuf[0])); |
| GLU_CHECK_CALL(glGetActiveUniformsiv(programGL, (GLsizei)validUniformIndices.size(), &validUniformIndices[0], |
| GL_UNIFORM_TYPE, &uniformTypeBuf[0])); |
| |
| { |
| int validNdx = |
| -1; // Keeps the corresponding index to validUniformIndices while unifNdx is the index to uniformIndices. |
| for (int unifNdx = 0; unifNdx < (int)uniformIndices.size(); unifNdx++) |
| { |
| if (uniformIndices[unifNdx] == GL_INVALID_INDEX) |
| continue; |
| |
| validNdx++; |
| |
| const BasicUniformReportRef &reference = basicUniformReportsRef[unifNdx]; |
| const int reportedIndex = validUniformIndices[validNdx]; |
| const int reportedNameLength = (int)uniformNameLengthBuf[validNdx]; |
| const int reportedSize = (int)uniformSizeBuf[validNdx]; |
| const glu::DataType reportedType = glu::getDataTypeFromGLType((uint32_t)uniformTypeBuf[validNdx]); |
| |
| TCU_CHECK_MSG(reportedType != glu::TYPE_LAST, "Invalid uniform type"); |
| |
| log << TestLog::Message << "// Got name length = " << reportedNameLength << ", size = " << reportedSize |
| << ", type = " << glu::getDataTypeName(reportedType) << " for the uniform at index " |
| << reportedIndex << " (" << reference.name << ")" << TestLog::EndMessage; |
| |
| DE_ASSERT(reference.type != glu::TYPE_LAST); |
| DE_ASSERT(reference.minSize >= 1 || (reference.minSize == 0 && !reference.isUsedInShader)); |
| DE_ASSERT(reference.minSize <= reference.maxSize); |
| basicUniformReportsDst.push_back(BasicUniformReportGL(reference.name.c_str(), reportedNameLength, |
| reportedSize, reportedType, reportedIndex)); |
| |
| if (reportedNameLength != (int)reference.name.length() + 1) |
| { |
| log << TestLog::Message << "// FAILURE: wrong name length reported, should be " |
| << reference.name.length() + 1 << TestLog::EndMessage; |
| success = false; |
| } |
| |
| 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; |
| } |
| } |
| } |
| } |
| |
| return success; |
| } |
| |
| bool UniformCase::uniformVsUniformsivComparison(const vector<BasicUniformReportGL> &uniformResults, |
| const vector<BasicUniformReportGL> &uniformsivResults) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| bool success = true; |
| |
| for (int uniformResultNdx = 0; uniformResultNdx < (int)uniformResults.size(); uniformResultNdx++) |
| { |
| const BasicUniformReportGL &uniformResult = uniformResults[uniformResultNdx]; |
| const string &uniformName = uniformResult.name; |
| const vector<BasicUniformReportGL>::const_iterator uniformsivResultIt = |
| BasicUniformReportGL::findWithName(uniformsivResults, uniformName.c_str()); |
| |
| if (uniformsivResultIt != uniformsivResults.end()) |
| { |
| const BasicUniformReportGL &uniformsivResult = *uniformsivResultIt; |
| |
| log << TestLog::Message << "// Checking uniform " << uniformName << TestLog::EndMessage; |
| |
| if (uniformResult.index != uniformsivResult.index) |
| { |
| log << TestLog::Message |
| << "// FAILURE: glGetActiveUniform() and glGetUniformIndices() gave different indices for uniform " |
| << uniformName << TestLog::EndMessage; |
| success = false; |
| } |
| if (uniformResult.nameLength + 1 != uniformsivResult.nameLength) |
| { |
| log << TestLog::Message |
| << "// FAILURE: glGetActiveUniform() and glGetActiveUniformsiv() gave incompatible name lengths " |
| "for uniform " |
| << uniformName << TestLog::EndMessage; |
| success = false; |
| } |
| if (uniformResult.size != uniformsivResult.size) |
| { |
| log << TestLog::Message |
| << "// FAILURE: glGetActiveUniform() and glGetActiveUniformsiv() gave different sizes for uniform " |
| << uniformName << TestLog::EndMessage; |
| success = false; |
| } |
| if (uniformResult.type != uniformsivResult.type) |
| { |
| log << TestLog::Message |
| << "// FAILURE: glGetActiveUniform() and glGetActiveUniformsiv() gave different types for uniform " |
| << uniformName << TestLog::EndMessage; |
| success = false; |
| } |
| } |
| else |
| { |
| log << TestLog::Message << "// FAILURE: uniform " << uniformName |
| << " was reported active by glGetActiveUniform() but not by glGetUniformIndices()" |
| << TestLog::EndMessage; |
| success = false; |
| } |
| } |
| |
| for (int uniformsivResultNdx = 0; uniformsivResultNdx < (int)uniformsivResults.size(); uniformsivResultNdx++) |
| { |
| const BasicUniformReportGL &uniformsivResult = uniformsivResults[uniformsivResultNdx]; |
| const string &uniformsivName = uniformsivResult.name; |
| const vector<BasicUniformReportGL>::const_iterator uniformsResultIt = |
| BasicUniformReportGL::findWithName(uniformsivResults, uniformsivName.c_str()); |
| |
| if (uniformsResultIt == uniformsivResults.end()) |
| { |
| log << TestLog::Message << "// FAILURE: uniform " << uniformsivName |
| << " was reported active by glGetUniformIndices() but not by glGetActiveUniform()" |
| << TestLog::EndMessage; |
| success = false; |
| } |
| } |
| |
| return success; |
| } |
| |
| bool UniformCase::getUniforms(vector<VarValue> &valuesDst, const vector<BasicUniform> &basicUniforms, |
| const uint32_t 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(GLuint) == sizeof(value.val.uintV[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::isDataTypeUintOrUVec(uniform.type)) |
| GLU_CHECK_CALL(glGetUniformuiv(programGL, location, &value.val.uintV[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 if (m_features & FEATURE_BOOLEANAPITYPE_UINT) |
| { |
| GLU_CHECK_CALL(glGetUniformuiv(programGL, location, &value.val.uintV[0])); |
| for (int i = 0; i < size; i++) |
| value.val.boolV[i] = value.val.uintV[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<VarValue> &values, const vector<BasicUniform> &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::isDataTypeUintOrUVec(uniform.type)) |
| CHECK_UNIFORM(uintV, 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<BasicUniform> &basicUniforms, uint32_t programGL, Random &rnd) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| const bool transpose = (m_features & FEATURE_MATRIXMODE_ROWMAJOR) != 0; |
| const GLboolean transposeGL = transpose ? GL_TRUE : GL_FALSE; |
| const glu::DataType boolApiType = m_features & FEATURE_BOOLEANAPITYPE_INT ? glu::TYPE_INT : |
| m_features & FEATURE_BOOLEANAPITYPE_UINT ? glu::TYPE_UINT : |
| 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<VarValue> 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<BasicUniform>::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(glu::isDataTypeMatrix(apiValue.type) && transpose ? getTransposeMatrix(apiValue) : |
| 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<float> 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<float> 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 (uniform.type) |
| { |
| case glu::TYPE_FLOAT_MAT2: |
| GLU_CHECK_CALL(glUniformMatrix2fv(location, (GLsizei)valuesToAssign.size(), transposeGL, &buffer[0])); |
| break; |
| case glu::TYPE_FLOAT_MAT3: |
| GLU_CHECK_CALL(glUniformMatrix3fv(location, (GLsizei)valuesToAssign.size(), transposeGL, &buffer[0])); |
| break; |
| case glu::TYPE_FLOAT_MAT4: |
| GLU_CHECK_CALL(glUniformMatrix4fv(location, (GLsizei)valuesToAssign.size(), transposeGL, &buffer[0])); |
| break; |
| case glu::TYPE_FLOAT_MAT2X3: |
| GLU_CHECK_CALL(glUniformMatrix2x3fv(location, (GLsizei)valuesToAssign.size(), transposeGL, &buffer[0])); |
| break; |
| case glu::TYPE_FLOAT_MAT2X4: |
| GLU_CHECK_CALL(glUniformMatrix2x4fv(location, (GLsizei)valuesToAssign.size(), transposeGL, &buffer[0])); |
| break; |
| case glu::TYPE_FLOAT_MAT3X2: |
| GLU_CHECK_CALL(glUniformMatrix3x2fv(location, (GLsizei)valuesToAssign.size(), transposeGL, &buffer[0])); |
| break; |
| case glu::TYPE_FLOAT_MAT3X4: |
| GLU_CHECK_CALL(glUniformMatrix3x4fv(location, (GLsizei)valuesToAssign.size(), transposeGL, &buffer[0])); |
| break; |
| case glu::TYPE_FLOAT_MAT4X2: |
| GLU_CHECK_CALL(glUniformMatrix4x2fv(location, (GLsizei)valuesToAssign.size(), transposeGL, &buffer[0])); |
| break; |
| case glu::TYPE_FLOAT_MAT4X3: |
| GLU_CHECK_CALL(glUniformMatrix4x3fv(location, (GLsizei)valuesToAssign.size(), transposeGL, &buffer[0])); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| else if (glu::isDataTypeIntOrIVec(valuesToAssign[0].type)) |
| { |
| if (assignByValue) |
| { |
| const int32_t *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<int32_t> 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::isDataTypeUintOrUVec(valuesToAssign[0].type)) |
| { |
| if (assignByValue) |
| { |
| const uint32_t *const ptr = &valuesToAssign[0].val.uintV[0]; |
| |
| switch (typeSize) |
| { |
| case 1: |
| GLU_CHECK_CALL(glUniform1ui(location, ptr[0])); |
| break; |
| case 2: |
| GLU_CHECK_CALL(glUniform2ui(location, ptr[0], ptr[1])); |
| break; |
| case 3: |
| GLU_CHECK_CALL(glUniform3ui(location, ptr[0], ptr[1], ptr[2])); |
| break; |
| case 4: |
| GLU_CHECK_CALL(glUniform4ui(location, ptr[0], ptr[1], ptr[2], ptr[3])); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| else |
| { |
| vector<uint32_t> 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(GLuint) == sizeof(buffer[0])); |
| switch (typeSize) |
| { |
| case 1: |
| GLU_CHECK_CALL(glUniform1uiv(location, (GLsizei)valuesToAssign.size(), &buffer[0])); |
| break; |
| case 2: |
| GLU_CHECK_CALL(glUniform2uiv(location, (GLsizei)valuesToAssign.size(), &buffer[0])); |
| break; |
| case 3: |
| GLU_CHECK_CALL(glUniform3uiv(location, (GLsizei)valuesToAssign.size(), &buffer[0])); |
| break; |
| case 4: |
| GLU_CHECK_CALL(glUniform4uiv(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<VarValue> &values, const vector<BasicUniform> &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<BasicUniform> &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 uint16_t 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()) ^ (uint32_t)m_context.getTestContext().getCommandLine().getBaseSeed()); |
| TestLog &log = m_testCtx.getLog(); |
| vector<BasicUniform> basicUniforms; |
| vector<BasicUniformReportRef> 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: |
| enum CaseType |
| { |
| CASETYPE_UNIFORM = 0, //!< Check info returned by glGetActiveUniform(). |
| CASETYPE_INDICES_UNIFORMSIV, //!< Check info returned by glGetUniformIndices() + glGetActiveUniformsiv(). |
| CASETYPE_CONSISTENCY, //!< Query info with both above methods, and check consistency. |
| |
| CASETYPE_LAST |
| }; |
| |
| UniformInfoQueryCase(Context &context, const char *name, const char *description, CaseShaderType shaderType, |
| const SharedPtr<const UniformCollection> &uniformCollection, CaseType caseType, |
| uint32_t additionalFeatures = 0); |
| bool test(const vector<BasicUniform> &basicUniforms, const vector<BasicUniformReportRef> &basicUniformReportsRef, |
| const ShaderProgram &program, Random &rnd); |
| |
| static const char *getCaseTypeName(CaseType caseType); |
| static const char *getCaseTypeDescription(CaseType caseType); |
| |
| private: |
| const CaseType m_caseType; |
| }; |
| |
| const char *UniformInfoQueryCase::getCaseTypeName(const CaseType caseType) |
| { |
| switch (caseType) |
| { |
| case CASETYPE_UNIFORM: |
| return "active_uniform"; |
| case CASETYPE_INDICES_UNIFORMSIV: |
| return "indices_active_uniformsiv"; |
| case CASETYPE_CONSISTENCY: |
| return "consistency"; |
| default: |
| DE_ASSERT(false); |
| return DE_NULL; |
| } |
| } |
| |
| const char *UniformInfoQueryCase::getCaseTypeDescription(const CaseType caseType) |
| { |
| switch (caseType) |
| { |
| case CASETYPE_UNIFORM: |
| return "Test glGetActiveUniform()"; |
| case CASETYPE_INDICES_UNIFORMSIV: |
| return "Test glGetUniformIndices() along with glGetActiveUniformsiv()"; |
| case CASETYPE_CONSISTENCY: |
| return "Check consistency between results from glGetActiveUniform() and glGetUniformIndices() + " |
| "glGetActiveUniformsiv()"; |
| default: |
| DE_ASSERT(false); |
| return DE_NULL; |
| } |
| } |
| |
| UniformInfoQueryCase::UniformInfoQueryCase(Context &context, const char *const name, const char *const description, |
| const CaseShaderType shaderType, |
| const SharedPtr<const UniformCollection> &uniformCollection, |
| const CaseType caseType, const uint32_t additionalFeatures) |
| : UniformCase(context, name, description, shaderType, uniformCollection, additionalFeatures) |
| , m_caseType(caseType) |
| { |
| } |
| |
| bool UniformInfoQueryCase::test(const vector<BasicUniform> &basicUniforms, |
| const vector<BasicUniformReportRef> &basicUniformReportsRef, |
| const ShaderProgram &program, Random &rnd) |
| { |
| DE_UNREF(basicUniforms); |
| DE_UNREF(rnd); |
| |
| const uint32_t programGL = program.getProgram(); |
| TestLog &log = m_testCtx.getLog(); |
| vector<BasicUniformReportGL> basicUniformReportsUniform; |
| vector<BasicUniformReportGL> basicUniformReportsUniformsiv; |
| |
| if (m_caseType == CASETYPE_UNIFORM || m_caseType == CASETYPE_CONSISTENCY) |
| { |
| bool success = false; |
| |
| { |
| const ScopedLogSection section(log, "InfoGetActiveUniform", |
| "Uniform information queries with glGetActiveUniform()"); |
| success = getActiveUniforms(basicUniformReportsUniform, basicUniformReportsRef, programGL); |
| } |
| |
| if (!success) |
| { |
| if (m_caseType == CASETYPE_UNIFORM) |
| return false; |
| else |
| { |
| DE_ASSERT(m_caseType == CASETYPE_CONSISTENCY); |
| log << TestLog::Message << "// Note: this is a consistency case, so ignoring above failure(s)" |
| << TestLog::EndMessage; |
| } |
| } |
| } |
| |
| if (m_caseType == CASETYPE_INDICES_UNIFORMSIV || m_caseType == CASETYPE_CONSISTENCY) |
| { |
| bool success = false; |
| |
| { |
| const ScopedLogSection section( |
| log, "InfoGetActiveUniformsiv", |
| "Uniform information queries with glGetUniformIndices() and glGetActiveUniformsiv()"); |
| success = getActiveUniformsiv(basicUniformReportsUniformsiv, basicUniformReportsRef, programGL); |
| } |
| |
| if (!success) |
| { |
| if (m_caseType == CASETYPE_INDICES_UNIFORMSIV) |
| return false; |
| else |
| { |
| DE_ASSERT(m_caseType == CASETYPE_CONSISTENCY); |
| log << TestLog::Message << "// Note: this is a consistency case, so ignoring above failure(s)" |
| << TestLog::EndMessage; |
| } |
| } |
| } |
| |
| if (m_caseType == CASETYPE_CONSISTENCY) |
| { |
| bool success = false; |
| |
| { |
| const ScopedLogSection section( |
| log, "CompareUniformVsUniformsiv", |
| "Comparison of results from glGetActiveUniform() and glGetActiveUniformsiv()"); |
| success = uniformVsUniformsivComparison(basicUniformReportsUniform, basicUniformReportsUniformsiv); |
| } |
| |
| 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<const UniformCollection> &uniformCollection, ValueToCheck valueToCheck, |
| CheckMethod checkMethod, AssignMethod assignMethod, uint32_t additionalFeatures = 0); |
| |
| bool test(const vector<BasicUniform> &basicUniforms, const vector<BasicUniformReportRef> &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<const UniformCollection> &uniformCollection, |
| const ValueToCheck valueToCheck, const CheckMethod checkMethod, |
| const AssignMethod assignMethod, const uint32_t 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<BasicUniform> &basicUniforms, |
| const vector<BasicUniformReportRef> &basicUniformReportsRef, const ShaderProgram &program, |
| Random &rnd) |
| { |
| DE_UNREF(basicUniformReportsRef); |
| |
| const uint32_t 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<VarValue> 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, uint32_t seed); |
| |
| bool test(const vector<BasicUniform> &basicUniforms, const vector<BasicUniformReportRef> &basicUniformReportsRef, |
| const ShaderProgram &program, Random &rnd); |
| }; |
| |
| RandomUniformCase::RandomUniformCase(Context &context, const char *const name, const char *const description, |
| const uint32_t seed) |
| : UniformCase(context, name, description, seed ^ (uint32_t)context.getTestContext().getCommandLine().getBaseSeed()) |
| { |
| } |
| |
| bool RandomUniformCase::test(const vector<BasicUniform> &basicUniforms, |
| const vector<BasicUniformReportRef> &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 performGetActiveUniformsiv = rnd.getBool(); |
| const bool performUniformVsUniformsivComparison = |
| performGetActiveUniforms && performGetActiveUniformsiv && 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 uint32_t programGL = program.getProgram(); |
| TestLog &log = m_testCtx.getLog(); |
| |
| if (!(performGetActiveUniforms || performGetActiveUniformsiv || performUniformVsUniformsivComparison || |
| 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) |
| |
| { |
| vector<BasicUniformReportGL> reportsUniform; |
| vector<BasicUniformReportGL> reportsUniformsiv; |
| |
| if (performGetActiveUniforms) |
| PERFORM_AND_CHECK(getActiveUniforms(reportsUniform, basicUniformReportsRef, programGL), |
| "InfoGetActiveUniform", "Uniform information queries with glGetActiveUniform()"); |
| if (performGetActiveUniformsiv) |
| PERFORM_AND_CHECK(getActiveUniformsiv(reportsUniformsiv, basicUniformReportsRef, programGL), |
| "InfoGetActiveUniformsiv", |
| "Uniform information queries with glGetIndices() and glGetActiveUniformsiv()"); |
| if (performUniformVsUniformsivComparison) |
| PERFORM_AND_CHECK(uniformVsUniformsivComparison(reportsUniform, reportsUniformsiv), |
| "CompareUniformVsUniformsiv", |
| "Comparison of results from glGetActiveUniform() and glGetActiveUniformsiv()"); |
| } |
| |
| { |
| vector<VarValue> 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<VarValue> 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<const UniformCollection> uniformCollection; |
| |
| UniformCollectionCase(const char *const name, const UniformCollection *uniformCollection_) |
| : namePrefix(name ? name + string("_") : "") |
| , uniformCollection(uniformCollection_) |
| { |
| } |
| }; |
| |
| } // namespace |
| |
| 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<UniformCollectionCase> 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 uniform info querying functions"); |
| addChild(infoQueryGroup); |
| for (int caseTypeI = 0; caseTypeI < (int)UniformInfoQueryCase::CASETYPE_LAST; caseTypeI++) |
| { |
| const UniformInfoQueryCase::CaseType caseType = (UniformInfoQueryCase::CaseType)caseTypeI; |
| TestCaseGroup *const caseTypeGroup = |
| new TestCaseGroup(m_context, UniformInfoQueryCase::getCaseTypeName(caseType), |
| UniformInfoQueryCase::getCaseTypeDescription(caseType)); |
| infoQueryGroup->addChild(caseTypeGroup); |
| |
| for (int collectionGroupNdx = 0; collectionGroupNdx < (int)UNIFORMCOLLECTIONS_LAST; collectionGroupNdx++) |
| { |
| const int numArrayFirstElemNameCases = caseType == UniformInfoQueryCase::CASETYPE_INDICES_UNIFORMSIV && |
| 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 *const collectionTestGroup = |
| new TestCaseGroup(m_context, collectionGroupName.c_str(), ""); |
| caseTypeGroup->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<const UniformCollection> &uniformCollection = |
| collectionCase.uniformCollection; |
| |
| collectionTestGroup->addChild( |
| new UniformInfoQueryCase(m_context, name.c_str(), "", (CaseShaderType)shaderType, |
| uniformCollection, (UniformInfoQueryCase::CaseType)caseType, |
| referToFirstArrayElemWithoutIndexI == 0 ? |
| 0 : |
| UniformCase::FEATURE_ARRAY_FIRST_ELEM_NAME_NO_INDEX)); |
| } |
| } |
| } |
| } |
| |
| // Info-querying cases when unused uniforms are present. |
| |
| { |
| TestCaseGroup *const unusedUniformsGroup = |
| new TestCaseGroup(m_context, "unused_uniforms", "Test with unused uniforms"); |
| caseTypeGroup->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<const UniformCollection> &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, (UniformInfoQueryCase::CaseType)caseType, |
| 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<const UniformCollection> &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 ? 3 : 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 uint32_t booleanTypeFeat = |
| booleanTypeI == 1 ? UniformCase::FEATURE_BOOLEANAPITYPE_INT : |
| booleanTypeI == 2 ? UniformCase::FEATURE_BOOLEANAPITYPE_UINT : |
| 0; |
| const char *const booleanTypeName = booleanTypeI == 1 ? "int" : |
| booleanTypeI == 2 ? "uint" : |
| "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<const UniformCollection> &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 ? 3 : 1; |
| const bool containsMatrices = |
| uniformCollection->containsMatchingBasicType(glu::isDataTypeMatrix); |
| const bool varyMatrixMode = |
| containsMatrices && (collectionGroupNdx == UNIFORMCOLLECTIONS_BASIC || |
| collectionGroupNdx == UNIFORMCOLLECTIONS_BASIC_ARRAY); |
| const int numMatVariations = varyMatrixMode ? 2 : 1; |
| |
| if (containsMatrices && assignMethod != UniformValueCase::ASSIGNMETHOD_POINTER) |
| continue; |
| |
| for (int booleanTypeI = 0; booleanTypeI < numBoolVariations; booleanTypeI++) |
| { |
| const uint32_t booleanTypeFeat = |
| booleanTypeI == 1 ? UniformCase::FEATURE_BOOLEANAPITYPE_INT : |
| booleanTypeI == 2 ? UniformCase::FEATURE_BOOLEANAPITYPE_UINT : |
| 0; |
| const char *const booleanTypeName = booleanTypeI == 1 ? "int" : |
| booleanTypeI == 2 ? "uint" : |
| "float"; |
| const string nameWithBoolType = |
| varyBoolApiType ? collName + "api_" + booleanTypeName + "_" : collName; |
| |
| for (int matrixTypeI = 0; matrixTypeI < numMatVariations; matrixTypeI++) |
| { |
| const string nameWithMatrixType = |
| nameWithBoolType + (matrixTypeI == 1 ? "row_major_" : ""); |
| |
| for (int shaderType = 0; shaderType < (int)CASESHADERTYPE_LAST; shaderType++) |
| { |
| const string name = |
| nameWithMatrixType + getCaseShaderTypeName((CaseShaderType)shaderType); |
| const uint32_t 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 | |
| (matrixTypeI == 1 ? UniformCase::FEATURE_MATRIXMODE_ROWMAJOR : 0))); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // 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<const UniformCollection> &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<const UniformCollection> &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(), "", (uint32_t)ndx)); |
| } |
| } |
| |
| } // namespace Functional |
| } // namespace gles3 |
| } // namespace deqp |