diff options
-rw-r--r-- | include/mbgl/platform/default/headless_backend.hpp | 59 | ||||
-rw-r--r-- | platform/darwin/src/headless_backend_cgl.cpp | 58 | ||||
-rw-r--r-- | platform/darwin/src/headless_backend_eagl.mm | 42 | ||||
-rw-r--r-- | platform/default/headless_backend.cpp | 24 | ||||
-rw-r--r-- | platform/default/headless_backend_osmesa.cpp | 61 | ||||
-rw-r--r-- | platform/linux/src/headless_backend_glx.cpp | 92 | ||||
-rw-r--r-- | platform/qt/test/headless_backend_qt.cpp | 35 |
7 files changed, 213 insertions, 158 deletions
diff --git a/include/mbgl/platform/default/headless_backend.hpp b/include/mbgl/platform/default/headless_backend.hpp index b6c654943f..2fef321d85 100644 --- a/include/mbgl/platform/default/headless_backend.hpp +++ b/include/mbgl/platform/default/headless_backend.hpp @@ -1,23 +1,9 @@ #pragma once #include <mbgl/gl/implementation.hpp> - -#if MBGL_USE_QT -class QGLWidget; -#elif MBGL_USE_CGL -#include <OpenGL/OpenGL.h> -#elif MBGL_USE_GLX -typedef struct _XDisplay Display; -typedef struct __GLXcontextRec* GLXContext; -typedef struct __GLXFBConfigRec* GLXFBConfig; -typedef long unsigned int XID; -typedef XID GLXPbuffer; -#elif MBGL_USE_OSMESA -#include <GL/osmesa.h> -#endif +#include <mbgl/gl/extension.hpp> #include <mbgl/map/backend.hpp> -#include <mbgl/gl/extension.hpp> #include <memory> #include <functional> @@ -39,47 +25,34 @@ public: void setMapChangeCallback(std::function<void(MapChange)>&& cb) { mapChangeCallback = std::move(cb); } -private: - void activateContext(); - void deactivateContext(); + struct Impl { + virtual ~Impl() {} + virtual void activateContext() = 0; + virtual void deactivateContext() {} + }; private: // Implementation specific functions static gl::glProc initializeExtension(const char*); + + bool hasContext() const { return bool(impl); } + bool hasDisplay(); + void createContext(); + +private: void destroyContext(); + void activateContext(); + void deactivateContext(); + + std::unique_ptr<Impl> impl; std::shared_ptr<HeadlessDisplay> display; bool extensionsLoaded = false; bool active = false; -#if MBGL_USE_QT - QGLWidget* glContext = nullptr; -#endif - -#if MBGL_USE_CGL - CGLContextObj glContext = nullptr; -#endif - -#if MBGL_USE_EAGL - void *glContext = nullptr; -#endif - -#if MBGL_USE_GLX - Display *xDisplay = nullptr; - GLXFBConfig *fbConfigs = nullptr; - GLXContext glContext = nullptr; - GLXPbuffer glxPbuffer = 0; -#endif - -#if MBGL_USE_OSMESA - OSMesaContext glContext = nullptr; - GLubyte fakeBuffer = 0; -#endif - std::function<void(MapChange)> mapChangeCallback; - }; } // namespace mbgl diff --git a/platform/darwin/src/headless_backend_cgl.cpp b/platform/darwin/src/headless_backend_cgl.cpp index 081156e8aa..dd062dd6af 100644 --- a/platform/darwin/src/headless_backend_cgl.cpp +++ b/platform/darwin/src/headless_backend_cgl.cpp @@ -1,6 +1,7 @@ #include <mbgl/platform/default/headless_backend.hpp> #include <mbgl/platform/default/headless_display.hpp> +#include <OpenGL/OpenGL.h> #include <CoreFoundation/CoreFoundation.h> #include <string> @@ -8,6 +9,33 @@ namespace mbgl { +struct CGLImpl : public HeadlessBackend::Impl { + CGLImpl(CGLContextObj glContext_) : glContext(glContext_) { + } + + ~CGLImpl() { + CGLDestroyContext(glContext); + } + + void activateContext() final { + CGLError error = CGLSetCurrentContext(glContext); + if (error != kCGLNoError) { + throw std::runtime_error(std::string("Switching OpenGL context failed:") + + CGLErrorString(error) + "\n"); + } + } + + void deactivateContext() final { + CGLError error = CGLSetCurrentContext(nullptr); + if (error != kCGLNoError) { + throw std::runtime_error(std::string("Removing OpenGL context failed:") + + CGLErrorString(error) + "\n"); + } + } + + CGLContextObj glContext = nullptr; +}; + gl::glProc HeadlessBackend::initializeExtension(const char* name) { static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); if (!framework) { @@ -21,7 +49,17 @@ gl::glProc HeadlessBackend::initializeExtension(const char* name) { return reinterpret_cast<gl::glProc>(symbol); } +bool HeadlessBackend::hasDisplay() { + if (!display) { + display.reset(new HeadlessDisplay); + } + return bool(display); +} + void HeadlessBackend::createContext() { + assert(!hasContext()); + + CGLContextObj glContext = nullptr; CGLError error = CGLCreateContext(display->attribute<CGLPixelFormatObj>(), nullptr, &glContext); if (error != kCGLNoError) { throw std::runtime_error(std::string("Error creating GL context object:") + @@ -33,26 +71,8 @@ void HeadlessBackend::createContext() { throw std::runtime_error(std::string("Error enabling OpenGL multithreading:") + CGLErrorString(error) + "\n"); } -} - -void HeadlessBackend::destroyContext() { - CGLDestroyContext(glContext); -} - -void HeadlessBackend::activateContext() { - CGLError error = CGLSetCurrentContext(glContext); - if (error != kCGLNoError) { - throw std::runtime_error(std::string("Switching OpenGL context failed:") + - CGLErrorString(error) + "\n"); - } -} -void HeadlessBackend::deactivateContext() { - CGLError error = CGLSetCurrentContext(nullptr); - if (error != kCGLNoError) { - throw std::runtime_error(std::string("Removing OpenGL context failed:") + - CGLErrorString(error) + "\n"); - } + impl.reset(new CGLImpl(glContext)); } } // namespace mbgl diff --git a/platform/darwin/src/headless_backend_eagl.mm b/platform/darwin/src/headless_backend_eagl.mm index 0a1ae706b8..aa5efe6de3 100644 --- a/platform/darwin/src/headless_backend_eagl.mm +++ b/platform/darwin/src/headless_backend_eagl.mm @@ -8,6 +8,27 @@ namespace mbgl { +struct EAGLImpl : public HeadlessBackend::Impl { + EAGLImpl(EAGLContext* glContext_) : glContext(glContext_) { + [reinterpret_cast<EAGLContext*>(glContext) retain]; + reinterpret_cast<EAGLContext*>(glContext).multiThreaded = YES; + } + + ~EAGLImpl() { + [glContext release]; + } + + void activateContext() { + [EAGLContext setCurrentContext:glContext]; + } + + void deactivateContext() { + [EAGLContext setCurrentContext:nil]; + } + + EAGLContext* glContext = nullptr; +}; + gl::glProc HeadlessBackend::initializeExtension(const char* name) { static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengles")); if (!framework) { @@ -21,26 +42,17 @@ gl::glProc HeadlessBackend::initializeExtension(const char* name) { return reinterpret_cast<gl::glProc>(symbol); } +bool HeadlessBackend::hasDisplay() { + return true; +} + void HeadlessBackend::createContext() { - glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + EAGLContext* glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; if (glContext == nil) { throw std::runtime_error("Error creating GL context object"); } - [reinterpret_cast<EAGLContext*>(glContext) retain]; - reinterpret_cast<EAGLContext*>(glContext).multiThreaded = YES; -} - -void HeadlessBackend::destroyContext() { - [reinterpret_cast<EAGLContext*>(glContext) release]; - glContext = nil; -} - -void HeadlessBackend::activateContext() { - [EAGLContext setCurrentContext:reinterpret_cast<EAGLContext*>(glContext)]; -} -void HeadlessBackend::deactivateContext() { - [EAGLContext setCurrentContext:nil]; + impl.reset(new EAGLImpl(glContext)); } } // namespace mbgl diff --git a/platform/default/headless_backend.cpp b/platform/default/headless_backend.cpp index 279a7973c9..30ce8d2230 100644 --- a/platform/default/headless_backend.cpp +++ b/platform/default/headless_backend.cpp @@ -3,15 +3,16 @@ #include <cassert> #include <stdexcept> +#include <type_traits> namespace mbgl { -HeadlessBackend::HeadlessBackend() : display(std::make_shared<HeadlessDisplay>()) { +HeadlessBackend::HeadlessBackend() { activate(); } HeadlessBackend::HeadlessBackend(std::shared_ptr<HeadlessDisplay> display_) - : display(std::move(display_)) { + : display(std::move(display_)) { activate(); } @@ -23,8 +24,8 @@ HeadlessBackend::~HeadlessBackend() { void HeadlessBackend::activate() { active = true; - if (!glContext) { - if (!display) { + if (!hasContext()) { + if (!hasDisplay()) { throw std::runtime_error("Display is not set"); } createContext(); @@ -47,6 +48,21 @@ void HeadlessBackend::invalidate() { assert(false); } +void HeadlessBackend::destroyContext() { + assert(hasContext()); + impl.reset(); +} + +void HeadlessBackend::activateContext() { + assert(hasContext()); + impl->activateContext(); +} + +void HeadlessBackend::deactivateContext() { + assert(hasContext()); + impl->deactivateContext(); +} + void HeadlessBackend::notifyMapChange(MapChange change) { if (mapChangeCallback) { mapChangeCallback(change); diff --git a/platform/default/headless_backend_osmesa.cpp b/platform/default/headless_backend_osmesa.cpp index e0e385fcc6..04709d4a1b 100644 --- a/platform/default/headless_backend_osmesa.cpp +++ b/platform/default/headless_backend_osmesa.cpp @@ -1,47 +1,54 @@ #include <mbgl/platform/default/headless_backend.hpp> #include <mbgl/platform/log.hpp> -namespace mbgl { +#include <GL/osmesa.h> -gl::glProc HeadlessBackend::initializeExtension(const char* name) { - return OSMesaGetProcAddress(name); -} +#include <cassert> -void HeadlessBackend::createContext() { - if (glContext == nullptr) { -#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305 - glContext = OSMesaCreateContextExt(OSMESA_RGBA, 16, 0, 0, nullptr); -#else - glContext = OSMesaCreateContext(OSMESA_RGBA, nullptr); -#endif - if (glContext == nullptr) { - Log::Error(Event::OpenGL, "failed to create OSMesa context"); - } - } +namespace mbgl { - if (glContext == nullptr) { - throw std::runtime_error("Error creating GL context object."); +struct OSMesaImpl : public HeadlessBackend::Impl { + OSMesaImpl(OSMesaContext glContext_) : glContext(glContext_) { } -} -void HeadlessBackend::destroyContext() { - if (glContext) { + ~OSMesaImpl() { if (glContext != OSMesaGetCurrentContext()) { activateContext(); } OSMesaDestroyContext(glContext); - glContext = nullptr; } -} -void HeadlessBackend::activateContext() { - if (!OSMesaMakeCurrent(glContext, &fakeBuffer, GL_UNSIGNED_BYTE, 1, 1)) { - throw std::runtime_error("Switching OpenGL context failed.\n"); + void activateContext() final { + if (!OSMesaMakeCurrent(glContext, &fakeBuffer, GL_UNSIGNED_BYTE, 1, 1)) { + throw std::runtime_error("Switching OpenGL context failed.\n"); + } } + + OSMesaContext glContext = nullptr; + GLubyte fakeBuffer = 0; +}; + +gl::glProc HeadlessBackend::initializeExtension(const char* name) { + return OSMesaGetProcAddress(name); } -void HeadlessBackend::deactivateContext() { - // no-op. +bool HeadlessBackend::hasDisplay() { + return true; +}; + +void HeadlessBackend::createContext() { + assert(!hasContext()); + +#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305 + OSMesaContext glContext = OSMesaCreateContextExt(OSMESA_RGBA, 16, 0, 0, nullptr); +#else + OSMesaContext glContext = OSMesaCreateContext(OSMESA_RGBA, nullptr); +#endif + if (glContext == nullptr) { + throw std::runtime_error("Error creating GL context object."); + } + + impl.reset(new OSMesaImpl(glContext)); } } // namespace mbgl diff --git a/platform/linux/src/headless_backend_glx.cpp b/platform/linux/src/headless_backend_glx.cpp index 929eb73f5a..e1ea9cef15 100644 --- a/platform/linux/src/headless_backend_glx.cpp +++ b/platform/linux/src/headless_backend_glx.cpp @@ -3,32 +3,71 @@ #include <mbgl/platform/log.hpp> -// #include <cassert> +#include <cassert> #include <GL/glx.h> namespace mbgl { +struct GLXImpl : public HeadlessBackend::Impl { + GLXImpl(GLXContext glContext_, GLXPbuffer glxPbuffer_, Display* xDisplay_, GLXFBConfig* fbConfigs_) + : glContext(glContext_), + glxPbuffer(glxPbuffer_), + xDisplay(xDisplay_), + fbConfigs(fbConfigs_) { + } + + ~GLXImpl() { + if (glxPbuffer) { + glXDestroyPbuffer(xDisplay, glxPbuffer); + } + glXDestroyContext(xDisplay, glContext); + } + + void activateContext() final { + if (!glXMakeContextCurrent(xDisplay, glxPbuffer, glxPbuffer, glContext)) { + throw std::runtime_error("Switching OpenGL context failed.\n"); + } + } + + void deactivateContext() final { + if (!glXMakeContextCurrent(xDisplay, 0, 0, nullptr)) { + throw std::runtime_error("Removing OpenGL context failed.\n"); + } + } + + GLXContext glContext = nullptr; + GLXPbuffer glxPbuffer = 0; + + // Needed for ImplDeleter. + Display* xDisplay = nullptr; + GLXFBConfig* fbConfigs = nullptr; +}; + gl::glProc HeadlessBackend::initializeExtension(const char* name) { return glXGetProcAddress(reinterpret_cast<const GLubyte*>(name)); } -void HeadlessBackend::createContext() { - xDisplay = display->attribute<Display*>(); - fbConfigs = display->attribute<GLXFBConfig*>(); - - if (!glContext) { - // Try to create a legacy context - glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, None, True); - if (glContext) { - if (!glXIsDirect(xDisplay, glContext)) { - Log::Error(Event::OpenGL, "failed to create direct OpenGL Legacy context"); - glXDestroyContext(xDisplay, glContext); - glContext = nullptr; - } - } +bool HeadlessBackend::hasDisplay() { + if (!display) { + display.reset(new HeadlessDisplay); } + return bool(display); +}; + +void HeadlessBackend::createContext() { + assert(!hasContext()); + + Display* xDisplay = display->attribute<Display*>(); + GLXFBConfig* fbConfigs = display->attribute<GLXFBConfig*>(); + // Try to create a legacy context. + GLXContext glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, None, True); + if (glContext && !glXIsDirect(xDisplay, glContext)) { + Log::Error(Event::OpenGL, "failed to create direct OpenGL Legacy context"); + glXDestroyContext(xDisplay, glContext); + glContext = nullptr; + } if (glContext == nullptr) { throw std::runtime_error("Error creating GL context object."); } @@ -40,28 +79,9 @@ void HeadlessBackend::createContext() { GLX_PBUFFER_HEIGHT, 8, None }; - glxPbuffer = glXCreatePbuffer(xDisplay, fbConfigs[0], pbufferAttributes); -} - -void HeadlessBackend::destroyContext() { - if (glxPbuffer) { - glXDestroyPbuffer(xDisplay, glxPbuffer); - glxPbuffer = 0; - } - - glXDestroyContext(xDisplay, glContext); -} + GLXPbuffer glxPbuffer = glXCreatePbuffer(xDisplay, fbConfigs[0], pbufferAttributes); -void HeadlessBackend::activateContext() { - if (!glXMakeContextCurrent(xDisplay, glxPbuffer, glxPbuffer, glContext)) { - throw std::runtime_error("Switching OpenGL context failed.\n"); - } -} - -void HeadlessBackend::deactivateContext() { - if (!glXMakeContextCurrent(xDisplay, 0, 0, nullptr)) { - throw std::runtime_error("Removing OpenGL context failed.\n"); - } + impl.reset(new GLXImpl(glContext, glxPbuffer, xDisplay, fbConfigs)); } } // namespace mbgl diff --git a/platform/qt/test/headless_backend_qt.cpp b/platform/qt/test/headless_backend_qt.cpp index 3f287ae578..f6552c4557 100644 --- a/platform/qt/test/headless_backend_qt.cpp +++ b/platform/qt/test/headless_backend_qt.cpp @@ -1,5 +1,4 @@ #include <mbgl/platform/default/headless_backend.hpp> -#include <mbgl/platform/default/headless_display.hpp> #include <QApplication> #include <QGLContext> @@ -9,8 +8,22 @@ #include <QOpenGLContext> #endif +#include <cassert> + namespace mbgl { +struct QtImpl : public HeadlessBackend::Impl { + void activateContext() final { + widget.makeCurrent(); + } + + void deactivateContext() final { + widget.doneCurrent(); + } + + QGLWidget widget; +}; + gl::glProc HeadlessBackend::initializeExtension(const char* name) { #if QT_VERSION >= 0x050000 QOpenGLContext* thisContext = QOpenGLContext::currentContext(); @@ -21,26 +34,20 @@ gl::glProc HeadlessBackend::initializeExtension(const char* name) { #endif } +bool HeadlessBackend::hasDisplay() { + return true; +}; + void HeadlessBackend::createContext() { + assert(!hasContext()); + static const char* argv[] = { "mbgl" }; static int argc = 1; static auto* app = new QApplication(argc, const_cast<char**>(argv)); Q_UNUSED(app); - glContext = new QGLWidget; -} - -void HeadlessBackend::destroyContext() { - delete glContext; -} - -void HeadlessBackend::activateContext() { - glContext->makeCurrent(); -} - -void HeadlessBackend::deactivateContext() { - glContext->doneCurrent(); + impl.reset(new QtImpl); } } // namespace mbgl |