blob: 8663b4ff86bd936c7d57e92685ebb77232481f37 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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 "gl4cContextFlushControlTests.hpp"
#include "deClock.h"
#include "gluContextInfo.hpp"
#include "gluDefs.hpp"
#include "gluPlatform.hpp"
#include "gluRenderContext.hpp"
#include "gluStrUtil.hpp"
#include "tcuTestLog.hpp"
#ifndef GL_CONTEXT_RELEASE_BEHAVIOR
#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB
#endif
#ifndef GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH
#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC
#endif
#define CONTEXT_FLUSH_CONTROL_FUNCTIONAL_TEST_DRAW_COUNT 1024
/******************************** Test Group Implementation ********************************/
/** @brief Context Flush Control tests group constructor.
*
* @param [in] context OpenGL context.
*/
gl4cts::ContextFlushControl::Tests::Tests(deqp::Context& context)
: TestCaseGroup(context, "context_flush_control", "Context Flush Control Test Suite")
{
/* Intentionally left blank */
}
/** @brief Context Flush Control tests initializer. */
void gl4cts::ContextFlushControl::Tests::init()
{
addChild(new gl4cts::ContextFlushControl::CoverageTest(m_context));
addChild(new gl4cts::ContextFlushControl::FunctionalTest(m_context));
}
/******************************** Coverage Tests Implementation ********************************/
/** @brief API coverage tests constructor.
*
* @param [in] context OpenGL context.
*/
gl4cts::ContextFlushControl::CoverageTest::CoverageTest(deqp::Context& context)
: deqp::TestCase(context, "coverage", "Context Flush Control API Coverage Test")
{
/* Intentionally left blank. */
}
/** @brief Iterate API coverage tests.
*
* @return Iteration result.
*/
tcu::TestNode::IterateResult gl4cts::ContextFlushControl::CoverageTest::iterate()
{
/* OpenGL support query. */
bool is_at_least_gl_44 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 4)));
bool is_khr_context_flush_control = m_context.getContextInfo().isExtensionSupported("GL_KHR_context_flush_control");
/* Running tests. */
bool is_ok = true;
/* This test should only be executed if we're running a GL4.4 context or related extension is available */
if (is_at_least_gl_44 || is_khr_context_flush_control)
{
/* Test deafult context which shall use implicit flush when swapped. */
is_ok = is_ok && testQuery(m_context.getRenderContext(), GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH);
/* Create context which shall swap without flush. */
glu::RenderContext* no_flush_context = createNoFlushContext();
/* Proceed only if such context has been created. */
if (DE_NULL != no_flush_context)
{
/* Test no-flush context. */
no_flush_context->makeCurrent();
is_ok = is_ok && testQuery(*no_flush_context, GL_NONE);
/* Release no-flush context. */
m_context.getRenderContext().makeCurrent();
delete no_flush_context;
}
}
/* Result's setup. */
if (is_ok)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "The Context Flush Control Coverage test have failed."
<< tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return STOP;
}
/** @brief Test getter coverage for the given GL context.
*
* This function tests following GL query functions:
* glGetIntegerv,
* glGetFloatv,
* glGetBooleanv,
* glGetDoublev,
* glGetInteger64v.
* Expected value is clamped to <0, 1> range flor glGetBooleanv.
* For reference see KHR_context_flush_control extension.
*
* @param [in] context Render context to be used with the test.
* @param [in] expected_value Expected value to be returned by glGet*v function.
*
* @return True if all tested functions returned expected value, false otherwise.
*/
bool gl4cts::ContextFlushControl::CoverageTest::testQuery(glu::RenderContext& context, glw::GLenum expected_value)
{
/* Shortcut for GL functionality. */
const glw::Functions& gl = context.getFunctions();
/* Variables for query. */
glw::GLint value_i = -1;
glw::GLint64 value_i64 = -1;
glw::GLfloat value_f = -1;
glw::GLdouble value_d = -1;
glw::GLboolean value_b = -1;
glw::GLboolean expected_bool_value = (glw::GLboolean)de::min((glw::GLint)expected_value, (glw::GLint)1);
/* Test. */
try
{
/* Fetch data. */
gl.getIntegerv(GL_CONTEXT_RELEASE_BEHAVIOR, &value_i);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed.");
gl.getInteger64v(GL_CONTEXT_RELEASE_BEHAVIOR, &value_i64);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetInteger64v call failed.");
gl.getFloatv(GL_CONTEXT_RELEASE_BEHAVIOR, &value_f);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetFloatv call failed.");
gl.getDoublev(GL_CONTEXT_RELEASE_BEHAVIOR, &value_d);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetDoublev call failed.");
gl.getBooleanv(GL_CONTEXT_RELEASE_BEHAVIOR, &value_b);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetBooleanv call failed.");
/* Check result. */
if ((expected_value == value_i) && (expected_value == value_i64) && (expected_value == value_f) &&
(expected_value == value_d) && (expected_bool_value == value_b))
{
return true;
}
}
catch (...)
{
return false;
}
return false;
}
/** @brief Create render context with CONTEXT_RELEASE_BEHAVIOR set as NONE.
*
* @return Render context pointer if creation succeeded, DE_NULL otherwise.
*/
glu::RenderContext* gl4cts::ContextFlushControl::CoverageTest::createNoFlushContext()
{
/* Get current platform.*/
glu::Platform& platform = dynamic_cast<glu::Platform&>(m_context.getTestContext().getPlatform());
/* Context to be returned (NULL if failed). */
glu::RenderContext* context = DE_NULL;
/* Get context related attributes needed to create no-flush context. */
const int* attributes = platform.getContextFlushControlContextAttributes();
/* Proceed only if it is possible to make no-flush context. */
if (DE_NULL != attributes)
{
glu::ContextType renderContextType = m_context.getRenderContext().getType();
/* Create no-flush context. */
context = platform.createRenderContext(renderContextType, m_context.getTestContext().getCommandLine(),
0 /* shared_context */, attributes);
}
return context;
}
/******************************** Functional Test Implementation ********************************/
/** @brief Functional test constructor.
*
* @param [in] context OpenGL context.
*/
gl4cts::ContextFlushControl::FunctionalTest::FunctionalTest(deqp::Context& context)
: deqp::TestCase(context, "functional", "Context Flush Control Functional Test")
{
/* Intentionally left blank. */
}
/** @brief Iterate Functional test cases.
*
* @return Iteration result.
*/
tcu::TestNode::IterateResult gl4cts::ContextFlushControl::FunctionalTest::iterate()
{
/* Shortcut for GL functionality. */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Get current platform.*/
glu::Platform& platform = dynamic_cast<glu::Platform&>(m_context.getTestContext().getPlatform());
/* OpenGL support query. */
bool is_at_least_gl_44 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 4)));
bool is_khr_context_flush_control = m_context.getContextInfo().isExtensionSupported("GL_KHR_context_flush_control");
/* Running tests. */
bool is_ok = true;
bool is_error = false;
/* This test should only be executed if we're running a GL4.4 context or related extension is available */
try
{
if ((is_at_least_gl_44 || is_khr_context_flush_control) &&
(DE_NULL != platform.getContextFlushControlContextAttributes()))
{
glw::GLfloat test_time_no_flush = testTime(false);
glw::GLfloat test_time_flush = testTime(true);
is_ok = (test_time_no_flush < test_time_flush);
}
else
{
is_error = true;
}
}
catch (...)
{
is_ok = false;
is_error = true;
}
/* Result's setup. */
if (is_ok)
{
if (is_error)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "The context does not support No-Flush behavior."
<< tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported.");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
}
else
{
if (is_error)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Internal error has occured during Context Flush Control Functional test."
<< tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Test error.");
}
else
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "The running time of no-flush context switches has been slower than flush "
"behavior context switching case. "
<< "This is not expected from quality implementation." << tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Quality warning.");
}
}
return STOP;
}
/** @brief This function measures time of loop consisting of draw and switch context,
* which shall or shall not flush on switch.
*
* The test is based on KHR_context_flush_control extension overview, that the main reason
* for no-flush context is to increase the performance of the implementation.
*
* @param [in] shall_flush_on_release Flag indicating that contexts shall flush when switched.
*
* @return Run-time of the test loop.
*/
glw::GLfloat gl4cts::ContextFlushControl::FunctionalTest::testTime(bool shall_flush_on_release)
{
/* Create two contexts to be switched during test. */
DrawSetup draw_context_setup[2] = { DrawSetup(m_context, shall_flush_on_release),
DrawSetup(m_context, shall_flush_on_release) };
/* Check starting time. */
deUint64 start_time = deGetMicroseconds();
/* Loop over draw-switch context. */
for (glw::GLuint i = 0; i < 1024; ++i)
{
draw_context_setup[i % 2].makeCurrent();
draw_context_setup[i % 2].draw();
}
/* Check end time. */
deUint64 end_time = deGetMicroseconds();
/* Return resulting run-time. */
return (glw::GLfloat)(end_time - start_time);
}
/** @brief Make context current.
*/
void gl4cts::ContextFlushControl::FunctionalTest::DrawSetup::makeCurrent()
{
/* Switch context to this. */
m_context->makeCurrent();
}
/** @brief Use program and draw full screen quad.
*/
void gl4cts::ContextFlushControl::FunctionalTest::DrawSetup::draw()
{
/* Shortcut for GL functionality. */
const glw::Functions& gl = m_context->getFunctions();
/* Use GLSL program. */
gl.useProgram(m_po);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed.");
/* Clear. */
gl.clear(GL_COLOR_BUFFER_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glClear call failed.");
/* Draw. */
gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4 /* quad */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed.");
}
/** @brief Draw Setup object constructor.
*
* The constructor will throw on error.
*
* @param [in] test_context Test context for platform, logging and switching to default context on destruction.
* @param [in] shall_flush_on_release Flag indicating that contexts shall flush when switched.
*/
gl4cts::ContextFlushControl::FunctionalTest::DrawSetup::DrawSetup(deqp::Context& test_context,
bool shall_flush_on_release)
: m_test_context(test_context), m_fbo(0), m_rbo(0), m_vao(0), m_po(0)
{
createContext(shall_flush_on_release);
if (DE_NULL == m_context)
{
throw 0;
}
createGeometry();
if (0 == m_vao)
{
throw 0;
}
createView();
if ((0 == m_fbo) || (0 == m_rbo))
{
throw 0;
}
createProgram();
if (0 == m_po)
{
throw 0;
}
}
/** @brief Draw Setup object destructor.
*/
gl4cts::ContextFlushControl::FunctionalTest::DrawSetup::~DrawSetup()
{
if (m_context)
{
/* Make sure context is current. */
makeCurrent();
/* Shortcut for GL functionality. */
const glw::Functions& gl = m_context->getFunctions();
/* Cleanup. */
if (m_vao)
{
gl.deleteVertexArrays(1, &m_vao);
m_vao = 0;
}
if (m_fbo)
{
gl.deleteFramebuffers(1, &m_fbo);
m_fbo = 0;
}
if (m_rbo)
{
gl.deleteRenderbuffers(1, &m_rbo);
m_rbo = 0;
}
if (m_po)
{
gl.deleteProgram(m_po);
m_po = 0;
}
/* Make default context current. */
m_test_context.getRenderContext().makeCurrent();
/* Cleanup context. */
delete m_context;
}
}
/** @brief Create render context with CONTEXT_RELEASE_BEHAVIOR set to NONE or CONTEXT_RELEASE_BEHAVIOR_FLUSH.
*
* @return Render context pointer if creation succeeded, DE_NULL otherwise.
*/
void gl4cts::ContextFlushControl::FunctionalTest::DrawSetup::createContext(bool shall_flush_on_release)
{
/* Get current platform.*/
glu::Platform& platform = dynamic_cast<glu::Platform&>(m_test_context.getTestContext().getPlatform());
/* Get context related attributes needed to create no-flush context. */
const int* attributes = DE_NULL;
if (!shall_flush_on_release)
{
attributes = platform.getContextFlushControlContextAttributes();
}
/* Proceed only if it is possible to make no-flush context. */
glu::ContextType renderContextType = m_test_context.getRenderContext().getType();
/* Create no-flush context. */
m_context = platform.createRenderContext(renderContextType, m_test_context.getTestContext().getCommandLine(),
0 /* shared_context */, attributes);
}
/** @brief Create RGBA8 framebuffer with attached renderbuffer.
*/
void gl4cts::ContextFlushControl::FunctionalTest::DrawSetup::createView()
{
/* Shortcut for GL functionality. */
const glw::Functions& gl = m_context->getFunctions();
/* Prepare framebuffer. */
gl.clearColor(0.f, 0.f, 0.f, 1.f);
GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor call failed.");
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, 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);
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 and bind empty vertex array object.
*/
void gl4cts::ContextFlushControl::FunctionalTest::DrawSetup::createGeometry()
{
/* Shortcut for GL functionality. */
const glw::Functions& gl = m_context->getFunctions();
/* Create and bind vertex array. */
gl.genVertexArrays(1, &m_vao);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed.");
gl.bindVertexArray(m_vao);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed.");
}
/** @brief Compile and link shader program.
*/
void gl4cts::ContextFlushControl::FunctionalTest::DrawSetup::createProgram()
{
/* Shortcut for GL functionality. */
const glw::Functions& gl = m_context->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)
{
throw 0;
}
}
}
/* 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
{
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;
}
}
}
const glw::GLuint gl4cts::ContextFlushControl::FunctionalTest::DrawSetup::s_view_size = 256;
const glw::GLchar gl4cts::ContextFlushControl::FunctionalTest::DrawSetup::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::ContextFlushControl::FunctionalTest::DrawSetup::s_fragment_shader[] =
"#version 130\n"
"\n"
"out vec4 pixel;\n"
"\n"
"void main()\n"
"{\n"
" pixel = vec4(1.0);\n"
"}\n";