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