/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.1 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 Program interface query test case *//*--------------------------------------------------------------------*/ #include "es31fProgramInterfaceQueryTestCase.hpp" #include "es31fProgramInterfaceDefinitionUtil.hpp" #include "tcuTestLog.hpp" #include "gluVarTypeUtil.hpp" #include "gluStrUtil.hpp" #include "gluContextInfo.hpp" #include "gluShaderProgram.hpp" #include "glwFunctions.hpp" #include "glwEnums.hpp" #include "deString.h" #include "deStringUtil.hpp" #include "deSTLUtil.hpp" namespace deqp { namespace gles31 { namespace Functional { namespace { using ProgramInterfaceDefinition::VariablePathComponent; using ProgramInterfaceDefinition::VariableSearchFilter; static glw::GLenum getProgramDefaultBlockInterfaceFromStorage(glu::Storage storage) { switch (storage) { case glu::STORAGE_IN: case glu::STORAGE_PATCH_IN: return GL_PROGRAM_INPUT; case glu::STORAGE_OUT: case glu::STORAGE_PATCH_OUT: return GL_PROGRAM_OUTPUT; case glu::STORAGE_UNIFORM: return GL_UNIFORM; default: DE_ASSERT(false); return 0; } } static bool isBufferBackedInterfaceBlockStorage(glu::Storage storage) { return storage == glu::STORAGE_BUFFER || storage == glu::STORAGE_UNIFORM; } const char *getRequiredExtensionForStage(glu::ShaderType stage) { switch (stage) { case glu::SHADERTYPE_COMPUTE: case glu::SHADERTYPE_VERTEX: case glu::SHADERTYPE_FRAGMENT: return DE_NULL; case glu::SHADERTYPE_GEOMETRY: return "GL_EXT_geometry_shader"; case glu::SHADERTYPE_TESSELLATION_CONTROL: case glu::SHADERTYPE_TESSELLATION_EVALUATION: return "GL_EXT_tessellation_shader"; default: DE_ASSERT(false); return DE_NULL; } } static int getTypeSize(glu::DataType type) { if (type == glu::TYPE_FLOAT) return 4; else if (type == glu::TYPE_INT || type == glu::TYPE_UINT) return 4; else if (type == glu::TYPE_BOOL) return 4; // uint DE_ASSERT(false); return 0; } static int getVarTypeSize(const glu::VarType &type) { if (type.isBasicType()) { // return in basic machine units return glu::getDataTypeScalarSize(type.getBasicType()) * getTypeSize(glu::getDataTypeScalarType(type.getBasicType())); } else if (type.isStructType()) { int size = 0; for (int ndx = 0; ndx < type.getStructPtr()->getNumMembers(); ++ndx) size += getVarTypeSize(type.getStructPtr()->getMember(ndx).getType()); return size; } else if (type.isArrayType()) { // unsized arrays are handled as if they had only one element if (type.getArraySize() == glu::VarType::UNSIZED_ARRAY) return getVarTypeSize(type.getElementType()); else return type.getArraySize() * getVarTypeSize(type.getElementType()); } else { DE_ASSERT(false); return 0; } } static glu::MatrixOrder getMatrixOrderFromPath(const std::vector &path) { glu::MatrixOrder order = glu::MATRIXORDER_LAST; // inherit majority for (int pathNdx = 0; pathNdx < (int)path.size(); ++pathNdx) { glu::MatrixOrder matOrder; if (path[pathNdx].isInterfaceBlock()) matOrder = path[pathNdx].getInterfaceBlock()->layout.matrixOrder; else if (path[pathNdx].isDeclaration()) matOrder = path[pathNdx].getDeclaration()->layout.matrixOrder; else if (path[pathNdx].isVariableType()) matOrder = glu::MATRIXORDER_LAST; else { DE_ASSERT(false); return glu::MATRIXORDER_LAST; } if (matOrder != glu::MATRIXORDER_LAST) order = matOrder; } return order; } class PropValidator { public: PropValidator(Context &context, ProgramResourcePropFlags validationProp, const char *requiredExtension); virtual std::string getHumanReadablePropertyString(glw::GLint propVal) const; virtual void validate(const ProgramInterfaceDefinition::Program *program, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const = 0; bool isSupported(void) const; bool isSelected(uint32_t caseFlags) const; protected: void setError(const std::string &err) const; tcu::TestContext &m_testCtx; const glu::RenderContext &m_renderContext; private: const glu::ContextInfo &m_contextInfo; const char *m_extension; const ProgramResourcePropFlags m_validationProp; }; PropValidator::PropValidator(Context &context, ProgramResourcePropFlags validationProp, const char *requiredExtension) : m_testCtx(context.getTestContext()) , m_renderContext(context.getRenderContext()) , m_contextInfo(context.getContextInfo()) , m_extension(requiredExtension) , m_validationProp(validationProp) { } std::string PropValidator::getHumanReadablePropertyString(glw::GLint propVal) const { return de::toString(propVal); } bool PropValidator::isSupported(void) const { if (glu::contextSupports(m_renderContext.getType(), glu::ApiType::es(3, 2)) || glu::contextSupports(m_renderContext.getType(), glu::ApiType::core(4, 5))) return true; return m_extension == DE_NULL || m_contextInfo.isExtensionSupported(m_extension); } bool PropValidator::isSelected(uint32_t caseFlags) const { return (caseFlags & (uint32_t)m_validationProp) != 0; } void PropValidator::setError(const std::string &err) const { // don't overwrite earlier errors if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, err.c_str()); } class SingleVariableValidator : public PropValidator { public: SingleVariableValidator(Context &context, ProgramResourcePropFlags validationProp, glw::GLuint programID, const VariableSearchFilter &filter, const char *requiredExtension); void validate(const ProgramInterfaceDefinition::Program *program, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; virtual void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const = 0; virtual void validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; protected: const VariableSearchFilter m_filter; const glw::GLuint m_programID; }; SingleVariableValidator::SingleVariableValidator(Context &context, ProgramResourcePropFlags validationProp, glw::GLuint programID, const VariableSearchFilter &filter, const char *requiredExtension) : PropValidator(context, validationProp, requiredExtension) , m_filter(filter) , m_programID(programID) { } void SingleVariableValidator::validate(const ProgramInterfaceDefinition::Program *program, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { std::vector path; if (findProgramVariablePathByPathName(path, program, resource, m_filter)) { const glu::VarType *variable = (path.back().isVariableType()) ? (path.back().getVariableType()) : (DE_NULL); if (!variable || !variable->isBasicType()) { m_testCtx.getLog() << tcu::TestLog::Message << "Error, resource name \"" << resource << "\" refers to a non-basic type." << tcu::TestLog::EndMessage; setError("resource not basic type"); } else validateSingleVariable(path, resource, propValue, implementationName); // finding matching variable in any shader is sufficient return; } else if (deStringBeginsWith(resource.c_str(), "gl_")) { // special case for builtins validateBuiltinVariable(resource, propValue, implementationName); return; } // we are only supplied good names, generated by ourselves DE_ASSERT(false); throw tcu::InternalError("Resource name consistency error"); } void SingleVariableValidator::validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(resource); DE_UNREF(propValue); DE_UNREF(implementationName); DE_ASSERT(false); } class SingleBlockValidator : public PropValidator { public: SingleBlockValidator(Context &context, ProgramResourcePropFlags validationProp, glw::GLuint programID, const VariableSearchFilter &filter, const char *requiredExtension); void validate(const ProgramInterfaceDefinition::Program *program, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; virtual void validateSingleBlock(const glu::InterfaceBlock &block, const std::vector &instanceIndex, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const = 0; protected: const VariableSearchFilter m_filter; const glw::GLuint m_programID; }; SingleBlockValidator::SingleBlockValidator(Context &context, ProgramResourcePropFlags validationProp, glw::GLuint programID, const VariableSearchFilter &filter, const char *requiredExtension) : PropValidator(context, validationProp, requiredExtension) , m_filter(filter) , m_programID(programID) { } void SingleBlockValidator::validate(const ProgramInterfaceDefinition::Program *program, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { glu::VarTokenizer tokenizer(resource.c_str()); const std::string blockName = tokenizer.getIdentifier(); std::vector instanceIndex; tokenizer.advance(); // array index while (tokenizer.getToken() == glu::VarTokenizer::TOKEN_LEFT_BRACKET) { tokenizer.advance(); DE_ASSERT(tokenizer.getToken() == glu::VarTokenizer::TOKEN_NUMBER); instanceIndex.push_back(tokenizer.getNumber()); tokenizer.advance(); DE_ASSERT(tokenizer.getToken() == glu::VarTokenizer::TOKEN_RIGHT_BRACKET); tokenizer.advance(); } // no trailing garbage DE_ASSERT(tokenizer.getToken() == glu::VarTokenizer::TOKEN_END); for (int shaderNdx = 0; shaderNdx < (int)program->getShaders().size(); ++shaderNdx) { const ProgramInterfaceDefinition::Shader *const shader = program->getShaders()[shaderNdx]; if (!m_filter.matchesFilter(shader)) continue; for (int blockNdx = 0; blockNdx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++blockNdx) { const glu::InterfaceBlock &block = shader->getDefaultBlock().interfaceBlocks[blockNdx]; if (m_filter.matchesFilter(block) && block.interfaceName == blockName) { // dimensions match DE_ASSERT(instanceIndex.size() == block.dimensions.size()); validateSingleBlock(block, instanceIndex, resource, propValue, implementationName); return; } } } // we are only supplied good names, generated by ourselves DE_ASSERT(false); throw tcu::InternalError("Resource name consistency error"); } class TypeValidator : public SingleVariableValidator { public: TypeValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter); std::string getHumanReadablePropertyString(glw::GLint propVal) const; void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; void validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; TypeValidator::TypeValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter) : SingleVariableValidator(context, PROGRAMRESOURCEPROP_TYPE, programID, filter, DE_NULL) { } std::string TypeValidator::getHumanReadablePropertyString(glw::GLint propVal) const { return de::toString(glu::getShaderVarTypeStr(propVal)); } void TypeValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { const glu::VarType *variable = path.back().getVariableType(); DE_UNREF(resource); DE_UNREF(implementationName); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying type, expecting " << glu::getDataTypeName(variable->getBasicType()) << tcu::TestLog::EndMessage; if (variable->getBasicType() != glu::getDataTypeFromGLType(propValue)) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << glu::getDataTypeName(glu::getDataTypeFromGLType(propValue)) << tcu::TestLog::EndMessage; setError("resource type invalid"); } } void TypeValidator::validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(implementationName); static const struct { const char *name; glu::DataType type; } builtins[] = { {"gl_Position", glu::TYPE_FLOAT_VEC4}, {"gl_FragCoord", glu::TYPE_FLOAT_VEC4}, {"gl_PerVertex.gl_Position", glu::TYPE_FLOAT_VEC4}, {"gl_VertexID", glu::TYPE_INT}, {"gl_InvocationID", glu::TYPE_INT}, {"gl_NumWorkGroups", glu::TYPE_UINT_VEC3}, {"gl_FragDepth", glu::TYPE_FLOAT}, {"gl_TessLevelOuter[0]", glu::TYPE_FLOAT}, {"gl_TessLevelInner[0]", glu::TYPE_FLOAT}, }; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(builtins); ++ndx) { if (resource == builtins[ndx].name) { m_testCtx.getLog() << tcu::TestLog::Message << "Verifying type, expecting " << glu::getDataTypeName(builtins[ndx].type) << tcu::TestLog::EndMessage; if (glu::getDataTypeFromGLType(propValue) != builtins[ndx].type) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << glu::getDataTypeName(glu::getDataTypeFromGLType(propValue)) << tcu::TestLog::EndMessage; setError("resource type invalid"); } return; } } DE_ASSERT(false); } class ArraySizeValidator : public SingleVariableValidator { public: ArraySizeValidator(Context &context, glw::GLuint programID, int unsizedArraySize, const VariableSearchFilter &filter); void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; void validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; private: const int m_unsizedArraySize; }; ArraySizeValidator::ArraySizeValidator(Context &context, glw::GLuint programID, int unsizedArraySize, const VariableSearchFilter &filter) : SingleVariableValidator(context, PROGRAMRESOURCEPROP_ARRAY_SIZE, programID, filter, DE_NULL) , m_unsizedArraySize(unsizedArraySize) { } void ArraySizeValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { const VariablePathComponent nullComponent; const VariablePathComponent &enclosingcomponent = (path.size() > 1) ? (path[path.size() - 2]) : (nullComponent); const bool isArray = enclosingcomponent.isVariableType() && enclosingcomponent.getVariableType()->isArrayType(); const bool inUnsizedArray = isArray && (enclosingcomponent.getVariableType()->getArraySize() == glu::VarType::UNSIZED_ARRAY); const int arraySize = (!isArray) ? (1) : (inUnsizedArray) ? (m_unsizedArraySize) : (enclosingcomponent.getVariableType()->getArraySize()); DE_ASSERT(arraySize >= 0); DE_UNREF(resource); DE_UNREF(implementationName); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying array size, expecting " << arraySize << tcu::TestLog::EndMessage; if (arraySize != propValue) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource array size invalid"); } } void ArraySizeValidator::validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(implementationName); static const struct { const char *name; int arraySize; } builtins[] = { {"gl_Position", 1}, {"gl_VertexID", 1}, {"gl_FragCoord", 1}, {"gl_PerVertex.gl_Position", 1}, {"gl_InvocationID", 1}, {"gl_NumWorkGroups", 1}, {"gl_FragDepth", 1}, {"gl_TessLevelOuter[0]", 4}, {"gl_TessLevelInner[0]", 2}, }; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(builtins); ++ndx) { if (resource == builtins[ndx].name) { m_testCtx.getLog() << tcu::TestLog::Message << "Verifying array size, expecting " << builtins[ndx].arraySize << tcu::TestLog::EndMessage; if (propValue != builtins[ndx].arraySize) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource array size invalid"); } return; } } DE_ASSERT(false); } class ArrayStrideValidator : public SingleVariableValidator { public: ArrayStrideValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter); void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; ArrayStrideValidator::ArrayStrideValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter) : SingleVariableValidator(context, PROGRAMRESOURCEPROP_ARRAY_STRIDE, programID, filter, DE_NULL) { } void ArrayStrideValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { const VariablePathComponent nullComponent; const VariablePathComponent &component = path.back(); const VariablePathComponent &enclosingcomponent = (path.size() > 1) ? (path[path.size() - 2]) : (nullComponent); const VariablePathComponent &firstComponent = path.front(); const bool isBufferBlock = firstComponent.isInterfaceBlock() && isBufferBackedInterfaceBlockStorage(firstComponent.getInterfaceBlock()->storage); const bool isArray = enclosingcomponent.isVariableType() && enclosingcomponent.getVariableType()->isArrayType(); const bool isAtomicCounter = glu::isDataTypeAtomicCounter( component.getVariableType() ->getBasicType()); // atomic counters are buffer backed with a stride of 4 basic machine units DE_UNREF(resource); DE_UNREF(implementationName); // Layout tests will verify layouts of buffer backed arrays properly. Here we just check values are greater or equal to the element size if (isBufferBlock && isArray) { const int elementSize = glu::getDataTypeScalarSize(component.getVariableType()->getBasicType()) * getTypeSize(glu::getDataTypeScalarType(component.getVariableType()->getBasicType())); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying array stride, expecting greater or equal to " << elementSize << tcu::TestLog::EndMessage; if (propValue < elementSize) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource array stride invalid"); } } else { // Atomics are buffer backed with stride of 4 even though they are not in an interface block const int arrayStride = (isAtomicCounter && isArray) ? (4) : (!isBufferBlock && !isAtomicCounter) ? (-1) : (0); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying array stride, expecting " << arrayStride << tcu::TestLog::EndMessage; if (arrayStride != propValue) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource array stride invalid"); } } } class BlockIndexValidator : public SingleVariableValidator { public: BlockIndexValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter); void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; BlockIndexValidator::BlockIndexValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter) : SingleVariableValidator(context, PROGRAMRESOURCEPROP_BLOCK_INDEX, programID, filter, DE_NULL) { } void BlockIndexValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { const VariablePathComponent &firstComponent = path.front(); DE_UNREF(resource); DE_UNREF(implementationName); if (!firstComponent.isInterfaceBlock()) { m_testCtx.getLog() << tcu::TestLog::Message << "Verifying block index, expecting -1" << tcu::TestLog::EndMessage; if (propValue != -1) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource block index invalid"); } } else { m_testCtx.getLog() << tcu::TestLog::Message << "Verifying block index, expecting a valid block index" << tcu::TestLog::EndMessage; if (propValue == -1) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource block index invalid"); } else { const glw::Functions &gl = m_renderContext.getFunctions(); const glw::GLenum interface = (firstComponent.getInterfaceBlock()->storage == glu::STORAGE_UNIFORM) ? (GL_UNIFORM_BLOCK) : (firstComponent.getInterfaceBlock()->storage == glu::STORAGE_BUFFER) ? (GL_SHADER_STORAGE_BLOCK) : (0); glw::GLint written = 0; std::vector nameBuffer(firstComponent.getInterfaceBlock()->interfaceName.size() + 3 * firstComponent.getInterfaceBlock()->dimensions.size() + 2, '\0'); // +3 for appended "[N]", +1 for '\0' and +1 just for safety gl.getProgramResourceName(m_programID, interface, propValue, (int)nameBuffer.size() - 1, &written, &nameBuffer[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "query block name"); TCU_CHECK(written < (int)nameBuffer.size()); TCU_CHECK(nameBuffer.back() == '\0'); { const std::string blockName(&nameBuffer[0], written); std::ostringstream expectedName; expectedName << firstComponent.getInterfaceBlock()->interfaceName; for (int dimensionNdx = 0; dimensionNdx < (int)firstComponent.getInterfaceBlock()->dimensions.size(); ++dimensionNdx) expectedName << "[0]"; m_testCtx.getLog() << tcu::TestLog::Message << "Block name with index " << propValue << " is \"" << blockName << "\"" << tcu::TestLog::EndMessage; if (blockName != expectedName.str()) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, expected " << expectedName.str() << tcu::TestLog::EndMessage; setError("resource block index invalid"); } } } } } class IsRowMajorValidator : public SingleVariableValidator { public: IsRowMajorValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter); std::string getHumanReadablePropertyString(glw::GLint propVal) const; void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; IsRowMajorValidator::IsRowMajorValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter) : SingleVariableValidator(context, PROGRAMRESOURCEPROP_MATRIX_ROW_MAJOR, programID, filter, DE_NULL) { } std::string IsRowMajorValidator::getHumanReadablePropertyString(glw::GLint propVal) const { return de::toString(glu::getBooleanStr(propVal)); } void IsRowMajorValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { const VariablePathComponent &component = path.back(); const VariablePathComponent &firstComponent = path.front(); const bool isBufferBlock = firstComponent.isInterfaceBlock() && isBufferBackedInterfaceBlockStorage(firstComponent.getInterfaceBlock()->storage); const bool isMatrix = glu::isDataTypeMatrix(component.getVariableType()->getBasicType()); const int expected = (isBufferBlock && isMatrix && getMatrixOrderFromPath(path) == glu::MATRIXORDER_ROW_MAJOR) ? (1) : (0); DE_UNREF(resource); DE_UNREF(implementationName); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying matrix order, expecting IS_ROW_MAJOR = " << expected << tcu::TestLog::EndMessage; if (propValue != expected) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource matrix order invalid"); } } class MatrixStrideValidator : public SingleVariableValidator { public: MatrixStrideValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter); void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; MatrixStrideValidator::MatrixStrideValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter) : SingleVariableValidator(context, PROGRAMRESOURCEPROP_MATRIX_STRIDE, programID, filter, DE_NULL) { } void MatrixStrideValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { const VariablePathComponent &component = path.back(); const VariablePathComponent &firstComponent = path.front(); const bool isBufferBlock = firstComponent.isInterfaceBlock() && isBufferBackedInterfaceBlockStorage(firstComponent.getInterfaceBlock()->storage); const bool isMatrix = glu::isDataTypeMatrix(component.getVariableType()->getBasicType()); DE_UNREF(resource); DE_UNREF(implementationName); // Layout tests will verify layouts of buffer backed arrays properly. Here we just check the stride is is greater or equal to the row/column size if (isBufferBlock && isMatrix) { const bool columnMajor = getMatrixOrderFromPath(path) != glu::MATRIXORDER_ROW_MAJOR; const int numMajorElements = (columnMajor) ? (glu::getDataTypeMatrixNumRows(component.getVariableType()->getBasicType())) : (glu::getDataTypeMatrixNumColumns(component.getVariableType()->getBasicType())); const int majorSize = numMajorElements * getTypeSize(glu::getDataTypeScalarType(component.getVariableType()->getBasicType())); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying matrix stride, expecting greater or equal to " << majorSize << tcu::TestLog::EndMessage; if (propValue < majorSize) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource matrix stride invalid"); } } else { const int matrixStride = (!isBufferBlock && !glu::isDataTypeAtomicCounter(component.getVariableType()->getBasicType())) ? (-1) : (0); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying matrix stride, expecting " << matrixStride << tcu::TestLog::EndMessage; if (matrixStride != propValue) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource matrix stride invalid"); } } } class AtomicCounterBufferIndexVerifier : public SingleVariableValidator { public: AtomicCounterBufferIndexVerifier(Context &context, glw::GLuint programID, const VariableSearchFilter &filter); void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; AtomicCounterBufferIndexVerifier::AtomicCounterBufferIndexVerifier(Context &context, glw::GLuint programID, const VariableSearchFilter &filter) : SingleVariableValidator(context, PROGRAMRESOURCEPROP_ATOMIC_COUNTER_BUFFER_INDEX, programID, filter, DE_NULL) { } void AtomicCounterBufferIndexVerifier::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(resource); DE_UNREF(implementationName); if (!glu::isDataTypeAtomicCounter(path.back().getVariableType()->getBasicType())) { m_testCtx.getLog() << tcu::TestLog::Message << "Verifying atomic counter buffer index, expecting -1" << tcu::TestLog::EndMessage; if (propValue != -1) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource atomic counter buffer index invalid"); } } else { m_testCtx.getLog() << tcu::TestLog::Message << "Verifying atomic counter buffer index, expecting a valid index" << tcu::TestLog::EndMessage; if (propValue == -1) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource atomic counter buffer index invalid"); } else { const glw::Functions &gl = m_renderContext.getFunctions(); glw::GLint numActiveResources = 0; gl.getProgramInterfaceiv(m_programID, GL_ATOMIC_COUNTER_BUFFER, GL_ACTIVE_RESOURCES, &numActiveResources); GLU_EXPECT_NO_ERROR(gl.getError(), "getProgramInterfaceiv(..., GL_ATOMIC_COUNTER_BUFFER, GL_ACTIVE_RESOURCES, ...)"); if (propValue >= numActiveResources) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << ", GL_ACTIVE_RESOURCES = " << numActiveResources << tcu::TestLog::EndMessage; setError("resource atomic counter buffer index invalid"); } } } } class LocationValidator : public SingleVariableValidator { public: LocationValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter); void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; void validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; LocationValidator::LocationValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter) : SingleVariableValidator(context, PROGRAMRESOURCEPROP_LOCATION, programID, filter, DE_NULL) { } static int getVariableLocationLength(const glu::VarType &type) { if (type.isBasicType()) { if (glu::isDataTypeMatrix(type.getBasicType())) return glu::getDataTypeMatrixNumColumns(type.getBasicType()); else return 1; } else if (type.isStructType()) { int size = 0; for (int ndx = 0; ndx < type.getStructPtr()->getNumMembers(); ++ndx) size += getVariableLocationLength(type.getStructPtr()->getMember(ndx).getType()); return size; } else if (type.isArrayType()) return type.getArraySize() * getVariableLocationLength(type.getElementType()); else { DE_ASSERT(false); return 0; } } static int getIOSubVariableLocation(const std::vector &path, int startNdx, int currentLocation) { if (currentLocation == -1) return -1; if (path[startNdx].getVariableType()->isBasicType()) return currentLocation; else if (path[startNdx].getVariableType()->isArrayType()) return getIOSubVariableLocation(path, startNdx + 1, currentLocation); else if (path[startNdx].getVariableType()->isStructType()) { for (int ndx = 0; ndx < path[startNdx].getVariableType()->getStructPtr()->getNumMembers(); ++ndx) { if (&path[startNdx].getVariableType()->getStructPtr()->getMember(ndx).getType() == path[startNdx + 1].getVariableType()) return getIOSubVariableLocation(path, startNdx + 1, currentLocation); if (currentLocation != -1) currentLocation += getVariableLocationLength( path[startNdx].getVariableType()->getStructPtr()->getMember(ndx).getType()); } // could not find member, never happens DE_ASSERT(false); return -1; } else { DE_ASSERT(false); return -1; } } static int getIOBlockVariableLocation(const std::vector &path) { const glu::InterfaceBlock *block = path.front().getInterfaceBlock(); int currentLocation = block->layout.location; // Find the block member for (int memberNdx = 0; memberNdx < (int)block->variables.size(); ++memberNdx) { if (block->variables[memberNdx].layout.location != -1) currentLocation = block->variables[memberNdx].layout.location; if (&block->variables[memberNdx] == path[1].getDeclaration()) break; // unspecified + unspecified = unspecified if (currentLocation != -1) currentLocation += getVariableLocationLength(block->variables[memberNdx].varType); } // Find subtype location in the complex type return getIOSubVariableLocation(path, 2, currentLocation); } static int getExplicitLocationFromPath(const std::vector &path) { const glu::VariableDeclaration *varDecl = (path[0].isInterfaceBlock()) ? (path[1].getDeclaration()) : (path[0].getDeclaration()); if (path.front().isInterfaceBlock() && path.front().getInterfaceBlock()->storage == glu::STORAGE_UNIFORM) { // inside uniform block return -1; } else if (path.front().isInterfaceBlock() && (path.front().getInterfaceBlock()->storage == glu::STORAGE_IN || path.front().getInterfaceBlock()->storage == glu::STORAGE_OUT || path.front().getInterfaceBlock()->storage == glu::STORAGE_PATCH_IN || path.front().getInterfaceBlock()->storage == glu::STORAGE_PATCH_OUT)) { // inside ioblock return getIOBlockVariableLocation(path); } else if (varDecl->storage == glu::STORAGE_UNIFORM) { // default block uniform return varDecl->layout.location; } else if (varDecl->storage == glu::STORAGE_IN || varDecl->storage == glu::STORAGE_OUT || varDecl->storage == glu::STORAGE_PATCH_IN || varDecl->storage == glu::STORAGE_PATCH_OUT) { // default block input/output return getIOSubVariableLocation(path, 1, varDecl->layout.location); } else { DE_ASSERT(false); return -1; } } void LocationValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { const bool isAtomicCounterUniform = glu::isDataTypeAtomicCounter(path.back().getVariableType()->getBasicType()); const bool isUniformBlockVariable = path.front().isInterfaceBlock() && path.front().getInterfaceBlock()->storage == glu::STORAGE_UNIFORM; const bool isVertexShader = m_filter.getShaderTypeBits() == (1u << glu::SHADERTYPE_VERTEX); const bool isFragmentShader = m_filter.getShaderTypeBits() == (1u << glu::SHADERTYPE_FRAGMENT); const glu::Storage storage = (path.front().isInterfaceBlock()) ? (path.front().getInterfaceBlock()->storage) : (path.front().getDeclaration()->storage); const bool isInputVariable = (storage == glu::STORAGE_IN || storage == glu::STORAGE_PATCH_IN); const bool isOutputVariable = (storage == glu::STORAGE_OUT || storage == glu::STORAGE_PATCH_OUT); const int explicitLayoutLocation = getExplicitLocationFromPath(path); bool expectLocation; std::string reasonStr; DE_UNREF(resource); if (isAtomicCounterUniform) { expectLocation = false; reasonStr = "Atomic counter uniforms have effective location of -1"; } else if (isUniformBlockVariable) { expectLocation = false; reasonStr = "Uniform block variables have effective location of -1"; } else if (isInputVariable && !isVertexShader && explicitLayoutLocation == -1) { expectLocation = false; reasonStr = "Inputs (except for vertex shader inputs) not declared with a location layout qualifier have " "effective location of -1"; } else if (isOutputVariable && !isFragmentShader && explicitLayoutLocation == -1) { expectLocation = false; reasonStr = "Outputs (except for fragment shader outputs) not declared with a location layout qualifier have " "effective location of -1"; } else { expectLocation = true; } if (!expectLocation) { m_testCtx.getLog() << tcu::TestLog::Message << "Verifying uniform location, expecting -1. (" << reasonStr << ")" << tcu::TestLog::EndMessage; if (propValue != -1) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource location invalid"); } } else { bool locationOk; if (explicitLayoutLocation == -1) { m_testCtx.getLog() << tcu::TestLog::Message << "Verifying location, expecting a valid location" << tcu::TestLog::EndMessage; locationOk = (propValue != -1); } else { m_testCtx.getLog() << tcu::TestLog::Message << "Verifying location, expecting " << explicitLayoutLocation << tcu::TestLog::EndMessage; locationOk = (propValue == explicitLayoutLocation); } if (!locationOk) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource location invalid"); } else { const VariablePathComponent nullComponent; const VariablePathComponent &enclosingcomponent = (path.size() > 1) ? (path[path.size() - 2]) : (nullComponent); const bool isArray = enclosingcomponent.isVariableType() && enclosingcomponent.getVariableType()->isArrayType(); const glw::Functions &gl = m_renderContext.getFunctions(); const glw::GLenum interface = getProgramDefaultBlockInterfaceFromStorage(storage); m_testCtx.getLog() << tcu::TestLog::Message << "Comparing location to the values returned by GetProgramResourceLocation" << tcu::TestLog::EndMessage; // Test all bottom-level array elements if (isArray) { const std::string arrayResourceName = (implementationName.size() > 3) ? (implementationName.substr(0, implementationName.size() - 3)) : (""); // chop "[0]" for (int arrayElementNdx = 0; arrayElementNdx < enclosingcomponent.getVariableType()->getArraySize(); ++arrayElementNdx) { const std::string elementResourceName = arrayResourceName + "[" + de::toString(arrayElementNdx) + "]"; const glw::GLint location = gl.getProgramResourceLocation(m_programID, interface, elementResourceName.c_str()); if (location != propValue + arrayElementNdx) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, getProgramResourceLocation (resource=\"" << elementResourceName << "\") returned location " << location << ", expected " << (propValue + arrayElementNdx) << tcu::TestLog::EndMessage; setError("resource location invalid"); } else m_testCtx.getLog() << tcu::TestLog::Message << "\tLocation of \"" << elementResourceName << "\":\t" << location << tcu::TestLog::EndMessage; } } else { const glw::GLint location = gl.getProgramResourceLocation(m_programID, interface, implementationName.c_str()); if (location != propValue) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, getProgramResourceLocation returned location " << location << ", expected " << propValue << tcu::TestLog::EndMessage; setError("resource location invalid"); } } } } } void LocationValidator::validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(resource); DE_UNREF(implementationName); // built-ins have no location m_testCtx.getLog() << tcu::TestLog::Message << "Verifying location, expecting -1" << tcu::TestLog::EndMessage; if (propValue != -1) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource location invalid"); } } class VariableNameLengthValidator : public SingleVariableValidator { public: VariableNameLengthValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter); void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; void validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; void validateNameLength(const std::string &implementationName, glw::GLint propValue) const; }; VariableNameLengthValidator::VariableNameLengthValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter) : SingleVariableValidator(context, PROGRAMRESOURCEPROP_NAME_LENGTH, programID, filter, DE_NULL) { } void VariableNameLengthValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(path); DE_UNREF(resource); validateNameLength(implementationName, propValue); } void VariableNameLengthValidator::validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(resource); validateNameLength(implementationName, propValue); } void VariableNameLengthValidator::validateNameLength(const std::string &implementationName, glw::GLint propValue) const { const int expected = (int)implementationName.length() + 1; // includes null byte m_testCtx.getLog() << tcu::TestLog::Message << "Verifying name length, expecting " << expected << " (" << (int)implementationName.length() << " for \"" << implementationName << "\" + 1 byte for terminating null character)" << tcu::TestLog::EndMessage; if (propValue != expected) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, invalid name length, got " << propValue << tcu::TestLog::EndMessage; setError("name length invalid"); } } class OffsetValidator : public SingleVariableValidator { public: OffsetValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter); void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; OffsetValidator::OffsetValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter) : SingleVariableValidator(context, PROGRAMRESOURCEPROP_OFFSET, programID, filter, DE_NULL) { } void OffsetValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { const bool isAtomicCounterUniform = glu::isDataTypeAtomicCounter(path.back().getVariableType()->getBasicType()); const bool isBufferBackedBlockStorage = path.front().isInterfaceBlock() && isBufferBackedInterfaceBlockStorage(path.front().getInterfaceBlock()->storage); DE_UNREF(resource); DE_UNREF(implementationName); if (!isAtomicCounterUniform && !isBufferBackedBlockStorage) { // Not buffer backed m_testCtx.getLog() << tcu::TestLog::Message << "Verifying offset, expecting -1" << tcu::TestLog::EndMessage; if (propValue != -1) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, invalid offset, got " << propValue << tcu::TestLog::EndMessage; setError("offset invalid"); } } else { // Expect a valid offset m_testCtx.getLog() << tcu::TestLog::Message << "Verifying offset, expecting a valid offset" << tcu::TestLog::EndMessage; if (propValue < 0) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, invalid offset, got " << propValue << tcu::TestLog::EndMessage; setError("offset invalid"); } } } class VariableReferencedByShaderValidator : public PropValidator { public: VariableReferencedByShaderValidator(Context &context, glu::ShaderType shaderType, const VariableSearchFilter &searchFilter); std::string getHumanReadablePropertyString(glw::GLint propVal) const; void validate(const ProgramInterfaceDefinition::Program *program, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; private: const VariableSearchFilter m_filter; const glu::ShaderType m_shaderType; }; VariableReferencedByShaderValidator::VariableReferencedByShaderValidator(Context &context, glu::ShaderType shaderType, const VariableSearchFilter &searchFilter) : PropValidator(context, PROGRAMRESOURCEPROP_REFERENCED_BY_SHADER, getRequiredExtensionForStage(shaderType)) , m_filter(VariableSearchFilter::logicalAnd(VariableSearchFilter::createShaderTypeFilter(shaderType), searchFilter)) , m_shaderType(shaderType) { DE_ASSERT(m_shaderType < glu::SHADERTYPE_LAST); } std::string VariableReferencedByShaderValidator::getHumanReadablePropertyString(glw::GLint propVal) const { return de::toString(glu::getBooleanStr(propVal)); } void VariableReferencedByShaderValidator::validate(const ProgramInterfaceDefinition::Program *program, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(implementationName); std::vector unusedPath; const bool referencedByShader = findProgramVariablePathByPathName(unusedPath, program, resource, m_filter); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying referenced by " << glu::getShaderTypeName(m_shaderType) << " shader, expecting " << ((referencedByShader) ? ("GL_TRUE") : ("GL_FALSE")) << tcu::TestLog::EndMessage; if (propValue != ((referencedByShader) ? (1) : (0))) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, invalid referenced_by_" << glu::getShaderTypeName(m_shaderType) << ", got " << propValue << tcu::TestLog::EndMessage; setError("referenced_by_" + std::string(glu::getShaderTypeName(m_shaderType)) + " invalid"); } } class BlockNameLengthValidator : public SingleBlockValidator { public: BlockNameLengthValidator(Context &context, const glw::GLuint programID, const VariableSearchFilter &filter); void validateSingleBlock(const glu::InterfaceBlock &block, const std::vector &instanceIndex, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; BlockNameLengthValidator::BlockNameLengthValidator(Context &context, const glw::GLuint programID, const VariableSearchFilter &filter) : SingleBlockValidator(context, PROGRAMRESOURCEPROP_NAME_LENGTH, programID, filter, DE_NULL) { } void BlockNameLengthValidator::validateSingleBlock(const glu::InterfaceBlock &block, const std::vector &instanceIndex, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(instanceIndex); DE_UNREF(block); DE_UNREF(resource); const int expected = (int)implementationName.length() + 1; // includes null byte m_testCtx.getLog() << tcu::TestLog::Message << "Verifying name length, expecting " << expected << " (" << (int)implementationName.length() << " for \"" << implementationName << "\" + 1 byte for terminating null character)" << tcu::TestLog::EndMessage; if (propValue != expected) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, invalid name length, got " << propValue << tcu::TestLog::EndMessage; setError("name length invalid"); } } class BufferBindingValidator : public SingleBlockValidator { public: BufferBindingValidator(Context &context, const glw::GLuint programID, const VariableSearchFilter &filter); void validateSingleBlock(const glu::InterfaceBlock &block, const std::vector &instanceIndex, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; BufferBindingValidator::BufferBindingValidator(Context &context, const glw::GLuint programID, const VariableSearchFilter &filter) : SingleBlockValidator(context, PROGRAMRESOURCEPROP_BUFFER_BINDING, programID, filter, DE_NULL) { } void BufferBindingValidator::validateSingleBlock(const glu::InterfaceBlock &block, const std::vector &instanceIndex, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(resource); DE_UNREF(implementationName); if (block.layout.binding != -1) { int flatIndex = 0; int dimensionSize = 1; for (int dimensionNdx = (int)(block.dimensions.size()) - 1; dimensionNdx >= 0; --dimensionNdx) { flatIndex += dimensionSize * instanceIndex[dimensionNdx]; dimensionSize *= block.dimensions[dimensionNdx]; } const int expected = (block.dimensions.empty()) ? (block.layout.binding) : (block.layout.binding + flatIndex); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying block binding, expecting " << expected << tcu::TestLog::EndMessage; if (propValue != expected) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, invalid buffer binding, got " << propValue << tcu::TestLog::EndMessage; setError("buffer binding invalid"); } } else { m_testCtx.getLog() << tcu::TestLog::Message << "Verifying buffer binding, expecting a valid binding" << tcu::TestLog::EndMessage; if (propValue < 0) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, invalid buffer binding, got " << propValue << tcu::TestLog::EndMessage; setError("buffer binding invalid"); } } } class BlockReferencedByShaderValidator : public PropValidator { public: BlockReferencedByShaderValidator(Context &context, glu::ShaderType shaderType, const VariableSearchFilter &searchFilter); std::string getHumanReadablePropertyString(glw::GLint propVal) const; void validate(const ProgramInterfaceDefinition::Program *program, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; private: const VariableSearchFilter m_filter; const glu::ShaderType m_shaderType; }; BlockReferencedByShaderValidator::BlockReferencedByShaderValidator(Context &context, glu::ShaderType shaderType, const VariableSearchFilter &searchFilter) : PropValidator(context, PROGRAMRESOURCEPROP_REFERENCED_BY_SHADER, getRequiredExtensionForStage(shaderType)) , m_filter(VariableSearchFilter::logicalAnd(VariableSearchFilter::createShaderTypeFilter(shaderType), searchFilter)) , m_shaderType(shaderType) { DE_ASSERT(m_shaderType < glu::SHADERTYPE_LAST); } std::string BlockReferencedByShaderValidator::getHumanReadablePropertyString(glw::GLint propVal) const { return de::toString(glu::getBooleanStr(propVal)); } void BlockReferencedByShaderValidator::validate(const ProgramInterfaceDefinition::Program *program, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { const std::string blockName = glu::parseVariableName(resource.c_str()); bool referencedByShader = false; DE_UNREF(implementationName); for (int shaderNdx = 0; shaderNdx < (int)program->getShaders().size(); ++shaderNdx) { const ProgramInterfaceDefinition::Shader *const shader = program->getShaders()[shaderNdx]; if (!m_filter.matchesFilter(shader)) continue; for (int blockNdx = 0; blockNdx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++blockNdx) { const glu::InterfaceBlock &block = shader->getDefaultBlock().interfaceBlocks[blockNdx]; if (m_filter.matchesFilter(block) && block.interfaceName == blockName) referencedByShader = true; } } m_testCtx.getLog() << tcu::TestLog::Message << "Verifying referenced by " << glu::getShaderTypeName(m_shaderType) << " shader, expecting " << ((referencedByShader) ? ("GL_TRUE") : ("GL_FALSE")) << tcu::TestLog::EndMessage; if (propValue != ((referencedByShader) ? (1) : (0))) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, invalid referenced_by_" << glu::getShaderTypeName(m_shaderType) << ", got " << propValue << tcu::TestLog::EndMessage; setError("referenced_by_" + std::string(glu::getShaderTypeName(m_shaderType)) + " invalid"); } } class TopLevelArraySizeValidator : public SingleVariableValidator { public: TopLevelArraySizeValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter); void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; TopLevelArraySizeValidator::TopLevelArraySizeValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter) : SingleVariableValidator(context, PROGRAMRESOURCEPROP_TOP_LEVEL_ARRAY_SIZE, programID, filter, DE_NULL) { } void TopLevelArraySizeValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { int expected; std::string reason; DE_ASSERT(path.front().isInterfaceBlock() && path.front().getInterfaceBlock()->storage == glu::STORAGE_BUFFER); DE_UNREF(resource); DE_UNREF(implementationName); if (!path[1].getDeclaration()->varType.isArrayType()) { expected = 1; reason = "Top-level block member is not an array"; } else if (path[1].getDeclaration()->varType.getElementType().isBasicType()) { expected = 1; reason = "Top-level block member is not an array of an aggregate type"; } else if (path[1].getDeclaration()->varType.getArraySize() == glu::VarType::UNSIZED_ARRAY) { expected = 0; reason = "Top-level block member is an unsized top-level array"; } else { expected = path[1].getDeclaration()->varType.getArraySize(); reason = "Top-level block member is a sized top-level array"; } m_testCtx.getLog() << tcu::TestLog::Message << "Verifying top level array size, expecting " << expected << ". (" << reason << ")." << tcu::TestLog::EndMessage; if (propValue != expected) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, invalid top level array size, got " << propValue << tcu::TestLog::EndMessage; setError("top level array size invalid"); } } class TopLevelArrayStrideValidator : public SingleVariableValidator { public: TopLevelArrayStrideValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter); void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; TopLevelArrayStrideValidator::TopLevelArrayStrideValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter) : SingleVariableValidator(context, PROGRAMRESOURCEPROP_TOP_LEVEL_ARRAY_STRIDE, programID, filter, DE_NULL) { } void TopLevelArrayStrideValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_ASSERT(path.front().isInterfaceBlock() && path.front().getInterfaceBlock()->storage == glu::STORAGE_BUFFER); DE_UNREF(resource); DE_UNREF(implementationName); if (!path[1].getDeclaration()->varType.isArrayType()) { m_testCtx.getLog() << tcu::TestLog::Message << "Verifying top level array stride, expecting 0. (Top-level block member is not an array)." << tcu::TestLog::EndMessage; if (propValue != 0) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, top level array stride, got " << propValue << tcu::TestLog::EndMessage; setError("top level array stride invalid"); } } else if (path[1].getDeclaration()->varType.getElementType().isBasicType()) { m_testCtx.getLog() << tcu::TestLog::Message << "Verifying top level array stride, expecting 0. (Top-level block member is not an array " "of an aggregate type)." << tcu::TestLog::EndMessage; if (propValue != 0) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, top level array stride, got " << propValue << tcu::TestLog::EndMessage; setError("top level array stride invalid"); } } else { const int minimumStride = getVarTypeSize(path[1].getDeclaration()->varType.getElementType()); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying top level array stride, expecting greater or equal to " << minimumStride << "." << tcu::TestLog::EndMessage; if (propValue < minimumStride) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, invalid top level array stride, got " << propValue << tcu::TestLog::EndMessage; setError("top level array stride invalid"); } } } class TransformFeedbackResourceValidator : public PropValidator { public: TransformFeedbackResourceValidator(Context &context, ProgramResourcePropFlags validationProp); void validate(const ProgramInterfaceDefinition::Program *program, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; private: virtual void validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const = 0; virtual void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const = 0; }; TransformFeedbackResourceValidator::TransformFeedbackResourceValidator(Context &context, ProgramResourcePropFlags validationProp) : PropValidator(context, validationProp, DE_NULL) { } void TransformFeedbackResourceValidator::validate(const ProgramInterfaceDefinition::Program *program, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { if (deStringBeginsWith(resource.c_str(), "gl_")) { validateBuiltinVariable(resource, propValue, implementationName); } else { // Check resource name is a xfb output. (quick check) #if defined(DE_DEBUG) bool generatorFound = false; // Check the resource name is a valid transform feedback resource and find the name generating resource for (int varyingNdx = 0; varyingNdx < (int)program->getTransformFeedbackVaryings().size(); ++varyingNdx) { const std::string varyingName = program->getTransformFeedbackVaryings()[varyingNdx]; std::vector path; std::vector resources; if (!findProgramVariablePathByPathName(path, program, varyingName, VariableSearchFilter::createShaderTypeStorageFilter( getProgramTransformFeedbackStage(program), glu::STORAGE_OUT))) { // program does not contain feedback varying, not valid program DE_ASSERT(false); return; } generateVariableTypeResourceNames(resources, varyingName, *path.back().getVariableType(), RESOURCE_NAME_GENERATION_FLAG_TRANSFORM_FEEDBACK_VARIABLE); if (de::contains(resources.begin(), resources.end(), resource)) { generatorFound = true; break; } } // resource name was not found, should never happen DE_ASSERT(generatorFound); DE_UNREF(generatorFound); #endif // verify resource { std::vector path; if (!findProgramVariablePathByPathName(path, program, resource, VariableSearchFilter::createShaderTypeStorageFilter( getProgramTransformFeedbackStage(program), glu::STORAGE_OUT))) DE_ASSERT(false); validateSingleVariable(path, resource, propValue, implementationName); } } } class TransformFeedbackArraySizeValidator : public TransformFeedbackResourceValidator { public: TransformFeedbackArraySizeValidator(Context &context); void validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; TransformFeedbackArraySizeValidator::TransformFeedbackArraySizeValidator(Context &context) : TransformFeedbackResourceValidator(context, PROGRAMRESOURCEPROP_ARRAY_SIZE) { } void TransformFeedbackArraySizeValidator::validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(implementationName); int arraySize = 0; if (resource == "gl_Position") arraySize = 1; else DE_ASSERT(false); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying array size, expecting " << arraySize << tcu::TestLog::EndMessage; if (arraySize != propValue) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource array size invalid"); } } void TransformFeedbackArraySizeValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(resource); DE_UNREF(implementationName); const int arraySize = (path.back().getVariableType()->isArrayType()) ? (path.back().getVariableType()->getArraySize()) : (1); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying array size, expecting " << arraySize << tcu::TestLog::EndMessage; if (arraySize != propValue) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource array size invalid"); } } class TransformFeedbackNameLengthValidator : public TransformFeedbackResourceValidator { public: TransformFeedbackNameLengthValidator(Context &context); private: void validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; void validateVariable(const std::string &implementationName, glw::GLint propValue) const; }; TransformFeedbackNameLengthValidator::TransformFeedbackNameLengthValidator(Context &context) : TransformFeedbackResourceValidator(context, PROGRAMRESOURCEPROP_NAME_LENGTH) { } void TransformFeedbackNameLengthValidator::validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(resource); validateVariable(implementationName, propValue); } void TransformFeedbackNameLengthValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(path); DE_UNREF(resource); validateVariable(implementationName, propValue); } void TransformFeedbackNameLengthValidator::validateVariable(const std::string &implementationName, glw::GLint propValue) const { const int expected = (int)implementationName.length() + 1; // includes null byte m_testCtx.getLog() << tcu::TestLog::Message << "Verifying name length, expecting " << expected << " (" << (int)implementationName.length() << " for \"" << implementationName << "\" + 1 byte for terminating null character)" << tcu::TestLog::EndMessage; if (propValue != expected) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, invalid name length, got " << propValue << tcu::TestLog::EndMessage; setError("name length invalid"); } } class TransformFeedbackTypeValidator : public TransformFeedbackResourceValidator { public: TransformFeedbackTypeValidator(Context &context); void validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; TransformFeedbackTypeValidator::TransformFeedbackTypeValidator(Context &context) : TransformFeedbackResourceValidator(context, PROGRAMRESOURCEPROP_TYPE) { } void TransformFeedbackTypeValidator::validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(implementationName); glu::DataType varType = glu::TYPE_INVALID; if (resource == "gl_Position") varType = glu::TYPE_FLOAT_VEC4; else DE_ASSERT(false); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying type, expecting " << glu::getDataTypeName(varType) << tcu::TestLog::EndMessage; if (glu::getDataTypeFromGLType(propValue) != varType) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << glu::getDataTypeName(glu::getDataTypeFromGLType(propValue)) << tcu::TestLog::EndMessage; setError("resource type invalid"); } return; } void TransformFeedbackTypeValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(resource); DE_UNREF(implementationName); // Unlike other interfaces, xfb program interface uses just variable name to refer to arrays of basic types. (Others use "variable[0]") // Thus we might end up querying a type for an array. In this case, return the type of an array element. const glu::VarType &variable = *path.back().getVariableType(); const glu::VarType &elementType = (variable.isArrayType()) ? (variable.getElementType()) : (variable); DE_ASSERT(elementType.isBasicType()); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying type, expecting " << glu::getDataTypeName(elementType.getBasicType()) << tcu::TestLog::EndMessage; if (elementType.getBasicType() != glu::getDataTypeFromGLType(propValue)) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << glu::getDataTypeName(glu::getDataTypeFromGLType(propValue)) << tcu::TestLog::EndMessage; setError("resource type invalid"); } } class PerPatchValidator : public SingleVariableValidator { public: PerPatchValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter); std::string getHumanReadablePropertyString(glw::GLint propVal) const; void validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; void validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const; }; PerPatchValidator::PerPatchValidator(Context &context, glw::GLuint programID, const VariableSearchFilter &filter) : SingleVariableValidator(context, PROGRAMRESOURCEPROP_IS_PER_PATCH, programID, filter, "GL_EXT_tessellation_shader") { } std::string PerPatchValidator::getHumanReadablePropertyString(glw::GLint propVal) const { return de::toString(glu::getBooleanStr(propVal)); } void PerPatchValidator::validateSingleVariable(const std::vector &path, const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { const glu::Storage storage = (path.front().isInterfaceBlock()) ? (path.front().getInterfaceBlock()->storage) : (path.front().getDeclaration()->storage); const int expected = (storage == glu::STORAGE_PATCH_IN || storage == glu::STORAGE_PATCH_OUT) ? (1) : (0); DE_UNREF(resource); DE_UNREF(implementationName); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying if is per patch, expecting IS_PER_PATCH = " << expected << tcu::TestLog::EndMessage; if (propValue != expected) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource is per patch invalid"); } } void PerPatchValidator::validateBuiltinVariable(const std::string &resource, glw::GLint propValue, const std::string &implementationName) const { DE_UNREF(implementationName); static const struct { const char *name; int isPerPatch; } builtins[] = { {"gl_Position", 0}, {"gl_PerVertex.gl_Position", 0}, {"gl_InvocationID", 0}, {"gl_TessLevelOuter[0]", 1}, {"gl_TessLevelInner[0]", 1}, }; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(builtins); ++ndx) { if (resource == builtins[ndx].name) { m_testCtx.getLog() << tcu::TestLog::Message << "Verifying if is per patch, expecting IS_PER_PATCH = " << builtins[ndx].isPerPatch << tcu::TestLog::EndMessage; if (propValue != builtins[ndx].isPerPatch) { m_testCtx.getLog() << tcu::TestLog::Message << "\tError, got " << propValue << tcu::TestLog::EndMessage; setError("resource is per patch invalid"); } return; } } DE_ASSERT(false); } } // namespace ProgramResourceQueryTestTarget::ProgramResourceQueryTestTarget(ProgramInterface interface_, uint32_t propFlags_) : interface(interface_) , propFlags(propFlags_) { switch (interface) { case PROGRAMINTERFACE_UNIFORM: DE_ASSERT((propFlags & PROGRAMRESOURCEPROP_UNIFORM_INTERFACE_MASK) == propFlags); break; case PROGRAMINTERFACE_UNIFORM_BLOCK: DE_ASSERT((propFlags & PROGRAMRESOURCEPROP_UNIFORM_BLOCK_INTERFACE_MASK) == propFlags); break; case PROGRAMINTERFACE_SHADER_STORAGE_BLOCK: DE_ASSERT((propFlags & PROGRAMRESOURCEPROP_SHADER_STORAGE_BLOCK_MASK) == propFlags); break; case PROGRAMINTERFACE_PROGRAM_INPUT: DE_ASSERT((propFlags & PROGRAMRESOURCEPROP_PROGRAM_INPUT_MASK) == propFlags); break; case PROGRAMINTERFACE_PROGRAM_OUTPUT: DE_ASSERT((propFlags & PROGRAMRESOURCEPROP_PROGRAM_OUTPUT_MASK) == propFlags); break; case PROGRAMINTERFACE_BUFFER_VARIABLE: DE_ASSERT((propFlags & PROGRAMRESOURCEPROP_BUFFER_VARIABLE_MASK) == propFlags); break; case PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING: DE_ASSERT((propFlags & PROGRAMRESOURCEPROP_TRANSFORM_FEEDBACK_VARYING_MASK) == propFlags); break; default: DE_ASSERT(false); } } ProgramInterfaceQueryTestCase::ProgramInterfaceQueryTestCase(Context &context, const char *name, const char *description, ProgramResourceQueryTestTarget queryTarget) : TestCase(context, name, description) , m_queryTarget(queryTarget) { } ProgramInterfaceQueryTestCase::~ProgramInterfaceQueryTestCase(void) { } ProgramInterface ProgramInterfaceQueryTestCase::getTargetInterface(void) const { return m_queryTarget.interface; } static glw::GLenum getGLInterfaceEnumValue(ProgramInterface interface) { switch (interface) { case PROGRAMINTERFACE_UNIFORM: return GL_UNIFORM; case PROGRAMINTERFACE_UNIFORM_BLOCK: return GL_UNIFORM_BLOCK; case PROGRAMINTERFACE_ATOMIC_COUNTER_BUFFER: return GL_ATOMIC_COUNTER_BUFFER; case PROGRAMINTERFACE_PROGRAM_INPUT: return GL_PROGRAM_INPUT; case PROGRAMINTERFACE_PROGRAM_OUTPUT: return GL_PROGRAM_OUTPUT; case PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING: return GL_TRANSFORM_FEEDBACK_VARYING; case PROGRAMINTERFACE_BUFFER_VARIABLE: return GL_BUFFER_VARIABLE; case PROGRAMINTERFACE_SHADER_STORAGE_BLOCK: return GL_SHADER_STORAGE_BLOCK; default: DE_ASSERT(false); return 0; } } static bool isInterfaceBlockInterfaceName(const ProgramInterfaceDefinition::Program *program, ProgramInterface interface, const std::string &blockInterfaceName) { uint32_t validStorageBits; uint32_t searchStageBits; DE_STATIC_ASSERT(glu::STORAGE_LAST < 32); DE_STATIC_ASSERT(glu::SHADERTYPE_LAST < 32); switch (interface) { case PROGRAMINTERFACE_UNIFORM_BLOCK: case PROGRAMINTERFACE_SHADER_STORAGE_BLOCK: case PROGRAMINTERFACE_ATOMIC_COUNTER_BUFFER: return false; case PROGRAMINTERFACE_PROGRAM_INPUT: validStorageBits = (1u << glu::STORAGE_IN) | (1u << glu::STORAGE_PATCH_IN); searchStageBits = (1u << program->getFirstStage()); break; case PROGRAMINTERFACE_PROGRAM_OUTPUT: validStorageBits = (1u << glu::STORAGE_OUT) | (1u << glu::STORAGE_PATCH_OUT); searchStageBits = (1u << program->getLastStage()); break; case PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING: validStorageBits = (1u << glu::STORAGE_OUT); searchStageBits = (1u << getProgramTransformFeedbackStage(program)); break; case PROGRAMINTERFACE_UNIFORM: validStorageBits = (1u << glu::STORAGE_UNIFORM); searchStageBits = 0xFFFFFFFFu; break; case PROGRAMINTERFACE_BUFFER_VARIABLE: validStorageBits = (1u << glu::STORAGE_BUFFER); searchStageBits = 0xFFFFFFFFu; break; default: DE_ASSERT(false); return false; } for (int shaderNdx = 0; shaderNdx < (int)program->getShaders().size(); ++shaderNdx) { const ProgramInterfaceDefinition::Shader *const shader = program->getShaders()[shaderNdx]; if (((1u << shader->getType()) & searchStageBits) == 0) continue; for (int blockNdx = 0; blockNdx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++blockNdx) { const glu::InterfaceBlock &block = shader->getDefaultBlock().interfaceBlocks[blockNdx]; if (((1u << block.storage) & validStorageBits) == 0) continue; if (block.interfaceName == blockInterfaceName) return true; } } return false; } static std::string getInterfaceBlockInteraceNameByMember(const ProgramInterfaceDefinition::Program *program, ProgramInterface interface, const std::string &memberName) { uint32_t validStorageBits; uint32_t searchStageBits; DE_STATIC_ASSERT(glu::STORAGE_LAST < 32); DE_STATIC_ASSERT(glu::SHADERTYPE_LAST < 32); switch (interface) { case PROGRAMINTERFACE_UNIFORM_BLOCK: case PROGRAMINTERFACE_SHADER_STORAGE_BLOCK: case PROGRAMINTERFACE_ATOMIC_COUNTER_BUFFER: return ""; case PROGRAMINTERFACE_PROGRAM_INPUT: validStorageBits = (1u << glu::STORAGE_IN) | (1u << glu::STORAGE_PATCH_IN); searchStageBits = (1u << program->getFirstStage()); break; case PROGRAMINTERFACE_PROGRAM_OUTPUT: validStorageBits = (1u << glu::STORAGE_OUT) | (1u << glu::STORAGE_PATCH_OUT); searchStageBits = (1u << program->getLastStage()); break; case PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING: validStorageBits = (1u << glu::STORAGE_OUT); searchStageBits = (1u << getProgramTransformFeedbackStage(program)); break; case PROGRAMINTERFACE_UNIFORM: validStorageBits = (1u << glu::STORAGE_UNIFORM); searchStageBits = 0xFFFFFFFFu; break; case PROGRAMINTERFACE_BUFFER_VARIABLE: validStorageBits = (1u << glu::STORAGE_BUFFER); searchStageBits = 0xFFFFFFFFu; break; default: DE_ASSERT(false); return ""; } for (int shaderNdx = 0; shaderNdx < (int)program->getShaders().size(); ++shaderNdx) { const ProgramInterfaceDefinition::Shader *const shader = program->getShaders()[shaderNdx]; if (((1u << shader->getType()) & searchStageBits) == 0) continue; for (int blockNdx = 0; blockNdx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++blockNdx) { const glu::InterfaceBlock &block = shader->getDefaultBlock().interfaceBlocks[blockNdx]; if (((1u << block.storage) & validStorageBits) == 0) continue; for (int varNdx = 0; varNdx < (int)block.variables.size(); ++varNdx) { if (block.variables[varNdx].name == memberName) return block.interfaceName; } } } return ""; } static void queryAndValidateProps(tcu::TestContext &testCtx, const glw::Functions &gl, glw::GLuint programID, ProgramInterface interface, const char *targetResourceName, const ProgramInterfaceDefinition::Program *programDefinition, const std::vector &props, const std::vector &validators) { const glw::GLenum glInterface = getGLInterfaceEnumValue(interface); std::string implementationResourceName = targetResourceName; glw::GLuint resourceNdx; glw::GLint written = -1; // prefill result buffer with an invalid value. -1 might be valid sometimes, avoid it. Make buffer one larger // to allow detection of too many return values std::vector propValues(props.size() + 1, -2); DE_ASSERT(props.size() == validators.size()); // query resourceNdx = gl.getProgramResourceIndex(programID, glInterface, targetResourceName); GLU_EXPECT_NO_ERROR(gl.getError(), "get resource index"); if (resourceNdx == GL_INVALID_INDEX) { static const struct { bool removeTrailingArray; // convert from "target[0]" -> "target" bool removeTrailingMember; // convert from "target.member" -> "target" bool removeIOBlock; // convert from "InterfaceName.target" -> "target" bool addIOBlock; // convert from "target" -> "InterfaceName.target" bool addIOBlockArray; // convert from "target" -> "InterfaceName[0].target" } recoveryStrategies[] = { // try one patch {true, false, false, false, false}, {false, true, false, false, false}, {false, false, true, false, false}, {false, false, false, true, false}, {false, false, false, false, true}, // patch both ends {true, false, true, false, false}, {true, false, false, true, false}, {true, false, false, false, true}, {false, true, true, false, false}, {false, true, false, true, false}, {false, true, false, false, true}, }; // The resource name generation in the GL implementations is very commonly broken. Try to // keep the tests producing useful data even in these cases by attempting to recover from // common naming bugs. Set test result to failure even if recovery succeeded to signal // incorrect name generation. testCtx.getLog() << tcu::TestLog::Message << "getProgramResourceIndex returned GL_INVALID_INDEX for \"" << targetResourceName << "\"" << tcu::TestLog::EndMessage; testCtx.setTestResult(QP_TEST_RESULT_FAIL, "could not find target resource"); for (int strategyNdx = 0; strategyNdx < DE_LENGTH_OF_ARRAY(recoveryStrategies); ++strategyNdx) { const std::string resourceName = std::string(targetResourceName); const size_t rootNameEnd = resourceName.find_first_of(".["); const std::string rootName = resourceName.substr(0, rootNameEnd); std::string simplifiedResourceName; if (recoveryStrategies[strategyNdx].removeTrailingArray) { if (de::endsWith(resourceName, "[0]")) simplifiedResourceName = resourceName.substr(0, resourceName.length() - 3); else continue; } if (recoveryStrategies[strategyNdx].removeTrailingMember) { const size_t lastMember = resourceName.find_last_of('.'); if (lastMember != std::string::npos) simplifiedResourceName = resourceName.substr(0, lastMember); else continue; } if (recoveryStrategies[strategyNdx].removeIOBlock) { if (deStringBeginsWith(resourceName.c_str(), "gl_PerVertex.")) { // builtin interface bock, remove block name simplifiedResourceName = resourceName.substr(13); } else if (isInterfaceBlockInterfaceName(programDefinition, interface, rootName)) { // user-defined inteface block, remove name const size_t accessorEnd = resourceName.find('.'); // includes potential array accessor if (accessorEnd != std::string::npos) simplifiedResourceName = resourceName.substr(0, accessorEnd + 1); else continue; } else { // recovery not applicable continue; } } if (recoveryStrategies[strategyNdx].addIOBlock || recoveryStrategies[strategyNdx].addIOBlockArray) { const std::string arrayAccessor = (recoveryStrategies[strategyNdx].addIOBlockArray) ? ("[0]") : (""); if (deStringBeginsWith(resourceName.c_str(), "gl_") && resourceName.find('.') == std::string::npos) { // free builtin variable, add block name simplifiedResourceName = "gl_PerVertex" + arrayAccessor + "." + resourceName; } else { const std::string interafaceName = getInterfaceBlockInteraceNameByMember(programDefinition, interface, rootName); if (!interafaceName.empty()) { // free user variable, add block name simplifiedResourceName = interafaceName + arrayAccessor + "." + resourceName; } else { // recovery not applicable continue; } } } if (simplifiedResourceName.empty()) continue; resourceNdx = gl.getProgramResourceIndex(programID, glInterface, simplifiedResourceName.c_str()); GLU_EXPECT_NO_ERROR(gl.getError(), "get resource index"); // recovery succeeded if (resourceNdx != GL_INVALID_INDEX) { implementationResourceName = simplifiedResourceName; testCtx.getLog() << tcu::TestLog::Message << "\tResource not found, continuing anyway using index obtained for resource \"" << simplifiedResourceName << "\"" << tcu::TestLog::EndMessage; break; } } if (resourceNdx == GL_INVALID_INDEX) return; } gl.getProgramResourceiv(programID, glInterface, resourceNdx, (int)props.size(), &props[0], (int)propValues.size(), &written, &propValues[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "get props"); if (written != (int)props.size()) { testCtx.getLog() << tcu::TestLog::Message << "getProgramResourceiv returned unexpected number of values, expected " << (int)props.size() << ", got " << written << tcu::TestLog::EndMessage; testCtx.setTestResult(QP_TEST_RESULT_FAIL, "getProgramResourceiv returned unexpected number of values"); return; } if (propValues.back() != -2) { testCtx.getLog() << tcu::TestLog::Message << "getProgramResourceiv post write buffer guard value was modified, too many return values" << tcu::TestLog::EndMessage; testCtx.setTestResult(QP_TEST_RESULT_FAIL, "getProgramResourceiv returned unexpected number of values"); return; } propValues.pop_back(); DE_ASSERT(validators.size() == propValues.size()); // log { tcu::MessageBuilder message(&testCtx.getLog()); message << "For resource index " << resourceNdx << " (\"" << targetResourceName << "\") got following properties:\n"; for (int propNdx = 0; propNdx < (int)propValues.size(); ++propNdx) message << "\t" << glu::getProgramResourcePropertyName(props[propNdx]) << ":\t" << validators[propNdx]->getHumanReadablePropertyString(propValues[propNdx]) << "\n"; message << tcu::TestLog::EndMessage; } // validate for (int propNdx = 0; propNdx < (int)propValues.size(); ++propNdx) validators[propNdx]->validate(programDefinition, targetResourceName, propValues[propNdx], implementationResourceName); } const ProgramInterfaceDefinition::Program *ProgramInterfaceQueryTestCase::getAndCheckProgramDefinition(void) { const ProgramInterfaceDefinition::Program *programDefinition = getProgramDefinition(); DE_ASSERT(programDefinition->isValid()); auto type = m_context.getRenderContext().getType(); if (glu::contextSupports(type, glu::ApiType::es(3, 2)) || glu::contextSupports(type, glu::ApiType::core(4, 5))) { return programDefinition; } if (programDefinition->hasStage(glu::SHADERTYPE_TESSELLATION_CONTROL) || programDefinition->hasStage(glu::SHADERTYPE_TESSELLATION_EVALUATION)) { if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader")) throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension"); } // Testing IS_PER_PATCH as a part of a larger set is ok, since the extension is checked // before query. However, we don't want IS_PER_PATCH-specific tests to become noop and pass. if (m_queryTarget.propFlags == PROGRAMRESOURCEPROP_IS_PER_PATCH) { if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader")) throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension"); } if (programDefinition->hasStage(glu::SHADERTYPE_GEOMETRY)) { if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")) throw tcu::NotSupportedError("Test requires GL_EXT_geometry_shader extension"); } if (programContainsIOBlocks(programDefinition)) { if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_shader_io_blocks")) throw tcu::NotSupportedError("Test requires GL_EXT_shader_io_blocks extension"); } return programDefinition; } int ProgramInterfaceQueryTestCase::getMaxPatchVertices(void) { const glw::Functions &gl = m_context.getRenderContext().getFunctions(); glw::GLint maxPatchVertices = 0; gl.getIntegerv(GL_MAX_PATCH_VERTICES, &maxPatchVertices); GLU_EXPECT_NO_ERROR(gl.getError(), "getIntegerv(GL_MAX_PATCH_VERTICES)"); return maxPatchVertices; } ProgramInterfaceQueryTestCase::IterateResult ProgramInterfaceQueryTestCase::iterate(void) { struct TestProperty { glw::GLenum prop; const PropValidator *validator; }; const ProgramInterfaceDefinition::Program *programDefinition = getAndCheckProgramDefinition(); const std::vector targetResources = getQueryTargetResources(); glu::ShaderProgram program(m_context.getRenderContext(), generateProgramInterfaceProgramSources(programDefinition)); m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); // Log program { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program", "Program"); // Feedback varyings if (!programDefinition->getTransformFeedbackVaryings().empty()) { tcu::MessageBuilder builder(&m_testCtx.getLog()); builder << "Transform feedback varyings: {"; for (int ndx = 0; ndx < (int)programDefinition->getTransformFeedbackVaryings().size(); ++ndx) { if (ndx) builder << ", "; builder << "\"" << programDefinition->getTransformFeedbackVaryings()[ndx] << "\""; } builder << "}" << tcu::TestLog::EndMessage; } m_testCtx.getLog() << program; if (!program.isOk()) { m_testCtx.getLog() << tcu::TestLog::Message << "Program build failed, checking if program exceeded implementation limits" << tcu::TestLog::EndMessage; checkProgramResourceUsage(programDefinition, m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); // within limits throw tcu::TestError("could not build program"); } } // Check interface props switch (m_queryTarget.interface) { case PROGRAMINTERFACE_UNIFORM: { const VariableSearchFilter uniformFilter = VariableSearchFilter::createStorageFilter(glu::STORAGE_UNIFORM); const TypeValidator typeValidator(m_context, program.getProgram(), uniformFilter); const ArraySizeValidator arraySizeValidator(m_context, program.getProgram(), -1, uniformFilter); const ArrayStrideValidator arrayStrideValidator(m_context, program.getProgram(), uniformFilter); const BlockIndexValidator blockIndexValidator(m_context, program.getProgram(), uniformFilter); const IsRowMajorValidator isRowMajorValidator(m_context, program.getProgram(), uniformFilter); const MatrixStrideValidator matrixStrideValidator(m_context, program.getProgram(), uniformFilter); const AtomicCounterBufferIndexVerifier atomicCounterBufferIndexVerifier(m_context, program.getProgram(), uniformFilter); const LocationValidator locationValidator(m_context, program.getProgram(), uniformFilter); const VariableNameLengthValidator nameLengthValidator(m_context, program.getProgram(), uniformFilter); const OffsetValidator offsetVerifier(m_context, program.getProgram(), uniformFilter); const VariableReferencedByShaderValidator referencedByVertexVerifier(m_context, glu::SHADERTYPE_VERTEX, uniformFilter); const VariableReferencedByShaderValidator referencedByFragmentVerifier(m_context, glu::SHADERTYPE_FRAGMENT, uniformFilter); const VariableReferencedByShaderValidator referencedByComputeVerifier(m_context, glu::SHADERTYPE_COMPUTE, uniformFilter); const VariableReferencedByShaderValidator referencedByGeometryVerifier(m_context, glu::SHADERTYPE_GEOMETRY, uniformFilter); const VariableReferencedByShaderValidator referencedByTessControlVerifier( m_context, glu::SHADERTYPE_TESSELLATION_CONTROL, uniformFilter); const VariableReferencedByShaderValidator referencedByTessEvaluationVerifier( m_context, glu::SHADERTYPE_TESSELLATION_EVALUATION, uniformFilter); const TestProperty allProperties[] = { {GL_ARRAY_SIZE, &arraySizeValidator}, {GL_ARRAY_STRIDE, &arrayStrideValidator}, {GL_ATOMIC_COUNTER_BUFFER_INDEX, &atomicCounterBufferIndexVerifier}, {GL_BLOCK_INDEX, &blockIndexValidator}, {GL_IS_ROW_MAJOR, &isRowMajorValidator}, {GL_LOCATION, &locationValidator}, {GL_MATRIX_STRIDE, &matrixStrideValidator}, {GL_NAME_LENGTH, &nameLengthValidator}, {GL_OFFSET, &offsetVerifier}, {GL_REFERENCED_BY_VERTEX_SHADER, &referencedByVertexVerifier}, {GL_REFERENCED_BY_FRAGMENT_SHADER, &referencedByFragmentVerifier}, {GL_REFERENCED_BY_COMPUTE_SHADER, &referencedByComputeVerifier}, {GL_REFERENCED_BY_GEOMETRY_SHADER, &referencedByGeometryVerifier}, {GL_REFERENCED_BY_TESS_CONTROL_SHADER, &referencedByTessControlVerifier}, {GL_REFERENCED_BY_TESS_EVALUATION_SHADER, &referencedByTessEvaluationVerifier}, {GL_TYPE, &typeValidator}, }; for (int targetResourceNdx = 0; targetResourceNdx < (int)targetResources.size(); ++targetResourceNdx) { const tcu::ScopedLogSection section(m_testCtx.getLog(), "UniformResource", "Uniform resource \"" + targetResources[targetResourceNdx] + "\""); const glw::Functions &gl = m_context.getRenderContext().getFunctions(); std::vector props; std::vector validators; for (int propNdx = 0; propNdx < DE_LENGTH_OF_ARRAY(allProperties); ++propNdx) { if (allProperties[propNdx].validator->isSelected(m_queryTarget.propFlags) && allProperties[propNdx].validator->isSupported()) { props.push_back(allProperties[propNdx].prop); validators.push_back(allProperties[propNdx].validator); } } DE_ASSERT(!props.empty()); queryAndValidateProps(m_testCtx, gl, program.getProgram(), m_queryTarget.interface, targetResources[targetResourceNdx].c_str(), programDefinition, props, validators); } break; } case PROGRAMINTERFACE_UNIFORM_BLOCK: case PROGRAMINTERFACE_SHADER_STORAGE_BLOCK: { const glu::Storage storage = (m_queryTarget.interface == PROGRAMINTERFACE_UNIFORM_BLOCK) ? (glu::STORAGE_UNIFORM) : (glu::STORAGE_BUFFER); const VariableSearchFilter blockFilter = VariableSearchFilter::createStorageFilter(storage); const BlockNameLengthValidator nameLengthValidator(m_context, program.getProgram(), blockFilter); const BlockReferencedByShaderValidator referencedByVertexVerifier(m_context, glu::SHADERTYPE_VERTEX, blockFilter); const BlockReferencedByShaderValidator referencedByFragmentVerifier(m_context, glu::SHADERTYPE_FRAGMENT, blockFilter); const BlockReferencedByShaderValidator referencedByComputeVerifier(m_context, glu::SHADERTYPE_COMPUTE, blockFilter); const BlockReferencedByShaderValidator referencedByGeometryVerifier(m_context, glu::SHADERTYPE_GEOMETRY, blockFilter); const BlockReferencedByShaderValidator referencedByTessControlVerifier( m_context, glu::SHADERTYPE_TESSELLATION_CONTROL, blockFilter); const BlockReferencedByShaderValidator referencedByTessEvaluationVerifier( m_context, glu::SHADERTYPE_TESSELLATION_EVALUATION, blockFilter); const BufferBindingValidator bufferBindingValidator(m_context, program.getProgram(), blockFilter); const TestProperty allProperties[] = { {GL_NAME_LENGTH, &nameLengthValidator}, {GL_REFERENCED_BY_VERTEX_SHADER, &referencedByVertexVerifier}, {GL_REFERENCED_BY_FRAGMENT_SHADER, &referencedByFragmentVerifier}, {GL_REFERENCED_BY_COMPUTE_SHADER, &referencedByComputeVerifier}, {GL_REFERENCED_BY_GEOMETRY_SHADER, &referencedByGeometryVerifier}, {GL_REFERENCED_BY_TESS_CONTROL_SHADER, &referencedByTessControlVerifier}, {GL_REFERENCED_BY_TESS_EVALUATION_SHADER, &referencedByTessEvaluationVerifier}, {GL_BUFFER_BINDING, &bufferBindingValidator}, }; for (int targetResourceNdx = 0; targetResourceNdx < (int)targetResources.size(); ++targetResourceNdx) { const tcu::ScopedLogSection section(m_testCtx.getLog(), "BlockResource", "Interface block \"" + targetResources[targetResourceNdx] + "\""); const glw::Functions &gl = m_context.getRenderContext().getFunctions(); std::vector props; std::vector validators; for (int propNdx = 0; propNdx < DE_LENGTH_OF_ARRAY(allProperties); ++propNdx) { if (allProperties[propNdx].validator->isSelected(m_queryTarget.propFlags) && allProperties[propNdx].validator->isSupported()) { props.push_back(allProperties[propNdx].prop); validators.push_back(allProperties[propNdx].validator); } } DE_ASSERT(!props.empty()); queryAndValidateProps(m_testCtx, gl, program.getProgram(), m_queryTarget.interface, targetResources[targetResourceNdx].c_str(), programDefinition, props, validators); } break; } case PROGRAMINTERFACE_PROGRAM_INPUT: case PROGRAMINTERFACE_PROGRAM_OUTPUT: { const bool isInputCase = (m_queryTarget.interface == PROGRAMINTERFACE_PROGRAM_INPUT); const glu::Storage varyingStorage = (isInputCase) ? (glu::STORAGE_IN) : (glu::STORAGE_OUT); const glu::Storage patchStorage = (isInputCase) ? (glu::STORAGE_PATCH_IN) : (glu::STORAGE_PATCH_OUT); const glu::ShaderType shaderType = (isInputCase) ? (programDefinition->getFirstStage()) : (programDefinition->getLastStage()); const int unsizedArraySize = (isInputCase && shaderType == glu::SHADERTYPE_GEOMETRY) ? (1) // input points : (isInputCase && shaderType == glu::SHADERTYPE_TESSELLATION_CONTROL) ? (getMaxPatchVertices()) // input batch size : (!isInputCase && shaderType == glu::SHADERTYPE_TESSELLATION_CONTROL) ? (programDefinition->getTessellationNumOutputPatchVertices()) // output batch size : (isInputCase && shaderType == glu::SHADERTYPE_TESSELLATION_EVALUATION) ? (getMaxPatchVertices()) // input batch size : (-1); const VariableSearchFilter variableFilter = VariableSearchFilter::logicalAnd( VariableSearchFilter::createShaderTypeFilter(shaderType), VariableSearchFilter::logicalOr(VariableSearchFilter::createStorageFilter(varyingStorage), VariableSearchFilter::createStorageFilter(patchStorage))); const TypeValidator typeValidator(m_context, program.getProgram(), variableFilter); const ArraySizeValidator arraySizeValidator(m_context, program.getProgram(), unsizedArraySize, variableFilter); const LocationValidator locationValidator(m_context, program.getProgram(), variableFilter); const VariableNameLengthValidator nameLengthValidator(m_context, program.getProgram(), variableFilter); const VariableReferencedByShaderValidator referencedByVertexVerifier(m_context, glu::SHADERTYPE_VERTEX, variableFilter); const VariableReferencedByShaderValidator referencedByFragmentVerifier(m_context, glu::SHADERTYPE_FRAGMENT, variableFilter); const VariableReferencedByShaderValidator referencedByComputeVerifier(m_context, glu::SHADERTYPE_COMPUTE, variableFilter); const VariableReferencedByShaderValidator referencedByGeometryVerifier(m_context, glu::SHADERTYPE_GEOMETRY, variableFilter); const VariableReferencedByShaderValidator referencedByTessControlVerifier( m_context, glu::SHADERTYPE_TESSELLATION_CONTROL, variableFilter); const VariableReferencedByShaderValidator referencedByTessEvaluationVerifier( m_context, glu::SHADERTYPE_TESSELLATION_EVALUATION, variableFilter); const PerPatchValidator perPatchValidator(m_context, program.getProgram(), variableFilter); const TestProperty allProperties[] = { {GL_ARRAY_SIZE, &arraySizeValidator}, {GL_LOCATION, &locationValidator}, {GL_NAME_LENGTH, &nameLengthValidator}, {GL_REFERENCED_BY_VERTEX_SHADER, &referencedByVertexVerifier}, {GL_REFERENCED_BY_FRAGMENT_SHADER, &referencedByFragmentVerifier}, {GL_REFERENCED_BY_COMPUTE_SHADER, &referencedByComputeVerifier}, {GL_REFERENCED_BY_GEOMETRY_SHADER, &referencedByGeometryVerifier}, {GL_REFERENCED_BY_TESS_CONTROL_SHADER, &referencedByTessControlVerifier}, {GL_REFERENCED_BY_TESS_EVALUATION_SHADER, &referencedByTessEvaluationVerifier}, {GL_TYPE, &typeValidator}, {GL_IS_PER_PATCH, &perPatchValidator}, }; for (int targetResourceNdx = 0; targetResourceNdx < (int)targetResources.size(); ++targetResourceNdx) { const std::string resourceInterfaceName = (m_queryTarget.interface == PROGRAMINTERFACE_PROGRAM_INPUT) ? ("Input") : ("Output"); const tcu::ScopedLogSection section(m_testCtx.getLog(), "BlockResource", resourceInterfaceName + " resource \"" + targetResources[targetResourceNdx] + "\""); const glw::Functions &gl = m_context.getRenderContext().getFunctions(); std::vector props; std::vector validators; for (int propNdx = 0; propNdx < DE_LENGTH_OF_ARRAY(allProperties); ++propNdx) { if (allProperties[propNdx].validator->isSelected(m_queryTarget.propFlags) && allProperties[propNdx].validator->isSupported()) { props.push_back(allProperties[propNdx].prop); validators.push_back(allProperties[propNdx].validator); } } DE_ASSERT(!props.empty()); queryAndValidateProps(m_testCtx, gl, program.getProgram(), m_queryTarget.interface, targetResources[targetResourceNdx].c_str(), programDefinition, props, validators); } break; } case PROGRAMINTERFACE_BUFFER_VARIABLE: { const VariableSearchFilter variableFilter = VariableSearchFilter::createStorageFilter(glu::STORAGE_BUFFER); const TypeValidator typeValidator(m_context, program.getProgram(), variableFilter); const ArraySizeValidator arraySizeValidator(m_context, program.getProgram(), 0, variableFilter); const ArrayStrideValidator arrayStrideValidator(m_context, program.getProgram(), variableFilter); const BlockIndexValidator blockIndexValidator(m_context, program.getProgram(), variableFilter); const IsRowMajorValidator isRowMajorValidator(m_context, program.getProgram(), variableFilter); const MatrixStrideValidator matrixStrideValidator(m_context, program.getProgram(), variableFilter); const OffsetValidator offsetValidator(m_context, program.getProgram(), variableFilter); const VariableNameLengthValidator nameLengthValidator(m_context, program.getProgram(), variableFilter); const VariableReferencedByShaderValidator referencedByVertexVerifier(m_context, glu::SHADERTYPE_VERTEX, variableFilter); const VariableReferencedByShaderValidator referencedByFragmentVerifier(m_context, glu::SHADERTYPE_FRAGMENT, variableFilter); const VariableReferencedByShaderValidator referencedByComputeVerifier(m_context, glu::SHADERTYPE_COMPUTE, variableFilter); const VariableReferencedByShaderValidator referencedByGeometryVerifier(m_context, glu::SHADERTYPE_GEOMETRY, variableFilter); const VariableReferencedByShaderValidator referencedByTessControlVerifier( m_context, glu::SHADERTYPE_TESSELLATION_CONTROL, variableFilter); const VariableReferencedByShaderValidator referencedByTessEvaluationVerifier( m_context, glu::SHADERTYPE_TESSELLATION_EVALUATION, variableFilter); const TopLevelArraySizeValidator topLevelArraySizeValidator(m_context, program.getProgram(), variableFilter); const TopLevelArrayStrideValidator topLevelArrayStrideValidator(m_context, program.getProgram(), variableFilter); const TestProperty allProperties[] = { {GL_ARRAY_SIZE, &arraySizeValidator}, {GL_ARRAY_STRIDE, &arrayStrideValidator}, {GL_BLOCK_INDEX, &blockIndexValidator}, {GL_IS_ROW_MAJOR, &isRowMajorValidator}, {GL_MATRIX_STRIDE, &matrixStrideValidator}, {GL_NAME_LENGTH, &nameLengthValidator}, {GL_OFFSET, &offsetValidator}, {GL_REFERENCED_BY_VERTEX_SHADER, &referencedByVertexVerifier}, {GL_REFERENCED_BY_FRAGMENT_SHADER, &referencedByFragmentVerifier}, {GL_REFERENCED_BY_COMPUTE_SHADER, &referencedByComputeVerifier}, {GL_REFERENCED_BY_GEOMETRY_SHADER, &referencedByGeometryVerifier}, {GL_REFERENCED_BY_TESS_CONTROL_SHADER, &referencedByTessControlVerifier}, {GL_REFERENCED_BY_TESS_EVALUATION_SHADER, &referencedByTessEvaluationVerifier}, {GL_TOP_LEVEL_ARRAY_SIZE, &topLevelArraySizeValidator}, {GL_TOP_LEVEL_ARRAY_STRIDE, &topLevelArrayStrideValidator}, {GL_TYPE, &typeValidator}, }; for (int targetResourceNdx = 0; targetResourceNdx < (int)targetResources.size(); ++targetResourceNdx) { const tcu::ScopedLogSection section(m_testCtx.getLog(), "BufferVariableResource", "Buffer variable \"" + targetResources[targetResourceNdx] + "\""); const glw::Functions &gl = m_context.getRenderContext().getFunctions(); std::vector props; std::vector validators; for (int propNdx = 0; propNdx < DE_LENGTH_OF_ARRAY(allProperties); ++propNdx) { if (allProperties[propNdx].validator->isSelected(m_queryTarget.propFlags) && allProperties[propNdx].validator->isSupported()) { props.push_back(allProperties[propNdx].prop); validators.push_back(allProperties[propNdx].validator); } } DE_ASSERT(!props.empty()); queryAndValidateProps(m_testCtx, gl, program.getProgram(), m_queryTarget.interface, targetResources[targetResourceNdx].c_str(), programDefinition, props, validators); } break; } case PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING: { const TransformFeedbackTypeValidator typeValidator(m_context); const TransformFeedbackArraySizeValidator arraySizeValidator(m_context); const TransformFeedbackNameLengthValidator nameLengthValidator(m_context); const TestProperty allProperties[] = { {GL_ARRAY_SIZE, &arraySizeValidator}, {GL_NAME_LENGTH, &nameLengthValidator}, {GL_TYPE, &typeValidator}, }; for (int targetResourceNdx = 0; targetResourceNdx < (int)targetResources.size(); ++targetResourceNdx) { const tcu::ScopedLogSection section(m_testCtx.getLog(), "XFBVariableResource", "Transform feedback varying \"" + targetResources[targetResourceNdx] + "\""); const glw::Functions &gl = m_context.getRenderContext().getFunctions(); std::vector props; std::vector validators; for (int propNdx = 0; propNdx < DE_LENGTH_OF_ARRAY(allProperties); ++propNdx) { if (allProperties[propNdx].validator->isSelected(m_queryTarget.propFlags) && allProperties[propNdx].validator->isSupported()) { props.push_back(allProperties[propNdx].prop); validators.push_back(allProperties[propNdx].validator); } } DE_ASSERT(!props.empty()); queryAndValidateProps(m_testCtx, gl, program.getProgram(), m_queryTarget.interface, targetResources[targetResourceNdx].c_str(), programDefinition, props, validators); } break; } default: DE_ASSERT(false); } return STOP; } static bool checkLimit(glw::GLenum pname, int usage, const glw::Functions &gl, tcu::TestLog &log) { if (usage > 0) { glw::GLint limit = 0; gl.getIntegerv(pname, &limit); GLU_EXPECT_NO_ERROR(gl.getError(), "query limits"); log << tcu::TestLog::Message << "\t" << glu::getGettableStateStr(pname) << " = " << limit << ", test requires " << usage << tcu::TestLog::EndMessage; if (limit < usage) { log << tcu::TestLog::Message << "\t\tLimit exceeded" << tcu::TestLog::EndMessage; return false; } } return true; } static bool checkShaderResourceUsage(const ProgramInterfaceDefinition::Program *program, const ProgramInterfaceDefinition::Shader *shader, const glw::Functions &gl, tcu::TestLog &log) { const ProgramInterfaceDefinition::ShaderResourceUsage usage = getShaderResourceUsage(program, shader); switch (shader->getType()) { case glu::SHADERTYPE_VERTEX: { const struct { glw::GLenum pname; int usage; } restrictions[] = { {GL_MAX_VERTEX_ATTRIBS, usage.numInputVectors}, {GL_MAX_VERTEX_UNIFORM_COMPONENTS, usage.numDefaultBlockUniformComponents}, {GL_MAX_VERTEX_UNIFORM_VECTORS, usage.numUniformVectors}, {GL_MAX_VERTEX_UNIFORM_BLOCKS, usage.numUniformBlocks}, {GL_MAX_VERTEX_OUTPUT_COMPONENTS, usage.numOutputComponents}, {GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, usage.numSamplers}, {GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS, usage.numAtomicCounterBuffers}, {GL_MAX_VERTEX_ATOMIC_COUNTERS, usage.numAtomicCounters}, {GL_MAX_VERTEX_IMAGE_UNIFORMS, usage.numImages}, {GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS, usage.numCombinedUniformComponents}, {GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, usage.numShaderStorageBlocks}, }; bool allOk = true; log << tcu::TestLog::Message << "Vertex shader:" << tcu::TestLog::EndMessage; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(restrictions); ++ndx) allOk &= checkLimit(restrictions[ndx].pname, restrictions[ndx].usage, gl, log); return allOk; } case glu::SHADERTYPE_FRAGMENT: { const struct { glw::GLenum pname; int usage; } restrictions[] = { {GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, usage.numDefaultBlockUniformComponents}, {GL_MAX_FRAGMENT_UNIFORM_VECTORS, usage.numUniformVectors}, {GL_MAX_FRAGMENT_UNIFORM_BLOCKS, usage.numUniformBlocks}, {GL_MAX_FRAGMENT_INPUT_COMPONENTS, usage.numInputComponents}, {GL_MAX_TEXTURE_IMAGE_UNITS, usage.numSamplers}, {GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS, usage.numAtomicCounterBuffers}, {GL_MAX_FRAGMENT_ATOMIC_COUNTERS, usage.numAtomicCounters}, {GL_MAX_FRAGMENT_IMAGE_UNIFORMS, usage.numImages}, {GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS, usage.numCombinedUniformComponents}, {GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, usage.numShaderStorageBlocks}, }; bool allOk = true; log << tcu::TestLog::Message << "Fragment shader:" << tcu::TestLog::EndMessage; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(restrictions); ++ndx) allOk &= checkLimit(restrictions[ndx].pname, restrictions[ndx].usage, gl, log); return allOk; } case glu::SHADERTYPE_COMPUTE: { const struct { glw::GLenum pname; int usage; } restrictions[] = { {GL_MAX_COMPUTE_UNIFORM_BLOCKS, usage.numUniformBlocks}, {GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, usage.numSamplers}, {GL_MAX_COMPUTE_UNIFORM_COMPONENTS, usage.numDefaultBlockUniformComponents}, {GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS, usage.numAtomicCounterBuffers}, {GL_MAX_COMPUTE_ATOMIC_COUNTERS, usage.numAtomicCounters}, {GL_MAX_COMPUTE_IMAGE_UNIFORMS, usage.numImages}, {GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS, usage.numCombinedUniformComponents}, {GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, usage.numShaderStorageBlocks}, }; bool allOk = true; log << tcu::TestLog::Message << "Compute shader:" << tcu::TestLog::EndMessage; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(restrictions); ++ndx) allOk &= checkLimit(restrictions[ndx].pname, restrictions[ndx].usage, gl, log); return allOk; } case glu::SHADERTYPE_GEOMETRY: { const int totalOutputComponents = program->getGeometryNumOutputVertices() * usage.numOutputComponents; const struct { glw::GLenum pname; int usage; } restrictions[] = { {GL_MAX_GEOMETRY_UNIFORM_COMPONENTS, usage.numDefaultBlockUniformComponents}, {GL_MAX_GEOMETRY_UNIFORM_BLOCKS, usage.numUniformBlocks}, {GL_MAX_GEOMETRY_INPUT_COMPONENTS, usage.numInputComponents}, {GL_MAX_GEOMETRY_OUTPUT_COMPONENTS, usage.numOutputComponents}, {GL_MAX_GEOMETRY_OUTPUT_VERTICES, (int)program->getGeometryNumOutputVertices()}, {GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, totalOutputComponents}, {GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, usage.numSamplers}, {GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS, usage.numAtomicCounterBuffers}, {GL_MAX_GEOMETRY_ATOMIC_COUNTERS, usage.numAtomicCounters}, {GL_MAX_GEOMETRY_IMAGE_UNIFORMS, usage.numImages}, {GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS, usage.numShaderStorageBlocks}, }; bool allOk = true; log << tcu::TestLog::Message << "Geometry shader:" << tcu::TestLog::EndMessage; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(restrictions); ++ndx) allOk &= checkLimit(restrictions[ndx].pname, restrictions[ndx].usage, gl, log); return allOk; } case glu::SHADERTYPE_TESSELLATION_CONTROL: { const int totalOutputComponents = program->getTessellationNumOutputPatchVertices() * usage.numOutputComponents + usage.numPatchOutputComponents; const struct { glw::GLenum pname; int usage; } restrictions[] = { {GL_MAX_PATCH_VERTICES, (int)program->getTessellationNumOutputPatchVertices()}, {GL_MAX_TESS_PATCH_COMPONENTS, usage.numPatchOutputComponents}, {GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS, usage.numDefaultBlockUniformComponents}, {GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, usage.numUniformBlocks}, {GL_MAX_TESS_CONTROL_INPUT_COMPONENTS, usage.numInputComponents}, {GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS, usage.numOutputComponents}, {GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS, totalOutputComponents}, {GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS, usage.numSamplers}, {GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS, usage.numAtomicCounterBuffers}, {GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS, usage.numAtomicCounters}, {GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, usage.numImages}, {GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS, usage.numShaderStorageBlocks}, }; bool allOk = true; log << tcu::TestLog::Message << "Tessellation control shader:" << tcu::TestLog::EndMessage; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(restrictions); ++ndx) allOk &= checkLimit(restrictions[ndx].pname, restrictions[ndx].usage, gl, log); return allOk; } case glu::SHADERTYPE_TESSELLATION_EVALUATION: { const struct { glw::GLenum pname; int usage; } restrictions[] = { {GL_MAX_PATCH_VERTICES, (int)program->getTessellationNumOutputPatchVertices()}, {GL_MAX_TESS_PATCH_COMPONENTS, usage.numPatchInputComponents}, {GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS, usage.numDefaultBlockUniformComponents}, {GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, usage.numUniformBlocks}, {GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS, usage.numInputComponents}, {GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS, usage.numOutputComponents}, {GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, usage.numSamplers}, {GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS, usage.numAtomicCounterBuffers}, {GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS, usage.numAtomicCounters}, {GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, usage.numImages}, {GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS, usage.numShaderStorageBlocks}, }; bool allOk = true; log << tcu::TestLog::Message << "Tessellation evaluation shader:" << tcu::TestLog::EndMessage; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(restrictions); ++ndx) allOk &= checkLimit(restrictions[ndx].pname, restrictions[ndx].usage, gl, log); return allOk; } default: DE_ASSERT(false); return false; } } static bool checkProgramCombinedResourceUsage(const ProgramInterfaceDefinition::Program *program, const glw::Functions &gl, tcu::TestLog &log) { const ProgramInterfaceDefinition::ProgramResourceUsage usage = getCombinedProgramResourceUsage(program); const struct { glw::GLenum pname; int usage; } restrictions[] = { {GL_MAX_UNIFORM_BUFFER_BINDINGS, usage.uniformBufferMaxBinding + 1}, {GL_MAX_UNIFORM_BLOCK_SIZE, usage.uniformBufferMaxSize}, {GL_MAX_COMBINED_UNIFORM_BLOCKS, usage.numUniformBlocks}, {GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS, usage.numCombinedVertexUniformComponents}, {GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS, usage.numCombinedFragmentUniformComponents}, {GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS, usage.numCombinedGeometryUniformComponents}, {GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS, usage.numCombinedTessControlUniformComponents}, {GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS, usage.numCombinedTessEvalUniformComponents}, {GL_MAX_VARYING_COMPONENTS, usage.numVaryingComponents}, {GL_MAX_VARYING_VECTORS, usage.numVaryingVectors}, {GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, usage.numCombinedSamplers}, {GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES, usage.numCombinedOutputResources}, {GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, usage.atomicCounterBufferMaxBinding + 1}, {GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE, usage.atomicCounterBufferMaxSize}, {GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS, usage.numAtomicCounterBuffers}, {GL_MAX_COMBINED_ATOMIC_COUNTERS, usage.numAtomicCounters}, {GL_MAX_IMAGE_UNITS, usage.maxImageBinding + 1}, {GL_MAX_COMBINED_IMAGE_UNIFORMS, usage.numCombinedImages}, {GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, usage.shaderStorageBufferMaxBinding + 1}, {GL_MAX_SHADER_STORAGE_BLOCK_SIZE, usage.shaderStorageBufferMaxSize}, {GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, usage.numShaderStorageBlocks}, {GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, usage.numXFBInterleavedComponents}, {GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, usage.numXFBSeparateAttribs}, {GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, usage.numXFBSeparateComponents}, {GL_MAX_DRAW_BUFFERS, usage.fragmentOutputMaxBinding + 1}, }; bool allOk = true; log << tcu::TestLog::Message << "Program combined:" << tcu::TestLog::EndMessage; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(restrictions); ++ndx) allOk &= checkLimit(restrictions[ndx].pname, restrictions[ndx].usage, gl, log); return allOk; } void checkProgramResourceUsage(const ProgramInterfaceDefinition::Program *program, const glw::Functions &gl, tcu::TestLog &log) { bool limitExceeded = false; for (int shaderNdx = 0; shaderNdx < (int)program->getShaders().size(); ++shaderNdx) limitExceeded |= !checkShaderResourceUsage(program, program->getShaders()[shaderNdx], gl, log); limitExceeded |= !checkProgramCombinedResourceUsage(program, gl, log); if (limitExceeded) { log << tcu::TestLog::Message << "One or more resource limits exceeded" << tcu::TestLog::EndMessage; throw tcu::NotSupportedError("one or more resource limits exceeded"); } } } // namespace Functional } // namespace gles31 } // namespace deqp