[gtk/ebassi/egl-x11: 2/4] x11: Use EGL for GL support




commit 83987867fd6db9cae63ec1464f56d7d6802c3a8b
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Mon May 10 18:11:05 2021 +0100

    x11: Use EGL for GL support
    
    This makes the X11 backend similar to the Wayland one, when it comes to
    OpenGL.
    
    Fall back to GLX only if EGL support is not available.

 gdk/x11/gdkdisplay-x11.h   |  20 +-
 gdk/x11/gdkglcontext-egl.c | 800 +++++++++++++++++++++++++++++++++++++++++++++
 gdk/x11/gdkglcontext-glx.c |   2 +-
 gdk/x11/gdkglcontext-x11.c |  16 +-
 gdk/x11/gdkglcontext-x11.h |  33 +-
 gdk/x11/gdkscreen-x11.h    |   1 -
 gdk/x11/gdkvisual-x11.c    |   8 +-
 gdk/x11/gdkx11glcontext.h  |   4 +
 gdk/x11/meson.build        |   1 +
 9 files changed, 865 insertions(+), 20 deletions(-)
---
diff --git a/gdk/x11/gdkdisplay-x11.h b/gdk/x11/gdkdisplay-x11.h
index 58eafeca61..5ffae57b74 100644
--- a/gdk/x11/gdkdisplay-x11.h
+++ b/gdk/x11/gdkdisplay-x11.h
@@ -126,11 +126,20 @@ struct _GdkX11Display
 
   int wm_moveresize_button;
 
+#ifdef HAVE_XDAMAGE
+  int damage_event_base;
+  int damage_error_base;
+  guint have_damage;
+#endif
+
   /* GLX information */
   int glx_version;
   int glx_error_base;
   int glx_event_base;
 
+  /* EGL information */
+  int egl_version;
+
   /* Translation between X server time and system-local monotonic time */
   gint64 server_time_query_time;
   gint64 server_time_offset;
@@ -138,6 +147,7 @@ struct _GdkX11Display
   guint server_time_is_monotonic_time : 1;
 
   guint have_glx : 1;
+  guint have_egl : 1;
 
   /* GLX extensions we check */
   guint has_glx_swap_interval : 1;
@@ -151,11 +161,11 @@ struct _GdkX11Display
   guint has_glx_create_es2_context : 1;
   guint has_async_glx_swap_buffers : 1;
 
-#ifdef HAVE_XDAMAGE
-  int damage_event_base;
-  int damage_error_base;
-  guint have_damage;
-#endif
+  /* EGL extensions we check */
+  guint has_egl_khr_create_context : 1;
+  guint has_egl_buffer_age : 1;
+  guint has_egl_swap_buffers_with_damage : 1;
+  guint has_egl_surfaceless_context : 1;
 };
 
 struct _GdkX11DisplayClass
