diff options
author | Tiago Vignatti <tvignatti@gmail.com> | 2016-08-01 17:30:43 +0300 |
---|---|---|
committer | Bruno de Oliveira Abinader <bruno@mapbox.com> | 2016-11-02 14:22:21 +0200 |
commit | d0add8653a795699ce663f5c7afa9616df73412c (patch) | |
tree | 4ee5c2490011ce79fc5ad1479c1cd3dc36892004 | |
parent | 4d0c42e36ae384102b624476e3d04c94a02190c7 (diff) | |
download | qtlocation-mapboxgl-d0add8653a795699ce663f5c7afa9616df73412c.tar.gz |
Implement EGL platform for headless view
Calling X11 window system is superfluous for headless rendering. This patch
implements EGL platform using GBM, which is slightly more simple than the GLX
path when using X11.
In principle there are no big advantages in terms of performance etc. My
motivation behind this was to get in touch with the code and the project. For
testing I'm using:
$ unset DISPLAY && ./build/linux-x86_64/Debug/mbgl-test
As a follow-up patch, one would now remove the GLX code all over (look for
MBGL_USE_GLX macro).
v2: rebased patch against the new cmake changes; walk through render node to
find a valid one; remove EGLSurface completely cause windows are not needed
here.
-rw-r--r-- | include/mbgl/platform/default/headless_backend.hpp | 13 | ||||
-rw-r--r-- | include/mbgl/platform/default/headless_display.hpp | 6 | ||||
-rw-r--r-- | platform/default/headless_backend_egl.cpp | 121 | ||||
-rw-r--r-- | platform/default/headless_display.cpp | 53 | ||||
-rw-r--r-- | platform/linux/config.cmake | 4 |
5 files changed, 196 insertions, 1 deletions
diff --git a/include/mbgl/platform/default/headless_backend.hpp b/include/mbgl/platform/default/headless_backend.hpp index 2f4886a365..8ee3d387b5 100644 --- a/include/mbgl/platform/default/headless_backend.hpp +++ b/include/mbgl/platform/default/headless_backend.hpp @@ -7,12 +7,19 @@ class QGLWidget; #elif MBGL_USE_CGL #include <OpenGL/OpenGL.h> #elif MBGL_USE_GLX +#define MBGL_USE_EGL 1 +struct gbm_device {}; +typedef void* EGLContext; +typedef void* EGLDisplay; +typedef void* EGLConfig; +#if 0 typedef struct _XDisplay Display; typedef struct __GLXcontextRec* GLXContext; typedef struct __GLXFBConfigRec* GLXFBConfig; typedef long unsigned int XID; typedef XID GLXPbuffer; #endif +#endif #include <mbgl/map/backend.hpp> #include <mbgl/gl/extension.hpp> @@ -64,6 +71,12 @@ private: void *glContext = nullptr; #endif +#if MBGL_USE_EGL + EGLDisplay dpy; + EGLContext glContext = nullptr; + EGLConfig config; +#endif + #if MBGL_USE_GLX Display *xDisplay = nullptr; GLXFBConfig *fbConfigs = nullptr; diff --git a/include/mbgl/platform/default/headless_display.hpp b/include/mbgl/platform/default/headless_display.hpp index f43e61340f..e8f8b7bcf3 100644 --- a/include/mbgl/platform/default/headless_display.hpp +++ b/include/mbgl/platform/default/headless_display.hpp @@ -20,6 +20,12 @@ public: CGLPixelFormatObj pixelFormat = nullptr; #endif +#if MBGL_USE_EGL + int fd; + struct gbm_device *gbm = nullptr; + EGLDisplay dpy = 0; + EGLConfig config = 0; +#endif #if MBGL_USE_GLX Display *xDisplay = nullptr; GLXFBConfig *fbConfigs = nullptr; diff --git a/platform/default/headless_backend_egl.cpp b/platform/default/headless_backend_egl.cpp new file mode 100644 index 0000000000..ee1c875531 --- /dev/null +++ b/platform/default/headless_backend_egl.cpp @@ -0,0 +1,121 @@ +#include <mbgl/platform/default/headless_backend.hpp> +#include <mbgl/platform/default/headless_display.hpp> + +#include <mbgl/platform/log.hpp> + +#include <cassert> + +#include <GL/gl.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <gbm.h> + +namespace mbgl { + +gl::glProc HeadlessView::initializeExtension(const char* name) { + return eglGetProcAddress(name); +} + +void HeadlessView::createContext() { + dpy = display->dpy; + config = display->config; + + assert(dpy != EGL_NO_DISPLAY); + assert(glContext == EGL_NO_CONTEXT); + assert(config != nullptr); + + eglBindAPI(EGL_OPENGL_API); + + glContext = eglCreateContext(dpy, config, EGL_NO_CONTEXT, NULL); + if (glContext == EGL_NO_CONTEXT) { + mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error 0x%04x", + eglGetError()); + throw std::runtime_error("Error creating GL context object"); + } +} + +void HeadlessView::destroyContext() { + if (glContext) { + if (!eglDestroyContext(dpy, glContext)) { + throw std::runtime_error("Failed to destroy context."); + } + + glContext = nullptr; + } +} + +void HeadlessView::resizeFramebuffer() { + const unsigned int w = dimensions[0] * pixelRatio; + const unsigned int h = dimensions[1] * pixelRatio; + + // Create depth/stencil buffer + MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil)); + MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthStencil)); + MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h)); + MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0)); + + MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboColor)); + MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboColor)); + MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h)); + MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0)); + + MBGL_CHECK_ERROR(glGenFramebuffersEXT(1, &fbo)); + MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo)); + + MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fboColor)); + MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, fboDepthStencil)); + + GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)); + + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + std::string error("Couldn't create framebuffer: "); + switch (status) { + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error += "incomplete attachment"); break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "incomplete missing attachment"; break; + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "incomplete dimensions"; break; + case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "incomplete formats"; break; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "incomplete draw buffer"; break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "incomplete read buffer"; break; + case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break; + default: error += "other"; break; + } + throw std::runtime_error(error); + } + + MBGL_CHECK_ERROR(glViewport(0, 0, w, h)); +} + +void HeadlessView::clearBuffers() { + assert(active); + + MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); + + if (fbo) { + MBGL_CHECK_ERROR(glDeleteFramebuffersEXT(1, &fbo)); + fbo = 0; + } + + if (fboColor) { + MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboColor)); + fboColor = 0; + } + + if (fboDepthStencil) { + MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboDepthStencil)); + fboDepthStencil = 0; + } +} + +void HeadlessView::activateContext() { + if (!eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, glContext)) { + throw std::runtime_error("Switching OpenGL context failed.\n"); + } +} + +void HeadlessView::deactivateContext() { + if (!eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + throw std::runtime_error("Removing OpenGL context failed.\n"); + } +} + +} // namespace mbgl diff --git a/platform/default/headless_display.cpp b/platform/default/headless_display.cpp index b98aef7903..09dbb74203 100644 --- a/platform/default/headless_display.cpp +++ b/platform/default/headless_display.cpp @@ -1,5 +1,13 @@ #include <mbgl/platform/default/headless_display.hpp> +#if MBGL_USE_EGL +#include <mbgl/util/string.hpp> +#include <EGL/egl.h> +#include <fcntl.h> +#include <gbm.h> +#include <unistd.h> +#endif + #if MBGL_USE_GLX #include <GL/glx.h> #endif @@ -29,6 +37,46 @@ HeadlessDisplay::HeadlessDisplay() { throw std::runtime_error("No pixel formats found."); } #endif +#if MBGL_USE_EGL + for (int i = 128; i < (128 + 16); i++) { + auto device_name = std::string{ "/dev/dri/renderD" } + mbgl::util::toString(i); + fd = open(device_name.c_str(), O_RDWR); + if (fd > 0) + break; + } + if (fd < 0) { + throw std::runtime_error("Couldn't open drm device."); + } + + gbm = gbm_create_device(fd); + if (gbm == NULL) { + throw std::runtime_error("Couldn't create gbm device."); + } + + dpy = eglGetDisplay(reinterpret_cast<EGLNativeDisplayType>(gbm)); + if (dpy == EGL_NO_DISPLAY) { + throw std::runtime_error("eglGetDisplay() failed."); + } + + EGLint major, minor, n; + if (!eglInitialize(dpy, &major, &minor)) { + throw std::runtime_error("eglInitialize() failed."); + } + + const EGLint attribs[] = { + EGL_SURFACE_TYPE, EGL_DONT_CARE, + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 1, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE + }; + if (!eglChooseConfig(dpy, attribs, &config, 1, &n) || n != 1) { + throw std::runtime_error("Failed to choose argb config."); + } +#endif #if MBGL_USE_GLX if (!XInitThreads()) { @@ -73,6 +121,11 @@ HeadlessDisplay::~HeadlessDisplay() { CGLDestroyPixelFormat(pixelFormat); #endif +#if MBGL_USE_EGL + eglTerminate(dpy); + gbm_device_destroy(gbm); + close(fd); +#endif #if MBGL_USE_GLX XFree(fbConfigs); XCloseDisplay(xDisplay); diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake index 6bde3136f2..c39a3da806 100644 --- a/platform/linux/config.cmake +++ b/platform/linux/config.cmake @@ -42,6 +42,7 @@ macro(mbgl_platform_core) # Headless view PRIVATE platform/default/headless_backend_glx.cpp + PRIVATE platform/default/headless_backend_egl.cpp PRIVATE platform/default/headless_backend.cpp PRIVATE platform/default/headless_display.cpp PRIVATE platform/default/offscreen_view.cpp @@ -64,7 +65,8 @@ macro(mbgl_platform_core) PUBLIC -lz PUBLIC -lcurl PUBLIC -lGL - PUBLIC -lX11 + PUBLIC -lEGL + PUBLIC -lgbm ) endmacro() |