Broothy
Broothy

Reputation: 721

EGL vs OpenGL context

I use BGFX framework for rendering in an application. I'm on Linux with an nvidia graphics card, and the BGFX build I use uses OpenGL as a backend (don't want to switch to Vulkan backend).

Everything worked fine, but one new feature requires me to use EGL. The first thing I do in the main is setting EGL to use OpenGL as a rendering API with:

if (not eglBindAPI(EGL_OPENGL_API) || (eglGetError() != EGL_SUCCESS))
    //error handling

It works well.

Then I create an X11 window, I call eglGetDisplay, call eglInitialize, call eglChooseConfig, all of them returns without any error.

Then I call BGFX init, it runs well without any error.

At this point I have an initialized BGFX (using OpenGL backend), a current OpenGL context (created by BGFX):

std::cout << "GL Cont: " << glXGetCurrentContext() << std::endl; // Valid pointer
std::cout << "EGL Cont: " << eglGetCurrentContext() << std::endl; // 0x0
std::cout << "BGFX Renderer: " << bgfx::getRendererType() << std::endl; // 8 - OpenGL

Then I would like to execute the new EGL stuff related to the new feature on a different thread (I call eglBindAPI on the new thread as well):

EGLContext globalEglContext{};
{
    static constexpr EGLint contextAttr[]{
        EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
    };
    globalEglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttr);
    if (EGL_NO_CONTEXT == globalEglContext)
    {    //error handling }
}

if (!eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, globalEglContext)) 
{
    printf("Error on eglMakeCurrent (error: 0x%x)", eglGetError());
}

The context creation is fine, but the eglMakeCurrent call returns false, but the error code is 0x3000 (EGL_SUCCESS):

Error on eglMakeCurrent (error: 0x3000)

I cannot ignore the error as the next EGL operation fails, so it is an error indeed.


If I execute the very same context creation code on the main thread I get:

Error on eglMakeCurrent (error: 0x3002)

Checking 0x3002 (EGL_BAD_ACCESS) in the manual doesn't explain my case.


If I create & make my EGL context current on the main thread before initializing BGFX and I add the following X11 error handler:

XSetErrorHandler(+[](Display *display, XErrorEvent *error)
{
    char buf[255];
    XGetErrorText(display, error->error_code, buf, 255);
    printf("X11 error: %s", buf);
    return 1;
}); 

Then the context creation and making it current works well, but during BGFX init I get the following error message:

X11 error: GLXBadDrawableX11 error: GLXBadDrawableX11

I have two questions:

  1. Is it possible that EGL and OpenGL contexts cannot be used in the same time? (On a thread I would have a current OpenGL context while on another thread an EGL context)
  2. If it is not possible to use OpenGL and EGL contexts in the same time not even on different threads then how could I use EGL features while I would like to continue using OpenGL as a rendering backend in the same time?

UPDATE:

I created a test app that creates and makes current a GLX context, then creates and tries to make current an EGL context and it fails.

Does it mean that EGL and OpenGL cannot be used in the same time?

The full source code (main.cpp):

#include <iostream>
#include <assert.h>
#include <thread>
#include <chrono>
#include <future>

#include <EGL/egl.h>
#include <EGL/eglext.h>

#include <GL/gl.h>
#include <GL/glx.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_syswm.h>

