/*------------------------------------------------------------------------- * 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 Pipeline State Query tests. *//*--------------------------------------------------------------------*/ #include "es31fProgramPipelineStateQueryTests.hpp" #include "es31fInfoLogQueryShared.hpp" #include "glsStateQueryUtil.hpp" #include "gluRenderContext.hpp" #include "gluCallLogWrapper.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_PIPELINE_INTEGER: return "get_program_pipelineiv"; default: DE_ASSERT(DE_FALSE); return DE_NULL; } } 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"; static const char* const s_computeSource = "#version 310 es\n" "layout (local_size_x = 1, local_size_y = 1) 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"; class ActiveProgramCase : public TestCase { public: ActiveProgramCase (Context& context, QueryType verifier, const char* name, const char* desc); IterateResult iterate (void); private: const QueryType m_verifier; }; ActiveProgramCase::ActiveProgramCase (Context& context, QueryType verifier, const char* name, const char* desc) : TestCase (context, name, desc) , m_verifier (verifier) { } ActiveProgramCase::IterateResult ActiveProgramCase::iterate (void) { const glu::ShaderProgram vtxProgram (m_context.getRenderContext(), glu::ProgramSources() << glu::ProgramSeparable(true) << glu::VertexSource(s_vertexSource)); const glu::ShaderProgram frgProgram (m_context.getRenderContext(), glu::ProgramSources() << glu::ProgramSeparable(true) << glu::FragmentSource(s_fragmentSource)); const glu::ProgramPipeline pipeline (m_context.getRenderContext()); glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "VtxProg", "Vertex program"); m_testCtx.getLog() << vtxProgram; } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "FrgProg", "Fragment program"); m_testCtx.getLog() << frgProgram; } if (!vtxProgram.isOk() || !frgProgram.isOk()) throw tcu::TestError("failed to build program"); gl.enableLogging(true); m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); gl.glBindProgramPipeline(pipeline.getPipeline()); gl.glUseProgramStages(pipeline.getPipeline(), GL_VERTEX_SHADER_BIT, vtxProgram.getProgram()); gl.glUseProgramStages(pipeline.getPipeline(), GL_FRAGMENT_SHADER_BIT, frgProgram.getProgram()); GLU_EXPECT_NO_ERROR(gl.glGetError(), "gen pipeline"); gl.glBindProgramPipeline(0); GLU_EXPECT_NO_ERROR(gl.glGetError(), "unbind pipeline"); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Initial", "Initial"); verifyStatePipelineInteger(result, gl, pipeline.getPipeline(), GL_ACTIVE_PROGRAM, 0, m_verifier); } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Set", "Set"); gl.glActiveShaderProgram(pipeline.getPipeline(), frgProgram.getProgram()); GLU_EXPECT_NO_ERROR(gl.glGetError(), "gen pipeline"); verifyStatePipelineInteger(result, gl, pipeline.getPipeline(), GL_ACTIVE_PROGRAM, (int)frgProgram.getProgram(), m_verifier); } result.setTestContextResult(m_testCtx); return STOP; } class PipelineProgramCase : public TestCase { public: PipelineProgramCase (Context& context, QueryType verifier, const char* name, const char* desc, glw::GLenum stage); IterateResult iterate (void); private: const QueryType m_verifier; const glw::GLenum m_targetStage; }; PipelineProgramCase::PipelineProgramCase (Context& context, QueryType verifier, const char* name, const char* desc, glw::GLenum stage) : TestCase (context, name, desc) , m_verifier (verifier) , m_targetStage (stage) { } PipelineProgramCase::IterateResult PipelineProgramCase::iterate (void) { glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); const int stageBit = (m_targetStage == GL_VERTEX_SHADER) ? (GL_VERTEX_SHADER_BIT) : (m_targetStage == GL_FRAGMENT_SHADER) ? (GL_FRAGMENT_SHADER_BIT) : (GL_COMPUTE_SHADER_BIT); glu::ProgramSources sources; if (m_targetStage == GL_VERTEX_SHADER) sources << glu::ProgramSeparable(true) << glu::VertexSource(s_vertexSource); else if (m_targetStage == GL_FRAGMENT_SHADER) sources << glu::ProgramSeparable(true) << glu::FragmentSource(s_fragmentSource); else if (m_targetStage == GL_COMPUTE_SHADER) sources << glu::ProgramSeparable(true) << glu::ComputeSource(s_computeSource); else DE_ASSERT(false); gl.enableLogging(true); { glu::ShaderProgram program(m_context.getRenderContext(), sources); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "program", "Program"); m_testCtx.getLog() << program; } if (!program.isOk()) throw tcu::TestError("failed to build program"); { const tcu::ScopedLogSection section (m_testCtx.getLog(), "Initial", "Initial"); glu::ProgramPipeline pipeline (m_context.getRenderContext()); gl.glBindProgramPipeline(pipeline.getPipeline()); GLU_EXPECT_NO_ERROR(gl.glGetError(), "setup pipeline"); verifyStatePipelineInteger(result, gl, pipeline.getPipeline(), m_targetStage, 0, m_verifier); } { const tcu::ScopedLogSection section (m_testCtx.getLog(), "Set", "Set"); glu::ProgramPipeline pipeline (m_context.getRenderContext()); gl.glBindProgramPipeline(pipeline.getPipeline()); gl.glUseProgramStages(pipeline.getPipeline(), stageBit, program.getProgram()); GLU_EXPECT_NO_ERROR(gl.glGetError(), "setup pipeline"); verifyStatePipelineInteger(result, gl, pipeline.getPipeline(), m_targetStage, program.getProgram(), m_verifier); } } result.setTestContextResult(m_testCtx); return STOP; } class ValidateStatusCase : public TestCase { public: ValidateStatusCase (Context& context, QueryType verifier, const char* name, const char* desc); IterateResult iterate (void); private: const QueryType m_verifier; }; ValidateStatusCase::ValidateStatusCase (Context& context, QueryType verifier, const char* name, const char* desc) : TestCase (context, name, desc) , m_verifier (verifier) { } ValidateStatusCase::IterateResult ValidateStatusCase::iterate (void) { glu::ShaderProgram vtxProgram (m_context.getRenderContext(), glu::ProgramSources() << glu::ProgramSeparable(true) << glu::VertexSource(s_vertexSource)); glu::ShaderProgram frgProgram (m_context.getRenderContext(), glu::ProgramSources() << glu::ProgramSeparable(true) << glu::FragmentSource(s_fragmentSource)); glu::ProgramPipeline pipeline (m_context.getRenderContext()); glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "VtxProg", "Vertex program"); m_testCtx.getLog() << vtxProgram; } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "FrgProg", "Fragment program"); m_testCtx.getLog() << frgProgram; } if (!vtxProgram.isOk() || !frgProgram.isOk()) throw tcu::TestError("failed to build program"); gl.enableLogging(true); gl.glBindProgramPipeline(pipeline.getPipeline()); gl.glUseProgramStages(pipeline.getPipeline(), GL_VERTEX_SHADER_BIT, vtxProgram.getProgram()); gl.glUseProgramStages(pipeline.getPipeline(), GL_FRAGMENT_SHADER_BIT, frgProgram.getProgram()); GLU_EXPECT_NO_ERROR(gl.glGetError(), "gen pipeline"); gl.glBindProgramPipeline(0); GLU_EXPECT_NO_ERROR(gl.glGetError(), "unbind pipeline"); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Initial", "Initial"); verifyStatePipelineInteger(result, gl, pipeline.getPipeline(), GL_VALIDATE_STATUS, 0, m_verifier); } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Set", "Validate"); gl.glValidateProgramPipeline(pipeline.getPipeline()); GLU_EXPECT_NO_ERROR(gl.glGetError(), "gen pipeline"); verifyStatePipelineInteger(result, gl, pipeline.getPipeline(), GL_VALIDATE_STATUS, GL_TRUE, m_verifier); } result.setTestContextResult(m_testCtx); return STOP; } class InfoLogCase : public TestCase { public: InfoLogCase (Context& context, const char* name, const char* desc); IterateResult iterate (void); }; InfoLogCase::InfoLogCase (Context& context, const char* name, const char* desc) : TestCase(context, name, desc) { } InfoLogCase::IterateResult InfoLogCase::iterate (void) { using gls::StateQueryUtil::StateQueryMemoryWriteGuard; static const char* const s_incompatibleFragmentSource = "#version 310 es\n" "in mediump vec2 v_colorB;\n" "in mediump vec2 v_colorC;\n" "layout(location=0) out highp vec4 o_color;\n" "void main()\n" "{\n" " o_color = v_colorB.xxyy + v_colorC.yyxy;\n" "}\n"; glu::ShaderProgram vtxProgram (m_context.getRenderContext(), glu::ProgramSources() << glu::ProgramSeparable(true) << glu::VertexSource(s_vertexSource)); glu::ShaderProgram frgProgram (m_context.getRenderContext(), glu::ProgramSources() << glu::ProgramSeparable(true) << glu::FragmentSource(s_incompatibleFragmentSource)); glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "VtxProg", "Vertex program"); m_testCtx.getLog() << vtxProgram; } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "FrgProg", "Fragment program"); m_testCtx.getLog() << frgProgram; } if (!vtxProgram.isOk() || !frgProgram.isOk()) throw tcu::TestError("failed to build program"); gl.enableLogging(true); { const tcu::ScopedLogSection section (m_testCtx.getLog(), "Initial", "Initial"); glu::ProgramPipeline pipeline (m_context.getRenderContext()); std::string buf (3, 'X'); int written = -1; verifyStatePipelineInteger(result, gl, pipeline.getPipeline(), GL_INFO_LOG_LENGTH, 0, QUERY_PIPELINE_INTEGER); gl.glGetProgramPipelineInfoLog(pipeline.getPipeline(), 2, &written, &buf[0]); GLU_EXPECT_NO_ERROR(gl.glGetError(), "query log"); if (written == -1) result.fail("'length' was not written to"); else if (written != 0) result.fail("'length' was not 0"); else if (buf[0] != '\0') result.fail("log was not 0-sized null-terminated string"); else if (buf[1] != 'X' || buf[2] != 'X') result.fail("buffer after returned length modified"); } { const tcu::ScopedLogSection superSection (m_testCtx.getLog(), "ValidationFail", "Failed validation"); glu::ProgramPipeline pipeline (m_context.getRenderContext()); StateQueryMemoryWriteGuard<glw::GLint> logLen; gl.glBindProgramPipeline(pipeline.getPipeline()); gl.glUseProgramStages(pipeline.getPipeline(), GL_VERTEX_SHADER_BIT, vtxProgram.getProgram()); gl.glUseProgramStages(pipeline.getPipeline(), GL_FRAGMENT_SHADER_BIT, frgProgram.getProgram()); GLU_EXPECT_NO_ERROR(gl.glGetError(), "gen pipeline"); gl.glBindProgramPipeline(0); GLU_EXPECT_NO_ERROR(gl.glGetError(), "unbind pipeline"); gl.glValidateProgramPipeline(pipeline.getPipeline()); GLU_EXPECT_NO_ERROR(gl.glGetError(), "gen pipeline"); gl.glGetProgramPipelineiv(pipeline.getPipeline(), GL_INFO_LOG_LENGTH, &logLen); GLU_EXPECT_NO_ERROR(gl.glGetError(), "get INFO_LOG_LENGTH"); if (logLen.verifyValidity(result)) verifyInfoLogQuery(result, gl, logLen, pipeline.getPipeline(), &glu::CallLogWrapper::glGetProgramPipelineInfoLog, "glGetProgramPipelineInfoLog"); } result.setTestContextResult(m_testCtx); return STOP; } } // anonymous ProgramPipelineStateQueryTests::ProgramPipelineStateQueryTests (Context& context) : TestCaseGroup(context, "program_pipeline", "Program Pipeline State Query tests") { } ProgramPipelineStateQueryTests::~ProgramPipelineStateQueryTests (void) { } void ProgramPipelineStateQueryTests::init (void) { static const QueryType verifiers[] = { QUERY_PIPELINE_INTEGER, }; #define FOR_EACH_VERIFIER(X) \ for (int verifierNdx = 0; verifierNdx < DE_LENGTH_OF_ARRAY(verifiers); ++verifierNdx) \ { \ const char* verifierSuffix = getVerifierSuffix(verifiers[verifierNdx]); \ const QueryType verifier = verifiers[verifierNdx]; \ this->addChild(X); \ } FOR_EACH_VERIFIER(new ActiveProgramCase(m_context, verifier, (std::string("active_program_") + verifierSuffix).c_str(), "Test ACTIVE_PROGRAM")); FOR_EACH_VERIFIER(new PipelineProgramCase(m_context, verifier, (std::string("vertex_shader_") + verifierSuffix).c_str(), "Test VERTEX_SHADER", GL_VERTEX_SHADER)); FOR_EACH_VERIFIER(new PipelineProgramCase(m_context, verifier, (std::string("fragment_shader_") + verifierSuffix).c_str(), "Test FRAGMENT_SHADER", GL_FRAGMENT_SHADER)); FOR_EACH_VERIFIER(new PipelineProgramCase(m_context, verifier, (std::string("compute_shader_") + verifierSuffix).c_str(), "Test COMPUTE_SHADER", GL_COMPUTE_SHADER)); FOR_EACH_VERIFIER(new ValidateStatusCase(m_context, verifier, (std::string("validate_status_") + verifierSuffix).c_str(), "Test VALIDATE_STATUS")); #undef FOR_EACH_VERIFIER this->addChild(new InfoLogCase(m_context, "info_log", "Test info log")); } } // Functional } // gles31 } // deqp