/*------------------------------------------------------------------------- * 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 */ /*-------------------------------------------------------------------*/ /** */ /*! * \file gl4cDirectStateAccessQueriesTests.cpp * \brief Conformance tests for the Direct State Access feature functionality (Queries access part). */ /*-----------------------------------------------------------------------------------------------------------*/ /* Includes. */ #include "gl4cDirectStateAccessTests.hpp" #include "deSharedPtr.hpp" #include "gluContextInfo.hpp" #include "gluDefs.hpp" #include "gluPixelTransfer.hpp" #include "gluStrUtil.hpp" #include "tcuFuzzyImageCompare.hpp" #include "tcuImageCompare.hpp" #include "tcuRenderTarget.hpp" #include "tcuSurface.hpp" #include "tcuTestLog.hpp" #include "glw.h" #include "glwFunctions.hpp" /* Define OpenGL enumerations not available in framework. */ #ifndef GL_QUERY_TARGET #define GL_QUERY_TARGET 0x82EA #endif namespace gl4cts { namespace DirectStateAccess { namespace Queries { /******************************** Creation Test Implementation ********************************/ /** @brief Creation Test constructor. * * @param [in] context OpenGL context. */ CreationTest::CreationTest(deqp::Context& context) : deqp::TestCase(context, "queries_creation", "Query Objects Creation Test") { /* Intentionally left blank. */ } /** @brief Iterate Creation Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult CreationTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; /* Query targets */ static const glw::GLenum targets[] = { GL_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED_CONSERVATIVE, GL_TIME_ELAPSED, GL_TIMESTAMP, GL_PRIMITIVES_GENERATED, GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN }; static const glw::GLuint targets_count = sizeof(targets) / sizeof(targets[0]); /* Queries objects */ static const glw::GLuint queries_count = 2; glw::GLuint queries_legacy[queries_count] = {}; glw::GLuint queries_dsa[targets_count][queries_count] = {}; try { /* Check legacy state creation. */ gl.genQueries(queries_count, queries_legacy); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenQueries have failed"); for (glw::GLuint i = 0; i < queries_count; ++i) { if (gl.isQuery(queries_legacy[i])) { is_ok = false; /* Log. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "GenQueries has created default objects, but it should create only a names." << tcu::TestLog::EndMessage; } } /* Check direct state creation. */ for (glw::GLuint i = 0; i < targets_count; ++i) { gl.createQueries(targets[i], queries_count, queries_dsa[i]); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateQueries have failed"); for (glw::GLuint j = 0; j < queries_count; ++j) { if (!gl.isQuery(queries_dsa[i][j])) { is_ok = false; /* Log. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "CreateQueries has not created default objects." << tcu::TestLog::EndMessage; } } } } catch (...) { is_ok = false; is_error = true; } /* Cleanup. */ for (glw::GLuint j = 0; j < queries_count; ++j) { if (queries_legacy[j]) { gl.deleteQueries(1, &queries_legacy[j]); queries_legacy[j] = 0; } for (glw::GLuint i = 0; i < targets_count; ++i) { if (queries_dsa[i][j]) { gl.deleteQueries(1, &queries_dsa[i][j]); queries_dsa[i][j] = 0; } } } /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /******************************** Defaults Test Implementation ********************************/ /** @brief Defaults Test constructor. * * @param [in] context OpenGL context. */ DefaultsTest::DefaultsTest(deqp::Context& context) : deqp::TestCase(context, "queries_defaults", "Queries Defaults Test"), m_query_dsa(0) { /* Intentionally left blank. */ } /** @brief Iterate Defaults Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult DefaultsTest::iterate() { /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; bool is_error = false; /* Query targets. */ static const glw::GLenum targets[] = { GL_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED_CONSERVATIVE, GL_TIME_ELAPSED, GL_TIMESTAMP, GL_PRIMITIVES_GENERATED, GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN }; static const glw::GLchar* target_names[] = { "GL_SAMPLES_PASSED", "GL_ANY_SAMPLES_PASSED", "GL_ANY_SAMPLES_PASSED_CONSERVATIVE", "GL_TIME_ELAPSED", "GL_TIMESTAMP", "GL_PRIMITIVES_GENERATED", "GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN" }; static const glw::GLuint targets_count = sizeof(targets) / sizeof(targets[0]); try { /* Check direct state creation. */ for (glw::GLuint i = 0; i < targets_count; ++i) { prepare(targets[i]); is_ok &= testQueryParameter(GL_QUERY_RESULT, GL_FALSE, target_names[i]); is_ok &= testQueryParameter(GL_QUERY_RESULT_AVAILABLE, GL_TRUE, target_names[i]); clean(); } } catch (...) { is_ok = false; is_error = true; clean(); } /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Create Query Objects. * * @note The function may throw if unexpected error has occured. * * @return True if test succeeded, false otherwise. */ void DefaultsTest::prepare(const glw::GLenum target) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Query object creation */ gl.createQueries(target, 1, &m_query_dsa); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateTransformFeedbacks have failed"); } /** @brief Test Query Integer Parameter. * * @note The function may throw if unexpected error has occured. * * @param [in] pname Parameter name to be tested. * @param [in] expected_value Expected value for comparison. * @param [in] target_name Target name of the tested query object - for logging purposes. * * @return True if test succeeded, false otherwise. */ bool DefaultsTest::testQueryParameter(const glw::GLenum pname, const glw::GLuint expected_value, const glw::GLchar* target_name) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get data. */ glw::GLuint value = 0; gl.getQueryObjectuiv(m_query_dsa, pname, &value); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryObjectuiv have failed"); if (expected_value != value) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryObjectuiv of query object with target " << target_name << " with parameter " << pname << " has returned " << value << ", however " << expected_value << " was expected." << tcu::TestLog::EndMessage; return false; } return true; } /** @brief Release GL objects. */ void DefaultsTest::clean() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (m_query_dsa) { gl.deleteQueries(1, &m_query_dsa); m_query_dsa = 0; } } /******************************** Errors Test Implementation ********************************/ /** @brief Errors Test constructor. * * @param [in] context OpenGL context. */ ErrorsTest::ErrorsTest(deqp::Context& context) : deqp::TestCase(context, "queries_errors", "Queries Errors Test") , m_pGetQueryBufferObjectiv(DE_NULL) , m_pGetQueryBufferObjectuiv(DE_NULL) , m_pGetQueryBufferObjecti64v(DE_NULL) , m_pGetQueryBufferObjectui64v(DE_NULL) { /* Intentionally left blank. */ } /** @brief Iterate Errors Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult ErrorsTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Getting function pointers. */ m_pGetQueryBufferObjectiv = (PFNGLGETQUERYBUFFEROBJECT)gl.getQueryBufferObjectiv; m_pGetQueryBufferObjectuiv = (PFNGLGETQUERYBUFFEROBJECT)gl.getQueryBufferObjectuiv; m_pGetQueryBufferObjecti64v = (PFNGLGETQUERYBUFFEROBJECT)gl.getQueryBufferObjecti64v; m_pGetQueryBufferObjectui64v = (PFNGLGETQUERYBUFFEROBJECT)gl.getQueryBufferObjectui64v; /* Running tests. */ bool is_ok = true; bool is_error = false; try { if ((DE_NULL == m_pGetQueryBufferObjectiv) || (DE_NULL == m_pGetQueryBufferObjectuiv) || (DE_NULL == m_pGetQueryBufferObjecti64v) || (DE_NULL == m_pGetQueryBufferObjectui64v)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Test could not get the pointers for glGetQueryBufferObject* functions." << tcu::TestLog::EndMessage; throw 0; } is_ok &= testNegativeNumberOfObjects(); is_ok &= testInvalidTarget(); is_ok &= testInvalidQueryName(); is_ok &= testInvalidBufferName(); is_ok &= testInvalidParameterName(); is_ok &= testBufferOverflow(); is_ok &= testBufferNegativeOffset(); } catch (...) { is_ok = false; is_error = true; } /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Check that CreateQueries generates INVALID_VALUE * error if number of query objects to create is * negative. * * @return True if test succeded, false otherwise. */ bool ErrorsTest::testNegativeNumberOfObjects() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Test for each target. */ for (glw::GLuint i = 0; i < s_targets_count; ++i) { glw::GLuint query = 0; gl.createQueries(s_targets[i], -1, &query); /* Create negative number of queries. */ glw::GLenum error = gl.getError(); if (GL_INVALID_VALUE != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glCreateQueries called with target " << s_target_names[i] << " with negative number of objects to be created (-1) has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_VALUE was expected." << tcu::TestLog::EndMessage; if (query) { gl.deleteQueries(1, &query); while (error == gl.getError()) ; m_context.getTestContext().getLog() << tcu::TestLog::Message << "glCreateQueries called with target " << s_target_names[i] << " with negative number of objects to be created (-1) has created at least one object." << tcu::TestLog::EndMessage; } return false; } } return true; } /** @brief Check that CreateQueries generates INVALID_ENUM error if target is not * one of accepted values: * - SAMPLES_PASSED, * - ANY_SAMPLES_PASSED, * - ANY_SAMPLES_PASSED_CONSERVATIVE, * - TIME_ELAPSED, * - TIMESTAMP, * - PRIMITIVES_GENERATED or * - TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN. * * @return True if test succeded, false otherwise. */ bool ErrorsTest::testInvalidTarget() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Creating invalid target. */ glw::GLenum invalid_target = 0; while (isTarget(++invalid_target)) ; /* Test. */ glw::GLuint query = 0; gl.createQueries(invalid_target, 1, &query); /* Create negative number of queries. */ glw::GLenum error = gl.getError(); if (GL_INVALID_ENUM != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glCreateQueries called with invalid target (" << invalid_target << ") has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_ENUM was expected." << tcu::TestLog::EndMessage; if (query) { gl.deleteQueries(1, &query); while (error == gl.getError()) ; m_context.getTestContext().getLog() << tcu::TestLog::Message << "glCreateQueries called with invalid target (" << invalid_target << ") has created an object." << tcu::TestLog::EndMessage; } return false; } return true; } /** @brief Check that GetQueryBufferObjectiv, GetQueryBufferObjectuiv, * GetQueryBufferObjecti64v and GetQueryBufferObjectui64v generate * INVALID_OPERATION error if is not the name of a query object, or * if the query object named by is currently active. * * @return True if test succeded, false otherwise. */ bool ErrorsTest::testInvalidQueryName() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Creating invalid query name. */ glw::GLuint invalid_query = 0; /* Default result. */ bool is_ok = true; bool is_error = false; while (gl.isQuery(++invalid_query)) ; /* Test's objects. */ glw::GLuint buffer = 0; glw::GLuint query = 0; try { /* Creating buffer for the test. */ gl.genBuffers(1, &buffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers have failed"); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffers have failed"); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(glw::GLint64), DE_NULL, GL_DYNAMIC_COPY); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffers have failed"); /* Test invalid query object name (integer version). */ m_pGetQueryBufferObjectiv(invalid_query, buffer, GL_QUERY_RESULT, 0); glw::GLenum error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectiv called with invalid query name has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test invalid query object name (unsigned integer version). */ m_pGetQueryBufferObjectuiv(invalid_query, buffer, GL_QUERY_RESULT, 0); error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectuiv called with invalid query name has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test invalid query object name (64-bit integer version). */ m_pGetQueryBufferObjecti64v(invalid_query, buffer, GL_QUERY_RESULT, 0); error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjecti64v called with invalid query name has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test invalid query object name (64-bit unsigned integer version). */ m_pGetQueryBufferObjectui64v(invalid_query, buffer, GL_QUERY_RESULT, 0); error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectui64v called with invalid query name has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Create query object for the test. */ gl.createQueries(s_targets[0], 1, &query); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateQueries have failed"); gl.beginQuery(s_targets[0], query); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateQueries have failed"); /* Test query of active query object name (integer version). */ m_pGetQueryBufferObjectiv(query, buffer, GL_QUERY_RESULT, 0); error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectiv called with active query object has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query of active query object name (unsigned integer version). */ m_pGetQueryBufferObjectuiv(query, buffer, GL_QUERY_RESULT, 0); error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectuiv called with active query object has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query of active query object name (64-bit integer version). */ m_pGetQueryBufferObjecti64v(query, buffer, GL_QUERY_RESULT, 0); error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjecti64v called with active query object has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query of active query object name (64-bit unsigned integer version). */ m_pGetQueryBufferObjectui64v(query, buffer, GL_QUERY_RESULT, 0); error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectui64v called with active query object has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } } catch (...) { is_error = true; } /* Releasing objects. */ if (query) { gl.endQuery(s_targets[0]); gl.deleteQueries(1, &query); } if (buffer) { gl.deleteBuffers(1, &buffer); } /* Error cleanup. */ while (gl.getError()) ; if (is_error) { throw 0; } return is_ok; } /** @brief Check that GetQueryBufferObjectiv, GetQueryBufferObjectuiv, * GetQueryBufferObjecti64v and GetQueryBufferObjectui64v generate * INVALID_OPERATION error if is not the name of an existing * buffer object. * * @return True if test succeded, false otherwise. */ bool ErrorsTest::testInvalidBufferName() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Default result. */ bool is_ok = true; bool is_error = false; /* Creating invalid buffer name. */ glw::GLuint invalid_buffer = 0; while (gl.isBuffer(++invalid_buffer)) ; /* Test's objects. */ glw::GLuint query = 0; try { /* Create query object for the test. */ gl.createQueries(s_targets[0], 1, &query); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateQueries have failed"); /* Test query of invalid buffer name (integer version). */ m_pGetQueryBufferObjectiv(query, invalid_buffer, GL_QUERY_RESULT_AVAILABLE, 0); glw::GLenum error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectiv which could generate buffers overflow generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query of invalid buffer name (unsigned integer version). */ m_pGetQueryBufferObjectuiv(query, invalid_buffer, GL_QUERY_RESULT_AVAILABLE, 0); error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectuiv which could generate buffers overflow generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query of invalid buffer name (64-bit integer version). */ m_pGetQueryBufferObjecti64v(query, invalid_buffer, GL_QUERY_RESULT_AVAILABLE, 0); error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjecti64v which could generate buffers overflow generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query of invalid buffer name (64-bit unsigned integer version). */ m_pGetQueryBufferObjectui64v(query, invalid_buffer, GL_QUERY_RESULT_AVAILABLE, 0); error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectui64v which could generate buffers overflow generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } } catch (...) { is_error = true; } /* Releasing objects. */ if (query) { gl.deleteQueries(1, &query); } /* Error cleanup. */ while (gl.getError()) ; if (is_error) { throw 0; } return is_ok; } /** @brief Check that GetQueryBufferObjectiv, GetQueryBufferObjectuiv, * GetQueryBufferObjecti64v and GetQueryBufferObjectui64v generate * INVALID_ENUM error if is not QUERY_RESULT, * QUERY_RESULT_AVAILABLE, QUERY_RESULT_NO_WAIT or QUERY_TARGET. * * @return True if test succeded, false otherwise. */ bool ErrorsTest::testInvalidParameterName() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Creating invalid parameter name. */ glw::GLuint invalid_pname = 0; while (isParameterName(++invalid_pname)) ; /* Default result. */ bool is_ok = true; bool is_error = false; /* Test's objects. */ glw::GLuint buffer = 0; glw::GLuint query = 0; try { /* Creating buffer for the test. */ gl.genBuffers(1, &buffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers have failed"); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffers have failed"); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(glw::GLint64), DE_NULL, GL_DYNAMIC_COPY); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffers have failed"); /* Create query object for the test. */ gl.createQueries(s_targets[0], 1, &query); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateQueries have failed"); /* Test query of invalid parameter name (integer version). */ m_pGetQueryBufferObjectiv(query, buffer, invalid_pname, 0); glw::GLenum error = gl.getError(); if (GL_INVALID_ENUM != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectiv called with invalid parameter name has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_ENUM was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query of invalid parameter name (unsigned integer version). */ m_pGetQueryBufferObjectuiv(query, buffer, invalid_pname, 0); error = gl.getError(); if (GL_INVALID_ENUM != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectuiv called with invalid parameter name has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_ENUM was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query of invalid parameter name (64-bit integer version). */ m_pGetQueryBufferObjecti64v(query, buffer, invalid_pname, 0); error = gl.getError(); if (GL_INVALID_ENUM != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjecti64v called with invalid parameter name has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_ENUM was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query of invalid parameter name (64-bit unsigned integer version). */ m_pGetQueryBufferObjectui64v(query, buffer, invalid_pname, 0); error = gl.getError(); if (GL_INVALID_ENUM != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectui64v called with invalid parameter name has generated error " << glu::getErrorStr(error) << ", however GL_INVALID_ENUM was expected." << tcu::TestLog::EndMessage; is_ok = false; } } catch (...) { is_error = true; } /* Releasing objects. */ if (query) { gl.deleteQueries(1, &query); } if (buffer) { gl.deleteBuffers(1, &buffer); } /* Error cleanup. */ while (gl.getError()) ; if (is_error) { throw 0; } return is_ok; } /** @brief Check that GetQueryBufferObjectiv, GetQueryBufferObjectuiv, * GetQueryBufferObjecti64v and GetQueryBufferObjectui64v generate * INVALID_OPERATION error if the query writes to a buffer object, and the * specified buffer offset would cause data to be written beyond the bounds * of that buffer object. * * @return True if test succeded, false otherwise. */ bool ErrorsTest::testBufferOverflow() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Default result. */ bool is_ok = true; bool is_error = false; /* Test's objects. */ glw::GLuint buffer = 0; glw::GLuint query = 0; try { /* Creating buffer for the test. */ gl.genBuffers(1, &buffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers have failed"); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffers have failed"); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(glw::GLint64), DE_NULL, GL_DYNAMIC_COPY); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffers have failed"); /* Create query object for the test. */ gl.createQueries(s_targets[0], 1, &query); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateQueries have failed"); /* Test query of buffer overflow (integer version). */ m_pGetQueryBufferObjectiv(query, buffer, GL_QUERY_RESULT_AVAILABLE, sizeof(glw::GLint64)); glw::GLenum error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectiv which could generate buffers overflow generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query of buffer overflow (unsigned integer version). */ m_pGetQueryBufferObjectuiv(query, buffer, GL_QUERY_RESULT_AVAILABLE, sizeof(glw::GLint64)); error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectuiv which could generate buffers overflow generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query of buffer overflow (64-bit integer version). */ m_pGetQueryBufferObjecti64v(query, buffer, GL_QUERY_RESULT_AVAILABLE, sizeof(glw::GLint64)); error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjecti64v which could generate buffers overflow generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query of buffer overflow (64-bit unsigned integer version). */ m_pGetQueryBufferObjectui64v(query, buffer, GL_QUERY_RESULT_AVAILABLE, sizeof(glw::GLint64)); error = gl.getError(); if (GL_INVALID_OPERATION != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectui64v which could generate buffers overflow generated error " << glu::getErrorStr(error) << ", however GL_INVALID_OPERATION was expected." << tcu::TestLog::EndMessage; is_ok = false; } } catch (...) { is_error = true; } /* Releasing objects. */ if (query) { gl.deleteQueries(1, &query); } if (buffer) { gl.deleteBuffers(1, &buffer); } /* Error cleanup. */ while (gl.getError()) ; if (is_error) { throw 0; } return is_ok; } /** @brief Check that GetQueryBufferObjectiv, GetQueryBufferObjectuiv, * GetQueryBufferObjecti64v and GetQueryBufferObjectui64v generate * INVALID_VALUE error if is negative. * * @return True if test succeded, false otherwise. */ bool ErrorsTest::testBufferNegativeOffset() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Default result. */ bool is_ok = true; bool is_error = false; /* Test's objects. */ glw::GLuint buffer = 0; glw::GLuint query = 0; try { /* Creating buffer for the test. */ gl.genBuffers(1, &buffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers have failed"); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffers have failed"); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(glw::GLint64), DE_NULL, GL_DYNAMIC_COPY); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffers have failed"); /* Create query object for the test. */ gl.createQueries(s_targets[0], 1, &query); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateQueries have failed"); /* Test query with negative offset (integer version). */ m_pGetQueryBufferObjectiv(query, buffer, GL_QUERY_RESULT_AVAILABLE, -1); glw::GLenum error = gl.getError(); if (GL_INVALID_VALUE != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectiv called with negative offset generated error " << glu::getErrorStr(error) << ", however GL_INVALID_VALUE was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query with negative offset (unsigned integer version). */ m_pGetQueryBufferObjectuiv(query, buffer, GL_QUERY_RESULT_AVAILABLE, -1); error = gl.getError(); if (GL_INVALID_VALUE != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectuiv called with negative offset generated error " << glu::getErrorStr(error) << ", however GL_INVALID_VALUE was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query with negative offset (64-bit integer version). */ m_pGetQueryBufferObjecti64v(query, buffer, GL_QUERY_RESULT_AVAILABLE, -1); error = gl.getError(); if (GL_INVALID_VALUE != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjecti64v called with negative offset generated error " << glu::getErrorStr(error) << ", however GL_INVALID_VALUE was expected." << tcu::TestLog::EndMessage; is_ok = false; } /* Test query with negative offset (64-bit unsigned integer version). */ m_pGetQueryBufferObjectui64v(query, buffer, GL_QUERY_RESULT_AVAILABLE, -1); error = gl.getError(); if (GL_INVALID_VALUE != error) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetQueryBufferObjectui64v called with negative offset generated error " << glu::getErrorStr(error) << ", however GL_INVALID_VALUE was expected." << tcu::TestLog::EndMessage; is_ok = false; } } catch (...) { is_error = true; } /* Releasing objects. */ if (query) { gl.deleteQueries(1, &query); } if (buffer) { gl.deleteBuffers(1, &buffer); } /* Error cleanup. */ while (gl.getError()) ; if (is_error) { throw 0; } return is_ok; } /** @brief Check if argument is one of the target names: * - SAMPLES_PASSED, * - TIME_ELAPSED, * - PRIMITIVES_GENERATED, * - TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN. * * @param [in] maybe_target Target to be checked. * * @return True if argument is one of the listed targets, false otherwise. */ bool ErrorsTest::isTarget(glw::GLenum maybe_target) { for (glw::GLuint i = 0; i < s_targets_count; ++i) { if (maybe_target == s_targets[i]) { return true; } } return false; } /** @brief Check if argument is one of the parameter names: * - QUERY_RESULT, * - QUERY_RESULT_AVAILABLE, * - QUERY_RESULT_NO_WAIT, * - QUERY_TARGET. * * @param [in] maybe_pname Parameter name to be checked. * * @return True if argument is one of the listed parameters, false otherwise. */ bool ErrorsTest::isParameterName(glw::GLenum maybe_pname) { glw::GLenum pnames[] = { GL_QUERY_RESULT, GL_QUERY_RESULT_AVAILABLE, GL_QUERY_RESULT_NO_WAIT, GL_QUERY_TARGET }; glw::GLuint pnames_count = sizeof(pnames) / sizeof(pnames[0]); for (glw::GLuint i = 0; i < pnames_count; ++i) { if (maybe_pname == pnames[i]) { return true; } } return false; } /** Targets to be tested. */ const glw::GLenum ErrorsTest::s_targets[] = { GL_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED_CONSERVATIVE, GL_TIME_ELAPSED, GL_TIMESTAMP, GL_PRIMITIVES_GENERATED, GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN }; /** Names of targets to be tested. */ const glw::GLchar* ErrorsTest::s_target_names[] = { "GL_SAMPLES_PASSED", "GL_ANY_SAMPLES_PASSED", "GL_ANY_SAMPLES_PASSED_CONSERVATIVE", "GL_TIME_ELAPSED", "GL_TIMESTAMP", "GL_PRIMITIVES_GENERATED", "GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN" }; /** Number of targets. */ const glw::GLuint ErrorsTest::s_targets_count = sizeof(s_targets) / sizeof(s_targets[0]); /******************************** Functional Test Implementation ********************************/ /** @brief Functional Test constructor. * * @param [in] context OpenGL context. */ FunctionalTest::FunctionalTest(deqp::Context& context) : deqp::TestCase(context, "queries_functional", "Queries Functional Test") , m_pGetQueryBufferObjectiv(DE_NULL) , m_pGetQueryBufferObjectuiv(DE_NULL) , m_pGetQueryBufferObjecti64v(DE_NULL) , m_pGetQueryBufferObjectui64v(DE_NULL) , m_fbo(0) , m_rbo(0) , m_vao(0) , m_bo_query(0) , m_bo_xfb(0) , m_qo(DE_NULL) , m_po(0) { /* Intentionally left blank. */ } /** @brief Do comparison (a == b). * * @tparam Type of the values to be compared. * * @param [in] a First value to be compared. * @param [in] b Second value to be compared. * * @return Result of the comparison. */ template bool FunctionalTest::equal(T a, T b) { return (a == b); } /** @brief Do comparison (a < b). * * @tparam Type of the values to be compared. * * @param [in] a First value to be compared. * @param [in] b Second value to be compared. * * @return Result of the comparison. */ template bool FunctionalTest::less(T a, T b) { return (a < b); } /** @brief Template specialization of glGetQueryBufferObject* function for GLint. * This is pass through function to glGetQueryBufferObjectiv * * @param [in] id Query object identifier. * @param [in] buffer Buffer object identifier. * @param [in] pname Parameter name to be queried. * @param [in] offset Offset of the buffer to be saved at. */ template <> void FunctionalTest::GetQueryBufferObject(glw::GLuint id, glw::GLuint buffer, glw::GLenum pname, glw::GLintptr offset) { m_pGetQueryBufferObjectiv(id, buffer, pname, offset); } /** @brief Template specialization of glGetQueryBufferObject* function for GLuint. * This is pass through function to glGetQueryBufferObjectuiv * * @param [in] id Query object identifier. * @param [in] buffer Buffer object identifier. * @param [in] pname Parameter name to be queried. * @param [in] offset Offset of the buffer to be saved at. */ template <> void FunctionalTest::GetQueryBufferObject(glw::GLuint id, glw::GLuint buffer, glw::GLenum pname, glw::GLintptr offset) { m_pGetQueryBufferObjectuiv(id, buffer, pname, offset); } /** @brief Template specialization of glGetQueryBufferObject* function for GLint64. * This is pass through function to glGetQueryBufferObjecti64v * * @param [in] id Query object identifier. * @param [in] buffer Buffer object identifier. * @param [in] pname Parameter name to be queried. * @param [in] offset Offset of the buffer to be saved at. */ template <> void FunctionalTest::GetQueryBufferObject(glw::GLuint id, glw::GLuint buffer, glw::GLenum pname, glw::GLintptr offset) { m_pGetQueryBufferObjecti64v(id, buffer, pname, offset); } /** @brief Template specialization of glGetQueryBufferObject* function for GLuint64. * This is pass through function to glGetQueryBufferObjectui64v * * @param [in] id Query object identifier. * @param [in] buffer Buffer object identifier. * @param [in] pname Parameter name to be queried. * @param [in] offset Offset of the buffer to be saved at. */ template <> void FunctionalTest::GetQueryBufferObject(glw::GLuint id, glw::GLuint buffer, glw::GLenum pname, glw::GLintptr offset) { m_pGetQueryBufferObjectui64v(id, buffer, pname, offset); } /** @brief Function template fetches query result to buffer object using * glGetQueryBufferObject* function (selected on the basis of template parameter). * Then buffer is mapped and the result is compared with expected_value using comparison function. * * @tparam Templated type of fetched data. * It shall be one of glw::GL[u]int[64]. * * @param [in] query Query object to be queried. * @param [in] pname Parameter name to be queried. * @param [in] expected_value Reference value to be compared. * @param [in] comparison Comparison function pointer. * Comparsion function shall NOT throw. * * @note The function may throw if unexpected error has occured. * * @return True if comparison succeeded, false otherwise. */ template bool FunctionalTest::checkQueryBufferObject(glw::GLuint query, glw::GLenum pname, T expected_value, bool (*comparison)(T, T)) { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Default result. */ bool is_ok = true; /* Saving results to buffer. */ GetQueryBufferObject(query, m_bo_query, pname, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryBufferObject* have failed"); /* Mapping buffer to user space. */ T* value = (T*)gl.mapBuffer(GL_QUERY_BUFFER, GL_READ_ONLY); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer have failed"); /* Doing test. */ if (!comparison(expected_value, *value)) { is_ok = false; } /* Cleanup. */ gl.unmapBuffer(GL_QUERY_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer have failed"); /* Return test result. */ return is_ok; } /** @brief Iterate Functional Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult FunctionalTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Fetching access point to GL functions. */ m_pGetQueryBufferObjectiv = (PFNGLGETQUERYBUFFEROBJECT)gl.getQueryBufferObjectiv; m_pGetQueryBufferObjectuiv = (PFNGLGETQUERYBUFFEROBJECT)gl.getQueryBufferObjectuiv; m_pGetQueryBufferObjecti64v = (PFNGLGETQUERYBUFFEROBJECT)gl.getQueryBufferObjecti64v; m_pGetQueryBufferObjectui64v = (PFNGLGETQUERYBUFFEROBJECT)gl.getQueryBufferObjectui64v; /* Running tests. */ bool is_ok = true; bool is_error = false; try { if ((DE_NULL == m_pGetQueryBufferObjectiv) || (DE_NULL == m_pGetQueryBufferObjectuiv) || (DE_NULL == m_pGetQueryBufferObjecti64v) || (DE_NULL == m_pGetQueryBufferObjectui64v)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Test could not get the pointers for glGetQueryBufferObject* functions." << tcu::TestLog::EndMessage; throw 0; } prepareView(); prepareVertexArray(); prepareBuffers(); prepareQueries(); prepareProgram(); draw(); /* Make sure that framebuffer and transform feedback buffer are filled with expectd data. */ is_ok &= checkView(); is_ok &= checkXFB(); /* Make comparisons for each query object. */ for (glw::GLuint i = 0; i < s_targets_count; ++i) { /* Checking targets. */ is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_TARGET, s_targets[i], &FunctionalTest::equal); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_TARGET, s_targets[i], &FunctionalTest::equal); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_TARGET, s_targets[i], &FunctionalTest::equal); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_TARGET, s_targets[i], &FunctionalTest::equal); /* Checking result availability. */ is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT_AVAILABLE, GL_TRUE, &FunctionalTest::equal); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT_AVAILABLE, GL_TRUE, &FunctionalTest::equal); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT_AVAILABLE, GL_TRUE, &FunctionalTest::equal); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT_AVAILABLE, GL_TRUE, &FunctionalTest::equal); if (GL_TIME_ELAPSED == s_targets[i]) { /* Checking result. */ is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT, (glw::GLint)s_results[i], &FunctionalTest::less); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT, (glw::GLuint)s_results[i], &FunctionalTest::less); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT, (glw::GLint64)s_results[i], &FunctionalTest::less); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT, (glw::GLuint64)s_results[i], &FunctionalTest::less); /* Checking result (no-wait). */ is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT_NO_WAIT, (glw::GLint)s_results[i], &FunctionalTest::less); is_ok &= checkQueryBufferObject( m_qo[i], GL_QUERY_RESULT_NO_WAIT, (glw::GLuint)s_results[i], &FunctionalTest::less); is_ok &= checkQueryBufferObject( m_qo[i], GL_QUERY_RESULT_NO_WAIT, (glw::GLint64)s_results[i], &FunctionalTest::less); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT_NO_WAIT, (glw::GLuint64)s_results[i], &FunctionalTest::less); } else { /* Checking result. */ is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT, (glw::GLint)s_results[i], &FunctionalTest::equal); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT, (glw::GLuint)s_results[i], &FunctionalTest::equal); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT, (glw::GLint64)s_results[i], &FunctionalTest::equal); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT, (glw::GLuint64)s_results[i], &FunctionalTest::equal); /* Checking result (no-wait). */ is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT_NO_WAIT, (glw::GLint)s_results[i], &FunctionalTest::equal); is_ok &= checkQueryBufferObject( m_qo[i], GL_QUERY_RESULT_NO_WAIT, (glw::GLuint)s_results[i], &FunctionalTest::equal); is_ok &= checkQueryBufferObject( m_qo[i], GL_QUERY_RESULT_NO_WAIT, (glw::GLint64)s_results[i], &FunctionalTest::equal); is_ok &= checkQueryBufferObject(m_qo[i], GL_QUERY_RESULT_NO_WAIT, (glw::GLuint64)s_results[i], &FunctionalTest::equal); } } } catch (...) { is_ok = false; is_error = true; } /* Clean up. */ clean(); /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (is_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } /** @brief Function prepares framebuffer with RGBA8 color attachment. * Viewport is set up. Content of the framebuffer is cleared. * * @note The function may throw if unexpected error has occured. */ void FunctionalTest::prepareView() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Prepare framebuffer. */ gl.genFramebuffers(1, &m_fbo); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers call failed."); gl.genRenderbuffers(1, &m_rbo); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers call failed."); gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer call failed."); gl.bindRenderbuffer(GL_RENDERBUFFER, m_rbo); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer call failed."); gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1 /* x size */, 1 /* y size */); GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage call failed."); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo); GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer call failed."); if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { throw 0; } gl.viewport(0, 0, 1, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport call failed."); /* Clear framebuffer's content. */ gl.clearColor(0.0f, 0.0f, 0.0f, 0.0f); GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor call failed."); gl.clear(GL_COLOR_BUFFER_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glClear call failed."); } /** @brief Function creates and binds empty vertex array. * * @note The function may throw if unexpected error has occured. */ void FunctionalTest::prepareVertexArray() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Creating and binding VAO. */ gl.genVertexArrays(1, &m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays have failed"); gl.bindVertexArray(m_vao); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray have failed"); } /** @brief Function creates buffers for query and transform feedback data storage. * The storage is allocated and buffers are bound to QUERY_BUFFER and * TRANSFORM_FEEDBACK_BUFFER binding points respectively. * * @note The function may throw if unexpected error has occured. */ void FunctionalTest::prepareBuffers() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Buffer for storing query's result. */ gl.genBuffers(1, &m_bo_query); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers have failed"); gl.bindBuffer(GL_QUERY_BUFFER, m_bo_query); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer have failed"); gl.bufferData(GL_QUERY_BUFFER, sizeof(glw::GLint64), DE_NULL, GL_DYNAMIC_COPY); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData have failed"); /* Buffer for storing transform feedback results. */ gl.genBuffers(1, &m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers have failed"); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer have failed"); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 3 /* number of vertices per triangle */ * 2 /* number of triangles */ * sizeof(glw::GLint), DE_NULL, GL_DYNAMIC_COPY); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData have failed"); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_bo_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase have failed"); } /** @brief Function creates array of query objects using DSA-style method. * * @note The function may throw if unexpected error has occured. */ void FunctionalTest::prepareQueries() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Allocating memory for queries array. */ m_qo = new glw::GLuint[s_targets_count]; if (DE_NULL == m_qo) { throw 0; } /* Creating query object for each target. */ for (glw::GLuint i = 0; i < s_targets_count; ++i) { gl.createQueries(s_targets[i], 1, &m_qo[i]); /* Error checking. */ if (GL_NO_ERROR != gl.getError()) { /* Remove previous. */ if (i > 0) { gl.deleteQueries(i, m_qo); } /* Deallocate storage. */ delete[] m_qo; m_qo = DE_NULL; /* Signalise test failure. */ throw 0; } } } /** @brief Function builds test's GLSL program. * If succeded, the program will be set to be used. * * @note The function may throw if unexpected error has occured. */ void FunctionalTest::prepareProgram() { /* 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 = 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, 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) { glw::GLint log_size = 0; gl.getShaderiv(shader[i].id, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getShaderInfoLog(shader[i].id, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Shader compilation has failed.\n" << "Shader type: " << glu::getShaderTypeStr(shader[i].type) << "\n" << "Shader compilation error log:\n" << log_text << "\n" << "Shader source code:\n" << shader[i].source << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderInfoLog call failed."); throw 0; } } } /* Transform Feedback setup. */ gl.transformFeedbackVaryings(m_po, 1, &s_xfb_varying_name, GL_INTERLEAVED_ATTRIBS); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); /* Link. */ gl.linkProgram(m_po); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); glw::GLint status = GL_FALSE; gl.getProgramiv(m_po, 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, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glDetachShader call failed."); } } } else { glw::GLint log_size = 0; gl.getProgramiv(m_po, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getProgramInfoLog(m_po, log_size, NULL, &log_text[0]); m_context.getTestContext().getLog() << tcu::TestLog::Message << "Program linkage has failed due to:\n" << log_text << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog call failed."); throw 0; } } catch (...) { if (m_po) { gl.deleteProgram(m_po); m_po = 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) { gl.useProgram(m_po); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); } if (0 == m_po) { throw 0; } } /** @brief Function draws full screen quad. Queries are measured during the process. * Also, transform feedback data is captured. * * @note The function may throw if unexpected error has occured. */ void FunctionalTest::draw() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Start queries. */ for (glw::GLuint i = 0; i < s_targets_count; ++i) { gl.beginQuery(s_targets[i], m_qo[i]); } /* Start XFB. */ gl.beginTransformFeedback(GL_TRIANGLES); /* Draw full screen quad. */ gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); /* Finish XFB. */ gl.endTransformFeedback(); /* Finish queries. */ for (glw::GLuint i = 0; i < s_targets_count; ++i) { gl.endQuery(s_targets[i]); } /* Make sure OpenGL finished drawing. */ gl.finish(); /* Error checking. */ GLU_EXPECT_NO_ERROR(gl.getError(), "Drawing function have failed."); } /** @brief Check that framebuffer is filled with red color. */ bool FunctionalTest::checkView() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Fetch framebuffer data. */ glw::GLubyte pixel[4] = { 0 }; gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels have failed"); /* Comparison with expected values. */ if ((255 != pixel[0]) || (0 != pixel[1]) || (0 != pixel[2]) || (255 != pixel[3])) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Frameuffer content (" << (unsigned int)pixel[0] << ", " << (unsigned int)pixel[1] << ", " << (unsigned int)pixel[2] << ", " << (unsigned int)pixel[3] << ") is different than expected (255, 0, 0, 255)." << tcu::TestLog::EndMessage; return false; } return true; } /** @brief Check that transform feedback buffer * contains values representing quad. */ bool FunctionalTest::checkXFB() { /* Shortcut for GL functionality */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Default result. */ bool is_ok = true; /* Mapping buffer object to the user-space. */ glw::GLint* buffer = (glw::GLint*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer call failed."); if ((0 != buffer[0]) || (1 != buffer[1]) || (2 != buffer[2]) || (2 != buffer[3]) || (1 != buffer[4]) || (3 != buffer[5])) { is_ok = false; } gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer call failed."); return is_ok; } /** @brief Release all created objects. */ void FunctionalTest::clean() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Releasing queries. */ if (DE_NULL != m_qo) { gl.deleteQueries(s_targets_count, m_qo); delete[] m_qo; m_qo = DE_NULL; } /* Release framebuffer. */ if (m_fbo) { gl.deleteFramebuffers(1, &m_fbo); m_fbo = 0; } /* Release renderbuffer. */ if (m_rbo) { gl.deleteRenderbuffers(1, &m_rbo); m_rbo = 0; } /* Release vertex array object. */ if (m_vao) { gl.deleteVertexArrays(1, &m_vao); m_vao = 0; } /* Release buffer object for storing queries' results. */ if (m_bo_query) { gl.deleteBuffers(1, &m_bo_query); m_bo_query = 0; } /* Release transform feedback buffer. */ if (m_bo_xfb) { gl.deleteBuffers(1, &m_bo_xfb); m_bo_xfb = 0; } /* Release GLSL program. */ if (m_po) { gl.useProgram(0); gl.deleteProgram(m_po); m_po = 0; } } /** Targets to be tested. */ const glw::GLenum FunctionalTest::s_targets[] = { GL_SAMPLES_PASSED, GL_TIME_ELAPSED, GL_PRIMITIVES_GENERATED, GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN }; /** Expected result for each target. */ const glw::GLint FunctionalTest::s_results[] = { 1, 0, 2, 2 }; /** Number of targets. */ const glw::GLuint FunctionalTest::s_targets_count = sizeof(s_targets) / sizeof(s_targets[0]); /** Vertex shader source code. */ const glw::GLchar FunctionalTest::s_vertex_shader[] = "#version 450\n" "\n" "out int xfb_result;\n" "\n" "void main()\n" "{\n" " switch(gl_VertexID)\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" " xfb_result = gl_VertexID;\n" "}\n"; /** Fragment shader source program. */ const glw::GLchar FunctionalTest::s_fragment_shader[] = "#version 450\n" "\n" "out vec4 color;\n" "\n" "void main()\n" "{\n" " color = vec4(1.0, 0.0, 0.0, 1.0);\n" "}\n"; /** Name of transform feedback varying in vertex shader. */ const glw::GLchar* FunctionalTest::s_xfb_varying_name = "xfb_result"; /******************************** Reuse Test Implementation ********************************/ /** @brief Reuse Test constructor. * * @param [in] context OpenGL context. */ ReuseTest::ReuseTest(deqp::Context& context) : deqp::TestCase(context, "queries_reuse", "Query Objects Reuse Test") { /* Intentionally left blank. */ } /** @brief Iterate Reuse Test cases. * * @return Iteration result. */ tcu::TestNode::IterateResult ReuseTest::iterate() { /* Shortcut for GL functionality. */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Get context setup. */ bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5))); bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access"); if ((!is_at_least_gl_45) && (!is_arb_direct_state_access)) { m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported"); return STOP; } /* Running tests. */ bool is_ok = true; GLuint query_id_a = 0; GLuint query_id_b = 0; /* Allocate query object */ gl.genQueries(1, &query_id_a); /* Associate object with GL_TIMESTAMP */ gl.queryCounter(query_id_a, GL_TIMESTAMP); /* Deallocate query object */ gl.deleteQueries(1, &query_id_a); /* Allocate query object again - should result in the same id */ gl.genQueries(1, &query_id_b); /* Use the id with something else */ gl.beginQuery(GL_TIME_ELAPSED, query_id_b); if (gl.getError() != 0) /* Crash was reported here. */ is_ok = false; gl.endQuery(GL_TIME_ELAPSED); /* Clean up */ gl.deleteQueries(1, &query_id_b); if (query_id_a != query_id_b) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Note: Queries got different id:s, so no actual reuse occurred." << tcu::TestLog::EndMessage; } /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } return STOP; } } /* Queries namespace. */ } /* DirectStateAccess namespace. */ } /* gl4cts namespace. */