Skip to content

Instantly share code, notes, and snippets.

@tiagovignatti
Created August 1, 2016 14:42
Show Gist options
  • Save tiagovignatti/7aaceebb220053bba7d171e4d8425d52 to your computer and use it in GitHub Desktop.
Save tiagovignatti/7aaceebb220053bba7d171e4d8425d52 to your computer and use it in GitHub Desktop.
commit 23b366e4738a56b0fa9fc376f1241aec8f0c7cf4
Author: Tiago Vignatti <[email protected]>
Date: Mon Aug 1 17:30:43 2016 +0300
[linux] WIP: Implement EGL platform in for headless view
To call the X11 windowing system through GLX is superfluous for headless
rendering. This patch implements EGL platform using GBM, which should be
slightly more simple than the GLX.
TODO:
- Call two context in a row seems to be broken still.
- Remove GLX code all over
- Fix the remaining "XXX" in the code
---
include/mbgl/platform/default/headless_display.hpp | 6 +
include/mbgl/platform/default/headless_view.hpp | 17 +++
mbgl.gypi | 12 ++
platform/default/headless_display.cpp | 50 +++++++++
platform/default/headless_view_egl.cpp | 125 +++++++++++++++++++++
platform/linux/platform.gyp | 2 +-
6 files changed, 211 insertions(+), 1 deletion(-)
diff --git a/include/mbgl/platform/default/headless_display.hpp b/include/mbgl/platform/default/headless_display.hpp
index edcc905..87b5f02 100644
--- a/include/mbgl/platform/default/headless_display.hpp
+++ b/include/mbgl/platform/default/headless_display.hpp
@@ -13,6 +13,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/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp
index e3acc8e..def927d 100644
--- a/include/mbgl/platform/default/headless_view.hpp
+++ b/include/mbgl/platform/default/headless_view.hpp
@@ -8,6 +8,14 @@
#define MBGL_USE_CGL 1
#endif
#else
+#define MBGL_USE_EGL 1
+struct gbm_surface {};
+struct gbm_device {};
+typedef void* EGLContext;
+typedef void* EGLDisplay;
+typedef void* EGLConfig;
+typedef void* EGLSurface;
+#if 0
#define GL_GLEXT_PROTOTYPES
#define MBGL_USE_GLX 1
typedef struct _XDisplay Display;
@@ -16,6 +24,7 @@ typedef struct __GLXFBConfigRec* GLXFBConfig;
typedef long unsigned int XID;
typedef XID GLXPbuffer;
#endif
+#endif
#include <mbgl/mbgl.hpp>
#include <mbgl/gl/gl.hpp>
@@ -71,6 +80,14 @@ private:
void *glContext = nullptr;
#endif
+#if MBGL_USE_EGL
+ EGLDisplay dpy;
+ EGLContext glContext = nullptr;
+ EGLConfig config;
+ EGLSurface surface;
+ struct gbm_surface *gs = nullptr;
+#endif
+
#if MBGL_USE_GLX
Display *xDisplay = nullptr;
GLXFBConfig *fbConfigs = nullptr;
diff --git a/mbgl.gypi b/mbgl.gypi
index 7f31d46..64da35e 100644
--- a/mbgl.gypi
+++ b/mbgl.gypi
@@ -264,6 +264,18 @@
},
}],
+ ['headless_lib == "egl"', {
+ 'sources': [
+ 'platform/default/headless_display.cpp',
+ 'platform/default/headless_view.cpp',
+ 'platform/default/headless_view_egl.cpp',
+ ],
+
+ 'link_settings': {
+ 'libraries': [ '-lGL -lEGL -lgbm -ldrm' ],
+ },
+ }],
+
['loop_lib == "darwin"', {
'sources': [
'platform/darwin/src/async_task.cpp',
diff --git a/platform/default/headless_display.cpp b/platform/default/headless_display.cpp
index 8b9f3fe..20efbee 100644
--- a/platform/default/headless_display.cpp
+++ b/platform/default/headless_display.cpp
@@ -3,12 +3,32 @@
#include <cstring>
#include <stdexcept>
+#if MBGL_USE_EGL
+#include <EGL/egl.h>
+#include <fcntl.h>
+#include <gbm.h>
+#include <unistd.h>
+#endif
+
#if MBGL_USE_GLX
#include <GL/glx.h>
#endif
namespace mbgl {
+#if MBGL_USE_EGL
+static const EGLint attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ 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
+};
+#endif
+
HeadlessDisplay::HeadlessDisplay() {
#if MBGL_USE_CGL
// TODO: test if OpenGL 4.1 with GL_ARB_ES2_compatibility is supported
@@ -29,6 +49,32 @@ HeadlessDisplay::HeadlessDisplay() {
}
#endif
+#if MBGL_USE_EGL
+ /* XXX(vignatti): this code seems to leak everywhere */
+ const char device_name[] = "/dev/dri/renderD128";
+ fd = open(device_name, O_RDWR);
+ 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\n");
+ }
+ eglBindAPI(EGL_OPENGL_API);
+ if (!eglChooseConfig(dpy, attribs, &config, 1, &n) || n != 1) {
+ throw std::runtime_error("failed to choose argb config\n");
+ }
+#endif
+
#if MBGL_USE_GLX
if (!XInitThreads()) {
throw std::runtime_error("Failed to XInitThreads.");
@@ -72,6 +118,10 @@ HeadlessDisplay::~HeadlessDisplay() {
CGLDestroyPixelFormat(pixelFormat);
#endif
+#if MBGL_USE_EGL
+ gbm_device_destroy(gbm);
+ close(fd);
+#endif
#if MBGL_USE_GLX
XFree(fbConfigs);
XCloseDisplay(xDisplay);
diff --git a/platform/default/headless_view_egl.cpp b/platform/default/headless_view_egl.cpp
new file mode 100644
index 0000000..3eb749e
--- /dev/null
+++ b/platform/default/headless_view_egl.cpp
@@ -0,0 +1,125 @@
+#include <mbgl/platform/default/headless_view.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;
+
+ glContext = eglCreateContext(dpy, config, EGL_NO_CONTEXT, NULL);
+ if (glContext == NULL) {
+ throw std::runtime_error("Error creating GL context object.");
+ }
+
+ /* XXX(vignatti): this size */
+ gs = gbm_surface_create(display->gbm, 800, 600,
+ GBM_BO_FORMAT_XRGB8888,
+ GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+ if (gs == NULL) {
+ throw std::runtime_error("unable to create gbm surface\n");
+ }
+
+ surface = eglCreateWindowSurface(dpy, config, reinterpret_cast<EGLNativeWindowType>(gs), NULL);
+ if (surface == EGL_NO_SURFACE) {
+ throw std::runtime_error("failed to create surface\n");
+ }
+}
+
+void HeadlessView::destroyContext() {
+ eglDestroyContext(dpy, glContext);
+
+ gbm_surface_destroy(gs);
+ if (surface) {
+ eglDestroySurface(dpy, surface);
+ surface = 0;
+ }
+}
+
+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, surface, 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/linux/platform.gyp b/platform/linux/platform.gyp
index 7adf533..7cf4652 100644
--- a/platform/linux/platform.gyp
+++ b/platform/linux/platform.gyp
@@ -1,7 +1,7 @@
{
'variables': {
'loop_lib': 'uv',
- 'headless_lib': 'glx',
+ 'headless_lib': 'egl',
'coverage': '<!(echo $ENABLE_COVERAGE)>',
},
'conditions': [
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment