/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.1 Module * ------------------------------------------------- * * Copyright 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Program State Query tests. *//*--------------------------------------------------------------------*/ #include "es31fProgramStateQueryTests.hpp" #include "es31fInfoLogQueryShared.hpp" #include "glsStateQueryUtil.hpp" #include "gluRenderContext.hpp" #include "gluCallLogWrapper.hpp" #include "gluContextInfo.hpp" #include "gluObjectWrapper.hpp" #include "gluShaderProgram.hpp" #include "glwFunctions.hpp" #include "glwEnums.hpp" namespace deqp { namespace gles31 { namespace Functional { namespace { using namespace gls::StateQueryUtil; static const char* getVerifierSuffix (QueryType type) { switch (type) { case QUERY_PROGRAM_INTEGER_VEC3: case QUERY_PROGRAM_INTEGER: return "get_programiv"; default: DE_ASSERT(DE_FALSE); return DE_NULL; } } class ProgramSeparableCase : public TestCase { public: ProgramSeparableCase (Context& context, QueryType verifier, const char* name, const char* desc); IterateResult iterate (void); private: const QueryType m_verifier; }; ProgramSeparableCase::ProgramSeparableCase (Context& context, QueryType verifier, const char* name, const char* desc) : TestCase (context, name, desc) , m_verifier (verifier) { } ProgramSeparableCase::IterateResult ProgramSeparableCase::iterate (void) { static const char* const s_vertexSource = "#version 310 es\n" "out highp vec4 v_color;\n" "void main()\n" "{\n" " gl_Position = vec4(float(gl_VertexID) * 0.5, float(gl_VertexID+1) * 0.5, 0.0, 1.0);\n" " v_color = vec4(float(gl_VertexID), 1.0, 0.0, 1.0);\n" "}\n"; static const char* const s_fragmentSource = "#version 310 es\n" "in highp vec4 v_color;\n" "layout(location=0) out highp vec4 o_color;\n" "void main()\n" "{\n" " o_color = v_color;\n" "}\n"; glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); glu::Shader vtxShader (m_context.getRenderContext(), glu::SHADERTYPE_VERTEX); glu::Shader frgShader (m_context.getRenderContext(), glu::SHADERTYPE_FRAGMENT); vtxShader.setSources(1, &s_vertexSource, DE_NULL); frgShader.setSources(1, &s_fragmentSource, DE_NULL); vtxShader.compile(); frgShader.compile(); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "VtxShader", "Vertex shader"); m_testCtx.getLog() << vtxShader; } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "FrgShader", "Fragment shader"); m_testCtx.getLog() << frgShader; } if (!vtxShader.getCompileStatus() || !frgShader.getCompileStatus()) throw tcu::TestError("failed to build shaders"); gl.enableLogging(true); { const tcu::ScopedLogSection section (m_testCtx.getLog(), "Initial", "Initial"); glu::Program program (m_context.getRenderContext()); verifyStateProgramInteger(result, gl, program.getProgram(), GL_PROGRAM_SEPARABLE, 0, m_verifier); } { const tcu::ScopedLogSection section (m_testCtx.getLog(), "SetFalse", "SetFalse"); glu::Program program (m_context.getRenderContext()); int linkStatus = 0; gl.glAttachShader(program.getProgram(), vtxShader.getShader()); gl.glAttachShader(program.getProgram(), frgShader.getShader()); gl.glProgramParameteri(program.getProgram(), GL_PROGRAM_SEPARABLE, GL_FALSE); gl.glLinkProgram(program.getProgram()); GLU_EXPECT_NO_ERROR(gl.glGetError(), "setup program"); gl.glGetProgramiv(program.getProgram(), GL_LINK_STATUS, &linkStatus); GLU_EXPECT_NO_ERROR(gl.glGetError(), "query link status"); if (linkStatus == GL_FALSE) throw tcu::TestError("failed to link program"); verifyStateProgramInteger(result, gl, program.getProgram(), GL_PROGRAM_SEPARABLE, 0, m_verifier); } { const tcu::ScopedLogSection section (m_testCtx.getLog(), "SetTrue", "SetTrue"); glu::Program program (m_context.getRenderContext()); int linkStatus = 0; gl.glAttachShader(program.getProgram(), vtxShader.getShader()); gl.glAttachShader(program.getProgram(), frgShader.getShader()); gl.glProgramParameteri(program.getProgram(), GL_PROGRAM_SEPARABLE, GL_TRUE); gl.glLinkProgram(program.getProgram()); GLU_EXPECT_NO_ERROR(gl.glGetError(), "setup program"); gl.glGetProgramiv(program.getProgram(), GL_LINK_STATUS, &linkStatus); GLU_EXPECT_NO_ERROR(gl.glGetError(), "query link status"); if (linkStatus == GL_FALSE) throw tcu::TestError("failed to link program"); verifyStateProgramInteger(result, gl, program.getProgram(), GL_PROGRAM_SEPARABLE, GL_TRUE, m_verifier); } result.setTestContextResult(m_testCtx); return STOP; } class ComputeWorkGroupSizeCase : public TestCase { public: ComputeWorkGroupSizeCase (Context& context, QueryType verifier, const char* name, const char* desc); IterateResult iterate (void); private: const QueryType m_verifier; }; ComputeWorkGroupSizeCase::ComputeWorkGroupSizeCase (Context& context, QueryType verifier, const char* name, const char* desc) : TestCase (context, name, desc) , m_verifier (verifier) { } ComputeWorkGroupSizeCase::IterateResult ComputeWorkGroupSizeCase::iterate (void) { static const char* const s_computeSource1D = "#version 310 es\n" "layout (local_size_x = 3) in;\n" "layout(binding = 0) buffer Output\n" "{\n" " highp float val;\n" "} sb_out;\n" "\n" "void main (void)\n" "{\n" " sb_out.val = 1.0;\n" "}\n"; static const char* const s_computeSource2D = "#version 310 es\n" "layout (local_size_x = 3, local_size_y = 2) in;\n" "layout(binding = 0) buffer Output\n" "{\n" " highp float val;\n" "} sb_out;\n" "\n" "void main (void)\n" "{\n" " sb_out.val = 1.0;\n" "}\n"; static const char* const s_computeSource3D = "#version 310 es\n" "layout (local_size_x = 3, local_size_y = 2, local_size_z = 4) in;\n" "layout(binding = 0) buffer Output\n" "{\n" " highp float val;\n" "} sb_out;\n" "\n" "void main (void)\n" "{\n" " sb_out.val = 1.0;\n" "}\n"; glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); gl.enableLogging(true); { const tcu::ScopedLogSection section (m_testCtx.getLog(), "OneDimensional", "1D"); glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(s_computeSource1D)); m_testCtx.getLog() << program; if (!program.isOk()) throw tcu::TestError("failed to build program"); verifyStateProgramIntegerVec3(result, gl, program.getProgram(), GL_COMPUTE_WORK_GROUP_SIZE, tcu::IVec3(3, 1, 1), m_verifier); } { const tcu::ScopedLogSection section (m_testCtx.getLog(), "TwoDimensional", "2D"); glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(s_computeSource2D)); m_testCtx.getLog() << program; if (!program.isOk()) throw tcu::TestError("failed to build program"); verifyStateProgramIntegerVec3(result, gl, program.getProgram(), GL_COMPUTE_WORK_GROUP_SIZE, tcu::IVec3(3, 2, 1), m_verifier); } { const tcu::ScopedLogSection section (m_testCtx.getLog(), "TreeDimensional", "3D"); glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(s_computeSource3D)); m_testCtx.getLog() << program; if (!program.isOk()) throw tcu::TestError("failed to build program"); verifyStateProgramIntegerVec3(result, gl, program.getProgram(), GL_COMPUTE_WORK_GROUP_SIZE, tcu::IVec3(3, 2, 4), m_verifier); } result.setTestContextResult(m_testCtx); return STOP; } class ActiveAtomicCounterBuffersCase : public TestCase { public: ActiveAtomicCounterBuffersCase (Context& context, QueryType verifier, const char* name, const char* desc); IterateResult iterate (void); private: const QueryType m_verifier; }; ActiveAtomicCounterBuffersCase::ActiveAtomicCounterBuffersCase (Context& context, QueryType verifier, const char* name, const char* desc) : TestCase (context, name, desc) , m_verifier (verifier) { } ActiveAtomicCounterBuffersCase::IterateResult ActiveAtomicCounterBuffersCase::iterate (void) { static const char* const s_computeSource0 = "#version 310 es\n" "layout (local_size_x = 3) in;\n" "layout(binding = 0) buffer Output\n" "{\n" " highp float val;\n" "} sb_out;\n" "\n" "void main (void)\n" "{\n" " sb_out.val = 1.0;\n" "}\n"; static const char* const s_computeSource1 = "#version 310 es\n" "layout (local_size_x = 3) in;\n" "layout(binding = 0) uniform highp atomic_uint u_counters[2];\n" "layout(binding = 0) buffer Output\n" "{\n" " highp float val;\n" "} sb_out;\n" "\n" "void main (void)\n" "{\n" " sb_out.val = float(atomicCounterIncrement(u_counters[0])) + float(atomicCounterIncrement(u_counters[1]));\n" "}\n"; glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); gl.enableLogging(true); { const tcu::ScopedLogSection section (m_testCtx.getLog(), "Initial", "Initial"); glu::Program program (m_context.getRenderContext()); verifyStateProgramInteger(result, gl, program.getProgram(), GL_ACTIVE_ATOMIC_COUNTER_BUFFERS, 0, m_verifier); } { const tcu::ScopedLogSection section (m_testCtx.getLog(), "NoBuffers", "No buffers"); glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(s_computeSource0)); m_testCtx.getLog() << program; if (!program.isOk()) throw tcu::TestError("failed to build program"); verifyStateProgramInteger(result, gl, program.getProgram(), GL_ACTIVE_ATOMIC_COUNTER_BUFFERS, 0, m_verifier); } { const tcu::ScopedLogSection section (m_testCtx.getLog(), "OneBuffer", "One buffer"); glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources() << glu::ComputeSource(s_computeSource1)); m_testCtx.getLog() << program; if (!program.isOk()) throw tcu::TestError("failed to build program"); verifyStateProgramInteger(result, gl, program.getProgram(), GL_ACTIVE_ATOMIC_COUNTER_BUFFERS, 1, m_verifier); } result.setTestContextResult(m_testCtx); return STOP; } class ProgramLogCase : public TestCase { public: enum BuildErrorType { BUILDERROR_VERTEX_FRAGMENT = 0, BUILDERROR_COMPUTE, BUILDERROR_GEOMETRY, BUILDERROR_TESSELLATION, }; ProgramLogCase (Context& ctx, const char* name, const char* desc, BuildErrorType errorType); private: void init (void); IterateResult iterate (void); glu::ProgramSources getProgramSources (void) const; const BuildErrorType m_buildErrorType; }; ProgramLogCase::ProgramLogCase (Context& ctx, const char* name, const char* desc, BuildErrorType errorType) : TestCase (ctx, name, desc) , m_buildErrorType (errorType) { } void ProgramLogCase::init (void) { switch (m_buildErrorType) { case BUILDERROR_VERTEX_FRAGMENT: case BUILDERROR_COMPUTE: break; case BUILDERROR_GEOMETRY: if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")) throw tcu::NotSupportedError("Test requires GL_EXT_geometry_shader extension"); break; case BUILDERROR_TESSELLATION: if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader")) throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension"); break; default: DE_ASSERT(false); break; } } ProgramLogCase::IterateResult ProgramLogCase::iterate (void) { using gls::StateQueryUtil::StateQueryMemoryWriteGuard; tcu::ResultCollector result (m_testCtx.getLog()); glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); glu::ShaderProgram program (m_context.getRenderContext(), getProgramSources()); StateQueryMemoryWriteGuard<glw::GLint> logLen; gl.enableLogging(true); m_testCtx.getLog() << tcu::TestLog::Message << "Trying to link a broken program." << tcu::TestLog::EndMessage; gl.glGetProgramiv(program.getProgram(), GL_INFO_LOG_LENGTH, &logLen); logLen.verifyValidity(result); if (logLen.verifyValidity(result)) verifyInfoLogQuery(result, gl, logLen, program.getProgram(), &glu::CallLogWrapper::glGetProgramInfoLog, "glGetProgramInfoLog"); result.setTestContextResult(m_testCtx); return STOP; } glu::ProgramSources ProgramLogCase::getProgramSources (void) const { switch (m_buildErrorType) { case BUILDERROR_VERTEX_FRAGMENT: return glu::ProgramSources() << glu::VertexSource("#version 310 es\n" "in highp vec4 a_pos;\n" "uniform highp vec4 u_uniform;\n" "void main()\n" "{\n" " gl_Position = a_pos + u_uniform;\n" "}\n") << glu::FragmentSource("#version 310 es\n" "in highp vec4 v_missingVar;\n" "uniform highp int u_uniform;\n" "layout(location = 0) out mediump vec4 fragColor;\n" "void main()\n" "{\n" " fragColor = v_missingVar + vec4(float(u_uniform));\n" "}\n"); case BUILDERROR_COMPUTE: return glu::ProgramSources() << glu::ComputeSource("#version 310 es\n" "layout (binding = 0) buffer IOBuffer { highp float buf_var; };\n" "uniform highp vec4 u_uniform;\n" "void main()\n" "{\n" " buf_var = u_uniform.x;\n" "}\n"); case BUILDERROR_GEOMETRY: return glu::ProgramSources() << glu::VertexSource("#version 310 es\n" "in highp vec4 a_pos;\n" "uniform highp vec4 u_uniform;\n" "void main()\n" "{\n" " gl_Position = a_pos + u_uniform;\n" "}\n") << glu::GeometrySource("#version 310 es\n" "#extension GL_EXT_geometry_shader : require\n" "layout(triangles) in;\n" "layout(max_vertices=1, points) out;\n" "in highp vec4 v_missingVar[];\n" "uniform highp int u_uniform;\n" "void main()\n" "{\n" " gl_Position = gl_in[0].gl_Position + v_missingVar[2] + vec4(float(u_uniform));\n" " EmitVertex();\n" "}\n") << glu::FragmentSource("#version 310 es\n" "layout(location = 0) out mediump vec4 fragColor;\n" "void main()\n" "{\n" " fragColor = vec4(1.0);\n" "}\n"); case BUILDERROR_TESSELLATION: return glu::ProgramSources() << glu::VertexSource("#version 310 es\n" "in highp vec4 a_pos;\n" "void main()\n" "{\n" " gl_Position = a_pos;\n" "}\n") << glu::TessellationControlSource("#version 310 es\n" "#extension GL_EXT_tessellation_shader : require\n" "layout(vertices=2) out;" "patch out highp vec2 vp_var;\n" "void main()\n" "{\n" " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position\n" " gl_TessLevelOuter[0] = 0.8;\n" " gl_TessLevelOuter[1] = 0.8;\n" " if (gl_InvocationID == 0)\n" " vp_var = gl_in[gl_InvocationID].gl_Position.xy;\n" "}\n") << glu::TessellationEvaluationSource("#version 310 es\n" "#extension GL_EXT_tessellation_shader : require\n" "layout(isolines) in;" "in highp float vp_var[];\n" "void main()\n" "{\n" " gl_Position = gl_in[gl_InvocationID].gl_Position + vec4(vp_var[1]);\n" "}\n") << glu::FragmentSource("#version 310 es\n" "layout(location = 0) out mediump vec4 fragColor;\n" "void main()\n" "{\n" " fragColor = vec4(1.0);\n" "}\n"); default: DE_ASSERT(false); return glu::ProgramSources(); } } } // anonymous ProgramStateQueryTests::ProgramStateQueryTests (Context& context) : TestCaseGroup(context, "program", "Program State Query tests") { } ProgramStateQueryTests::~ProgramStateQueryTests (void) { } void ProgramStateQueryTests::init (void) { static const QueryType intVerifiers[] = { QUERY_PROGRAM_INTEGER, }; static const QueryType intVec3Verifiers[] = { QUERY_PROGRAM_INTEGER_VEC3, }; #define FOR_EACH_INT_VERIFIER(X) \ for (int verifierNdx = 0; verifierNdx < DE_LENGTH_OF_ARRAY(intVerifiers); ++verifierNdx) \ { \ const char* verifierSuffix = getVerifierSuffix(intVerifiers[verifierNdx]); \ const QueryType verifier = intVerifiers[verifierNdx]; \ this->addChild(X); \ } #define FOR_EACH_VEC_VERIFIER(X) \ for (int verifierNdx = 0; verifierNdx < DE_LENGTH_OF_ARRAY(intVec3Verifiers); ++verifierNdx) \ { \ const char* verifierSuffix = getVerifierSuffix(intVec3Verifiers[verifierNdx]); \ const QueryType verifier = intVec3Verifiers[verifierNdx]; \ this->addChild(X); \ } FOR_EACH_INT_VERIFIER(new ProgramSeparableCase (m_context, verifier, (std::string("program_separable_") + verifierSuffix).c_str(), "Test PROGRAM_SEPARABLE")); FOR_EACH_VEC_VERIFIER(new ComputeWorkGroupSizeCase (m_context, verifier, (std::string("compute_work_group_size_") + verifierSuffix).c_str(), "Test COMPUTE_WORK_GROUP_SIZE")); FOR_EACH_INT_VERIFIER(new ActiveAtomicCounterBuffersCase (m_context, verifier, (std::string("active_atomic_counter_buffers_") + verifierSuffix).c_str(), "Test ACTIVE_ATOMIC_COUNTER_BUFFERS")); #undef FOR_EACH_INT_VERIFIER #undef FOR_EACH_VEC_VERIFIER // program info log tests // \note, there exists similar tests in gles3 module. However, the gles31 could use a different // shader compiler with different INFO_LOG bugs. { static const struct { const char* caseName; ProgramLogCase::BuildErrorType caseType; } shaderTypes[] = { { "info_log_vertex_fragment_link_fail", ProgramLogCase::BUILDERROR_VERTEX_FRAGMENT }, { "info_log_compute_link_fail", ProgramLogCase::BUILDERROR_COMPUTE }, { "info_log_geometry_link_fail", ProgramLogCase::BUILDERROR_GEOMETRY }, { "info_log_tessellation_link_fail", ProgramLogCase::BUILDERROR_TESSELLATION }, }; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(shaderTypes); ++ndx) addChild(new ProgramLogCase(m_context, shaderTypes[ndx].caseName, "", shaderTypes[ndx].caseType)); } } } // Functional } // gles31 } // deqp