summaryrefslogtreecommitdiff
path: root/common/headless_view.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/headless_view.cpp')
-rw-r--r--common/headless_view.cpp244
1 files changed, 161 insertions, 83 deletions
diff --git a/common/headless_view.cpp b/common/headless_view.cpp
index ace41d38c0..d1d13db5f8 100644
--- a/common/headless_view.cpp
+++ b/common/headless_view.cpp
@@ -1,88 +1,158 @@
#include "headless_view.hpp"
-#include <mbgl/util/timer.hpp>
+#include "headless_display.hpp"
#include <stdexcept>
+#include <sstream>
+#include <string>
+
+#if MBGL_USE_GLX
+#ifdef GLX_ARB_create_context
+static PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = nullptr;
+#endif
+#endif
namespace mbgl {
-HeadlessView::HeadlessView() {
-#if MBGL_USE_CGL
- // TODO: test if OpenGL 4.1 with GL_ARB_ES2_compatibility is supported
- // If it is, use kCGLOGLPVersion_3_2_Core and enable that extension.
- CGLPixelFormatAttribute attributes[] = {
- kCGLPFAOpenGLProfile,
- (CGLPixelFormatAttribute) kCGLOGLPVersion_Legacy,
- kCGLPFAAccelerated,
- (CGLPixelFormatAttribute) 0
- };
+HeadlessView::HeadlessView()
+ : display_(std::make_shared<HeadlessDisplay>()) {
+ createContext();
+}
- CGLPixelFormatObj pixelFormat;
- GLint num;
- CGLError error = CGLChoosePixelFormat(attributes, &pixelFormat, &num);
- if (error) {
- fprintf(stderr, "Error pixel format\n");
- return;
+HeadlessView::HeadlessView(std::shared_ptr<HeadlessDisplay> display)
+ : display_(display) {
+ createContext();
+}
+
+
+#if MBGL_USE_GLX
+#ifdef GLX_ARB_create_context
+
+// These are all of the OpenGL Core profile version that we know about.
+struct core_profile_version { int major, minor; };
+static const core_profile_version core_profile_versions[] = {
+ {4, 5},
+ {4, 4},
+ {4, 3},
+ {4, 2},
+ {4, 1},
+ {4, 0},
+ {3, 3},
+ {3, 2},
+ {3, 1},
+ {3, 0},
+ {0, 0},
+};
+
+GLXContext createCoreProfile(Display *dpy, GLXFBConfig fbconfig) {
+ static bool context_creation_failed = false;
+ GLXContext ctx = 0;
+
+ // Set the Error Handler to avoid crashing the program when the context creation fails.
+ // It is expected that some context creation attempts fail, e.g. because the OpenGL
+ // implementation does not support the version we're requesting.
+ int (*previous_error_handler)(Display *, XErrorEvent *) = XSetErrorHandler([](Display *, XErrorEvent *) {
+ context_creation_failed = true;
+ return 0;
+ });
+
+ // Try to create core profiles from the highest known version on down.
+ for (int i = 0; !ctx && core_profile_versions[i].major; i++) {
+ context_creation_failed = false;
+ const int context_flags[] = {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, core_profile_versions[i].major,
+ GLX_CONTEXT_MINOR_VERSION_ARB, core_profile_versions[i].minor,
+ None
+ };
+ ctx = glXCreateContextAttribsARB(dpy, fbconfig, 0, True, context_flags);
+ if (context_creation_failed) {
+ ctx = 0;
+ }
}
- error = CGLCreateContext(pixelFormat, NULL, &gl_context);
- CGLDestroyPixelFormat(pixelFormat);
+ // Restore the old error handler.
+ XSetErrorHandler(previous_error_handler);
+ return ctx;
+}
+#endif
+#endif
+
+void HeadlessView::createContext() {
+#if MBGL_USE_CGL
+ CGLError error = CGLCreateContext(display_->pixelFormat, NULL, &gl_context);
if (error) {
- fprintf(stderr, "Error creating GL context object\n");
- return;
+ throw std::runtime_error("Error creating GL context object\n");
+ }
+
+ error = CGLEnable(gl_context, kCGLCEMPEngine);
+ if (error != kCGLNoError ) {
+ throw std::runtime_error("Error enabling OpenGL multithreading\n");
}
#endif
#if MBGL_USE_GLX
- x_display = XOpenDisplay(0);
-
- if (x_display == nullptr) {
- throw std::runtime_error("Failed to open X display");
+#ifdef GLX_ARB_create_context
+ if (glXCreateContextAttribsARB == nullptr) {
+ glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddressARB((const GLubyte *)"glXCreateContextAttribsARB");
}
+#endif
- static int pixelFormat[] = {
- GLX_RGBA,
- GLX_DOUBLEBUFFER,
- GLX_RED_SIZE, 8,
- GLX_GREEN_SIZE, 8,
- GLX_BLUE_SIZE, 8,
- GLX_ALPHA_SIZE, 8,
- GLX_DEPTH_SIZE, 24,
- GLX_STENCIL_SIZE, 8,
- None
- };
+ x_display = display_->x_display;
+ fb_configs = display_->fb_configs;
- x_info = glXChooseVisual(x_display, DefaultScreen(x_display), pixelFormat);
+#ifdef GLX_ARB_create_context
+ if (glXCreateContextAttribsARB) {
+ // Try to create a core profile context.
+ gl_context = createCoreProfile(x_display, fb_configs[0]);
+ }
+#endif
- if (x_info == nullptr) {
- throw std::runtime_error("Error pixel format");
+ if (!gl_context) {
+ // Try to create a legacy context
+ gl_context = glXCreateNewContext(x_display, fb_configs[0], GLX_RGBA_TYPE, 0, True);
+ if (gl_context) {
+ if (!glXIsDirect(x_display, gl_context)) {
+ glXDestroyContext(x_display, gl_context);
+ gl_context = 0;
+ }
+ }
}
- gl_context = glXCreateContext(x_display, x_info, 0, GL_TRUE);
- if (gl_context == nullptr) {
+ if (gl_context == 0) {
throw std::runtime_error("Error creating GL context object");
}
+
+ // Create a dummy pbuffer. We will render to framebuffers anyway, but we need a pbuffer to
+ // activate the context.
+ int pbuffer_attributes[] = {
+ GLX_PBUFFER_WIDTH, 8,
+ GLX_PBUFFER_HEIGHT, 8,
+ None
+ };
+ glx_pbuffer = glXCreatePbuffer(x_display, fb_configs[0], pbuffer_attributes);
#endif
}
-
void HeadlessView::resize(uint16_t width, uint16_t height, float pixelRatio) {
clear_buffers();
- width *= pixelRatio;
- height *= pixelRatio;
+ width_ = width;
+ height_ = height;
+ pixelRatio_ = pixelRatio;
+
+ const unsigned int w = width_ * pixelRatio_;
+ const unsigned int h = height_ * pixelRatio_;
-#if MBGL_USE_CGL
make_active();
// Create depth/stencil buffer
glGenRenderbuffersEXT(1, &fbo_depth_stencil);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo_depth_stencil);
- glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, width, height);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
glGenRenderbuffersEXT(1, &fbo_color);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo_color);
- glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, width, height);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
glGenFramebuffersEXT(1, &fbo);
@@ -94,29 +164,39 @@ void HeadlessView::resize(uint16_t width, uint16_t height, float pixelRatio) {
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
- fprintf(stderr, "Couldn't create framebuffer: ");
+ std::stringstream error("Couldn't create framebuffer: ");
switch (status) {
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: fprintf(stderr, "incomplete attachment\n"); break;
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: fprintf(stderr, "incomplete missing attachment\n"); break;
- case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: fprintf(stderr, "incomplete draw buffer\n"); break;
- case GL_FRAMEBUFFER_UNSUPPORTED: fprintf(stderr, "unsupported\n"); break;
- default: fprintf(stderr, "other\n"); break;
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error << "incomplete attachment\n"); break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error << "incomplete missing attachment\n"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error << "incomplete dimensions\n"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error << "incomplete formats\n"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error << "incomplete draw buffer\n"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error << "incomplete read buffer\n"; break;
+ case GL_FRAMEBUFFER_UNSUPPORTED: error << "unsupported\n"; break;
+ default: error << "other\n"; break;
}
- return;
+ throw std::runtime_error(error.str());
}
-#endif
-#if MBGL_USE_GLX
- x_pixmap = XCreatePixmap(x_display, DefaultRootWindow(x_display), width, height, 32);
- glx_pixmap = glXCreateGLXPixmap(x_display, x_info, x_pixmap);
+ make_inactive();
+}
+
+const std::unique_ptr<uint32_t[]> HeadlessView::readPixels() {
+ const unsigned int w = width_ * pixelRatio_;
+ const unsigned int h = height_ * pixelRatio_;
+
+ std::unique_ptr<uint32_t[]> pixels(new uint32_t[w * h]);
make_active();
-#endif
+ glReadPixels(0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
+ make_inactive();
+ return pixels;
}
void HeadlessView::clear_buffers() {
-#if MBGL_USE_CGL
+ make_active();
+
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
if (fbo) {
@@ -133,19 +213,8 @@ void HeadlessView::clear_buffers() {
glDeleteRenderbuffersEXT(1, &fbo_depth_stencil);
fbo_depth_stencil = 0;
}
-#endif
-#if MBGL_USE_GLX
- if (glx_pixmap) {
- glXDestroyGLXPixmap(x_display, glx_pixmap);
- glx_pixmap = 0;
- }
-
- if (x_pixmap) {
- XFreePixmap(x_display, x_pixmap);
- x_pixmap = 0;
- }
-#endif
+ make_inactive();
}
HeadlessView::~HeadlessView() {
@@ -156,10 +225,12 @@ HeadlessView::~HeadlessView() {
#endif
#if MBGL_USE_GLX
- glXMakeCurrent(x_display, None, NULL);
+ if (glx_pbuffer) {
+ glXDestroyPbuffer(x_display, glx_pbuffer);
+ glx_pbuffer = 0;
+ }
+
glXDestroyContext(x_display, gl_context);
- XFree(x_info);
- XCloseDisplay(x_display);
#endif
}
@@ -175,26 +246,33 @@ void HeadlessView::make_active() {
#if MBGL_USE_CGL
CGLError error = CGLSetCurrentContext(gl_context);
if (error) {
- fprintf(stderr, "Switching OpenGL context failed\n");
+ throw std::runtime_error("Switching OpenGL context failed\n");
}
#endif
#if MBGL_USE_GLX
- if (!glXMakeCurrent(x_display, glx_pixmap, gl_context)) {
- fprintf(stderr, "Switching OpenGL context failed\n");
+ if (!glXMakeContextCurrent(x_display, glx_pbuffer, glx_pbuffer, gl_context)) {
+ throw std::runtime_error("Switching OpenGL context failed\n");
}
#endif
}
-void HeadlessView::swap() {}
-
-unsigned int HeadlessView::root_fbo() {
+void HeadlessView::make_inactive() {
#if MBGL_USE_CGL
- return fbo;
+ CGLError error = CGLSetCurrentContext(nullptr);
+ if (error) {
+ throw std::runtime_error("Removing OpenGL context failed\n");
+ }
#endif
- return 0;
+#if MBGL_USE_GLX
+ if (!glXMakeContextCurrent(x_display, 0, 0, nullptr)) {
+ throw std::runtime_error("Removing OpenGL context failed\n");
+ }
+#endif
}
+void HeadlessView::swap() {}
+
}