Linux: update EGL context code to fully work, including offscreen rendering
authorChristian Rauch <christian.rauch>
Tue, 28 Jan 2020 09:32:29 +0000 (10:32 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Tue, 28 Jan 2020 09:59:42 +0000 (10:59 +0100)
This is a step towards Wayland and headless rendering support, using EGL
instead of GLX. The EGL backend is not enabled by default, it can be tested
using WITH_GL_EGL=ON.

Differential Revision: https://developer.blender.org/D6585

CMakeLists.txt
intern/ghost/intern/GHOST_ContextEGL.cpp
intern/ghost/intern/GHOST_ContextEGL.h
intern/ghost/intern/GHOST_EventPrinter.cpp
intern/ghost/intern/GHOST_SystemX11.cpp
intern/ghost/intern/GHOST_WindowX11.cpp

index bbb528607c8d53d63388ab8ff0270b08b4d0e608..17e4ec23ed952c05735aa1be48d6641609ceb53f 100644 (file)
@@ -978,7 +978,7 @@ if(WITH_GL_PROFILE_ES20)
       )
     endif()
 
-    list(APPEND BLENDER_GL_LIBRARIES OPENGLES_LIBRARY)
+    list(APPEND BLENDER_GL_LIBRARIES "${OPENGLES_LIBRARY}")
 
   else()
     set(OPENGLES_LIBRARY "" CACHE FILEPATH "OpenGL ES 2.0 library file")
@@ -1038,7 +1038,10 @@ else()
 endif()
 
 if(WITH_GL_EGL)
-  list(APPEND GL_DEFINITIONS -DWITH_GL_EGL)
+  find_package(OpenGL REQUIRED EGL)
+  list(APPEND BLENDER_GL_LIBRARIES OpenGL::EGL)
+
+  list(APPEND GL_DEFINITIONS -DWITH_GL_EGL -DGLEW_EGL -DGLEW_INC_EGL)
 
   if(WITH_SYSTEM_GLES)
     if(NOT OPENGLES_EGL_LIBRARY)
@@ -1048,7 +1051,7 @@ if(WITH_GL_EGL)
       )
     endif()
 
-    list(APPEND BLENDER_GL_LIBRARIES OPENGLES_EGL_LIBRARY)
+    list(APPEND BLENDER_GL_LIBRARIES ${OPENGLES_EGL_LIBRARY})
 
   else()
     set(OPENGLES_EGL_LIBRARY "" CACHE FILEPATH "EGL library file")
@@ -1088,10 +1091,6 @@ else()
   list(APPEND GL_DEFINITIONS -DWITH_GL_PROFILE_CORE)
 endif()
 
-if(WITH_GL_EGL)
-  list(APPEND GL_DEFINITIONS -DWITH_EGL)
-endif()
-
 #-----------------------------------------------------------------------------
 # Configure OpenMP.
 if(WITH_OPENMP)
@@ -1163,10 +1162,6 @@ else()
       list(APPEND GL_DEFINITIONS -DGL_ES_VERSION_1_0=0 -DGL_ES_VERSION_CL_1_1=0 -DGL_ES_VERSION_CM_1_1=0)
     endif()
 
-    if(WITH_GL_EGL)
-      list(APPEND GL_DEFINITIONS -DGLEW_INC_EGL)
-    endif()
-
     set(BLENDER_GLEW_LIBRARIES extern_glew_es bf_intern_glew_mx)
 
   else()
index d4eeda2a9effcb21dae547aa4184fe8a1d7e2f32..e072f0823f345524dba0f690d7007816be57dcc2 100644 (file)
@@ -37,7 +37,7 @@
   case code: \
     return #code;
 
-static const char *get_egl_error_enum_string(EGLenum error)
+static const char *get_egl_error_enum_string(EGLint error)
 {
   switch (error) {
     CASE_CODE_RETURN_STR(EGL_SUCCESS)
@@ -60,7 +60,7 @@ static const char *get_egl_error_enum_string(EGLenum error)
   }
 }
 
