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