blob: 4b8aef4b48733b62d0b419e6ee04181b74002532 [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2014-2016 The Khronos Group Inc.
*
* 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
*/ /*-------------------------------------------------------------------*/
#include "es31cLayoutBindingTests.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuStringTemplate.hpp"
#include "tcuSurface.hpp"
#include "tcuTestLog.hpp"
#include "tcuTexture.hpp"
#include "tcuTextureUtil.hpp"
#include "deRandom.hpp"
#include "deStringUtil.hpp"
#include "glwDefs.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "gluDrawUtil.hpp"
#include "gluPixelTransfer.hpp"
#include "gluShaderProgram.hpp"
#include "gluTexture.hpp"
#include "gluTextureUtil.hpp"
namespace glcts
{
//=========================================================================
//= typedefs
//=========================================================================
typedef std::string String;
typedef std::map<String, String> StringMap;
typedef std::map<String, glw::GLint> StringIntMap;
typedef std::map<glw::GLint, glw::GLint> IntIntMap;
typedef std::vector<String> StringVector;
typedef std::vector<int> IntVector;
typedef std::map<int, glu::Texture2D*> Texture2DMap;
typedef std::map<int, glu::Texture2DArray*> Texture2DArrayMap;
typedef std::map<int, glu::Texture3D*> Texture3DMap;
//=========================================================================
//= utility classes
//=========================================================================
//= string stream that saves some typing
class StringStream : public std::ostringstream
{
public:
void reset()
{
clear();
str("");
}
};
class LayoutBindingProgram;
class IProgramContextSupplier
{
public:
virtual ~IProgramContextSupplier()
{
}
virtual Context& getContext() = 0;
virtual const LayoutBindingParameters& getTestParameters() = 0;
virtual eStageType getStage() = 0;
virtual const String& getSource(eStageType stage) = 0;
virtual LayoutBindingProgram* createProgram() = 0;
};
class LayoutBindingProgram
{
public:
LayoutBindingProgram(IProgramContextSupplier& contextSupplier)
: m_contextSupplier(contextSupplier)
, m_context(contextSupplier.getContext().getRenderContext())
, m_stage(contextSupplier.getStage())
, m_testParams(contextSupplier.getTestParameters())
, m_gl(contextSupplier.getContext().getRenderContext().getFunctions())
{
if (getStage() != ComputeShader)
m_program = new glu::ShaderProgram(
m_context, glu::makeVtxFragSources(m_contextSupplier.getSource(VertexShader).c_str(),
m_contextSupplier.getSource(FragmentShader).c_str()));
else
m_program = new glu::ShaderProgram(
m_context,
glu::ProgramSources() << glu::ComputeSource(m_contextSupplier.getSource(ComputeShader).c_str()));
}
virtual ~LayoutBindingProgram()
{
delete m_program;
}
class LayoutBindingProgramAutoPtr
{
public:
LayoutBindingProgramAutoPtr(IProgramContextSupplier& contextSupplier)
{
m_program = contextSupplier.createProgram();
}
~LayoutBindingProgramAutoPtr()
{
delete m_program;
}
LayoutBindingProgram* operator->()
{
return m_program;
}
private:
LayoutBindingProgram* m_program;
};
String getErrorLog(bool dumpShaders = false)
{
StringStream errLog;
if (getStage() != ComputeShader)
{
const glu::ShaderInfo& fragmentShaderInfo = m_program->getShaderInfo(glu::SHADERTYPE_FRAGMENT);
const glu::ShaderInfo& vertexShaderInfo = m_program->getShaderInfo(glu::SHADERTYPE_VERTEX);
if (!fragmentShaderInfo.compileOk || !m_program->getProgramInfo().linkOk || dumpShaders)
{
errLog << "### dump of " << stageToName(FragmentShader) << "###\n";
String msg((dumpShaders ? "Fragment shader should not have compiled" :
"Vertex shader compile failed while testing "));
errLog << "Fragment shader compile failed while testing " << stageToName(getStage()) << ": "
<< fragmentShaderInfo.infoLog << "\n";
errLog << m_contextSupplier.getSource(FragmentShader);
}
if (!vertexShaderInfo.compileOk || !m_program->getProgramInfo().linkOk || dumpShaders)
{
errLog << "### dump of " << stageToName(VertexShader) << "###\n";
String msg((dumpShaders ? "Vertex shader should not have compiled" :
"Vertex shader compile failed while testing "));
errLog << msg << stageToName(getStage()) << ": " << vertexShaderInfo.infoLog << "\n";
errLog << m_contextSupplier.getSource(VertexShader);
}
}
else
{
const glu::ShaderInfo& computeShaderInfo = m_program->getShaderInfo(glu::SHADERTYPE_COMPUTE);
if (!computeShaderInfo.compileOk || !m_program->getProgramInfo().linkOk || dumpShaders)
{
errLog << "### dump of " << stageToName(ComputeShader) << "###\n";
String msg((dumpShaders ? "Compute shader should not have compiled" :
"Compute shader compile failed while testing "));
errLog << msg << stageToName(ComputeShader) << ": " << computeShaderInfo.infoLog << "\n";
errLog << m_contextSupplier.getSource(ComputeShader);
}
}
if (!m_program->getProgramInfo().linkOk)
{
getStage();
errLog << "Linking failed while testing " << stageToName(getStage()) << ": "
<< m_program->getProgramInfo().infoLog << "\n";
errLog << "### other stages ###\n";
switch (getStage())
{
case FragmentShader:
errLog << "### dump of " << stageToName(VertexShader) << "###\n";
errLog << m_contextSupplier.getSource(VertexShader);
break;
case VertexShader:
errLog << "### dump of " << stageToName(FragmentShader) << "###\n";
errLog << stageToName(FragmentShader) << "\n";
errLog << m_contextSupplier.getSource(FragmentShader);
break;
case ComputeShader:
errLog << "### dump of " << stageToName(ComputeShader) << "###\n";
errLog << stageToName(ComputeShader) << "\n";
errLog << m_contextSupplier.getSource(ComputeShader);
break;
default:
DE_ASSERT(0);
break;
}
}
return errLog.str();
}
private:
IProgramContextSupplier& m_contextSupplier;
const glu::RenderContext& m_context;
const eStageType m_stage; // shader stage currently tested
const LayoutBindingParameters& m_testParams; // parameters for shader generation (table at end of file)
const glw::Functions& m_gl;
glu::ShaderProgram* m_program;
private:
StringIntMap getUniformLocations(StringVector args) const
{
StringVector::iterator it;
StringIntMap locations;
bool passed = true;
for (it = args.begin(); it != args.end(); it++)
{
const char* name = (*it).c_str();
glw::GLint location = m_gl.getUniformLocation(getProgram(), name);
passed &= (0 <= location);
if (passed)
{
locations[name] = location;
}
}
return locations;
}
public:
glw::GLint getProgram() const
{
return m_program->getProgram();
}
const glw::Functions& gl() const
{
return m_gl;
}
virtual eStageType getStage() const
{
return m_stage;
}
String stageToName(eStageType stage) const
{
switch (stage)
{
case FragmentShader:
return "FragmentShader";
case VertexShader:
return "VertexShader";
case ComputeShader:
return "ComputeShader";
default:
DE_ASSERT(0);
break;
}
return String();
}
bool error() const
{
return (m_gl.getError() == GL_NO_ERROR);
}
bool compiledAndLinked() const
{
if (getStage() != ComputeShader)
return m_program->getShaderInfo(glu::SHADERTYPE_FRAGMENT).compileOk &&
m_program->getShaderInfo(glu::SHADERTYPE_FRAGMENT).compileOk && m_program->getProgramInfo().linkOk;
return m_program->getShaderInfo(glu::SHADERTYPE_COMPUTE).compileOk && m_program->getProgramInfo().linkOk;
}
virtual StringIntMap getBindingPoints(StringVector args) const
{
StringIntMap bindingPoints;
StringIntMap locations = getUniformLocations(args);
if (!locations.empty())
{
glw::GLint bindingPoint;
for (StringIntMap::iterator it = locations.begin(); it != locations.end(); it++)
{
glw::GLint location = it->second;
m_gl.getUniformiv(getProgram(), location, &bindingPoint);
bool hasNoError = (GL_NO_ERROR == m_gl.getError());
if (hasNoError)
{
bindingPoints[it->first] = bindingPoint;
}
}
}
return bindingPoints;
}
virtual bool setBindingPoints(StringVector list, glw::GLint bindingPoint) const
{
bool bNoError = true;
StringIntMap locations = getUniformLocations(list);
if (!locations.empty())
{
for (StringIntMap::iterator it = locations.begin(); it != locations.end(); it++)
{
m_gl.uniform1i(it->second, bindingPoint);
bNoError &= (GL_NO_ERROR == m_gl.getError());
}
}
return bNoError;
}
virtual StringIntMap getOffsets(StringVector /*args*/) const
{
return StringIntMap();
}
};
class LayoutBindingTestResult
{
public:
LayoutBindingTestResult(bool passed = true, const String& reason = String(), bool notRunforThisContext = false)
: m_passed(passed), m_notRunForThisContext(notRunforThisContext), m_reason(reason)
{
}
public:
bool testPassed() const
{
return m_passed;
}
String getReason() const
{
return m_reason;
}
bool runForThisContext() const
{
return !m_notRunForThisContext;
}
private:
bool m_passed;
bool m_notRunForThisContext;
String m_reason;
};
class IntegerConstant
{
public:
enum Literals
{
decimal = 0,
decimal_u,
decimal_U,
octal,
octal_u,
octal_U,
hex_x,
hex_X,
hex_u,
hex_u_X,
hex_U,
hex_U_X,
last
};
public:
IntegerConstant(Literals lit, int ai) : asInt(ai)
{
StringStream s;
switch (lit)
{
case decimal:
s << asInt;
break;
case decimal_u:
s << asInt << "u";
break;
case decimal_U:
s << asInt << "U";
break;
case octal:
s << "0" << std::oct << asInt;
break;
case octal_u:
s << "0" << std::oct << asInt << "u";
break;
case octal_U:
s << "0" << std::oct << asInt << "U";
break;
case hex_x:
s << "0x" << std::hex << asInt;
break;
case hex_X:
s << "0X" << std::hex << asInt;
break;
case hex_u:
s << "0x" << std::hex << asInt << "u";
break;
case hex_u_X:
s << "0X" << std::hex << asInt << "u";
break;
case hex_U:
s << "0x" << std::hex << asInt << "U";
break;
case hex_U_X:
s << "0X" << std::hex << asInt << "U";
break;
case last:
default:
DE_ASSERT(0);
}
asString = s.str();
}
public:
String asString;
int asInt;
};
//*****************************************************************************
class LayoutBindingBaseCase : public TestCase, public IProgramContextSupplier
{
public:
LayoutBindingBaseCase(Context& context, const char* name, const char* description, StageType stage,
LayoutBindingParameters& samplerType, glu::GLSLVersion glslVersion);
virtual ~LayoutBindingBaseCase(void);
IterateResult iterate(void);
// overrideable subtests
virtual LayoutBindingTestResult binding_basic_default(void);
virtual LayoutBindingTestResult binding_basic_explicit(void);
virtual LayoutBindingTestResult binding_basic_multiple(void);
virtual LayoutBindingTestResult binding_basic_render(void);
virtual LayoutBindingTestResult binding_integer_constant(void);
virtual LayoutBindingTestResult binding_integer_constant_expression(void);
virtual LayoutBindingTestResult binding_array_size(void);
virtual LayoutBindingTestResult binding_array_implicit(void);
virtual LayoutBindingTestResult binding_array_multiple(void);
virtual LayoutBindingTestResult binding_api_update(void);
virtual LayoutBindingTestResult binding_compilation_errors(void);
virtual LayoutBindingTestResult binding_link_errors(void);
virtual LayoutBindingTestResult binding_examples(void);
virtual LayoutBindingTestResult binding_mixed_order(void);
private:
// drawTest normal vs. compute
typedef LayoutBindingTestResult (LayoutBindingBaseCase::*LayoutBindingDrawTestPtr)(glw::GLint program, int binding);
LayoutBindingDrawTestPtr m_drawTest;
// pointer type for subtests
typedef LayoutBindingTestResult (LayoutBindingBaseCase::*LayoutBindingSubTestPtr)();
// test table entry
struct LayoutBindingSubTest
{
char const* name;
char const* description;
LayoutBindingSubTestPtr test;
};
// IProgramContextSupplier interface
protected:
const LayoutBindingParameters& getTestParameters()
{
return m_testParams;
}
const glu::RenderContext& getRenderContext()
{
return m_context.getRenderContext();
}
virtual eStageType getStage()
{
return m_stage.type;
}
const String& getSource(eStageType stage)
{
return m_sources[stage];
}
Context& getContext()
{
return m_context;
}
const glw::Functions& gl()
{
return m_context.getRenderContext().getFunctions();
}
bool needsPrecision() const
{
if (isContextTypeES(m_context.getRenderContext().getType()) || m_glslVersion == glu::GLSL_VERSION_450)
{
return (m_testParams.surface_type != UniformBlock) && (m_testParams.surface_type != ShaderStorageBuffer);
}
else
{
return (m_testParams.surface_type != UniformBlock) && (m_testParams.surface_type != ShaderStorageBuffer) &&
(m_testParams.surface_type != AtomicCounter) && (m_testParams.surface_type != Image);
}
}
protected:
std::vector<int> makeSparseRange(int maxElement, int minElement = 0) const
{
static float rangeT[] = { 0.0f, 0.1f, 0.5f, 0.6f, 0.9f, 1.0f };
std::vector<float> rangeTemplate = makeVector(rangeT);
float max = rangeTemplate.back();
float range = (float)((maxElement - 1) - minElement);
std::vector<int> result;
for (std::vector<float>::iterator it = rangeTemplate.begin(); it != rangeTemplate.end(); it++)
{
float e = *it;
e = (e * range) / max;
result.insert(result.end(), minElement + (int)e);
}
return result;
}
glu::GLSLVersion getGLSLVersion()
{
return m_glslVersion;
}
bool isStage(eStageType stage)
{
return (stage == m_stage.type);
}
void setTemplateParam(eStageType stage, const char* param, const String& value)
{
m_templateParams[stage][param] = value.c_str();
}
void setTemplateParam(const char* param, const String& value)
{
setTemplateParam(m_stage.type, param, value);
}
void updateTemplate(eStageType stage)
{
m_sources[stage] = tcu::StringTemplate(m_templates[stage]).specialize(m_templateParams[stage]);
}
void updateTemplate()
{
updateTemplate(m_stage.type);
}
template <class T0, class T1>
String generateLog(const String& msg, T0 result, T1 expected)
{
StringStream s;
s << msg << " expected: " << expected << " actual: " << result << "\n";
s << getSource(VertexShader) << "\n";
s << getSource(FragmentShader) << "\n";
return s.str();
}
private:
std::vector<LayoutBindingSubTest> m_tests;
void init(void)
{
m_drawTest =
(getStage() == ComputeShader) ? &LayoutBindingBaseCase::drawTestCompute : &LayoutBindingBaseCase::drawTest;
#define MAKE_TEST_ENTRY(__subtest_name__) { #__subtest_name__, "", &LayoutBindingBaseCase::__subtest_name__ }
LayoutBindingSubTest tests[] = {
MAKE_TEST_ENTRY(binding_basic_default), MAKE_TEST_ENTRY(binding_basic_explicit),
MAKE_TEST_ENTRY(binding_basic_multiple), MAKE_TEST_ENTRY(binding_basic_render),
MAKE_TEST_ENTRY(binding_integer_constant), MAKE_TEST_ENTRY(binding_integer_constant_expression),
MAKE_TEST_ENTRY(binding_array_size), MAKE_TEST_ENTRY(binding_array_implicit),
MAKE_TEST_ENTRY(binding_array_multiple), MAKE_TEST_ENTRY(binding_api_update),
MAKE_TEST_ENTRY(binding_compilation_errors), MAKE_TEST_ENTRY(binding_link_errors),
MAKE_TEST_ENTRY(binding_examples), MAKE_TEST_ENTRY(binding_mixed_order)
};
m_tests = makeVector(tests);
m_uniformDeclTemplate = "${LAYOUT}${KEYWORD}${UNIFORM_TYPE}${UNIFORM_BLOCK_NAME}${UNIFORM_BLOCK}${UNIFORM_"
"INSTANCE_NAME}${UNIFORM_ARRAY};\n";
m_expectedColor = tcu::Vec4(0.0, 1.0f, 0.0f, 1.0f);
switch (getTestParameters().texture_type)
{
case TwoD:
{
// 2D
glu::ImmutableTexture2D* texture2D =
new glu::ImmutableTexture2D(getContext().getRenderContext(), GL_RGBA8, 2, 2);
texture2D->getRefTexture().allocLevel(0);
tcu::clear(texture2D->getRefTexture().getLevel(0), m_expectedColor);
texture2D->upload();
if (m_textures2D.find(0) != m_textures2D.end())
{
delete m_textures2D[0];
}
m_textures2D[0] = texture2D;
}
break;
case TwoDArray:
{
// 2DArray
glu::Texture2DArray* texture2DArray =
new glu::Texture2DArray(getContext().getRenderContext(), GL_RGBA8, 2, 2, 1);
texture2DArray->getRefTexture().allocLevel(0);
tcu::clear(texture2DArray->getRefTexture().getLevel(0), m_expectedColor);
texture2DArray->upload();
if (m_textures2DArray.find(0) != m_textures2DArray.end())
{
delete m_textures2DArray[0];
}
m_textures2DArray[0] = texture2DArray;
}
break;
// 3D
case ThreeD:
{
glu::Texture3D* texture3D = new glu::Texture3D(getContext().getRenderContext(), GL_RGBA8, 2, 2, 1);
texture3D->getRefTexture().allocLevel(0);
tcu::clear(texture3D->getRefTexture().getLevel(0), m_expectedColor);
texture3D->upload();
if (m_textures3D.find(0) != m_textures3D.end())
{
delete m_textures3D[0];
}
m_textures3D[0] = texture3D;
}
break;
case None:
// test case where no texture allocation is needed
break;
default:
DE_ASSERT(0);
break;
}
}
String initDefaultVSContext()
{
m_templates[VertexShader] = "${VERSION}"
"layout(location=0) in vec2 inPosition;\n"
"${UNIFORM_DECL}\n"
"flat out ${OUT_VAR_TYPE} fragColor;\n"
"${OPTIONAL_FUNCTION_BLOCK}\n"
"void main(void)\n"
"{\n"
" ${OUT_ASSIGNMENT} ${UNIFORM_ACCESS}\n"
" gl_Position = vec4(inPosition, 0.0, 1.0);\n"
"}\n";
StringMap& args = m_templateParams[VertexShader];
// some samplers and all images don't have default precision qualifier (sampler3D)
// so append a precision default in all sampler and image cases.
StringStream s;
s << glu::getGLSLVersionDeclaration(m_glslVersion) << "\n";
s << "precision highp float;\n";
if (needsPrecision())
{
s << "precision highp " << getTestParameters().uniform_type << ";\n";
}
args["VERSION"] = s.str();
args["UNIFORM_DECL"] = "";
args["OPTIONAL_FUNCTION_BLOCK"] = "";
args["UNIFORM_ACCESS"] = "";
args["OUT_ASSIGNMENT"] = "fragColor =";
args["OUT_VAR_TYPE"] = getTestParameters().vector_type;
args["OUT_VAR"] = "fragColor";
if (m_stage.type != VertexShader)
{
args["OUT_ASSIGNMENT"] = "";
}
return tcu::StringTemplate(m_templates[VertexShader]).specialize(args);
}
String initDefaultFSContext()
{
// build fragment shader
m_templates[FragmentShader] = "${VERSION}"
"layout(location=0) out ${OUT_VAR_TYPE} ${OUT_VAR};\n"
"flat in ${OUT_VAR_TYPE} fragColor;\n"
"${UNIFORM_DECL}\n"
"${OPTIONAL_FUNCTION_BLOCK}\n"
"void main(void)\n"
"{\n"
" ${OUT_ASSIGNMENT} ${UNIFORM_ACCESS}\n"
"}\n";
StringMap& args = m_templateParams[FragmentShader];
// samplers and images don't have default precision qualifier
StringStream s;
s << glu::getGLSLVersionDeclaration(m_glslVersion) << "\n";
s << "precision highp float;\n";
if (needsPrecision())
{
s << "precision highp " << getTestParameters().uniform_type << ";\n";
}
args["VERSION"] = s.str();
args["OUT_VAR_TYPE"] = getTestParameters().vector_type;
args["UNIFORM_ACCESS"] = "vec4(0.0,0.0,0.0,0.0);";
args["UNIFORM_DECL"] = "";
args["OPTIONAL_FUNCTION_BLOCK"] = "";
args["OUT_ASSIGNMENT"] = "outColor =";
args["OUT_VAR"] = "outColor";
// must have a linkage between stage and fragment shader
// that the compiler can't optimize away
if (FragmentShader != m_stage.type)
{
args["UNIFORM_ACCESS"] = "fragColor;";
}
return tcu::StringTemplate(m_templates[FragmentShader]).specialize(args);
}
String initDefaultCSContext()
{
// build compute shader
m_templates[ComputeShader] = "${VERSION}"
"${UNIFORM_DECL}\n"
"${OPTIONAL_FUNCTION_BLOCK}\n"
"void main(void)\n"
"{\n"
" ${OUT_VAR_TYPE} tmp = ${UNIFORM_ACCESS}\n"
" ${OUT_ASSIGNMENT} tmp ${OUT_END}\n"
"}\n";
StringMap& args = m_templateParams[ComputeShader];
// images don't have default precision qualifier
StringStream s;
s << glu::getGLSLVersionDeclaration(m_glslVersion) << "\n";
s << "layout (local_size_x = 1) in;\n"
"precision highp float;\n";
if (needsPrecision())
{
s << "precision highp " << getTestParameters().uniform_type << ";\n";
}
// bindings are per uniform type...
if (getTestParameters().surface_type == Image)
{
s << "layout(binding=0, std430) buffer outData {\n"
" "
<< getTestParameters().vector_type << " outColor;\n"
"};\n";
args["OUT_ASSIGNMENT"] = "outColor =";
args["OUT_END"] = ";";
}
else
{
s << "layout(binding=0, rgba8) uniform highp writeonly image2D outImage;\n";
args["OUT_ASSIGNMENT"] = "imageStore(outImage, ivec2(0), ";
args["OUT_END"] = ");";
}
args["VERSION"] = s.str();
args["OUT_VAR_TYPE"] = getTestParameters().vector_type;
args["UNIFORM_ACCESS"] = "vec4(0.0,0.0,0.0,0.0);";
args["UNIFORM_DECL"] = "";
args["OPTIONAL_FUNCTION_BLOCK"] = "";
return tcu::StringTemplate(m_templates[ComputeShader]).specialize(args);
}
protected:
String buildUniformDecl(const String& keyword, const String& layout, const String& uniform_type,
const String& uniform_block_name, const String& uniform_block,
const String& uniform_instance, const String& uniform_array) const
{
StringMap args;
setArg(args["LAYOUT"], layout);
setArg(args["KEYWORD"], keyword);
if (uniform_block_name.empty())
setArg(args["UNIFORM_TYPE"], uniform_type);
else
args["UNIFORM_TYPE"] = "";
if (!uniform_instance.empty() && !uniform_block_name.empty())
setArg(args["UNIFORM_BLOCK_NAME"], uniform_block_name + "_block");
else
setArg(args["UNIFORM_BLOCK_NAME"], uniform_block_name);
setArg(args["UNIFORM_BLOCK"], uniform_block);
if ((uniform_block_name.empty() && uniform_block.empty()) || !uniform_array.empty())
args["UNIFORM_INSTANCE_NAME"] = uniform_instance;
else
args["UNIFORM_INSTANCE_NAME"] = "";
args["UNIFORM_ARRAY"] = uniform_array;
return tcu::StringTemplate(m_uniformDeclTemplate).specialize(args);
}
virtual String getDefaultUniformName(int idx = 0)
{
StringStream s;
s << "uniform" << idx;
return s.str();
}
virtual String buildUniformName(String& var)
{
std::ostringstream s;
s << var;
return s.str();
}
virtual String buildLayout(const String& binding)
{
std::ostringstream s;
if (!binding.empty())
s << "layout(binding=" << binding << ") ";
return s.str();
}
virtual String buildLayout(int binding)
{
std::ostringstream bindingStr;
bindingStr << binding;
return buildLayout(bindingStr.str());
}
virtual String buildAccess(const String& var)
{
std::ostringstream s;
s << getTestParameters().access_function << "(" << var << "," << getTestParameters().coord_vector_type << "(0)"
<< ")";
return s.str();
}
virtual String buildBlockName(const String& /*name*/)
{
return String();
}
virtual String buildBlock(const String& /*name*/, const String& /*type*/ = String("float"))
{
return String();
}
virtual String buildArray(int idx)
{
StringStream s;
s << "[" << idx << "]";
return s.str();
}
virtual String buildArrayAccess(int uniform, int idx)
{
StringStream s;
s << getDefaultUniformName(uniform) << buildArray(idx);
if (!buildBlockName(getDefaultUniformName()).empty())
{
s << "." << getDefaultUniformName(uniform);
}
return s.str();
}
// return max. binding point allowed
virtual int maxBindings()
{
int units = 0;
if (getTestParameters().surface_type == Image)
{
gl().getIntegerv(GL_MAX_IMAGE_UNITS, &units);
}
else
{
gl().getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &units);
}
return units;
}
// return max. array size allowed
virtual int maxArraySize()
{
int units = 0;
if (getTestParameters().surface_type == Image)
{
switch (m_stage.type)
{
case VertexShader:
gl().getIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &units);
break;
case FragmentShader:
gl().getIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &units);
break;
default:
DE_ASSERT(0);
break;
}
}
else
{
switch (m_stage.type)
{
case VertexShader:
gl().getIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &units);
break;
case FragmentShader:
gl().getIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &units);
break;
default:
DE_ASSERT(0);
break;
}
}
return units;
}
virtual bool isSupported()
{
return (maxArraySize() > 0);
}
virtual void bind(int binding)
{
glw::GLint texTarget = 0;
glw::GLint texName = 0;
switch (getTestParameters().texture_type)
{
case TwoD:
texTarget = GL_TEXTURE_2D;
texName = m_textures2D[0]->getGLTexture();
break;
case TwoDArray:
texTarget = GL_TEXTURE_2D_ARRAY;
texName = m_textures2DArray[0]->getGLTexture();
break;
case ThreeD:
texTarget = GL_TEXTURE_3D;
texName = m_textures3D[0]->getGLTexture();
break;
default:
DE_ASSERT(0);
break;
}
switch (getTestParameters().surface_type)
{
case Texture:
gl().activeTexture(GL_TEXTURE0 + binding);
gl().bindTexture(texTarget, texName);
gl().texParameteri(texTarget, GL_TEXTURE_WRAP_S, glu::getGLWrapMode(tcu::Sampler::CLAMP_TO_EDGE));
gl().texParameteri(texTarget, GL_TEXTURE_WRAP_T, glu::getGLWrapMode(tcu::Sampler::CLAMP_TO_EDGE));
gl().texParameteri(texTarget, GL_TEXTURE_MIN_FILTER, glu::getGLFilterMode(tcu::Sampler::NEAREST));
gl().texParameteri(texTarget, GL_TEXTURE_MAG_FILTER, glu::getGLFilterMode(tcu::Sampler::NEAREST));
break;
case Image:
gl().bindImageTexture(binding, texName, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
break;
default:
DE_ASSERT(0);
break;
}
}
virtual void unbind(int binding)
{
glw::GLint texTarget = 0;
switch (getTestParameters().texture_type)
{
case TwoD:
texTarget = GL_TEXTURE_2D;
break;
case TwoDArray:
texTarget = GL_TEXTURE_2D_ARRAY;
break;
case ThreeD:
texTarget = GL_TEXTURE_3D;
break;
default:
DE_ASSERT(0);
break;
}
switch (getTestParameters().surface_type)
{
case Texture:
gl().activeTexture(GL_TEXTURE0 + binding);
gl().bindTexture(texTarget, 0);
break;
case Image:
gl().bindImageTexture(binding, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32F);
break;
default:
DE_ASSERT(0);
break;
}
}
virtual LayoutBindingTestResult drawTest(glw::GLint program, int binding);
virtual LayoutBindingTestResult drawTestCompute(glw::GLint program, int binding);
// allocate resources needed for all subtests, i.e. textures
virtual void setupTest()
{
}
// cleanup resources needed for all subtests
virtual void teardownTest()
{
for (Texture2DMap::iterator it = m_textures2D.begin(); it != m_textures2D.end(); it++)
{
delete it->second;
}
m_textures2D.clear();
for (Texture2DArrayMap::iterator it = m_textures2DArray.begin(); it != m_textures2DArray.end(); it++)
{
delete it->second;
}
m_textures2DArray.clear();
for (Texture3DMap::iterator it = m_textures3D.begin(); it != m_textures3D.end(); it++)
{
delete it->second;
}
m_textures3D.clear();
}
private:
// appends a space to the argument (make shader source pretty)
void setArg(String& arg, const String& value) const
{
if (!value.empty())
arg = value + String(" ");
else
arg = String();
}
private:
LayoutBindingParameters m_testParams;
StageType m_stage;
std::map<eStageType, String> m_sources;
std::map<eStageType, StringMap> m_templateParams;
std::map<eStageType, const char*> m_templates;
Texture2DMap m_textures2D;
Texture2DArrayMap m_textures2DArray;
Texture3DMap m_textures3D;
tcu::Vec4 m_expectedColor;
const char* m_uniformDeclTemplate;
glu::GLSLVersion m_glslVersion;
};
LayoutBindingBaseCase::LayoutBindingBaseCase(Context& context, const char* name, const char* description,
StageType stage, LayoutBindingParameters& samplerType,
glu::GLSLVersion glslVersion)
: TestCase(context, name, description)
, m_drawTest(DE_NULL)
, m_testParams(samplerType)
, m_stage(stage)
, m_uniformDeclTemplate(DE_NULL)
, m_glslVersion(glslVersion)
{
}
LayoutBindingBaseCase::~LayoutBindingBaseCase(void)
{
teardownTest();
}
LayoutBindingBaseCase::IterateResult LayoutBindingBaseCase::iterate(void)
{
tcu::TestLog& log = m_context.getTestContext().getLog();
bool passed = true;
if (!isSupported())
{
log << tcu::TestLog::Section("NotSupported", "");
log << tcu::TestLog::Message << "This test was not run as minimum requirements were not met."
<< tcu::TestLog::EndMessage;
log << tcu::TestLog::EndSection;
getContext().getTestContext().setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "NotSupported");
return STOP;
}
// init test case (create shader templates and textures)
init();
// allocate resources for all subtests
setupTest();
for (std::vector<LayoutBindingSubTest>::iterator it = m_tests.begin(); it != m_tests.end(); it++)
{
// need to reset templates and their args to a clean state before every
// test to avoid bleeding.
m_sources[VertexShader] = initDefaultVSContext();
m_sources[FragmentShader] = initDefaultFSContext();
m_sources[ComputeShader] = initDefaultCSContext();
LayoutBindingTestResult result = ((*this).*((*it).test))();
if (!result.testPassed())
{
log << tcu::TestLog::Section((*it).name, (*it).description);
log << tcu::TestLog::Message << result.getReason() << tcu::TestLog::EndMessage;
log << tcu::TestLog::EndSection;
}
if (!result.runForThisContext())
{
log << tcu::TestLog::Section((*it).name, (*it).description);
log << tcu::TestLog::Message << "This test was not run for this context as it does not apply."
<< tcu::TestLog::EndMessage;
log << tcu::TestLog::EndSection;
}
passed &= result.testPassed();
}
// cleanup resources
teardownTest();
/*=========================================================================
TEST results
=========================================================================*/
getContext().getTestContext().setTestResult(passed ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
passed ? "Pass" : "Fail");
return STOP;
}
/*=========================================================================
// bind resource to specified binding point and program and
// dispatch a computation, read back image at (0,0)
=========================================================================*/
LayoutBindingTestResult LayoutBindingBaseCase::drawTestCompute(glw::GLint program, int binding)
{
const glw::Functions& l_gl = getContext().getRenderContext().getFunctions();
bool passed = true;
DE_TEST_ASSERT(getStage() == ComputeShader);
l_gl.useProgram(program);
deUint32 fb_or_ssb;
if (getTestParameters().surface_type == Image)
{
bind(binding);
glw::GLfloat buffer[4] = { 0.1f, 0.2f, 0.3f, 0.4f };
l_gl.genBuffers(1, &fb_or_ssb);
l_gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, fb_or_ssb);
l_gl.bufferData(GL_SHADER_STORAGE_BUFFER, 4 * sizeof(glw::GLfloat), buffer, GL_DYNAMIC_COPY);
l_gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, fb_or_ssb);
l_gl.dispatchCompute(1, 1, 1);
l_gl.memoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
tcu::Vec4 pixel =
*(tcu::Vec4*)l_gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 4 * sizeof(glw::GLfloat), GL_MAP_READ_BIT);
l_gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
l_gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
l_gl.deleteBuffers(1, &fb_or_ssb);
unbind(binding);
tcu::Vec4 expected(0.0f, 1.0f, 0.0f, 1.0f);
passed = (pixel == expected);
if (!passed)
{
return LayoutBindingTestResult(passed, generateLog(String("drawTestCompute failed"), expected, pixel));
}
}
else
{
deUint32 something = 0x01020304, tex;
l_gl.genTextures(1, &tex);
l_gl.bindTexture(GL_TEXTURE_2D, tex);
l_gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
l_gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
l_gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &something);
bind(binding);
l_gl.bindImageTexture(0, tex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);
l_gl.dispatchCompute(1, 1, 1);
l_gl.memoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT);
l_gl.bindImageTexture(0, 0, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);
l_gl.genFramebuffers(1, &fb_or_ssb);
l_gl.bindFramebuffer(GL_FRAMEBUFFER, fb_or_ssb);
l_gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
l_gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &something);
l_gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
l_gl.deleteFramebuffers(1, &fb_or_ssb);
l_gl.deleteTextures(1, &tex);
unbind(binding);
const deUint32 expected = 0xff00ff00;
passed = (expected == something);
if (!passed)
{
return LayoutBindingTestResult(
passed, generateLog(String("drawTestCompute failed"), tcu::RGBA(expected), tcu::RGBA(something)));
}
}
return passed;
}
/*=========================================================================
// bind resource to specified binding point and program and
// return result of comparison of rendered pixel at (0,0) with expected
=========================================================================*/
LayoutBindingTestResult LayoutBindingBaseCase::drawTest(glw::GLint program, int binding)
{
const glw::Functions& GL = getContext().getRenderContext().getFunctions();
const tcu::RenderTarget& renderTarget = getContext().getRenderContext().getRenderTarget();
glw::GLuint viewportW = renderTarget.getWidth();
glw::GLuint viewportH = renderTarget.getHeight();
tcu::Surface renderedFrame(viewportW, viewportH);
bool passed = true;
DE_TEST_ASSERT(getStage() != ComputeShader);
GL.viewport(0, 0, viewportW, viewportH);
GL.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
GL.clear(GL_COLOR_BUFFER_BIT);
static const float position[] = {
-1.0f, -1.0f, -1.0f, +1.0f, +1.0f, -1.0f, +1.0f, +1.0f,
};
static const deUint16 quadIndices[] = { 0, 1, 2, 2, 1, 3 };
GL.useProgram(program);
bind(binding);
static const glu::VertexArrayBinding vertexArrays[] = {
glu::va::Float("inPosition", 2, 4, 0, &position[0]),
};
glu::draw(getContext().getRenderContext(), program, DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0],
glu::pr::TriangleStrip(DE_LENGTH_OF_ARRAY(quadIndices), &quadIndices[0]));
glu::readPixels(getContext().getRenderContext(), 0, 0, renderedFrame.getAccess());
tcu::RGBA pixel = renderedFrame.getPixel(0, 0);
passed = (pixel == tcu::RGBA(m_expectedColor));
unbind(binding);
if (!passed)
{
return LayoutBindingTestResult(passed,
generateLog(String("drawTest failed"), m_expectedColor, pixel.getPacked()));
}
return true;
}
//== verify that binding point is default w/o layout binding
LayoutBindingTestResult LayoutBindingBaseCase::binding_basic_default()
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
String decl = buildUniformDecl(String(getTestParameters().keyword), buildLayout(String()),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", decl);
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
const String& u = buildBlockName(getDefaultUniformName());
if (!u.empty())
list.push_back(u + "_block");
else
list.push_back(getDefaultUniformName());
StringIntMap bindingPoints = program->getBindingPoints(list);
passed &= bindingPoints.size() == list.size() && (bindingPoints[u] == 0);
if (!passed)
{
return LayoutBindingTestResult(passed,
generateLog(String("binding point did not match default"), bindingPoints[u], 0));
}
return true;
}
//== verify that binding point has specified value
LayoutBindingTestResult LayoutBindingBaseCase::binding_basic_explicit()
{
bool passed = true;
{
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
}
std::vector<int> bindings = makeSparseRange(maxBindings());
for (std::vector<int>::iterator it = bindings.begin(); it < bindings.end(); it++)
{
int binding = *it;
String decl =
buildUniformDecl(String(getTestParameters().keyword), buildLayout(binding),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", decl);
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
const String& s = buildBlockName(getDefaultUniformName());
if (!s.empty())
list.push_back(s + "_block");
else
list.push_back(getDefaultUniformName());
StringIntMap bindingPoints = program->getBindingPoints(list);
passed &= bindingPoints.size() == list.size() && (binding == bindingPoints[list[0]]);
if (!passed)
{
return LayoutBindingTestResult(
passed, generateLog(String("binding point did not match default"), bindingPoints[list[0]], binding));
}
}
return true;
}
//== verify that binding works with multiple samplers (same binding points)
LayoutBindingTestResult LayoutBindingBaseCase::binding_basic_multiple()
{
bool passed = true;
glw::GLint baseBindingPoint = maxBindings() - 1;
String decl0 = buildUniformDecl(String(getTestParameters().keyword), buildLayout(baseBindingPoint),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
String decl1 = buildUniformDecl(String(getTestParameters().keyword), buildLayout(baseBindingPoint),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName(1)),
buildBlock(getDefaultUniformName(1)), getDefaultUniformName(1), String());
setTemplateParam("UNIFORM_DECL", decl0 + decl1);
StringStream s;
s << buildAccess(getDefaultUniformName()) << " + " << buildAccess(getDefaultUniformName(1)) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
String u = buildBlockName(getDefaultUniformName());
if (!u.empty())
list.push_back(u + "_block");
else
list.push_back(getDefaultUniformName());
u = buildBlockName(getDefaultUniformName(1));
if (!u.empty())
list.push_back(u + "_block");
else
list.push_back(getDefaultUniformName(1));
StringIntMap bindingPoints = program->getBindingPoints(list);
passed &= (baseBindingPoint == bindingPoints[list[0]]) && (baseBindingPoint == bindingPoints[list[1]]);
if (!passed)
{
String err;
err = generateLog(String("binding point did not match default"), bindingPoints[list[0]], baseBindingPoint);
err += generateLog(String("binding point did not match default"), bindingPoints[list[1]], baseBindingPoint);
return LayoutBindingTestResult(passed, err);
}
return true;
}
//== verify that binding point has specified value
LayoutBindingTestResult LayoutBindingBaseCase::binding_basic_render()
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
std::vector<int> bindings = makeSparseRange(maxBindings());
for (std::vector<int>::iterator it = bindings.begin(); it < bindings.end(); it++)
{
int binding = *it;
String decl =
buildUniformDecl(String(getTestParameters().keyword), buildLayout(binding),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", decl);
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
LayoutBindingTestResult drawTestResult = ((*this).*(m_drawTest))(program->getProgram(), binding);
if (!drawTestResult.testPassed())
{
return LayoutBindingTestResult(drawTestResult.testPassed(), drawTestResult.getReason());
}
}
return true;
}
LayoutBindingTestResult LayoutBindingBaseCase::binding_integer_constant()
{
bool passed = true;
std::vector<int> integers = makeSparseRange(maxBindings(), 0);
std::vector<IntegerConstant> integerConstants;
for (int idx = 0; idx < IntegerConstant::last; idx++)
{
for (IntVector::iterator it = integers.begin(); it != integers.end(); it++)
{
integerConstants.push_back(IntegerConstant((IntegerConstant::Literals)idx, (*it)));
}
}
//== verify that binding point can be set with integer constant
for (std::vector<IntegerConstant>::iterator it = integerConstants.begin(); it != integerConstants.end(); it++)
{
String& intConst = (*it).asString;
String decl =
buildUniformDecl(String(getTestParameters().keyword), buildLayout(intConst),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", decl);
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
const String& u = buildBlockName(getDefaultUniformName());
if (!u.empty())
list.push_back(u + "_block");
else
list.push_back(getDefaultUniformName());
StringIntMap bindingPoints = program->getBindingPoints(list);
passed &= ((*it).asInt == bindingPoints[list[0]]);
if (!passed)
{
return LayoutBindingTestResult(passed, generateLog(String("binding point did not match default"),
bindingPoints[list[0]], (*it).asInt));
}
}
//== verify that binding point can be set with integer constant resulting from a preprocessor substitution
for (std::vector<IntegerConstant>::iterator it = integerConstants.begin(); it != integerConstants.end(); it++)
{
String& intConst = (*it).asString;
StringStream s;
s << "#define INT_CONST " << intConst << std::endl;
String decl =
buildUniformDecl(String(getTestParameters().keyword), buildLayout(String("INT_CONST")),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", s.str() + decl);
s.reset();
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
const String& u = buildBlockName(getDefaultUniformName());
if (!u.empty())
list.push_back(u + "_block");
else
list.push_back(getDefaultUniformName());
StringIntMap bindingPoints = program->getBindingPoints(list);
passed &= ((*it).asInt == bindingPoints[list[0]]);
if (!passed)
{
return LayoutBindingTestResult(passed, generateLog(String("binding point did not match default"),
bindingPoints[list[0]], (*it).asInt));
}
}
return true;
}
//== test integer constant expressions
//== only for GL
LayoutBindingTestResult LayoutBindingBaseCase::binding_integer_constant_expression()
{
bool passed = true;
if (getGLSLVersion() == glu::GLSL_VERSION_310_ES)
return LayoutBindingTestResult(passed, String(), true);
return LayoutBindingTestResult(passed, String(), true);
}
//== test different sized arrays
LayoutBindingTestResult LayoutBindingBaseCase::binding_array_size(void)
{
bool passed = true;
std::vector<int> arraySizes = makeSparseRange(maxArraySize(), 1);
for (std::vector<int>::iterator it = arraySizes.begin(); it < arraySizes.end(); it++)
{
int arraySize = *it;
String decl =
buildUniformDecl(String(getTestParameters().keyword), buildLayout(maxBindings() - arraySize - 1),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), buildArray(arraySize));
setTemplateParam("UNIFORM_DECL", decl);
StringStream s;
for (int idx = 0; idx < arraySize; idx++)
{
s << (idx ? " + " : "") << buildAccess(buildArrayAccess(0, idx));
}
s << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
for (int idx = 0; idx < arraySize; idx++)
{
std::ostringstream texUnitStr;
texUnitStr << getDefaultUniformName();
const String& u = buildBlockName(getDefaultUniformName());
if (!u.empty())
texUnitStr << "_block";
texUnitStr << buildArray(idx);
list.push_back(texUnitStr.str());
}
StringIntMap bindingPoints = program->getBindingPoints(list);
for (int idx = 0; idx < arraySize; idx++)
{
passed &= (((maxBindings() - arraySize - 1) + idx) == bindingPoints[list[idx]]);
if (!passed)
{
return LayoutBindingTestResult(passed, generateLog(String("binding point did not match default"),
bindingPoints[list[idx]],
(maxBindings() - arraySize - 1) + idx));
}
}
}
return LayoutBindingTestResult(passed, String());
}
//== verify first element takes binding point specified in binding and
//== subsequent entries take the next consecutive units
LayoutBindingTestResult LayoutBindingBaseCase::binding_array_implicit(void)
{
bool passed = true;
std::vector<int> bindings = makeSparseRange(maxBindings(), 0);
for (std::vector<int>::iterator it = bindings.begin(); it < bindings.end(); it++)
{
int baseBindingPoint = *it;
int arraySize = std::min(maxBindings() - baseBindingPoint, 4);
String decl =
buildUniformDecl(String(getTestParameters().keyword), buildLayout(baseBindingPoint),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), buildArray(arraySize));
setTemplateParam("UNIFORM_DECL", decl);
StringStream s;
for (int idx = 0; idx < arraySize; idx++)
{
s << (idx ? " + " : "") << buildAccess(buildArrayAccess(0, idx));
}
s << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
for (int idx = 0; idx < arraySize; idx++)
{
std::ostringstream texUnitStr;
texUnitStr << getDefaultUniformName();
const String& u = buildBlockName(getDefaultUniformName());
if (!u.empty())
texUnitStr << "_block";
texUnitStr << buildArray(idx);
list.push_back(texUnitStr.str());
}
StringIntMap bindingPoints = program->getBindingPoints(list);
for (int idx = 0; idx < arraySize; idx++)
{
passed &= ((baseBindingPoint + idx) == bindingPoints[list[idx]]);
if (!passed)
{
return LayoutBindingTestResult(passed, generateLog(String("binding point did not match default"),
bindingPoints[list[idx]], (baseBindingPoint + idx)));
}
}
}
return LayoutBindingTestResult(passed, String());
}
//== multiple arrays :verify first element takes binding point specified in binding and
//== subsequent entries take the next consecutive units
LayoutBindingTestResult LayoutBindingBaseCase::binding_array_multiple(void)
{
bool passed = true;
// two arrays, limit max. binding to one
std::vector<int> bindings = makeSparseRange(maxBindings() - 2, 0);
for (std::vector<int>::iterator it = bindings.begin(); it < bindings.end(); it++)
{
int baseBindingPoint = *it;
// total distance from current binding point to end of binding range
// split over two arrays, making sure that the array sizes don't
// exceed max. array sizes per stage
int arraySize = (maxBindings() - baseBindingPoint - 1) / 2;
arraySize = std::min(arraySize, maxArraySize() / 2);
StringStream s;
String decl =
buildUniformDecl(String(getTestParameters().keyword), buildLayout(baseBindingPoint),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), buildArray(arraySize));
String another_decl =
buildUniformDecl(String(getTestParameters().keyword), buildLayout(baseBindingPoint),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName(1)),
buildBlock(getDefaultUniformName(1)), getDefaultUniformName(1), buildArray(arraySize));
setTemplateParam("UNIFORM_DECL", decl + another_decl);
s.reset();
for (int uniform = 0; uniform < 2; uniform++)
{
for (int idx = 0; idx < arraySize; idx++)
{
s << ((idx | uniform) ? " + " : "") << buildAccess(buildArrayAccess(uniform, idx));
}
}
s << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
for (int uniform = 0; uniform < 2; uniform++)
{
list.clear();
for (int idx = 0; idx < arraySize; idx++)
{
std::ostringstream texUnitStr;
texUnitStr << getDefaultUniformName(uniform);
const String& u = buildBlockName(getDefaultUniformName(uniform));
if (!u.empty())
texUnitStr << "_block";
texUnitStr << buildArray(idx);
list.push_back(texUnitStr.str());
}
StringIntMap bindingPoints = program->getBindingPoints(list);
for (int idx = 0; idx < arraySize; idx++)
{
passed &= ((baseBindingPoint + idx) == bindingPoints[list[idx]]);
if (!passed)
{
return LayoutBindingTestResult(passed,
generateLog(String("binding point did not match default"),
bindingPoints[list[idx]], (baseBindingPoint + idx)));
}
}
}
}
return LayoutBindingTestResult(passed, String());
}
//== verify that explicit binding point can be changed via API
LayoutBindingTestResult LayoutBindingBaseCase::binding_api_update(void)
{
bool passed = true;
String decl = buildUniformDecl(String(getTestParameters().keyword), buildLayout(1),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", decl);
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
const String& u = buildBlockName(getDefaultUniformName());
if (!u.empty())
list.push_back(u + "_block");
else
list.push_back(getDefaultUniformName());
StringIntMap bindingPoints = program->getBindingPoints(list);
gl().useProgram(program->getProgram());
program->setBindingPoints(list, maxBindings() - 1);
gl().useProgram(0);
bindingPoints = program->getBindingPoints(list);
passed &= bindingPoints[list[0]] == (maxBindings() - 1);
if (!passed)
{
return LayoutBindingTestResult(passed, generateLog(String("binding point did not match default"),
bindingPoints[list[0]], maxBindings() - 1));
}
return LayoutBindingTestResult(passed, String());
}
LayoutBindingTestResult LayoutBindingBaseCase::binding_compilation_errors(void)
{
bool passed = true;
String decl;
// verify "uniform float var;" doesn't compile
{
StringStream s;
s << getTestParameters().vector_type << "(0.0);";
setTemplateParam("UNIFORM_ACCESS", s.str());
s.reset();
s << "layout(binding=0) "
<< "uniform float tex0;";
setTemplateParam("UNIFORM_DECL", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= !program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog(true));
}
}
// verify that non-constant integer expression in binding fails
{
decl = buildUniformDecl(String(getTestParameters().keyword), buildLayout(String("0.0")),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", decl);
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= !program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog(true));
}
}
{
decl = buildUniformDecl(String(getTestParameters().keyword), buildLayout(String("-1")),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", decl);
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= !program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog(true));
}
}
{
decl = buildUniformDecl(String(getTestParameters().keyword), buildLayout(maxBindings()),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", decl);
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= !program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog(true));
}
}
{
decl = buildUniformDecl(String(getTestParameters().keyword), buildLayout(maxBindings()),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", decl);
StringStream s;
s << "vec4(0.0);\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= !program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog(true));
}
}
return LayoutBindingTestResult(passed, String());
}
LayoutBindingTestResult LayoutBindingBaseCase::binding_link_errors(void)
{
bool passed = true;
// same sampler with different binding in two compilation units
if (isStage(VertexShader))
{
String decl =
buildUniformDecl(String(getTestParameters().keyword), buildLayout(1),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam(VertexShader, "UNIFORM_DECL", decl);
setTemplateParam(VertexShader, "OUT_ASSIGNMENT", String("fragColor ="));
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam(VertexShader, "UNIFORM_ACCESS", s.str());
updateTemplate(VertexShader);
decl = buildUniformDecl(String(getTestParameters().keyword), buildLayout(3),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam(FragmentShader, "UNIFORM_DECL", decl);
s.reset();
s << "fragColor + " << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam(FragmentShader, "UNIFORM_ACCESS", s.str());
updateTemplate(FragmentShader);
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed = !program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog(true));
}
}
return LayoutBindingTestResult(passed, String());
}
// this subtest is generically empty. Overwritten in test cases as needed.
LayoutBindingTestResult LayoutBindingBaseCase::binding_examples(void)
{
return true;
}
// just for image and atomic counter cases
LayoutBindingTestResult LayoutBindingBaseCase::binding_mixed_order(void)
{
return true;
}
//=========================================================================
// test case Sampler layout binding
//=========================================================================
class SamplerLayoutBindingCase : public LayoutBindingBaseCase
{
public:
SamplerLayoutBindingCase(Context& context, const char* name, const char* description, StageType stage,
LayoutBindingParameters& samplerType, glu::GLSLVersion glslVersion);
~SamplerLayoutBindingCase(void);
private:
/*virtual*/
LayoutBindingProgram* createProgram()
{
return new LayoutBindingProgram(*this);
}
/*virtual*/
String getDefaultUniformName(int idx = 0)
{
StringStream s;
s << "sampler" << idx;
return s.str();
}
/*virtual*/
String buildLayout(const String& binding)
{
std::ostringstream s;
if (!binding.empty())
s << "layout(binding=" << binding << ") ";
return s.str();
}
virtual int maxBindings()
{
int units = 0;
gl().getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &units);
return units;
}
// return max. array size allowed
virtual int maxArraySize()
{
int units = 0;
switch (getStage())
{
case VertexShader:
gl().getIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &units);
break;
case FragmentShader:
gl().getIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &units);
break;
case ComputeShader:
gl().getIntegerv(GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, &units);
break;
default:
DE_ASSERT(0);
break;
}
return units;
}
};
SamplerLayoutBindingCase::SamplerLayoutBindingCase(Context& context, const char* name, const char* description,
StageType stage, LayoutBindingParameters& samplerType,
glu::GLSLVersion glslVersion)
: LayoutBindingBaseCase(context, name, description, stage, samplerType, glslVersion)
{
}
SamplerLayoutBindingCase::~SamplerLayoutBindingCase(void)
{
}
//=========================================================================
// test case Image layout binding
//=========================================================================
class ImageLayoutBindingCase : public LayoutBindingBaseCase
{
public:
ImageLayoutBindingCase(Context& context, const char* name, const char* description, StageType stage,
LayoutBindingParameters& samplerType, glu::GLSLVersion glslVersion);
~ImageLayoutBindingCase(void);
private:
class ImageLayoutBindingProgram : public LayoutBindingProgram
{
public:
ImageLayoutBindingProgram(IProgramContextSupplier& contextSupplier) : LayoutBindingProgram(contextSupplier)
{
}
};
private:
// IProgramContextSupplier
/*virtual*/
LayoutBindingProgram* createProgram()
{
return new ImageLayoutBindingProgram(*this);
}
private:
/*virtual*/
String getDefaultUniformName(int idx = 0)
{
StringStream s;
s << "image" << idx;
return s.str();
}
/*virtual*/
String buildLayout(const String& binding)
{
std::ostringstream s;
if (!binding.empty())
s << "layout(binding=" << binding << ", rgba8) readonly ";
else
s << "layout(rgba8) readonly ";
return s.str();
}
/*virtual*/
int maxBindings()
{
int units = 0;
gl().getIntegerv(GL_MAX_IMAGE_UNITS, &units);
return units;
}
/*virtual*/
int maxArraySize()
{
int units = 0;
switch (getStage())
{
case VertexShader:
gl().getIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &units);
break;
case FragmentShader:
gl().getIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &units);
break;
case ComputeShader:
gl().getIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, &units);
break;
default:
DE_ASSERT(0);
break;
}
return units;
}
private:
//virtual LayoutBindingTestResult binding_basic_default (void);
//virtual LayoutBindingTestResult binding_basic_explicit (void);
//virtual LayoutBindingTestResult binding_basic_multiple (void);
//virtual LayoutBindingTestResult binding_basic_render (void);
//virtual LayoutBindingTestResult binding_integer_constant (void);
//virtual LayoutBindingTestResult binding_integer_constant_expression (void);
//virtual LayoutBindingTestResult binding_array_size (void);
//virtual LayoutBindingTestResult binding_array_implicit (void);
//virtual LayoutBindingTestResult binding_array_multiple (void);
/*virtual*/
LayoutBindingTestResult binding_api_update(void)
{
// only for GL
if (getGLSLVersion() == glu::GLSL_VERSION_310_ES)
return LayoutBindingTestResult(true, String(), true);
return LayoutBindingBaseCase::binding_api_update();
}
//virtual LayoutBindingTestResult binding_compilation_errors (void);
//virtual LayoutBindingTestResult binding_link_errors (void);
//virtual LayoutBindingTestResult binding_link_examples (void);
/*virtual*/
LayoutBindingTestResult binding_mixed_order(void)
{
bool passed = true;
{
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
String decl =
buildUniformDecl(String(getTestParameters().keyword), String("layout(binding=0, rgba8) readonly"),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", decl);
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
}
{
String decl =
buildUniformDecl(String(getTestParameters().keyword), String("layout(r32f, binding=0) readonly"),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", decl);
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
}
return true;
}
};
ImageLayoutBindingCase::ImageLayoutBindingCase(Context& context, const char* name, const char* description,
StageType stage, LayoutBindingParameters& samplerType,
glu::GLSLVersion glslVersion)
: LayoutBindingBaseCase(context, name, description, stage, samplerType, glslVersion)
{
}
ImageLayoutBindingCase::~ImageLayoutBindingCase(void)
{
}
//=========================================================================
// test case Atomic counter binding
//=========================================================================
class AtomicCounterLayoutBindingCase : public LayoutBindingBaseCase
{
public:
AtomicCounterLayoutBindingCase(Context& context, const char* name, const char* description, StageType stage,
LayoutBindingParameters& samplerType, glu::GLSLVersion glslVersion);
~AtomicCounterLayoutBindingCase(void)
{
}
private:
class AtomicCounterLayoutBindingProgram : public LayoutBindingProgram
{
public:
AtomicCounterLayoutBindingProgram(IProgramContextSupplier& contextSupplier)
: LayoutBindingProgram(contextSupplier)
{
}
private:
/*virtual*/
StringIntMap getBindingPoints(StringVector args) const
{
StringIntMap bindingPoints;
for (StringVector::iterator it = args.begin(); it != args.end(); it++)
{
glw::GLuint idx = gl().getProgramResourceIndex(getProgram(), GL_UNIFORM, (*it).c_str());
if (idx != GL_INVALID_INDEX)
{
glw::GLenum param = GL_ATOMIC_COUNTER_BUFFER_INDEX;
glw::GLint atomic_counter_buffer_idx = -1;
gl().getProgramResourceiv(getProgram(), GL_UNIFORM, idx, 1, &param, 1, NULL,
&atomic_counter_buffer_idx);
bool hasNoError = (GL_NO_ERROR == gl().getError()) && (-1 != atomic_counter_buffer_idx);
if (!hasNoError)
continue;
param = GL_BUFFER_BINDING;
glw::GLint value = -1;
gl().getProgramResourceiv(getProgram(), GL_ATOMIC_COUNTER_BUFFER, atomic_counter_buffer_idx, 1,
&param, 1, NULL, &value);
hasNoError = (GL_NO_ERROR == gl().getError()) && (-1 != value);
if (!hasNoError)
continue;
bindingPoints[*it] = value;
}
}
return bindingPoints;
}
/*virtual*/
StringIntMap getOffsets(StringVector args) const
{
StringIntMap bindingPoints;
for (StringVector::iterator it = args.begin(); it != args.end(); it++)
{
glw::GLuint idx = gl().getProgramResourceIndex(getProgram(), GL_UNIFORM, (*it).c_str());
if (idx != GL_INVALID_INDEX)
{
glw::GLenum param = GL_OFFSET;
glw::GLint value = -1;
gl().getProgramResourceiv(getProgram(), GL_UNIFORM, idx, 1, &param, 1, NULL, &value);
bool hasNoError = (GL_NO_ERROR == gl().getError());
if (hasNoError)
{
bindingPoints[*it] = value;
}
}
}
return bindingPoints;
}
};
private:
// IProgramContextSupplier
/*virtual*/
LayoutBindingProgram* createProgram()
{
return new AtomicCounterLayoutBindingProgram(*this);
}
private:
/*virtual*/
String getDefaultUniformName(int idx = 0)
{
StringStream s;
s << "atomic" << idx;
return s.str();
}
/*virtual*/
String buildAccess(const String& var)
{
std::ostringstream s;
s << "vec4(float(atomicCounter(" << var << ")), 1.0, 0.0, 1.0)";
return s.str();
}
int maxBindings()
{
int units = 0;
gl().getIntegerv(GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, &units);
return units;
}
// return max. array size allowed
int maxArraySize()
{
int units = 0;
switch (getStage())
{
case FragmentShader:
gl().getIntegerv(GL_MAX_FRAGMENT_ATOMIC_COUNTERS, &units);
break;
case VertexShader:
gl().getIntegerv(GL_MAX_VERTEX_ATOMIC_COUNTERS, &units);
break;
case ComputeShader:
gl().getIntegerv(GL_MAX_COMPUTE_ATOMIC_COUNTERS, &units);
break;
default:
DE_ASSERT(0);
break;
}
return units;
}
//=========================================================================
// sub-tests overrides
//=========================================================================
private:
LayoutBindingTestResult binding_basic_default(void)
{
return true;
}
//virtual LayoutBindingTestResult binding_basic_explicit (void);
//virtual LayoutBindingTestResult binding_basic_multiple (void);
LayoutBindingTestResult binding_basic_render(void)
{
return true;
}
//virtual LayoutBindingTestResult binding_integer_constant (void);
/*virtual*/
LayoutBindingTestResult binding_array_size(void)
{
bool passed = true;
//== test different sized arrays
std::vector<int> arraySizes = makeSparseRange(maxArraySize(), 1);
for (std::vector<int>::iterator it = arraySizes.begin(); it < arraySizes.end(); it++)
{
int arraySize = *it;
StringStream s;
s << "[" << arraySize << "]";
String decl =
buildUniformDecl(String(getTestParameters().keyword), buildLayout(0),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), buildArray(arraySize));
setTemplateParam("UNIFORM_DECL", decl);
s.reset();
// build a function that accesses the whole array
s << "float accumulate(void)\n";
s << "{\n";
s << " float acc = 0.0;\n";
s << " for(int i=0; i < " << arraySize << " ; i++)\n";
s << " acc = float(atomicCounter(" << getDefaultUniformName() << "[i]));\n";
s << " return acc;\n";
s << "}\n";
setTemplateParam("OPTIONAL_FUNCTION_BLOCK", s.str());
s.reset();
s << "vec4(accumulate(), 1.0, 0.0, 1.0);\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
updateTemplate();
setTemplateParam("OPTIONAL_FUNCTION_BLOCK", String());
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
std::ostringstream texUnitStr;
texUnitStr << getDefaultUniformName() << "[0]";
list.push_back(texUnitStr.str());
StringIntMap bindingPoints = program->getBindingPoints(list);
passed &= (0 == bindingPoints[list[0]]);
if (!passed)
{
return LayoutBindingTestResult(
passed, generateLog(String("binding point did not match default"), bindingPoints[list[0]], 1));
}
}
return LayoutBindingTestResult(passed, String());
}
LayoutBindingTestResult binding_array_implicit(void)
{
return true;
}
LayoutBindingTestResult binding_array_multiple(void)
{
return true;
}
LayoutBindingTestResult binding_api_update(void)
{
return true;
}
//virtual LayoutBindingTestResult binding_compilation_errors (void);
//virtual LayoutBindingTestResult binding_link_errors (void);
LayoutBindingTestResult binding_examples_many_bindings(void)
{
int max_bindings = 0;
if (isStage(VertexShader))
{
gl().getIntegerv(GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS, &max_bindings);
}
else if (isStage(FragmentShader))
{
gl().getIntegerv(GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS, &max_bindings);
}
else
{
gl().getIntegerv(GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS, &max_bindings);
}
// example 1 in atomic counter CTS spec ac-binding-examples
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
s.reset();
s << "layout(binding=2, offset=4) uniform atomic_uint;\n";
s << "layout(binding=2) uniform atomic_uint " << getDefaultUniformName() << ";\n";
setTemplateParam("UNIFORM_DECL", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
list.push_back(getDefaultUniformName());
StringIntMap offsets = program->getOffsets(list);
passed &= (4 == offsets[list[0]]);
if (!passed)
{
return LayoutBindingTestResult(
passed, generateLog(String("offset did not match requested"), offsets[list[0]], 1));
}
}
// example 2 in atomic counter CTS spec ac-binding-examples
if (max_bindings >= 2)
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
s << "+" << buildAccess(getDefaultUniformName(1)) << ";\n";
s << "+" << buildAccess(getDefaultUniformName(2)) << ";\n";
s << "+" << buildAccess(getDefaultUniformName(3)) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
s.reset();
s << "layout(binding=3, offset=4) uniform atomic_uint " << getDefaultUniformName() << ";\n";
s << "layout(binding=2) uniform atomic_uint " << getDefaultUniformName(1) << ";\n";
s << "layout(binding=3) uniform atomic_uint " << getDefaultUniformName(2) << ";\n";
s << "layout(binding=2) uniform atomic_uint " << getDefaultUniformName(3) << ";\n";
setTemplateParam("UNIFORM_DECL", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
list.push_back(getDefaultUniformName());
list.push_back(getDefaultUniformName(1));
list.push_back(getDefaultUniformName(2));
list.push_back(getDefaultUniformName(3));
StringIntMap offsets = program->getOffsets(list);
IntVector expected;
expected.insert(expected.end(), 4);
expected.insert(expected.end(), 0);
expected.insert(expected.end(), 8);
expected.insert(expected.end(), 4);
for (unsigned int idx = 0; idx < list.size(); idx++)
{
passed &= (expected[idx] == offsets[list[idx]]);
if (!passed)
{
return LayoutBindingTestResult(
passed, generateLog(String("offset of") + String(list[idx]) + String("did not match requested"),
offsets[list[idx]], 4));
}
}
}
// example 3 in atomic counter CTS spec ac-binding-examples
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
s.reset();
s << "layout(binding=2, offset=4) uniform atomic_uint;\n";
s << "layout(offset=8) uniform atomic_uint " << getDefaultUniformName() << ";\n";
setTemplateParam("UNIFORM_DECL", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (passed)
{
return LayoutBindingTestResult(!passed, String("should not compile"));
}
}
// example 4 in atomic counter CTS spec ac-binding-examples
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
s.reset();
s << "layout(offset=4) uniform atomic_uint " << getDefaultUniformName() << ";\n";
setTemplateParam("UNIFORM_DECL", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (passed)
{
return LayoutBindingTestResult(!passed, String("should not compile"));
}
}
// example 5 in atomic counter CTS spec ac-binding-examples
// first check working example, then amend it with an error
if (max_bindings >= 2)
{
for (int pass = 0; pass < 2; pass++)
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
s << "+" << buildAccess(getDefaultUniformName(1)) << ";\n";
if (pass)
s << "+" << buildAccess(getDefaultUniformName(2)) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
s.reset();
s << "layout(binding=1, offset=0) uniform atomic_uint " << getDefaultUniformName() << ";\n";
s << "layout(binding=2, offset=0) uniform atomic_uint " << getDefaultUniformName(1) << ";\n";
if (pass)
s << "layout(binding=1, offset=0) uniform atomic_uint " << getDefaultUniformName(2) << ";\n";
setTemplateParam("UNIFORM_DECL", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (pass != 0)
{
if (passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
}
else
{
if (!passed)
{
return LayoutBindingTestResult(passed, String("should not compile"));
}
}
}
}
// example 6 in atomic counter CTS spec ac-binding-examples
// first check working example, then amend it with an error
if (max_bindings >= 2)
{
for (int pass = 0; pass < 2; pass++)
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
s << "+" << buildAccess(getDefaultUniformName(1)) << ";\n";
if (pass)
s << "+" << buildAccess(getDefaultUniformName(2)) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
s.reset();
s << "layout(binding=1, offset=0) uniform atomic_uint " << getDefaultUniformName() << ";\n";
s << "layout(binding=2, offset=0) uniform atomic_uint " << getDefaultUniformName(1) << ";\n";
if (pass)
s << "layout(binding=1, offset=2) uniform atomic_uint " << getDefaultUniformName(2) << ";\n";
setTemplateParam("UNIFORM_DECL", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (pass != 0)
{
if (passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
}
else
{
if (!passed)
{
return LayoutBindingTestResult(passed, String("should not compile"));
}
}
}
}
return true;
}
LayoutBindingTestResult binding_examples_one_binding(void)
{
// example 1 in atomic counter CTS spec ac-binding-examples
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
s.reset();
s << "layout(binding=0, offset=4) uniform atomic_uint;\n";
s << "layout(binding=0) uniform atomic_uint " << getDefaultUniformName() << ";\n";
setTemplateParam("UNIFORM_DECL", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
list.push_back(getDefaultUniformName());
StringIntMap offsets = program->getOffsets(list);
passed &= (4 == offsets[list[0]]);
if (!passed)
{
return LayoutBindingTestResult(
passed, generateLog(String("offset did not match requested"), offsets[list[0]], 1));
}
}
// example 2 in atomic counter CTS spec ac-binding-examples
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
s << "+" << buildAccess(getDefaultUniformName(1)) << ";\n";
s << "+" << buildAccess(getDefaultUniformName(2)) << ";\n";
s << "+" << buildAccess(getDefaultUniformName(3)) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
s.reset();
s << "layout(binding=0, offset=4) uniform atomic_uint " << getDefaultUniformName() << ";\n";
s << "layout(binding=0) uniform atomic_uint " << getDefaultUniformName(1) << ";\n";
s << "layout(binding=0) uniform atomic_uint " << getDefaultUniformName(2) << ";\n";
s << "layout(binding=0) uniform atomic_uint " << getDefaultUniformName(3) << ";\n";
setTemplateParam("UNIFORM_DECL", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
StringVector list;
list.push_back(getDefaultUniformName());
list.push_back(getDefaultUniformName(1));
list.push_back(getDefaultUniformName(2));
list.push_back(getDefaultUniformName(3));
StringIntMap offsets = program->getOffsets(list);
IntVector expected;
expected.insert(expected.end(), 4);
expected.insert(expected.end(), 8);
expected.insert(expected.end(), 12);
expected.insert(expected.end(), 16);
for (unsigned int idx = 0; idx < list.size(); idx++)
{
passed &= (expected[idx] == offsets[list[idx]]);
if (!passed)
{
return LayoutBindingTestResult(
passed, generateLog(String("offset of") + String(list[idx]) + String("did not match requested"),
offsets[list[idx]], expected[idx]));
}
}
}
// example 3 in atomic counter CTS spec ac-binding-examples
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
s.reset();
s << "layout(binding=0, offset=4) uniform atomic_uint;\n";
s << "layout(offset=8) uniform atomic_uint " << getDefaultUniformName() << ";\n";
setTemplateParam("UNIFORM_DECL", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (passed)
{
return LayoutBindingTestResult(!passed, String("should not compile"));
}
}
// example 4 in atomic counter CTS spec ac-binding-examples
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
s.reset();
s << "layout(offset=4) uniform atomic_uint " << getDefaultUniformName() << ";\n";
setTemplateParam("UNIFORM_DECL", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (passed)
{
return LayoutBindingTestResult(!passed, String("should not compile"));
}
}
// example 5 in atomic counter CTS spec ac-binding-examples
// first check working example, then amend it with an error
for (int pass = 0; pass < 2; pass++)
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
if (pass)
s << "+" << buildAccess(getDefaultUniformName(1)) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
s.reset();
s << "layout(binding=0, offset=0) uniform atomic_uint " << getDefaultUniformName() << ";\n";
if (pass)
s << "layout(binding=0, offset=0) uniform atomic_uint " << getDefaultUniformName(1) << ";\n";
setTemplateParam("UNIFORM_DECL", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (pass != 0)
{
if (passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
}
else
{
if (!passed)
{
return LayoutBindingTestResult(passed, String("should not compile"));
}
}
}
// example 6 in atomic counter CTS spec ac-binding-examples
// first check working example, then amend it with an error
for (int pass = 0; pass < 2; pass++)
{
bool passed = true;
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
if (pass)
s << "+" << buildAccess(getDefaultUniformName(1)) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
s.reset();
s << "layout(binding=0, offset=0) uniform atomic_uint " << getDefaultUniformName() << ";\n";
if (pass)
s << "layout(binding=0, offset=2) uniform atomic_uint " << getDefaultUniformName(1) << ";\n";
setTemplateParam("UNIFORM_DECL", s.str());
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (pass != 0)
{
if (passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
}
else
{
if (!passed)
{
return LayoutBindingTestResult(passed, String("should not compile"));
}
}
}
return true;
}
/*virtual*/
LayoutBindingTestResult binding_examples(void)
{
if (maxBindings() >= 4)
{
return binding_examples_many_bindings();
}
else
{
return binding_examples_one_binding();
}
}
/*virtual*/
LayoutBindingTestResult binding_mixed_order(void)
{
bool passed = true;
{
StringStream s;
s << buildAccess(getDefaultUniformName()) << ";\n";
setTemplateParam("UNIFORM_ACCESS", s.str());
String decl =
buildUniformDecl(String(getTestParameters().keyword), String("layout(binding=0, offset=0)"),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", decl);
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
}
{
String decl =
buildUniformDecl(String(getTestParameters().keyword), String("layout(offset=0, binding=0)"),
String(getTestParameters().uniform_type), buildBlockName(getDefaultUniformName()),
buildBlock(getDefaultUniformName()), getDefaultUniformName(), String());
setTemplateParam("UNIFORM_DECL", decl);
updateTemplate();
LayoutBindingProgram::LayoutBindingProgramAutoPtr program(*this);
passed &= program->compiledAndLinked();
if (!passed)
{
return LayoutBindingTestResult(passed, program->getErrorLog());
}
}
return true;
}
};
AtomicCounterLayoutBindingCase::AtomicCounterLayoutBindingCase(Context& context, const char* name,
const char* description, StageType stage,
LayoutBindingParameters& samplerType,
glu::GLSLVersion glslVersion)
: LayoutBindingBaseCase(context, name, description, stage, samplerType, glslVersion)
{
}
//=========================================================================
// test case Uniform blocks binding
//=========================================================================
class UniformBlocksLayoutBindingCase : public LayoutBindingBaseCase
{
public:
UniformBlocksLayoutBindingCase(Context& context, const char* name, const char* description, StageType stage,
LayoutBindingParameters& samplerType, glu::GLSLVersion glslVersion);
~UniformBlocksLayoutBindingCase(void);
private:
class UniformBlocksLayoutBindingProgram : public LayoutBindingProgram
{
public:
UniformBlocksLayoutBindingProgram(IProgramContextSupplier& contextSupplier)
: LayoutBindingProgram(contextSupplier)
{
}
~UniformBlocksLayoutBindingProgram()
{
}
private:
/*virtual*/
StringIntMap getBindingPoints(StringVector args) const
{
StringIntMap bindingPoints;
for (StringVector::iterator it = args.begin(); it != args.end(); it++)
{
glw::GLuint idx = gl().getProgramResourceIndex(getProgram(), GL_UNIFORM_BLOCK, (*it).c_str());
if (idx != glw::GLuint(-1))
{
glw::GLenum param = GL_BUFFER_BINDING;
glw::GLint value = -1;
gl().getProgramResourceiv(getProgram(), GL_UNIFORM_BLOCK, idx, 1, &param, 1, NULL, &value);
bool hasNoError = (GL_NO_ERROR == gl().getError());
if (hasNoError)
{
bindingPoints[*it] = value;
}
}
}
return bindingPoints;
}
/*virtual*/
bool setBindingPoints(StringVector list, glw::GLint bindingPoint) const
{
bool bNoError = true;
for (StringVector::iterator it = list.begin(); it != list.end(); it++)
{
glw::GLuint blockIndex = gl().getUniformBlockIndex(getProgram(), (*it).c_str());
if (blockIndex == GL_INVALID_INDEX)
{
return false;
}
gl().uniformBlockBinding(getProgram(), blockIndex, bindingPoint);
}
return bNoError;
}
};
private:
// IProgramContextSupplier
/*virtual*/
LayoutBindingProgram* createProgram()
{
return new UniformBlocksLayoutBindingProgram(*this);
}
private:
/*virtual*/
String buildBlockName(const String& name)
{
return name;
}
/*virtual*/
String buildBlock(const String& name, const String& type)
{
std::ostringstream s;
s << "{";
s << type << " " << name << "_a; ";
s << type << " " << name << "_b; ";
s << "}";
return s.str();
}
/*virtual*/
String buildAccess(const String& var)
{
std::ostringstream s;
s << "vec4(0.0, " << var << "_a, 0.0, 1.0) + vec4(0.0, " << var << "_b, 0.0, 1.0)";
return s.str();
}
/*virtual*/
String buildLayout(const String& binding)
{
std::ostringstream s;
if (!binding.empty())
s << "layout(binding=" << binding << ", std140) ";
else
s << "layout(std140) ";
return s.str();
}
/*virtual*/
int maxBindings()
{
int units = 0;
gl().getIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &units);
return units;
}
// return max. array size allowed
/*virtual*/
int maxArraySize()
{
int units = 0;
switch (getStage())
{
case FragmentShader:
gl().getIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, &units);
break;
case VertexShader:
gl().getIntegerv(GL_MAX_VERTEX_UNIFORM_BLOCKS, &units);
break;
case ComputeShader:
gl().getIntegerv(GL_MAX_COMPUTE_UNIFORM_BLOCKS, &units);
break;
default:
DE_ASSERT(0);
break;
}
return units;
}
/*virtual*/
void bind(int binding)
{
gl().bindBufferBase(GL_UNIFORM_BUFFER, binding, m_buffername);
}
/*virtual*/
void unbind(int binding)
{
gl().bindBufferBase(GL_UNIFORM_BUFFER, binding, 0);
}
/*virtual*/
void setupTest(void)
{
const float f[2] = { 0.25f, 0.75f };
gl().genBuffers(1, &m_buffername);
gl().bindBuffer(GL_UNIFORM_BUFFER, m_buffername);
gl().bufferData(GL_UNIFORM_BUFFER, sizeof(f), f, GL_STATIC_DRAW);
}
/*virtual*/
void teardownTest(void)
{
gl().bindBuffer(GL_UNIFORM_BUFFER, 0);
gl().deleteBuffers(1, &m_buffername);
}
private:
glw::GLuint m_buffername;
};
UniformBlocksLayoutBindingCase::UniformBlocksLayoutBindingCase(Context& context, const char* name,
const char* description, StageType stage,
LayoutBindingParameters& samplerType,
glu::GLSLVersion glslVersion)
: LayoutBindingBaseCase(context, name, description, stage, samplerType, glslVersion), m_buffername(0)
{
}
UniformBlocksLayoutBindingCase::~UniformBlocksLayoutBindingCase()
{
}
//*****************************************************************************
//=========================================================================
// test case Shader storage buffer binding
//=========================================================================
class ShaderStorageBufferLayoutBindingCase : public LayoutBindingBaseCase
{
public:
ShaderStorageBufferLayoutBindingCase(Context& context, const char* name, const char* description, StageType stage,
LayoutBindingParameters& samplerType, glu::GLSLVersion glslVersion);
~ShaderStorageBufferLayoutBindingCase(void);
private:
class ShaderStorageBufferLayoutBindingProgram : public LayoutBindingProgram
{
public:
ShaderStorageBufferLayoutBindingProgram(IProgramContextSupplier& contextSupplier)
: LayoutBindingProgram(contextSupplier)
{
}
~ShaderStorageBufferLayoutBindingProgram()
{
}
//*******************************************************************************
// overwritten virtual methods
private:
/*virtual*/
StringIntMap getBindingPoints(StringVector args) const
{
StringIntMap bindingPoints;
for (StringVector::iterator it = args.begin(); it != args.end(); it++)
{
glw::GLuint idx = gl().getProgramResourceIndex(getProgram(), GL_SHADER_STORAGE_BLOCK, (*it).c_str());
if (idx != GL_INVALID_INDEX)
{
glw::GLenum param = GL_BUFFER_BINDING;
glw::GLint value = -1;
gl().getProgramResourceiv(getProgram(), GL_SHADER_STORAGE_BLOCK, idx, 1, &param, 1, NULL, &value);
bool hasNoError = (GL_NO_ERROR == gl().getError());
if (hasNoError)
{
bindingPoints[*it] = value;
}
}
}
return bindingPoints;
}
/*virtual*/
bool setBindingPoints(StringVector list, glw::GLint bindingPoint) const
{
for (StringVector::iterator it = list.begin(); it != list.end(); it++)
{
glw::GLuint blockIndex =
gl().getProgramResourceIndex(getProgram(), GL_SHADER_STORAGE_BLOCK, (*it).c_str());
if (blockIndex == GL_INVALID_INDEX)
{
return false;
}
gl().shaderStorageBlockBinding(getProgram(), blockIndex, bindingPoint);
}
return true;
}
};
private:
// IProgramContextSupplier
/*virtual*/
LayoutBindingProgram* createProgram()
{
return new ShaderStorageBufferLayoutBindingProgram(*this);
}
private:
/*virtual*/
String buildLayout(const String& binding)
{
std::ostringstream s;
if (!binding.empty())
s << "layout(binding=" << binding << ", std430) ";
else
s << "layout(std430) ";
return s.str();
}
/*virtual*/
String getDefaultUniformName(int idx = 0)
{
StringStream s;
s << "buffer" << idx;
return s.str();
}
/*virtual*/
String buildBlockName(const String& name)
{
return name;
}
/*virtual*/
String buildBlock(const String& name, const String& type)
{
std::ostringstream s;
s << "{";
s << type << " " << name << "_a[2];\n";
s << "}";
return s.str();
}
/*virtual*/
String buildAccess(const String& var)
{
std::ostringstream s;
s << "vec4(0.0, " << var << "_a[0] + " << var << "_a[1], 0.0, 1.0)";
return s.str();
}
int maxBindings()
{
int units = 0;
gl().getIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &units);
return units;
}
// return max. array size allowed
int maxArraySize()
{
int units = 0;
switch (getStage())
{
case FragmentShader:
gl().getIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &units);
break;
case VertexShader:
gl().getIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &units);
break;
case ComputeShader:
gl().getIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &units);
break;
default:
DE_ASSERT(0);
break;
}
return units;
}
/*virtual*/
void bind(int binding)
{
gl().bindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, m_buffername);
}
/*virtual*/
void unbind(int binding)
{
gl().bindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, 0);
}
/*virtual*/
void setupTest(void)
{
const float f[2] = { 0.25f, 0.75f };
gl().genBuffers(1, &m_buffername);
gl().bindBuffer(GL_SHADER_STORAGE_BUFFER, m_buffername);
gl().bufferData(GL_SHADER_STORAGE_BUFFER, sizeof(f), f, GL_STATIC_DRAW);
}
/*virtual*/
void teardownTest(void)
{
gl().bindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
gl().deleteBuffers(1, &m_buffername);
}
//=========================================================================
// sub-tests overrides
//=========================================================================
private:
private:
//virtual LayoutBindingTestResult binding_basic_default (void);
//virtual LayoutBindingTestResult binding_basic_explicit (void);
//virtual LayoutBindingTestResult binding_basic_multiple (void);
//virtual LayoutBindingTestResult binding_basic_render (void);
//virtual LayoutBindingTestResult binding_integer_constant (void);
//virtual LayoutBindingTestResult binding_integer_constant_expression (void);
//virtual LayoutBindingTestResult binding_array_size (void);
//virtual LayoutBindingTestResult binding_array_implicit (void);
//virtual LayoutBindingTestResult binding_array_multiple (void);
/*virtual*/
LayoutBindingTestResult binding_api_update(void)
{
// only for GL
if (getGLSLVersion() == glu::GLSL_VERSION_310_ES)
return LayoutBindingTestResult(true, String(), true);
return LayoutBindingBaseCase::binding_api_update();
}
//virtual LayoutBindingTestResult binding_compilation_errors (void);
//virtual LayoutBindingTestResult binding_link_errors (void);
//virtual LayoutBindingTestResult binding_examples (void);
private:
glw::GLuint m_buffername;
};
ShaderStorageBufferLayoutBindingCase::ShaderStorageBufferLayoutBindingCase(Context& context, const char* name,
const char* description, StageType stage,
LayoutBindingParameters& samplerType,
glu::GLSLVersion glslVersion)
: LayoutBindingBaseCase(context, name, description, stage, samplerType, glslVersion), m_buffername(0)
{
}
ShaderStorageBufferLayoutBindingCase::~ShaderStorageBufferLayoutBindingCase()
{
}
//*****************************************************************************
LayoutBindingTests::LayoutBindingTests(Context& context, glu::GLSLVersion glslVersion)
: TestCaseGroup(context, "layout_binding", "Layout Binding LayoutBindingSubTest"), m_glslVersion(glslVersion)
{
}
LayoutBindingTests::~LayoutBindingTests(void)
{
}
StageType LayoutBindingTests::stageTypes[] = {
{ "ComputeShader", ComputeShader },
{ "FragmentShader", FragmentShader },
{ "VertexShader", VertexShader },
};
// uniform_type must match vector_type, i.e. isampler2D=>ivec4
LayoutBindingParameters LayoutBindingTests::test_args[] = {
{ "uniform", Texture, TwoD, "vec4", "sampler2D", "vec2", "texture" },
{ "uniform", Texture, ThreeD, "vec4", "sampler3D", "vec3", "texture" },
{ "uniform", Texture, TwoDArray, "vec4", "sampler2DArray", "vec3", "texture" },
{ "uniform", Image, TwoD, "vec4", "image2D", "ivec2", "imageLoad" },
{ "uniform", AtomicCounter, TwoD, "vec4", "atomic_uint", "vec3", "atomic" },
{ "uniform", UniformBlock, None, "vec4", "block", "vec3", "block" },
{ "buffer", ShaderStorageBuffer, None, "vec4", "buffer", "vec3", "atomicAdd" },
};
// create test name which must be unique or dEQP framework will throw
// example: sampler2D_layout_binding_vec4_texture_0
String LayoutBindingTests::createTestName(const StageType& stageType, const LayoutBindingParameters& testArgs)
{
StringStream s;
s << testArgs.uniform_type;
s << "_layout_binding_";
s << testArgs.access_function << "_";
s << stageType.name;
return s.str();
}
void LayoutBindingTests::init(void)
{
std::vector<StageType> stages = makeVector(stageTypes);
std::vector<LayoutBindingParameters> samplers = makeVector(test_args);
for (std::vector<StageType>::iterator stagesIter = stages.begin(); stagesIter != stages.end(); stagesIter++)
{
for (std::vector<LayoutBindingParameters>::iterator testArgsIter = samplers.begin();
testArgsIter != samplers.end(); testArgsIter++)
{
String testName = createTestName(*stagesIter, *testArgsIter);
switch ((*testArgsIter).surface_type)
{
case Texture:
addChild(new SamplerLayoutBindingCase(m_context, testName.c_str(),
"test sampler layout binding functionality", *stagesIter,
*testArgsIter, m_glslVersion));
break;
case Image:
addChild(new ImageLayoutBindingCase(m_context, testName.c_str(),
"test image layout binding functionality", *stagesIter,
*testArgsIter, m_glslVersion));
break;
case AtomicCounter:
addChild(new AtomicCounterLayoutBindingCase(m_context, testName.c_str(),
"test atomic counters layout binding functionality",
*stagesIter, *testArgsIter, m_glslVersion));
break;
case UniformBlock:
addChild(new UniformBlocksLayoutBindingCase(m_context, testName.c_str(),
"test uniform block layout binding functionality",
*stagesIter, *testArgsIter, m_glslVersion));
break;
case ShaderStorageBuffer:
addChild(new ShaderStorageBufferLayoutBindingCase(
m_context, testName.c_str(), "test shader storage buffer layout binding functionality", *stagesIter,
*testArgsIter, m_glslVersion));
break;
default:
DE_ASSERT(0);
break;
}
}
}
}
} // glcts