/*------------------------------------------------------------------------- * OpenGL Conformance Test Suite * ----------------------------- * * Copyright (c) 2015-2016 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /*! * \file * \brief */ /*-------------------------------------------------------------------*/ /* Includes. */ #include "gl4cConditionalRenderInvertedTests.hpp" #include "gluContextInfo.hpp" #include "gluDefs.hpp" #include "gluRenderContext.hpp" #include "gluStrUtil.hpp" #include "tcuTestLog.hpp" /******************************** Test Group Implementation ********************************/ /** @brief Context Flush Control tests group constructor. * * @param [in] context OpenGL context. */ gl4cts::ConditionalRenderInverted::Tests::Tests(deqp::Context& context) : TestCaseGroup(context, "conditional_render_inverted", "Conditional Render Inverted Test Suite") { /* Intentionally left blank */ } /** @brief Context Flush Control tests initializer. */ void gl4cts::ConditionalRenderInverted::Tests::init() { addChild(new gl4cts::ConditionalRenderInverted::CoverageTest(m_context)); addChild(new gl4cts::ConditionalRenderInverted::FunctionalTest(m_context)); } /******************************** Coverage Tests Implementation ********************************/ /** @brief API coverage tests constructor. * * @param [in] context OpenGL context. */ gl4cts::ConditionalRenderInverted::CoverageTest::CoverageTest(deqp::Context& context) : deqp::TestCase(context, "coverage", "Conditional Render Inverted Coverage Test"), m_qo_id(0) { /* Intentionally left blank. */ } /** @brief Iterate API coverage tests. * * @return Iteration result. */ tcu::TestNode::IterateResult gl4cts::ConditionalRenderInverted::CoverageTest::iterate() { /* OpenGL support query. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_conditional_render_inverted = m_context.getContextInfo().isExtensionSupported("GL_ARB_conditional_render_inverted"); /* Running tests. */ bool is_ok = true; bool is_error = false; /* This test should only be executed if we're running a GL4.5 context or related extension is available */ try { if (is_at_least_gl_45 || is_arb_conditional_render_inverted) { /* Prepare common objects. */ createQueryObject(); /* Test cases. */ static const glw::GLenum modes[] = { GL_QUERY_WAIT_INVERTED, GL_QUERY_NO_WAIT_INVERTED, GL_QUERY_BY_REGION_WAIT_INVERTED, GL_QUERY_BY_REGION_NO_WAIT_INVERTED }; static const glw::GLuint modes_count = sizeof(modes) / sizeof(modes[0]); /* Iterate over the test cases. */ for (glw::GLuint i = 0; i < modes_count; ++i) { is_ok &= test(modes[i]); } } } catch (...) { is_ok = false; is_error = true; } /* Cleanup. */ clean(); /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Internal error has occured during Conditional Render Inverted Coverage Test." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Test error."); } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "The Conditional Render Inverted Coverage Test has failed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Create query object. */ void gl4cts::ConditionalRenderInverted::CoverageTest::createQueryObject() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Create valid query object. */ gl.genQueries(1, &m_qo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenQueries() call failed."); gl.beginQuery(GL_SAMPLES_PASSED, m_qo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginQuery() call failed."); gl.endQuery(GL_SAMPLES_PASSED); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQuery() call failed."); } /** @brief Clean query object and error values. */ void gl4cts::ConditionalRenderInverted::CoverageTest::clean() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Clean query object. */ gl.deleteQueries(1, &m_qo_id); m_qo_id = 0; /* Make sure no errors are left. */ while (gl.getError()) ; } /** @brief Test that glBeginConditionalRender accept mode. * * @param [in] mode Render condition mode. * * @return True if glBeginConditionalRender did not generate an error, false otherwise. */ bool gl4cts::ConditionalRenderInverted::CoverageTest::test(glw::GLenum mode) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Default return value;*/ bool is_no_error = true; /* Test. */ gl.beginConditionalRender(m_qo_id, mode); while (GL_NO_ERROR != gl.getError()) { is_no_error = false; } /* Clean up. */ if (is_no_error) { gl.endConditionalRender(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndConditionalRender() call failed."); } /* Logging. */ if (!is_no_error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glBeginConditionalRender failed when used with mode " << Utilities::modeToChars(mode) << "." << tcu::TestLog::EndMessage; } /* Return test result. */ return is_no_error; } /******************************** Functional Test Implementation ********************************/ /** @brief Functional test constructor. * * @param [in] context OpenGL context. */ gl4cts::ConditionalRenderInverted::FunctionalTest::FunctionalTest(deqp::Context& context) : deqp::TestCase(context, "functional", "Conditional Render Inverted Functional Test") , m_fbo_id(0) , m_rbo_id(0) , m_vao_id(0) , m_po_id(0) , m_qo_id(0) { /* Intentionally left blank. */ } /** @brief Iterate Functional test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult gl4cts::ConditionalRenderInverted::FunctionalTest::iterate() { /* OpenGL support query. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_conditional_render_inverted = m_context.getContextInfo().isExtensionSupported("GL_ARB_conditional_render_inverted"); /* Running tests. */ bool is_ok = true; bool is_error = false; /* This test should only be executed if we're running a GL4.5 context or related extension is available */ try { if (is_at_least_gl_45 || is_arb_conditional_render_inverted) { /* Test cases. */ static const bool render_cases[] = { false, true }; static const glw::GLuint render_cases_count = sizeof(render_cases) / sizeof(render_cases[0]); static const glw::GLenum query_cases[] = { GL_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED }; static const glw::GLuint query_cases_count = sizeof(query_cases) / sizeof(query_cases[0]); static const glw::GLenum modes[] = { GL_QUERY_WAIT_INVERTED, GL_QUERY_NO_WAIT_INVERTED, GL_QUERY_BY_REGION_WAIT_INVERTED, GL_QUERY_BY_REGION_NO_WAIT_INVERTED }; static const glw::GLuint modes_count = sizeof(modes) / sizeof(modes[0]); /* Creating common objects. */ createProgram(); createView(); createVertexArrayObject(); /* Iterating over test cases. */ for (glw::GLuint i = 0; i < render_cases_count; ++i) { for (glw::GLuint j = 0; j < query_cases_count; ++j) { for (glw::GLuint k = 0; k < modes_count; ++k) { createQueryObject(); setupColor(1.f); setupPassSwitch(render_cases[i]); clearView(); draw(false, query_cases[j]); if (render_cases[i] == fragmentsPassed()) { setupColor(0.f); setupPassSwitch(true); draw(true, modes[k]); glw::GLfloat expected_value = (render_cases[i]) ? 1.f : 0.f; glw::GLfloat resulted_value = readPixel(); if (de::abs(expected_value - resulted_value) > 0.0078125f /* Precission (1/128) */) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "The functional test's expected value (" << expected_value << ") is different than resulted value (" << resulted_value << "). The tested mode was " << Utilities::modeToChars(modes[k]) << ". Query was done for target " << Utilities::queryTargetToChars(query_cases[j]) << ", and the test was prepared to " << ((render_cases[i]) ? "pass" : "discard") << " all fragments." << tcu::TestLog::EndMessage; is_ok = false; } } else { is_ok = false; } cleanQueryObject(); } } } } } catch (...) { is_ok = false; is_error = true; cleanQueryObject(); } /* Clean-up. */ cleanProgramViewAndVAO(); /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Internal error has occured during Conditional Render Inverted Functional Test." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Test error."); } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "The Conditional Render Inverted Functional Test has failed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail."); } } return STOP; } /** @brief Compile and link test's GLSL program. */ void gl4cts::ConditionalRenderInverted::FunctionalTest::createProgram() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); struct Shader { glw::GLchar const* const source; glw::GLenum const type; glw::GLuint id; } shader[] = { { s_vertex_shader, GL_VERTEX_SHADER, 0 }, { s_fragment_shader, GL_FRAGMENT_SHADER, 0 } }; glw::GLuint const shader_count = sizeof(shader) / sizeof(shader[0]); try { /* Create program. */ m_po_id = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram call failed."); /* Shader compilation. */ for (glw::GLuint i = 0; i < shader_count; ++i) { if (DE_NULL != shader[i].source) { shader[i].id = gl.createShader(shader[i].type); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader call failed."); gl.attachShader(m_po_id, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader call failed."); gl.shaderSource(shader[i].id, 1, &(shader[i].source), NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource call failed."); gl.compileShader(shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader call failed."); glw::GLint status = GL_FALSE; gl.getShaderiv(shader[i].id, GL_COMPILE_STATUS, &status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); if (GL_FALSE == status) { throw 0; } } } /* Link. */ gl.linkProgram(m_po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); glw::GLint status = GL_FALSE; gl.getProgramiv(m_po_id, GL_LINK_STATUS, &status); if (GL_TRUE == status) { for (glw::GLuint i = 0; i < shader_count; ++i) { if (shader[i].id) { gl.detachShader(m_po_id, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glDetachShader call failed."); } } } else { throw 0; } } catch (...) { if (m_po_id) { gl.deleteProgram(m_po_id); m_po_id = 0; } } for (glw::GLuint i = 0; i < shader_count; ++i) { if (0 != shader[i].id) { gl.deleteShader(shader[i].id); shader[i].id = 0; } } if (m_po_id) { gl.useProgram(m_po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); } } /** @brief Create and bind framebuffer with renderbuffer color attachment. */ void gl4cts::ConditionalRenderInverted::FunctionalTest::createView() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Prepare framebuffer. */ gl.clearColor(0.5f, 0.5f, 0.5f, 0.5f); GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor call failed."); gl.genFramebuffers(1, &m_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers call failed."); gl.genRenderbuffers(1, &m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers call failed."); gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer call failed."); gl.bindRenderbuffer(GL_RENDERBUFFER, m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer call failed."); gl.renderbufferStorage(GL_RENDERBUFFER, GL_R8, s_view_size, s_view_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage call failed."); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer call failed."); if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { throw 0; } gl.viewport(0, 0, s_view_size, s_view_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport call failed."); } /** @brief Create test's query object. */ void gl4cts::ConditionalRenderInverted::FunctionalTest::createQueryObject() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Create valid query object. */ gl.genQueries(1, &m_qo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenQueries() call failed."); } /** @brief Setup color uniform of the test's program. */ void gl4cts::ConditionalRenderInverted::FunctionalTest::setupColor(const glw::GLfloat red) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Fetch where to set. */ glw::GLuint location = gl.getUniformLocation(m_po_id, s_color_uniform_name); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformLocation() call failed."); /* Set. */ gl.uniform1f(location, red); GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1f() call failed."); } /** @brief Setup pass or discard switch uniform of the test's program. */ void gl4cts::ConditionalRenderInverted::FunctionalTest::setupPassSwitch(const bool shall_pass) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Fetch where to set. */ glw::GLuint location = gl.getUniformLocation(m_po_id, s_pass_switch_uniform_name); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformLocation() call failed."); /* Set. */ gl.uniform1i(location, (int)shall_pass); GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1f() call failed."); } /** @brief Draw full screen within query or conditional block. * * @param [in] conditional_or_query_draw If true draw will be done in conditional rendering block, otherwise in query block. * @param [in] condition_mode_or_query_target The param needed by query or conditional block - target or mode. */ void gl4cts::ConditionalRenderInverted::FunctionalTest::draw(const bool conditional_or_query_draw, const glw::GLenum condition_mode_or_query_target) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (conditional_or_query_draw) { gl.beginConditionalRender(m_qo_id, condition_mode_or_query_target); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginConditionalRender() call failed."); } else { gl.beginQuery(condition_mode_or_query_target, m_qo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginQuery() call failed."); } gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); if (conditional_or_query_draw) { gl.endConditionalRender(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndConditionalRender() call failed."); } else { gl.endQuery(condition_mode_or_query_target); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQuery() call failed."); } } /** @brief Check if any fragments have passed rendering. * * @return True if any sample passed, false otherwise. */ bool gl4cts::ConditionalRenderInverted::FunctionalTest::fragmentsPassed() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Fetch result. */ glw::GLint result = -1; gl.getQueryObjectiv(m_qo_id, GL_QUERY_RESULT, &result); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryObjectiv() call failed."); /* Check for unusual errors. */ if (-1 == result) { throw 0; } /* Return results. */ return (result > 0); } /** @brief Read framebuffer's first pixel red component (left, bottom). * * @return Red value of the pixel. */ glw::GLfloat gl4cts::ConditionalRenderInverted::FunctionalTest::readPixel() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLfloat red = -1.f; gl.readPixels(0, 0, 1, 1, GL_RED, GL_FLOAT, &red); GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() call failed."); return red; } /** @brief Destroy test's query object. */ void gl4cts::ConditionalRenderInverted::FunctionalTest::cleanQueryObject() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Clean query object. */ if (m_qo_id) { gl.deleteQueries(1, &m_qo_id); m_qo_id = 0; } } /** @brief Create test's empty Vertex Array Object. */ void gl4cts::ConditionalRenderInverted::FunctionalTest::createVertexArrayObject() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Create and bind vertex array. */ gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() call failed."); gl.bindVertexArray(m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed."); } /** @brief Destroy test's Vertex Array Object. */ void gl4cts::ConditionalRenderInverted::FunctionalTest::cleanProgramViewAndVAO() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Deleting view. */ if (m_fbo_id) { gl.deleteFramebuffers(1, &m_fbo_id); m_fbo_id = 0; } if (m_rbo_id) { gl.deleteRenderbuffers(1, &m_rbo_id); m_rbo_id = 0; } if (m_vao_id) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } } /** @brief Destroy test's framebuffer with related objects. */ void gl4cts::ConditionalRenderInverted::FunctionalTest::clearView() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Clear screen. */ gl.clear(GL_COLOR_BUFFER_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glClear() call failed."); } const glw::GLchar gl4cts::ConditionalRenderInverted::FunctionalTest::s_vertex_shader[] = "#version 130\n" "\n" "void main()\n" "{\n" " switch(gl_VertexID % 4)\n" " {\n" " case 0:\n" " gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n" " break;\n" " case 1:\n" " gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n" " break;\n" " case 2:\n" " gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n" " break;\n" " case 3:\n" " gl_Position = vec4( 1.0, 1.0, 0.0, 1.0);\n" " break;\n" " }\n" "}\n"; const glw::GLchar gl4cts::ConditionalRenderInverted::FunctionalTest::s_fragment_shader[] = "#version 130\n" "\n" "uniform float color;\n" "uniform int shall_pass;\n" "\n" "out vec4 pixel;\n" "\n" "void main()\n" "{\n" " if(0 == shall_pass)\n" " {\n" " discard;\n" " }\n" "\n" " pixel = vec4(color);\n" "}\n"; const glw::GLchar gl4cts::ConditionalRenderInverted::FunctionalTest::s_color_uniform_name[] = "color"; const glw::GLchar gl4cts::ConditionalRenderInverted::FunctionalTest::s_pass_switch_uniform_name[] = "shall_pass"; const glw::GLuint gl4cts::ConditionalRenderInverted::FunctionalTest::s_view_size = 1; /******************************** Utilities Implementation ********************************/ /** @brief Return string representation of condional rendering mode. * * @param [in] mode Render condition mode. * * @return Constant C-String representation of mode. */ const glw::GLchar* gl4cts::ConditionalRenderInverted::Utilities::modeToChars(glw::GLenum mode) { /* Const name values. */ static const glw::GLchar* query_wait_inverted_mode_name = "GL_QUERY_WAIT_INVERTED"; static const glw::GLchar* query_no_wait_inverted_mode_name = "GL_QUERY_NO_WAIT_INVERTED"; static const glw::GLchar* query_by_region_wait_inverted_mode_name = "GL_QUERY_BY_REGION_WAIT_INVERTED"; static const glw::GLchar* query_by_region_no_wait_inverted_mode_name = "GL_QUERY_BY_REGION_NO_WAIT_INVERTED"; static const glw::GLchar* invalid_mode_name = "unknow mode"; /* Return proper value. */ if (GL_QUERY_WAIT_INVERTED == mode) { return query_wait_inverted_mode_name; } if (GL_QUERY_NO_WAIT_INVERTED == mode) { return query_no_wait_inverted_mode_name; } if (GL_QUERY_BY_REGION_WAIT_INVERTED == mode) { return query_by_region_wait_inverted_mode_name; } if (GL_QUERY_BY_REGION_NO_WAIT_INVERTED == mode) { return query_by_region_no_wait_inverted_mode_name; } /* If not, return invalid name. */ return invalid_mode_name; } /** @brief Return string representation of glBeginQuery's target. * * @param [in] mode Render condition mode. * * @return Constant C-String representation of mode. */ const glw::GLchar* gl4cts::ConditionalRenderInverted::Utilities::queryTargetToChars(glw::GLenum mode) { /* Const name values. */ static const glw::GLchar* any_samples_name = "GL_ANY_SAMPLES_PASSED"; static const glw::GLchar* samples_name = "GL_SAMPLES_PASSED"; static const glw::GLchar* invalid_target_name = "unknow mode"; /* Return proper value. */ if (GL_ANY_SAMPLES_PASSED == mode) { return any_samples_name; } if (GL_SAMPLES_PASSED == mode) { return samples_name; } /* If not, return invalid name. */ return invalid_target_name; }