You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2758 lines
113 KiB
2758 lines
113 KiB
/*-------------------------------------------------------------------------
|
|
* 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
|