/*------------------------------------------------------------------------- * drawElements Quality Program Tester Core * ---------------------------------------- * * 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 GL context factory using EGL. *//*--------------------------------------------------------------------*/ #include "egluGLContextFactory.hpp" #include "tcuRenderTarget.hpp" #include "tcuPlatform.hpp" #include "tcuCommandLine.hpp" #include "gluDefs.hpp" #include "egluDefs.hpp" #include "egluUtil.hpp" #include "egluGLUtil.hpp" #include "egluNativeWindow.hpp" #include "egluNativePixmap.hpp" #include "egluStrUtil.hpp" #include "eglwLibrary.hpp" #include "eglwEnums.hpp" #include "glwInitFunctions.hpp" #include "glwInitES20Direct.hpp" #include "glwInitES30Direct.hpp" #include "glwInitES31Direct.hpp" #include "glwInitES32Direct.hpp" #include "deDynamicLibrary.hpp" #include "deSTLUtil.hpp" #include "deSharedPtr.hpp" #include #include #include using std::string; using std::vector; // \todo [2014-03-12 pyry] Use command line arguments for libraries? // Default library names #if !defined(DEQP_GLES2_LIBRARY_PATH) #if (DE_OS == DE_OS_WIN32) #define DEQP_GLES2_LIBRARY_PATH "libGLESv2.dll" #else #define DEQP_GLES2_LIBRARY_PATH "libGLESv2.so" #endif #endif #if !defined(DEQP_GLES3_LIBRARY_PATH) #define DEQP_GLES3_LIBRARY_PATH DEQP_GLES2_LIBRARY_PATH #endif #if !defined(DEQP_OPENGL_LIBRARY_PATH) #if (DE_OS == DE_OS_WIN32) #define DEQP_OPENGL_LIBRARY_PATH "opengl32.dll" #else #define DEQP_OPENGL_LIBRARY_PATH "libGL.so" #endif #endif namespace eglu { using namespace eglw; namespace { enum { DEFAULT_OFFSCREEN_WIDTH = 512, DEFAULT_OFFSCREEN_HEIGHT = 512 }; class GetProcFuncLoader : public glw::FunctionLoader { public: GetProcFuncLoader(const Library &egl) : m_egl(egl) { } glw::GenericFuncType get(const char *name) const { return (glw::GenericFuncType)m_egl.getProcAddress(name); } protected: const Library &m_egl; }; class DynamicFuncLoader : public glw::FunctionLoader { public: DynamicFuncLoader(de::DynamicLibrary *library) : m_library(library) { } glw::GenericFuncType get(const char *name) const { return (glw::GenericFuncType)m_library->getFunction(name); } private: de::DynamicLibrary *m_library; }; class RenderContext : public GLRenderContext { public: RenderContext(const NativeDisplayFactory *displayFactory, const NativeWindowFactory *windowFactory, const NativePixmapFactory *pixmapFactory, const glu::RenderConfig &config, const glu::RenderContext *sharedContext = DE_NULL); virtual ~RenderContext(void); virtual glu::ContextType getType(void) const { return m_renderConfig.type; } virtual const glw::Functions &getFunctions(void) const { return m_glFunctions; } virtual const tcu::RenderTarget &getRenderTarget(void) const { return m_glRenderTarget; } virtual void postIterate(void); virtual EGLDisplay getEGLDisplay(void) const { return m_eglDisplay; } virtual EGLContext getEGLContext(void) const { return m_eglContext; } virtual EGLConfig getEGLConfig(void) const { return m_eglConfig; } virtual const eglw::Library &getLibrary(void) const { return m_display->getLibrary(); } virtual eglw::GenericFuncType getProcAddress(const char *name) const; virtual void makeCurrent(void); private: void create(const NativeDisplayFactory *displayFactory, const NativeWindowFactory *windowFactory, const NativePixmapFactory *pixmapFactory, const glu::RenderConfig &config, const glu::RenderContext *sharedContext); void destroy(void); const glu::RenderConfig m_renderConfig; const NativeWindowFactory *const m_nativeWindowFactory; // Stored in case window must be re-created de::SharedPtr m_display; NativeWindow *m_window; NativePixmap *m_pixmap; EGLDisplay m_eglDisplay; EGLConfig m_eglConfig; EGLSurface m_eglSurface; EGLContext m_eglContext; EGLContext m_eglSharedContext; tcu::RenderTarget m_glRenderTarget; de::DynamicLibrary *m_dynamicGLLibrary; glw::Functions m_glFunctions; }; RenderContext::RenderContext(const NativeDisplayFactory *displayFactory, const NativeWindowFactory *windowFactory, const NativePixmapFactory *pixmapFactory, const glu::RenderConfig &config, const glu::RenderContext *sharedContext) : m_renderConfig(config) , m_nativeWindowFactory(windowFactory) , m_display(DE_NULL) , m_window(DE_NULL) , m_pixmap(DE_NULL) , m_eglDisplay(EGL_NO_DISPLAY) , m_eglSurface(EGL_NO_SURFACE) , m_eglContext(EGL_NO_CONTEXT) , m_eglSharedContext(EGL_NO_CONTEXT) , m_dynamicGLLibrary(DE_NULL) { DE_ASSERT(displayFactory); try { create(displayFactory, windowFactory, pixmapFactory, config, sharedContext); } catch (...) { destroy(); throw; } } RenderContext::~RenderContext(void) { try { destroy(); } catch (...) { // destroy() calls EGL functions that are checked and may throw exceptions } delete m_window; delete m_pixmap; delete m_dynamicGLLibrary; } static WindowParams::Visibility getNativeWindowVisibility(glu::RenderConfig::Visibility visibility) { using glu::RenderConfig; switch (visibility) { case RenderConfig::VISIBILITY_HIDDEN: return WindowParams::VISIBILITY_HIDDEN; case RenderConfig::VISIBILITY_VISIBLE: return WindowParams::VISIBILITY_VISIBLE; case RenderConfig::VISIBILITY_FULLSCREEN: return WindowParams::VISIBILITY_FULLSCREEN; default: DE_ASSERT((int)visibility == RenderConfig::DONT_CARE); return WindowParams::VISIBILITY_DONT_CARE; } } typedef std::pair WindowSurfacePair; typedef std::pair PixmapSurfacePair; WindowSurfacePair createWindow(NativeDisplay *nativeDisplay, const NativeWindowFactory *windowFactory, EGLDisplay eglDisplay, EGLConfig eglConfig, const glu::RenderConfig &config) { const int width = (config.width == glu::RenderConfig::DONT_CARE ? WindowParams::SIZE_DONT_CARE : config.width); const int height = (config.height == glu::RenderConfig::DONT_CARE ? WindowParams::SIZE_DONT_CARE : config.height); const WindowParams::Visibility visibility = getNativeWindowVisibility(config.windowVisibility); NativeWindow *nativeWindow = DE_NULL; EGLSurface surface = EGL_NO_SURFACE; const EGLAttrib attribList[] = {EGL_NONE}; nativeWindow = windowFactory->createWindow(nativeDisplay, eglDisplay, eglConfig, &attribList[0], WindowParams(width, height, visibility)); try { surface = eglu::createWindowSurface(*nativeDisplay, *nativeWindow, eglDisplay, eglConfig, attribList); } catch (...) { delete nativeWindow; throw; } return WindowSurfacePair(nativeWindow, surface); } PixmapSurfacePair createPixmap(NativeDisplay *nativeDisplay, const NativePixmapFactory *pixmapFactory, EGLDisplay eglDisplay, EGLConfig eglConfig, const glu::RenderConfig &config) { const int width = (config.width == glu::RenderConfig::DONT_CARE ? DEFAULT_OFFSCREEN_WIDTH : config.width); const int height = (config.height == glu::RenderConfig::DONT_CARE ? DEFAULT_OFFSCREEN_HEIGHT : config.height); NativePixmap *nativePixmap = DE_NULL; EGLSurface surface = EGL_NO_SURFACE; const EGLAttrib attribList[] = {EGL_NONE}; nativePixmap = pixmapFactory->createPixmap(nativeDisplay, eglDisplay, eglConfig, &attribList[0], width, height); try { surface = eglu::createPixmapSurface(*nativeDisplay, *nativePixmap, eglDisplay, eglConfig, attribList); } catch (...) { delete nativePixmap; throw; } return PixmapSurfacePair(nativePixmap, surface); } EGLSurface createPBuffer(const Library &egl, EGLDisplay display, EGLConfig eglConfig, const glu::RenderConfig &config) { const int width = (config.width == glu::RenderConfig::DONT_CARE ? DEFAULT_OFFSCREEN_WIDTH : config.width); const int height = (config.height == glu::RenderConfig::DONT_CARE ? DEFAULT_OFFSCREEN_HEIGHT : config.height); EGLSurface surface; const EGLint attribList[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE}; surface = egl.createPbufferSurface(display, eglConfig, &(attribList[0])); EGLU_CHECK_MSG(egl, "eglCreatePbufferSurface()"); return surface; } void RenderContext::makeCurrent(void) { const Library &egl = m_display->getLibrary(); EGLU_CHECK_CALL(egl, makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)); } glw::GenericFuncType RenderContext::getProcAddress(const char *name) const { return (glw::GenericFuncType)m_display->getLibrary().getProcAddress(name); } void RenderContext::create(const NativeDisplayFactory *displayFactory, const NativeWindowFactory *windowFactory, const NativePixmapFactory *pixmapFactory, const glu::RenderConfig &config, const glu::RenderContext *sharedContext) { glu::RenderConfig::SurfaceType surfaceType = config.surfaceType; DE_ASSERT(displayFactory); if (DE_NULL == sharedContext) m_display = de::SharedPtr(displayFactory->createDisplay()); else { const RenderContext *context = dynamic_cast(sharedContext); m_eglSharedContext = context->m_eglContext; m_display = context->m_display; } m_eglDisplay = eglu::getDisplay(*m_display); const Library &egl = m_display->getLibrary(); { EGLint major = 0; EGLint minor = 0; EGLU_CHECK_CALL(egl, initialize(m_eglDisplay, &major, &minor)); } m_eglConfig = chooseConfig(egl, m_eglDisplay, config); if (surfaceType == glu::RenderConfig::SURFACETYPE_DONT_CARE) { // Choose based on what selected configuration supports const EGLint supportedTypes = eglu::getConfigAttribInt(egl, m_eglDisplay, m_eglConfig, EGL_SURFACE_TYPE); if ((supportedTypes & EGL_WINDOW_BIT) != 0) surfaceType = glu::RenderConfig::SURFACETYPE_WINDOW; else if ((supportedTypes & EGL_PBUFFER_BIT) != 0) surfaceType = glu::RenderConfig::SURFACETYPE_OFFSCREEN_GENERIC; else if ((supportedTypes & EGL_PIXMAP_BIT) != 0) surfaceType = glu::RenderConfig::SURFACETYPE_OFFSCREEN_NATIVE; else throw tcu::NotSupportedError("Selected EGL config doesn't support any surface types", DE_NULL, __FILE__, __LINE__); } switch (surfaceType) { case glu::RenderConfig::SURFACETYPE_WINDOW: { if (windowFactory) { const WindowSurfacePair windowSurface = createWindow(m_display.get(), windowFactory, m_eglDisplay, m_eglConfig, config); m_window = windowSurface.first; m_eglSurface = windowSurface.second; } else throw tcu::NotSupportedError("EGL platform doesn't support windows", DE_NULL, __FILE__, __LINE__); break; } case glu::RenderConfig::SURFACETYPE_OFFSCREEN_NATIVE: { if (pixmapFactory) { const PixmapSurfacePair pixmapSurface = createPixmap(m_display.get(), pixmapFactory, m_eglDisplay, m_eglConfig, config); m_pixmap = pixmapSurface.first; m_eglSurface = pixmapSurface.second; } else throw tcu::NotSupportedError("EGL platform doesn't support pixmaps", DE_NULL, __FILE__, __LINE__); break; } case glu::RenderConfig::SURFACETYPE_OFFSCREEN_GENERIC: m_eglSurface = createPBuffer(egl, m_eglDisplay, m_eglConfig, config); break; default: throw tcu::InternalError("Invalid surface type"); } m_eglContext = createGLContext(egl, m_eglDisplay, m_eglConfig, config.type, m_eglSharedContext, config.resetNotificationStrategy); EGLU_CHECK_CALL(egl, makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)); // Init core functions if (hasExtension(egl, m_eglDisplay, "EGL_KHR_get_all_proc_addresses")) { // Use eglGetProcAddress() for core functions GetProcFuncLoader funcLoader(egl); glu::initCoreFunctions(&m_glFunctions, &funcLoader, config.type.getAPI()); } #if defined(DEQP_GLES2_DIRECT_LINK) else if (config.type.getAPI() == glu::ApiType::es(2, 0)) { glw::initES20Direct(&m_glFunctions); } #endif #if defined(DEQP_GLES3_DIRECT_LINK) else if (config.type.getAPI() == glu::ApiType::es(3, 0)) { glw::initES30Direct(&m_glFunctions); } #endif #if defined(DEQP_GLES31_DIRECT_LINK) else if (config.type.getAPI() == glu::ApiType::es(3, 1)) { glw::initES31Direct(&m_glFunctions); } #endif #if defined(DEQP_GLES32_DIRECT_LINK) else if (config.type.getAPI() == glu::ApiType::es(3, 2)) { glw::initES32Direct(&m_glFunctions); } #endif else { const char *libraryPath = DE_NULL; if (glu::isContextTypeES(config.type)) { if (config.type.getMinorVersion() <= 2) libraryPath = DEQP_GLES2_LIBRARY_PATH; else libraryPath = DEQP_GLES3_LIBRARY_PATH; } else libraryPath = DEQP_OPENGL_LIBRARY_PATH; m_dynamicGLLibrary = new de::DynamicLibrary(libraryPath); DynamicFuncLoader funcLoader(m_dynamicGLLibrary); glu::initCoreFunctions(&m_glFunctions, &funcLoader, config.type.getAPI()); } // Init extension functions { GetProcFuncLoader extLoader(egl); glu::initExtensionFunctions(&m_glFunctions, &extLoader, config.type.getAPI()); } { EGLint width, height, depthBits, stencilBits, numSamples; tcu::PixelFormat pixelFmt; egl.querySurface(m_eglDisplay, m_eglSurface, EGL_WIDTH, &width); egl.querySurface(m_eglDisplay, m_eglSurface, EGL_HEIGHT, &height); egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_RED_SIZE, &pixelFmt.redBits); egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_GREEN_SIZE, &pixelFmt.greenBits); egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_BLUE_SIZE, &pixelFmt.blueBits); egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_ALPHA_SIZE, &pixelFmt.alphaBits); egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_DEPTH_SIZE, &depthBits); egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_STENCIL_SIZE, &stencilBits); egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_SAMPLES, &numSamples); EGLU_CHECK_MSG(egl, "Failed to query config attributes"); m_glRenderTarget = tcu::RenderTarget(width, height, pixelFmt, depthBits, stencilBits, numSamples); } egl.swapInterval(m_eglDisplay, 0); } void RenderContext::destroy(void) { if (m_eglDisplay != EGL_NO_DISPLAY) { const Library &egl = m_display->getLibrary(); EGLU_CHECK_CALL(egl, makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); if (m_eglSurface != EGL_NO_SURFACE) EGLU_CHECK_CALL(egl, destroySurface(m_eglDisplay, m_eglSurface)); if (m_eglContext != EGL_NO_CONTEXT) EGLU_CHECK_CALL(egl, destroyContext(m_eglDisplay, m_eglContext)); if (m_eglSharedContext == EGL_NO_CONTEXT) EGLU_CHECK_CALL(egl, terminate(m_eglDisplay)); m_eglDisplay = EGL_NO_DISPLAY; m_eglSurface = EGL_NO_SURFACE; m_eglContext = EGL_NO_CONTEXT; } delete m_window; delete m_pixmap; delete m_dynamicGLLibrary; m_window = DE_NULL; m_pixmap = DE_NULL; m_dynamicGLLibrary = DE_NULL; } void RenderContext::postIterate(void) { const Library &egl = m_display->getLibrary(); if (m_window) { EGLBoolean swapOk = egl.swapBuffers(m_eglDisplay, m_eglSurface); EGLint error = egl.getError(); const bool badWindow = error == EGL_BAD_SURFACE || error == EGL_BAD_NATIVE_WINDOW; if (!swapOk && !badWindow) throw tcu::ResourceError(string("eglSwapBuffers() failed: ") + getErrorStr(error).toString()); try { m_window->processEvents(); } catch (const WindowDestroyedError &) { tcu::print("Warning: Window destroyed, recreating...\n"); EGLU_CHECK_CALL(egl, makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); EGLU_CHECK_CALL(egl, destroySurface(m_eglDisplay, m_eglSurface)); m_eglSurface = EGL_NO_SURFACE; delete m_window; m_window = DE_NULL; try { WindowSurfacePair windowSurface = createWindow(m_display.get(), m_nativeWindowFactory, m_eglDisplay, m_eglConfig, m_renderConfig); m_window = windowSurface.first; m_eglSurface = windowSurface.second; EGLU_CHECK_CALL(egl, makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)); swapOk = EGL_TRUE; error = EGL_SUCCESS; } catch (const std::exception &e) { if (m_eglSurface) { egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); egl.destroySurface(m_eglDisplay, m_eglSurface); m_eglSurface = EGL_NO_SURFACE; } delete m_window; m_window = DE_NULL; throw tcu::ResourceError(string("Failed to re-create window: ") + e.what()); } } if (!swapOk) { DE_ASSERT(badWindow); throw tcu::ResourceError(string("eglSwapBuffers() failed: ") + getErrorStr(error).toString()); } // Refresh dimensions { int newWidth = 0; int newHeight = 0; egl.querySurface(m_eglDisplay, m_eglSurface, EGL_WIDTH, &newWidth); egl.querySurface(m_eglDisplay, m_eglSurface, EGL_HEIGHT, &newHeight); EGLU_CHECK_MSG(egl, "Failed to query window size"); if (newWidth != m_glRenderTarget.getWidth() || newHeight != m_glRenderTarget.getHeight()) { tcu::print("Warning: Window size changed (%dx%d -> %dx%d), test results might be invalid!\n", m_glRenderTarget.getWidth(), m_glRenderTarget.getHeight(), newWidth, newHeight); m_glRenderTarget = tcu::RenderTarget(newWidth, newHeight, m_glRenderTarget.getPixelFormat(), m_glRenderTarget.getDepthBits(), m_glRenderTarget.getStencilBits(), m_glRenderTarget.getNumSamples()); } } } else m_glFunctions.flush(); } } // namespace GLContextFactory::GLContextFactory(const NativeDisplayFactoryRegistry &displayFactoryRegistry) : glu::ContextFactory("egl", "EGL OpenGL Context") , m_displayFactoryRegistry(displayFactoryRegistry) { } glu::RenderContext *GLContextFactory::createContext(const glu::RenderConfig &config, const tcu::CommandLine &cmdLine, const glu::RenderContext *sharedContext) const { const NativeDisplayFactory &displayFactory = selectNativeDisplayFactory(m_displayFactoryRegistry, cmdLine); const NativeWindowFactory *windowFactory; const NativePixmapFactory *pixmapFactory; try { windowFactory = &selectNativeWindowFactory(displayFactory, cmdLine); } catch (const tcu::NotSupportedError &) { windowFactory = DE_NULL; } try { pixmapFactory = &selectNativePixmapFactory(displayFactory, cmdLine); } catch (const tcu::NotSupportedError &) { pixmapFactory = DE_NULL; } return new RenderContext(&displayFactory, windowFactory, pixmapFactory, config, sharedContext); } } // namespace eglu