From a2294b229a1e2fd70ec139ef56b88d498020a294 Mon Sep 17 00:00:00 2001 From: Simon Fels Date: Tue, 31 Jan 2017 17:02:32 +0100 Subject: [PATCH 1/2] Use a surfaceless EGL context if possible instead of a dummy pbuffer --- src/CMakeLists.txt | 1 + src/anbox/graphics/emugl/Renderer.cpp | 40 ++++++++++++--------- src/anbox/graphics/gl_extensions.h | 52 +++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 16 deletions(-) create mode 100644 src/anbox/graphics/gl_extensions.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9a8fe0a..5675ccb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -125,6 +125,7 @@ set(SOURCES anbox/graphics/program_family.cpp anbox/graphics/primitives.h anbox/graphics/renderer.h + anbox/graphics/gl_extensions.h anbox/graphics/emugl/ColorBuffer.cpp anbox/graphics/emugl/DisplayManager.cpp diff --git a/src/anbox/graphics/emugl/Renderer.cpp b/src/anbox/graphics/emugl/Renderer.cpp index cdea661..664c2f7 100644 --- a/src/anbox/graphics/emugl/Renderer.cpp +++ b/src/anbox/graphics/emugl/Renderer.cpp @@ -23,6 +23,8 @@ #include "OpenGLESDispatch/EGLDispatch.h" +#include "anbox/graphics/gl_extensions.h" + #include "anbox/logger.h" #include @@ -78,7 +80,6 @@ class ColorBufferHelper : public ColorBuffer::Helper { private: Renderer *mFb; }; - } // namespace HandleType Renderer::s_nextHandle = 0; @@ -105,11 +106,9 @@ static char *getGLES1ExtensionString(EGLDisplay p_dpy) { return NULL; } - static const GLint gles1ContextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 1, - EGL_NONE}; + static const GLint gles1ContextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE}; - EGLContext ctx = s_egl.eglCreateContext(p_dpy, config, EGL_NO_CONTEXT, - gles1ContextAttribs); + EGLContext ctx = s_egl.eglCreateContext(p_dpy, config, EGL_NO_CONTEXT, gles1ContextAttribs); if (ctx == EGL_NO_CONTEXT) { ERROR("%s: Could not create GLES 1.x Context!", __FUNCTION__); s_egl.eglDestroySurface(p_dpy, surface); @@ -156,6 +155,12 @@ bool Renderer::initialize(EGLNativeDisplayType nativeDisplay) { return false; } + anbox::graphics::GLExtensions egl_extensions{s_egl.eglQueryString(m_eglDisplay, EGL_EXTENSIONS)}; + + const auto surfaceless_supported = egl_extensions.support("EGL_KHR_surfaceless_context"); + if (!surfaceless_supported) + DEBUG("EGL doesn't support surfaceless context"); + s_egl.eglBindAPI(EGL_OPENGL_ES_API); // If GLES2 plugin was loaded - try to make GLES2 context and @@ -201,24 +206,27 @@ bool Renderer::initialize(EGLNativeDisplayType nativeDisplay) { // The main purpose of it is to solve a "blanking" behaviour we see on // on Mac platform when switching binded drawable for a context however // it is more efficient on other platforms as well. - m_pbufContext = s_egl.eglCreateContext( - m_eglDisplay, m_eglConfig, m_eglContext, glContextAttribs); + m_pbufContext = s_egl.eglCreateContext(m_eglDisplay, m_eglConfig, m_eglContext, glContextAttribs); if (m_pbufContext == EGL_NO_CONTEXT) { ERROR("Failed to create pbuffer context: error=0x%x", s_egl.eglGetError()); free(gles1Extensions); return false; } - // Create a 1x1 pbuffer surface which will be used for binding - // the FB context. The FB output will go to a subwindow, if one exist. - static const EGLint pbufAttribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; + if (!surfaceless_supported) { + // Create a 1x1 pbuffer surface which will be used for binding + // the FB context. The FB output will go to a subwindow, if one exist. + static const EGLint pbufAttribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; - m_pbufSurface = s_egl.eglCreatePbufferSurface( - m_eglDisplay, m_eglConfig, pbufAttribs); - if (m_pbufSurface == EGL_NO_SURFACE) { - ERROR("Failed to create pbuffer surface: error=0x%x", s_egl.eglGetError()); - free(gles1Extensions); - return false; + m_pbufSurface = s_egl.eglCreatePbufferSurface(m_eglDisplay, m_eglConfig, pbufAttribs); + if (m_pbufSurface == EGL_NO_SURFACE) { + ERROR("Failed to create pbuffer surface: error=0x%x", s_egl.eglGetError()); + free(gles1Extensions); + return false; + } + } else { + DEBUG("Using a surfaceless EGL context"); + m_pbufSurface = EGL_NO_SURFACE; } // Make the context current diff --git a/src/anbox/graphics/gl_extensions.h b/src/anbox/graphics/gl_extensions.h new file mode 100644 index 0000000..7b5738f --- /dev/null +++ b/src/anbox/graphics/gl_extensions.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_GRAPHICS_GL_EXTENSIONS_H_ +#define ANBOX_GRAPHICS_GL_EXTENSIONS_H_ + +#include +#include + +namespace anbox { +namespace graphics { +class GLExtensions { + public: + GLExtensions(char const* extensions) : extensions{extensions} { + if (!extensions) + throw std::runtime_error("Couldn't get list of GL extensions"); + } + + bool support(char const* ext) const { + char const* ext_ptr = extensions; + size_t const len = strlen(ext); + while ((ext_ptr = strstr(ext_ptr, ext)) != nullptr) { + if (ext_ptr[len] == ' ' || ext_ptr[len] == '\0') + break; + ext_ptr += len; + } + return ext_ptr != nullptr; + } + + char const* raw() { return extensions; } + + private: + char const* extensions; +}; +} // namespace graphics +} // namespace anbox + +#endif From eb7640fb8694ddb0a819650c84ccfdbe58a31c46 Mon Sep 17 00:00:00 2001 From: Simon Fels Date: Tue, 28 Feb 2017 17:06:55 +0100 Subject: [PATCH 2/2] Use extension helper class also to check GL ES extensions --- src/anbox/graphics/emugl/Renderer.cpp | 82 ++------------------------- 1 file changed, 5 insertions(+), 77 deletions(-) diff --git a/src/anbox/graphics/emugl/Renderer.cpp b/src/anbox/graphics/emugl/Renderer.cpp index 664c2f7..f4e5a76 100644 --- a/src/anbox/graphics/emugl/Renderer.cpp +++ b/src/anbox/graphics/emugl/Renderer.cpp @@ -84,55 +84,6 @@ class ColorBufferHelper : public ColorBuffer::Helper { HandleType Renderer::s_nextHandle = 0; -static char *getGLES1ExtensionString(EGLDisplay p_dpy) { - EGLConfig config; - EGLSurface surface; - - static const GLint configAttribs[] = {EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, - EGL_NONE}; - - int n; - if (!s_egl.eglChooseConfig(p_dpy, configAttribs, &config, 1, &n) || n == 0) { - ERROR("%s: Could not find GLES 1.x config!", __FUNCTION__); - return NULL; - } - - static const EGLint pbufAttribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; - - surface = s_egl.eglCreatePbufferSurface(p_dpy, config, pbufAttribs); - if (surface == EGL_NO_SURFACE) { - ERROR("%s: Could not create GLES 1.x Pbuffer!", __FUNCTION__); - return NULL; - } - - static const GLint gles1ContextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE}; - - EGLContext ctx = s_egl.eglCreateContext(p_dpy, config, EGL_NO_CONTEXT, gles1ContextAttribs); - if (ctx == EGL_NO_CONTEXT) { - ERROR("%s: Could not create GLES 1.x Context!", __FUNCTION__); - s_egl.eglDestroySurface(p_dpy, surface); - return NULL; - } - - if (!s_egl.eglMakeCurrent(p_dpy, surface, surface, ctx)) { - ERROR("%s: Could not make GLES 1.x context current!", __FUNCTION__); - s_egl.eglDestroySurface(p_dpy, surface); - s_egl.eglDestroyContext(p_dpy, ctx); - return NULL; - } - - // the string pointer may become invalid when the context is destroyed - const char *s = reinterpret_cast(s_gles1.glGetString(GL_EXTENSIONS)); - char *extString = strdup(s ? s : ""); - - s_egl.eglMakeCurrent(p_dpy, NULL, NULL, NULL); - s_egl.eglDestroyContext(p_dpy, ctx); - s_egl.eglDestroySurface(p_dpy, surface); - - return extString; -} - void Renderer::finalize() { m_colorbuffers.clear(); m_windows.clear(); @@ -163,15 +114,6 @@ bool Renderer::initialize(EGLNativeDisplayType nativeDisplay) { s_egl.eglBindAPI(EGL_OPENGL_ES_API); - // If GLES2 plugin was loaded - try to make GLES2 context and - // get GLES2 extension string - char *gles1Extensions = NULL; - gles1Extensions = getGLES1ExtensionString(m_eglDisplay); - if (!gles1Extensions) { - ERROR("Failed to obtain GLES 2.x extensions string!"); - return false; - } - // Create EGL context for framebuffer post rendering. GLint surfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT; const GLint configAttribs[] = {EGL_RED_SIZE, 1, @@ -185,7 +127,6 @@ bool Renderer::initialize(EGLNativeDisplayType nativeDisplay) { if (!s_egl.eglChooseConfig(m_eglDisplay, configAttribs, &m_eglConfig, 1, &n)) { ERROR("Failed to select EGL configuration"); - free(gles1Extensions); return false; } @@ -196,7 +137,6 @@ bool Renderer::initialize(EGLNativeDisplayType nativeDisplay) { EGL_NO_CONTEXT, glContextAttribs); if (m_eglContext == EGL_NO_CONTEXT) { ERROR("Failed to create context: error=0x%x", s_egl.eglGetError()); - free(gles1Extensions); return false; } @@ -209,7 +149,6 @@ bool Renderer::initialize(EGLNativeDisplayType nativeDisplay) { m_pbufContext = s_egl.eglCreateContext(m_eglDisplay, m_eglConfig, m_eglContext, glContextAttribs); if (m_pbufContext == EGL_NO_CONTEXT) { ERROR("Failed to create pbuffer context: error=0x%x", s_egl.eglGetError()); - free(gles1Extensions); return false; } @@ -221,7 +160,6 @@ bool Renderer::initialize(EGLNativeDisplayType nativeDisplay) { m_pbufSurface = s_egl.eglCreatePbufferSurface(m_eglDisplay, m_eglConfig, pbufAttribs); if (m_pbufSurface == EGL_NO_SURFACE) { ERROR("Failed to create pbuffer surface: error=0x%x", s_egl.eglGetError()); - free(gles1Extensions); return false; } } else { @@ -233,23 +171,13 @@ bool Renderer::initialize(EGLNativeDisplayType nativeDisplay) { ScopedBind bind(this); if (!bind.isValid()) { ERROR("Failed to make current"); - free(gles1Extensions); return false; } - // Initilize framebuffer capabilities - auto has_gl_oes_image = strstr(gles1Extensions, "GL_OES_EGL_image") != NULL; - free(gles1Extensions); - gles1Extensions = NULL; - - const char *eglExtensions = - s_egl.eglQueryString(m_eglDisplay, EGL_EXTENSIONS); - - if (eglExtensions && has_gl_oes_image) { - m_caps.has_eglimage_texture_2d = - strstr(eglExtensions, "EGL_KHR_gl_texture_2D_image") != NULL; - m_caps.has_eglimage_renderbuffer = - strstr(eglExtensions, "EGL_KHR_gl_renderbuffer_image") != NULL; + anbox::graphics::GLExtensions gl_extensions{reinterpret_cast(s_gles2.glGetString(GL_EXTENSIONS))}; + if (gl_extensions.support("GL_OES_EGL_image")) { + m_caps.has_eglimage_texture_2d = egl_extensions.support("EGL_KHR_gl_texture_2D_image"); + m_caps.has_eglimage_renderbuffer = egl_extensions.support("EGL_KHR_gl_renderbuffer_image"); } else { m_caps.has_eglimage_texture_2d = false; m_caps.has_eglimage_renderbuffer = false; @@ -258,7 +186,7 @@ bool Renderer::initialize(EGLNativeDisplayType nativeDisplay) { // Fail initialization if not all of the following extensions // exist: // EGL_KHR_gl_texture_2d_image - // GL_OES_EGL_IMAGE (by both GLES implementations [1 and 2]) + // GL_OES_EGL_IMAGE if (!m_caps.has_eglimage_texture_2d) { ERROR("Failed: Missing egl_image related extension(s)"); bind.release();