summaryrefslogtreecommitdiff
path: root/platform/darwin/src/headless_backend_cgl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/darwin/src/headless_backend_cgl.cpp')
-rw-r--r--platform/darwin/src/headless_backend_cgl.cpp132
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 6ad98f4326..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.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:") +
- 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