From bb65f49005ed17806bce2b048d37903cc1342d27 Mon Sep 17 00:00:00 2001 From: Christian Rauch Date: Tue, 28 Jan 2020 10:32:29 +0100 Subject: [PATCH] Linux: update EGL context code to fully work, including offscreen rendering 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 | 17 ++--- intern/ghost/intern/GHOST_ContextEGL.cpp | 74 ++++++++++++++------ intern/ghost/intern/GHOST_ContextEGL.h | 2 +- intern/ghost/intern/GHOST_EventPrinter.cpp | 4 +- intern/ghost/intern/GHOST_SystemX11.cpp | 51 ++++++++++++-- intern/ghost/intern/GHOST_WindowX11.cpp | 78 +++++++++++++++++++--- 6 files changed, 177 insertions(+), 49 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bbb528607c8..17e4ec23ed9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/intern/ghost/intern/GHOST_ContextEGL.cpp b/intern/ghost/intern/GHOST_ContextEGL.cpp index d4eeda2a9ef..e072f0823f3 100644 --- a/intern/ghost/intern/GHOST_ContextEGL.cpp +++ b/intern/ghost/intern/GHOST_ContextEGL.cpp @@ -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(error), code ? code : "", msg ? msg : ""); #else fprintf(stderr, "EGL Error (0x%04X): %s: %s\n", - error, + static_cast(error), code ? code : "", msg ? msg : ""); #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; diff --git a/intern/ghost/intern/GHOST_ContextEGL.h b/intern/ghost/intern/GHOST_ContextEGL.h index cd6b0c959b7..da5ca7ef93f 100644 --- a/intern/ghost/intern/GHOST_ContextEGL.h +++ b/intern/ghost/intern/GHOST_ContextEGL.h @@ -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; diff --git a/intern/ghost/intern/GHOST_EventPrinter.cpp b/intern/ghost/intern/GHOST_EventPrinter.cpp index 119c9f28223..e459da39d14 100644 --- a/intern/ghost/intern/GHOST_EventPrinter.cpp +++ b/intern/ghost/intern/GHOST_EventPrinter.cpp @@ -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; diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index f15641352e0..4c77dbd7abe 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -50,6 +50,7 @@ #if defined(WITH_GL_EGL) # include "GHOST_ContextEGL.h" +# include #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) - GLX_CONTEXT_CORE_PROFILE_BIT_ARB; -#elif defined(WITH_GL_PROFILE_COMPAT) - GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; +#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 -# error // must specify either core or compat at build time +# if defined(WITH_GL_PROFILE_CORE) + GLX_CONTEXT_CORE_PROFILE_BIT_ARB; +# elif defined(WITH_GL_PROFILE_COMPAT) + GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; +# 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; diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index 6fbdacf98d6..349b11728bd 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #ifdef WITH_X11_ALPHA # include #endif @@ -38,8 +39,9 @@ # include "GHOST_DropTargetX11.h" #endif -#if defined(WITH_GL_EGL) +#ifdef WITH_GL_EGL # include "GHOST_ContextEGL.h" +# include #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) - GLX_CONTEXT_CORE_PROFILE_BIT_ARB; -#elif defined(WITH_GL_PROFILE_COMPAT) - GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; +#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 -# error // must specify either core or compat at build time +# if defined(WITH_GL_PROFILE_CORE) + GLX_CONTEXT_CORE_PROFILE_BIT_ARB; +# elif defined(WITH_GL_PROFILE_COMPAT) + GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; +# 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;