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