diff --git a/gdk/x11/gdkglcontext-egl.c b/gdk/x11/gdkglcontext-egl.c
new file mode 100644
index 0000000000..a03a5b75f0
--- /dev/null
+++ b/gdk/x11/gdkglcontext-egl.c
@@ -0,0 +1,800 @@
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontext-x11.c: X11 specific OpenGL wrappers
+ *
+ * Copyright © 2014  Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gdkglcontext-x11.h"
+#include "gdkdisplay-x11.h"
+#include "gdkprivate-x11.h"
+#include "gdkscreen-x11.h"
+
+#include "gdkx11display.h"
+#include "gdkx11glcontext.h"
+#include "gdkx11screen.h"
+#include "gdkx11surface.h"
+#include "gdkvisual-x11.h"
+#include "gdkx11property.h"
+#include <X11/Xatom.h>
+
+#include "gdkinternals.h"
+
+#include "gdkintl.h"
+
+#include <cairo-xlib.h>
+
+#include <epoxy/egl.h>
+
+struct _GdkX11GLContextEGL
+{
+  GdkX11GLContext parent_instance;
+
+  EGLDisplay egl_display;
+  EGLConfig egl_config;
+  EGLContext egl_context;
+};
+
+typedef struct {
+  EGLDisplay egl_display;
+  EGLConfig egl_config;
+  EGLSurface egl_surface;
+
+  /* Only set by the dummy surface we attach to the display */
+  Display *xdisplay;
+  Window dummy_xwin;
+  XVisualInfo *xvisinfo;
+} DrawableInfo;
+
+typedef struct _GdkX11GLContextClass    GdkX11GLContextEGLClass;
+
+G_DEFINE_TYPE (GdkX11GLContextEGL, gdk_x11_gl_context_egl, GDK_TYPE_X11_GL_CONTEXT)
+
+static void
+drawable_info_free (gpointer data)
+{
+  DrawableInfo *info = data;
+
+  if (data == NULL)
+    return;
+
+  if (info->egl_surface != NULL)
+    {
+      eglDestroySurface (info->egl_display, info->egl_surface);
+      info->egl_surface = NULL;
+    }
+
+  if (info->dummy_xwin != None)
+    {
+      XDestroyWindow (info->xdisplay, info->dummy_xwin);
+      info->dummy_xwin = None;
+    }
+
+  if (info->xvisinfo != NULL)
+    {
+      XFree (info->xvisinfo);
+      info->xvisinfo = NULL;
+    }
+
+  g_free (info);
+}
+
+static EGLDisplay
+gdk_x11_display_get_egl_display (GdkDisplay *display)
+{
+  EGLDisplay edpy = NULL;
+  Display *dpy;
+
+  edpy = g_object_get_data (G_OBJECT (display), "-gdk-x11-egl-display");
+  if (edpy != NULL)
+    return edpy;
+
+  dpy = gdk_x11_display_get_xdisplay (display);
+
+  if (epoxy_has_egl_extension (NULL, "EGL_KHR_platform_base"))
+    {
+      PFNEGLGETPLATFORMDISPLAYPROC getPlatformDisplay =
+        (void *) eglGetProcAddress ("eglGetPlatformDisplay");
+
+      if (getPlatformDisplay != NULL)
+        edpy = getPlatformDisplay (EGL_PLATFORM_X11_KHR, dpy, NULL);
+
+      if (edpy != NULL)
+        goto out;
+    }
+
+  if (epoxy_has_egl_extension (NULL, "EGL_EXT_platform_base"))
+    {
+      PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplay =
+        (void *) eglGetProcAddress ("eglGetPlatformDisplayEXT");
+
+      if (getPlatformDisplay)
+        edpy = getPlatformDisplay (EGL_PLATFORM_X11_EXT, dpy, NULL);
+
+      if (edpy != NULL)
+        goto out;
+    }
+
+  edpy = eglGetDisplay ((EGLNativeDisplayType) dpy);
+
+out:
+  if (edpy != NULL)
+    g_object_set_data (G_OBJECT (display), "-gdk-x11-egl-display", edpy);
+
+  return edpy;
+}
+
+static XVisualInfo *
+get_visual_info_for_egl_config (GdkDisplay *display,
+                                EGLConfig   egl_config)
+{
+  XVisualInfo visinfo_template;
+  int template_mask = 0;
+  XVisualInfo *visinfo = NULL;
+  int visinfos_count;
+  EGLint visualid, red_size, green_size, blue_size, alpha_size;
+  EGLDisplay egl_display = gdk_x11_display_get_egl_display (display);
+
+  eglGetConfigAttrib (egl_display, egl_config, EGL_NATIVE_VISUAL_ID, &visualid);
+
+  if (visualid != 0)
+    {
+      visinfo_template.visualid = visualid;
+      template_mask |= VisualIDMask;
+    }
+  else
+    {
+      /* some EGL drivers don't implement the EGL_NATIVE_VISUAL_ID
+       * attribute, so attempt to find the closest match.
+       */
+      eglGetConfigAttrib (egl_display, egl_config, EGL_RED_SIZE, &red_size);
+      eglGetConfigAttrib (egl_display, egl_config, EGL_GREEN_SIZE, &green_size);
+      eglGetConfigAttrib (egl_display, egl_config, EGL_BLUE_SIZE, &blue_size);
+      eglGetConfigAttrib (egl_display, egl_config, EGL_ALPHA_SIZE, &alpha_size);
+
+      visinfo_template.depth = red_size + green_size + blue_size + alpha_size;
+      template_mask |= VisualDepthMask;
+
+      visinfo_template.screen = DefaultScreen (gdk_x11_display_get_xdisplay (display));
+      template_mask |= VisualScreenMask;
+    }
+
+  visinfo = XGetVisualInfo (gdk_x11_display_get_xdisplay (display),
+                            template_mask,
+                            &visinfo_template,
+                            &visinfos_count);
+
+  if (visinfos_count < 1)
+    return NULL;
+
+  return visinfo;
+}
+
+static EGLSurface
+gdk_x11_display_get_egl_dummy_surface (GdkDisplay *display,
+                                       EGLConfig   egl_config)
+{
+  DrawableInfo *info;
+  XVisualInfo *xvisinfo;
+  XSetWindowAttributes attrs;
+
+  info = g_object_get_data (G_OBJECT (display), "-gdk-x11-egl-dummy-surface");
+  if (info != NULL)
+    return info->egl_surface;
+
+  xvisinfo = get_visual_info_for_egl_config (display, egl_config);
+  if (xvisinfo == NULL)
+    return NULL;
+
+  info = g_new (DrawableInfo, 1);
+  info->xdisplay = gdk_x11_display_get_xdisplay (display);
+  info->xvisinfo = xvisinfo;
+  info->egl_display = gdk_x11_display_get_egl_display (display);
+  info->egl_config = egl_config;
+
+  attrs.override_redirect = True;
+  attrs.colormap = XCreateColormap (info->xdisplay,
+                                    DefaultRootWindow (info->xdisplay),
+                                    xvisinfo->visual,
+                                    AllocNone);
+  attrs.border_pixel = 0;
+
+  info->dummy_xwin =
+    XCreateWindow (info->xdisplay,
+                   DefaultRootWindow (info->xdisplay),
+                   -100, -100, 1, 1,
+                   0,
+                   xvisinfo->depth,
+                   CopyFromParent,
+                   xvisinfo->visual,
+                   CWOverrideRedirect | CWColormap | CWBorderPixel,
+                   &attrs);
+
+  info->egl_surface =
+    eglCreateWindowSurface (info->egl_display,
+                            info->egl_config,
+                            (EGLNativeWindowType) info->dummy_xwin,
+                            NULL);
+
+  g_object_set_data_full (G_OBJECT (display), "-gdk-x11-egl-dummy-surface",
+                          info,
+                          drawable_info_free);
+
+  return info->egl_surface;
+}
+
+static EGLSurface
+gdk_x11_surface_get_egl_surface (GdkSurface *surface,
+                                 EGLConfig   config)
+{
+  GdkDisplay *display = gdk_surface_get_display (surface);
+  EGLDisplay egl_display = gdk_x11_display_get_egl_display (display);
+  DrawableInfo *info;
+
+  info = g_object_get_data (G_OBJECT (surface), "-gdk-x11-egl-drawable");
+  if (info != NULL)
+    return info->egl_surface;
+
+  info = g_new0 (DrawableInfo, 1);
+  info->egl_display = egl_display;
+  info->egl_config = config;
+  info->egl_surface =
+    eglCreateWindowSurface (info->egl_display, config,
+                            (EGLNativeWindowType) gdk_x11_surface_get_xid (surface),
+                            NULL);
+
+  g_object_set_data_full (G_OBJECT (surface), "-gdk-x11-egl-drawable",
+                          info,
+                          drawable_info_free);
+
+  return info->egl_surface;
+}
+
+static void
+gdk_x11_gl_context_egl_end_frame (GdkDrawContext *draw_context,
+                                  cairo_region_t *painted)
+{
+  GdkGLContext *context = GDK_GL_CONTEXT (draw_context);
+  GdkX11GLContextEGL *context_egl = GDK_X11_GL_CONTEXT_EGL (context);
+  GdkSurface *surface = gdk_gl_context_get_surface (context);
+  GdkDisplay *display = gdk_surface_get_display (surface);
+  GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+  EGLDisplay egl_display = gdk_x11_display_get_egl_display (display);
+  EGLSurface egl_surface;
+
+  GDK_DRAW_CONTEXT_CLASS (gdk_x11_gl_context_egl_parent_class)->end_frame (draw_context, painted);
+  if (gdk_gl_context_get_shared_context (context) != NULL)
+    return;
+
+  gdk_gl_context_make_current (context);
+
+  egl_surface = gdk_x11_surface_get_egl_surface (surface, context_egl->egl_config);
+
+  if (display_x11->has_egl_swap_buffers_with_damage)
+    {
+      int i, j, n_rects = cairo_region_num_rectangles (painted);
+      int surface_height = gdk_surface_get_height (surface);
+      int scale = gdk_surface_get_scale_factor (surface);
+      EGLint stack_rects[4 * 4]; /* 4 rects */
+      EGLint *heap_rects = NULL;
+      EGLint *rects;
+      
+      if (n_rects < G_N_ELEMENTS (stack_rects) / 4)
+        rects = (EGLint *) &stack_rects; 
+      else
+        rects = heap_rects = g_new (EGLint, n_rects * 4);
+
+      for (i = 0, j = 0; i < n_rects; i++)
+        {
+          cairo_rectangle_int_t rect;
+
+          cairo_region_get_rectangle (painted, i, &rect);
+
+          rects[j++] = rect.x * scale;
+          rects[j++] = (surface_height - rect.height - rect.y) * scale;
+          rects[j++] = rect.width * scale;
+          rects[j++] = rect.height * scale;
+        }
+
+      eglSwapBuffersWithDamageEXT (egl_display, egl_surface, rects, n_rects);
+      g_free (heap_rects);
+    }
+  else
+    eglSwapBuffers (egl_display, egl_surface);
+}
+
+static cairo_region_t *
+gdk_x11_gl_context_egl_get_damage (GdkGLContext *context)
+{
+  GdkDisplay *display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
+  GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+
+  if (display_x11->has_egl_buffer_age)
+    {
+      GdkSurface *surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
+      GdkGLContext *shared = gdk_gl_context_get_shared_context (context);
+      GdkX11GLContextEGL *shared_egl;
+      EGLSurface egl_surface;
+      int buffer_age = 0;
+
+      shared = gdk_gl_context_get_shared_context (context);
+      if (shared == NULL)
+        shared = context;
+      shared_egl = GDK_X11_GL_CONTEXT_EGL (shared);
+
+      egl_surface = gdk_x11_surface_get_egl_surface (surface, shared_egl->egl_config);
+      gdk_gl_context_make_current (shared);
+
+      eglQuerySurface (gdk_x11_display_get_egl_display (display),
+                       egl_surface,
+                       EGL_BUFFER_AGE_EXT,
+                       &buffer_age);
+
+      switch (buffer_age)
+        {
+        case 1:
+          return cairo_region_create ();
+
+        case 2:
+          if (context->old_updated_area[0])
+            return cairo_region_copy (context->old_updated_area[0]);
+          break;
+
+        case 3:
+          if (context->old_updated_area[0] && context->old_updated_area[1])
+            {
+              cairo_region_t *damage = cairo_region_copy (context->old_updated_area[0]);
+              cairo_region_union (damage, context->old_updated_area[1]);
+              return damage;
+            }
+          break;
+
+        default:
+          break;
+        }
+    }
+
+  return GDK_GL_CONTEXT_CLASS (gdk_x11_gl_context_egl_parent_class)->get_damage (context);
+}
+
+#define N_EGL_ATTRS 16
+
+static gboolean
+gdk_x11_gl_context_egl_realize (GdkGLContext  *context,
+                                GError       **error)
+{
+  GdkX11Display *display_x11;
+  GdkDisplay *display;
+  GdkX11GLContextEGL *context_egl;
+  GdkGLContext *share, *shared_data_context;
+  GdkSurface *surface;
+  gboolean debug_bit, forward_bit, legacy_bit, use_es;
+  int major, minor, i = 0;
+  EGLint context_attrs[N_EGL_ATTRS];
+  EGLDisplay egl_display;
+
+  surface = gdk_gl_context_get_surface (context);
+  display = gdk_surface_get_display (surface);
+
+  context_egl = GDK_X11_GL_CONTEXT_EGL (context);
+  display_x11 = GDK_X11_DISPLAY (display);
+  share = gdk_gl_context_get_shared_context (context);
+  shared_data_context = gdk_surface_get_shared_data_gl_context (surface);
+
+  gdk_gl_context_get_required_version (context, &major, &minor);
+  debug_bit = gdk_gl_context_get_debug_enabled (context);
+  forward_bit = gdk_gl_context_get_forward_compatible (context);
+  legacy_bit = GDK_DISPLAY_DEBUG_CHECK (display, GL_LEGACY) ||
+               (share != NULL && gdk_gl_context_is_legacy (share));
+  use_es = GDK_DISPLAY_DEBUG_CHECK (display, GL_GLES) ||
+           (share != NULL && gdk_gl_context_get_use_es (share));
+
+  if (!use_es)
+    {
+      eglBindAPI (EGL_OPENGL_API);
+
+      if (display_x11->has_egl_khr_create_context)
+        {
+          int flags = 0;
+
+          if (debug_bit)
+            flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
+          if (forward_bit)
+            flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;
+
+          context_attrs[i++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
+          context_attrs[i++] = legacy_bit
+                             ? EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR
+                             : EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
+          context_attrs[i++] = EGL_CONTEXT_MAJOR_VERSION_KHR;
+          context_attrs[i++] = legacy_bit ? 3 : major;
+          context_attrs[i++] = EGL_CONTEXT_MINOR_VERSION_KHR;
+          context_attrs[i++] = legacy_bit ? 0 : minor;
+          context_attrs[i++] = EGL_CONTEXT_FLAGS_KHR;
+          context_attrs[i++] = flags;
+          context_attrs[i++] = EGL_NONE;
+        }
+      else
+        {
+          context_attrs[i++] = EGL_NONE;
+        }
+    }
+  else
+    {
+      eglBindAPI (EGL_OPENGL_ES_API);
+
+      context_attrs[i++] = EGL_CONTEXT_CLIENT_VERSION;
+      if (major == 3)
+        context_attrs[i++] = 3;
+      else
+        context_attrs[i++] = 2;
+    }
+
+  context_attrs[i++] = EGL_NONE;
+  g_assert (i < N_EGL_ATTRS);
+
+  GDK_DISPLAY_NOTE (display, OPENGL,
+                    g_message ("Creating EGL context version %d.%d (shared:%s, debug:%s, forward:%s, 
legacy:%s, es:%s)",
+                               major, minor,
+                               share != NULL ? "yes" : "no",
+                               debug_bit ? "yes" : "no",
+                               forward_bit ? "yes" : "no",
+                               legacy_bit ? "yes" : "no",
+                               use_es ? "yes" : "no"));
+
+  egl_display = gdk_x11_display_get_egl_display (display);
+
+  context_egl->egl_context =
+    eglCreateContext (egl_display,
+                      context_egl->egl_config,
+                      share != NULL
+                        ? GDK_X11_GL_CONTEXT_EGL (share)->egl_context
+                        : shared_data_context != NULL
+                            ? GDK_X11_GL_CONTEXT_EGL (shared_data_context)->egl_context
+                            : EGL_NO_CONTEXT,
+                      context_attrs);
+
+  /* If we're not asking for a GLES context, and we don't have the legacy bit set
+   * already, try again with a legacy context
+   */
+  if (context_egl->egl_context == NULL && !use_es && !legacy_bit)
+    {
+      context_attrs[1] = EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR;
+      context_attrs[3] = 3;
+      context_attrs[5] = 0;
+
+      legacy_bit = TRUE;
+      use_es = FALSE;
+
+      GDK_NOTE (OPENGL,
+                g_message ("Context creation failed; trying legacy EGL context"));
+
+      context_egl->egl_context =
+        eglCreateContext (egl_display,
+                          context_egl->egl_config,
+                          share != NULL
+                            ? GDK_X11_GL_CONTEXT_EGL (share)->egl_context
+                            : shared_data_context != NULL
+                                ? GDK_X11_GL_CONTEXT_EGL (shared_data_context)->egl_context
+                                : EGL_NO_CONTEXT,
+                          context_attrs);
+    }
+
+  if (context_egl->egl_context == NULL)
+    {
+      g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
+                           _("Unable to create a GL context"));
+      return FALSE;
+    }
+
+  gdk_gl_context_set_is_legacy (context, legacy_bit);
+  gdk_gl_context_set_use_es (context, use_es);
+
+  GDK_NOTE (OPENGL,
+            g_message ("Realized EGL context[%p]",
+                       context_egl->egl_context));
+
+  return TRUE;
+}
+
+#undef N_EGL_ATTRS
+
+static void
+gdk_x11_gl_context_egl_dispose (GObject *gobject)
+{
+  GdkX11GLContextEGL *context_egl = GDK_X11_GL_CONTEXT_EGL (gobject);
+
+  if (context_egl->egl_context != NULL)
+    {
+      GdkGLContext *context = GDK_GL_CONTEXT (gobject);
+      GdkDisplay *display = gdk_gl_context_get_display (context);
+      EGLDisplay egl_display = gdk_x11_display_get_egl_display (display);
+
+      /* Unset the current context if we're disposing it */
+      if (eglGetCurrentContext () == context_egl->egl_context)
+        eglMakeCurrent (egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+      GDK_NOTE (OPENGL, g_message ("Destroying EGL context"));
+      eglDestroyContext (egl_display, context_egl->egl_context);
+      context_egl->egl_context = NULL;
+    }
+
+  G_OBJECT_CLASS (gdk_x11_gl_context_egl_parent_class)->dispose (gobject);
+}
+
+static void
+gdk_x11_gl_context_egl_class_init (GdkX11GLContextEGLClass *klass)
+{
+  GdkGLContextClass *context_class = GDK_GL_CONTEXT_CLASS (klass);
+  GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  context_class->realize = gdk_x11_gl_context_egl_realize;
+  context_class->get_damage = gdk_x11_gl_context_egl_get_damage;
+
+  draw_context_class->end_frame = gdk_x11_gl_context_egl_end_frame;
+
+  gobject_class->dispose = gdk_x11_gl_context_egl_dispose;
+}
+
+static void
+gdk_x11_gl_context_egl_init (GdkX11GLContextEGL *self)
+{
+}
+
+gboolean
+gdk_x11_screen_init_egl (GdkX11Screen *screen)
+{
+  GdkDisplay *display = GDK_SCREEN_DISPLAY (screen);
+  GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+  EGLDisplay edpy;
+  Display *dpy;
+  int major, minor;
+
+  if (display_x11->have_egl)
+    return TRUE;
+
+  dpy = gdk_x11_display_get_xdisplay (display);
+
+  if (!epoxy_has_egl ())
+    return FALSE;
+
+  edpy = gdk_x11_display_get_egl_display (display);
+  if (edpy == NULL)
+    return FALSE;
+
+  if (!eglInitialize (edpy, &major, &minor))
+    return FALSE;
+
+  display_x11->have_egl = TRUE;
+  display_x11->egl_version = epoxy_egl_version (dpy);
+
+  display_x11->has_egl_khr_create_context =
+    epoxy_has_egl_extension (edpy, "EGL_KHR_create_context");
+  display_x11->has_egl_buffer_age =
+    epoxy_has_egl_extension (edpy, "EGL_EXT_buffer_age");
+  display_x11->has_egl_swap_buffers_with_damage =
+    epoxy_has_egl_extension (edpy, "EGL_EXT_swap_buffers_with_damage");
+  display_x11->has_egl_surfaceless_context =
+    epoxy_has_egl_extension (edpy, "EGL_KHR_surfaceless_context");
+
+  GDK_DISPLAY_NOTE (display, OPENGL,
+                    g_message ("EGL found\n"
+                               " - Version: %s\n"
+                               " - Vendor: %s\n"
+                               " - Client API: %s\n"
+                               " - Checked extensions:\n"
+                               "\t* EGL_KHR_create_context: %s\n"
+                               "\t* EGL_EXT_buffer_age: %s\n"
+                               "\t* EGL_EXT_swap_buffers_with_damage: %s\n"
+                               "\t* EGL_KHR_surfaceless_context: %s\n",
+                               eglQueryString (edpy, EGL_VERSION),
+                               eglQueryString (edpy, EGL_VENDOR),
+                               eglQueryString (edpy, EGL_CLIENT_APIS),
+                               display_x11->has_egl_khr_create_context ? "yes" : "no",
+                               display_x11->has_egl_buffer_age ? "yes" : "no",
+                               display_x11->has_egl_swap_buffers_with_damage ? "yes" : "no",
+                               display_x11->has_egl_surfaceless_context ? "yes" : "no"));
+
+  return TRUE;
+}
+
+#define MAX_EGL_ATTRS   30
+
+static gboolean
+find_eglconfig_for_display (GdkDisplay  *display,
+                            EGLConfig   *egl_config_out,
+                            GError     **error)
+{
+  EGLint attrs[MAX_EGL_ATTRS];
+  EGLint count;
+  EGLConfig egl_config;
+  EGLDisplay egl_display;
+  int i = 0;
+
+  attrs[i++] = EGL_SURFACE_TYPE;
+  attrs[i++] = EGL_WINDOW_BIT;
+
+  attrs[i++] = EGL_COLOR_BUFFER_TYPE;
+  attrs[i++] = EGL_RGB_BUFFER;
+
+  attrs[i++] = EGL_RED_SIZE;
+  attrs[i++] = 8;
+  attrs[i++] = EGL_GREEN_SIZE;
+  attrs[i++] = 8;
+  attrs[i++] = EGL_BLUE_SIZE;
+  attrs[i++] = 8;
+  attrs[i++] = EGL_ALPHA_SIZE;
+  attrs[i++] = 8;
+
+  attrs[i++] = EGL_NONE;
+  g_assert (i < MAX_EGL_ATTRS);
+
+  /* Pick first valid configuration that the driver returns us */
+  egl_display = gdk_x11_display_get_egl_display (display);
+  if (!eglChooseConfig (egl_display, attrs, &egl_config, 1, &count) || count < 1)
+    {
+      g_set_error_literal (error, GDK_GL_ERROR,
+                           GDK_GL_ERROR_UNSUPPORTED_FORMAT,
+                           _("No available configurations for the given pixel format"));
+      return FALSE;
+    }
+
+  g_assert (egl_config_out);
+  *egl_config_out = egl_config;
+
+  return TRUE;
+}
+
+#undef MAX_EGL_ATTRS
+
+GdkX11GLContext *
+gdk_x11_gl_context_egl_new (GdkSurface    *surface,
+                            gboolean       attached,
+                            GdkGLContext  *share,
+                            GError       **error)
+{
+  GdkDisplay *display;
+  GdkX11GLContextEGL *context;
+  EGLConfig egl_config;
+
+  display = gdk_surface_get_display (surface);
+
+  if (!find_eglconfig_for_display (display, &egl_config, error))
+    return NULL;
+
+  context = g_object_new (GDK_TYPE_X11_GL_CONTEXT_EGL,
+                          "surface", surface,
+                          "shared-context", share,
+                          NULL);
+
+  context->egl_config = egl_config;
+
+  return GDK_X11_GL_CONTEXT (context);
+}
+
+gboolean
+gdk_x11_gl_context_egl_make_current (GdkDisplay   *display,
+                                     GdkGLContext *context)
+{
+  GdkX11GLContextEGL *context_egl = GDK_X11_GL_CONTEXT_EGL (context);
+  GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context);
+  GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+  GdkSurface *surface;
+  EGLDisplay egl_display;
+  EGLSurface egl_surface;
+
+  egl_display = gdk_x11_display_get_egl_display (display);
+
+  if (context == NULL)
+    {
+      eglMakeCurrent (egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+      return TRUE;
+    }
+
+  if (context_egl->egl_context == NULL)
+    {
+      g_critical ("No EGL context associated to the GdkGLContext; you must "
+                  "call gdk_gl_context_realize() first.");
+      return FALSE;
+    }
+
+  surface = gdk_gl_context_get_surface (context);
+
+  if (context_x11->is_attached || gdk_draw_context_is_in_frame (GDK_DRAW_CONTEXT (context)))
+    egl_surface = gdk_x11_surface_get_egl_surface (surface, context_egl->egl_config);
+  else
+    {
+      if (display_x11->has_egl_surfaceless_context)
+        egl_surface = EGL_NO_SURFACE;
+      else
+        egl_surface = gdk_x11_display_get_egl_dummy_surface (display, context_egl->egl_config);
+    }
+
+  GDK_DISPLAY_NOTE (display, OPENGL,
+                    g_message ("Making EGL context %p current to surface %p",
+                               context_egl->egl_context, egl_surface));
+
+  if (!eglMakeCurrent (egl_display, egl_surface, egl_surface, context_egl->egl_context))
+    {
+      GDK_DISPLAY_NOTE (display, OPENGL,
+                        g_message ("Making EGL context current failed"));
+      return FALSE;
+    }
+
+  if (context_x11->is_attached)
+    {
+      gboolean do_frame_sync = FALSE;
+
+      /* If the WM is compositing there is no particular need to delay
+       * the swap when drawing on the offscreen, rendering to the screen
+       * happens later anyway, and its up to the compositor to sync that
+       * to the vblank. */
+      do_frame_sync = ! gdk_display_is_composited (display);
+
+      if (do_frame_sync != context_x11->do_frame_sync)
+        {
+          context_x11->do_frame_sync = do_frame_sync;
+
+          if (do_frame_sync)
+            eglSwapInterval (egl_display, 1);
+          else
+            eglSwapInterval (egl_display, 0);
+        }
+    }
+
+  return TRUE;
+}
+
+/**
+ * gdk_x11_display_get_egl_version:
+ * @display: (type GdkX11Display): a #GdkDisplay
+ * @major: (out): return location for the EGL major version
+ * @minor: (out): return location for the EGL minor version
+ *
+ * Retrieves the version of the EGL implementation.
+ *
+ * Returns: %TRUE if EGL is available
+ *
+ * Since: 4.4
+ */
+gboolean
+gdk_x11_display_get_egl_version (GdkDisplay *display,
+                                 int        *major,
+                                 int        *minor)
+{
+  g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+
+  if (!GDK_IS_X11_DISPLAY (display))
+    return FALSE;
+
+  GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+
+  if (display_x11->have_glx)
+    return FALSE;
+
+  if (!gdk_x11_screen_init_egl (display_x11->screen))
+    return FALSE;
+
+  if (major != NULL)
+    *major = display_x11->egl_version / 10;
+  if (minor != NULL)
+    *minor = display_x11->egl_version % 10;
+
+  return TRUE;
+}
diff --git a/gdk/x11/gdkglcontext-glx.c b/gdk/x11/gdkglcontext-glx.c
index 5b56cf7b76..a623066a66 100644
--- a/gdk/x11/gdkglcontext-glx.c
+++ b/gdk/x11/gdkglcontext-glx.c
@@ -866,7 +866,7 @@ save_cached_gl_visuals (GdkDisplay *display, int system, int rgba)
 }
 
 void
