[linux] Implement EGL headless backend

Original author: Tiago Vignatti <tvignatti@gmail.com>

Calling X11 window system is superfluous for headless rendering. This patch
implements EGL platform using GBM, which is slightly more simple than the GLX
path when using X11.

In principle there are no big advantages in terms of performance etc. My
motivation behind this was to get in touch with the code and the project. For
testing I'm using:

  $ unset DISPLAY && ./build/linux-x86_64/Debug/mbgl-test

v2: rebased patch against the new cmake changes; walk through render node to
find a valid one; remove EGLSurface completely cause windows are not needed
here.
This commit is contained in:
Tiago Vignatti
2016-08-01 17:30:43 +03:00
committed by Bruno de Oliveira Abinader
parent 50f0f919c3
commit 32b6f2fa03
7 changed files with 158 additions and 4 deletions

2
.mason

Submodule .mason updated: 43933edaba...4fff08aefa

View File

@@ -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()

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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