-static const char *get_egl_error_message_string(EGLenum error)
+static const char *get_egl_error_message_string(EGLint error)
 {
   switch (error) {
     case EGL_SUCCESS:
@@ -129,7 +129,7 @@ static const char *get_egl_error_message_string(EGLenum error)
 static bool egl_chk(bool result, const char *file = NULL, int line = 0, const char *text = NULL)
 {
   if (!result) {
-    EGLenum error = eglGetError();
+    const EGLint error = eglGetError();
 
     const char *code = get_egl_error_enum_string(error);
     const char *msg = get_egl_error_message_string(error);
@@ -140,13 +140,13 @@ static bool egl_chk(bool result, const char *file = NULL, int line = 0, const ch
             file,
             line,
             text,
-            error,
+            static_cast<unsigned int>(error),
             code ? code : "<Unknown>",
             msg ? msg : "<Unknown>");
 #else
     fprintf(stderr,
             "EGL Error (0x%04X): %s: %s\n",
-            error,
+            static_cast<unsigned int>(error),
             code ? code : "<Unknown>",
             msg ? msg : "<Unknown>");
 #endif
@@ -225,8 +225,6 @@ GHOST_ContextEGL::GHOST_ContextEGL(bool stereoVisual,
           choose_api(api, s_gl_sharedContext, s_gles_sharedContext, s_vg_sharedContext)),
       m_sharedCount(choose_api(api, s_gl_sharedCount, s_gles_sharedCount, s_vg_sharedCount))
 {
-  assert(m_nativeWindow != 0);
-  assert(m_nativeDisplay != NULL);
 }
 
 GHOST_ContextEGL::~GHOST_ContextEGL()
@@ -253,8 +251,6 @@ GHOST_ContextEGL::~GHOST_ContextEGL()
 
     if (m_surface != EGL_NO_SURFACE)
       EGL_CHK(::eglDestroySurface(m_display, m_surface));
-
-    EGL_CHK(::eglTerminate(m_display));
   }
 }
 
@@ -307,18 +303,35 @@ GHOST_TSuccess GHOST_ContextEGL::releaseDrawingContext()
   if (m_display) {
     bindAPI(m_api);
 
-    return EGL_CHK(::eglMakeCurrent(m_display, None, None, NULL)) ? GHOST_kSuccess :
-                                                                    GHOST_kFailure;
+    return EGL_CHK(::eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) ?
+               GHOST_kSuccess :
+               GHOST_kFailure;
   }
   else {
     return GHOST_kFailure;
   }
 }
 
-void GHOST_ContextEGL::initContextEGLEW()
+bool GHOST_ContextEGL::initContextEGLEW()
 {
-  if (GLEW_CHK(eglewInit(m_display)) != GLEW_OK)
+  /* We have to manually get this function before we can call eglewInit, since
+   * it requires a display argument. glewInit() does the same, but we only want
+   * to intialize EGLEW here. */
+  eglGetDisplay = (PFNEGLGETDISPLAYPROC)eglGetProcAddress("eglGetDisplay");
+  if (eglGetDisplay == NULL) {
+    return false;
+  }
+
+  if (!EGL_CHK((m_display = ::eglGetDisplay(m_nativeDisplay)) != EGL_NO_DISPLAY)) {
+    return false;
+  }
+
+  if (GLEW_CHK(eglewInit(m_display)) != GLEW_OK) {
     fprintf(stderr, "Warning! EGLEW failed to initialize properly.\n");
+    return false;
+  }
+
+  return true;
 }
 
 static const std::string &api_string(EGLenum api)
@@ -341,6 +354,10 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
 
   m_stereoVisual = false;  // It doesn't matter what the Window wants.
 
+  if (!initContextEGLEW()) {
+    return GHOST_kFailure;
+  }
+
 #ifdef WITH_GL_ANGLE
   // d3dcompiler_XX.dll needs to be loaded before ANGLE will work
   if (s_d3dcompiler == NULL) {
@@ -360,11 +377,6 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
   EGLSurface prev_read = eglGetCurrentSurface(EGL_READ);
   EGLContext prev_context = eglGetCurrentContext();
 
-  m_display = ::eglGetDisplay(m_nativeDisplay);
-
-  if (!EGL_CHK(m_display != EGL_NO_DISPLAY))
-    return GHOST_kFailure;
-
   EGLint egl_major, egl_minor;
 
   if (!EGL_CHK(::eglInitialize(m_display, &egl_major, &egl_minor)))
@@ -375,8 +387,6 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
   if (!EGL_CHK(::eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)))
     goto error;
 
-  initContextEGLEW();
-
   if (!bindAPI(m_api))
     goto error;
 
@@ -419,6 +429,10 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
               egl_minor);
     }
   }
