| /*------------------------------------------------------------------------- |
| * 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 tests. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es31fProgramInterfaceQueryTests.hpp" |
| #include "es31fProgramInterfaceQueryTestCase.hpp" |
| #include "es31fProgramInterfaceDefinition.hpp" |
| #include "es31fProgramInterfaceDefinitionUtil.hpp" |
| #include "tcuTestLog.hpp" |
| #include "tcuStringTemplate.hpp" |
| #include "gluShaderProgram.hpp" |
| #include "gluVarTypeUtil.hpp" |
| #include "gluStrUtil.hpp" |
| #include "gluContextInfo.hpp" |
| #include "glwFunctions.hpp" |
| #include "glwEnums.hpp" |
| #include "deRandom.hpp" |
| #include "deString.h" |
| #include "deStringUtil.hpp" |
| #include "deSharedPtr.hpp" |
| #include "deUniquePtr.hpp" |
| #include "deSTLUtil.hpp" |
| #include "deArrayUtil.hpp" |
| |
| #include <set> |
| #include <map> |
| |
| namespace deqp |
| { |
| namespace gles31 |
| { |
| namespace Functional |
| { |
| namespace |
| { |
| |
| 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 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()) |
| { |
| 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 std::string convertGLTypeNameToTestName (const char* glName) |
| { |
| // vectors and matrices are fine as is |
| { |
| if (deStringBeginsWith(glName, "vec") == DE_TRUE || |
| deStringBeginsWith(glName, "ivec") == DE_TRUE || |
| deStringBeginsWith(glName, "uvec") == DE_TRUE || |
| deStringBeginsWith(glName, "bvec") == DE_TRUE || |
| deStringBeginsWith(glName, "mat") == DE_TRUE) |
| return std::string(glName); |
| } |
| |
| // convert camel case to use underscore |
| { |
| std::ostringstream buf; |
| std::istringstream name (glName); |
| bool mergeNextToken = false; |
| bool previousTokenWasDigit = false; |
| |
| while (!name.eof()) |
| { |
| std::ostringstream token; |
| |
| while (name.peek() != EOF) |
| { |
| if ((de::isDigit((char)name.peek()) || de::isUpper((char)name.peek())) && token.tellp()) |
| break; |
| |
| token << de::toLower((char)name.get()); |
| } |
| |
| if (buf.str().empty() || mergeNextToken) |
| buf << token.str(); |
| else |
| buf << '_' << token.str(); |
| |
| // Single char causes next char to be merged (don't split initialisms or acronyms) unless it is 'D' after a number (split to ..._2d_acronym_aa |
| mergeNextToken = false; |
| if (token.tellp() == (std::streamoff)1) |
| { |
| if (!previousTokenWasDigit || token.str()[0] != 'd') |
| mergeNextToken = true; |
| |
| previousTokenWasDigit = de::isDigit(token.str()[0]); |
| } |
| else |
| previousTokenWasDigit = false; |
| } |
| |
| return buf.str(); |
| } |
| } |
| |
| static glw::GLenum getProgramInterfaceGLEnum (ProgramInterface interface) |
| { |
| static const glw::GLenum s_enums[] = |
| { |
| GL_UNIFORM, // PROGRAMINTERFACE_UNIFORM |
| GL_UNIFORM_BLOCK, // PROGRAMINTERFACE_UNIFORM_BLOCK |
| GL_ATOMIC_COUNTER_BUFFER, // PROGRAMINTERFACE_ATOMIC_COUNTER_BUFFER |
| GL_PROGRAM_INPUT, // PROGRAMINTERFACE_PROGRAM_INPUT |
| GL_PROGRAM_OUTPUT, // PROGRAMINTERFACE_PROGRAM_OUTPUT |
| GL_TRANSFORM_FEEDBACK_VARYING, // PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING |
| GL_BUFFER_VARIABLE, // PROGRAMINTERFACE_BUFFER_VARIABLE |
| GL_SHADER_STORAGE_BLOCK, // PROGRAMINTERFACE_SHADER_STORAGE_BLOCK |
| }; |
| |
| return de::getSizedArrayElement<PROGRAMINTERFACE_LAST>(s_enums, interface); |
| } |
| |
| static glu::ShaderType getShaderMaskFirstStage (deUint32 mask) |
| { |
| if (mask & (1u << glu::SHADERTYPE_COMPUTE)) |
| return glu::SHADERTYPE_COMPUTE; |
| |
| if (mask & (1u << glu::SHADERTYPE_VERTEX)) |
| return glu::SHADERTYPE_VERTEX; |
| |
| if (mask & (1u << glu::SHADERTYPE_TESSELLATION_CONTROL)) |
| return glu::SHADERTYPE_TESSELLATION_CONTROL; |
| |
| if (mask & (1u << glu::SHADERTYPE_TESSELLATION_EVALUATION)) |
| return glu::SHADERTYPE_TESSELLATION_EVALUATION; |
| |
| if (mask & (1u << glu::SHADERTYPE_GEOMETRY)) |
| return glu::SHADERTYPE_GEOMETRY; |
| |
| if (mask & (1u << glu::SHADERTYPE_FRAGMENT)) |
| return glu::SHADERTYPE_FRAGMENT; |
| |
| DE_ASSERT(false); |
| return glu::SHADERTYPE_LAST; |
| } |
| |
| static glu::ShaderType getShaderMaskLastStage (deUint32 mask) |
| { |
| if (mask & (1u << glu::SHADERTYPE_FRAGMENT)) |
| return glu::SHADERTYPE_FRAGMENT; |
| |
| if (mask & (1u << glu::SHADERTYPE_GEOMETRY)) |
| return glu::SHADERTYPE_GEOMETRY; |
| |
| if (mask & (1u << glu::SHADERTYPE_TESSELLATION_EVALUATION)) |
| return glu::SHADERTYPE_TESSELLATION_EVALUATION; |
| |
| if (mask & (1u << glu::SHADERTYPE_TESSELLATION_CONTROL)) |
| return glu::SHADERTYPE_TESSELLATION_CONTROL; |
| |
| if (mask & (1u << glu::SHADERTYPE_VERTEX)) |
| return glu::SHADERTYPE_VERTEX; |
| |
| if (mask & (1u << glu::SHADERTYPE_COMPUTE)) |
| return glu::SHADERTYPE_COMPUTE; |
| |
| DE_ASSERT(false); |
| return glu::SHADERTYPE_LAST; |
| } |
| |
| static std::string specializeShader(Context& context, const char* code) |
| { |
| const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(context.getRenderContext().getType()); |
| std::map<std::string, std::string> specializationMap; |
| |
| specializationMap["GLSL_VERSION_DECL"] = glu::getGLSLVersionDeclaration(glslVersion); |
| |
| return tcu::StringTemplate(code).specialize(specializationMap); |
| } |
| |
| namespace ResourceDefinition |
| { |
| |
| class Node |
| { |
| public: |
| enum NodeType |
| { |
| TYPE_PROGRAM = 0, |
| TYPE_SHADER, |
| TYPE_DEFAULT_BLOCK, |
| TYPE_VARIABLE, |
| TYPE_INTERFACE_BLOCK, |
| TYPE_ARRAY_ELEMENT, |
| TYPE_STRUCT_MEMBER, |
| TYPE_STORAGE_QUALIFIER, |
| TYPE_LAYOUT_QUALIFIER, |
| TYPE_SHADER_SET, |
| TYPE_INTERPOLATION_QUALIFIER, |
| TYPE_TRANSFORM_FEEDBACK_TARGET, |
| |
| TYPE_LAST |
| }; |
| |
| typedef de::SharedPtr<const Node> SharedPtr; |
| |
| Node (NodeType type, const SharedPtr& enclosingNode) : m_type(type), m_enclosingNode(enclosingNode) { DE_ASSERT(type < TYPE_LAST); } |
| virtual ~Node (void) { } |
| |
| inline const Node* getEnclosingNode (void) const { return m_enclosingNode.get(); } |
| inline NodeType getType (void) const { return m_type; } |
| |
| private: |
| const NodeType m_type; |
| const SharedPtr m_enclosingNode; |
| }; |
| |
| class Program : public Node |
| { |
| public: |
| Program (bool separable = false) |
| : Node (TYPE_PROGRAM, SharedPtr()) |
| , m_separable (separable) |
| { |
| } |
| |
| const bool m_separable; |
| }; |
| |
| class Shader : public Node |
| { |
| public: |
| Shader (const SharedPtr& enclosingNode, glu::ShaderType type, glu::GLSLVersion version) |
| : Node (TYPE_SHADER, enclosingNode) |
| , m_type (type) |
| , m_version (version) |
| { |
| DE_ASSERT(enclosingNode->getType() == TYPE_PROGRAM); |
| DE_ASSERT(type < glu::SHADERTYPE_LAST); |
| } |
| |
| const glu::ShaderType m_type; |
| const glu::GLSLVersion m_version; |
| }; |
| |
| class DefaultBlock : public Node |
| { |
| public: |
| DefaultBlock (const SharedPtr& enclosing) |
| : Node(TYPE_DEFAULT_BLOCK, enclosing) |
| { |
| // enclosed by the shader |
| DE_ASSERT(enclosing->getType() == TYPE_SHADER || |
| enclosing->getType() == TYPE_SHADER_SET); |
| } |
| }; |
| |
| class StorageQualifier : public Node |
| { |
| public: |
| StorageQualifier (const SharedPtr& enclosing, glu::Storage storage) |
| : Node (TYPE_STORAGE_QUALIFIER, enclosing) |
| , m_storage (storage) |
| { |
| // not a part of any block |
| DE_ASSERT(enclosing->getType() == TYPE_DEFAULT_BLOCK); |
| } |
| |
| const glu::Storage m_storage; |
| }; |
| |
| class Variable : public Node |
| { |
| public: |
| Variable (const SharedPtr& enclosing, glu::DataType dataType) |
| : Node (TYPE_VARIABLE, enclosing) |
| , m_dataType (dataType) |
| { |
| DE_ASSERT(enclosing->getType() == TYPE_STORAGE_QUALIFIER || |
| enclosing->getType() == TYPE_LAYOUT_QUALIFIER || |
| enclosing->getType() == TYPE_INTERPOLATION_QUALIFIER || |
| enclosing->getType() == TYPE_INTERFACE_BLOCK || |
| enclosing->getType() == TYPE_ARRAY_ELEMENT || |
| enclosing->getType() == TYPE_STRUCT_MEMBER || |
| enclosing->getType() == TYPE_TRANSFORM_FEEDBACK_TARGET); |
| } |
| |
| const glu::DataType m_dataType; |
| }; |
| |
| class InterfaceBlock : public Node |
| { |
| public: |
| InterfaceBlock (const SharedPtr& enclosing, bool named) |
| : Node (TYPE_INTERFACE_BLOCK, enclosing) |
| , m_named (named) |
| { |
| // Must be storage qualified |
| const Node* storageNode = enclosing.get(); |
| while (storageNode->getType() == TYPE_ARRAY_ELEMENT || |
| storageNode->getType() == TYPE_LAYOUT_QUALIFIER) |
| { |
| storageNode = storageNode->getEnclosingNode(); |
| } |
| |
| DE_ASSERT(storageNode->getType() == TYPE_STORAGE_QUALIFIER); |
| DE_UNREF(storageNode); |
| } |
| |
| const bool m_named; |
| }; |
| |
| class ArrayElement : public Node |
| { |
| public: |
| ArrayElement (const SharedPtr& enclosing, int arraySize = DEFAULT_SIZE) |
| : Node (TYPE_ARRAY_ELEMENT, enclosing) |
| , m_arraySize (arraySize) |
| { |
| DE_ASSERT(enclosing->getType() == TYPE_STORAGE_QUALIFIER || |
| enclosing->getType() == TYPE_LAYOUT_QUALIFIER || |
| enclosing->getType() == TYPE_INTERPOLATION_QUALIFIER || |
| enclosing->getType() == TYPE_INTERFACE_BLOCK || |
| enclosing->getType() == TYPE_ARRAY_ELEMENT || |
| enclosing->getType() == TYPE_STRUCT_MEMBER || |
| enclosing->getType() == TYPE_TRANSFORM_FEEDBACK_TARGET); |
| } |
| |
| const int m_arraySize; |
| |
| enum |
| { |
| DEFAULT_SIZE = -1, |
| UNSIZED_ARRAY = -2, |
| }; |
| }; |
| |
| class StructMember : public Node |
| { |
| public: |
| StructMember (const SharedPtr& enclosing) |
| : Node(TYPE_STRUCT_MEMBER, enclosing) |
| { |
| DE_ASSERT(enclosing->getType() == TYPE_STORAGE_QUALIFIER || |
| enclosing->getType() == TYPE_LAYOUT_QUALIFIER || |
| enclosing->getType() == TYPE_INTERPOLATION_QUALIFIER || |
| enclosing->getType() == TYPE_INTERFACE_BLOCK || |
| enclosing->getType() == TYPE_ARRAY_ELEMENT || |
| enclosing->getType() == TYPE_STRUCT_MEMBER || |
| enclosing->getType() == TYPE_TRANSFORM_FEEDBACK_TARGET); |
| } |
| }; |
| |
| class LayoutQualifier : public Node |
| { |
| public: |
| LayoutQualifier (const SharedPtr& enclosing, const glu::Layout& layout) |
| : Node (TYPE_LAYOUT_QUALIFIER, enclosing) |
| , m_layout (layout) |
| { |
| DE_ASSERT(enclosing->getType() == TYPE_STORAGE_QUALIFIER || |
| enclosing->getType() == TYPE_LAYOUT_QUALIFIER || |
| enclosing->getType() == TYPE_INTERPOLATION_QUALIFIER || |
| enclosing->getType() == TYPE_DEFAULT_BLOCK || |
| enclosing->getType() == TYPE_INTERFACE_BLOCK); |
| } |
| |
| const glu::Layout m_layout; |
| }; |
| |
| class InterpolationQualifier : public Node |
| { |
| public: |
| InterpolationQualifier (const SharedPtr& enclosing, const glu::Interpolation& interpolation) |
| : Node (TYPE_INTERPOLATION_QUALIFIER, enclosing) |
| , m_interpolation (interpolation) |
| { |
| DE_ASSERT(enclosing->getType() == TYPE_STORAGE_QUALIFIER || |
| enclosing->getType() == TYPE_LAYOUT_QUALIFIER || |
| enclosing->getType() == TYPE_INTERPOLATION_QUALIFIER || |
| enclosing->getType() == TYPE_DEFAULT_BLOCK || |
| enclosing->getType() == TYPE_INTERFACE_BLOCK); |
| } |
| |
| const glu::Interpolation m_interpolation; |
| }; |
| |
| class ShaderSet : public Node |
| { |
| public: |
| ShaderSet (const SharedPtr& enclosing, glu::GLSLVersion version); |
| ShaderSet (const SharedPtr& enclosing, glu::GLSLVersion version, deUint32 stagesPresentBits, deUint32 stagesReferencingBits); |
| |
| void setStage (glu::ShaderType type, bool referencing); |
| bool isStagePresent (glu::ShaderType stage) const; |
| bool isStageReferencing (glu::ShaderType stage) const; |
| |
| deUint32 getReferencingMask (void) const; |
| |
| const glu::GLSLVersion m_version; |
| private: |
| bool m_stagePresent[glu::SHADERTYPE_LAST]; |
| bool m_stageReferencing[glu::SHADERTYPE_LAST]; |
| }; |
| |
| ShaderSet::ShaderSet (const SharedPtr& enclosing, glu::GLSLVersion version) |
| : Node (TYPE_SHADER_SET, enclosing) |
| , m_version (version) |
| { |
| DE_ASSERT(enclosing->getType() == TYPE_PROGRAM); |
| |
| deMemset(m_stagePresent, 0, sizeof(m_stagePresent)); |
| deMemset(m_stageReferencing, 0, sizeof(m_stageReferencing)); |
| } |
| |
| ShaderSet::ShaderSet (const SharedPtr& enclosing, |
| glu::GLSLVersion version, |
| deUint32 stagesPresentBits, |
| deUint32 stagesReferencingBits) |
| : Node (TYPE_SHADER_SET, enclosing) |
| , m_version (version) |
| { |
| for (deUint32 stageNdx = 0; stageNdx < glu::SHADERTYPE_LAST; ++stageNdx) |
| { |
| const deUint32 stageMask = (1u << stageNdx); |
| const bool stagePresent = (stagesPresentBits & stageMask) != 0; |
| const bool stageReferencing = (stagesReferencingBits & stageMask) != 0; |
| |
| DE_ASSERT(stagePresent || !stageReferencing); |
| |
| m_stagePresent[stageNdx] = stagePresent; |
| m_stageReferencing[stageNdx] = stageReferencing; |
| } |
| } |
| |
| void ShaderSet::setStage (glu::ShaderType type, bool referencing) |
| { |
| DE_ASSERT(type < glu::SHADERTYPE_LAST); |
| m_stagePresent[type] = true; |
| m_stageReferencing[type] = referencing; |
| } |
| |
| bool ShaderSet::isStagePresent (glu::ShaderType stage) const |
| { |
| DE_ASSERT(stage < glu::SHADERTYPE_LAST); |
| return m_stagePresent[stage]; |
| } |
| |
| bool ShaderSet::isStageReferencing (glu::ShaderType stage) const |
| { |
| DE_ASSERT(stage < glu::SHADERTYPE_LAST); |
| return m_stageReferencing[stage]; |
| } |
| |
| deUint32 ShaderSet::getReferencingMask (void) const |
| { |
| deUint32 mask = 0; |
| for (deUint32 stage = 0; stage < glu::SHADERTYPE_LAST; ++stage) |
| { |
| if (m_stageReferencing[stage]) |
| mask |= (1u << stage); |
| } |
| return mask; |
| } |
| |
| class TransformFeedbackTarget : public Node |
| { |
| public: |
| TransformFeedbackTarget (const SharedPtr& enclosing, const char* builtinVarName = DE_NULL) |
| : Node (TYPE_TRANSFORM_FEEDBACK_TARGET, enclosing) |
| , m_builtinVarName (builtinVarName) |
| { |
| } |
| |
| const char* const m_builtinVarName; |
| }; |
| |
| } // ResourceDefinition |
| |
| static glu::Precision getDataTypeDefaultPrecision (const glu::DataType& type) |
| { |
| if (glu::isDataTypeBoolOrBVec(type)) |
| return glu::PRECISION_LAST; |
| else if (glu::isDataTypeScalarOrVector(type) || glu::isDataTypeMatrix(type)) |
| return glu::PRECISION_HIGHP; |
| else if (glu::isDataTypeSampler(type)) |
| return glu::PRECISION_HIGHP; |
| else if (glu::isDataTypeImage(type)) |
| return glu::PRECISION_HIGHP; |
| else if (type == glu::TYPE_UINT_ATOMIC_COUNTER) |
| return glu::PRECISION_HIGHP; |
| |
| DE_ASSERT(false); |
| return glu::PRECISION_LAST; |
| } |
| |
| static de::MovePtr<ProgramInterfaceDefinition::Program> generateProgramDefinitionFromResource (const ResourceDefinition::Node* resource) |
| { |
| de::MovePtr<ProgramInterfaceDefinition::Program> program (new ProgramInterfaceDefinition::Program()); |
| const ResourceDefinition::Node* head = resource; |
| |
| if (head->getType() == ResourceDefinition::Node::TYPE_VARIABLE) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::Variable*>(resource)); |
| |
| enum BindingType |
| { |
| BINDING_VARIABLE, |
| BINDING_INTERFACE_BLOCK, |
| BINDING_DEFAULT_BLOCK |
| }; |
| |
| int structNdx = 0; |
| int autoAssignArraySize = 0; |
| const glu::DataType basicType = static_cast<const ResourceDefinition::Variable*>(resource)->m_dataType; |
| BindingType boundObject = BINDING_VARIABLE; |
| glu::VariableDeclaration variable (glu::VarType(basicType, getDataTypeDefaultPrecision(basicType)), "target"); |
| glu::InterfaceBlock interfaceBlock; |
| ProgramInterfaceDefinition::DefaultBlock defaultBlock; |
| std::vector<std::string> feedbackTargetVaryingPath; |
| bool feedbackTargetSet = false; |
| |
| // image specific |
| if (glu::isDataTypeImage(basicType)) |
| { |
| variable.memoryAccessQualifierBits |= glu::MEMORYACCESSQUALIFIER_READONLY_BIT; |
| variable.layout.binding = 1; |
| |
| if (basicType >= glu::TYPE_IMAGE_2D && basicType <= glu::TYPE_IMAGE_3D) |
| variable.layout.format = glu::FORMATLAYOUT_RGBA8; |
| else if (basicType >= glu::TYPE_INT_IMAGE_2D && basicType <= glu::TYPE_INT_IMAGE_3D) |
| variable.layout.format = glu::FORMATLAYOUT_RGBA8I; |
| else if (basicType >= glu::TYPE_UINT_IMAGE_2D && basicType <= glu::TYPE_UINT_IMAGE_3D) |
| variable.layout.format = glu::FORMATLAYOUT_RGBA8UI; |
| else |
| DE_ASSERT(false); |
| } |
| |
| // atomic counter specific |
| if (basicType == glu::TYPE_UINT_ATOMIC_COUNTER) |
| variable.layout.binding = 1; |
| |
| for (head = head->getEnclosingNode(); head; head = head->getEnclosingNode()) |
| { |
| if (head->getType() == ResourceDefinition::Node::TYPE_STORAGE_QUALIFIER) |
| { |
| const ResourceDefinition::StorageQualifier* qualifier = static_cast<const ResourceDefinition::StorageQualifier*>(head); |
| |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::StorageQualifier*>(head)); |
| |
| if (boundObject == BINDING_VARIABLE) |
| { |
| DE_ASSERT(variable.storage == glu::STORAGE_LAST); |
| variable.storage = qualifier->m_storage; |
| } |
| else if (boundObject == BINDING_INTERFACE_BLOCK) |
| { |
| DE_ASSERT(interfaceBlock.storage == glu::STORAGE_LAST); |
| interfaceBlock.storage = qualifier->m_storage; |
| } |
| else |
| DE_ASSERT(false); |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_LAYOUT_QUALIFIER) |
| { |
| const ResourceDefinition::LayoutQualifier* qualifier = static_cast<const ResourceDefinition::LayoutQualifier*>(head); |
| glu::Layout* targetLayout = DE_NULL; |
| |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::LayoutQualifier*>(head)); |
| |
| if (boundObject == BINDING_VARIABLE) |
| targetLayout = &variable.layout; |
| else if (boundObject == BINDING_INTERFACE_BLOCK) |
| targetLayout = &interfaceBlock.layout; |
| else |
| DE_ASSERT(false); |
| |
| if (qualifier->m_layout.location != -1) |
| targetLayout->location = qualifier->m_layout.location; |
| |
| if (qualifier->m_layout.binding != -1) |
| targetLayout->binding = qualifier->m_layout.binding; |
| |
| if (qualifier->m_layout.offset != -1) |
| targetLayout->offset = qualifier->m_layout.offset; |
| |
| if (qualifier->m_layout.format != glu::FORMATLAYOUT_LAST) |
| targetLayout->format = qualifier->m_layout.format; |
| |
| if (qualifier->m_layout.matrixOrder != glu::MATRIXORDER_LAST) |
| targetLayout->matrixOrder = qualifier->m_layout.matrixOrder; |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_INTERPOLATION_QUALIFIER) |
| { |
| const ResourceDefinition::InterpolationQualifier* qualifier = static_cast<const ResourceDefinition::InterpolationQualifier*>(head); |
| |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::InterpolationQualifier*>(head)); |
| |
| if (boundObject == BINDING_VARIABLE) |
| variable.interpolation = qualifier->m_interpolation; |
| else |
| DE_ASSERT(false); |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_ARRAY_ELEMENT) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::ArrayElement*>(head)); |
| |
| const ResourceDefinition::ArrayElement* arrayElement = static_cast<const ResourceDefinition::ArrayElement*>(head); |
| int arraySize; |
| |
| // Vary array size per level |
| if (arrayElement->m_arraySize == ResourceDefinition::ArrayElement::DEFAULT_SIZE) |
| { |
| if (--autoAssignArraySize <= 1) |
| autoAssignArraySize = 3; |
| |
| arraySize = autoAssignArraySize; |
| } |
| else if (arrayElement->m_arraySize == ResourceDefinition::ArrayElement::UNSIZED_ARRAY) |
| arraySize = glu::VarType::UNSIZED_ARRAY; |
| else |
| arraySize = arrayElement->m_arraySize; |
| |
| if (boundObject == BINDING_VARIABLE) |
| variable.varType = glu::VarType(variable.varType, arraySize); |
| else if (boundObject == BINDING_INTERFACE_BLOCK) |
| interfaceBlock.dimensions.push_back(arraySize); |
| else |
| DE_ASSERT(false); |
| |
| if (feedbackTargetSet) |
| feedbackTargetVaryingPath.back().append("[0]"); |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_STRUCT_MEMBER) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::StructMember*>(head)); |
| DE_ASSERT(boundObject == BINDING_VARIABLE); |
| |
| // Struct members cannot contain any qualifiers except precision |
| DE_ASSERT(variable.interpolation == glu::INTERPOLATION_LAST); |
| DE_ASSERT(variable.layout == glu::Layout()); |
| DE_ASSERT(variable.memoryAccessQualifierBits == 0); |
| DE_ASSERT(variable.storage == glu::STORAGE_LAST); |
| |
| { |
| glu::StructType* structPtr = new glu::StructType(("StructType" + de::toString(structNdx++)).c_str()); |
| structPtr->addMember(variable.name.c_str(), variable.varType); |
| |
| variable = glu::VariableDeclaration(glu::VarType(structPtr), "target"); |
| } |
| |
| if (feedbackTargetSet) |
| feedbackTargetVaryingPath.push_back("target"); |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::InterfaceBlock*>(head)); |
| DE_ASSERT(boundObject == BINDING_VARIABLE); |
| |
| const bool named = static_cast<const ResourceDefinition::InterfaceBlock*>(head)->m_named; |
| |
| boundObject = BINDING_INTERFACE_BLOCK; |
| |
| interfaceBlock.interfaceName = "TargetInterface"; |
| interfaceBlock.instanceName = (named) ? ("targetInstance") : (""); |
| interfaceBlock.variables.push_back(variable); |
| |
| if (feedbackTargetSet && !interfaceBlock.instanceName.empty()) |
| feedbackTargetVaryingPath.push_back(interfaceBlock.interfaceName); |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::DefaultBlock*>(head)); |
| DE_ASSERT(boundObject == BINDING_VARIABLE || boundObject == BINDING_INTERFACE_BLOCK); |
| |
| if (boundObject == BINDING_VARIABLE) |
| defaultBlock.variables.push_back(variable); |
| else if (boundObject == BINDING_INTERFACE_BLOCK) |
| defaultBlock.interfaceBlocks.push_back(interfaceBlock); |
| else |
| DE_ASSERT(false); |
| |
| boundObject = BINDING_DEFAULT_BLOCK; |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_SHADER) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::Shader*>(head)); |
| |
| const ResourceDefinition::Shader* shaderDef = static_cast<const ResourceDefinition::Shader*>(head); |
| ProgramInterfaceDefinition::Shader* shader = program->addShader(shaderDef->m_type, shaderDef->m_version); |
| |
| shader->getDefaultBlock() = defaultBlock; |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_SHADER_SET) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::ShaderSet*>(head)); |
| |
| const ResourceDefinition::ShaderSet* shaderDef = static_cast<const ResourceDefinition::ShaderSet*>(head); |
| |
| for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; ++shaderType) |
| { |
| if (shaderDef->isStagePresent((glu::ShaderType)shaderType)) |
| { |
| ProgramInterfaceDefinition::Shader* shader = program->addShader((glu::ShaderType)shaderType, shaderDef->m_version); |
| |
| if (shaderDef->isStageReferencing((glu::ShaderType)shaderType)) |
| shader->getDefaultBlock() = defaultBlock; |
| } |
| } |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_PROGRAM) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::Program*>(head)); |
| |
| const ResourceDefinition::Program* programDef = static_cast<const ResourceDefinition::Program*>(head); |
| |
| program->setSeparable(programDef->m_separable); |
| |
| DE_ASSERT(feedbackTargetSet == !feedbackTargetVaryingPath.empty()); |
| if (!feedbackTargetVaryingPath.empty()) |
| { |
| std::ostringstream buf; |
| |
| for (std::vector<std::string>::reverse_iterator it = feedbackTargetVaryingPath.rbegin(); it != feedbackTargetVaryingPath.rend(); ++it) |
| { |
| if (it != feedbackTargetVaryingPath.rbegin()) |
| buf << "."; |
| buf << *it; |
| } |
| |
| program->addTransformFeedbackVarying(buf.str()); |
| program->setTransformFeedbackMode(GL_INTERLEAVED_ATTRIBS); |
| } |
| break; |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_TRANSFORM_FEEDBACK_TARGET) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::TransformFeedbackTarget*>(head)); |
| |
| const ResourceDefinition::TransformFeedbackTarget* feedbackTarget = static_cast<const ResourceDefinition::TransformFeedbackTarget*>(head); |
| |
| DE_ASSERT(feedbackTarget->m_builtinVarName == DE_NULL); |
| DE_UNREF(feedbackTarget); |
| |
| feedbackTargetSet = true; |
| feedbackTargetVaryingPath.push_back(variable.name); |
| } |
| else |
| { |
| DE_ASSERT(DE_FALSE); |
| break; |
| } |
| } |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK || |
| head->getType() == ResourceDefinition::Node::TYPE_TRANSFORM_FEEDBACK_TARGET) |
| { |
| const char* feedbackTargetVaryingName = DE_NULL; |
| |
| // empty default block |
| |
| for (; head; head = head->getEnclosingNode()) |
| { |
| if (head->getType() == ResourceDefinition::Node::TYPE_SHADER) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::Shader*>(head)); |
| |
| const ResourceDefinition::Shader* shaderDef = static_cast<const ResourceDefinition::Shader*>(head); |
| |
| program->addShader(shaderDef->m_type, shaderDef->m_version); |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_SHADER_SET) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::ShaderSet*>(head)); |
| |
| const ResourceDefinition::ShaderSet* shaderDef = static_cast<const ResourceDefinition::ShaderSet*>(head); |
| |
| for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; ++shaderType) |
| if (shaderDef->isStagePresent((glu::ShaderType)shaderType)) |
| program->addShader((glu::ShaderType)shaderType, shaderDef->m_version); |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_PROGRAM) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::Program*>(head)); |
| |
| const ResourceDefinition::Program* programDef = static_cast<const ResourceDefinition::Program*>(head); |
| |
| program->setSeparable(programDef->m_separable); |
| if (feedbackTargetVaryingName) |
| { |
| program->addTransformFeedbackVarying(std::string(feedbackTargetVaryingName)); |
| program->setTransformFeedbackMode(GL_INTERLEAVED_ATTRIBS); |
| } |
| break; |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_TRANSFORM_FEEDBACK_TARGET) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::TransformFeedbackTarget*>(head)); |
| |
| const ResourceDefinition::TransformFeedbackTarget* feedbackTarget = static_cast<const ResourceDefinition::TransformFeedbackTarget*>(head); |
| |
| DE_ASSERT(feedbackTarget->m_builtinVarName != DE_NULL); |
| |
| feedbackTargetVaryingName = feedbackTarget->m_builtinVarName; |
| } |
| else if (head->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK) |
| { |
| } |
| else |
| { |
| DE_ASSERT(DE_FALSE); |
| break; |
| } |
| } |
| } |
| |
| if (program->hasStage(glu::SHADERTYPE_GEOMETRY)) |
| program->setGeometryNumOutputVertices(1); |
| if (program->hasStage(glu::SHADERTYPE_TESSELLATION_CONTROL) || program->hasStage(glu::SHADERTYPE_TESSELLATION_EVALUATION)) |
| program->setTessellationNumOutputPatchVertices(1); |
| |
| return program; |
| } |
| |
| static void checkAndLogProgram (const glu::ShaderProgram& program, const ProgramInterfaceDefinition::Program* programDefinition, const glw::Functions& gl, tcu::TestLog& log) |
| { |
| const tcu::ScopedLogSection section(log, "Program", "Program"); |
| |
| log << program; |
| if (!program.isOk()) |
| { |
| log << tcu::TestLog::Message << "Program build failed, checking if program exceeded implementation limits" << tcu::TestLog::EndMessage; |
| checkProgramResourceUsage(programDefinition, gl, log); |
| |
| // within limits |
| throw tcu::TestError("could not build program"); |
| } |
| } |
| |
| // Resource list query case |
| |
| class ResourceListTestCase : public TestCase |
| { |
| public: |
| ResourceListTestCase (Context& context, const ResourceDefinition::Node::SharedPtr& targetResource, ProgramInterface interface, const char* name = DE_NULL); |
| ~ResourceListTestCase (void); |
| |
| protected: |
| void init (void); |
| void deinit (void); |
| IterateResult iterate (void); |
| |
| void queryResourceList (std::vector<std::string>& dst, glw::GLuint program); |
| bool verifyResourceList (const std::vector<std::string>& resourceList, const std::vector<std::string>& expectedResources); |
| bool verifyResourceIndexQuery (const std::vector<std::string>& resourceList, const std::vector<std::string>& referenceResources, glw::GLuint program); |
| bool verifyMaxNameLength (const std::vector<std::string>& referenceResourceList, glw::GLuint program); |
| |
| static std::string genTestCaseName (ProgramInterface interface, const ResourceDefinition::Node*); |
| static bool isArrayedInterface (ProgramInterface interface, deUint32 stageBits); |
| |
| const ProgramInterface m_programInterface; |
| ResourceDefinition::Node::SharedPtr m_targetResource; |
| ProgramInterfaceDefinition::Program* m_programDefinition; |
| }; |
| |
| ResourceListTestCase::ResourceListTestCase (Context& context, const ResourceDefinition::Node::SharedPtr& targetResource, ProgramInterface interface, const char* name) |
| : TestCase (context, (name == DE_NULL) ? (genTestCaseName(interface, targetResource.get()).c_str()) : (name), "") |
| , m_programInterface (interface) |
| , m_targetResource (targetResource) |
| , m_programDefinition (DE_NULL) |
| { |
| // GL_ATOMIC_COUNTER_BUFFER: no resource names |
| DE_ASSERT(m_programInterface != PROGRAMINTERFACE_ATOMIC_COUNTER_BUFFER); |
| } |
| |
| ResourceListTestCase::~ResourceListTestCase (void) |
| { |
| deinit(); |
| } |
| |
| void ResourceListTestCase::init (void) |
| { |
| m_programDefinition = generateProgramDefinitionFromResource(m_targetResource.get()).release(); |
| const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)); |
| |
| if ((m_programDefinition->hasStage(glu::SHADERTYPE_TESSELLATION_CONTROL) || m_programDefinition->hasStage(glu::SHADERTYPE_TESSELLATION_EVALUATION)) && |
| !supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader")) |
| { |
| throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension"); |
| } |
| if (m_programDefinition->hasStage(glu::SHADERTYPE_GEOMETRY) && |
| !supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")) |
| { |
| throw tcu::NotSupportedError("Test requires GL_EXT_geometry_shader extension"); |
| } |
| if (programContainsIOBlocks(m_programDefinition) && |
| !supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_shader_io_blocks")) |
| { |
| throw tcu::NotSupportedError("Test requires GL_EXT_shader_io_blocks extension"); |
| } |
| } |
| |
| void ResourceListTestCase::deinit (void) |
| { |
| m_targetResource.clear(); |
| |
| delete m_programDefinition; |
| m_programDefinition = DE_NULL; |
| } |
| |
| ResourceListTestCase::IterateResult ResourceListTestCase::iterate (void) |
| { |
| const glu::ShaderProgram program(m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_programDefinition)); |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| checkAndLogProgram(program, m_programDefinition, m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| |
| // Check resource list |
| { |
| const tcu::ScopedLogSection section (m_testCtx.getLog(), "ResourceList", "Resource list"); |
| std::vector<std::string> resourceList; |
| std::vector<std::string> expectedResources; |
| |
| queryResourceList(resourceList, program.getProgram()); |
| expectedResources = getProgramInterfaceResourceList(m_programDefinition, m_programInterface); |
| |
| // verify the list and the expected list match |
| |
| if (!verifyResourceList(resourceList, expectedResources)) |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "invalid resource list"); |
| |
| // verify GetProgramResourceIndex() matches the indices of the list |
| |
| if (!verifyResourceIndexQuery(resourceList, expectedResources, program.getProgram())) |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "GetProgramResourceIndex returned unexpected values"); |
| |
| // Verify MAX_NAME_LENGTH |
| if (!verifyMaxNameLength(resourceList, program.getProgram())) |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "MAX_NAME_LENGTH invalid"); |
| } |
| |
| return STOP; |
| } |
| |
| void ResourceListTestCase::queryResourceList (std::vector<std::string>& dst, glw::GLuint program) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const glw::GLenum programInterface = getProgramInterfaceGLEnum(m_programInterface); |
| glw::GLint numActiveResources = 0; |
| glw::GLint maxNameLength = 0; |
| std::vector<char> buffer; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Querying " << glu::getProgramInterfaceName(programInterface) << " interface:" << tcu::TestLog::EndMessage; |
| |
| gl.getProgramInterfaceiv(program, programInterface, GL_ACTIVE_RESOURCES, &numActiveResources); |
| gl.getProgramInterfaceiv(program, programInterface, GL_MAX_NAME_LENGTH, &maxNameLength); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query interface"); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "\tGL_ACTIVE_RESOURCES = " << numActiveResources << "\n" |
| << "\tGL_MAX_NAME_LENGTH = " << maxNameLength |
| << tcu::TestLog::EndMessage; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Querying all active resources" << tcu::TestLog::EndMessage; |
| |
| buffer.resize(maxNameLength+1, '\0'); |
| |
| for (int resourceNdx = 0; resourceNdx < numActiveResources; ++resourceNdx) |
| { |
| glw::GLint written = 0; |
| |
| gl.getProgramResourceName(program, programInterface, resourceNdx, maxNameLength, &written, &buffer[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query resource name"); |
| |
| dst.push_back(std::string(&buffer[0], written)); |
| } |
| } |
| |
| bool ResourceListTestCase::verifyResourceList (const std::vector<std::string>& resourceList, const std::vector<std::string>& expectedResources) |
| { |
| bool error = false; |
| |
| // Log and compare resource lists |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "GL returned resources:" << tcu::TestLog::EndMessage; |
| |
| for (int ndx = 0; ndx < (int)resourceList.size(); ++ndx) |
| { |
| // dummyZero is a uniform that may be added by |
| // generateProgramInterfaceProgramSources. Omit it here to avoid |
| // confusion about the output. |
| if (resourceList[ndx] != getDummyZeroUniformName()) |
| m_testCtx.getLog() << tcu::TestLog::Message << "\t" << ndx << ": " << resourceList[ndx] << tcu::TestLog::EndMessage; |
| } |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Expected list of resources:" << tcu::TestLog::EndMessage; |
| |
| for (int ndx = 0; ndx < (int)expectedResources.size(); ++ndx) |
| m_testCtx.getLog() << tcu::TestLog::Message << "\t" << ndx << ": " << expectedResources[ndx] << tcu::TestLog::EndMessage; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Verifying resource list contents." << tcu::TestLog::EndMessage; |
| |
| for (int ndx = 0; ndx < (int)expectedResources.size(); ++ndx) |
| { |
| if (!de::contains(resourceList.begin(), resourceList.end(), expectedResources[ndx])) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, resource list did not contain active resource " << expectedResources[ndx] << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| } |
| |
| for (int ndx = 0; ndx < (int)resourceList.size(); ++ndx) |
| { |
| if (!de::contains(expectedResources.begin(), expectedResources.end(), resourceList[ndx])) |
| { |
| // Ignore all builtin variables or the variable dummyZero, |
| // mismatch causes errors otherwise. dummyZero is a uniform that |
| // may be added by generateProgramInterfaceProgramSources. |
| if (deStringBeginsWith(resourceList[ndx].c_str(), "gl_") == DE_FALSE && |
| resourceList[ndx] != getDummyZeroUniformName()) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, resource list contains unexpected resource name " << resourceList[ndx] << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| else |
| m_testCtx.getLog() << tcu::TestLog::Message << "Note, resource list contains unknown built-in " << resourceList[ndx] << ". This variable is ignored." << tcu::TestLog::EndMessage; |
| } |
| } |
| |
| return !error; |
| } |
| |
| bool ResourceListTestCase::verifyResourceIndexQuery (const std::vector<std::string>& resourceList, const std::vector<std::string>& referenceResources, glw::GLuint program) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const glw::GLenum programInterface = getProgramInterfaceGLEnum(m_programInterface); |
| bool error = false; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Verifying GetProgramResourceIndex returns correct indices for resource names." << tcu::TestLog::EndMessage; |
| |
| for (int ndx = 0; ndx < (int)referenceResources.size(); ++ndx) |
| { |
| const glw::GLuint index = gl.getProgramResourceIndex(program, programInterface, referenceResources[ndx].c_str()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query resource index"); |
| |
| if (index == GL_INVALID_INDEX) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, for active resource \"" << referenceResources[ndx] << "\" got index GL_INVALID_INDEX." << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| else if ((int)index >= (int)resourceList.size()) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, for active resource \"" << referenceResources[ndx] << "\" got index " << index << " (larger or equal to GL_ACTIVE_RESOURCES)." << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| else if (resourceList[index] != referenceResources[ndx]) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, for active resource \"" << referenceResources[ndx] << "\" got index (index = " << index << ") of another resource (" << resourceList[index] << ")." << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| } |
| |
| // Query for "name" should match "name[0]" except for XFB |
| |
| if (m_programInterface != PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING) |
| { |
| for (int ndx = 0; ndx < (int)referenceResources.size(); ++ndx) |
| { |
| if (de::endsWith(referenceResources[ndx], "[0]")) |
| { |
| const std::string queryString = referenceResources[ndx].substr(0, referenceResources[ndx].length()-3); |
| const glw::GLuint index = gl.getProgramResourceIndex(program, programInterface, queryString.c_str()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query resource index"); |
| |
| if (index == GL_INVALID_INDEX) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for \"" << queryString << "\" resulted in index GL_INVALID_INDEX." << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| else if ((int)index >= (int)resourceList.size()) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for \"" << queryString << "\" resulted in index " << index << " (larger or equal to GL_ACTIVE_RESOURCES)." << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| else if (resourceList[index] != queryString + "[0]") |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for \"" << queryString << "\" got index (index = " << index << ") of another resource (\"" << resourceList[index] << "\")." << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| } |
| } |
| } |
| |
| return !error; |
| } |
| |
| bool ResourceListTestCase::verifyMaxNameLength (const std::vector<std::string>& resourceList, glw::GLuint program) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const glw::GLenum programInterface = getProgramInterfaceGLEnum(m_programInterface); |
| glw::GLint maxNameLength = 0; |
| glw::GLint expectedMaxNameLength = 0; |
| |
| gl.getProgramInterfaceiv(program, programInterface, GL_MAX_NAME_LENGTH, &maxNameLength); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query interface"); |
| |
| for (int ndx = 0; ndx < (int)resourceList.size(); ++ndx) |
| expectedMaxNameLength = de::max(expectedMaxNameLength, (int)resourceList[ndx].size() + 1); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Verifying MAX_NAME_LENGTH, expecting " << expectedMaxNameLength << " (i.e. consistent with the queried resource list)" << tcu::TestLog::EndMessage; |
| |
| if (expectedMaxNameLength != maxNameLength) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, got " << maxNameLength << tcu::TestLog::EndMessage; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| std::string ResourceListTestCase::genTestCaseName (ProgramInterface interface, const ResourceDefinition::Node* root) |
| { |
| bool isImplicitlySizedArray = false; |
| bool hasVariable = false; |
| bool accumulateName = true; |
| std::string buf = "var"; |
| std::string prefix; |
| |
| for (const ResourceDefinition::Node* node = root; node; node = node->getEnclosingNode()) |
| { |
| switch (node->getType()) |
| { |
| case ResourceDefinition::Node::TYPE_VARIABLE: |
| { |
| hasVariable = true; |
| break; |
| } |
| |
| case ResourceDefinition::Node::TYPE_STRUCT_MEMBER: |
| { |
| if (accumulateName) |
| buf += "_struct"; |
| break; |
| } |
| |
| case ResourceDefinition::Node::TYPE_ARRAY_ELEMENT: |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::ArrayElement*>(node)); |
| const ResourceDefinition::ArrayElement* arrayElement = static_cast<const ResourceDefinition::ArrayElement*>(node); |
| |
| isImplicitlySizedArray = (arrayElement->m_arraySize == ResourceDefinition::ArrayElement::UNSIZED_ARRAY); |
| |
| if (accumulateName) |
| buf += "_array"; |
| break; |
| } |
| |
| case ResourceDefinition::Node::TYPE_STORAGE_QUALIFIER: |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::StorageQualifier*>(node)); |
| const ResourceDefinition::StorageQualifier* storageDef = static_cast<const ResourceDefinition::StorageQualifier*>(node); |
| |
| if (storageDef->m_storage == glu::STORAGE_PATCH_IN || |
| storageDef->m_storage == glu::STORAGE_PATCH_OUT) |
| { |
| if (accumulateName) |
| prefix += "patch_"; |
| } |
| break; |
| } |
| |
| case ResourceDefinition::Node::TYPE_SHADER: |
| case ResourceDefinition::Node::TYPE_SHADER_SET: |
| { |
| bool arrayedInterface; |
| |
| if (node->getType() == ResourceDefinition::Node::TYPE_SHADER) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::Shader*>(node)); |
| const ResourceDefinition::Shader* shaderDef = static_cast<const ResourceDefinition::Shader*>(node); |
| |
| arrayedInterface = isArrayedInterface(interface, (1u << shaderDef->m_type)); |
| } |
| else |
| { |
| DE_ASSERT(node->getType() == ResourceDefinition::Node::TYPE_SHADER_SET); |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::ShaderSet*>(node)); |
| const ResourceDefinition::ShaderSet* shaderDef = static_cast<const ResourceDefinition::ShaderSet*>(node); |
| |
| arrayedInterface = isArrayedInterface(interface, shaderDef->getReferencingMask()); |
| } |
| |
| if (arrayedInterface && isImplicitlySizedArray) |
| { |
| // omit implicit arrayness from name, i.e. remove trailing "_array" |
| DE_ASSERT(de::endsWith(buf, "_array")); |
| buf = buf.substr(0, buf.length() - 6); |
| } |
| |
| break; |
| } |
| |
| case ResourceDefinition::Node::TYPE_INTERFACE_BLOCK: |
| { |
| accumulateName = false; |
| break; |
| } |
| |
| default: |
| break; |
| } |
| } |
| |
| if (!hasVariable) |
| return prefix + "empty"; |
| else |
| return prefix + buf; |
| } |
| |
| bool ResourceListTestCase::isArrayedInterface (ProgramInterface interface, deUint32 stageBits) |
| { |
| if (interface == PROGRAMINTERFACE_PROGRAM_INPUT) |
| { |
| const glu::ShaderType firstStage = getShaderMaskFirstStage(stageBits); |
| return firstStage == glu::SHADERTYPE_TESSELLATION_CONTROL || |
| firstStage == glu::SHADERTYPE_TESSELLATION_EVALUATION || |
| firstStage == glu::SHADERTYPE_GEOMETRY; |
| } |
| else if (interface == PROGRAMINTERFACE_PROGRAM_OUTPUT) |
| { |
| const glu::ShaderType lastStage = getShaderMaskLastStage(stageBits); |
| return lastStage == glu::SHADERTYPE_TESSELLATION_CONTROL; |
| } |
| return false; |
| } |
| |
| // Resouce property query case |
| |
| class ResourceTestCase : public ProgramInterfaceQueryTestCase |
| { |
| public: |
| ResourceTestCase (Context& context, const ResourceDefinition::Node::SharedPtr& targetResource, const ProgramResourceQueryTestTarget& queryTarget, const char* name = DE_NULL); |
| ~ResourceTestCase (void); |
| |
| private: |
| void init (void); |
| void deinit (void); |
| const ProgramInterfaceDefinition::Program* getProgramDefinition (void) const; |
| std::vector<std::string> getQueryTargetResources (void) const; |
| |
| static std::string genTestCaseName (const ResourceDefinition::Node*); |
| static std::string genMultilineDescription (const ResourceDefinition::Node*); |
| |
| ResourceDefinition::Node::SharedPtr m_targetResource; |
| ProgramInterfaceDefinition::Program* m_program; |
| std::vector<std::string> m_targetResources; |
| }; |
| |
| ResourceTestCase::ResourceTestCase (Context& context, const ResourceDefinition::Node::SharedPtr& targetResource, const ProgramResourceQueryTestTarget& queryTarget, const char* name) |
| : ProgramInterfaceQueryTestCase (context, (name == DE_NULL) ? (genTestCaseName(targetResource.get()).c_str()) : (name), "", queryTarget) |
| , m_targetResource (targetResource) |
| , m_program (DE_NULL) |
| { |
| } |
| |
| ResourceTestCase::~ResourceTestCase (void) |
| { |
| deinit(); |
| } |
| |
| void ResourceTestCase::init (void) |
| { |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << genMultilineDescription(m_targetResource.get()) |
| << tcu::TestLog::EndMessage; |
| |
| // Program |
| { |
| // Generate interface with target resource |
| m_program = generateProgramDefinitionFromResource(m_targetResource.get()).release(); |
| m_targetResources = getProgramInterfaceResourceList(m_program, getTargetInterface()); |
| } |
| } |
| |
| void ResourceTestCase::deinit (void) |
| { |
| m_targetResource.clear(); |
| |
| delete m_program; |
| m_program = DE_NULL; |
| |
| m_targetResources = std::vector<std::string>(); |
| } |
| |
| const ProgramInterfaceDefinition::Program* ResourceTestCase::getProgramDefinition (void) const |
| { |
| return m_program; |
| } |
| |
| std::vector<std::string> ResourceTestCase::getQueryTargetResources (void) const |
| { |
| return m_targetResources; |
| } |
| |
| std::string ResourceTestCase::genTestCaseName (const ResourceDefinition::Node* resource) |
| { |
| if (resource->getType() == ResourceDefinition::Node::TYPE_VARIABLE) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::Variable*>(resource)); |
| |
| const ResourceDefinition::Variable* variable = static_cast<const ResourceDefinition::Variable*>(resource); |
| |
| return convertGLTypeNameToTestName(glu::getDataTypeName(variable->m_dataType)); |
| } |
| |
| DE_ASSERT(false); |
| return ""; |
| } |
| |
| std::string ResourceTestCase::genMultilineDescription (const ResourceDefinition::Node* resource) |
| { |
| if (resource->getType() == ResourceDefinition::Node::TYPE_VARIABLE) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::Variable*>(resource)); |
| |
| const ResourceDefinition::Variable* varDef = static_cast<const ResourceDefinition::Variable*>(resource); |
| std::ostringstream buf; |
| std::ostringstream structureDescriptor; |
| std::string uniformType; |
| |
| for (const ResourceDefinition::Node* node = resource; node; node = node->getEnclosingNode()) |
| { |
| if (node->getType() == ResourceDefinition::Node::TYPE_STORAGE_QUALIFIER) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::StorageQualifier*>(node)); |
| |
| const ResourceDefinition::StorageQualifier* storageDef = static_cast<const ResourceDefinition::StorageQualifier*>(node); |
| |
| uniformType = std::string(" ") + glu::getStorageName(storageDef->m_storage); |
| structureDescriptor << "\n\tdeclared as \"" << glu::getStorageName(storageDef->m_storage) << "\""; |
| } |
| |
| if (node->getType() == ResourceDefinition::Node::TYPE_ARRAY_ELEMENT) |
| structureDescriptor << "\n\tarray"; |
| |
| if (node->getType() == ResourceDefinition::Node::TYPE_STRUCT_MEMBER) |
| structureDescriptor << "\n\tin a struct"; |
| |
| if (node->getType() == ResourceDefinition::Node::TYPE_DEFAULT_BLOCK) |
| structureDescriptor << "\n\tin the default block"; |
| |
| if (node->getType() == ResourceDefinition::Node::TYPE_INTERFACE_BLOCK) |
| structureDescriptor << "\n\tin an interface block"; |
| } |
| |
| buf << "Querying properties of " << glu::getDataTypeName(varDef->m_dataType) << uniformType << " variable.\n" |
| << "Variable is:\n" |
| << "\t" << glu::getDataTypeName(varDef->m_dataType) |
| << structureDescriptor.str(); |
| |
| return buf.str(); |
| } |
| else if (resource->getType() == ResourceDefinition::Node::TYPE_TRANSFORM_FEEDBACK_TARGET) |
| { |
| DE_ASSERT(dynamic_cast<const ResourceDefinition::TransformFeedbackTarget*>(resource)); |
| |
| const ResourceDefinition::TransformFeedbackTarget* xfbDef = static_cast<const ResourceDefinition::TransformFeedbackTarget*>(resource); |
| |
| DE_ASSERT(xfbDef->m_builtinVarName); |
| |
| return std::string("Querying properties of a builtin variable ") + xfbDef->m_builtinVarName; |
| } |
| |
| DE_ASSERT(false); |
| return DE_NULL; |
| } |
| |
| class ResourceNameBufferLimitCase : public TestCase |
| { |
| public: |
| ResourceNameBufferLimitCase (Context& context, const char* name, const char* description); |
| ~ResourceNameBufferLimitCase (void); |
| |
| private: |
| IterateResult iterate (void); |
| }; |
| |
| ResourceNameBufferLimitCase::ResourceNameBufferLimitCase (Context& context, const char* name, const char* description) |
| : TestCase(context, name, description) |
| { |
| } |
| |
| ResourceNameBufferLimitCase::~ResourceNameBufferLimitCase (void) |
| { |
| } |
| |
| ResourceNameBufferLimitCase::IterateResult ResourceNameBufferLimitCase::iterate (void) |
| { |
| static const char* const computeSource = "${GLSL_VERSION_DECL}\n" |
| "layout(local_size_x = 1) in;\n" |
| "uniform highp int u_uniformWithALongName;\n" |
| "writeonly buffer OutputBufferBlock { highp int b_output_int; };\n" |
| "void main ()\n" |
| "{\n" |
| " b_output_int = u_uniformWithALongName;\n" |
| "}\n"; |
| |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(specializeShader(m_context, computeSource))); |
| glw::GLuint uniformIndex; |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| // Log program |
| { |
| const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program", "Program"); |
| |
| m_testCtx.getLog() << program; |
| if (!program.isOk()) |
| throw tcu::TestError("could not build program"); |
| } |
| |
| uniformIndex = gl.getProgramResourceIndex(program.getProgram(), GL_UNIFORM, "u_uniformWithALongName"); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query resource index"); |
| |
| if (uniformIndex == GL_INVALID_INDEX) |
| throw tcu::TestError("Uniform u_uniformWithALongName resource index was GL_INVALID_INDEX"); |
| |
| // Query with different sized buffers, len("u_uniformWithALongName") == 22 |
| |
| { |
| static const struct |
| { |
| const char* description; |
| int querySize; |
| bool returnLength; |
| } querySizes[] = |
| { |
| { "Query to larger buffer", 24, true }, |
| { "Query to buffer the same size", 23, true }, |
| { "Query to one byte too small buffer", 22, true }, |
| { "Query to one byte buffer", 1, true }, |
| { "Query to zero sized buffer", 0, true }, |
| { "Query to one byte too small buffer, null length argument", 22, false }, |
| { "Query to one byte buffer, null length argument", 1, false }, |
| { "Query to zero sized buffer, null length argument", 0, false }, |
| }; |
| |
| for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(querySizes); ++ndx) |
| { |
| const tcu::ScopedLogSection section (m_testCtx.getLog(), "Query", querySizes[ndx].description); |
| const int uniformNameLen = 22; |
| const int expectedWriteLen = (querySizes[ndx].querySize != 0) ? (de::min(uniformNameLen, (querySizes[ndx].querySize - 1))) : (0); |
| char buffer [26]; |
| glw::GLsizei written = -1; |
| |
| // One byte for guard |
| DE_ASSERT((int)sizeof(buffer) > querySizes[ndx].querySize); |
| |
| deMemset(buffer, 'x', sizeof(buffer)); |
| |
| if (querySizes[ndx].querySize) |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Querying uniform name to a buffer of size " << querySizes[ndx].querySize |
| << ", expecting query to write " << expectedWriteLen << " bytes followed by a null terminator" |
| << tcu::TestLog::EndMessage; |
| else |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Querying uniform name to a buffer of size " << querySizes[ndx].querySize |
| << ", expecting query to write 0 bytes" |
| << tcu::TestLog::EndMessage; |
| |
| gl.getProgramResourceName(program.getProgram(), GL_UNIFORM, uniformIndex, querySizes[ndx].querySize, (querySizes[ndx].returnLength) ? (&written) : (DE_NULL), buffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query resource name"); |
| |
| if (querySizes[ndx].returnLength && written != expectedWriteLen) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, expected write length of " << expectedWriteLen << ", got " << written << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected write lenght"); |
| } |
| else if (querySizes[ndx].querySize != 0 && buffer[expectedWriteLen] != 0) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, expected null terminator at " << expectedWriteLen << ", got dec=" << (int)buffer[expectedWriteLen] << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Missing null terminator"); |
| } |
| else if (querySizes[ndx].querySize != 0 && buffer[expectedWriteLen+1] != 'x') |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, guard at index " << (expectedWriteLen+1) << " was modified, got dec=" << (int)buffer[expectedWriteLen+1] << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Wrote over buffer size"); |
| } |
| else if (querySizes[ndx].querySize == 0 && buffer[0] != 'x') |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, buffer size was 0 but buffer contents were modified. At index 0 got dec=" << (int)buffer[0] << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Buffer contents were modified"); |
| } |
| } |
| } |
| |
| return STOP; |
| } |
| |
| class ResourceQueryBufferLimitCase : public TestCase |
| { |
| public: |
| ResourceQueryBufferLimitCase (Context& context, const char* name, const char* description); |
| ~ResourceQueryBufferLimitCase (void); |
| |
| private: |
| IterateResult iterate (void); |
| }; |
| |
| ResourceQueryBufferLimitCase::ResourceQueryBufferLimitCase (Context& context, const char* name, const char* description) |
| : TestCase(context, name, description) |
| { |
| } |
| |
| ResourceQueryBufferLimitCase::~ResourceQueryBufferLimitCase (void) |
| { |
| } |
| |
| ResourceQueryBufferLimitCase::IterateResult ResourceQueryBufferLimitCase::iterate (void) |
| { |
| static const char* const computeSource = "${GLSL_VERSION_DECL}\n" |
| "layout(local_size_x = 1) in;\n" |
| "uniform highp int u_uniform;\n" |
| "writeonly buffer OutputBufferBlock { highp int b_output_int; };\n" |
| "void main ()\n" |
| "{\n" |
| " b_output_int = u_uniform;\n" |
| "}\n"; |
| |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(specializeShader(m_context, computeSource))); |
| glw::GLuint uniformIndex; |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| // Log program |
| { |
| const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program", "Program"); |
| |
| m_testCtx.getLog() << program; |
| if (!program.isOk()) |
| throw tcu::TestError("could not build program"); |
| } |
| |
| uniformIndex = gl.getProgramResourceIndex(program.getProgram(), GL_UNIFORM, "u_uniform"); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query resource index"); |
| |
| if (uniformIndex == GL_INVALID_INDEX) |
| throw tcu::TestError("Uniform u_uniform resource index was GL_INVALID_INDEX"); |
| |
| // Query uniform properties |
| |
| { |
| static const struct |
| { |
| const char* description; |
| int numProps; |
| int bufferSize; |
| bool returnLength; |
| } querySizes[] = |
| { |
| { "Query to a larger buffer", 2, 3, true }, |
| { "Query to too small a buffer", 3, 2, true }, |
| { "Query to zero sized buffer", 3, 0, true }, |
| { "Query to a larger buffer, null length argument", 2, 3, false }, |
| { "Query to too small a buffer, null length argument", 3, 2, false }, |
| { "Query to zero sized buffer, null length argument", 3, 0, false }, |
| }; |
| |
| for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(querySizes); ++ndx) |
| { |
| const tcu::ScopedLogSection section (m_testCtx.getLog(), "QueryToLarger", querySizes[ndx].description); |
| const glw::GLenum props[] = { GL_LOCATION, GL_LOCATION, GL_LOCATION }; |
| const int expectedWriteLen = de::min(querySizes[ndx].bufferSize, querySizes[ndx].numProps); |
| int params[] = { 255, 255, 255, 255 }; |
| glw::GLsizei written = -1; |
| |
| DE_ASSERT(querySizes[ndx].numProps <= DE_LENGTH_OF_ARRAY(props)); |
| DE_ASSERT(querySizes[ndx].bufferSize < DE_LENGTH_OF_ARRAY(params)); // leave at least one element for overflow detection |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Querying " << querySizes[ndx].numProps << " uniform prop(s) to a buffer with size " << querySizes[ndx].bufferSize << ". Expecting query to return " << expectedWriteLen << " prop(s)" |
| << tcu::TestLog::EndMessage; |
| |
| gl.getProgramResourceiv(program.getProgram(), GL_UNIFORM, uniformIndex, querySizes[ndx].numProps, props, querySizes[ndx].bufferSize, (querySizes[ndx].returnLength) ? (&written) : (DE_NULL), params); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query program resources"); |
| |
| if (querySizes[ndx].returnLength && written != expectedWriteLen) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, expected write length of " << expectedWriteLen << ", got " << written << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected write lenght"); |
| } |
| else if (params[expectedWriteLen] != 255) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, guard at index " << (expectedWriteLen) << " was modified. Was 255 before call, got dec=" << params[expectedWriteLen] << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Wrote over buffer size"); |
| } |
| } |
| } |
| |
| return STOP; |
| } |
| |
| class InterfaceBlockBaseCase : public TestCase |
| { |
| public: |
| enum CaseType |
| { |
| CASE_NAMED_BLOCK = 0, |
| CASE_UNNAMED_BLOCK, |
| CASE_BLOCK_ARRAY, |
| |
| CASE_LAST |
| }; |
| |
| InterfaceBlockBaseCase (Context& context, const char* name, const char* description, glu::Storage storage, CaseType caseType); |
| ~InterfaceBlockBaseCase (void); |
| |
| private: |
| void init (void); |
| void deinit (void); |
| |
| protected: |
| const glu::Storage m_storage; |
| const CaseType m_caseType; |
| ProgramInterfaceDefinition::Program* m_program; |
| }; |
| |
| InterfaceBlockBaseCase::InterfaceBlockBaseCase (Context& context, const char* name, const char* description, glu::Storage storage, CaseType caseType) |
| : TestCase (context, name, description) |
| , m_storage (storage) |
| , m_caseType (caseType) |
| , m_program (DE_NULL) |
| { |
| DE_ASSERT(storage == glu::STORAGE_UNIFORM || storage == glu::STORAGE_BUFFER); |
| } |
| |
| InterfaceBlockBaseCase::~InterfaceBlockBaseCase (void) |
| { |
| deinit(); |
| } |
| |
| void InterfaceBlockBaseCase::init (void) |
| { |
| const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType()); |
| ProgramInterfaceDefinition::Shader* shader; |
| |
| m_program = new ProgramInterfaceDefinition::Program(); |
| shader = m_program->addShader(glu::SHADERTYPE_COMPUTE, glslVersion); |
| |
| // PrecedingInterface |
| { |
| glu::InterfaceBlock precedingInterfaceBlock; |
| |
| precedingInterfaceBlock.interfaceName = "PrecedingInterface"; |
| precedingInterfaceBlock.layout.binding = 0; |
| precedingInterfaceBlock.storage = m_storage; |
| precedingInterfaceBlock.instanceName = "precedingInstance"; |
| |
| precedingInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), "precedingMember")); |
| |
| // Unsized array type |
| if (m_storage == glu::STORAGE_BUFFER) |
| precedingInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), glu::VarType::UNSIZED_ARRAY), "precedingMemberUnsizedArray")); |
| else |
| precedingInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), 2), "precedingMemberArray")); |
| |
| shader->getDefaultBlock().interfaceBlocks.push_back(precedingInterfaceBlock); |
| } |
| |
| // TargetInterface |
| { |
| glu::InterfaceBlock targetInterfaceBlock; |
| |
| targetInterfaceBlock.interfaceName = "TargetInterface"; |
| targetInterfaceBlock.layout.binding = 1; |
| targetInterfaceBlock.storage = m_storage; |
| |
| if (m_caseType == CASE_UNNAMED_BLOCK) |
| targetInterfaceBlock.instanceName = ""; |
| else |
| targetInterfaceBlock.instanceName = "targetInstance"; |
| |
| if (m_caseType == CASE_BLOCK_ARRAY) |
| targetInterfaceBlock.dimensions.push_back(2); |
| |
| // Basic type |
| { |
| targetInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), "blockMemberBasic")); |
| } |
| |
| // Array type |
| { |
| targetInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), 3), "blockMemberArray")); |
| } |
| |
| // Struct type |
| { |
| glu::StructType* structPtr = new glu::StructType("StructType"); |
| structPtr->addMember("structMemberBasic", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP)); |
| structPtr->addMember("structMemberArray", glu::VarType(glu::VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), 2)); |
| |
| targetInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(structPtr), 2), "blockMemberStruct")); |
| } |
| |
| // Unsized array type |
| if (m_storage == glu::STORAGE_BUFFER) |
| targetInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), glu::VarType::UNSIZED_ARRAY), "blockMemberUnsizedArray")); |
| |
| shader->getDefaultBlock().interfaceBlocks.push_back(targetInterfaceBlock); |
| } |
| |
| // TrailingInterface |
| { |
| glu::InterfaceBlock trailingInterfaceBlock; |
| |
| trailingInterfaceBlock.interfaceName = "TrailingInterface"; |
| trailingInterfaceBlock.layout.binding = 3; |
| trailingInterfaceBlock.storage = m_storage; |
| trailingInterfaceBlock.instanceName = "trailingInstance"; |
| trailingInterfaceBlock.variables.push_back(glu::VariableDeclaration(glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), "trailingMember")); |
| |
| shader->getDefaultBlock().interfaceBlocks.push_back(trailingInterfaceBlock); |
| } |
| |
| DE_ASSERT(m_program->isValid()); |
| } |
| |
| void InterfaceBlockBaseCase::deinit (void) |
| { |
| delete m_program; |
| m_program = DE_NULL; |
| } |
| |
| class InterfaceBlockActiveVariablesTestCase : public InterfaceBlockBaseCase |
| { |
| public: |
| InterfaceBlockActiveVariablesTestCase (Context& context, const char* name, const char* description, glu::Storage storage, CaseType caseType); |
| |
| private: |
| IterateResult iterate (void); |
| }; |
| |
| InterfaceBlockActiveVariablesTestCase::InterfaceBlockActiveVariablesTestCase (Context& context, const char* name, const char* description, glu::Storage storage, CaseType caseType) |
| : InterfaceBlockBaseCase(context, name, description, storage, caseType) |
| { |
| } |
| |
| InterfaceBlockActiveVariablesTestCase::IterateResult InterfaceBlockActiveVariablesTestCase::iterate (void) |
| { |
| const ProgramInterface programInterface = (m_storage == glu::STORAGE_UNIFORM) ? (PROGRAMINTERFACE_UNIFORM_BLOCK) : |
| (m_storage == glu::STORAGE_BUFFER) ? (PROGRAMINTERFACE_SHADER_STORAGE_BLOCK) : |
| (PROGRAMINTERFACE_LAST); |
| const glw::GLenum programGLInterfaceValue = getProgramInterfaceGLEnum(programInterface); |
| const glw::GLenum programMemberInterfaceValue = (m_storage == glu::STORAGE_UNIFORM) ? (GL_UNIFORM) : |
| (m_storage == glu::STORAGE_BUFFER) ? (GL_BUFFER_VARIABLE) : |
| (0); |
| const std::vector<std::string> blockNames = getProgramInterfaceResourceList(m_program, programInterface); |
| glu::ShaderProgram program (m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_program)); |
| int expectedMaxNumActiveVariables = 0; |
| |
| DE_ASSERT(programInterface != PROGRAMINTERFACE_LAST); |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| checkAndLogProgram(program, m_program, m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| |
| // Verify all blocks |
| |
| for (int blockNdx = 0; blockNdx < (int)blockNames.size(); ++blockNdx) |
| { |
| const tcu::ScopedLogSection section (m_testCtx.getLog(), "Block", "Block \"" + blockNames[blockNdx] + "\""); |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const glw::GLuint resourceNdx = gl.getProgramResourceIndex(program.getProgram(), programGLInterfaceValue, blockNames[blockNdx].c_str()); |
| glw::GLint numActiveResources; |
| std::vector<std::string> activeResourceNames; |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query resource index"); |
| |
| if (resourceNdx == GL_INVALID_INDEX) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, getProgramResourceIndex returned GL_INVALID_INDEX for \"" << blockNames[blockNdx] << "\"" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Resource not found"); |
| continue; |
| } |
| |
| // query block information |
| |
| { |
| const glw::GLenum props[] = { GL_NUM_ACTIVE_VARIABLES }; |
| glw::GLint retBuffer[2] = { -1, -1 }; |
| glw::GLint written = -1; |
| |
| gl.getProgramResourceiv(program.getProgram(), programGLInterfaceValue, resourceNdx, DE_LENGTH_OF_ARRAY(props), props, 1, &written, retBuffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query GL_NUM_ACTIVE_VARIABLES"); |
| |
| numActiveResources = retBuffer[0]; |
| expectedMaxNumActiveVariables = de::max(expectedMaxNumActiveVariables, numActiveResources); |
| m_testCtx.getLog() << tcu::TestLog::Message << "NUM_ACTIVE_VARIABLES = " << numActiveResources << tcu::TestLog::EndMessage; |
| |
| if (written == -1 || retBuffer[0] == -1) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, Query for NUM_ACTIVE_VARIABLES did not return a value" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Query for NUM_ACTIVE_VARIABLES failed"); |
| continue; |
| } |
| else if (retBuffer[1] != -1) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, Query for NUM_ACTIVE_VARIABLES returned too many values" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Query for NUM_ACTIVE_VARIABLES returned too many values"); |
| continue; |
| } |
| else if (retBuffer[0] < 0) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, NUM_ACTIVE_VARIABLES < 0" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "NUM_ACTIVE_VARIABLES < 0"); |
| continue; |
| } |
| } |
| |
| // query block variable information |
| |
| { |
| const glw::GLenum props[] = { GL_ACTIVE_VARIABLES }; |
| std::vector<glw::GLint> activeVariableIndices (numActiveResources + 1, -1); // Allocate one extra trailing to detect wrong write lengths |
| glw::GLint written = -1; |
| |
| gl.getProgramResourceiv(program.getProgram(), programGLInterfaceValue, resourceNdx, DE_LENGTH_OF_ARRAY(props), props, (glw::GLsizei)activeVariableIndices.size(), &written, &activeVariableIndices[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query GL_ACTIVE_VARIABLES"); |
| |
| if (written == -1) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, Query for GL_ACTIVE_VARIABLES did not return any values" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Query for GL_ACTIVE_VARIABLES failed"); |
| continue; |
| } |
| else if (written != numActiveResources) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, Query for GL_ACTIVE_VARIABLES did not return NUM_ACTIVE_VARIABLES values" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Query for GL_ACTIVE_VARIABLES returned invalid number of values"); |
| continue; |
| } |
| else if (activeVariableIndices.back() != -1) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, GL_ACTIVE_VARIABLES query return buffer trailing guard value was modified, getProgramResourceiv returned more than NUM_ACTIVE_VARIABLES values" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Query for GL_ACTIVE_VARIABLES returned too many values"); |
| continue; |
| } |
| |
| // log indices |
| { |
| tcu::MessageBuilder builder(&m_testCtx.getLog()); |
| |
| builder << "Active variable indices: {"; |
| for (int varNdx = 0; varNdx < numActiveResources; ++varNdx) |
| { |
| if (varNdx) |
| builder << ", "; |
| builder << activeVariableIndices[varNdx]; |
| } |
| builder << "}" << tcu::TestLog::EndMessage; |
| } |
| |
| // collect names |
| |
| activeResourceNames.resize(numActiveResources); |
| |
| for (int varNdx = 0; varNdx < numActiveResources; ++varNdx) |
| { |
| const glw::GLenum nameProp = GL_NAME_LENGTH; |
| glw::GLint nameLength = -1; |
| std::vector<char> nameBuffer; |
| |
| written = -1; |
| gl.getProgramResourceiv(program.getProgram(), programMemberInterfaceValue, activeVariableIndices[varNdx], 1, &nameProp, 1, &written, &nameLength); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query GL_NAME_LENGTH"); |
| |
| if (nameLength <= 0 || written <= 0) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, GL_NAME_LENGTH query failed" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "GL_NAME_LENGTH query failed"); |
| continue; |
| } |
| |
| nameBuffer.resize(nameLength + 2, 'X'); // allocate more than required |
| written = -1; |
| gl.getProgramResourceName(program.getProgram(), programMemberInterfaceValue, activeVariableIndices[varNdx], nameLength+1, &written, &nameBuffer[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "getProgramResourceName"); |
| |
| if (written <= 0) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, name query failed, no data written" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "name query failed"); |
| continue; |
| } |
| else if (written > nameLength) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, name query failed, query returned too much data" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "name query failed"); |
| continue; |
| } |
| |
| activeResourceNames[varNdx] = std::string(&nameBuffer[0], written); |
| } |
| |
| // log collected names |
| { |
| tcu::MessageBuilder builder(&m_testCtx.getLog()); |
| |
| builder << "Active variables:\n"; |
| for (int varNdx = 0; varNdx < numActiveResources; ++varNdx) |
| builder << "\t" << activeResourceNames[varNdx] << "\n"; |
| builder << tcu::TestLog::EndMessage; |
| } |
| } |
| |
| // verify names |
| { |
| glu::InterfaceBlock* block = DE_NULL; |
| const std::string blockName = glu::parseVariableName(blockNames[blockNdx].c_str()); |
| std::vector<std::string> referenceList; |
| |
| for (int interfaceNdx = 0; interfaceNdx < (int)m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks.size(); ++interfaceNdx) |
| { |
| if (m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks[interfaceNdx].interfaceName == blockName) |
| { |
| block = &m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks[interfaceNdx]; |
| break; |
| } |
| } |
| |
| if (!block) |
| throw tcu::InternalError("could not find block referenced in the reference resource list"); |
| |
| // generate reference list |
| |
| referenceList = getProgramInterfaceBlockMemberResourceList(*block); |
| { |
| tcu::MessageBuilder builder(&m_testCtx.getLog()); |
| |
| builder << "Expected variable names:\n"; |
| for (int varNdx = 0; varNdx < (int)referenceList.size(); ++varNdx) |
| builder << "\t" << referenceList[varNdx] << "\n"; |
| builder << tcu::TestLog::EndMessage; |
| } |
| |
| // compare lists |
| { |
| bool listsIdentical = true; |
| |
| for (int ndx = 0; ndx < (int)referenceList.size(); ++ndx) |
| { |
| if (!de::contains(activeResourceNames.begin(), activeResourceNames.end(), referenceList[ndx])) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, variable name list did not contain active variable " << referenceList[ndx] << tcu::TestLog::EndMessage; |
| listsIdentical = false; |
| } |
| } |
| |
| for (int ndx = 0; ndx < (int)activeResourceNames.size(); ++ndx) |
| { |
| if (!de::contains(referenceList.begin(), referenceList.end(), activeResourceNames[ndx])) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, variable name list contains unexpected resource \"" << activeResourceNames[ndx] << "\"" << tcu::TestLog::EndMessage; |
| listsIdentical = false; |
| } |
| } |
| |
| if (listsIdentical) |
| m_testCtx.getLog() << tcu::TestLog::Message << "Lists identical" << tcu::TestLog::EndMessage; |
| else |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, invalid active variable list" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "invalid active variable list"); |
| continue; |
| } |
| } |
| } |
| } |
| |
| // Max num active variables |
| { |
| const tcu::ScopedLogSection section (m_testCtx.getLog(), "MaxNumActiveVariables", "MAX_NUM_ACTIVE_VARIABLES"); |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| glw::GLint maxNumActiveVariables = -1; |
| |
| gl.getProgramInterfaceiv(program.getProgram(), programGLInterfaceValue, GL_MAX_NUM_ACTIVE_VARIABLES, &maxNumActiveVariables); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query MAX_NUM_ACTIVE_VARIABLES"); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "MAX_NUM_ACTIVE_VARIABLES = " << maxNumActiveVariables << tcu::TestLog::EndMessage; |
| |
| if (expectedMaxNumActiveVariables != maxNumActiveVariables) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, got unexpected MAX_NUM_ACTIVE_VARIABLES" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "unexpected MAX_NUM_ACTIVE_VARIABLES"); |
| } |
| else |
| m_testCtx.getLog() << tcu::TestLog::Message << "MAX_NUM_ACTIVE_VARIABLES valid" << tcu::TestLog::EndMessage; |
| } |
| |
| return STOP; |
| } |
| |
| class InterfaceBlockDataSizeTestCase : public InterfaceBlockBaseCase |
| { |
| public: |
| InterfaceBlockDataSizeTestCase (Context& context, const char* name, const char* description, glu::Storage storage, CaseType caseType); |
| |
| private: |
| IterateResult iterate (void); |
| int getBlockMinDataSize (const std::string& blockName) const; |
| int getBlockMinDataSize (const glu::InterfaceBlock& block) const; |
| }; |
| |
| InterfaceBlockDataSizeTestCase::InterfaceBlockDataSizeTestCase (Context& context, const char* name, const char* description, glu::Storage storage, CaseType caseType) |
| : InterfaceBlockBaseCase(context, name, description, storage, caseType) |
| { |
| } |
| |
| InterfaceBlockDataSizeTestCase::IterateResult InterfaceBlockDataSizeTestCase::iterate (void) |
| { |
| const ProgramInterface programInterface = (m_storage == glu::STORAGE_UNIFORM) ? (PROGRAMINTERFACE_UNIFORM_BLOCK) : |
| (m_storage == glu::STORAGE_BUFFER) ? (PROGRAMINTERFACE_SHADER_STORAGE_BLOCK) : |
| (PROGRAMINTERFACE_LAST); |
| const glw::GLenum programGLInterfaceValue = getProgramInterfaceGLEnum(programInterface); |
| const std::vector<std::string> blockNames = getProgramInterfaceResourceList(m_program, programInterface); |
| glu::ShaderProgram program (m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_program)); |
| |
| DE_ASSERT(programInterface != PROGRAMINTERFACE_LAST); |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| checkAndLogProgram(program, m_program, m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| |
| // Verify all blocks |
| for (int blockNdx = 0; blockNdx < (int)blockNames.size(); ++blockNdx) |
| { |
| const tcu::ScopedLogSection section (m_testCtx.getLog(), "Block", "Block \"" + blockNames[blockNdx] + "\""); |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const glw::GLuint resourceNdx = gl.getProgramResourceIndex(program.getProgram(), programGLInterfaceValue, blockNames[blockNdx].c_str()); |
| const int expectedMinDataSize = getBlockMinDataSize(blockNames[blockNdx]); |
| glw::GLint queryDataSize = -1; |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query resource index"); |
| |
| if (resourceNdx == GL_INVALID_INDEX) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, getProgramResourceIndex returned GL_INVALID_INDEX for \"" << blockNames[blockNdx] << "\"" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Resource not found"); |
| continue; |
| } |
| |
| // query |
| { |
| const glw::GLenum prop = GL_BUFFER_DATA_SIZE; |
| |
| gl.getProgramResourceiv(program.getProgram(), programGLInterfaceValue, resourceNdx, 1, &prop, 1, DE_NULL, &queryDataSize); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query resource BUFFER_DATA_SIZE"); |
| } |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "BUFFER_DATA_SIZE = " << queryDataSize << "\n" |
| << "Buffer data size with tight packing: " << expectedMinDataSize |
| << tcu::TestLog::EndMessage; |
| |
| if (queryDataSize < expectedMinDataSize) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, buffer size was less than minimum buffer data size" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Buffer data size invalid"); |
| continue; |
| } |
| else |
| m_testCtx.getLog() << tcu::TestLog::Message << "Buffer size valid" << tcu::TestLog::EndMessage; |
| } |
| |
| return STOP; |
| } |
| |
| int InterfaceBlockDataSizeTestCase::getBlockMinDataSize (const std::string& blockFullName) const |
| { |
| const std::string blockName = glu::parseVariableName(blockFullName.c_str()); |
| |
| for (int interfaceNdx = 0; interfaceNdx < (int)m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks.size(); ++interfaceNdx) |
| { |
| if (m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks[interfaceNdx].interfaceName == blockName && |
| m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks[interfaceNdx].storage == m_storage) |
| return getBlockMinDataSize(m_program->getShaders()[0]->getDefaultBlock().interfaceBlocks[interfaceNdx]); |
| } |
| |
| DE_ASSERT(false); |
| return -1; |
| } |
| |
| class AtomicCounterCase : public TestCase |
| { |
| public: |
| AtomicCounterCase (Context& context, const char* name, const char* description); |
| ~AtomicCounterCase (void); |
| |
| private: |
| void init (void); |
| void deinit (void); |
| |
| protected: |
| int getNumAtomicCounterBuffers (void) const; |
| int getMaxNumActiveVariables (void) const; |
| int getBufferVariableCount (int binding) const; |
| int getBufferMinimumDataSize (int binding) const; |
| |
| ProgramInterfaceDefinition::Program* m_program; |
| }; |
| |
| AtomicCounterCase::AtomicCounterCase (Context& context, const char* name, const char* description) |
| : TestCase (context, name, description) |
| , m_program (DE_NULL) |
| { |
| } |
| |
| AtomicCounterCase::~AtomicCounterCase (void) |
| { |
| deinit(); |
| } |
| |
| void AtomicCounterCase::init (void) |
| { |
| ProgramInterfaceDefinition::Shader* shader; |
| glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_context.getRenderContext().getType()); |
| |
| m_program = new ProgramInterfaceDefinition::Program(); |
| shader = m_program->addShader(glu::SHADERTYPE_COMPUTE, glslVersion); |
| |
| { |
| glu::VariableDeclaration decl(glu::VarType(glu::TYPE_UINT_ATOMIC_COUNTER, glu::PRECISION_LAST), "binding1_counter1", glu::STORAGE_UNIFORM); |
| decl.layout.binding = 1; |
| shader->getDefaultBlock().variables.push_back(decl); |
| } |
| { |
| glu::VariableDeclaration decl(glu::VarType(glu::TYPE_UINT_ATOMIC_COUNTER, glu::PRECISION_LAST), "binding1_counter2", glu::STORAGE_UNIFORM); |
| decl.layout.binding = 1; |
| decl.layout.offset = 8; |
| |
| shader->getDefaultBlock().variables.push_back(decl); |
| } |
| { |
| glu::VariableDeclaration decl(glu::VarType(glu::TYPE_UINT_ATOMIC_COUNTER, glu::PRECISION_LAST), "binding2_counter1", glu::STORAGE_UNIFORM); |
| decl.layout.binding = 2; |
| shader->getDefaultBlock().variables.push_back(decl); |
| } |
| |
| DE_ASSERT(m_program->isValid()); |
| } |
| |
| void AtomicCounterCase::deinit (void) |
| { |
| delete m_program; |
| m_program = DE_NULL; |
| } |
| |
| int AtomicCounterCase::getNumAtomicCounterBuffers (void) const |
| { |
| std::set<int> buffers; |
| |
| for (int ndx = 0; ndx < (int)m_program->getShaders()[0]->getDefaultBlock().variables.size(); ++ndx) |
| { |
| if (m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.isBasicType() && |
| glu::isDataTypeAtomicCounter(m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.getBasicType())) |
| { |
| buffers.insert(m_program->getShaders()[0]->getDefaultBlock().variables[ndx].layout.binding); |
| } |
| } |
| |
| return (int)buffers.size(); |
| } |
| |
| int AtomicCounterCase::getMaxNumActiveVariables (void) const |
| { |
| int maxVars = 0; |
| std::map<int,int> numBufferVars; |
| |
| for (int ndx = 0; ndx < (int)m_program->getShaders()[0]->getDefaultBlock().variables.size(); ++ndx) |
| { |
| if (m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.isBasicType() && |
| glu::isDataTypeAtomicCounter(m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.getBasicType())) |
| { |
| const int binding = m_program->getShaders()[0]->getDefaultBlock().variables[ndx].layout.binding; |
| |
| if (numBufferVars.find(binding) == numBufferVars.end()) |
| numBufferVars[binding] = 1; |
| else |
| ++numBufferVars[binding]; |
| } |
| } |
| |
| for (std::map<int,int>::const_iterator it = numBufferVars.begin(); it != numBufferVars.end(); ++it) |
| maxVars = de::max(maxVars, it->second); |
| |
| return maxVars; |
| } |
| |
| int AtomicCounterCase::getBufferVariableCount (int binding) const |
| { |
| int numVars = 0; |
| |
| for (int ndx = 0; ndx < (int)m_program->getShaders()[0]->getDefaultBlock().variables.size(); ++ndx) |
| { |
| if (m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.isBasicType() && |
| glu::isDataTypeAtomicCounter(m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.getBasicType()) && |
| m_program->getShaders()[0]->getDefaultBlock().variables[ndx].layout.binding == binding) |
| ++numVars; |
| } |
| |
| return numVars; |
| } |
| |
| int AtomicCounterCase::getBufferMinimumDataSize (int binding) const |
| { |
| int minSize = -1; |
| int currentOffset = 0; |
| |
| for (int ndx = 0; ndx < (int)m_program->getShaders()[0]->getDefaultBlock().variables.size(); ++ndx) |
| { |
| if (m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.isBasicType() && |
| glu::isDataTypeAtomicCounter(m_program->getShaders()[0]->getDefaultBlock().variables[ndx].varType.getBasicType()) && |
| m_program->getShaders()[0]->getDefaultBlock().variables[ndx].layout.binding == binding) |
| { |
| const int thisOffset = (m_program->getShaders()[0]->getDefaultBlock().variables[ndx].layout.offset != -1) ? (m_program->getShaders()[0]->getDefaultBlock().variables[ndx].layout.offset) : (currentOffset); |
| currentOffset = thisOffset + 4; |
| |
| minSize = de::max(minSize, thisOffset + 4); |
| } |
| } |
| |
| return minSize; |
| } |
| |
| class AtomicCounterResourceListCase : public AtomicCounterCase |
| { |
| public: |
| AtomicCounterResourceListCase (Context& context, const char* name, const char* description); |
| |
| private: |
| IterateResult iterate (void); |
| }; |
| |
| AtomicCounterResourceListCase::AtomicCounterResourceListCase (Context& context, const char* name, const char* description) |
| : AtomicCounterCase(context, name, description) |
| { |
| } |
| |
| AtomicCounterResourceListCase::IterateResult AtomicCounterResourceListCase::iterate (void) |
| { |
| const glu::ShaderProgram program(m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_program)); |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| checkAndLogProgram(program, m_program, m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| |
| { |
| const tcu::ScopedLogSection section (m_testCtx.getLog(), "ActiveResources", "ACTIVE_RESOURCES"); |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| glw::GLint numActiveResources = -1; |
| const int numExpectedActiveResources = 2; // 2 buffer bindings |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Verifying ACTIVE_RESOURCES, expecting " << numExpectedActiveResources << tcu::TestLog::EndMessage; |
| |
| gl.getProgramInterfaceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, GL_ACTIVE_RESOURCES, &numActiveResources); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query GL_ACTIVE_RESOURCES"); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "ACTIVE_RESOURCES = " << numActiveResources << tcu::TestLog::EndMessage; |
| |
| if (numActiveResources != numExpectedActiveResources) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, got unexpected ACTIVE_RESOURCES" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected ACTIVE_RESOURCES"); |
| } |
| else |
| m_testCtx.getLog() << tcu::TestLog::Message << "ACTIVE_RESOURCES valid" << tcu::TestLog::EndMessage; |
| } |
| |
| return STOP; |
| } |
| |
| class AtomicCounterActiveVariablesCase : public AtomicCounterCase |
| { |
| public: |
| AtomicCounterActiveVariablesCase (Context& context, const char* name, const char* description); |
| |
| private: |
| IterateResult iterate (void); |
| }; |
| |
| AtomicCounterActiveVariablesCase::AtomicCounterActiveVariablesCase (Context& context, const char* name, const char* description) |
| : AtomicCounterCase(context, name, description) |
| { |
| } |
| |
| AtomicCounterActiveVariablesCase::IterateResult AtomicCounterActiveVariablesCase::iterate (void) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const glu::ShaderProgram program (m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_program)); |
| const int numAtomicBuffers = getNumAtomicCounterBuffers(); |
| const int expectedMaxNumActiveVariables = getMaxNumActiveVariables(); |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| checkAndLogProgram(program, m_program, m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| |
| // check active variables |
| { |
| const tcu::ScopedLogSection section (m_testCtx.getLog(), "Interface", "ATOMIC_COUNTER_BUFFER interface"); |
| glw::GLint queryActiveResources = -1; |
| glw::GLint queryMaxNumActiveVariables = -1; |
| |
| gl.getProgramInterfaceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, GL_ACTIVE_RESOURCES, &queryActiveResources); |
| gl.getProgramInterfaceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, GL_MAX_NUM_ACTIVE_VARIABLES, &queryMaxNumActiveVariables); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query interface"); |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "GL_ACTIVE_RESOURCES = " << queryActiveResources << "\n" |
| << "GL_MAX_NUM_ACTIVE_VARIABLES = " << queryMaxNumActiveVariables << "\n" |
| << tcu::TestLog::EndMessage; |
| |
| if (queryActiveResources != numAtomicBuffers) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, got unexpected GL_ACTIVE_RESOURCES, expected " << numAtomicBuffers << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected GL_ACTIVE_RESOURCES"); |
| } |
| |
| if (queryMaxNumActiveVariables != expectedMaxNumActiveVariables) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, got unexpected GL_MAX_NUM_ACTIVE_VARIABLES, expected " << expectedMaxNumActiveVariables << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected GL_MAX_NUM_ACTIVE_VARIABLES"); |
| } |
| } |
| |
| // Check each buffer |
| for (int bufferNdx = 0; bufferNdx < numAtomicBuffers; ++bufferNdx) |
| { |
| const tcu::ScopedLogSection section (m_testCtx.getLog(), "Resource", "Resource index " + de::toString(bufferNdx)); |
| std::vector<glw::GLint> activeVariables; |
| std::vector<std::string> memberNames; |
| |
| // Find active variables |
| { |
| const glw::GLenum numActiveVariablesProp = GL_NUM_ACTIVE_VARIABLES; |
| const glw::GLenum activeVariablesProp = GL_ACTIVE_VARIABLES; |
| glw::GLint numActiveVariables = -2; |
| glw::GLint written = -1; |
| |
| gl.getProgramResourceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, bufferNdx, 1, &numActiveVariablesProp, 1, &written, &numActiveVariables); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query num active variables"); |
| |
| if (numActiveVariables <= 0) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, got unexpected NUM_ACTIVE_VARIABLES: " << numActiveVariables << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected NUM_ACTIVE_VARIABLES"); |
| continue; |
| } |
| |
| if (written <= 0) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for NUM_ACTIVE_VARIABLES returned no values" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "NUM_ACTIVE_VARIABLES query failed"); |
| continue; |
| } |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "GL_NUM_ACTIVE_VARIABLES = " << numActiveVariables << tcu::TestLog::EndMessage; |
| |
| written = -1; |
| activeVariables.resize(numActiveVariables + 1, -2); |
| |
| gl.getProgramResourceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, bufferNdx, 1, &activeVariablesProp, numActiveVariables, &written, &activeVariables[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query active variables"); |
| |
| if (written != numActiveVariables) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, unexpected number of ACTIVE_VARIABLES, NUM_ACTIVE_VARIABLES = " << numActiveVariables << ", query returned " << written << " values" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected ACTIVE_VARIABLES"); |
| continue; |
| } |
| |
| if (activeVariables.back() != -2) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for ACTIVE_VARIABLES wrote over target buffer bounds" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "ACTIVE_VARIABLES query failed"); |
| continue; |
| } |
| |
| activeVariables.pop_back(); |
| } |
| |
| // log indices |
| { |
| tcu::MessageBuilder builder(&m_testCtx.getLog()); |
| |
| builder << "Active variable indices: {"; |
| for (int varNdx = 0; varNdx < (int)activeVariables.size(); ++varNdx) |
| { |
| if (varNdx) |
| builder << ", "; |
| builder << activeVariables[varNdx]; |
| } |
| builder << "}" << tcu::TestLog::EndMessage; |
| } |
| |
| // collect member names |
| for (int ndx = 0; ndx < (int)activeVariables.size(); ++ndx) |
| { |
| const glw::GLenum nameLengthProp = GL_NAME_LENGTH; |
| glw::GLint nameLength = -1; |
| glw::GLint written = -1; |
| std::vector<char> nameBuf; |
| |
| gl.getProgramResourceiv(program.getProgram(), GL_UNIFORM, activeVariables[ndx], 1, &nameLengthProp, 1, &written, &nameLength); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query buffer variable name length"); |
| |
| if (written <= 0 || nameLength == -1) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for GL_NAME_LENGTH returned no values" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "GL_NAME_LENGTH query failed"); |
| continue; |
| } |
| |
| nameBuf.resize(nameLength + 2, 'X'); // +2 to tolerate potential off-by-ones in some implementations, name queries will check these cases better |
| written = -1; |
| |
| gl.getProgramResourceName(program.getProgram(), GL_UNIFORM, activeVariables[ndx], (int)nameBuf.size(), &written, &nameBuf[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query buffer variable name"); |
| |
| if (written <= 0) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for resource name returned no name" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Name query failed"); |
| continue; |
| } |
| |
| memberNames.push_back(std::string(&nameBuf[0], written)); |
| } |
| |
| // log names |
| { |
| tcu::MessageBuilder builder(&m_testCtx.getLog()); |
| |
| builder << "Active variables:\n"; |
| for (int varNdx = 0; varNdx < (int)memberNames.size(); ++varNdx) |
| { |
| builder << "\t" << memberNames[varNdx] << "\n"; |
| } |
| builder << tcu::TestLog::EndMessage; |
| } |
| |
| // check names are all in the same buffer |
| { |
| bool bindingsValid = true; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Verifying names" << tcu::TestLog::EndMessage; |
| |
| for (int nameNdx = 0; nameNdx < (int)memberNames.size(); ++nameNdx) |
| { |
| int prevBinding = -1; |
| |
| for (int varNdx = 0; varNdx < (int)m_program->getShaders()[0]->getDefaultBlock().variables.size(); ++varNdx) |
| { |
| if (m_program->getShaders()[0]->getDefaultBlock().variables[varNdx].name == memberNames[nameNdx]) |
| { |
| const int varBinding = m_program->getShaders()[0]->getDefaultBlock().variables[varNdx].layout.binding; |
| |
| if (prevBinding == -1 || prevBinding == varBinding) |
| prevBinding = varBinding; |
| else |
| bindingsValid = false; |
| } |
| } |
| |
| if (prevBinding == -1) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, could not find variable with name \"" << memberNames[nameNdx] << "\"" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Variable name invalid"); |
| } |
| else if (getBufferVariableCount(prevBinding) != (int)memberNames.size()) |
| { |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Error, unexpected variable count for binding " << prevBinding |
| << ". Expected " << getBufferVariableCount(prevBinding) << ", got " << (int)memberNames.size() |
| << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Variable names invalid"); |
| } |
| } |
| |
| if (!bindingsValid) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, all resource do not share the same buffer" << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Active variables invalid"); |
| continue; |
| } |
| } |
| } |
| |
| return STOP; |
| } |
| |
| class AtomicCounterBufferBindingCase : public AtomicCounterCase |
| { |
| public: |
| AtomicCounterBufferBindingCase (Context& context, const char* name, const char* description); |
| |
| private: |
| IterateResult iterate (void); |
| }; |
| |
| AtomicCounterBufferBindingCase::AtomicCounterBufferBindingCase (Context& context, const char* name, const char* description) |
| : AtomicCounterCase(context, name, description) |
| { |
| } |
| |
| AtomicCounterBufferBindingCase::IterateResult AtomicCounterBufferBindingCase::iterate (void) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const glu::ShaderProgram program (m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_program)); |
| const int numAtomicBuffers = getNumAtomicCounterBuffers(); |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| checkAndLogProgram(program, m_program, m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| |
| // check every buffer |
| for (int bufferNdx = 0; bufferNdx < numAtomicBuffers; ++bufferNdx) |
| { |
| const tcu::ScopedLogSection section (m_testCtx.getLog(), "Resource", "Resource index " + de::toString(bufferNdx)); |
| const glw::GLenum bufferBindingProp = GL_BUFFER_BINDING; |
| glw::GLint bufferBinding = -1; |
| glw::GLint written = -1; |
| |
| gl.getProgramResourceiv(program.getProgram(), GL_ATOMIC_COUNTER_BUFFER, bufferNdx, 1, &bufferBindingProp, 1, &written, &bufferBinding); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "query buffer binding"); |
| |
| if (written <= 0) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, query for BUFFER_BINDING returned no values." << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "BUFFER_BINDING query failed"); |
| } |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "GL_BUFFER_BINDING = " << bufferBinding << tcu::TestLog::EndMessage; |
| |
| // no such buffer binding? |
| if (getBufferVariableCount(bufferBinding) == 0) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Error, got buffer with BUFFER_BINDING = " << bufferBinding << ", but such buffer does not exist." << tcu::TestLog::EndMessage; |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected BUFFER_BINDING"); |
| } |
| } |
| |
| return STOP; |
| } |
| |
| class AtomicCounterBufferDataSizeCase : public AtomicCounterCase |
| { |
| public: |
| AtomicCounterBufferDataSizeCase (Context& context, const char* name, const char* description); |
| |
| private: |
| IterateResult iterate (void); |
| }; |
| |
| AtomicCounterBufferDataSizeCase::AtomicCounterBufferDataSizeCase (Context& context, const char* name, const char* description) |
| : AtomicCounterCase(context, name, description) |
| { |
| } |
| |
| AtomicCounterBufferDataSizeCase::IterateResult AtomicCounterBufferDataSizeCase::iterate (void) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const glu::ShaderProgram program (m_context.getRenderContext(), generateProgramInterfaceProgramSources(m_program)); |
| const int numAtomicBuffers = getNumAtomicCounterBuffers(); |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| checkAndLogProgram(program, m_program, m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| |
| // check every buffer |
| |