-_gdk_x11_screen_update_visuals_for_gl (GdkX11Screen *x11_screen)
+gdk_x11_screen_update_visuals_for_glx (GdkX11Screen *x11_screen)
 {
   GdkDisplay *display;
   GdkX11Display *display_x11;
diff --git a/gdk/x11/gdkglcontext-x11.c b/gdk/x11/gdkglcontext-x11.c
index 7e73a66956..a6807395e4 100644
--- a/gdk/x11/gdkglcontext-x11.c
+++ b/gdk/x11/gdkglcontext-x11.c
@@ -238,6 +238,10 @@ gdk_x11_screen_init_gl (GdkX11Screen *screen)
   if (GDK_DISPLAY_DEBUG_CHECK (display, GL_DISABLE))
     return FALSE;
 
+  /* We favour EGL */
+  if (gdk_x11_screen_init_egl (screen))
+    return TRUE;
+
   if (gdk_x11_screen_init_glx (screen))
     return TRUE;
 
@@ -250,8 +254,8 @@ gdk_x11_surface_create_gl_context (GdkSurface    *surface,
                                    GdkGLContext  *share,
                                    GError       **error)
 {
+  GdkX11GLContext *context = NULL;
   GdkX11Display *display_x11;
-  GdkX11GLContext *context;
   GdkDisplay *display;
 
   display = gdk_surface_get_display (surface);
@@ -265,7 +269,9 @@ gdk_x11_surface_create_gl_context (GdkSurface    *surface,
     }
 
   display_x11 = GDK_X11_DISPLAY (display);
-  if (display_x11->have_glx)
+  if (display_x11->have_egl)
+    context = gdk_x11_gl_context_egl_new (surface, attached, share, error);
+  else if (display_x11->have_glx)
     context = gdk_x11_gl_context_glx_new (surface, attached, share, error);
   else
     g_assert_not_reached ();
@@ -284,8 +290,12 @@ gdk_x11_display_make_gl_context_current (GdkDisplay   *display,
 {
   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
 
-  if (display_x11->have_glx)
+  if (display_x11->have_egl)
+    return gdk_x11_gl_context_egl_make_current (display, context);
+  else if (display_x11->have_glx)
     return gdk_x11_gl_context_glx_make_current (display, context);
+  else
+    g_assert_not_reached ();
 
   return FALSE;
 }
diff --git a/gdk/x11/gdkglcontext-x11.h b/gdk/x11/gdkglcontext-x11.h
index 6b1c0a5cec..d515ecf5e8 100644
--- a/gdk/x11/gdkglcontext-x11.h
+++ b/gdk/x11/gdkglcontext-x11.h
@@ -65,14 +65,7 @@ struct _GdkX11GLContextClass
   void (* bind_for_frame_fence) (GdkX11GLContext *self);
 };
 
-#define GDK_TYPE_X11_GL_CONTEXT_GLX     (gdk_x11_gl_context_glx_get_type())
-#define GDK_X11_GL_CONTEXT_GLX(obj)     (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_X11_GL_CONTEXT_GLX, 
GdkX11GLContextGLX))
-#define GDK_IS_X11_GL_CONTEXT_GLX(obj)  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_X11_GL_CONTEXT_GLX))
-
-typedef struct _GdkX11GLContextGLX      GdkX11GLContextGLX;
-
 gboolean        gdk_x11_screen_init_gl                  (GdkX11Screen  *screen);
