-
-
Notifications
You must be signed in to change notification settings - Fork 551
Expand file tree
/
Copy pathheadless_backend_egl.cpp
More file actions
133 lines (108 loc) · 4.84 KB
/
Copy pathheadless_backend_egl.cpp
File metadata and controls
133 lines (108 loc) · 4.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <mbgl/gl/headless_backend.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/logging.hpp>
#include <EGL/egl.h>
#include <cassert>
#include <memory>
#include <sstream>
namespace mbgl {
namespace gl {
// This class provides a singleton that contains information about the
// configuration used for instantiating new headless rendering contexts.
class EGLDisplayConfig {
private:
// Key for singleton construction.
struct Key {
explicit Key() = default;
};
public:
EGLDisplayConfig(Key) {
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");
}
if (!eglBindAPI(EGL_OPENGL_ES_API)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglBindAPI(EGL_OPENGL_ES_API) returned error " + std::to_string(eglGetError()));
throw std::runtime_error("eglBindAPI() failed");
}
const EGLint attribs[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_NONE};
// Note: we're choosing an arbitrary pixel format, since we're not using
// the default surface anyway; all rendering will be directed to
// framebuffers which have their own configuration.
if (!eglChooseConfig(display, attribs, &config, 1, &numConfigs) || numConfigs != 1) {
throw std::runtime_error("Failed to choose ARGB config.\n");
}
}
~EGLDisplayConfig() { eglTerminate(display); }
static std::shared_ptr<const EGLDisplayConfig> create() {
// C++11 magic static guarantees thread-safe one-shot initialization.
static const auto instance = std::make_shared<EGLDisplayConfig>(Key{});
return instance;
}
public:
EGLDisplay display = EGL_NO_DISPLAY;
EGLConfig config = 0;
};
class EGLBackendImpl final : public HeadlessBackend::Impl {
public:
EGLBackendImpl() {
// EGL initializes the context client version to 1 by default. We want
// to use OpenGL ES 2.0 which has the ability to create shader and
// program objects and also to write vertex and fragment shaders in the
// OpenGL ES Shading Language.
const EGLint attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
eglContext = eglCreateContext(eglDisplay->display, eglDisplay->config, EGL_NO_CONTEXT, attribs);
if (eglContext == EGL_NO_CONTEXT) {
std::ostringstream logMsg;
logMsg << "eglCreateContext() returned error 0x" << std::hex << eglGetError();
mbgl::Log::Error(mbgl::Event::OpenGL, logMsg.str());
throw std::runtime_error("Error creating the EGL context object.\n");
}
// Create a dummy pbuffer. We will render to framebuffers anyway, but we
// need a pbuffer to activate the context. Note that to be able to
// create pbuffer surfaces, we need to choose a config that includes
// EGL_SURFACE_TYPE, EGL_PBUFFER_BIT in HeadlessDisplay.
const EGLint surfAttribs[] = {EGL_WIDTH, 8, EGL_HEIGHT, 8, EGL_LARGEST_PBUFFER, EGL_TRUE, EGL_NONE};
eglSurface = eglCreatePbufferSurface(eglDisplay->display, eglDisplay->config, surfAttribs);
if (eglSurface == EGL_NO_SURFACE) {
throw std::runtime_error("Could not create surface: " + util::toString(eglGetError()));
}
}
~EGLBackendImpl() final {
if (eglSurface != EGL_NO_SURFACE) {
if (!eglDestroySurface(eglDisplay->display, eglSurface)) {
Log::Error(Event::OpenGL, "Failed to destroy EGL surface.");
}
eglSurface = EGL_NO_SURFACE;
}
if (!eglDestroyContext(eglDisplay->display, eglContext)) {
Log::Error(Event::OpenGL, "Failed to destroy EGL context.");
}
}
gl::ProcAddress getExtensionFunctionPointer(const char* name) final { return eglGetProcAddress(name); }
void activateContext() final {
if (!eglMakeCurrent(eglDisplay->display, eglSurface, eglSurface, eglContext)) {
throw std::runtime_error("Switching OpenGL context failed.\n");
}
}
void deactivateContext() final {
if (!eglMakeCurrent(eglDisplay->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
throw std::runtime_error("Removing OpenGL context failed.\n");
}
}
private:
const std::shared_ptr<const EGLDisplayConfig> eglDisplay = EGLDisplayConfig::create();
EGLContext eglContext = EGL_NO_CONTEXT;
EGLSurface eglSurface = EGL_NO_SURFACE;
};
void HeadlessBackend::createImpl() {
assert(!impl);
impl = std::make_unique<EGLBackendImpl>();
}
} // namespace gl
} // namespace mbgl