Blender  V2.93
GHOST_ContextEGL.cpp
Go to the documentation of this file.
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 
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 
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 
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 
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 
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 
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 
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 
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 
258 {
259  return EGL_CHK(::eglSwapBuffers(m_display, m_surface)) ? GHOST_kSuccess : GHOST_kFailure;
260 }
261 
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 
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 
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 :
295  }
296  else {
297  return GHOST_kFailure;
298  }
299 }
300 
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)) ?
309  }
310  else {
311  return GHOST_kFailure;
312  }
313 }
314 
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 
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 
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 
608 {
609  m_nativeWindow = 0;
610  m_nativeDisplay = NULL;
611 
612  return GHOST_kSuccess;
613 }
static const char * get_egl_error_message_string(EGLint error)
T & choose_api(EGLenum api, T &a, T &b, T &c)
#define EGL_CHK(x)
static const std::string & api_string(EGLenum api)
static const char * get_egl_error_enum_string(EGLint error)
static bool egl_chk(bool result, const char *file=NULL, int line=0, const char *text=NULL)
static bool bindAPI(EGLenum api)
#define CASE_CODE_RETURN_STR(code)
GHOST_TSuccess
Definition: GHOST_Types.h:91
@ GHOST_kFailure
Definition: GHOST_Types.h:91
@ GHOST_kSuccess
Definition: GHOST_Types.h:91
GHOST_TSuccess releaseDrawingContext()
GHOST_TSuccess activateDrawingContext()
GHOST_TSuccess getSwapInterval(int &intervalOut)
GHOST_TSuccess initializeDrawingContext()
GHOST_TSuccess setSwapInterval(int interval)
GHOST_ContextEGL(bool stereoVisual, EGLNativeWindowType nativeWindow, EGLNativeDisplayType nativeDisplay, EGLint contextProfileMask, EGLint contextMajorVersion, EGLint contextMinorVersion, EGLint contextFlags, EGLint contextResetNotificationStrategy, EGLenum api)
GHOST_TSuccess releaseNativeHandles()
GHOST_TSuccess swapBuffers()
void initContextGLEW()
static void initClearGL()
FILE * file
#define GLEW_CHK(x)
Definition: glew-mx.h:64
#define T
static void error(const char *str)
Definition: meshlaplacian.c:65
static unsigned c
Definition: RandGen.cpp:97
static unsigned a[3]
Definition: RandGen.cpp:92