1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2013 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup GHOST
22 *
23 * Definition of GHOST_ContextEGL class.
24 */
25
26 #include "GHOST_ContextEGL.h"
27
28 #include <set>
29 #include <sstream>
30 #include <vector>
31
32 #include <cassert>
33 #include <cstdio>
34 #include <cstring>
35
36 #define CASE_CODE_RETURN_STR(code) \
37 case code: \
38 return #code;
39
get_egl_error_enum_string(EGLint error)40 static const char *get_egl_error_enum_string(EGLint error)
41 {
42 switch (error) {
43 CASE_CODE_RETURN_STR(EGL_SUCCESS)
44 CASE_CODE_RETURN_STR(EGL_NOT_INITIALIZED)
45 CASE_CODE_RETURN_STR(EGL_BAD_ACCESS)
46 CASE_CODE_RETURN_STR(EGL_BAD_ALLOC)
47 CASE_CODE_RETURN_STR(EGL_BAD_ATTRIBUTE)
48 CASE_CODE_RETURN_STR(EGL_BAD_CONTEXT)
49 CASE_CODE_RETURN_STR(EGL_BAD_CONFIG)
50 CASE_CODE_RETURN_STR(EGL_BAD_CURRENT_SURFACE)
51 CASE_CODE_RETURN_STR(EGL_BAD_DISPLAY)
52 CASE_CODE_RETURN_STR(EGL_BAD_SURFACE)
53 CASE_CODE_RETURN_STR(EGL_BAD_MATCH)
54 CASE_CODE_RETURN_STR(EGL_BAD_PARAMETER)
55 CASE_CODE_RETURN_STR(EGL_BAD_NATIVE_PIXMAP)
56 CASE_CODE_RETURN_STR(EGL_BAD_NATIVE_WINDOW)
57 CASE_CODE_RETURN_STR(EGL_CONTEXT_LOST)
58 default:
59 return NULL;
60 }
61 }
62
get_egl_error_message_string(EGLint error)63 static const char *get_egl_error_message_string(EGLint error)
64 {
65 switch (error) {
66 case EGL_SUCCESS:
67 return "The last function succeeded without error.";
68
69 case EGL_NOT_INITIALIZED:
70 return (
71 "EGL is not initialized, or could not be initialized, "
72 "for the specified EGL display connection.");
73
74 case EGL_BAD_ACCESS:
75 return (
76 "EGL cannot access a requested resource "
77 "(for example a context is bound in another thread).");
78
79 case EGL_BAD_ALLOC:
80 return "EGL failed to allocate resources for the requested operation.";
81
82 case EGL_BAD_ATTRIBUTE:
83 return "An unrecognized attribute or attribute value was passed in the attribute list.";
84
85 case EGL_BAD_CONTEXT:
86 return "An EGLContext argument does not name a valid EGL rendering context.";
87
88 case EGL_BAD_CONFIG:
89 return "An EGLConfig argument does not name a valid EGL frame buffer configuration.";
90
91 case EGL_BAD_CURRENT_SURFACE:
92 return (
93 "The current surface of the calling thread is a window, "
94 "pixel buffer or pixmap that is no longer valid.");
95
96 case EGL_BAD_DISPLAY:
97 return "An EGLDisplay argument does not name a valid EGL display connection.";
98
99 case EGL_BAD_SURFACE:
100 return (
101 "An EGLSurface argument does not name a valid surface "
102 "(window, pixel buffer or pixmap) configured for GL rendering.");
103
104 case EGL_BAD_MATCH:
105 return (
106 "Arguments are inconsistent "
107 "(for example, a valid context requires buffers not supplied by a valid surface).");
108
109 case EGL_BAD_PARAMETER:
110 return "One or more argument values are invalid.";
111
112 case EGL_BAD_NATIVE_PIXMAP:
113 return "A NativePixmapType argument does not refer to a valid native pixmap.";
114
115 case EGL_BAD_NATIVE_WINDOW:
116 return "A NativeWindowType argument does not refer to a valid native window.";
117
118 case EGL_CONTEXT_LOST:
119 return (
120 "A power management event has occurred. "
121 "The application must destroy all contexts and reinitialize OpenGL ES state "
122 "and objects to continue rendering.");
123
124 default:
125 return NULL;
126 }
127 }
128
egl_chk(bool result,const char * file=NULL,int line=0,const char * text=NULL)129 static bool egl_chk(bool result, const char *file = NULL, int line = 0, const char *text = NULL)
130 {
131 if (!result) {
132 const EGLint error = eglGetError();
133
134 const char *code = get_egl_error_enum_string(error);
135 const char *msg = get_egl_error_message_string(error);
136
137 #ifndef NDEBUG
138 fprintf(stderr,
139 "%s(%d):[%s] -> EGL Error (0x%04X): %s: %s\n",
140 file,
141 line,
142 text,
143 static_cast<unsigned int>(error),
144 code ? code : "<Unknown>",
145 msg ? msg : "<Unknown>");
146 #else
147 fprintf(stderr,
148 "EGL Error (0x%04X): %s: %s\n",
149 static_cast<unsigned int>(error),
150 code ? code : "<Unknown>",
151 msg ? msg : "<Unknown>");
152 #endif
153 }
154
155 return result;
156 }
157
158 #ifndef NDEBUG
159 # define EGL_CHK(x) egl_chk((x), __FILE__, __LINE__, # x)
160 #else
161 # define EGL_CHK(x) egl_chk(x)
162 #endif
163
bindAPI(EGLenum api)164 static inline bool bindAPI(EGLenum api)
165 {
166 if (EGLEW_VERSION_1_2) {
167 return (EGL_CHK(eglBindAPI(api)) == EGL_TRUE);
168 }
169
170 return false;
171 }
172
173 #ifdef WITH_GL_ANGLE
174 HMODULE GHOST_ContextEGL::s_d3dcompiler = NULL;
175 #endif
176
177 EGLContext GHOST_ContextEGL::s_gl_sharedContext = EGL_NO_CONTEXT;
178 EGLint GHOST_ContextEGL::s_gl_sharedCount = 0;
179
180 EGLContext GHOST_ContextEGL::s_gles_sharedContext = EGL_NO_CONTEXT;
181 EGLint GHOST_ContextEGL::s_gles_sharedCount = 0;
182
183 EGLContext GHOST_ContextEGL::s_vg_sharedContext = EGL_NO_CONTEXT;
184 EGLint GHOST_ContextEGL::s_vg_sharedCount = 0;
185
186 #pragma warning(disable : 4715)
187
choose_api(EGLenum api,T & a,T & b,T & c)188 template<typename T> T &choose_api(EGLenum api, T &a, T &b, T &c)
189 {
190 switch (api) {
191 case EGL_OPENGL_API:
192 return a;
193 case EGL_OPENGL_ES_API:
194 return b;
195 case EGL_OPENVG_API:
196 return c;
197 default:
198 abort();
199 }
200 }
201
GHOST_ContextEGL(bool stereoVisual,EGLNativeWindowType nativeWindow,EGLNativeDisplayType nativeDisplay,EGLint contextProfileMask,EGLint contextMajorVersion,EGLint contextMinorVersion,EGLint contextFlags,EGLint contextResetNotificationStrategy,EGLenum api)202 GHOST_ContextEGL::GHOST_ContextEGL(bool stereoVisual,
203 EGLNativeWindowType nativeWindow,
204 EGLNativeDisplayType nativeDisplay,
205 EGLint contextProfileMask,
206 EGLint contextMajorVersion,
207 EGLint contextMinorVersion,
208 EGLint contextFlags,
209 EGLint contextResetNotificationStrategy,
210 EGLenum api)
211 : GHOST_Context(stereoVisual),
212 m_nativeDisplay(nativeDisplay),
213 m_nativeWindow(nativeWindow),
214 m_contextProfileMask(contextProfileMask),
215 m_contextMajorVersion(contextMajorVersion),
216 m_contextMinorVersion(contextMinorVersion),
217 m_contextFlags(contextFlags),
218 m_contextResetNotificationStrategy(contextResetNotificationStrategy),
219 m_api(api),
220 m_context(EGL_NO_CONTEXT),
221 m_surface(EGL_NO_SURFACE),
222 m_display(EGL_NO_DISPLAY),
223 m_swap_interval(1),
224 m_sharedContext(
225 choose_api(api, s_gl_sharedContext, s_gles_sharedContext, s_vg_sharedContext)),
226 m_sharedCount(choose_api(api, s_gl_sharedCount, s_gles_sharedCount, s_vg_sharedCount))
227 {
228 }
229
~GHOST_ContextEGL()230 GHOST_ContextEGL::~GHOST_ContextEGL()
231 {
232 if (m_display != EGL_NO_DISPLAY) {
233
234 bindAPI(m_api);
235
236 if (m_context != EGL_NO_CONTEXT) {
237 if (m_context == ::eglGetCurrentContext())
238 EGL_CHK(::eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
239
240 if (m_context != m_sharedContext || m_sharedCount == 1) {
241 assert(m_sharedCount > 0);
242
243 m_sharedCount--;
244
245 if (m_sharedCount == 0)
246 m_sharedContext = EGL_NO_CONTEXT;
247
248 EGL_CHK(::eglDestroyContext(m_display, m_context));
249 }
250 }
251
252 if (m_surface != EGL_NO_SURFACE)
253 EGL_CHK(::eglDestroySurface(m_display, m_surface));
254 }
255 }
256
swapBuffers()257 GHOST_TSuccess GHOST_ContextEGL::swapBuffers()
258 {
259 return EGL_CHK(::eglSwapBuffers(m_display, m_surface)) ? GHOST_kSuccess : GHOST_kFailure;
260 }
261
setSwapInterval(int interval)262 GHOST_TSuccess GHOST_ContextEGL::setSwapInterval(int interval)
263 {
264 if (EGLEW_VERSION_1_1) {
265 if (EGL_CHK(::eglSwapInterval(m_display, interval))) {
266 m_swap_interval = interval;
267
268 return GHOST_kSuccess;
269 }
270 else {
271 return GHOST_kFailure;
272 }
273 }
274 else {
275 return GHOST_kFailure;
276 }
277 }
278
getSwapInterval(int & intervalOut)279 GHOST_TSuccess GHOST_ContextEGL::getSwapInterval(int &intervalOut)
280 {
281 // This is a bit of a kludge because there does not seem to
282 // be a way to query the swap interval with EGL.
283 intervalOut = m_swap_interval;
284
285 return GHOST_kSuccess;
286 }
287
activateDrawingContext()288 GHOST_TSuccess GHOST_ContextEGL::activateDrawingContext()
289 {
290 if (m_display) {
291 bindAPI(m_api);
292
293 return EGL_CHK(::eglMakeCurrent(m_display, m_surface, m_surface, m_context)) ? GHOST_kSuccess :
294 GHOST_kFailure;
295 }
296 else {
297 return GHOST_kFailure;
298 }
299 }
300
releaseDrawingContext()301 GHOST_TSuccess GHOST_ContextEGL::releaseDrawingContext()
302 {
303 if (m_display) {
304 bindAPI(m_api);
305
306 return EGL_CHK(::eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) ?
307 GHOST_kSuccess :
308 GHOST_kFailure;
309 }
310 else {
311 return GHOST_kFailure;
312 }
313 }
314
initContextEGLEW()315 bool GHOST_ContextEGL::initContextEGLEW()
316 {
317 /* We have to manually get this function before we can call eglewInit, since
318 * it requires a display argument. glewInit() does the same, but we only want
319 * to initialize EGLEW here. */
320 eglGetDisplay = (PFNEGLGETDISPLAYPROC)eglGetProcAddress("eglGetDisplay");
321 if (eglGetDisplay == NULL) {
322 return false;
323 }
324
325 if (!EGL_CHK((m_display = ::eglGetDisplay(m_nativeDisplay)) != EGL_NO_DISPLAY)) {
326 return false;
327 }
328
329 if (GLEW_CHK(eglewInit(m_display)) != GLEW_OK) {
330 fprintf(stderr, "Warning! EGLEW failed to initialize properly.\n");
331 return false;
332 }
333
334 return true;
335 }
336
api_string(EGLenum api)337 static const std::string &api_string(EGLenum api)
338 {
339 static const std::string a("OpenGL");
340 static const std::string b("OpenGL ES");
341 static const std::string c("OpenVG");
342
343 return choose_api(api, a, b, c);
344 }
345
initializeDrawingContext()346 GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
347 {
348 // objects have to be declared here due to the use of goto
349 std::vector<EGLint> attrib_list;
350 EGLint num_config = 0;
351
352 if (m_stereoVisual)
353 fprintf(stderr, "Warning! Stereo OpenGL ES contexts are not supported.\n");
354
355 m_stereoVisual = false; // It doesn't matter what the Window wants.
356
357 if (!initContextEGLEW()) {
358 return GHOST_kFailure;
359 }
360
361 #ifdef WITH_GL_ANGLE
362 // d3dcompiler_XX.dll needs to be loaded before ANGLE will work
363 if (s_d3dcompiler == NULL) {
364 s_d3dcompiler = LoadLibrary(D3DCOMPILER);
365
366 WIN32_CHK(s_d3dcompiler != NULL);
367
368 if (s_d3dcompiler == NULL) {
369 fprintf(stderr, "LoadLibrary(\"" D3DCOMPILER "\") failed!\n");
370 return GHOST_kFailure;
371 }
372 }
373 #endif
374
375 EGLDisplay prev_display = eglGetCurrentDisplay();
376 EGLSurface prev_draw = eglGetCurrentSurface(EGL_DRAW);
377 EGLSurface prev_read = eglGetCurrentSurface(EGL_READ);
378 EGLContext prev_context = eglGetCurrentContext();
379
380 EGLint egl_major, egl_minor;
381
382 if (!EGL_CHK(::eglInitialize(m_display, &egl_major, &egl_minor)))
383 goto error;
384
385 fprintf(stderr, "EGL Version %d.%d\n", egl_major, egl_minor);
386
387 if (!EGL_CHK(::eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)))
388 goto error;
389
390 if (!bindAPI(m_api))
391 goto error;
392
393 // build attribute list
394
395 attrib_list.reserve(20);
396
397 if (m_api == EGL_OPENGL_ES_API && EGLEW_VERSION_1_2) {
398 // According to the spec it seems that you are required to set EGL_RENDERABLE_TYPE,
399 // but some implementations (ANGLE) do not seem to care.
400
401 if (m_contextMajorVersion == 1) {
402 attrib_list.push_back(EGL_RENDERABLE_TYPE);
403 attrib_list.push_back(EGL_OPENGL_ES_BIT);
404 }
405 else if (m_contextMajorVersion == 2) {
406 attrib_list.push_back(EGL_RENDERABLE_TYPE);
407 attrib_list.push_back(EGL_OPENGL_ES2_BIT);
408 }
409 else if (m_contextMajorVersion == 3) {
410 attrib_list.push_back(EGL_RENDERABLE_TYPE);
411 attrib_list.push_back(EGL_OPENGL_ES3_BIT_KHR);
412 }
413 else {
414 fprintf(stderr,
415 "Warning! Unable to request an ES context of version %d.%d\n",
416 m_contextMajorVersion,
417 m_contextMinorVersion);
418 }
419
420 if (!((m_contextMajorVersion == 1) || (m_contextMajorVersion == 2 && EGLEW_VERSION_1_3) ||
421 (m_contextMajorVersion == 3 && /*EGLEW_VERSION_1_4 &&*/ EGLEW_KHR_create_context) ||
422 (m_contextMajorVersion == 3 && EGLEW_VERSION_1_5))) {
423 fprintf(stderr,
424 "Warning! May not be able to create a version %d.%d ES context with version %d.%d "
425 "of EGL\n",
426 m_contextMajorVersion,
427 m_contextMinorVersion,
428 egl_major,
429 egl_minor);
430 }
431 }
432 else {
433 attrib_list.push_back(EGL_RENDERABLE_TYPE);
434 attrib_list.push_back(EGL_OPENGL_BIT);
435 }
436
437 attrib_list.push_back(EGL_RED_SIZE);
438 attrib_list.push_back(8);
439
440 attrib_list.push_back(EGL_GREEN_SIZE);
441 attrib_list.push_back(8);
442
443 attrib_list.push_back(EGL_BLUE_SIZE);
444 attrib_list.push_back(8);
445
446 #ifdef GHOST_OPENGL_ALPHA
447 attrib_list.push_back(EGL_ALPHA_SIZE);
448 attrib_list.push_back(8);
449 #endif
450
451 if (m_nativeWindow == 0) {
452 // off-screen surface
453 attrib_list.push_back(EGL_SURFACE_TYPE);
454 attrib_list.push_back(EGL_PBUFFER_BIT);
455 }
456
457 attrib_list.push_back(EGL_NONE);
458
459 EGLConfig config;
460
461 if (!EGL_CHK(::eglChooseConfig(m_display, &(attrib_list[0]), &config, 1, &num_config)))
462 goto error;
463
464 // A common error is to assume that ChooseConfig worked because it returned EGL_TRUE
465 if (num_config != 1) // num_config should be exactly 1
466 goto error;
467
468 if (m_nativeWindow != 0) {
469 m_surface = ::eglCreateWindowSurface(m_display, config, m_nativeWindow, NULL);
470 }
471 else {
472 static const EGLint pb_attrib_list[] = {
473 EGL_WIDTH,
474 1,
475 EGL_HEIGHT,
476 1,
477 EGL_NONE,
478 };
479 m_surface = ::eglCreatePbufferSurface(m_display, config, pb_attrib_list);
480 }
481
482 if (!EGL_CHK(m_surface != EGL_NO_SURFACE))
483 goto error;
484
485 attrib_list.clear();
486
487 if (EGLEW_VERSION_1_5 || EGLEW_KHR_create_context) {
488 if (m_api == EGL_OPENGL_API || m_api == EGL_OPENGL_ES_API) {
489 if (m_contextMajorVersion != 0) {
490 attrib_list.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
491 attrib_list.push_back(m_contextMajorVersion);
492 }
493
494 if (m_contextMinorVersion != 0) {
495 attrib_list.push_back(EGL_CONTEXT_MINOR_VERSION_KHR);
496 attrib_list.push_back(m_contextMinorVersion);
497 }
498
499 if (m_contextFlags != 0) {
500 attrib_list.push_back(EGL_CONTEXT_FLAGS_KHR);
501 attrib_list.push_back(m_contextFlags);
502 }
503 }
504 else {
505 if (m_contextMajorVersion != 0 || m_contextMinorVersion != 0) {
506 fprintf(stderr,
507 "Warning! Cannot request specific versions of %s contexts.",
508 api_string(m_api).c_str());
509 }
510
511 if (m_contextFlags != 0) {
512 fprintf(stderr, "Warning! Flags cannot be set on %s contexts.", api_string(m_api).c_str());
513 }
514 }
515
516 if (m_api == EGL_OPENGL_API) {
517 if (m_contextProfileMask != 0) {
518 attrib_list.push_back(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR);
519 attrib_list.push_back(m_contextProfileMask);
520 }
521 }
522 else {
523 if (m_contextProfileMask != 0)
524 fprintf(
525 stderr, "Warning! Cannot select profile for %s contexts.", api_string(m_api).c_str());
526 }
527
528 if (m_api == EGL_OPENGL_API || EGLEW_VERSION_1_5) {
529 if (m_contextResetNotificationStrategy != 0) {
530 attrib_list.push_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR);
531 attrib_list.push_back(m_contextResetNotificationStrategy);
532 }
533 }
534 else {
535 if (m_contextResetNotificationStrategy != 0) {
536 fprintf(stderr,
537 "Warning! EGL %d.%d cannot set the reset notification strategy on %s contexts.",
538 egl_major,
539 egl_minor,
540 api_string(m_api).c_str());
541 }
542 }
543 }
544 else {
545 if (m_api == EGL_OPENGL_ES_API) {
546 if (m_contextMajorVersion != 0) {
547 attrib_list.push_back(EGL_CONTEXT_CLIENT_VERSION);
548 attrib_list.push_back(m_contextMajorVersion);
549 }
550 }
551 else {
552 if (m_contextMajorVersion != 0 || m_contextMinorVersion != 0) {
553 fprintf(stderr,
554 "Warning! EGL %d.%d is unable to select between versions of %s.",
555 egl_major,
556 egl_minor,
557 api_string(m_api).c_str());
558 }
559 }
560
561 if (m_contextFlags != 0) {
562 fprintf(stderr, "Warning! EGL %d.%d is unable to set context flags.", egl_major, egl_minor);
563 }
564 if (m_contextProfileMask != 0) {
565 fprintf(stderr,
566 "Warning! EGL %d.%d is unable to select between profiles.",
567 egl_major,
568 egl_minor);
569 }
570 if (m_contextResetNotificationStrategy != 0) {
571 fprintf(stderr,
572 "Warning! EGL %d.%d is unable to set the reset notification strategies.",
573 egl_major,
574 egl_minor);
575 }
576 }
577
578 attrib_list.push_back(EGL_NONE);
579
580 m_context = ::eglCreateContext(m_display, config, m_sharedContext, &(attrib_list[0]));
581
582 if (!EGL_CHK(m_context != EGL_NO_CONTEXT))
583 goto error;
584
585 if (m_sharedContext == EGL_NO_CONTEXT)
586 m_sharedContext = m_context;
587
588 m_sharedCount++;
589
590 if (!EGL_CHK(::eglMakeCurrent(m_display, m_surface, m_surface, m_context)))
591 goto error;
592
593 initContextGLEW();
594
595 initClearGL();
596 ::eglSwapBuffers(m_display, m_surface);
597
598 return GHOST_kSuccess;
599
600 error:
601 if (prev_display != EGL_NO_DISPLAY)
602 EGL_CHK(eglMakeCurrent(prev_display, prev_draw, prev_read, prev_context));
603
604 return GHOST_kFailure;
605 }
606
releaseNativeHandles()607 GHOST_TSuccess GHOST_ContextEGL::releaseNativeHandles()
608 {
609 m_nativeWindow = 0;
610 m_nativeDisplay = NULL;
611
612 return GHOST_kSuccess;
613 }
614