+  else {
+    attrib_list.push_back(EGL_RENDERABLE_TYPE);
+    attrib_list.push_back(EGL_OPENGL_BIT);
+  }
 
   attrib_list.push_back(EGL_RED_SIZE);
   attrib_list.push_back(8);
@@ -434,6 +448,12 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
   attrib_list.push_back(8);
 #endif
 
+  if (m_nativeWindow == 0) {
+    // off-screen surface
+    attrib_list.push_back(EGL_SURFACE_TYPE);
+    attrib_list.push_back(EGL_PBUFFER_BIT);
+  }
+
   attrib_list.push_back(EGL_NONE);
 
   EGLConfig config;
@@ -445,7 +465,19 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
   if (num_config != 1)  // num_config should be exactly 1
     goto error;
 
-  m_surface = ::eglCreateWindowSurface(m_display, config, m_nativeWindow, NULL);
+  if (m_nativeWindow != 0) {
+    m_surface = ::eglCreateWindowSurface(m_display, config, m_nativeWindow, NULL);
+  }
+  else {
+    static const EGLint pb_attrib_list[] = {
+        EGL_WIDTH,
+        1,
+        EGL_HEIGHT,
+        1,
+        EGL_NONE,
+    };
+    m_surface = ::eglCreatePbufferSurface(m_display, config, pb_attrib_list);
+  }
 
   if (!EGL_CHK(m_surface != EGL_NO_SURFACE))
     goto error;
