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