/*------------------------------------------------------------------------- * 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