-gboolean        gdk_x11_screen_init_glx                 (GdkX11Screen  *screen);
 
 GdkGLContext *  gdk_x11_surface_create_gl_context       (GdkSurface    *window,
                                                          gboolean       attached,
@@ -81,6 +74,16 @@ GdkGLContext *  gdk_x11_surface_create_gl_context       (GdkSurface    *window,
 gboolean        gdk_x11_display_make_gl_context_current (GdkDisplay    *display,
                                                          GdkGLContext  *context);
 
+/* GLX */
+#define GDK_TYPE_X11_GL_CONTEXT_GLX     (gdk_x11_gl_context_glx_get_type())
+#define GDK_X11_GL_CONTEXT_GLX(obj)     (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_X11_GL_CONTEXT_GLX, 
GdkX11GLContextGLX))
+#define GDK_IS_X11_GL_CONTEXT_GLX(obj)  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_X11_GL_CONTEXT_GLX))
+
+typedef struct _GdkX11GLContextGLX      GdkX11GLContextGLX;
+
+gboolean                gdk_x11_screen_init_glx                 (GdkX11Screen  *screen);
+void                    gdk_x11_screen_update_visuals_for_glx   (GdkX11Screen  *screen);
+
 GType                   gdk_x11_gl_context_glx_get_type         (void) G_GNUC_CONST;
 GdkX11GLContext *       gdk_x11_gl_context_glx_new              (GdkSurface    *surface,
                                                                  gboolean       attached,
@@ -90,6 +93,22 @@ gboolean                gdk_x11_gl_context_glx_make_current     (GdkDisplay    *
                                                                  GdkGLContext  *context);
 
 
+/* EGL */
+#define GDK_TYPE_X11_GL_CONTEXT_EGL     (gdk_x11_gl_context_egl_get_type())
+#define GDK_X11_GL_CONTEXT_EGL(obj)     (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_X11_GL_CONTEXT_EGL, 
GdkX11GLContextEGL))
+#define GDK_IS_X11_GL_CONTEXT_EGL(obj)  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_X11_GL_CONTEXT_EGL))
+
+typedef struct _GdkX11GLContextEGL      GdkX11GLContextEGL;
+
+gboolean                gdk_x11_screen_init_egl                 (GdkX11Screen  *screen);
+GType                   gdk_x11_gl_context_egl_get_type         (void) G_GNUC_CONST;
+GdkX11GLContext *       gdk_x11_gl_context_egl_new              (GdkSurface    *surface,
+                                                                 gboolean       attached,
+                                                                 GdkGLContext  *share,
+                                                                 GError       **error);
+gboolean                gdk_x11_gl_context_egl_make_current     (GdkDisplay    *display,
+                                                                 GdkGLContext  *context);
+
 G_END_DECLS
 
 #endif /* __GDK_X11_GL_CONTEXT__ */
