blob: fe4f7ca84ed4c8c90b4e7e0ac9c38e9e98fda8d2 [file] [log] [blame] [edit]
/*-------------------------------------------------------------------------
* drawElements Quality Program EGL Module
* ---------------------------------------
*
* Copyright 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Test KHR_mutable_render_buffer
*//*--------------------------------------------------------------------*/
#include "teglMutableRenderBufferTests.hpp"
#include "egluUtil.hpp"
#include "eglwLibrary.hpp"
#include "eglwEnums.hpp"
#include "gluDefs.hpp"
#include "gluRenderContext.hpp"
#include "glwFunctions.hpp"
#include "glwEnums.hpp"
using namespace eglw;
using std::vector;
namespace deqp
{
namespace egl
{
namespace
{
class MutableRenderBufferTest : public TestCase
{
public:
MutableRenderBufferTest(EglTestContext &eglTestCtx, const char *name, const char *description,
bool enableConfigBit);
~MutableRenderBufferTest(void);
void init(void);
void deinit(void);
IterateResult iterate(void);
protected:
uint32_t drawAndSwap(const Library &egl, uint32_t color, bool flush);
bool m_enableConfigBit;
EGLDisplay m_eglDisplay;
EGLSurface m_eglSurface;
EGLConfig m_eglConfig;
eglu::NativeWindow *m_window;
EGLContext m_eglContext;
glw::Functions m_gl;
};
MutableRenderBufferTest::MutableRenderBufferTest(EglTestContext &eglTestCtx, const char *name, const char *description,
bool enableConfigBit)
: TestCase(eglTestCtx, name, description)
, m_enableConfigBit(enableConfigBit)
, m_eglDisplay(EGL_NO_DISPLAY)
, m_eglSurface(EGL_NO_SURFACE)
, m_eglConfig(DE_NULL)
, m_window(DE_NULL)
, m_eglContext(EGL_NO_CONTEXT)
{
}
MutableRenderBufferTest::~MutableRenderBufferTest(void)
{
deinit();
}
void MutableRenderBufferTest::init(void)
{
const Library &egl = m_eglTestCtx.getLibrary();
// create display
m_eglDisplay = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
if (!eglu::hasExtension(egl, m_eglDisplay, "EGL_KHR_mutable_render_buffer"))
{
TCU_THROW(NotSupportedError, "EGL_KHR_mutable_render_buffer is not supported");
}
// get mutable render buffer config
const EGLint attribs[] = {EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
8,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT | EGL_MUTABLE_RENDER_BUFFER_BIT_KHR,
EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES2_BIT,
EGL_NONE};
const EGLint attribsNoBit[] = {EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
8,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES2_BIT,
EGL_NONE};
if (m_enableConfigBit)
{
m_eglConfig = eglu::chooseSingleConfig(egl, m_eglDisplay, attribs);
}
else
{
const vector<EGLConfig> configs = eglu::chooseConfigs(egl, m_eglDisplay, attribsNoBit);
for (vector<EGLConfig>::const_iterator config = configs.begin(); config != configs.end(); ++config)
{
EGLint surfaceType = -1;
EGLU_CHECK_CALL(egl, getConfigAttrib(m_eglDisplay, *config, EGL_SURFACE_TYPE, &surfaceType));
if (!(surfaceType & EGL_MUTABLE_RENDER_BUFFER_BIT_KHR))
{
m_eglConfig = *config;
break;
}
}
if (m_eglConfig == DE_NULL)
TCU_THROW(NotSupportedError, "No config without support for mutable_render_buffer found");
}
// create surface
const eglu::NativeWindowFactory &factory =
eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());
m_window =
factory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_eglDisplay, m_eglConfig, DE_NULL,
eglu::WindowParams(480, 480, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
m_eglSurface =
eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *m_window, m_eglDisplay, m_eglConfig, DE_NULL);
// create context and make current
const EGLint contextAttribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
egl.bindAPI(EGL_OPENGL_ES_API);
m_eglContext = egl.createContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttribList);
EGLU_CHECK_MSG(egl, "eglCreateContext");
TCU_CHECK(m_eglSurface != EGL_NO_SURFACE);
egl.makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
EGLU_CHECK_MSG(egl, "eglMakeCurrent");
m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2, 0));
}
void MutableRenderBufferTest::deinit(void)
{
const Library &egl = m_eglTestCtx.getLibrary();
if (m_eglContext != EGL_NO_CONTEXT)
{
egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
egl.destroyContext(m_eglDisplay, m_eglContext);
m_eglContext = EGL_NO_CONTEXT;
}
if (m_eglSurface != EGL_NO_SURFACE)
{
egl.destroySurface(m_eglDisplay, m_eglSurface);
m_eglSurface = EGL_NO_SURFACE;
}
if (m_eglDisplay != EGL_NO_DISPLAY)
{
egl.terminate(m_eglDisplay);
m_eglDisplay = EGL_NO_DISPLAY;
}
if (m_window != DE_NULL)
{
delete m_window;
m_window = DE_NULL;
}
}
uint32_t MutableRenderBufferTest::drawAndSwap(const Library &egl, uint32_t color, bool flush)
{
DE_ASSERT(color < 256);
m_gl.clearColor((float)color / 255.f, (float)color / 255.f, (float)color / 255.f, (float)color / 255.f);
m_gl.clear(GL_COLOR_BUFFER_BIT);
if (flush)
{
m_gl.flush();
}
else
{
EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface));
}
return (color | color << 8 | color << 16 | color << 24);
}
TestCase::IterateResult MutableRenderBufferTest::iterate(void)
{
const Library &egl = m_eglTestCtx.getLibrary();
int frameNumber = 1;
// run a few back-buffered frames even if we can't verify their contents
for (; frameNumber < 5; frameNumber++)
{
drawAndSwap(egl, frameNumber, false);
}
// switch to single-buffer rendering
EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER));
// Use eglSwapBuffers for the first frame
drawAndSwap(egl, frameNumber, false);
frameNumber++;
// test a few single-buffered frames
for (; frameNumber < 10; frameNumber++)
{
uint32_t backBufferPixel = 0xFFFFFFFF;
uint32_t frontBufferPixel = drawAndSwap(egl, frameNumber, true);
m_gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &backBufferPixel);
// when single buffered, front-buffer == back-buffer
if (backBufferPixel != frontBufferPixel)
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Surface isn't single-buffered");
return STOP;
}
}
// switch back to back-buffer rendering
EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_RENDER_BUFFER, EGL_BACK_BUFFER));
// run a few back-buffered frames even if we can't verify their contents
for (; frameNumber < 14; frameNumber++)
{
drawAndSwap(egl, frameNumber, false);
}
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
class MutableRenderBufferQueryTest : public MutableRenderBufferTest
{
public:
MutableRenderBufferQueryTest(EglTestContext &eglTestCtx, const char *name, const char *description);
~MutableRenderBufferQueryTest(void);
IterateResult iterate(void);
};
MutableRenderBufferQueryTest::MutableRenderBufferQueryTest(EglTestContext &eglTestCtx, const char *name,
const char *description)
: MutableRenderBufferTest(eglTestCtx, name, description, true)
{
}
MutableRenderBufferQueryTest::~MutableRenderBufferQueryTest(void)
{
deinit();
}
TestCase::IterateResult MutableRenderBufferQueryTest::iterate(void)
{
const Library &egl = m_eglTestCtx.getLibrary();
// check that by default the query returns back buffered
EGLint curRenderBuffer = -1;
EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_RENDER_BUFFER, &curRenderBuffer));
if (curRenderBuffer != EGL_BACK_BUFFER)
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Surface didn't default to back-buffered rendering");
return STOP;
}
// switch to single-buffer rendering and check that the query output changed
EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER));
EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_RENDER_BUFFER, &curRenderBuffer));
if (curRenderBuffer != EGL_SINGLE_BUFFER)
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Surface didn't switch to single-buffer rendering");
return STOP;
}
// switch back to back-buffer rendering and check the query again
EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_RENDER_BUFFER, EGL_BACK_BUFFER));
EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_RENDER_BUFFER, &curRenderBuffer));
if (curRenderBuffer != EGL_BACK_BUFFER)
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Surface didn't switch back to back-buffer rendering");
return STOP;
}
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
class MutableRenderBufferQueryNegativeTest : public MutableRenderBufferTest
{
public:
MutableRenderBufferQueryNegativeTest(EglTestContext &eglTestCtx, const char *name, const char *description);
~MutableRenderBufferQueryNegativeTest(void);
IterateResult iterate(void);
};
MutableRenderBufferQueryNegativeTest::MutableRenderBufferQueryNegativeTest(EglTestContext &eglTestCtx, const char *name,
const char *description)
: MutableRenderBufferTest(eglTestCtx, name, description, false)
{
}
MutableRenderBufferQueryNegativeTest::~MutableRenderBufferQueryNegativeTest(void)
{
deinit();
}
TestCase::IterateResult MutableRenderBufferQueryNegativeTest::iterate(void)
{
const Library &egl = m_eglTestCtx.getLibrary();
// check that by default the query returns back buffered
EGLint curRenderBuffer = -1;
EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_RENDER_BUFFER, &curRenderBuffer));
if (curRenderBuffer != EGL_BACK_BUFFER)
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Surface didn't default to back-buffered rendering");
return STOP;
}
// check that trying to switch to single-buffer rendering fails when the config bit is not set
EGLBoolean ret = egl.surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER);
EGLint err = egl.getError();
if (ret != EGL_FALSE)
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL,
"eglSurfaceAttrib didn't return false when trying to enable single-buffering on a "
"context without the mutable render buffer bit set");
return STOP;
}
if (err != EGL_BAD_MATCH)
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL,
"eglSurfaceAttrib didn't set the EGL_BAD_MATCH error when trying to enable "
"single-buffering on a context without the mutable render buffer bit set");
return STOP;
}
EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_RENDER_BUFFER, &curRenderBuffer));
if (curRenderBuffer != EGL_BACK_BUFFER)
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Surface didn't stay in back-buffered rendering after error");
return STOP;
}
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
} // namespace
MutableRenderBufferTests::MutableRenderBufferTests(EglTestContext &eglTestCtx)
: TestCaseGroup(eglTestCtx, "mutable_render_buffer", "Mutable render buffer tests")
{
}
void MutableRenderBufferTests::init(void)
{
addChild(new MutableRenderBufferQueryTest(
m_eglTestCtx, "querySurface", "Tests if querySurface returns the correct value after surfaceAttrib is called"));
addChild(new MutableRenderBufferQueryNegativeTest(
m_eglTestCtx, "negativeConfigBit",
"Tests trying to enable single-buffering on a context without the mutable render buffer bit set"));
addChild(new MutableRenderBufferTest(
m_eglTestCtx, "basic", "Tests enabling/disabling single-buffer rendering and checks the buffering behavior",
true));
}
} // namespace egl
} // namespace deqp