diff options
m--------- | .mason | 0 | ||||
-rw-r--r-- | CMakeLists.txt | 8 | ||||
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | platform/default/glfw_view.cpp | 2 | ||||
-rw-r--r-- | platform/linux/config.cmake | 16 | ||||
-rw-r--r-- | platform/linux/src/headless_backend_egl.cpp | 75 | ||||
-rw-r--r-- | platform/linux/src/headless_display_egl.cpp | 56 |
7 files changed, 157 insertions, 3 deletions
diff --git a/.mason b/.mason -Subproject 43933edabaca8f877f270466d20759fe84e62b2 +Subproject 4fff08aefa99f79252c4bb5a0c6c42ca32534eb diff --git a/CMakeLists.txt b/CMakeLists.txt index 10d1076e29..064710780f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,15 +15,23 @@ include(.mason/mason.cmake) option(WITH_CXX11ABI "Use cxx11abi mason packages" OFF) option(WITH_COVERAGE "Enable coverage reports" OFF) option(WITH_OSMESA "Use OSMesa headless backend" OFF) +option(WITH_EGL "Use EGL backend" OFF) option(IS_CI_BUILD "Continuous integration build" OFF) if(WITH_CXX11ABI) set(MASON_CXXABI_SUFFIX -cxx11abi) endif() +if(WITH_OSMESA AND WITH_EGL) + message(FATAL_ERROR "WITH_OSMESA and WITH_EGL are mutually exclusive.") +endif() + if(WITH_OSMESA) # Default mesa mason binary is OSMesa. set(MASON_MESA_SUFFIX "") +elseif(WITH_EGL) + add_definitions(-DMBGL_USE_GLES2=1) + set(MASON_MESA_SUFFIX "-egl") else() set(MASON_MESA_SUFFIX "-glx") endif() @@ -295,7 +295,8 @@ $(LINUX_BUILD): $(BUILD_DEPS) -DWITH_CXX11ABI=$(shell scripts/check-cxx11abi.sh) \ -DWITH_COVERAGE=${WITH_COVERAGE} \ -DIS_CI_BUILD=${CI} \ - -DWITH_OSMESA=${WITH_OSMESA}) + -DWITH_OSMESA=${WITH_OSMESA} \ + -DWITH_EGL=${WITH_EGL}) .PHONY: linux linux: glfw-app render offline diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index a8fa17548d..07959b2002 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -172,6 +172,7 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, } } break; +#if not MBGL_USE_GLES2 case GLFW_KEY_B: { auto debug = view->map->getDebug(); if (debug & mbgl::MapDebugOptions::StencilClip) { @@ -184,6 +185,7 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, } view->map->setDebug(debug); } break; +#endif // MBGL_USE_GLES2 case GLFW_KEY_N: if (!mods) view->map->resetNorth(); diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake index b4bc030064..d2f3d3aa94 100644 --- a/platform/linux/config.cmake +++ b/platform/linux/config.cmake @@ -1,5 +1,5 @@ mason_use(glfw VERSION 3.2.1) -if (WITH_OSMESA OR IS_CI_BUILD) +if(IS_CI_BUILD) mason_use(mesa VERSION 13.0.0${MASON_MESA_SUFFIX}${MASON_CXXABI_SUFFIX}) endif() mason_use(boost_libprogram_options VERSION 1.60.0) @@ -15,12 +15,24 @@ mason_use(benchmark VERSION 1.0.0) include(cmake/loop-uv.cmake) macro(mbgl_platform_core) - if (WITH_OSMESA) + if(WITH_OSMESA) target_sources(mbgl-core PRIVATE platform/default/headless_backend_osmesa.cpp PRIVATE platform/default/headless_display.cpp ) target_add_mason_package(mbgl-core PUBLIC mesa) + elseif(WITH_EGL) + target_sources(mbgl-core + PRIVATE platform/linux/src/headless_backend_egl.cpp + PRIVATE platform/linux/src/headless_display_egl.cpp + ) + # TODO: Provide surface-less EGL mesa for CI builds. + # https://github.com/mapbox/mapbox-gl-native/issues/7020 + target_link_libraries(mbgl-core + PUBLIC -lGLESv2 + PUBLIC -lEGL + PUBLIC -lgbm + ) else() target_sources(mbgl-core PRIVATE platform/linux/src/headless_backend_glx.cpp diff --git a/platform/linux/src/headless_backend_egl.cpp b/platform/linux/src/headless_backend_egl.cpp new file mode 100644 index 0000000000..5c68976ada --- /dev/null +++ b/platform/linux/src/headless_backend_egl.cpp @@ -0,0 +1,75 @@ +#include <mbgl/platform/default/headless_backend.hpp> +#include <mbgl/platform/default/headless_display.hpp> + +#include <mbgl/platform/log.hpp> + +#include <EGL/egl.h> + +#include <cassert> + +namespace mbgl { + +struct EGLImpl : public HeadlessBackend::Impl { + EGLImpl(EGLContext glContext_, EGLDisplay display_) + : glContext(glContext_), + display(display_) { + } + + ~EGLImpl() { + if (glContext != eglGetCurrentContext()) { + activateContext(); + } + if (!eglDestroyContext(display, glContext)) { + throw std::runtime_error("Failed to destroy EGL context.\n"); + } + } + + void activateContext() final { + if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, glContext)) { + throw std::runtime_error("Switching OpenGL context failed.\n"); + } + } + + void deactivateContext() final { + if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + throw std::runtime_error("Removing OpenGL context failed.\n"); + } + } + + EGLContext glContext = EGL_NO_CONTEXT; + EGLDisplay display = EGL_NO_DISPLAY; +}; + +gl::glProc HeadlessBackend::initializeExtension(const char* name) { + return eglGetProcAddress(name); +} + +bool HeadlessBackend::hasDisplay() { + if (!display) { + display.reset(new HeadlessDisplay); + } + return bool(display); +}; + +void HeadlessBackend::createContext() { + assert(!hasContext()); + assert(hasDisplay()); + + EGLDisplay display_ = display->attribute<EGLDisplay>(); + EGLConfig& config = display->attribute<EGLConfig&>(); + + if (!eglBindAPI(EGL_OPENGL_API)) { + throw std::runtime_error("Error setting the EGL rendering API.\n"); + } + + EGLContext glContext = eglCreateContext(display_, config, EGL_NO_CONTEXT, NULL); + if (glContext == EGL_NO_CONTEXT) { + mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error 0x%04x", + eglGetError()); + throw std::runtime_error("Error creating the EGL context object.\n"); + } + + impl.reset(new EGLImpl(glContext, display_)); +} + +} // namespace mbgl diff --git a/platform/linux/src/headless_display_egl.cpp b/platform/linux/src/headless_display_egl.cpp new file mode 100644 index 0000000000..2c0481ddd1 --- /dev/null +++ b/platform/linux/src/headless_display_egl.cpp @@ -0,0 +1,56 @@ +#include <mbgl/platform/default/headless_display.hpp> +#include <mbgl/util/string.hpp> + +#include <EGL/egl.h> + +namespace mbgl { + +class HeadlessDisplay::Impl { +public: + Impl(); + ~Impl(); + + EGLDisplay display = EGL_NO_DISPLAY; + EGLConfig config = 0; +}; + +HeadlessDisplay::Impl::Impl() { + display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (display == EGL_NO_DISPLAY) { + throw std::runtime_error("Failed to obtain a valid EGL display.\n"); + } + + EGLint major, minor, numConfigs; + if (!eglInitialize(display, &major, &minor)) { + throw std::runtime_error("eglInitialize() failed.\n"); + } + + // This shouldn't matter as we're rendering to a framebuffer. + const EGLint attribs[] = { EGL_NONE }; + if (!eglChooseConfig(display, attribs, &config, 1, &numConfigs) || numConfigs != 1) { + throw std::runtime_error("Failed to choose ARGB config.\n"); + } +} + +HeadlessDisplay::Impl::~Impl() { + eglTerminate(display); +} + +template <> +EGLDisplay HeadlessDisplay::attribute() const { + return impl->display; +} + +template <> +EGLConfig& HeadlessDisplay::attribute() const { + return impl->config; +} + +HeadlessDisplay::HeadlessDisplay() + : impl(std::make_unique<Impl>()) { +} + +HeadlessDisplay::~HeadlessDisplay() { +} + +} // namespace mbgl |