int main() 
{
    if (not eglBindAPI(EGL_OPENGL_API) || (eglGetError() != EGL_SUCCESS))
    {
        printf("Could not bind EGL ES API (error: 0x%0x)\n", eglGetError());
        return -1;
    }

    XSetErrorHandler(+[](Display *display, XErrorEvent *error)
    {
        char buf[255];
        XGetErrorText(display, error->error_code, buf, 255);
        printf("X11 error: %s\n", buf);
        return 1;
    });

    //
    // WINDOW
    //
    uint32_t flags = SDL_WINDOW_RESIZABLE;

    const auto sdlWindow = SDL_CreateWindow("win", 0, 0, 640, 480, flags);
    SDL_ShowWindow(sdlWindow);

    SDL_SysWMinfo wmi;
    SDL_VERSION(&wmi.version);
    if (!SDL_GetWindowWMInfo(sdlWindow, &wmi))
    {
        return -1;
    }
    auto display = wmi.info.x11.display;

    //
    // EGL INIT
    //

    void *eglConfig{};
    void *eglDisplay{};
    void *eglSurface{};

    // EGL init
    {
        // Get EGL display
        eglDisplay = eglGetDisplay((EGLNativeDisplayType)display);
        if (eglDisplay == EGL_NO_DISPLAY)
        {
            printf("Could not create EGLDisplay (error: 0x%0x)\n", eglGetError());
            return -1;
        }

        // Init EGL display
        {
            EGLint major;
            EGLint minor;
            if (!eglInitialize(eglDisplay, &major, &minor))
            {
                printf("Failed initializing EGL (error: 0x%0x)\n", eglGetError());
                return -1;
            }
            else
            {
                printf("EGL initialized (Version: %d.%d)\n", major, minor);
            }
        }

        // Choose EGL config
        {
            static constexpr EGLint cfgAttr[]{
                EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
                EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
                EGL_RED_SIZE, 1,
                EGL_GREEN_SIZE, 1,
                EGL_BLUE_SIZE, 1,
                EGL_ALPHA_SIZE, 1,
                EGL_DEPTH_SIZE, 1,
                EGL_NONE
            };

            EGLint numConfigs{0};

            if (!eglChooseConfig(eglDisplay, cfgAttr, &eglConfig, 1, &numConfigs))
            {
                printf("Failed on eglChooseConfig (error: 0x%0x)\n", eglGetError());
                return false;
            }
        }

        // Create EGL surface
        eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, wmi.info.x11.window, nullptr);
        if(eglSurface == EGL_NO_SURFACE)
        {
            printf("Could not create EGLSurface (error: 0x%0x)\n", eglGetError());
            return -1;
        }
    }

    //
    // OpenGL context
    //
    const auto screen = DefaultScreenOfDisplay(display);
    const auto screenId = DefaultScreen(display);

    static GLint glxAttribs[] = {
        GLX_RGBA,
        GLX_DOUBLEBUFFER,
        GLX_DEPTH_SIZE,     24,
        GLX_STENCIL_SIZE,   8,
        GLX_RED_SIZE,       8,
        GLX_GREEN_SIZE,     8,
        GLX_BLUE_SIZE,      8,
        GLX_SAMPLE_BUFFERS, 0,
        GLX_SAMPLES,        0,
        None
    };
    XVisualInfo* visual = glXChooseVisual(display, screenId, glxAttribs);

    if (visual == 0)
    {
        printf("Could not create correct visual window.\n");
        return -1;
    }

    GLXContext context = glXCreateContext(display, visual, NULL, GL_TRUE);
    
    if( !glXMakeContextCurrent(display, None, None, context))
    {
        printf("Could not make context current.\n");
        return -1;
    }

    std::cout << "GL Cont: " << glXGetCurrentContext() << std::endl;
    std::cout << "EGL Cont: " << eglGetCurrentContext() << std::endl;

    /*
    // Uncomment this and EGL context creation works
    if( !glXMakeContextCurrent(display, None, None, NULL))
    {
        printf("Could not make context current.\n");
        return -1;
    }

    std::cout << "GL Cont: " << glXGetCurrentContext() << std::endl;
    std::cout << "EGL Cont: " << eglGetCurrentContext() << std::endl;
    */

    //
    // EGL CONTEXT
    //

    auto launchPolicy = std::launch::deferred; // change it to std::launch::async to create EGL context on a thread

    auto res = std::async(launchPolicy, [&](){
        void *globalEglContext;
        {
            static constexpr EGLint contextAttr[]{
                EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
            };
            globalEglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttr);
            if (EGL_NO_CONTEXT == globalEglContext)
            {
                printf("Error creating EGL context (error: 0x%x)\n", eglGetError());
                exit(-2);
            }
        }

        // fails with 0x3000 (EGL_SUCCESS) on a different thread.
        // fails with 0x3002 (EGL_BAD_ACCESS) on the main thread.
        if (!eglMakeCurrent(eglDisplay, eglSurface, eglSurface, globalEglContext)) 
        {
            printf("Error on eglMakeCurrent (error: 0x%x)\n", eglGetError());
            exit(-3);
        }
        return 0;
    });

    res.wait();

    std::cout << "GL Cont: " << glXGetCurrentContext() << std::endl;
    std::cout << "EGL Cont: " << eglGetCurrentContext() << std::endl;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)

project(EGLTest LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(OpenGL REQUIRED COMPONENTS EGL)
find_package(PkgConfig REQUIRED)

pkg_check_modules(X11 REQUIRED x11)
pkg_check_modules(SDL2 REQUIRED sdl2)

add_executable(${PROJECT_NAME} main.cpp)

target_include_directories(
    ${PROJECT_NAME}
    SYSTEM
    PUBLIC ${OPENGL_EGL_INCLUDE_DIRS}
    PUBLIC ${SDL2_INCLUDE_DIRS}
)

target_link_libraries(
    ${PROJECT_NAME}
    OpenGL::EGL
    ${SDL2_LIBRARIES}
)

UPDATE 2: My config:

Kubuntu 22.04 LTS 5.15.0-52-generic
Operating System: Ubuntu 22.04
KDE Plasma Version: 5.24.6
KDE Frameworks Version: 5.98.0
Qt Version: 5.15.3
Kernel Version: 5.15.0-52-generic (64-bit)
Graphics Platform: X11
Processors: 16 × 11th Gen Intel® Core™ i7-11800H @ 2.30GHz

NVIDIA-SMI 470.141.03   Driver Version: 470.141.03   CUDA Version: 11.4

OpenGL vendor string: NVIDIA Corporation
OpenGL renderer string: NVIDIA GeForce RTX 3050 Ti Laptop GPU/PCIe/SSE2
OpenGL core profile version string: 4.6.0 NVIDIA 470.141.03
OpenGL core profile shading language version string: 4.60 NVIDIA
OpenGL ES profile version string: OpenGL ES 3.2 NVIDIA 470.141.03
OpenGL ES profile shading language version string: OpenGL ES GLSL ES 3.20

UPDATE 3:

adam@pc:~/git/bgfx_test/build$ ldd BgfxTest | grep GL
        libEGL.so.1 => /lib/x86_64-linux-gnu/libEGL.so.1 (0x00007f32b95dd000)
        libGLX.so.0 => /lib/x86_64-linux-gnu/libGLX.so.0 (0x00007f32b95a9000)
        libGLdispatch.so.0 => /lib/x86_64-linux-gnu/libGLdispatch.so.0 (0x00007f32b8d9d000)

Upvotes: 2

Views: 1953

Answers (0)

Related Questions