/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.1 Module * ------------------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Basic Layout Binding Tests. *//*--------------------------------------------------------------------*/ #include "es31fLayoutBindingTests.hpp" #include "gluShaderProgram.hpp" #include "gluPixelTransfer.hpp" #include "gluTextureUtil.hpp" #include "gluContextInfo.hpp" #include "glwFunctions.hpp" #include "glwEnums.hpp" #include "tcuSurface.hpp" #include "tcuTestLog.hpp" #include "tcuTexture.hpp" #include "tcuTextureUtil.hpp" #include "tcuImageCompare.hpp" #include "tcuStringTemplate.hpp" #include "tcuRenderTarget.hpp" #include "deString.h" #include "deStringUtil.hpp" #include "deRandom.hpp" using tcu::TestLog; using tcu::Vec2; using tcu::Vec3; using tcu::Vec4; namespace deqp { namespace gles31 { namespace Functional { namespace { enum TestType { TESTTYPE_BINDING_SINGLE = 0, TESTTYPE_BINDING_MAX, TESTTYPE_BINDING_MULTIPLE, TESTTYPE_BINDING_ARRAY, TESTTYPE_BINDING_MAX_ARRAY, TESTTYPE_BINDING_LAST, }; enum ShaderType { SHADERTYPE_VERTEX = 0, SHADERTYPE_FRAGMENT, SHADERTYPE_TESS_CONTROL, SHADERTYPE_TESS_EVALUATION, SHADERTYPE_ALL, SHADERTYPE_LAST, }; enum { MAX_UNIFORM_MULTIPLE_INSTANCES = 7, MAX_UNIFORM_ARRAY_SIZE = 7, }; std::string generateVertexShader (ShaderType shaderType, const std::string& shaderUniformDeclarations, const std::string& shaderBody) { static const char* const s_simpleVertexShaderSource = "#version 310 es\n" "in highp vec4 a_position;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" "}\n"; switch (shaderType) { case SHADERTYPE_VERTEX: case SHADERTYPE_ALL: { std::ostringstream vertexShaderSource; vertexShaderSource << "#version 310 es\n" << "in highp vec4 a_position;\n" << "out highp vec4 v_color;\n" << "uniform highp int u_arrayNdx;\n\n" << shaderUniformDeclarations << "\n" << "void main (void)\n" << "{\n" << " highp vec4 color;\n\n" << shaderBody << "\n" << " v_color = color;\n" << " gl_Position = a_position;\n" << "}\n"; return vertexShaderSource.str(); } case SHADERTYPE_FRAGMENT: case SHADERTYPE_TESS_CONTROL: case SHADERTYPE_TESS_EVALUATION: return s_simpleVertexShaderSource; default: DE_ASSERT(false); return ""; } } std::string generateFragmentShader (ShaderType shaderType, const std::string& shaderUniformDeclarations, const std::string& shaderBody) { static const char* const s_simpleFragmentShaderSource = "#version 310 es\n" "in highp vec4 v_color;\n" "layout(location = 0) out highp vec4 fragColor;\n" "void main (void)\n" "{\n" " fragColor = v_color;\n" "}\n"; switch (shaderType) { case SHADERTYPE_VERTEX: case SHADERTYPE_TESS_CONTROL: case SHADERTYPE_TESS_EVALUATION: return s_simpleFragmentShaderSource; case SHADERTYPE_FRAGMENT: { std::ostringstream fragmentShaderSource; fragmentShaderSource << "#version 310 es\n" << "layout(location = 0) out highp vec4 fragColor;\n" << "uniform highp int u_arrayNdx;\n\n" << shaderUniformDeclarations << "\n" << "void main (void)\n" << "{\n" << " highp vec4 color;\n\n" << shaderBody << "\n" << " fragColor = color;\n" << "}\n"; return fragmentShaderSource.str(); } case SHADERTYPE_ALL: { std::ostringstream fragmentShaderSource; fragmentShaderSource << "#version 310 es\n" << "in highp vec4 v_color;\n" << "layout(location = 0) out highp vec4 fragColor;\n" << "uniform highp int u_arrayNdx;\n\n" << shaderUniformDeclarations << "\n" << "void main (void)\n" << "{\n" << " if (v_color.x > 2.0) discard;\n" << " highp vec4 color;\n\n" << shaderBody << "\n" << " fragColor = color;\n" << "}\n"; return fragmentShaderSource.str(); } default: DE_ASSERT(false); return ""; } } std::string generateTessControlShader (ShaderType shaderType, const std::string& shaderUniformDeclarations, const std::string& shaderBody) { static const char* const s_simpleTessContorlShaderSource = "#version 310 es\n" "#extension GL_EXT_tessellation_shader : require\n" "layout (vertices=3) out;\n" "\n" "void main (void)\n" "{\n" " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" "}\n"; switch (shaderType) { case SHADERTYPE_VERTEX: case SHADERTYPE_FRAGMENT: case SHADERTYPE_TESS_EVALUATION: return s_simpleTessContorlShaderSource; case SHADERTYPE_TESS_CONTROL: case SHADERTYPE_ALL: { std::ostringstream tessControlShaderSource; tessControlShaderSource << "#version 310 es\n" << "#extension GL_EXT_tessellation_shader : require\n" << "layout (vertices=3) out;\n" << "\n" << "uniform highp int u_arrayNdx;\n\n" << shaderUniformDeclarations << "\n" << "void main (void)\n" << "{\n" << " highp vec4 color;\n\n" << shaderBody << "\n" << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" << "}\n"; return tessControlShaderSource.str(); } default: DE_ASSERT(false); return ""; } } std::string generateTessEvaluationShader (ShaderType shaderType, const std::string& shaderUniformDeclarations, const std::string& shaderBody) { static const char* const s_simpleTessEvaluationShaderSource = "#version 310 es\n" "#extension GL_EXT_tessellation_shader : require\n" "layout (triangles) in;\n" "\n" "void main (void)\n" "{\n" " gl_Position = gl_TessCoord[0] * gl_in[0].gl_Position + gl_TessCoord[1] * gl_in[1].gl_Position + gl_TessCoord[2] * gl_in[2].gl_Position;\n" "}\n"; switch (shaderType) { case SHADERTYPE_VERTEX: case SHADERTYPE_FRAGMENT: case SHADERTYPE_TESS_CONTROL: return s_simpleTessEvaluationShaderSource; case SHADERTYPE_TESS_EVALUATION: case SHADERTYPE_ALL: { std::ostringstream tessEvaluationShaderSource; tessEvaluationShaderSource << "#version 310 es\n" << "#extension GL_EXT_tessellation_shader : require\n" << "layout (triangles) in;\n" << "\n" << "uniform highp int u_arrayNdx;\n\n" << shaderUniformDeclarations << "\n" << "out mediump vec4 v_color;\n" << "void main (void)\n" << "{\n" << " highp vec4 color;\n\n" << shaderBody << "\n" << " v_color = color;\n" << " gl_Position = gl_TessCoord[0] * gl_in[0].gl_Position + gl_TessCoord[1] * gl_in[1].gl_Position + gl_TessCoord[2] * gl_in[2].gl_Position;\n" << "}\n"; return tessEvaluationShaderSource.str(); } default: DE_ASSERT(false); return ""; } } std::string getUniformName (const std::string& name, int declNdx) { return name + de::toString(declNdx); } std::string getUniformName (const std::string& name, int declNdx, int arrNdx) { return name + de::toString(declNdx) + "[" + de::toString(arrNdx) + "]"; } Vec4 getRandomColor (de::Random& rnd) { const float r = rnd.getFloat(0.2f, 0.9f); const float g = rnd.getFloat(0.2f, 0.9f); const float b = rnd.getFloat(0.2f, 0.9f); return Vec4(r, g, b, 1.0f); } class LayoutBindingRenderCase : public TestCase { public: enum { MAX_TEST_RENDER_WIDTH = 256, MAX_TEST_RENDER_HEIGHT = 256, TEST_TEXTURE_SIZE = 1, }; LayoutBindingRenderCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, glw::GLenum maxBindingPointEnum, glw::GLenum maxVertexUnitsEnum, glw::GLenum maxFragmentUnitsEnum, glw::GLenum maxCombinedUnitsEnum, const std::string& uniformName); virtual ~LayoutBindingRenderCase (void); virtual void init (void); virtual void deinit (void); int getRenderWidth (void) const { return de::min((int)MAX_TEST_RENDER_WIDTH, m_context.getRenderTarget().getWidth()); } int getRenderHeight (void) const { return de::min((int)MAX_TEST_RENDER_HEIGHT, m_context.getRenderTarget().getHeight()); } protected: virtual glu::ShaderProgram* generateShaders (void) const = 0; void initRenderState (void); bool drawAndVerifyResult (const Vec4& expectedColor); void setTestResult (bool queryTestPassed, bool imageTestPassed); const glu::ShaderProgram* m_program; const ShaderType m_shaderType; const TestType m_testType; const std::string m_uniformName; const glw::GLenum m_maxBindingPointEnum; const glw::GLenum m_maxVertexUnitsEnum; const glw::GLenum m_maxFragmentUnitsEnum; const glw::GLenum m_maxCombinedUnitsEnum; glw::GLuint m_vertexBuffer; glw::GLuint m_indexBuffer; glw::GLint m_shaderProgramLoc; glw::GLint m_shaderProgramPosLoc; glw::GLint m_shaderProgramArrayNdxLoc; glw::GLint m_numBindings; std::vector<glw::GLint> m_bindings; private: void initBindingPoints (int minBindingPoint, int numBindingPoints); }; LayoutBindingRenderCase::LayoutBindingRenderCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, glw::GLenum maxBindingPointEnum, glw::GLenum maxVertexUnitsEnum, glw::GLenum maxFragmentUnitsEnum, glw::GLenum maxCombinedUnitsEnum, const std::string& uniformName) : TestCase (context, name, desc) , m_program (DE_NULL) , m_shaderType (shaderType) , m_testType (testType) , m_uniformName (uniformName) , m_maxBindingPointEnum (maxBindingPointEnum) , m_maxVertexUnitsEnum (maxVertexUnitsEnum) , m_maxFragmentUnitsEnum (maxFragmentUnitsEnum) , m_maxCombinedUnitsEnum (maxCombinedUnitsEnum) , m_vertexBuffer (0) , m_indexBuffer (0) , m_shaderProgramLoc (0) , m_shaderProgramPosLoc (0) , m_shaderProgramArrayNdxLoc (0) , m_numBindings (0) { } LayoutBindingRenderCase::~LayoutBindingRenderCase (void) { deinit(); } void LayoutBindingRenderCase::init (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); { de::Random rnd (deStringHash(getName()) ^ 0xff23a4); glw::GLint numBindingPoints = 0; // Number of available binding points glw::GLint maxVertexUnits = 0; // Available uniforms in the vertex shader glw::GLint maxFragmentUnits = 0; // Available uniforms in the fragment shader glw::GLint maxCombinedUnits = 0; // Available uniforms in all the shader stages combined glw::GLint maxUnits = 0; // Maximum available uniforms for this test gl.getIntegerv(m_maxVertexUnitsEnum, &maxVertexUnits); gl.getIntegerv(m_maxFragmentUnitsEnum, &maxFragmentUnits); gl.getIntegerv(m_maxCombinedUnitsEnum, &maxCombinedUnits); gl.getIntegerv(m_maxBindingPointEnum, &numBindingPoints); GLU_EXPECT_NO_ERROR(gl.getError(), "Querying available uniform numbers failed"); m_testCtx.getLog() << tcu::TestLog::Message << "Maximum units for uniform type in the vertex shader: " << maxVertexUnits << tcu::TestLog::EndMessage; m_testCtx.getLog() << tcu::TestLog::Message << "Maximum units for uniform type in the fragment shader: " << maxFragmentUnits << tcu::TestLog::EndMessage; m_testCtx.getLog() << tcu::TestLog::Message << "Maximum combined units for uniform type: " << maxCombinedUnits << tcu::TestLog::EndMessage; m_testCtx.getLog() << tcu::TestLog::Message << "Maximum binding point for uniform type: " << numBindingPoints-1 << tcu::TestLog::EndMessage; // Select maximum number of uniforms used for the test switch (m_shaderType) { case SHADERTYPE_VERTEX: maxUnits = maxVertexUnits; break; case SHADERTYPE_FRAGMENT: maxUnits = maxFragmentUnits; break; case SHADERTYPE_ALL: maxUnits = maxCombinedUnits/2; break; default: DE_ASSERT(false); } // Select the number of uniforms (= bindings) used for this test switch (m_testType) { case TESTTYPE_BINDING_SINGLE: case TESTTYPE_BINDING_MAX: m_numBindings = 1; break; case TESTTYPE_BINDING_MULTIPLE: if (maxUnits < 2) throw tcu::NotSupportedError("Not enough uniforms available for test"); m_numBindings = rnd.getInt(2, deMin32(MAX_UNIFORM_MULTIPLE_INSTANCES, maxUnits)); break; case TESTTYPE_BINDING_ARRAY: case TESTTYPE_BINDING_MAX_ARRAY: if (maxUnits < 2) throw tcu::NotSupportedError("Not enough uniforms available for test"); m_numBindings = rnd.getInt(2, deMin32(MAX_UNIFORM_ARRAY_SIZE, maxUnits)); break; default: DE_ASSERT(false); } // Check that we have enough uniforms in different shaders to perform the tests if ( ((m_shaderType == SHADERTYPE_VERTEX) || (m_shaderType == SHADERTYPE_ALL)) && (maxVertexUnits < m_numBindings) ) throw tcu::NotSupportedError("Vertex shader: not enough uniforms available for test"); if ( ((m_shaderType == SHADERTYPE_FRAGMENT) || (m_shaderType == SHADERTYPE_ALL)) && (maxFragmentUnits < m_numBindings) ) throw tcu::NotSupportedError("Fragment shader: not enough uniforms available for test"); if ( (m_shaderType == SHADERTYPE_ALL) && (maxCombinedUnits < m_numBindings*2) ) throw tcu::NotSupportedError("Not enough uniforms available for test"); // Check that we have enough binding points to perform the tests if (numBindingPoints < m_numBindings) throw tcu::NotSupportedError("Not enough binding points available for test"); // Initialize the binding points i.e. populate the two binding point vectors initBindingPoints(0, numBindingPoints); } // Generate the shader program - note: this must be done after deciding the binding points DE_ASSERT(!m_program); m_testCtx.getLog() << tcu::TestLog::Message << "Creating test shaders" << tcu::TestLog::EndMessage; m_program = generateShaders(); m_testCtx.getLog() << *m_program; if (!m_program->isOk()) throw tcu::TestError("Shader compile failed"); // Setup vertex and index buffers { // Get attribute and uniform locations const deUint32 program = m_program->getProgram(); m_shaderProgramPosLoc = gl.getAttribLocation(program, "a_position"); m_shaderProgramArrayNdxLoc = gl.getUniformLocation(program, "u_arrayNdx"); m_vertexBuffer = 0; m_indexBuffer = 0; // Setup buffers so that we render one quad covering the whole viewport const Vec3 vertices[] = { Vec3(-1.0f, -1.0f, +1.0f), Vec3(+1.0f, -1.0f, +1.0f), Vec3(+1.0f, +1.0f, +1.0f), Vec3(-1.0f, +1.0f, +1.0f), }; const deUint16 indices[] = { 0, 1, 2, 0, 2, 3, }; TCU_CHECK((m_shaderProgramPosLoc >= 0) && (m_shaderProgramArrayNdxLoc >= 0)); // Generate and bind index buffer gl.genBuffers(1, &m_indexBuffer); gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer); gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, (DE_LENGTH_OF_ARRAY(indices)*(glw::GLsizeiptr)sizeof(indices[0])), &indices[0], GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "Index buffer setup failed"); // Generate and bind vertex buffer gl.genBuffers(1, &m_vertexBuffer); gl.bindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); gl.bufferData(GL_ARRAY_BUFFER, (DE_LENGTH_OF_ARRAY(vertices)*(glw::GLsizeiptr)sizeof(vertices[0])), &vertices[0], GL_STATIC_DRAW); gl.enableVertexAttribArray(m_shaderProgramPosLoc); gl.vertexAttribPointer(m_shaderProgramPosLoc, 3, GL_FLOAT, GL_FALSE, 0, DE_NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "Vertex buffer setup failed"); } } void LayoutBindingRenderCase::deinit (void) { if (m_program) { delete m_program; m_program = DE_NULL; } if (m_shaderProgramPosLoc) m_context.getRenderContext().getFunctions().disableVertexAttribArray(m_shaderProgramPosLoc); if (m_vertexBuffer) { m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_vertexBuffer); m_context.getRenderContext().getFunctions().bindBuffer(GL_ARRAY_BUFFER, 0); } if (m_indexBuffer) { m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_indexBuffer); m_context.getRenderContext().getFunctions().bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } } void LayoutBindingRenderCase::initBindingPoints (int minBindingPoint, int numBindingPoints) { de::Random rnd(deStringHash(getName()) ^ 0xff23a4); switch (m_testType) { case TESTTYPE_BINDING_SINGLE: { const int bpoint = rnd.getInt(minBindingPoint, numBindingPoints-1); m_bindings.push_back(bpoint); break; } case TESTTYPE_BINDING_MAX: m_bindings.push_back(numBindingPoints-1); break; case TESTTYPE_BINDING_MULTIPLE: { // Choose multiple unique binding points from the low and high end of available binding points std::vector<deUint32> lowBindingPoints; std::vector<deUint32> highBindingPoints; for (int bpoint = 0; bpoint < numBindingPoints/2; ++bpoint) lowBindingPoints.push_back(bpoint); for (int bpoint = numBindingPoints/2; bpoint < numBindingPoints; ++bpoint) highBindingPoints.push_back(bpoint); rnd.shuffle(lowBindingPoints.begin(), lowBindingPoints.end()); rnd.shuffle(highBindingPoints.begin(), highBindingPoints.end()); for (int ndx = 0; ndx < m_numBindings; ++ndx) { if (ndx%2 == 0) { const int bpoint = lowBindingPoints.back(); lowBindingPoints.pop_back(); m_bindings.push_back(bpoint); } else { const int bpoint = highBindingPoints.back(); highBindingPoints.pop_back(); m_bindings.push_back(bpoint); } } break; } case TESTTYPE_BINDING_ARRAY: { const glw::GLint binding = rnd.getInt(minBindingPoint, numBindingPoints-m_numBindings); for (int ndx = 0; ndx < m_numBindings; ++ndx) m_bindings.push_back(binding+ndx); break; } case TESTTYPE_BINDING_MAX_ARRAY: { const glw::GLint binding = numBindingPoints-m_numBindings; for (int ndx = 0; ndx < m_numBindings; ++ndx) m_bindings.push_back(binding+ndx); break; } default: DE_ASSERT(false); } } void LayoutBindingRenderCase::initRenderState (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(m_program->getProgram()); gl.viewport(0, 0, getRenderWidth(), getRenderHeight()); gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to set render state"); } bool LayoutBindingRenderCase::drawAndVerifyResult (const Vec4& expectedColor) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::Surface reference (getRenderWidth(), getRenderHeight()); // the point of these test is to check layout_binding. For this purpose, we can use quite // large thresholds. const tcu::RGBA surfaceThreshold = m_context.getRenderContext().getRenderTarget().getPixelFormat().getColorThreshold(); const tcu::RGBA compareThreshold = tcu::RGBA(de::clamp(2 * surfaceThreshold.getRed(), 0, 255), de::clamp(2 * surfaceThreshold.getGreen(), 0, 255), de::clamp(2 * surfaceThreshold.getBlue(), 0, 255), de::clamp(2 * surfaceThreshold.getAlpha(), 0, 255)); gl.clear(GL_COLOR_BUFFER_BIT); // Draw gl.drawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, DE_NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "Drawing failed"); // Verify tcu::Surface result(getRenderWidth(), getRenderHeight()); m_testCtx.getLog() << TestLog::Message << "Reading pixels" << TestLog::EndMessage; glu::readPixels(m_context.getRenderContext(), 0, 0, result.getAccess()); GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels failed"); tcu::clear(reference.getAccess(), expectedColor); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying output image, fragment output color is " << expectedColor << tcu::TestLog::EndMessage; return tcu::pixelThresholdCompare(m_testCtx.getLog(), "Render result", "Result verification", reference, result, compareThreshold, tcu::COMPARE_LOG_RESULT); } void LayoutBindingRenderCase::setTestResult (bool queryTestPassed, bool imageTestPassed) { if (queryTestPassed && imageTestPassed) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else if (!queryTestPassed && !imageTestPassed) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "One or more binding point queries and image comparisons failed"); else if (!queryTestPassed) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "One or more binding point queries failed"); else m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "One or more image comparisons failed"); } class LayoutBindingNegativeCase : public TestCase { public: enum ErrorType { ERRORTYPE_OVER_MAX_UNITS = 0, ERRORTYPE_LESS_THAN_ZERO, ERRORTYPE_CONTRADICTORY, ERRORTYPE_LAST, }; LayoutBindingNegativeCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, ErrorType errorType, glw::GLenum maxBindingPointEnum, glw::GLenum maxVertexUnitsEnum, glw::GLenum maxFragmentUnitsEnum, glw::GLenum maxTessCtrlUnitsEnum, glw::GLenum maxTessEvalUnitsEnum, glw::GLenum maxCombinedUnitsEnum, const std::string& uniformName); virtual ~LayoutBindingNegativeCase (void); virtual void init (void); virtual void deinit (void); virtual IterateResult iterate (void); protected: virtual glu::ShaderProgram* generateShaders (void) const = 0; const glu::ShaderProgram* m_program; const ShaderType m_shaderType; const TestType m_testType; const ErrorType m_errorType; const glw::GLenum m_maxBindingPointEnum; const glw::GLenum m_maxVertexUnitsEnum; const glw::GLenum m_maxFragmentUnitsEnum; const glw::GLenum m_maxTessCtrlUnitsEnum; const glw::GLenum m_maxTessEvalUnitsEnum; const glw::GLenum m_maxCombinedUnitsEnum; const std::string m_uniformName; glw::GLint m_numBindings; std::vector<glw::GLint> m_vertexShaderBinding; std::vector<glw::GLint> m_fragmentShaderBinding; std::vector<glw::GLint> m_tessCtrlShaderBinding; std::vector<glw::GLint> m_tessEvalShaderBinding; bool m_tessSupport; private: void initBindingPoints (int minBindingPoint, int numBindingPoints); }; LayoutBindingNegativeCase::LayoutBindingNegativeCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, ErrorType errorType, glw::GLenum maxBindingPointEnum, glw::GLenum maxVertexUnitsEnum, glw::GLenum maxTessCtrlUnitsEnum, glw::GLenum maxTessEvalUnitsEnum, glw::GLenum maxFragmentUnitsEnum, glw::GLenum maxCombinedUnitsEnum, const std::string& uniformName) : TestCase (context, name, desc) , m_program (DE_NULL) , m_shaderType (shaderType) , m_testType (testType) , m_errorType (errorType) , m_maxBindingPointEnum (maxBindingPointEnum) , m_maxVertexUnitsEnum (maxVertexUnitsEnum) , m_maxFragmentUnitsEnum (maxFragmentUnitsEnum) , m_maxTessCtrlUnitsEnum (maxTessCtrlUnitsEnum) , m_maxTessEvalUnitsEnum (maxTessEvalUnitsEnum) , m_maxCombinedUnitsEnum (maxCombinedUnitsEnum) , m_uniformName (uniformName) , m_numBindings (0) , m_tessSupport (false) { } LayoutBindingNegativeCase::~LayoutBindingNegativeCase (void) { deinit(); } void LayoutBindingNegativeCase::init (void) { // Decide appropriate binding points for the vertex and fragment shaders const glw::Functions& gl = m_context.getRenderContext().getFunctions(); de::Random rnd (deStringHash(getName()) ^ 0xff23a4); glw::GLint numBindingPoints = 0; // Number of binding points glw::GLint maxVertexUnits = 0; // Available uniforms in the vertex shader glw::GLint maxFragmentUnits = 0; // Available uniforms in the fragment shader glw::GLint maxCombinedUnits = 0; // Available uniforms in all the shader stages combined glw::GLint maxTessCtrlUnits = 0; // Available uniforms in tessellation control shader glw::GLint maxTessEvalUnits = 0; // Available uniforms in tessellation evaluation shader glw::GLint maxUnits = 0; // Maximum available uniforms for this test m_tessSupport = m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") || contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)); if (!m_tessSupport && (m_shaderType == SHADERTYPE_TESS_EVALUATION || m_shaderType == SHADERTYPE_TESS_CONTROL)) TCU_THROW(NotSupportedError, "Tesselation shaders not supported"); int numShaderStages = m_tessSupport ? 4 : 2; gl.getIntegerv(m_maxVertexUnitsEnum, &maxVertexUnits); gl.getIntegerv(m_maxFragmentUnitsEnum, &maxFragmentUnits); if (m_tessSupport) { gl.getIntegerv(m_maxTessCtrlUnitsEnum, &maxTessCtrlUnits); gl.getIntegerv(m_maxTessEvalUnitsEnum, &maxTessEvalUnits); } gl.getIntegerv(m_maxCombinedUnitsEnum, &maxCombinedUnits); gl.getIntegerv(m_maxBindingPointEnum, &numBindingPoints); GLU_EXPECT_NO_ERROR(gl.getError(), "Querying available uniform numbers failed"); m_testCtx.getLog() << tcu::TestLog::Message << "Maximum units for uniform type in the vertex shader: " << maxVertexUnits << tcu::TestLog::EndMessage; m_testCtx.getLog() << tcu::TestLog::Message << "Maximum units for uniform type in the fragment shader: " << maxFragmentUnits << tcu::TestLog::EndMessage; if (m_tessSupport) { m_testCtx.getLog() << tcu::TestLog::Message << "Maximum units for uniform type in the tessellation control shader: " << maxTessCtrlUnits << tcu::TestLog::EndMessage; m_testCtx.getLog() << tcu::TestLog::Message << "Maximum units for uniform type in the tessellation evaluation shader: " << maxTessCtrlUnits << tcu::TestLog::EndMessage; } m_testCtx.getLog() << tcu::TestLog::Message << "Maximum combined units for uniform type: " << maxCombinedUnits << tcu::TestLog::EndMessage; m_testCtx.getLog() << tcu::TestLog::Message << "Maximum binding point for uniform type: " << numBindingPoints-1 << tcu::TestLog::EndMessage; // Select maximum number of uniforms used for the test switch (m_shaderType) { case SHADERTYPE_VERTEX: maxUnits = maxVertexUnits; break; case SHADERTYPE_FRAGMENT: maxUnits = maxFragmentUnits; break; case SHADERTYPE_ALL: maxUnits = de::min(de::min(de::min(maxVertexUnits, maxFragmentUnits), de::min(maxTessCtrlUnits, maxTessEvalUnits)), maxCombinedUnits/numShaderStages); break; case SHADERTYPE_TESS_CONTROL: maxUnits = maxTessCtrlUnits; break; case SHADERTYPE_TESS_EVALUATION: maxUnits = maxTessEvalUnits; break; default: DE_ASSERT(false); } // Select the number of uniforms (= bindings) used for this test switch (m_testType) { case TESTTYPE_BINDING_SINGLE: case TESTTYPE_BINDING_MAX: m_numBindings = 1; break; case TESTTYPE_BINDING_MULTIPLE: case TESTTYPE_BINDING_ARRAY: case TESTTYPE_BINDING_MAX_ARRAY: if (m_errorType == ERRORTYPE_CONTRADICTORY) { // leave room for contradictory case if (maxUnits < 3) TCU_THROW(NotSupportedError, "Not enough uniforms available for test"); m_numBindings = rnd.getInt(2, deMin32(MAX_UNIFORM_ARRAY_SIZE, maxUnits-1)); } else { if (maxUnits < 2) TCU_THROW(NotSupportedError, "Not enough uniforms available for test"); m_numBindings = rnd.getInt(2, deMin32(MAX_UNIFORM_ARRAY_SIZE, maxUnits)); } break; default: DE_ASSERT(false); } // Check that we have enough uniforms in different shaders to perform the tests if (((m_shaderType == SHADERTYPE_VERTEX) || (m_shaderType == SHADERTYPE_ALL)) && (maxVertexUnits < m_numBindings) ) TCU_THROW(NotSupportedError, "Vertex shader: not enough uniforms available for test"); if (((m_shaderType == SHADERTYPE_FRAGMENT) || (m_shaderType == SHADERTYPE_ALL)) && (maxFragmentUnits < m_numBindings) ) TCU_THROW(NotSupportedError, "Fragment shader: not enough uniforms available for test"); if (m_tessSupport && ((m_shaderType == SHADERTYPE_TESS_CONTROL) || (m_shaderType == SHADERTYPE_ALL)) && (maxTessCtrlUnits < m_numBindings) ) TCU_THROW(NotSupportedError, "Tessellation control shader: not enough uniforms available for test"); if (m_tessSupport && ((m_shaderType == SHADERTYPE_TESS_EVALUATION) || (m_shaderType == SHADERTYPE_ALL)) && (maxTessEvalUnits < m_numBindings) ) TCU_THROW(NotSupportedError, "Tessellation evaluation shader: not enough uniforms available for test"); if ((m_shaderType == SHADERTYPE_ALL) && (maxCombinedUnits < m_numBindings*numShaderStages) ) TCU_THROW(NotSupportedError, "Not enough uniforms available for test"); // Check that we have enough binding points to perform the tests if (numBindingPoints < m_numBindings) TCU_THROW(NotSupportedError, "Not enough binding points available for test"); if (m_errorType == ERRORTYPE_CONTRADICTORY && numBindingPoints == m_numBindings) TCU_THROW(NotSupportedError, "Not enough binding points available for test"); // Initialize the binding points i.e. populate the two binding point vectors initBindingPoints(0, numBindingPoints); // Generate the shader program - note: this must be done after deciding the binding points DE_ASSERT(!m_program); m_testCtx.getLog() << tcu::TestLog::Message << "Creating test shaders" << tcu::TestLog::EndMessage; m_program = generateShaders(); m_testCtx.getLog() << *m_program; } void LayoutBindingNegativeCase::deinit (void) { if (m_program) { delete m_program; m_program = DE_NULL; } } TestCase::IterateResult LayoutBindingNegativeCase::iterate (void) { bool pass = false; std::string failMessage; switch (m_errorType) { case ERRORTYPE_CONTRADICTORY: // Contradictory binding points should cause a link-time error if (!(m_program->getProgramInfo()).linkOk) pass = true; failMessage = "Test failed - expected a link-time error"; break; case ERRORTYPE_LESS_THAN_ZERO: // Out of bounds binding points should cause a compile-time error case ERRORTYPE_OVER_MAX_UNITS: if (m_tessSupport) { if (!(m_program->getShaderInfo(glu::SHADERTYPE_VERTEX)).compileOk || !(m_program->getShaderInfo(glu::SHADERTYPE_FRAGMENT).compileOk) || !(m_program->getShaderInfo(glu::SHADERTYPE_TESSELLATION_CONTROL).compileOk) || !(m_program->getShaderInfo(glu::SHADERTYPE_TESSELLATION_EVALUATION)).compileOk) pass = true; } else { if (!(m_program->getShaderInfo(glu::SHADERTYPE_VERTEX)).compileOk || !(m_program->getShaderInfo(glu::SHADERTYPE_FRAGMENT).compileOk)) pass = true; } failMessage = "Test failed - expected a compile-time error"; break; default: DE_ASSERT(false); } if (pass) m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); else m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, failMessage.c_str()); return STOP; } void LayoutBindingNegativeCase::initBindingPoints (int minBindingPoint, int numBindingPoints) { de::Random rnd(deStringHash(getName()) ^ 0xff23a4); switch (m_errorType) { case ERRORTYPE_OVER_MAX_UNITS: // Select a binding point that is 1 over the maximum { m_vertexShaderBinding.push_back(numBindingPoints+1-m_numBindings); m_fragmentShaderBinding.push_back(numBindingPoints+1-m_numBindings); m_tessCtrlShaderBinding.push_back(numBindingPoints+1-m_numBindings); m_tessEvalShaderBinding.push_back(numBindingPoints+1-m_numBindings); break; } case ERRORTYPE_LESS_THAN_ZERO: // Select a random negative binding point { const glw::GLint binding = -rnd.getInt(1, m_numBindings); m_vertexShaderBinding.push_back(binding); m_fragmentShaderBinding.push_back(binding); m_tessCtrlShaderBinding.push_back(binding); m_tessEvalShaderBinding.push_back(binding); break; } case ERRORTYPE_CONTRADICTORY: // Select two valid, but contradictory binding points { m_vertexShaderBinding.push_back(minBindingPoint); m_fragmentShaderBinding.push_back((minBindingPoint+1)%numBindingPoints); m_tessCtrlShaderBinding.push_back((minBindingPoint+2)%numBindingPoints); m_tessEvalShaderBinding.push_back((minBindingPoint+3)%numBindingPoints); DE_ASSERT(m_vertexShaderBinding.back() != m_fragmentShaderBinding.back()); DE_ASSERT(m_fragmentShaderBinding.back() != m_tessEvalShaderBinding.back()); DE_ASSERT(m_tessEvalShaderBinding.back() != m_tessCtrlShaderBinding.back()); DE_ASSERT(m_tessCtrlShaderBinding.back() != m_vertexShaderBinding.back()); DE_ASSERT(m_vertexShaderBinding.back() != m_tessEvalShaderBinding.back()); DE_ASSERT(m_tessCtrlShaderBinding.back() != m_fragmentShaderBinding.back()); break; } default: DE_ASSERT(false); } // In case we are testing with multiple uniforms populate the rest of the binding points for (int ndx = 1; ndx < m_numBindings; ++ndx) { m_vertexShaderBinding.push_back(m_vertexShaderBinding.front()+ndx); m_fragmentShaderBinding.push_back(m_fragmentShaderBinding.front()+ndx); m_tessCtrlShaderBinding.push_back(m_tessCtrlShaderBinding.front()+ndx); m_tessEvalShaderBinding.push_back(m_tessCtrlShaderBinding.front()+ndx); } } class SamplerBindingRenderCase : public LayoutBindingRenderCase { public: SamplerBindingRenderCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, glw::GLenum samplerType, glw::GLenum textureType); ~SamplerBindingRenderCase (void); void init (void); void deinit (void); IterateResult iterate (void); private: glu::ShaderProgram* generateShaders (void) const; glu::DataType getSamplerTexCoordType (void) const; void initializeTexture (glw::GLint bindingPoint, glw::GLint textureName, const Vec4& color) const; const glw::GLenum m_samplerType; const glw::GLenum m_textureType; std::vector<glw::GLuint> m_textures; std::vector<Vec4> m_textureColors; }; SamplerBindingRenderCase::SamplerBindingRenderCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, glw::GLenum samplerType, glw::GLenum textureType) : LayoutBindingRenderCase (context, name, desc, shaderType, testType, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, GL_MAX_TEXTURE_IMAGE_UNITS, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, "u_sampler") , m_samplerType (samplerType) , m_textureType (textureType) { } SamplerBindingRenderCase::~SamplerBindingRenderCase (void) { deinit(); } void SamplerBindingRenderCase::init (void) { LayoutBindingRenderCase::init(); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); de::Random rnd (deStringHash(getName()) ^ 0xff23a4); // Initialize texture resources m_textures = std::vector<glw::GLuint>(m_numBindings, 0); // Texture colors for (int texNdx = 0; texNdx < (int)m_textures.size(); ++texNdx) m_textureColors.push_back(getRandomColor(rnd)); // Textures gl.genTextures((glw::GLsizei)m_textures.size(), &m_textures[0]); for (int texNdx = 0; texNdx < (int)m_textures.size(); ++texNdx) initializeTexture(m_bindings[texNdx], m_textures[texNdx], m_textureColors[texNdx]); gl.activeTexture(GL_TEXTURE0); } void SamplerBindingRenderCase::deinit(void) { LayoutBindingRenderCase::deinit(); // Clean up texture data for (int i = 0; i < (int)m_textures.size(); ++i) { if (m_textures[i]) { m_context.getRenderContext().getFunctions().deleteTextures(1, &m_textures[i]); m_context.getRenderContext().getFunctions().bindTexture(m_textureType, 0); } } } TestCase::IterateResult SamplerBindingRenderCase::iterate (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const int iterations = m_numBindings; const bool arrayInstance = (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY); bool imageTestPassed = true; bool queryTestPassed = true; // Set the viewport and enable the shader program initRenderState(); for (int iterNdx = 0; iterNdx < iterations; ++iterNdx) { // Set the uniform value indicating the current array index gl.uniform1i(m_shaderProgramArrayNdxLoc, iterNdx); // Query binding point const std::string name = arrayInstance ? getUniformName(m_uniformName, 0, iterNdx) : getUniformName(m_uniformName, iterNdx); const glw::GLint binding = m_bindings[iterNdx]; glw::GLint val = -1; gl.getUniformiv(m_program->getProgram(), gl.getUniformLocation(m_program->getProgram(), name.c_str()), &val); m_testCtx.getLog() << tcu::TestLog::Message << "Querying binding point for " << name << ": " << val << " == " << binding << tcu::TestLog::EndMessage; GLU_EXPECT_NO_ERROR(gl.getError(), "Binding point query failed"); // Draw and verify if (val != binding) queryTestPassed = false; if (!drawAndVerifyResult(m_textureColors[iterNdx])) imageTestPassed = false; } setTestResult(queryTestPassed, imageTestPassed); return STOP; } glu::ShaderProgram* SamplerBindingRenderCase::generateShaders (void) const { std::ostringstream shaderUniformDecl; std::ostringstream shaderBody; const std::string texCoordType = glu::getDataTypeName(getSamplerTexCoordType()); const std::string samplerType = glu::getDataTypeName(glu::getDataTypeFromGLType(m_samplerType)); const bool arrayInstance = (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY) ? true : false; const int numDeclarations = arrayInstance ? 1 : m_numBindings; // Generate the uniform declarations for the vertex and fragment shaders for (int declNdx = 0; declNdx < numDeclarations; ++declNdx) { shaderUniformDecl << "layout(binding = " << m_bindings[declNdx] << ") uniform highp " << samplerType << " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n"; } // Generate the shader body for the vertex and fragment shaders for (int bindNdx = 0; bindNdx < m_numBindings; ++bindNdx) { shaderBody << " " << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n" << " {\n" << " color = texture(" << (arrayInstance ? getUniformName(m_uniformName, 0, bindNdx) : getUniformName(m_uniformName, bindNdx)) << ", " << texCoordType << "(0.5));\n" << " }\n"; } shaderBody << " else\n" << " {\n" << " color = vec4(0.0, 0.0, 0.0, 1.0);\n" << " }\n"; return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(generateVertexShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str())) << glu::FragmentSource(generateFragmentShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str()))); } void SamplerBindingRenderCase::initializeTexture (glw::GLint bindingPoint, glw::GLint textureName, const Vec4& color) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.activeTexture(GL_TEXTURE0 + bindingPoint); gl.bindTexture(m_textureType, textureName); gl.texParameteri(m_textureType, GL_TEXTURE_MIN_FILTER, GL_LINEAR); switch (m_textureType) { case GL_TEXTURE_2D: { tcu::TextureLevel level(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE); tcu::clear(level.getAccess(), color); glu::texImage2D(m_context.getRenderContext(), m_textureType, 0, GL_RGBA8, level.getAccess()); break; } case GL_TEXTURE_3D: { tcu::TextureLevel level(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE); tcu::clear(level.getAccess(), color); glu::texImage3D(m_context.getRenderContext(), m_textureType, 0, GL_RGBA8, level.getAccess()); break; } default: DE_ASSERT(false); } GLU_EXPECT_NO_ERROR(gl.getError(), "Texture initialization failed"); } glu::DataType SamplerBindingRenderCase::getSamplerTexCoordType (void) const { switch (m_samplerType) { case GL_SAMPLER_2D: return glu::TYPE_FLOAT_VEC2; case GL_SAMPLER_3D: return glu::TYPE_FLOAT_VEC3; default: DE_ASSERT(false); return glu::TYPE_INVALID; } } class SamplerBindingNegativeCase : public LayoutBindingNegativeCase { public: SamplerBindingNegativeCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, ErrorType errorType, glw::GLenum samplerType); ~SamplerBindingNegativeCase (void); private: glu::ShaderProgram* generateShaders (void) const; glu::DataType getSamplerTexCoordType (void) const; const glw::GLenum m_samplerType; }; SamplerBindingNegativeCase::SamplerBindingNegativeCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, ErrorType errorType, glw::GLenum samplerType) : LayoutBindingNegativeCase (context, name, desc, shaderType, testType, errorType, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS, GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, GL_MAX_TEXTURE_IMAGE_UNITS, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, "u_sampler") , m_samplerType (samplerType) { } SamplerBindingNegativeCase::~SamplerBindingNegativeCase (void) { LayoutBindingNegativeCase::deinit(); } glu::ShaderProgram* SamplerBindingNegativeCase::generateShaders (void) const { std::ostringstream vertexUniformDecl; std::ostringstream fragmentUniformDecl; std::ostringstream tessCtrlUniformDecl; std::ostringstream tessEvalUniformDecl; std::ostringstream shaderBody; const std::string texCoordType = glu::getDataTypeName(getSamplerTexCoordType()); const std::string samplerType = glu::getDataTypeName(glu::getDataTypeFromGLType(m_samplerType)); const bool arrayInstance = (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY); const int numDeclarations = arrayInstance ? 1 : m_numBindings; // Generate the uniform declarations for the vertex and fragment shaders for (int declNdx = 0; declNdx < numDeclarations; ++declNdx) { vertexUniformDecl << "layout(binding = " << m_vertexShaderBinding[declNdx] << ") uniform highp " << samplerType << " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n"; fragmentUniformDecl << "layout(binding = " << m_fragmentShaderBinding[declNdx] << ") uniform highp " << samplerType << " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n"; tessCtrlUniformDecl << "layout(binding = " << m_tessCtrlShaderBinding[declNdx] << ") uniform highp " << samplerType << " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n"; tessEvalUniformDecl << "layout(binding = " << m_tessEvalShaderBinding[declNdx] << ") uniform highp " << samplerType << " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n"; } // Generate the shader body for the vertex and fragment shaders for (int bindNdx = 0; bindNdx < m_numBindings; ++bindNdx) { shaderBody << " " << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n" << " {\n" << " color = texture(" << (arrayInstance ? getUniformName(m_uniformName, 0, bindNdx) : getUniformName(m_uniformName, bindNdx)) << ", " << texCoordType << "(0.5));\n" << " }\n"; } shaderBody << " else\n" << " {\n" << " color = vec4(0.0, 0.0, 0.0, 1.0);\n" << " }\n"; glu::ProgramSources sources = glu::ProgramSources() << glu::VertexSource(generateVertexShader(m_shaderType, vertexUniformDecl.str(), shaderBody.str())) << glu::FragmentSource(generateFragmentShader(m_shaderType, fragmentUniformDecl.str(), shaderBody.str())); if (m_tessSupport) sources << glu::TessellationControlSource(generateTessControlShader(m_shaderType, tessCtrlUniformDecl.str(), shaderBody.str())) << glu::TessellationEvaluationSource(generateTessEvaluationShader(m_shaderType, tessEvalUniformDecl.str(), shaderBody.str())); return new glu::ShaderProgram(m_context.getRenderContext(), sources); } glu::DataType SamplerBindingNegativeCase::getSamplerTexCoordType(void) const { switch (m_samplerType) { case GL_SAMPLER_2D: return glu::TYPE_FLOAT_VEC2; case GL_SAMPLER_3D: return glu::TYPE_FLOAT_VEC3; default: DE_ASSERT(false); return glu::TYPE_INVALID; } } class ImageBindingRenderCase : public LayoutBindingRenderCase { public: ImageBindingRenderCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, glw::GLenum imageType, glw::GLenum textureType); ~ImageBindingRenderCase (void); void init (void); void deinit (void); IterateResult iterate (void); private: glu::ShaderProgram* generateShaders (void) const; void initializeImage (glw::GLint imageBindingPoint, glw::GLint textureBindingPoint, glw::GLint textureName, const Vec4& color) const; glu::DataType getImageTexCoordType (void) const; const glw::GLenum m_imageType; const glw::GLenum m_textureType; std::vector<glw::GLuint> m_textures; std::vector<Vec4> m_textureColors; }; ImageBindingRenderCase::ImageBindingRenderCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, glw::GLenum imageType, glw::GLenum textureType) : LayoutBindingRenderCase (context, name, desc, shaderType, testType, GL_MAX_IMAGE_UNITS, GL_MAX_VERTEX_IMAGE_UNIFORMS, GL_MAX_FRAGMENT_IMAGE_UNIFORMS, GL_MAX_COMBINED_IMAGE_UNIFORMS, "u_image") , m_imageType (imageType) , m_textureType (textureType) { } ImageBindingRenderCase::~ImageBindingRenderCase (void) { deinit(); } void ImageBindingRenderCase::init (void) { LayoutBindingRenderCase::init(); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); de::Random rnd (deStringHash(getName()) ^ 0xff23a4); // Initialize image / texture resources m_textures = std::vector<glw::GLuint>(m_numBindings, 0); // Texture colors for (int texNdx = 0; texNdx < (int)m_textures.size(); ++texNdx) m_textureColors.push_back(getRandomColor(rnd)); // Image textures gl.genTextures(m_numBindings, &m_textures[0]); for (int texNdx = 0; texNdx < (int)m_textures.size(); ++texNdx) initializeImage(m_bindings[texNdx], texNdx, m_textures[texNdx], m_textureColors[texNdx]); } void ImageBindingRenderCase::deinit (void) { LayoutBindingRenderCase::deinit(); // Clean up texture data for (int texNdx = 0; texNdx < (int)m_textures.size(); ++texNdx) { if (m_textures[texNdx]) { m_context.getRenderContext().getFunctions().deleteTextures(1, &m_textures[texNdx]); m_context.getRenderContext().getFunctions().bindTexture(m_textureType, 0); } } } TestCase::IterateResult ImageBindingRenderCase::iterate (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const int iterations = m_numBindings; const bool arrayInstance = (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY); bool queryTestPassed = true; bool imageTestPassed = true; // Set the viewport and enable the shader program initRenderState(); for (int iterNdx = 0; iterNdx < iterations; ++iterNdx) { // Set the uniform value indicating the current array index gl.uniform1i(m_shaderProgramArrayNdxLoc, iterNdx); const std::string name = (arrayInstance ? getUniformName(m_uniformName, 0, iterNdx) : getUniformName(m_uniformName, iterNdx)); const glw::GLint binding = m_bindings[iterNdx]; glw::GLint val = -1; gl.getUniformiv(m_program->getProgram(), gl.getUniformLocation(m_program->getProgram(), name.c_str()), &val); m_testCtx.getLog() << tcu::TestLog::Message << "Querying binding point for " << name << ": " << val << " == " << binding << tcu::TestLog::EndMessage; GLU_EXPECT_NO_ERROR(gl.getError(), "Binding point query failed"); // Draw and verify if (val != binding) queryTestPassed = false; if (!drawAndVerifyResult(m_textureColors[iterNdx])) imageTestPassed = false; } setTestResult(queryTestPassed, imageTestPassed); return STOP; } void ImageBindingRenderCase::initializeImage (glw::GLint imageBindingPoint, glw::GLint textureBindingPoint, glw::GLint textureName, const Vec4& color) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.activeTexture(GL_TEXTURE0 + textureBindingPoint); gl.bindTexture(m_textureType, textureName); gl.texParameteri(m_textureType, GL_TEXTURE_MIN_FILTER, GL_LINEAR); switch (m_textureType) { case GL_TEXTURE_2D: { tcu::TextureLevel level(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE); tcu::clear(level.getAccess(), color); gl.texStorage2D(m_textureType, 1, GL_RGBA8, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE); gl.texSubImage2D(m_textureType, 0, 0, 0, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, GL_RGBA, GL_UNSIGNED_BYTE, level.getAccess().getDataPtr()); break; } case GL_TEXTURE_3D: { tcu::TextureLevel level(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE); tcu::clear(level.getAccess(), color); gl.texStorage3D(m_textureType, 1, GL_RGBA8, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE); gl.texSubImage3D(m_textureType, 0, 0, 0, 0, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, GL_RGBA, GL_UNSIGNED_BYTE, level.getAccess().getDataPtr()); break; } default: DE_ASSERT(false); } gl.bindTexture(m_textureType, 0); gl.bindImageTexture(imageBindingPoint, textureName, 0, GL_TRUE, 0, GL_READ_ONLY, GL_RGBA8); GLU_EXPECT_NO_ERROR(gl.getError(), "Image initialization failed"); } glu::ShaderProgram* ImageBindingRenderCase::generateShaders (void) const { std::ostringstream shaderUniformDecl; std::ostringstream shaderBody; const std::string texCoordType = glu::getDataTypeName(getImageTexCoordType()); const std::string imageType = glu::getDataTypeName(glu::getDataTypeFromGLType(m_imageType)); const bool arrayInstance = (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY) ? true : false; const int numDeclarations = (arrayInstance ? 1 : m_numBindings); // Generate the uniform declarations for the vertex and fragment shaders for (int declNdx = 0; declNdx < numDeclarations; ++declNdx) { shaderUniformDecl << "layout(rgba8, binding = " << m_bindings[declNdx] << ") uniform readonly highp " << imageType << " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n"; } // Generate the shader body for the vertex and fragment shaders for (int bindNdx = 0; bindNdx < m_numBindings; ++bindNdx) { shaderBody << " " << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n" << " {\n" << " color = imageLoad(" << (arrayInstance ? getUniformName(m_uniformName, 0, bindNdx) : getUniformName(m_uniformName, bindNdx)) << ", " << texCoordType << "(0));\n" << " }\n"; } shaderBody << " else\n" << " {\n" << " color = vec4(0.0, 0.0, 0.0, 1.0);\n" << " }\n"; return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(generateVertexShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str())) << glu::FragmentSource(generateFragmentShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str()))); } glu::DataType ImageBindingRenderCase::getImageTexCoordType(void) const { switch (m_imageType) { case GL_IMAGE_2D: return glu::TYPE_INT_VEC2; case GL_IMAGE_3D: return glu::TYPE_INT_VEC3; default: DE_ASSERT(false); return glu::TYPE_INVALID; } } class ImageBindingNegativeCase : public LayoutBindingNegativeCase { public: ImageBindingNegativeCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, ErrorType errorType, glw::GLenum imageType); ~ImageBindingNegativeCase (void); private: glu::ShaderProgram* generateShaders (void) const; glu::DataType getImageTexCoordType (void) const; const glw::GLenum m_imageType; }; ImageBindingNegativeCase::ImageBindingNegativeCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, ErrorType errorType, glw::GLenum imageType) : LayoutBindingNegativeCase (context, name, desc, shaderType, testType, errorType, GL_MAX_IMAGE_UNITS, GL_MAX_VERTEX_IMAGE_UNIFORMS, GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, GL_MAX_FRAGMENT_IMAGE_UNIFORMS, GL_MAX_COMBINED_IMAGE_UNIFORMS, "u_image") , m_imageType (imageType) { } ImageBindingNegativeCase::~ImageBindingNegativeCase (void) { deinit(); } glu::ShaderProgram* ImageBindingNegativeCase::generateShaders (void) const { std::ostringstream vertexUniformDecl; std::ostringstream fragmentUniformDecl; std::ostringstream tessCtrlUniformDecl; std::ostringstream tessEvalUniformDecl; std::ostringstream shaderBody; const std::string texCoordType = glu::getDataTypeName(getImageTexCoordType()); const std::string imageType = glu::getDataTypeName(glu::getDataTypeFromGLType(m_imageType)); const bool arrayInstance = (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY); const int numDeclarations = (arrayInstance ? 1 : m_numBindings); // Generate the uniform declarations for the vertex and fragment shaders for (int declNdx = 0; declNdx < numDeclarations; ++declNdx) { vertexUniformDecl << "layout(rgba8, binding = " << m_vertexShaderBinding[declNdx] << ") uniform readonly highp " << imageType << " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n"; fragmentUniformDecl << "layout(rgba8, binding = " << m_fragmentShaderBinding[declNdx] << ") uniform readonly highp " << imageType << " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n"; tessCtrlUniformDecl << "layout(rgba8, binding = " << m_tessCtrlShaderBinding[declNdx] << ") uniform readonly highp " << imageType << " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n"; tessEvalUniformDecl << "layout(rgba8, binding = " << m_tessEvalShaderBinding[declNdx] << ") uniform readonly highp " << imageType << " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n"; } // Generate the shader body for the vertex and fragment shaders for (int bindNdx = 0; bindNdx < m_numBindings; ++bindNdx) { shaderBody << " " << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n" << " {\n" << " color = imageLoad(" << (arrayInstance ? getUniformName(m_uniformName, 0, bindNdx) : getUniformName(m_uniformName, bindNdx)) << ", " << texCoordType << "(0));\n" << " }\n"; } shaderBody << " else\n" << " {\n" << " color = vec4(0.0, 0.0, 0.0, 1.0);\n" << " }\n"; glu::ProgramSources sources = glu::ProgramSources() << glu::VertexSource(generateVertexShader(m_shaderType, vertexUniformDecl.str(), shaderBody.str())) << glu::FragmentSource(generateFragmentShader(m_shaderType, fragmentUniformDecl.str(), shaderBody.str())); if (m_tessSupport) sources << glu::TessellationControlSource(generateTessControlShader(m_shaderType, tessCtrlUniformDecl.str(), shaderBody.str())) << glu::TessellationEvaluationSource(generateTessEvaluationShader(m_shaderType, tessEvalUniformDecl.str(), shaderBody.str())); return new glu::ShaderProgram(m_context.getRenderContext(), sources); } glu::DataType ImageBindingNegativeCase::getImageTexCoordType(void) const { switch (m_imageType) { case GL_IMAGE_2D: return glu::TYPE_INT_VEC2; case GL_IMAGE_3D: return glu::TYPE_INT_VEC3; default: DE_ASSERT(false); return glu::TYPE_INVALID; } } class UBOBindingRenderCase : public LayoutBindingRenderCase { public: UBOBindingRenderCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType); ~UBOBindingRenderCase (void); void init (void); void deinit (void); IterateResult iterate (void); private: glu::ShaderProgram* generateShaders (void) const; std::vector<deUint32> m_buffers; std::vector<Vec4> m_expectedColors; }; UBOBindingRenderCase::UBOBindingRenderCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType) : LayoutBindingRenderCase (context, name, desc, shaderType, testType, GL_MAX_UNIFORM_BUFFER_BINDINGS, GL_MAX_VERTEX_UNIFORM_BLOCKS, GL_MAX_FRAGMENT_UNIFORM_BLOCKS, GL_MAX_COMBINED_UNIFORM_BLOCKS, "ColorBlock") { } UBOBindingRenderCase::~UBOBindingRenderCase (void) { deinit(); } void UBOBindingRenderCase::init (void) { LayoutBindingRenderCase::init(); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); de::Random rnd (deStringHash(getName()) ^ 0xff23a4); // Initialize UBOs and related data m_buffers = std::vector<glw::GLuint>(m_numBindings, 0); gl.genBuffers((glw::GLsizei)m_buffers.size(), &m_buffers[0]); for (int bufNdx = 0; bufNdx < (int)m_buffers.size(); ++bufNdx) { m_expectedColors.push_back(getRandomColor(rnd)); m_expectedColors.push_back(getRandomColor(rnd)); } for (int bufNdx = 0; bufNdx < (int)m_buffers.size(); ++bufNdx) { gl.bindBuffer(GL_UNIFORM_BUFFER, m_buffers[bufNdx]); gl.bufferData(GL_UNIFORM_BUFFER, 2*sizeof(Vec4), &(m_expectedColors[2*bufNdx]), GL_STATIC_DRAW); gl.bindBufferBase(GL_UNIFORM_BUFFER, m_bindings[bufNdx], m_buffers[bufNdx]); } GLU_EXPECT_NO_ERROR(gl.getError(), "UBO setup failed"); } void UBOBindingRenderCase::deinit (void) { LayoutBindingRenderCase::deinit(); // Clean up UBO data for (int bufNdx = 0; bufNdx < (int)m_buffers.size(); ++bufNdx) { if (m_buffers[bufNdx]) { m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_buffers[bufNdx]); m_context.getRenderContext().getFunctions().bindBuffer(GL_UNIFORM_BUFFER, 0); } } } TestCase::IterateResult UBOBindingRenderCase::iterate (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const int iterations = m_numBindings; const glw::GLenum prop = GL_BUFFER_BINDING; const bool arrayInstance = (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY); bool queryTestPassed = true; bool imageTestPassed = true; // Set the viewport and enable the shader program initRenderState(); for (int iterNdx = 0; iterNdx < iterations; ++iterNdx) { // Query binding point const std::string name = (arrayInstance ? getUniformName(m_uniformName, 0, iterNdx) : getUniformName(m_uniformName, iterNdx)); const glw::GLint binding = m_bindings[iterNdx]; glw::GLint val = -1; gl.getProgramResourceiv(m_program->getProgram(), GL_UNIFORM_BLOCK, gl.getProgramResourceIndex(m_program->getProgram(), GL_UNIFORM_BLOCK, name.c_str() ), 1, &prop, 1, DE_NULL, &val); m_testCtx.getLog() << tcu::TestLog::Message << "Querying binding point for " << name << ": " << val << " == " << binding << tcu::TestLog::EndMessage; GLU_EXPECT_NO_ERROR(gl.getError(), "Binding point query failed"); if (val != binding) queryTestPassed = false; // Draw twice to render both colors within the UBO for (int drawCycle = 0; drawCycle < 2; ++drawCycle) { // Set the uniform indicating the array index to be used and set the expected color const int arrayNdx = iterNdx*2 + drawCycle; gl.uniform1i(m_shaderProgramArrayNdxLoc, arrayNdx); if (!drawAndVerifyResult(m_expectedColors[arrayNdx])) imageTestPassed = false; } } setTestResult(queryTestPassed, imageTestPassed); return STOP; } glu::ShaderProgram* UBOBindingRenderCase::generateShaders (void) const { std::ostringstream shaderUniformDecl; std::ostringstream shaderBody; const bool arrayInstance = (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY); const int numDeclarations = (arrayInstance ? 1 : m_numBindings); // Generate the uniform declarations for the vertex and fragment shaders for (int declNdx = 0; declNdx < numDeclarations; ++declNdx) { shaderUniformDecl << "layout(std140, binding = " << m_bindings[declNdx] << ") uniform " << getUniformName(m_uniformName, declNdx) << "\n" << "{\n" << " highp vec4 color1;\n" << " highp vec4 color2;\n" << "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n"; } // Generate the shader body for the vertex and fragment shaders for (int bindNdx = 0; bindNdx < m_numBindings*2; ++bindNdx) // Multiply by two to cover cases for both colors for each UBO { const std::string uname = (arrayInstance ? getUniformName("colors", 0, bindNdx/2) : getUniformName("colors", bindNdx/2)); shaderBody << " " << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n" << " {\n" << " color = " << uname << (bindNdx%2 == 0 ? ".color1" : ".color2") << ";\n" << " }\n"; } shaderBody << " else\n" << " {\n" << " color = vec4(0.0, 0.0, 0.0, 1.0);\n" << " }\n"; return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(generateVertexShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str())) << glu::FragmentSource(generateFragmentShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str()))); } class UBOBindingNegativeCase : public LayoutBindingNegativeCase { public: UBOBindingNegativeCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, ErrorType errorType); ~UBOBindingNegativeCase (void); private: glu::ShaderProgram* generateShaders (void) const; }; UBOBindingNegativeCase::UBOBindingNegativeCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, ErrorType errorType) : LayoutBindingNegativeCase(context, name, desc, shaderType, testType, errorType, GL_MAX_UNIFORM_BUFFER_BINDINGS, GL_MAX_VERTEX_UNIFORM_BLOCKS, GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, GL_MAX_FRAGMENT_UNIFORM_BLOCKS, GL_MAX_COMBINED_UNIFORM_BLOCKS, "ColorBlock") { } UBOBindingNegativeCase::~UBOBindingNegativeCase (void) { deinit(); } glu::ShaderProgram* UBOBindingNegativeCase::generateShaders (void) const { std::ostringstream vertexUniformDecl; std::ostringstream fragmentUniformDecl; std::ostringstream tessCtrlUniformDecl; std::ostringstream tessEvalUniformDecl; std::ostringstream shaderBody; const bool arrayInstance = (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY); const int numDeclarations = (arrayInstance ? 1 : m_numBindings); // Generate the uniform declarations for the vertex and fragment shaders for (int declNdx = 0; declNdx < numDeclarations; ++declNdx) { vertexUniformDecl << "layout(std140, binding = " << m_vertexShaderBinding[declNdx] << ") uniform " << getUniformName(m_uniformName, declNdx) << "\n" << "{\n" << " highp vec4 color1;\n" << " highp vec4 color2;\n" << "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n"; fragmentUniformDecl << "layout(std140, binding = " << m_fragmentShaderBinding[declNdx] << ") uniform " << getUniformName(m_uniformName, declNdx) << "\n" << "{\n" << " highp vec4 color1;\n" << " highp vec4 color2;\n" << "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n"; tessCtrlUniformDecl << "layout(std140, binding = " << m_tessCtrlShaderBinding[declNdx] << ") uniform " << getUniformName(m_uniformName, declNdx) << "\n" << "{\n" << " highp vec4 color1;\n" << " highp vec4 color2;\n" << "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n"; tessEvalUniformDecl << "layout(std140, binding = " << m_tessCtrlShaderBinding[declNdx] << ") uniform " << getUniformName(m_uniformName, declNdx) << "\n" << "{\n" << " highp vec4 color1;\n" << " highp vec4 color2;\n" << "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n"; } // Generate the shader body for the vertex and fragment shaders for (int bindNdx = 0; bindNdx < m_numBindings*2; ++bindNdx) // Multiply by two to cover cases for both colors for each UBO { const std::string uname = (arrayInstance ? getUniformName("colors", 0, bindNdx/2) : getUniformName("colors", bindNdx/2)); shaderBody << " " << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n" << " {\n" << " color = " << uname << (bindNdx%2 == 0 ? ".color1" : ".color2") << ";\n" << " }\n"; } shaderBody << " else\n" << " {\n" << " color = vec4(0.0, 0.0, 0.0, 1.0);\n" << " }\n"; glu::ProgramSources sources = glu::ProgramSources() << glu::VertexSource(generateVertexShader(m_shaderType, vertexUniformDecl.str(), shaderBody.str())) << glu::FragmentSource(generateFragmentShader(m_shaderType, fragmentUniformDecl.str(), shaderBody.str())); if (m_tessSupport) sources << glu::TessellationControlSource(generateTessControlShader(m_shaderType, tessCtrlUniformDecl.str(), shaderBody.str())) << glu::TessellationEvaluationSource(generateTessEvaluationShader(m_shaderType, tessEvalUniformDecl.str(), shaderBody.str())); return new glu::ShaderProgram(m_context.getRenderContext(), sources); } class SSBOBindingRenderCase : public LayoutBindingRenderCase { public: SSBOBindingRenderCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType); ~SSBOBindingRenderCase (void); void init (void); void deinit (void); IterateResult iterate (void); private: glu::ShaderProgram* generateShaders (void) const; std::vector<glw::GLuint> m_buffers; std::vector<Vec4> m_expectedColors; }; SSBOBindingRenderCase::SSBOBindingRenderCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType) : LayoutBindingRenderCase (context, name, desc, shaderType, testType, GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, "ColorBuffer") { } SSBOBindingRenderCase::~SSBOBindingRenderCase (void) { deinit(); } void SSBOBindingRenderCase::init (void) { LayoutBindingRenderCase::init(); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); de::Random rnd (deStringHash(getName()) ^ 0xff23a4); // Initialize SSBOs and related data m_buffers = std::vector<glw::GLuint>(m_numBindings, 0); gl.genBuffers((glw::GLsizei)m_buffers.size(), &m_buffers[0]); for (int bufNdx = 0; bufNdx < (int)m_buffers.size(); ++bufNdx) { m_expectedColors.push_back(getRandomColor(rnd)); m_expectedColors.push_back(getRandomColor(rnd)); } for (int bufNdx = 0; bufNdx < (int)m_buffers.size(); ++bufNdx) { gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_buffers[bufNdx]); gl.bufferData(GL_SHADER_STORAGE_BUFFER, 2*sizeof(Vec4), &(m_expectedColors[2*bufNdx]), GL_STATIC_DRAW); gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, m_bindings[bufNdx], m_buffers[bufNdx]); } GLU_EXPECT_NO_ERROR(gl.getError(), "SSBO setup failed"); } void SSBOBindingRenderCase::deinit (void) { LayoutBindingRenderCase::deinit(); // Clean up SSBO data for (int bufNdx = 0; bufNdx < (int)m_buffers.size(); ++bufNdx) { if (m_buffers[bufNdx]) { m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_buffers[bufNdx]); m_context.getRenderContext().getFunctions().bindBuffer(GL_SHADER_STORAGE_BUFFER, 0); m_buffers[bufNdx] = 0; } } } TestCase::IterateResult SSBOBindingRenderCase::iterate (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const int iterations = m_numBindings; const glw::GLenum prop = GL_BUFFER_BINDING; const bool arrayInstance = (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY); bool queryTestPassed = true; bool imageTestPassed = true; initRenderState(); for (int iterNdx = 0; iterNdx < iterations; ++iterNdx) { // Query binding point const std::string name = (arrayInstance ? getUniformName(m_uniformName, 0, iterNdx) : getUniformName(m_uniformName, iterNdx)); const glw::GLint binding = m_bindings[iterNdx]; glw::GLint val = -1; gl.getProgramResourceiv(m_program->getProgram(), GL_SHADER_STORAGE_BLOCK, gl.getProgramResourceIndex(m_program->getProgram(), GL_SHADER_STORAGE_BLOCK, name.c_str() ), 1, &prop, 1, DE_NULL, &val); m_testCtx.getLog() << tcu::TestLog::Message << "Querying binding point for " << name << ": " << val << " == " << binding << tcu::TestLog::EndMessage; GLU_EXPECT_NO_ERROR(gl.getError(), "Binding point query failed"); if (val != binding) queryTestPassed = false; // Draw twice to render both colors within the SSBO for (int drawCycle = 0; drawCycle < 2; ++drawCycle) { // Set the uniform indicating the array index to be used and set the expected color const int arrayNdx = iterNdx*2 + drawCycle; gl.uniform1i(m_shaderProgramArrayNdxLoc, arrayNdx); if (!drawAndVerifyResult(m_expectedColors[arrayNdx])) imageTestPassed = false; } } setTestResult(queryTestPassed, imageTestPassed); return STOP; } glu::ShaderProgram* SSBOBindingRenderCase::generateShaders (void) const { std::ostringstream shaderUniformDecl; std::ostringstream shaderBody; const bool arrayInstance = (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY); const int numDeclarations = (arrayInstance ? 1 : m_numBindings); // Generate the uniform declarations for the vertex and fragment shaders for (int declNdx = 0; declNdx < numDeclarations; ++declNdx) { shaderUniformDecl << "layout(std140, binding = " << m_bindings[declNdx] << ") buffer " << getUniformName(m_uniformName, declNdx) << "\n" << "{\n" << " highp vec4 color1;\n" << " highp vec4 color2;\n" << "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n"; } // Generate the shader body for the vertex and fragment shaders for (int bindNdx = 0; bindNdx < m_numBindings*2; ++bindNdx) // Multiply by two to cover cases for both colors for each UBO { const std::string uname = (arrayInstance ? getUniformName("colors", 0, bindNdx/2) : getUniformName("colors", bindNdx/2)); shaderBody << " " << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n" << " {\n" << " color = " << uname << (bindNdx%2 == 0 ? ".color1" : ".color2") << ";\n" << " }\n"; } shaderBody << " else\n" << " {\n" << " color = vec4(0.0, 0.0, 0.0, 1.0);\n" << " }\n"; return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(generateVertexShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str())) << glu::FragmentSource(generateFragmentShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str()))); } class SSBOBindingNegativeCase : public LayoutBindingNegativeCase { public: SSBOBindingNegativeCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, ErrorType errorType); ~SSBOBindingNegativeCase (void); private: glu::ShaderProgram* generateShaders (void) const; }; SSBOBindingNegativeCase::SSBOBindingNegativeCase (Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, ErrorType errorType) : LayoutBindingNegativeCase(context, name, desc, shaderType, testType, errorType, GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS, GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS, GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, "ColorBuffer") { } SSBOBindingNegativeCase::~SSBOBindingNegativeCase (void) { deinit(); } glu::ShaderProgram* SSBOBindingNegativeCase::generateShaders (void) const { std::ostringstream vertexUniformDecl; std::ostringstream fragmentUniformDecl; std::ostringstream tessCtrlUniformDecl; std::ostringstream tessEvalUniformDecl; std::ostringstream shaderBody; const bool arrayInstance = (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY); const int numDeclarations = (arrayInstance ? 1 : m_numBindings); // Generate the uniform declarations for the vertex and fragment shaders for (int declNdx = 0; declNdx < numDeclarations; ++declNdx) { vertexUniformDecl << "layout(std140, binding = " << m_vertexShaderBinding[declNdx] << ") buffer " << getUniformName(m_uniformName, declNdx) << "\n" << "{\n" << " highp vec4 color1;\n" << " highp vec4 color2;\n" << "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n"; fragmentUniformDecl << "layout(std140, binding = " << m_fragmentShaderBinding[declNdx] << ") buffer " << getUniformName(m_uniformName, declNdx) << "\n" << "{\n" << " highp vec4 color1;\n" << " highp vec4 color2;\n" << "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n"; tessCtrlUniformDecl << "layout(std140, binding = " << m_tessCtrlShaderBinding[declNdx] << ") buffer " << getUniformName(m_uniformName, declNdx) << "\n" << "{\n" << " highp vec4 color1;\n" << " highp vec4 color2;\n" << "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n"; tessEvalUniformDecl << "layout(std140, binding = " << m_tessEvalShaderBinding[declNdx] << ") buffer " << getUniformName(m_uniformName, declNdx) << "\n" << "{\n" << " highp vec4 color1;\n" << " highp vec4 color2;\n" << "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n"; } // Generate the shader body for the vertex and fragment shaders for (int bindNdx = 0; bindNdx < m_numBindings*2; ++bindNdx) // Multiply by two to cover cases for both colors for each UBO { const std::string uname = (arrayInstance ? getUniformName("colors", 0, bindNdx/2) : getUniformName("colors", bindNdx/2)); shaderBody << " " << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n" << " {\n" << " color = " << uname << (bindNdx%2 == 0 ? ".color1" : ".color2") << ";\n" << " }\n"; } shaderBody << " else\n" << " {\n" << " color = vec4(0.0, 0.0, 0.0, 1.0);\n" << " }\n"; glu::ProgramSources sources = glu::ProgramSources() << glu::VertexSource(generateVertexShader(m_shaderType, vertexUniformDecl.str(), shaderBody.str())) << glu::FragmentSource(generateFragmentShader(m_shaderType, fragmentUniformDecl.str(), shaderBody.str())); if (m_tessSupport) sources << glu::TessellationControlSource(generateTessControlShader(m_shaderType, tessCtrlUniformDecl.str(), shaderBody.str())) << glu::TessellationEvaluationSource(generateTessEvaluationShader(m_shaderType, tessEvalUniformDecl.str(), shaderBody.str())); return new glu::ShaderProgram(m_context.getRenderContext(), sources); } } // Anonymous LayoutBindingTests::LayoutBindingTests (Context& context) : TestCaseGroup (context, "layout_binding", "Layout binding tests") { } LayoutBindingTests::~LayoutBindingTests (void) { } void LayoutBindingTests::init (void) { // Render test groups tcu::TestCaseGroup* const samplerBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "sampler", "Test sampler layout binding"); tcu::TestCaseGroup* const sampler2dBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "sampler2d", "Test sampler2d layout binding"); tcu::TestCaseGroup* const sampler3dBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "sampler3d", "Test sampler3d layout binding"); tcu::TestCaseGroup* const imageBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "image", "Test image layout binding"); tcu::TestCaseGroup* const image2dBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "image2d", "Test image2d layout binding"); tcu::TestCaseGroup* const image3dBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "image3d", "Test image3d layout binding"); tcu::TestCaseGroup* const UBOBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "ubo", "Test UBO layout binding"); tcu::TestCaseGroup* const SSBOBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "ssbo", "Test SSBO layout binding"); // Negative test groups tcu::TestCaseGroup* const negativeBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "negative", "Test layout binding with invalid bindings"); tcu::TestCaseGroup* const negativeSamplerBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "sampler", "Test sampler layout binding with invalid bindings"); tcu::TestCaseGroup* const negativeSampler2dBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "sampler2d", "Test sampler2d layout binding with invalid bindings"); tcu::TestCaseGroup* const negativeSampler3dBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "sampler3d", "Test sampler3d layout binding with invalid bindings"); tcu::TestCaseGroup* const negativeImageBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "image", "Test image layout binding with invalid bindings"); tcu::TestCaseGroup* const negativeImage2dBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "image2d", "Test image2d layout binding with invalid bindings"); tcu::TestCaseGroup* const negativeImage3dBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "image3d", "Test image3d layout binding with invalid bindings"); tcu::TestCaseGroup* const negativeUBOBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "ubo", "Test UBO layout binding with invalid bindings"); tcu::TestCaseGroup* const negativeSSBOBindingTestGroup = new tcu::TestCaseGroup(m_testCtx, "ssbo", "Test SSBO layout binding with invalid bindings"); static const struct RenderTestType { ShaderType shaderType; TestType testType; std::string name; std::string descPostfix; } s_renderTestTypes[] = { { SHADERTYPE_VERTEX, TESTTYPE_BINDING_SINGLE, "vertex_binding_single", "a single instance" }, { SHADERTYPE_VERTEX, TESTTYPE_BINDING_MAX, "vertex_binding_max", "maximum binding point" }, { SHADERTYPE_VERTEX, TESTTYPE_BINDING_MULTIPLE, "vertex_binding_multiple", "multiple instances"}, { SHADERTYPE_VERTEX, TESTTYPE_BINDING_ARRAY, "vertex_binding_array", "an array instance" }, { SHADERTYPE_VERTEX, TESTTYPE_BINDING_MAX_ARRAY, "vertex_binding_max_array", "an array instance with maximum binding point" }, { SHADERTYPE_FRAGMENT, TESTTYPE_BINDING_SINGLE, "fragment_binding_single", "a single instance" }, { SHADERTYPE_FRAGMENT, TESTTYPE_BINDING_MAX, "fragment_binding_max", "maximum binding point" }, { SHADERTYPE_FRAGMENT, TESTTYPE_BINDING_MULTIPLE, "fragment_binding_multiple", "multiple instances"}, { SHADERTYPE_FRAGMENT, TESTTYPE_BINDING_ARRAY, "fragment_binding_array", "an array instance" }, { SHADERTYPE_FRAGMENT, TESTTYPE_BINDING_MAX_ARRAY, "fragment_binding_max_array", "an array instance with maximum binding point" }, }; static const struct NegativeTestType { ShaderType shaderType; TestType testType; LayoutBindingNegativeCase::ErrorType errorType; std::string name; std::string descPostfix; } s_negativeTestTypes[] = { { SHADERTYPE_VERTEX, TESTTYPE_BINDING_SINGLE, LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS, "vertex_binding_over_max", "over maximum binding point" }, { SHADERTYPE_FRAGMENT, TESTTYPE_BINDING_SINGLE, LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS, "fragment_binding_over_max", "over maximum binding point" }, { SHADERTYPE_TESS_CONTROL, TESTTYPE_BINDING_SINGLE, LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS, "tess_control_binding_over_max", "over maximum binding point" }, { SHADERTYPE_TESS_EVALUATION, TESTTYPE_BINDING_SINGLE, LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS, "tess_evaluation_binding_over_max", "over maximum binding point" }, { SHADERTYPE_VERTEX, TESTTYPE_BINDING_SINGLE, LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO, "vertex_binding_neg", "negative binding point" }, { SHADERTYPE_FRAGMENT, TESTTYPE_BINDING_SINGLE, LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO, "fragment_binding_neg", "negative binding point" }, { SHADERTYPE_TESS_CONTROL, TESTTYPE_BINDING_SINGLE, LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO, "tess_control_binding_neg", "negative binding point" }, { SHADERTYPE_TESS_EVALUATION, TESTTYPE_BINDING_SINGLE, LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO, "tess_evaluation_binding_neg", "negative binding point" }, { SHADERTYPE_VERTEX, TESTTYPE_BINDING_ARRAY, LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS, "vertex_binding_over_max_array", "over maximum binding point" }, { SHADERTYPE_FRAGMENT, TESTTYPE_BINDING_ARRAY, LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS, "fragment_binding_over_max_array", "over maximum binding point" }, { SHADERTYPE_TESS_CONTROL, TESTTYPE_BINDING_ARRAY, LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS, "tess_control_binding_over_max_array", "over maximum binding point" }, { SHADERTYPE_TESS_EVALUATION, TESTTYPE_BINDING_ARRAY, LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS, "tess_evaluation_binding_over_max_array", "over maximum binding point" }, { SHADERTYPE_VERTEX, TESTTYPE_BINDING_ARRAY, LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO, "vertex_binding_neg_array", "negative binding point" }, { SHADERTYPE_FRAGMENT, TESTTYPE_BINDING_ARRAY, LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO, "fragment_binding_neg_array", "negative binding point" }, { SHADERTYPE_TESS_CONTROL, TESTTYPE_BINDING_ARRAY, LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO, "tess_control_binding_neg_array", "negative binding point" }, { SHADERTYPE_TESS_EVALUATION, TESTTYPE_BINDING_ARRAY, LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO, "tess_evaluation_binding_neg_array", "negative binding point" }, { SHADERTYPE_ALL, TESTTYPE_BINDING_SINGLE, LayoutBindingNegativeCase::ERRORTYPE_CONTRADICTORY, "binding_contradictory", "contradictory binding points" }, { SHADERTYPE_ALL, TESTTYPE_BINDING_ARRAY, LayoutBindingNegativeCase::ERRORTYPE_CONTRADICTORY, "binding_contradictory_array", "contradictory binding points" }, }; // Render tests for (int testNdx = 0; testNdx < DE_LENGTH_OF_ARRAY(s_renderTestTypes); ++testNdx) { const RenderTestType& test = s_renderTestTypes[testNdx]; // Render sampler binding tests sampler2dBindingTestGroup->addChild(new SamplerBindingRenderCase(m_context, test.name.c_str(), ("Sampler2D layout binding with " + test.descPostfix).c_str(), test.shaderType, test.testType, GL_SAMPLER_2D, GL_TEXTURE_2D)); sampler3dBindingTestGroup->addChild(new SamplerBindingRenderCase(m_context, test.name.c_str(), ("Sampler3D layout binding with " + test.descPostfix).c_str(), test.shaderType, test.testType, GL_SAMPLER_3D, GL_TEXTURE_3D)); // Render image binding tests image2dBindingTestGroup->addChild(new ImageBindingRenderCase(m_context, test.name.c_str(), ("Image2D layout binding with " + test.descPostfix).c_str(), test.shaderType, test.testType, GL_IMAGE_2D, GL_TEXTURE_2D)); image3dBindingTestGroup->addChild(new ImageBindingRenderCase(m_context, test.name.c_str(), ("Image3D layout binding with " + test.descPostfix).c_str(), test.shaderType, test.testType, GL_IMAGE_3D, GL_TEXTURE_3D)); // Render UBO binding tests UBOBindingTestGroup->addChild(new UBOBindingRenderCase(m_context, test.name.c_str(), ("UBO layout binding with " + test.descPostfix).c_str(), test.shaderType, test.testType)); // Render SSBO binding tests SSBOBindingTestGroup->addChild(new SSBOBindingRenderCase(m_context, test.name.c_str(), ("SSBO layout binding with " + test.descPostfix).c_str(), test.shaderType, test.testType)); } // Negative binding tests for (int testNdx = 0; testNdx < DE_LENGTH_OF_ARRAY(s_negativeTestTypes); ++testNdx) { const NegativeTestType& test = s_negativeTestTypes[testNdx]; // Negative sampler binding tests negativeSampler2dBindingTestGroup->addChild(new SamplerBindingNegativeCase(m_context, test.name.c_str(), ("Invalid sampler2d layout binding using " + test.descPostfix).c_str(), test.shaderType, test.testType, test.errorType, GL_SAMPLER_2D)); negativeSampler3dBindingTestGroup->addChild(new SamplerBindingNegativeCase(m_context, test.name.c_str(), ("Invalid sampler3d layout binding using " + test.descPostfix).c_str(), test.shaderType, test.testType, test.errorType, GL_SAMPLER_3D)); // Negative image binding tests negativeImage2dBindingTestGroup->addChild(new ImageBindingNegativeCase(m_context, test.name.c_str(), ("Invalid image2d layout binding using " + test.descPostfix).c_str(), test.shaderType, test.testType, test.errorType, GL_IMAGE_2D)); negativeImage3dBindingTestGroup->addChild(new ImageBindingNegativeCase(m_context, test.name.c_str(), ("Invalid image3d layout binding using " + test.descPostfix).c_str(), test.shaderType, test.testType, test.errorType, GL_IMAGE_3D)); // Negative UBO binding tests negativeUBOBindingTestGroup->addChild(new UBOBindingNegativeCase(m_context, test.name.c_str(), ("Invalid UBO layout binding using " + test.descPostfix).c_str(), test.shaderType, test.testType, test.errorType)); // Negative SSBO binding tests negativeSSBOBindingTestGroup->addChild(new SSBOBindingNegativeCase(m_context, test.name.c_str(), ("Invalid SSBO layout binding using " + test.descPostfix).c_str(), test.shaderType, test.testType, test.errorType)); } samplerBindingTestGroup->addChild(sampler2dBindingTestGroup); samplerBindingTestGroup->addChild(sampler3dBindingTestGroup); imageBindingTestGroup->addChild(image2dBindingTestGroup); imageBindingTestGroup->addChild(image3dBindingTestGroup); negativeSamplerBindingTestGroup->addChild(negativeSampler2dBindingTestGroup); negativeSamplerBindingTestGroup->addChild(negativeSampler3dBindingTestGroup); negativeImageBindingTestGroup->addChild(negativeImage2dBindingTestGroup); negativeImageBindingTestGroup->addChild(negativeImage3dBindingTestGroup); negativeBindingTestGroup->addChild(negativeSamplerBindingTestGroup); negativeBindingTestGroup->addChild(negativeUBOBindingTestGroup); negativeBindingTestGroup->addChild(negativeSSBOBindingTestGroup); negativeBindingTestGroup->addChild(negativeImageBindingTestGroup); addChild(samplerBindingTestGroup); addChild(UBOBindingTestGroup); addChild(SSBOBindingTestGroup); addChild(imageBindingTestGroup); addChild(negativeBindingTestGroup); } } // Functional } // gles31 } // deqp