diff options
Diffstat (limited to 'platform/darwin/src/headless_backend_cgl.cpp')
-rw-r--r-- | platform/darwin/src/headless_backend_cgl.cpp | 132 |
1 files changed, 91 insertions, 41 deletions
diff --git a/platform/darwin/src/headless_backend_cgl.cpp b/platform/darwin/src/headless_backend_cgl.cpp index 3b0c3aaf35..46a5beb870 100644 --- a/platform/darwin/src/headless_backend_cgl.cpp +++ b/platform/darwin/src/headless_backend_cgl.cpp @@ -1,5 +1,5 @@ #include <mbgl/gl/headless_backend.hpp> -#include <mbgl/gl/headless_display.hpp> +#include <mbgl/util/logging.hpp> #include <OpenGL/OpenGL.h> #include <CoreFoundation/CoreFoundation.h> @@ -9,14 +9,96 @@ namespace mbgl { -struct CGLImpl : public HeadlessBackend::Impl { - CGLImpl(CGLContextObj glContext_) : glContext(glContext_) { +// This class provides a singleton that contains information about the pixel format used for +// instantiating new headless rendering contexts. +class CGLDisplayConfig { +private: + // Key for singleton construction. + struct Key { explicit Key() = default; }; + +public: + CGLDisplayConfig(Key) { + // 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, static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_Legacy), + // Not adding kCGLPFAAccelerated, as this will make headless rendering impossible when running in VMs. + kCGLPFAClosestPolicy, + kCGLPFAAccumSize, static_cast<CGLPixelFormatAttribute>(32), + kCGLPFAColorSize, static_cast<CGLPixelFormatAttribute>(24), + kCGLPFAAlphaSize, static_cast<CGLPixelFormatAttribute>(8), + kCGLPFADepthSize, static_cast<CGLPixelFormatAttribute>(16), + kCGLPFAStencilSize, static_cast<CGLPixelFormatAttribute>(8), + kCGLPFASupportsAutomaticGraphicsSwitching, + kCGLPFAAllowOfflineRenderers, // Allows using the integrated GPU + static_cast<CGLPixelFormatAttribute>(0) + }; + + GLint num; + // TODO: obtain all configurations and choose the best one. + const CGLError error = CGLChoosePixelFormat(attributes, &pixelFormat, &num); + if (error != kCGLNoError) { + throw std::runtime_error(std::string("Error choosing pixel format:") + CGLErrorString(error) + "\n"); + } + if (num <= 0) { + throw std::runtime_error("No pixel formats found."); + } + } + + ~CGLDisplayConfig() { + const CGLError error = CGLDestroyPixelFormat(pixelFormat); + if (error != kCGLNoError) { + Log::Error(Event::OpenGL, std::string("Error destroying pixel format:") + CGLErrorString(error)); + } + } + + static std::shared_ptr<const CGLDisplayConfig> create() { + static std::weak_ptr<const CGLDisplayConfig> instance; + auto shared = instance.lock(); + if (!shared) { + instance = shared = std::make_shared<CGLDisplayConfig>(Key{}); + } + return shared; + } + +public: + CGLPixelFormatObj pixelFormat = nullptr; +}; + +class CGLBackendImpl : public HeadlessBackend::Impl { +public: + CGLBackendImpl() { + CGLError error = CGLCreateContext(cglDisplay->pixelFormat, nullptr, &glContext); + if (error != kCGLNoError) { + throw std::runtime_error(std::string("Error creating GL context object:") + + CGLErrorString(error) + "\n"); + } + + error = CGLEnable(glContext, kCGLCEMPEngine); + if (error != kCGLNoError) { + throw std::runtime_error(std::string("Error enabling OpenGL multithreading:") + + CGLErrorString(error) + "\n"); + } } - ~CGLImpl() { + ~CGLBackendImpl() final { CGLDestroyContext(glContext); } + gl::ProcAddress getExtensionFunctionPointer(const char* name) final { + static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + if (!framework) { + throw std::runtime_error("Failed to load OpenGL framework."); + } + + CFStringRef str = + CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII); + void* symbol = CFBundleGetFunctionPointerForName(framework, str); + CFRelease(str); + + return reinterpret_cast<gl::ProcAddress>(symbol); + } + void activateContext() final { CGLError error = CGLSetCurrentContext(glContext); if (error != kCGLNoError) { @@ -33,46 +115,14 @@ struct CGLImpl : public HeadlessBackend::Impl { } } +private: + const std::shared_ptr<const CGLDisplayConfig> cglDisplay = CGLDisplayConfig::create(); CGLContextObj glContext = nullptr; }; -gl::ProcAddress HeadlessBackend::initializeExtension(const char* name) { - static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); - if (!framework) { - throw std::runtime_error("Failed to load OpenGL framework."); - } - - CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII); - void* symbol = CFBundleGetFunctionPointerForName(framework, str); - CFRelease(str); - - return reinterpret_cast<gl::ProcAddress>(symbol); -} - -bool HeadlessBackend::hasDisplay() { - if (!display) { - display = HeadlessDisplay::create(); - } - 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:") + - CGLErrorString(error) + "\n"); - } - - error = CGLEnable(glContext, kCGLCEMPEngine); - if (error != kCGLNoError) { - throw std::runtime_error(std::string("Error enabling OpenGL multithreading:") + - CGLErrorString(error) + "\n"); - } - - impl.reset(new CGLImpl(glContext)); +void HeadlessBackend::createImpl() { + assert(!impl); + impl = std::make_unique<CGLBackendImpl>(); } } // namespace mbgl |