index cd6b0c959b7ed6e00304a7ac555088404b07ee39..da5ca7ef93f96929e6f99e2e5c03df6e574f19e0 100644 (file)
@@ -102,7 +102,7 @@ class GHOST_ContextEGL : public GHOST_Context {
   GHOST_TSuccess getSwapInterval(int &intervalOut);
 
  private:
-  void initContextEGLEW();
+  bool initContextEGLEW();
 
   EGLNativeDisplayType m_nativeDisplay;
   EGLNativeWindowType m_nativeWindow;
index 119c9f28223449e1d4b7a5ba26001c1e20d3f047..e459da39d1471eb5e39eb718f22101ce6f72bb14 100644 (file)
@@ -39,7 +39,7 @@ bool GHOST_EventPrinter::processEvent(GHOST_IEvent *event)
   if (event->getType() == GHOST_kEventWindowUpdate)
     return false;
 
-  std::cout << "\nGHOST_EventPrinter::processEvent, time: " << (GHOST_TInt32)event->getTime()
+  std::cout << "GHOST_EventPrinter::processEvent, time: " << (GHOST_TInt32)event->getTime()
             << ", type: ";
   switch (event->getType()) {
     case GHOST_kEventUnknown:
@@ -164,6 +164,8 @@ bool GHOST_EventPrinter::processEvent(GHOST_IEvent *event)
       break;
   }
 
+  std::cout << std::endl;
+
   std::cout.flush();
 
   return handled;
index f15641352e03e01078d6b6f3825026a404393496..4c77dbd7abe737ddb94942fee89e5f7a71158552 100644 (file)
@@ -50,6 +50,7 @@
 
 #if defined(WITH_GL_EGL)
 #  include "GHOST_ContextEGL.h"
+#  include <EGL/eglext.h>
 #else
 #  include "GHOST_ContextGLX.h"
 #endif
@@ -243,6 +244,10 @@ GHOST_SystemX11::~GHOST_SystemX11()
   clearXInputDevices();
 #endif /* WITH_X11_XINPUT */
 
+#ifdef WITH_GL_EGL
+  ::eglTerminate(::eglGetDisplay(m_display));
+#endif
+
   if (m_xkb_descr) {
     XkbFreeKeyboard(m_xkb_descr, XkbAllComponentsMask, true);
   }
@@ -406,17 +411,39 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext()
 #endif
 
   const int profile_mask =
-#if defined(WITH_GL_PROFILE_CORE)
+#ifdef WITH_GL_EGL
+#  if defined(WITH_GL_PROFILE_CORE)
+      EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT;
+#  elif defined(WITH_GL_PROFILE_COMPAT)
+      EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT;
+#  else
+#    error  // must specify either core or compat at build time
+#  endif
+#else
+#  if defined(WITH_GL_PROFILE_CORE)
       GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
-#elif defined(WITH_GL_PROFILE_COMPAT)
+#  elif defined(WITH_GL_PROFILE_COMPAT)
       GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
-#else
-#  error  // must specify either core or compat at build time
+#  else
+#    error  // must specify either core or compat at build time
+#  endif
 #endif
 
   GHOST_Context *context;
 
   for (int minor = 5; minor >= 0; --minor) {
+#if defined(WITH_GL_EGL)
+    context = new GHOST_ContextEGL(false,
+                                   EGLNativeWindowType(nullptr),
+                                   EGLNativeDisplayType(m_display),
+                                   profile_mask,
+                                   4,
+                                   minor,
+                                   GHOST_OPENGL_EGL_CONTEXT_FLAGS |
+                                       (false ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
+                                   GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
+                                   EGL_OPENGL_API);
+#else
     context = new GHOST_ContextGLX(false,
                                    (Window)NULL,
                                    m_display,
@@ -427,6 +454,7 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext()
                                    GHOST_OPENGL_GLX_CONTEXT_FLAGS |
                                        (false ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
                                    GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
+#endif
 
     if (context->initializeDrawingContext())
       return context;
@@ -434,6 +462,18 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext()
       delete context;
   }
 
+#if defined(WITH_GL_EGL)
+  context = new GHOST_ContextEGL(false,
+                                 EGLNativeWindowType(nullptr),
+                                 EGLNativeDisplayType(m_display),
+                                 profile_mask,
+                                 3,
+                                 3,
+                                 GHOST_OPENGL_EGL_CONTEXT_FLAGS |
+                                     (false ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
+                                 GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
+                                 EGL_OPENGL_API);
+#else
   context = new GHOST_ContextGLX(false,
                                  (Window)NULL,
                                  m_display,
@@ -444,6 +484,7 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext()
                                  GHOST_OPENGL_GLX_CONTEXT_FLAGS |
                                      (false ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
                                  GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
+#endif
 
   if (context->initializeDrawingContext())
     return context;
index 6fbdacf98d6652a56021d905daee6260e7e4166b..349b11728bdf2d00469fb860e226aeaf5b81c6c7 100644 (file)
@@ -25,6 +25,7 @@
 #include <X11/cursorfont.h>
 #include <X11/Xatom.h>
 #include <X11/Xutil.h>
+#include <X11/Xmd.h>
 #ifdef WITH_X11_ALPHA
 #  include <X11/extensions/Xrender.h>
 #endif
@@ -38,8 +39,9 @@
 #  include "GHOST_DropTargetX11.h"
 #endif
 
-#if defined(WITH_GL_EGL)
+#ifdef WITH_GL_EGL
 #  include "GHOST_ContextEGL.h"
+#  include <EGL/eglext.h>
 #else
 #  include "GHOST_ContextGLX.h"
 #endif
@@ -101,6 +103,18 @@ enum {
 #define _NET_WM_STATE_ADD 1
 // #define _NET_WM_STATE_TOGGLE 2 // UNUSED
 
+#ifdef WITH_GL_EGL
+
+static XVisualInfo *x11_visualinfo_from_egl(Display *display)
+{
+  int num_visuals;
+  XVisualInfo vinfo_template;
+  vinfo_template.screen = DefaultScreen(display);
+  return XGetVisualInfo(display, VisualScreenMask, &vinfo_template, &num_visuals);
+}
+
+#else
+
 static XVisualInfo *x11_visualinfo_from_glx(Display *display,
                                             bool stereoVisual,
                                             bool needAlpha,
@@ -124,11 +138,11 @@ static XVisualInfo *x11_visualinfo_from_glx(Display *display,
     return NULL;
   }
   glx_version = glx_major * 100 + glx_minor;
-#ifndef WITH_X11_ALPHA
+#  ifndef WITH_X11_ALPHA
   (void)glx_version;
-#endif
+#  endif
 
-#ifdef WITH_X11_ALPHA
+#  ifdef WITH_X11_ALPHA
   if (needAlpha && glx_version >= 103 &&
       (glXChooseFBConfig || (glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC)glXGetProcAddressARB(
                                  (const GLubyte *)"glXChooseFBConfig")) != NULL) &&
@@ -170,7 +184,7 @@ static XVisualInfo *x11_visualinfo_from_glx(Display *display,
     }
   }
   else
-#endif
+#  endif
   {
     /* legacy, don't use extension */
     GHOST_X11_GL_GetAttributes(glx_attribs, 64, stereoVisual, needAlpha, false);
@@ -194,6 +208,8 @@ static XVisualInfo *x11_visualinfo_from_glx(Display *display,
   return NULL;
 }
 
+#endif  // WITH_GL_EGL
+
 GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
                                  Display *display,
                                  const STR_String &title,
@@ -230,8 +246,13 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
       m_is_debug_context(is_debug)
 {
   if (type == GHOST_kDrawingContextTypeOpenGL) {
+#ifdef WITH_GL_EGL
+    m_visualInfo = x11_visualinfo_from_egl(m_display);
+    (void)alphaBackground;
+#else
     m_visualInfo = x11_visualinfo_from_glx(
         m_display, stereoVisual, alphaBackground, (GLXFBConfig *)&m_fbconfig);
+#endif
   }
   else {
     XVisualInfo tmp = {0};
@@ -1318,17 +1339,40 @@ GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type
 #endif
 
     const int profile_mask =
-#if defined(WITH_GL_PROFILE_CORE)
+#ifdef WITH_GL_EGL
+#  if defined(WITH_GL_PROFILE_CORE)
+        EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT;
+#  elif defined(WITH_GL_PROFILE_COMPAT)
+        EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT;
+#  else
+#    error  // must specify either core or compat at build time
+#  endif
+#else
+#  if defined(WITH_GL_PROFILE_CORE)
         GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
-#elif defined(WITH_GL_PROFILE_COMPAT)
+#  elif defined(WITH_GL_PROFILE_COMPAT)
         GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
-#else
-#  error  // must specify either core or compat at build time
+#  else
+#    error  // must specify either core or compat at build time
+#  endif
 #endif
 
     GHOST_Context *context;
 
     for (int minor = 5; minor >= 0; --minor) {
+#ifdef WITH_GL_EGL
+      context = new GHOST_ContextEGL(
+          m_wantStereoVisual,
+          EGLNativeWindowType(m_window),
+          EGLNativeDisplayType(m_display),
+          profile_mask,
+          4,
+          minor,
+          GHOST_OPENGL_EGL_CONTEXT_FLAGS |
+              (m_is_debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
+          GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
+          EGL_OPENGL_API);
+#else
       context = new GHOST_ContextGLX(m_wantStereoVisual,
                                      m_window,
                                      m_display,
@@ -1339,6 +1383,7 @@ GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type
                                      GHOST_OPENGL_GLX_CONTEXT_FLAGS |
                                          (m_is_debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
                                      GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
+#endif
 
       if (context->initializeDrawingContext())
         return context;
@@ -1346,6 +1391,18 @@ GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type
         delete context;
     }
 
+#ifdef WITH_GL_EGL
+    context = new GHOST_ContextEGL(m_wantStereoVisual,
+                                   EGLNativeWindowType(m_window),
+                                   EGLNativeDisplayType(m_display),
+                                   profile_mask,
+                                   3,
+                                   3,
+                                   GHOST_OPENGL_EGL_CONTEXT_FLAGS |
+                                       (m_is_debug_context ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
+                                   GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
+                                   EGL_OPENGL_API);
+#else
     context = new GHOST_ContextGLX(m_wantStereoVisual,
                                    m_window,
                                    m_display,
@@ -1356,6 +1413,7 @@ GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type
                                    GHOST_OPENGL_GLX_CONTEXT_FLAGS |
                                        (m_is_debug_context ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
                                    GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY);
+#endif
 
     if (context->initializeDrawingContext())
       return context;