thefunkyjunky thefunkyjunky - 27 days ago 10
C++ Question

How to create an OpenGL 3.3 or 4.x context through EGL

I am interested in making an OpenGL app which doesn't depend on X11. As I have seen, this should be possible through EGL. There are even examples on the internet. But how can I control the context version? The sample code below creates an OpenGL context(on wayland) with version 2.1, but on my computer it shows that the highest OpenGL version supported is 3.3(such context can be created with glXCreateContextAttribsARB using GLX and xlib in the X server). So my question is: Can I somehow create an OpenGL context with higher version through EGL and if yes, how?

Example code:

#include <wayland-client.h>
#include <wayland-egl.h>
#include <EGL/egl.h>
#include <GL/gl.h>
#include <string.h>
#include <stdio.h>

#define WIDTH 256
#define HEIGHT 256

static struct wl_display *display;
static struct wl_compositor *compositor = NULL;
static struct wl_shell *shell = NULL;
static EGLDisplay egl_display;
static char running = 1;

struct window {
EGLContext egl_context;
struct wl_surface *surface;
struct wl_shell_surface *shell_surface;
struct wl_egl_window *egl_window;
EGLSurface egl_surface;
};

// listeners
static void registry_add_object (void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) {
if (!strcmp(interface,"wl_compositor")) {
compositor = wl_registry_bind (registry, name, &wl_compositor_interface, 0);
}
else if (!strcmp(interface,"wl_shell")) {
shell = wl_registry_bind (registry, name, &wl_shell_interface, 0);
}
}
static void registry_remove_object (void *data, struct wl_registry *registry, uint32_t name) {

}
static struct wl_registry_listener registry_listener = {&registry_add_object, &registry_remove_object};

static void shell_surface_ping (void *data, struct wl_shell_surface *shell_surface, uint32_t serial) {
wl_shell_surface_pong (shell_surface, serial);
}
static void shell_surface_configure (void *data, struct wl_shell_surface *shell_surface, uint32_t edges, int32_t width, int32_t height) {
struct window *window = data;
wl_egl_window_resize (window->egl_window, width, height, 0, 0);
}
static void shell_surface_popup_done (void *data, struct wl_shell_surface *shell_surface) {

}
static struct wl_shell_surface_listener shell_surface_listener = {&shell_surface_ping, &shell_surface_configure, &shell_surface_popup_done};

static void create_window (struct window *window, int32_t width, int32_t height) {
eglBindAPI (EGL_OPENGL_API);
EGLint attributes[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_NONE};
EGLConfig config;
EGLint num_config;
eglChooseConfig (egl_display, attributes, &config, 1, &num_config);
window->egl_context = eglCreateContext (egl_display, config, EGL_NO_CONTEXT, NULL);

window->surface = wl_compositor_create_surface (compositor);
window->shell_surface = wl_shell_get_shell_surface (shell, window->surface);
wl_shell_surface_add_listener (window->shell_surface, &shell_surface_listener, window);
wl_shell_surface_set_toplevel (window->shell_surface);
window->egl_window = wl_egl_window_create (window->surface, width, height);
window->egl_surface = eglCreateWindowSurface (egl_display, config, window->egl_window, NULL);
eglMakeCurrent (egl_display, window->egl_surface, window->egl_surface, window->egl_context);
}
static void delete_window (struct window *window) {
eglDestroySurface (egl_display, window->egl_surface);
wl_egl_window_destroy (window->egl_window);
wl_shell_surface_destroy (window->shell_surface);
wl_surface_destroy (window->surface);
eglDestroyContext (egl_display, window->egl_context);
}
static void draw_window (struct window *window) {
glClearColor (0.0, 1.0, 0.0, 1.0);
glClear (GL_COLOR_BUFFER_BIT);

printf("%s\n", glGetString(GL_VERSION));

eglSwapBuffers (egl_display, window->egl_surface);
}

int main () {
display = wl_display_connect (NULL);
struct wl_registry *registry = wl_display_get_registry (display);
wl_registry_add_listener (registry, &registry_listener, NULL);
wl_display_dispatch (display);

egl_display = eglGetDisplay (display);
eglInitialize (egl_display, NULL, NULL);

struct window window;
create_window (&window, WIDTH, HEIGHT);

while (running) {
wl_display_dispatch_pending (display);
draw_window (&window);
}

delete_window (&window);
eglTerminate (egl_display);
wl_display_disconnect (display);
return 0;
}


Source: https://github.com/eyelash/tutorials

Answer

EGL was initially developed for OpenGL ES only. The support for desktop OpenGL was added with EGL 1.4 (released 2008). However, at that time, the distinction between "legacy" GL contexts, forward compatible contexts and OpenGL profiles like core and compatibility did not yet exist, so EGL 1.4 does not include means to request any of these. The only thing you can rely on is that you get a GL context which is compatible to the "legacy" GL 1.x/2.x. This does not imply that it cannot also create a compatibility profile of a higher version, though - you just can't rely on that, and have no way to control it.

The ability to request a GL context compatible to a specific version, or implementing a certain profile, was first provided by the EGL_KHR_create_context EGL extension in 2012, which is functionally quite similiar to the WGL/glX counterparts for the classical desktop GL binding APIs. This functionality also has been added to core EGL version 1.5.

This means you need an implementation supporting either EGL 1.5 or 1.4 with that extension to be able to reliably create modern OpenGL contexts in a controlled way by specifying the

  • EGL_CONTEXT_MAJOR_VERSION, EGL_CONTEXT_MINOR_VERSION
  • EGL_CONTEXT_FLAGS
  • EGL_CONTEXT_OPENGL_PROFILE_MASK

attributes (or their extension counterparts with the _KHR suffix, which end up having the same values - the latter are just defined in elgext.h, while the core versions are in egl.h for version 1.5) at context creation.