| /*------------------------------------------------------------------------- |
| * 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<VariablePathComponent>& 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 (deUint32 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))) |
| return true; |
| return m_extension == DE_NULL || m_contextInfo.isExtensionSupported(m_extension); |
| } |
| |
| bool PropValidator::isSelected (deUint32 caseFlags) const |
| { |
| return (caseFlags & (deUint32)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<VariablePathComponent>& 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<VariablePathComponent> 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<int>& 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<int> 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<char> 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent> 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<int>& 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<int>& 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<int>& 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<int>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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. (sanity 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<VariablePathComponent> path; |
| std::vector<std::string> 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<VariablePathComponent> 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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<VariablePathComponent>& 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); |
| } |
| |
| } // anonymous |
| |
| ProgramResourceQueryTestTarget::ProgramResourceQueryTestTarget (ProgramInterface interface_, deUint32 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) |
| { |
| deUint32 validStorageBits; |
| deUint32 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) |
| { |
| deUint32 validStorageBits; |
| deUint32 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<glw::GLenum>& props, |
| const std::vector<const PropValidator*>& 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<glw::GLint> 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()); |
| |
| const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)); |
| |
| if (programDefinition->hasStage(glu::SHADERTYPE_TESSELLATION_CONTROL) || |
| programDefinition->hasStage(glu::SHADERTYPE_TESSELLATION_EVALUATION)) |
| { |
| if (!supportsES32 && !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 (!supportsES32 && !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 (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")) |
| throw tcu::NotSupportedError("Test requires GL_EXT_geometry_shader extension"); |
| } |
| |
| if (programContainsIOBlocks(programDefinition)) |
| { |
| if (!supportsES32 && !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<std::string> 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<glw::GLenum> props; |
| std::vector<const PropValidator*> 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<glw::GLenum> props; |
| std::vector<const PropValidator*> 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<glw::GLenum> props; |
| std::vector<const PropValidator*> 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<glw::GLenum> props; |
| std::vector<const PropValidator*> 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<glw::GLenum> props; |
| std::vector<const PropValidator*> 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); |
| } |
| |
|