Cogs.Core
EGLContext.cpp
1#ifdef COGS_EGL
2
3#include "GLFuncPointers.h"
4#include "GLContext.h"
5
6#include "../IGraphicsDevice.h"
7#include "Foundation/Logging/Logger.h"
8#include "Foundation/StringView.h"
9#include "Foundation/Platform/WindowData.h"
10
11#ifndef __EMSCRIPTEN__
12#include <dlfcn.h>
13
14PFNEGLGETPROCADDRESSPROC _eglGetProcAddress = nullptr;
15#endif
16
17namespace {
18
19 Cogs::Logging::Log logger = Cogs::Logging::getLogger("EGLContext");
20
21static bool initialized = false;
22
23#ifdef __EMSCRIPTEN__
24
25#define _eglBindAPI eglBindAPI
26#define _eglChooseConfig eglChooseConfig
27#define _eglCreateContext eglCreateContext
28#define _eglCreateWindowSurface eglCreateWindowSurface
29#define _eglDestroyContext eglDestroyContext
30#define _eglDestroySurface eglDestroySurface
31#define _eglGetConfigAttrib eglGetConfigAttrib
32#define _eglGetDisplay eglGetDisplay
33#define _eglGetError eglGetError
34#define _eglGetPlatformDisplay eglGetPlatformDisplay
35#define _eglGetProcAddress eglGetProcAddress
36#define _eglInitialize eglInitialize
37#define _eglMakeCurrent eglMakeCurrent
38#define _eglQueryString eglQueryString
39#define _eglSwapBuffers eglSwapBuffers
40#define _eglTerminate eglTerminate
41
42 bool initialize() { return true; }
43
44#else
45
46 PFNEGLBINDAPIPROC _eglBindAPI = nullptr;
47 PFNEGLCHOOSECONFIGPROC _eglChooseConfig = nullptr;
48 PFNEGLCREATECONTEXTPROC _eglCreateContext = nullptr;
49 PFNEGLCREATEWINDOWSURFACEPROC _eglCreateWindowSurface = nullptr;
50 PFNEGLDESTROYCONTEXTPROC _eglDestroyContext = nullptr;
51 PFNEGLDESTROYSURFACEPROC _eglDestroySurface = nullptr;
52 PFNEGLGETCONFIGATTRIBPROC _eglGetConfigAttrib = nullptr;
53 PFNEGLGETDISPLAYPROC _eglGetDisplay = nullptr;
54 PFNEGLGETPLATFORMDISPLAYPROC _eglGetPlatformDisplay = nullptr;
55 PFNEGLGETERRORPROC _eglGetError = nullptr;
56 PFNEGLINITIALIZEPROC _eglInitialize = nullptr;
57 PFNEGLMAKECURRENTPROC _eglMakeCurrent = nullptr;
58 PFNEGLQUERYSTRINGPROC _eglQueryString = nullptr;
59 PFNEGLSWAPBUFFERSPROC _eglSwapBuffers = nullptr;
60 PFNEGLTERMINATEPROC _eglTerminate = nullptr;
61
62 PFNEGLGETDISPLAYDRIVERCONFIGPROC _eglGetDisplayDriverConfig = nullptr;
63 PFNEGLGETDISPLAYDRIVERNAMEPROC _eglGetDisplayDriverName = nullptr;
64
65 void* libEGL = nullptr;
66 static const char* libEGLNames[] = {
67 "libEGL.so"
68 };
69 struct {
70 void** ptr;
71 const char* name;
72 } libEGLFuncs[] = {
73 { (void**)&_eglBindAPI, "eglBindAPI" },
74 { (void**)&_eglChooseConfig, "eglChooseConfig" },
75 { (void**)&_eglCreateContext, "eglCreateContext" },
76 { (void**)&_eglCreateWindowSurface, "eglCreateWindowSurface" },
77 { (void**)&_eglDestroyContext, "eglDestroyContext" },
78 { (void**)&_eglDestroySurface, "eglDestroySurface" },
79 { (void**)&_eglGetConfigAttrib, "eglGetConfigAttrib" },
80 { (void**)&_eglGetDisplay, "eglGetDisplay" },
81 { (void**)&_eglGetError, "eglGetError" },
82 { (void**)&_eglGetPlatformDisplay, "eglGetPlatformDisplay" },
83 { (void**)&_eglInitialize, "eglInitialize" },
84 { (void**)&_eglMakeCurrent, "eglMakeCurrent" },
85 { (void**)&_eglQueryString, "eglQueryString" },
86 { (void**)&_eglSwapBuffers, "eglSwapBuffers" },
87 { (void**)&_eglTerminate, "eglTerminate" },
88 };
89
90 bool initialize()
91 {
92 // Open libEGL
93 for (const char* libEGLName : libEGLNames) {
94 libEGL = dlopen(libEGLName, RTLD_LAZY | RTLD_LOCAL);
95 if (libEGL) { goto egl_loaded; }
96 }
97 LOG_FATAL(logger, "Failed to load libEGL");
98 return false;
99
100 egl_loaded:
101
102 _eglGetProcAddress = (PFNEGLGETPROCADDRESSPROC)dlsym(libEGL, "eglGetProcAddress");
103 if(_eglGetProcAddress == nullptr) {
104 LOG_FATAL(logger, "Failed to lookup libEGL function eglGetProcAddress");
105 //return false;
106 }
107
108 for (const auto& libFunc : libEGLFuncs) {
109 *libFunc.ptr = (void*)_eglGetProcAddress(libFunc.name);
110 if(*libFunc.ptr == nullptr) {
111 LOG_FATAL(logger, "Failed to look up libEGL function %s", libFunc.name);
112 return false;
113 }
114 }
115 return true;
116 }
117
118#endif
119
120
121 const char* eglErrorString(EGLint error)
122 {
123 switch (error) {
124 case EGL_SUCCESS: return "EGL_SUCCESS";
125 case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
126 case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
127 case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
128 case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
129 case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
130 case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
131 case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
132 case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
133 case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
134 case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
135 case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
136 case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
137 case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
138 case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
139 default:
140 return "<unknown>";
141 }
142 }
143
144
145
146 EGLConfig chooseConfig(EGLDisplay eglDisplay, EGLint surfaceType, EGLint requestedSampleCount)
147 {
148 EGLint attribList[] =
149 {
150 EGL_DEPTH_SIZE, 24,
151 EGL_SURFACE_TYPE, surfaceType,
152 //EGL_SAMPLE_BUFFERS, 1,
153 //EGL_SAMPLES, 4,
154 EGL_NONE
155 };
156 EGLint numConfigs = 0;
157 if (!_eglChooseConfig(eglDisplay, attribList, nullptr, 0, &numConfigs)) {
158 LOG_ERROR(logger, "Failed to choose EGL config: %s", eglErrorString(_eglGetError()));
159 return nullptr;
160 }
161 if(numConfigs == 0) {
162 LOG_FATAL(logger, "eglChooseConfig return 0 matching configs");
163 return nullptr;
164 }
165 std::vector<EGLConfig> configs(numConfigs);
166 if (!_eglChooseConfig(eglDisplay, attribList, configs.data(), numConfigs, &numConfigs)) {
167 LOG_FATAL(logger, "Failed to choose EGL config: %s", eglErrorString(_eglGetError()));
168 return nullptr;
169 }
170
171 EGLint bestFbc = -1;
172 EGLint bestSampleCount = -1;
173 LOG_DEBUG(logger, "eglChooseConfig returned %d configs:", numConfigs);
174 for(EGLint i=0; i<numConfigs; i++) {
175 struct {
176 GLint a;
177 GLint v = 0;
178 } a[] = {
179 { EGL_NATIVE_VISUAL_ID },
180 { EGL_RED_SIZE },
181 { EGL_GREEN_SIZE },
182 { EGL_BLUE_SIZE },
183 { EGL_ALPHA_SIZE },
184 { EGL_DEPTH_SIZE },
185 { EGL_SURFACE_TYPE },
186 { EGL_SAMPLE_BUFFERS },
187 { EGL_SAMPLES },
188 };
189 for(auto& attrib : a) {
190 if(!_eglGetConfigAttrib(eglDisplay, configs[i], attrib.a, &attrib.v)) {
191 LOG_ERROR(logger, "eglGetConfigAttrib failed for attribute 0x%x: %s", attrib.a, eglErrorString(_eglGetError()));
192 }
193 }
194
195 EGLint sampleBuffers = a[7].v;
196 EGLint sampleCount = a[8].v;
197 if ((bestFbc < 0) || (sampleBuffers && (bestSampleCount < sampleCount) && (sampleCount <= requestedSampleCount))) {
198 bestFbc = i;
199 bestSampleCount = sampleCount;
200 }
201 LOG_DEBUG(logger, "%i: conf=0x%zx vid=0x%x, rgba={%d %d %d %d} d=%d surfype=0x%x sampleBufs=%d samples=%d",
202 i, (size_t)configs[i], a[0].v,
203 a[1].v, a[2].v, a[2].v, a[4].v, a[5].v,
204 a[6].v, sampleBuffers, sampleCount);
205 }
206 LOG_DEBUG(logger, "Requested %d samples, chose config %d", requestedSampleCount, bestFbc);
207
208 // Just choose the first for now
209 return configs[0];
210 }
211
212 static bool has_EGL_KHR_no_config_context = false; // https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_no_config_context.txt
213 static bool has_EGL_MESA_query_driver = false; // https://registry.khronos.org/EGL/extensions/MESA/EGL_MESA_query_driver.txt
214
215 void queryDisplayExtensions(EGLDisplay eglDisplay)
216 {
217 if(const char* eglExtensions = _eglQueryString(eglDisplay, EGL_EXTENSIONS); eglExtensions) {
218 for(const char* a=eglExtensions; a && *a!='\0'; ) {
219 const char* b = a;
220 while(*b != ' ' && *b != '\0') b++;
221 Cogs::StringView extension(a, b-a);
222 LOG_DEBUG(logger, "EGL extension %.*s", StringViewFormat(extension));
223 switch(Cogs::hash(extension)) {
224 case Cogs::hash("EGL_KHR_no_config_context"):
225 has_EGL_KHR_no_config_context = true;
226 break;
227#ifndef __EMSCRIPTEN__
228 case Cogs::hash("EGL_MESA_query_driver"):
229 _eglGetDisplayDriverName = (PFNEGLGETDISPLAYDRIVERNAMEPROC)_eglGetProcAddress("eglGetDisplayDriverName");
230 _eglGetDisplayDriverConfig = (PFNEGLGETDISPLAYDRIVERCONFIGPROC)_eglGetProcAddress("eglGetDisplayDriverConfig");
231 has_EGL_MESA_query_driver = true;
232 break;
233#endif
234 }
235 while(*b == ' ' && *b != '\0') b++;
236 a = b;
237 }
238 }
239 else {
240 LOG_DEBUG(logger, "No EGL extensions");
241 }
242 if(has_EGL_MESA_query_driver) {
243#ifndef __EMSCRIPTEN__
244 LOG_DEBUG(logger, "EGL display driver: %s", _eglGetDisplayDriverName(eglDisplay));
245 //LOG_DEBUG(logger, "EGL display config: %s", _eglGetDisplayDriverConfig(eglDisplay));
246#endif
247 }
248
249 }
250
251}
252
253
254bool Cogs::GLContext::create(NativeDisplay display, WindowData* windowData, GLContext* shareContext, const GraphicsDeviceSettings* settings, Platform desiredPlatform, bool debug)
255{
256 assert(eglDisplay == nullptr);
257 assert(eglContext == nullptr);
258 assert(eglSurface == nullptr);
259 if (!initialized) {
260 if (!initialize()) return false;
261 }
262
263 // ------- Set up display connection ---------------------------------------
264 if(shareContext && shareContext->eglDisplay != EGL_NO_DISPLAY) {
265 eglDisplay = shareContext->eglDisplay;
266 }
267
268 else {
269
270 if(display) {
271#ifdef __linux__
272 // Requires EGL 1.5
273 eglDisplay = _eglGetPlatformDisplay(EGL_PLATFORM_X11_EXT, display, NULL);
274#else
275 eglDisplay = _eglGetDisplay(display);
276#endif
277 }
278 else {
279 #if 0
280 PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = reinterpret_cast<PFNEGLQUERYDEVICESEXTPROC>(_eglGetProcAddress("eglQueryDevicesEXT"));
281 PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(_eglGetProcAddress("eglGetPlatformDisplayEXT"));
282
283 if(eglQueryDevicesEXT && eglGetPlatformDisplayEXT){
284 constexpr int maxDevices = 4;
285 EGLDeviceEXT eglDevs[maxDevices];
286 EGLint numDevices;
287
288 _eglQueryDevicesEXT(maxDevices, eglDevs, &numDevices);
289 eglDisplay = _eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, eglDevs[0], 0);
290 }
291 else{
292 eglDisplay = _eglGetDisplay(EGL_DEFAULT_DISPLAY);
293 }
294 #endif
295 eglDisplay = _eglGetDisplay(EGL_DEFAULT_DISPLAY);
296 }
297
298 if (eglDisplay == EGL_NO_DISPLAY) {
299 LOG_FATAL(logger, "Failed to aquire EGL display: %s", eglErrorString(_eglGetError()));
300 return false;
301 }
302 ownDisplay = true;
303
304 // ------- Initialize EGL and bind to either GL or GLES --------------------
305 EGLint major = 0;
306 EGLint minor = 0;
307 if (!_eglInitialize(eglDisplay, &major, &minor)) {
308 LOG_FATAL(logger, "Failed to initialize EGL: %s", eglErrorString(_eglGetError()));
309 return false;
310 }
311 LOG_DEBUG(logger, "EGL %d.%d initialized", major, minor);
312 LOG_DEBUG(logger, "EGL vendor: %s", _eglQueryString(eglDisplay, EGL_VENDOR));
313 LOG_DEBUG(logger, "EGL version: %s", _eglQueryString(eglDisplay, EGL_VERSION));
314 LOG_DEBUG(logger, "EGL client APIs: %s", _eglQueryString(eglDisplay, EGL_CLIENT_APIS));
315 // Version specified implies ES
316 if(desiredPlatform == Platform::ES3) {
317 if((major == 1) && (minor < 2)) {
318 LOG_FATAL(logger, "Insufficient EGL version, minimum supported is 1.2");
319 return false;
320 }
321 if (!_eglBindAPI(EGL_OPENGL_ES_API)) {
322 LOG_FATAL(logger, "Failed to bind OPENGL ES API");
323 return false;
324 }
325 }
326 else {
327 if((major == 1) && (minor < 4)) {
328 LOG_FATAL(logger, "Insufficient EGL version for OpenGL usage, minimum supported is 1.4");
329 return false;
330 }
331 if (!_eglBindAPI(EGL_OPENGL_API)) {
332 LOG_FATAL(logger, "Failed to bind OPENGL API");
333 return false;
334 }
335 }
336 }
337
338
339 if(!initialized) {
340 queryDisplayExtensions(eglDisplay);
341 }
342
343 EGLConfig config = nullptr;
344
345 // ------- Create surface if needed ----------------------------------------
346 if(windowData && windowData->windowHandle) {
347
348 if(!config) {
349 config = chooseConfig(eglDisplay, EGL_WINDOW_BIT, settings ? settings->numSamples : 0);
350 if(!config) return false;
351 }
352
353 eglSurface = _eglCreateWindowSurface(eglDisplay, config, windowData->windowHandle, nullptr);
354 if (eglSurface == EGL_NO_SURFACE) {
355 LOG_FATAL(logger, "Failed to create EGL window surface: %s", eglErrorString(_eglGetError()));
356 return false;
357 }
358 }
359
360 else {
361#if 0
362 // Not necessary for dummy contexts
363 static constexpr EGLint attributes[] = {
364 EGL_WIDTH, 32,
365 EGL_HEIGHT, 32,
366 //EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB,
367 //EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
368 EGL_NONE
369 };
370 eglSurface = _eglCreatePbufferSurface(eglDisplay, config, attributes);
371 if (eglSurface == EGL_NO_SURFACE) {
372 LOG_FATAL(logger, "Failed to create EGL pbuffer surface: %s", eglErrorString(_eglGetError()));
373 return false;
374 }
375#endif
376 }
377
378 // ------- Create context --------------------------------------------------
379 if(has_EGL_KHR_no_config_context && shareContext && shareContext->eglContext) {
380 eglContext = shareContext->eglContext;
381 }
382 else {
383 std::vector<EGLint> contextAttributes;
384 switch (desiredPlatform) {
385 case Platform::ES3:
386 contextAttributes.push_back(EGL_CONTEXT_MAJOR_VERSION);
387 contextAttributes.push_back(3);
388 break;
389
390 case Platform::GL:
391 contextAttributes.push_back(EGL_CONTEXT_MAJOR_VERSION);
392 contextAttributes.push_back(4);
393 contextAttributes.push_back(EGL_CONTEXT_MINOR_VERSION);
394 contextAttributes.push_back(3);
395 //contextAttributes.push_back(EGL_CONTEXT_OPENGL_PROFILE_MASK);
396 //contextAttributes.push_back(EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT);
397 break;
398
399 default:
400 assert(false);
401 return false;
402 }
403
404 #ifndef __EMSCRIPTEN__
405 if (debug) {
406 contextAttributes.push_back(EGL_CONTEXT_FLAGS_KHR);
407 contextAttributes.push_back(EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR);
408 }
409 #endif // of not __EMSCRIPTEN__
410 contextAttributes.push_back(EGL_NONE);
411 contextAttributes.push_back(EGL_NONE);
412
413 if(!config && !has_EGL_KHR_no_config_context) {
414 config = chooseConfig(eglDisplay, 0, settings ? settings->numSamples : 0); // EGL_PBUFFER_BIT ?
415 if(!config) return false;
416 }
417
418 eglContext = _eglCreateContext(eglDisplay,
419 has_EGL_KHR_no_config_context ? nullptr : config,
420 shareContext ? shareContext->eglContext : EGL_CAST(::EGLContext, 0),
421 contextAttributes.data());
422 if (!eglContext) {
423 LOG_ERROR(logger, "Failed to create EGL context: %s", eglErrorString(_eglGetError()));
424 return false;
425 }
426 LOG_DEBUG(logger, "Created EGL context");
427 ownContext = true;
428 }
429
430 makeCurrent();
431#ifndef __EMSCRIPTEN__
432 if (!initialized) {
433 getGLFuncPointers();
434 }
435 if (debug) {
436 Cogs::setUpGLDebugging(nullptr, platform);
437 }
438#else
439 (void)debug;
440#endif // of else not __EMSCRIPTEN__
441
442 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
443
444 initialized = true;
445 return true;
446}
447
448bool Cogs::GLContext::makeCurrent()
449{
450 if (!_eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
451 LOG_ERROR_ONCE(logger, "Failed to make EGL context current: %s", eglErrorString(_eglGetError()));
452 return false;
453 }
454
455 return true;
456}
457
458void Cogs::GLContext::swapBuffers(uint32_t /*syncInterval*/, uint32_t /*presentFlags*/)
459{
460#ifndef __EMSCRIPTEN__
461 //_eglSwapInterval(this->eglDisplay, 1);
462 if(!_eglSwapBuffers(eglDisplay, eglSurface)) {
463 LOG_ERROR_ONCE(logger, "Failed to swap EGL buffers: %s", eglErrorString(_eglGetError()));
464 }
465#else
466 glFlush();
467#endif
468}
469
470void Cogs::GLContext::destroy()
471{
472 if(eglDisplay) {
473 if(eglSurface) {
474 _eglDestroySurface(eglDisplay, eglSurface);
475 eglSurface = nullptr;
476 }
477 if(eglContext) {
478 if(ownContext) {
479 _eglDestroyContext(eglDisplay, eglContext);
480 }
481 eglContext = nullptr;
482 }
483 if(ownDisplay) {
484 _eglTerminate(eglDisplay);
485 }
486 eglDisplay = nullptr;
487 }
488}
489
490#endif // of COGS_EGL
Log implementation class.
Definition: LogManager.h:139
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62