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