summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiago Vignatti <tvignatti@gmail.com>2016-08-01 17:30:43 +0300
committerBruno de Oliveira Abinader <bruno@mapbox.com>2016-11-02 14:22:21 +0200
commitd0add8653a795699ce663f5c7afa9616df73412c (patch)
tree4ee5c2490011ce79fc5ad1479c1cd3dc36892004
parent4d0c42e36ae384102b624476e3d04c94a02190c7 (diff)
downloadqtlocation-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.hpp13
-rw-r--r--include/mbgl/platform/default/headless_display.hpp6
-rw-r--r--platform/default/headless_backend_egl.cpp121
-rw-r--r--platform/default/headless_display.cpp53
-rw-r--r--platform/linux/config.cmake4
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()