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
This commit is contained in:
Christian Rauch 2020-01-28 10:32:29 +01:00 committed by Brecht Van Lommel
parent acb576c762
commit bb65f49005
6 changed files with 177 additions and 49 deletions

@ -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()

@ -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;

@ -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;

@ -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;

@ -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)
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;

@ -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)
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;