diff --git a/gdk/x11/gdkscreen-x11.h b/gdk/x11/gdkscreen-x11.h
index 83f1933db7..cba79af75d 100644
--- a/gdk/x11/gdkscreen-x11.h
+++ b/gdk/x11/gdkscreen-x11.h
@@ -97,7 +97,6 @@ GdkX11Screen *_gdk_x11_screen_new           (GdkDisplay   *display,
                                              int           screen_number,
                                              gboolean      setup_display);
 
-void _gdk_x11_screen_update_visuals_for_gl  (GdkX11Screen *screen);
 void _gdk_x11_screen_window_manager_changed (GdkX11Screen *screen);
 void _gdk_x11_screen_size_changed           (GdkX11Screen *screen,
                                              const XEvent *event);
diff --git a/gdk/x11/gdkvisual-x11.c b/gdk/x11/gdkvisual-x11.c
index 8e2f296e8b..36d95bff2e 100644
--- a/gdk/x11/gdkvisual-x11.c
+++ b/gdk/x11/gdkvisual-x11.c
@@ -27,6 +27,7 @@
 #include "gdkprivate-x11.h"
 #include "gdkscreen-x11.h"
 #include "gdkvisual-x11.h"
+#include "gdkglcontext-x11.h"
 
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
@@ -249,9 +250,10 @@ _gdk_x11_screen_init_visuals (GdkX11Screen *x11_screen,
   x11_screen->nvisuals = nvisuals;
 
   /* If GL is available we want to pick better default/rgba visuals,
-     as we care about glx details such as alpha/depth/stencil depth,
-     stereo and double buffering */
-  _gdk_x11_screen_update_visuals_for_gl (x11_screen);
+   * as we care about GLX details such as alpha/depth/stencil depth,
+   * stereo and double buffering
+   */
+  gdk_x11_screen_update_visuals_for_glx (x11_screen);
 
   if (setup_display)
     {
diff --git a/gdk/x11/gdkx11glcontext.h b/gdk/x11/gdkx11glcontext.h
index 4a6e049f7e..34cbd70d75 100644
--- a/gdk/x11/gdkx11glcontext.h
+++ b/gdk/x11/gdkx11glcontext.h
@@ -43,6 +43,10 @@ GDK_AVAILABLE_IN_ALL
 gboolean        gdk_x11_display_get_glx_version (GdkDisplay *display,
                                                  int        *major,
                                                  int        *minor);
+GDK_AVAILABLE_IN_4_4
+gboolean        gdk_x11_display_get_egl_version (GdkDisplay *display,
+                                                 int        *major,
+                                                 int        *minor);
 
 G_END_DECLS
 
diff --git a/gdk/x11/meson.build b/gdk/x11/meson.build
index dde50f2169..082fc9803a 100644
--- a/gdk/x11/meson.build
+++ b/gdk/x11/meson.build
@@ -8,6 +8,7 @@ gdk_x11_public_sources = files([
   'gdkdevicemanager-x11.c',
   'gdkdevicemanager-xi2.c',
   'gdkdisplay-x11.c',
+  'gdkglcontext-egl.c',
   'gdkglcontext-glx.c',
   'gdkglcontext-x11.c',
   'gdkkeys-x11.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]