/*-------------------------------------------------------------------------
* drawElements Quality Program EGL Module
* ---------------------------------------
*
* Copyright 2014 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 Base class for rendering tests.
*//*--------------------------------------------------------------------*/
#include "teglRenderCase.hpp"
#include "teglSimpleConfigCase.hpp"
#include "egluNativeDisplay.hpp"
#include "egluNativeWindow.hpp"
#include "egluNativePixmap.hpp"
#include "egluUtil.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuTestLog.hpp"
#include "tcuCommandLine.hpp"
#include "deStringUtil.hpp"
#include "deUniquePtr.hpp"
#include <algorithm>
#include <iterator>
#include <memory>
#include <set>
#include <EGL/eglext.h>
#if !defined(EGL_OPENGL_ES3_BIT_KHR)
# define EGL_OPENGL_ES3_BIT_KHR 0x0040
#endif
#if !defined(EGL_CONTEXT_MAJOR_VERSION_KHR)
# define EGL_CONTEXT_MAJOR_VERSION_KHR EGL_CONTEXT_CLIENT_VERSION
#endif
using std::string;
using std::vector;
using std::set;
using tcu::TestLog;
namespace deqp
{
namespace egl
{
// \todo [2013-04-24 pyry] Should we instead store surface bit somewhere?
template<class Derived, class Base>
inline bool instanceOf (Base& obj)
{
return dynamic_cast<Derived*>(&obj) != DE_NULL;
}
static void postSurface (tcu::egl::Surface& surface)
{
const bool isWindow = instanceOf<tcu::egl::WindowSurface>(surface);
const bool isPixmap = instanceOf<tcu::egl::PixmapSurface>(surface);
const bool isPbuffer = instanceOf<tcu::egl::PbufferSurface>(surface);
DE_ASSERT((isWindow?1:0) + (isPixmap?1:0) + (isPbuffer?1:0) == 1);
if (isWindow)
{
tcu::egl::WindowSurface& window = static_cast<tcu::egl::WindowSurface&>(surface);
window.swapBuffers();
}
else if (isPixmap)
{
TCU_CHECK_EGL_CALL(eglWaitClient());
}
else
{
DE_ASSERT(isPbuffer);
DE_UNREF(isPbuffer);
TCU_CHECK_EGL_CALL(eglWaitClient());
}
}
// RenderCase
RenderCase::RenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint apiMask, EGLint surfaceTypeMask, const vector<EGLint>& configIds)
: SimpleConfigCase (eglTestCtx, name, description, configIds)
, m_apiMask (apiMask)
, m_surfaceTypeMask (surfaceTypeMask)
{
}
RenderCase::~RenderCase (void)
{
}
EGLint RenderCase::getSupportedApis (void)
{
EGLint apiMask = 0;
#if defined(DEQP_SUPPORT_GLES2)
apiMask |= EGL_OPENGL_ES2_BIT;
#endif
#if defined(DEQP_SUPPORT_GLES3)
apiMask |= EGL_OPENGL_ES3_BIT_KHR;
#endif
#if defined(DEQP_SUPPORT_GLES1)
apiMask |= EGL_OPENGL_ES_BIT;
#endif
#if defined(DEQP_SUPPORT_VG)
apiMask |= EGL_OPENVG_BIT;
#endif
return apiMask;
}
void RenderCase::executeForConfig (tcu::egl::Display& defaultDisplay, EGLConfig config)
{
tcu::TestLog& log = m_testCtx.getLog();
int width = 128;
int height = 128;
EGLint configId = defaultDisplay.getConfigAttrib(config, EGL_CONFIG_ID);
bool isOk = true;
string failReason = "";
if (m_surfaceTypeMask & EGL_WINDOW_BIT)
{
tcu::ScopedLogSection(log, (string("Config") + de::toString(configId) + "-Window").c_str(),
(string("Config ID ") + de::toString(configId) + ", window surface").c_str());
try
{
tcu::egl::Display& display = m_eglTestCtx.getDisplay();
de::UniquePtr<eglu::NativeWindow> window (m_eglTestCtx.createNativeWindow(display.getEGLDisplay(), config, DE_NULL, width, height, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
EGLSurface eglSurface = createWindowSurface(m_eglTestCtx.getNativeDisplay(), *window, display.getEGLDisplay(), config, DE_NULL);
tcu::egl::WindowSurface surface (display, eglSurface);
executeForSurface(display, surface, config);
}
catch (const tcu::TestError& e)
{
log << e;
isOk = false;
failReason = e.what();
}
}
if (m_surfaceTypeMask & EGL_PIXMAP_BIT)
{
tcu::ScopedLogSection(log, (string("Config") + de::toString(configId) + "-Pixmap").c_str(),
(string("Config ID ") + de::toString(configId) + ", pixmap surface").c_str());
try
{
tcu::egl::Display& display = m_eglTestCtx.getDisplay();
std::auto_ptr<eglu::NativePixmap> pixmap (m_eglTestCtx.createNativePixmap(display.getEGLDisplay(), config, DE_NULL, width, height));
EGLSurface eglSurface = createPixmapSurface(m_eglTestCtx.getNativeDisplay(), *pixmap, display.getEGLDisplay(), config, DE_NULL);
tcu::egl::PixmapSurface surface (display, eglSurface);
executeForSurface(display, surface, config);
}
catch (const tcu::TestError& e)
{
log << e;
isOk = false;
failReason = e.what();
}
}
if (m_surfaceTypeMask & EGL_PBUFFER_BIT)
{
tcu::ScopedLogSection(log, (string("Config") + de::toString(configId) + "-Pbuffer").c_str(),
(string("Config ID ") + de::toString(configId) + ", pbuffer surface").c_str());
try
{
EGLint surfaceAttribs[] =
{
EGL_WIDTH, width,
EGL_HEIGHT, height,
EGL_NONE
};
tcu::egl::PbufferSurface surface(defaultDisplay, config, surfaceAttribs);
executeForSurface(defaultDisplay, surface, config);
}
catch (const tcu::TestError& e)
{
log << e;
isOk = false;
failReason = e.what();
}
}
if (!isOk && m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, failReason.c_str());
}
// SingleContextRenderCase
SingleContextRenderCase::SingleContextRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint apiMask, EGLint surfaceTypeMask, const std::vector<EGLint>& configIds)
: RenderCase(eglTestCtx, name, description, apiMask, surfaceTypeMask, configIds)
{
}
SingleContextRenderCase::~SingleContextRenderCase (void)
{
}
void SingleContextRenderCase::executeForSurface (tcu::egl::Display& display, tcu::egl::Surface& surface, EGLConfig config)
{
EGLint supportedApis = getSupportedApis();
const EGLint apis[] = { EGL_OPENGL_ES2_BIT, EGL_OPENGL_ES3_BIT_KHR, EGL_OPENGL_ES_BIT, EGL_OPENVG_BIT };
tcu::TestLog& log = m_testCtx.getLog();
// Check if case is supported
if ((m_apiMask & supportedApis) != m_apiMask)
throw tcu::NotSupportedError("Client APIs not supported", "", __FILE__, __LINE__);
for (int apiNdx = 0; apiNdx < DE_LENGTH_OF_ARRAY(apis); apiNdx++)
{
EGLint apiBit = apis[apiNdx];
if ((apiBit & m_apiMask) == 0)
continue; // Skip this api.
EGLint api = EGL_NONE;
const char* apiName = DE_NULL;
vector<EGLint> contextAttribs;
// Select api enum and build context attributes.
switch (apiBit)
{
case EGL_OPENGL_ES2_BIT:
api = EGL_OPENGL_ES_API;
apiName = "OpenGL ES 2.x";
contextAttribs.push_back(EGL_CONTEXT_CLIENT_VERSION);
contextAttribs.push_back(2);
break;
case EGL_OPENGL_ES3_BIT_KHR:
api = EGL_OPENGL_ES_API;
apiName = "OpenGL ES 3.x";
contextAttribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
contextAttribs.push_back(3);
break;
case EGL_OPENGL_ES_BIT:
api = EGL_OPENGL_ES_API;
apiName = "OpenGL ES 1.x";
contextAttribs.push_back(EGL_CONTEXT_CLIENT_VERSION);
contextAttribs.push_back(1);
break;
case EGL_OPENVG_BIT:
api = EGL_OPENVG_API;
apiName = "OpenVG";
break;
default:
DE_ASSERT(DE_FALSE);
}
contextAttribs.push_back(EGL_NONE);
log << TestLog::Message << apiName << TestLog::EndMessage;
tcu::egl::Context context(display, config, &contextAttribs[0], api);
context.makeCurrent(surface, surface);
executeForContext(display, context, surface, apiBit);
// Call SwapBuffers() / WaitClient() to finish rendering
postSurface(surface);
}
}
// MultiContextRenderCase
MultiContextRenderCase::MultiContextRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const vector<EGLint>& configIds, int numContextsPerApi)
: RenderCase (eglTestCtx, name, description, api, surfaceType, configIds)
, m_numContextsPerApi (numContextsPerApi)
{
}
MultiContextRenderCase::~MultiContextRenderCase (void)
{
}
void MultiContextRenderCase::executeForSurface (tcu::egl::Display& display, tcu::egl::Surface& surface, EGLConfig config)
{
vector<std::pair<EGLint, tcu::egl::Context*> > contexts;
contexts.reserve(3*m_numContextsPerApi); // 3 types of contexts at maximum.
try
{
// Create contexts that will participate in rendering.
for (int ndx = 0; ndx < m_numContextsPerApi; ndx++)
{
if (m_apiMask & EGL_OPENGL_ES2_BIT)
{
static const EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
contexts.push_back(std::make_pair(EGL_OPENGL_ES2_BIT, new tcu::egl::Context(display, config, &attribs[0], EGL_OPENGL_ES_API)));
}
if (m_apiMask & EGL_OPENGL_ES3_BIT_KHR)
{
static const EGLint attribs[] = { EGL_CONTEXT_MAJOR_VERSION_KHR, 3, EGL_NONE };
contexts.push_back(std::make_pair(EGL_OPENGL_ES3_BIT_KHR, new tcu::egl::Context(display, config, &attribs[0], EGL_OPENGL_ES_API)));
}
if (m_apiMask & EGL_OPENGL_ES_BIT)
{
static const EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE };
contexts.push_back(std::make_pair(EGL_OPENGL_ES_BIT, new tcu::egl::Context(display, config, &attribs[0], EGL_OPENGL_ES_API)));
}
if (m_apiMask & EGL_OPENVG_BIT)
{
static const EGLint attribs[] = { EGL_NONE };
contexts.push_back(std::make_pair(EGL_OPENVG_BIT, new tcu::egl::Context(display, config, &attribs[0], EGL_OPENVG_API)));
}
}
// Execute for contexts.
executeForContexts(display, surface, config, contexts);
}
catch (const std::exception&)
{
// Make sure all contexts have been destroyed.
for (vector<std::pair<EGLint, tcu::egl::Context*> >::iterator i = contexts.begin(); i != contexts.end(); i++)
delete i->second;
throw;
}
// Destroy contexts.
for (vector<std::pair<EGLint, tcu::egl::Context*> >::iterator i = contexts.begin(); i != contexts.end(); i++)
delete i->second;
}
// Utilities
void addRenderConfigIdSet (
vector<RenderConfigIdSet>& configSets,
const vector<eglu::ConfigInfo>& configInfos,
const eglu::FilterList& baseFilters,
const char* name,
tcu::RGBA colorBits,
EGLint surfaceType)
{
eglu::FilterList filters = baseFilters;
filters << (eglu::ConfigColorBits() == colorBits) << (eglu::ConfigSurfaceType() & surfaceType);
vector<EGLint> matchingConfigs;
for (vector<eglu::ConfigInfo>::const_iterator configIter = configInfos.begin(); configIter != configInfos.end(); configIter++)
{
if (!filters.match(*configIter))
continue;
matchingConfigs.push_back(configIter->configId);
}
configSets.push_back(RenderConfigIdSet(name, "", matchingConfigs, surfaceType));
}
void addRenderConfigIdSet (
vector<RenderConfigIdSet>& configSets,
const vector<eglu::ConfigInfo>& configInfos,
const eglu::FilterList& baseFilters,
const char* name,
tcu::RGBA colorBits)
{
addRenderConfigIdSet(configSets, configInfos, baseFilters, (string(name) + "_window").c_str(), colorBits, EGL_WINDOW_BIT);
addRenderConfigIdSet(configSets, configInfos, baseFilters, (string(name) + "_pixmap").c_str(), colorBits, EGL_PIXMAP_BIT);
addRenderConfigIdSet(configSets, configInfos, baseFilters, (string(name) + "_pbuffer").c_str(), colorBits, EGL_PBUFFER_BIT);
}
void getDefaultRenderConfigIdSets (vector<RenderConfigIdSet>& configSets, const vector<eglu::ConfigInfo>& configInfos, const eglu::FilterList& baseFilters)
{
using tcu::RGBA;
addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgb565", RGBA(5, 6, 5, 0));
addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgb888", RGBA(8, 8, 8, 0));
addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgba4444", RGBA(4, 4, 4, 4));
addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgba5551", RGBA(5, 5, 5, 1));
addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgba8888", RGBA(8, 8, 8, 8));
// Add other config ids to "other" set
{
set<EGLint> usedConfigs;
vector<EGLint> otherCfgSet;
for (vector<RenderConfigIdSet>::const_iterator setIter = configSets.begin(); setIter != configSets.end(); setIter++)
{
const vector<EGLint>& setCfgs = setIter->getConfigIds();
for (vector<EGLint>::const_iterator i = setCfgs.begin(); i != setCfgs.end(); i++)
usedConfigs.insert(*i);
}
for (vector<eglu::ConfigInfo>::const_iterator cfgIter = configInfos.begin(); cfgIter != configInfos.end(); cfgIter++)
{
if (!baseFilters.match(*cfgIter))
continue;
EGLint id = cfgIter->configId;
if (usedConfigs.find(id) == usedConfigs.end())
otherCfgSet.push_back(id);
}
configSets.push_back(RenderConfigIdSet("other", "", otherCfgSet, EGL_WINDOW_BIT|EGL_PIXMAP_BIT|EGL_PBUFFER_BIT));
}
}
} // egl
} // deqp