blob: 0c8e336c378d9b911e254986ea69f84e9bcb4459 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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)
{
// unusedZero is a uniform that may be added by
// generateProgramInterfaceProgramSources. Omit it here to avoid
// confusion about the output.
if (resourceList[ndx] != getUnusedZeroUniformName())
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 unusedZero,
// mismatch causes errors otherwise. unusedZero is a uniform that
// may be added by generateProgramInterfaceProgramSources.
if (deStringBeginsWith(resourceList[ndx].c_str(), "gl_") == DE_FALSE &&
resourceList[ndx] != getUnusedZeroUniformName())
{
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 "";
}
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: " <<