[gtk+/wip/gdk-gl2: 5/13] gdk: Add support for OpenGL



commit 1cc57bd8afc9ff2da05c7bec60be956e2e0d8f38
Author: Alexander Larsson <alexl redhat com>
Date:   Thu Oct 9 10:45:44 2014 +0200

    gdk: Add support for OpenGL
    
    This adds the new type GdkGLContext that wraps an OpenGL context for a
    particular native window. It also adds support for the gdk paint
    machinery to use OpenGL to draw everything. As soon as anyone creates
    a GL context for a native window we create a "paint context" for that
    GdkWindow and switch to using GL for painting it.
    
    This commit contains only an implementation for X11 (using GLX).
    
    The way painting works is that all client gl contexts draw into
    offscreen buffers rather than directly to the back buffer, and the
    way something gets onto the window is by using gdk_cairo_draw_from_gl()
    to draw part of that buffer onto the draw cairo context.
    
    As a fallback (if we're doing redirected drawing or some effect like a
    cairo_push_group()) we read back the gl buffer into memory and composite
    using cairo. This means that GL rendering works in all cases, including
    rendering to a PDF. However, this is not particularly fast.
    
    In the *typical* case, where we're drawing directly to the window in
    the regular paint loop we hit the fast path. The fast path uses opengl
    to draw the buffer to the window back buffer, either by blitting or
    texturing. Then we track the region that was drawn, and when the draw
    ends we paint the normal cairo surface to the window (using
    texture-from-pixmap in the X11 case, or texture from cairo image
    otherwise) in the regions where there is no gl painted.
    
    There are some complexities wrt layering of gl and cairo areas though:
    * We track via gdk_window_mark_paint_from_clip() whenever gtk is
      painting over a region we previously rendered with opengl
      (flushed_region). This area (needs_blend_region) is blended
      rather than copied at the end of the frame.
    * If we're drawing a gl texture with alpha we first copy the current
      cairo_surface inside the target region to the back buffer before
      we blend over it.
    
    These two operations allow us full stacking of transparent gl and cairo
    regions.

 docs/reference/gdk/gdk-docs.sgml     |    1 +
 docs/reference/gdk/gdk3-sections.txt |   31 ++
 docs/reference/gdk/gdk3.types        |    1 +
 gdk/Makefile.am                      |    4 +
 gdk/gdk.c                            |    3 +-
 gdk/gdk.h                            |    1 +
 gdk/gdkcairo.h                       |   11 +
 gdk/gdkdisplay.c                     |   65 +++
 gdk/gdkdisplayprivate.h              |   11 +
 gdk/gdkgl.c                          |  459 ++++++++++++++++++
 gdk/gdkglcontext.c                   |  340 +++++++++++++
 gdk/gdkglcontext.h                   |   59 +++
 gdk/gdkglcontextprivate.h            |   58 +++
 gdk/gdkinternals.h                   |   20 +-
 gdk/gdktypes.h                       |   22 +-
 gdk/gdkwindow.c                      |  244 ++++++++--
 gdk/gdkwindow.h                      |    6 +
 gdk/gdkwindowimpl.h                  |    6 +
 gdk/x11/Makefile.am                  |    3 +
 gdk/x11/gdkdisplay-x11.c             |    4 +
 gdk/x11/gdkdisplay-x11.h             |   15 +
 gdk/x11/gdkglcontext-x11.c           |  886 ++++++++++++++++++++++++++++++++++
 gdk/x11/gdkglcontext-x11.h           |   71 +++
 gdk/x11/gdkwindow-x11.c              |    3 +
 gdk/x11/gdkx.h                       |    1 +
 gdk/x11/gdkx11glcontext.h            |   49 ++
 26 files changed, 2342 insertions(+), 32 deletions(-)
---
diff --git a/docs/reference/gdk/gdk-docs.sgml b/docs/reference/gdk/gdk-docs.sgml
index 6db8174..55abb80 100644
--- a/docs/reference/gdk/gdk-docs.sgml
+++ b/docs/reference/gdk/gdk-docs.sgml
@@ -32,6 +32,7 @@
     <xi:include href="xml/windows.xml" />
     <xi:include href="xml/gdkframeclock.xml" />
     <xi:include href="xml/gdkframetimings.xml" />
+    <xi:include href="xml/gdkglcontext.xml" />
     <xi:include href="xml/events.xml" />
     <xi:include href="xml/event_structs.xml" />
     <xi:include href="xml/keys.xml" />
diff --git a/docs/reference/gdk/gdk3-sections.txt b/docs/reference/gdk/gdk3-sections.txt
index fd281cf..36ff42f 100644
--- a/docs/reference/gdk/gdk3-sections.txt
+++ b/docs/reference/gdk/gdk3-sections.txt
@@ -394,6 +394,7 @@ gdk_window_constrain_size
 gdk_window_beep
 gdk_window_get_scale_factor
 gdk_window_set_opaque_region
+gdk_window_create_gl_context
 gdk_window_mark_paint_from_clip
 
 <SUBSECTION>
@@ -618,6 +619,7 @@ gdk_cairo_rectangle
 gdk_cairo_region
 gdk_cairo_region_create_from_surface
 gdk_cairo_surface_create_from_pixbuf
+gdk_cairo_draw_from_gl
 </SECTION>
 
 <SECTION>
@@ -1005,6 +1007,7 @@ gdk_x11_display_error_trap_pop
 gdk_x11_display_error_trap_pop_ignored
 gdk_x11_display_set_cursor_theme
 gdk_x11_display_set_window_scale
+gdk_x11_display_get_glx_version
 gdk_x11_register_standard_event_type
 gdk_x11_screen_get_screen_number
 gdk_x11_screen_get_xscreen
@@ -1124,6 +1127,11 @@ GDK_X11_KEYMAP_CLASS
 GDK_IS_X11_KEYMAP
 GDK_IS_X11_KEYMAP_CLASS
 GDK_X11_KEYMAP_GET_CLASS
+GDK_TYPE_X11_GL_CONTEXT
+GDK_X11_GL_CONTEXT
+GDK_X11_GL_CONTEXT_CLASS
+GDK_IS_X11_GL_CONTEXT
+GDK_IS_X11_GL_CONTEXT_CLASS
 GDK_TYPE_X11_SCREEN
 GDK_X11_SCREEN
 GDK_X11_SCREEN_CLASS
@@ -1274,3 +1282,26 @@ gdk_frame_timings_get_predicted_presentation_time
 <SUBSECTION Private>
 gdk_frame_get_type
 </SECTION>
+
+<SECTION>
+<FILE>gdkglcontext</FILE>
+GdkGLContext
+gdk_gl_context_get_visual
+gdk_gl_context_get_window
+gdk_gl_context_make_current
+gdk_gl_context_clear_current
+gdk_gl_context_get_current
+GDK_GL_ERROR
+GDK_TYPE_GL_ERROR
+GDK_TYPE_GL_PROFILE
+
+<SUBSECTION Standard>
+GDK_GL_CONTEXT
+GDK_IS_GL_CONTEXT
+GDK_TYPE_GL_CONTEXT
+GDK_GL_CONTEXT_CLASS
+GDK_GL_CONTEXT_GET_CLASS
+GDK_IS_GL_CONTEXT_CLASS
+gdk_gl_context_get_type
+gdk_gl_error_quark
+</SECTION>
diff --git a/docs/reference/gdk/gdk3.types b/docs/reference/gdk/gdk3.types
index 0f9447b..caa8e17 100644
--- a/docs/reference/gdk/gdk3.types
+++ b/docs/reference/gdk/gdk3.types
@@ -8,6 +8,7 @@ gdk_display_get_type
 gdk_display_manager_get_type
 gdk_drag_context_get_type
 gdk_frame_clock_get_type
+gdk_gl_context_get_type
 gdk_keymap_get_type
 gdk_screen_get_type
 gdk_visual_get_type
diff --git a/gdk/Makefile.am b/gdk/Makefile.am
index 5f0c1e5..3ff5ff0 100644
--- a/gdk/Makefile.am
+++ b/gdk/Makefile.am
@@ -71,6 +71,7 @@ gdk_public_h_sources =                                \
        gdkdnd.h                                \
        gdkevents.h                             \
        gdkframetimings.h                       \
+       gdkglcontext.h                          \
        gdkkeys.h                               \
        gdkkeysyms.h                            \
        gdkkeysyms-compat.h                     \
@@ -107,6 +108,7 @@ gdk_private_headers =                               \
        gdkdndprivate.h                         \
        gdkframeclockidle.h                     \
        gdkframeclockprivate.h                  \
+       gdkglcontextprivate.h                   \
        gdkscreenprivate.h                      \
        gdkinternals.h                          \
        gdkintl.h                               \
@@ -131,6 +133,8 @@ gdk_c_sources =                             \
        gdkdnd.c                                \
        gdkevents.c                             \
        gdkframetimings.c                       \
+       gdkgl.c                                 \
+       gdkglcontext.c                          \
        gdkglobals.c                            \
        gdkkeys.c                               \
        gdkkeyuni.c                             \
diff --git a/gdk/gdk.c b/gdk/gdk.c
index 81f5427..0b2c388 100644
--- a/gdk/gdk.c
+++ b/gdk/gdk.c
@@ -148,7 +148,8 @@ static const GDebugKey gdk_debug_keys[] = {
   {"draw",          GDK_DEBUG_DRAW},
   {"eventloop",     GDK_DEBUG_EVENTLOOP},
   {"frames",        GDK_DEBUG_FRAMES},
-  {"settings",      GDK_DEBUG_SETTINGS}
+  {"settings",      GDK_DEBUG_SETTINGS},
+  {"opengl",        GDK_DEBUG_OPENGL},
 };
 
 static gboolean
diff --git a/gdk/gdk.h b/gdk/gdk.h
index 5761fa1..6b442ce 100644
--- a/gdk/gdk.h
+++ b/gdk/gdk.h
@@ -41,6 +41,7 @@
 #include <gdk/gdkevents.h>
 #include <gdk/gdkframeclock.h>
 #include <gdk/gdkframetimings.h>
+#include <gdk/gdkglcontext.h>
 #include <gdk/gdkkeys.h>
 #include <gdk/gdkkeysyms.h>
 #include <gdk/gdkmain.h>
diff --git a/gdk/gdkcairo.h b/gdk/gdkcairo.h
index bc9ee97..8d62e4f 100644
--- a/gdk/gdkcairo.h
+++ b/gdk/gdkcairo.h
@@ -70,6 +70,17 @@ GDK_AVAILABLE_IN_3_10
 cairo_surface_t * gdk_cairo_surface_create_from_pixbuf      (const GdkPixbuf *pixbuf,
                                                              int scale,
                                                              GdkWindow *for_window);
+GDK_AVAILABLE_IN_3_16
+void       gdk_cairo_draw_from_gl (cairo_t              *cr,
+                                   GdkWindow            *window,
+                                   int                   source,
+                                   int                   source_type,
+                                   int                   buffer_scale,
+                                   int                   x,
+                                   int                   y,
+                                   int                   width,
+                                   int                   height);
+
 
 G_END_DECLS
 
diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c
index 34b6cab..c7babfc 100644
--- a/gdk/gdkdisplay.c
+++ b/gdk/gdkdisplay.c
@@ -2225,3 +2225,68 @@ gdk_error_trap_pop (void)
 {
   return gdk_error_trap_pop_internal (TRUE);
 }
+
+/*< private >
+ * gdk_display_destroy_gl_context:
+ * @display: a #GdkDisplay
+ * @context: a #GdkGLContext
+ *
+ * Destroys the platform-specific parts of the @context.
+ *
+ * The @context instance is still valid, though inert, after
+ * this functionr returns.
+ */
+void
+gdk_display_destroy_gl_context (GdkDisplay   *display,
+                                GdkGLContext *context)
+{
+  GdkGLContext *current = gdk_display_get_current_gl_context (display);
+
+  if  (current == context)
+    g_object_set_data (G_OBJECT (display), "-gdk-gl-current-context", NULL);
+
+  GDK_DISPLAY_GET_CLASS (display)->destroy_gl_context (display, context);
+}
+
+/*< private >
+ * gdk_display_make_gl_context_current:
+ * @display: a #GdkDisplay
+ * @context: (optional): a #GdkGLContext, or %NULL
+ *
+ * Makes the given @context the current GL context, or unsets
+ * the current GL context if @context is %NULL.
+ *
+ * Returns: %TRUE if successful
+ */
+gboolean
+gdk_display_make_gl_context_current (GdkDisplay   *display,
+                                     GdkGLContext *context)
+{
+  GdkGLContext *current = gdk_display_get_current_gl_context (display);
+
+  if (current == context)
+    return TRUE;
+
+  if (context == NULL)
+    g_object_set_data (G_OBJECT (display), "-gdk-gl-current-context", NULL);
+  else
+    g_object_set_data_full (G_OBJECT (display), "-gdk-gl-current-context",
+                            g_object_ref (context),
+                            (GDestroyNotify) g_object_unref);
+
+  return GDK_DISPLAY_GET_CLASS (display)->make_gl_context_current (display, context);
+}
+
+/*< private >
+ * gdk_display_get_current_gl_context:
+ * @display: a #GdkDisplay
+ *
+ * Retrieves the current #GdkGLContext associated with @display.
+ *
+ * Returns: (transfer none): the current #GdkGLContext or %NULL
+ */
+GdkGLContext *
+gdk_display_get_current_gl_context (GdkDisplay *display)
+{
+  return g_object_get_data (G_OBJECT (display), "-gdk-gl-current-context");
+}
diff --git a/gdk/gdkdisplayprivate.h b/gdk/gdkdisplayprivate.h
index 7911d25..65f134e 100644
--- a/gdk/gdkdisplayprivate.h
+++ b/gdk/gdkdisplayprivate.h
@@ -225,6 +225,11 @@ struct _GdkDisplayClass
   gchar *                (*utf8_to_string_target)      (GdkDisplay     *display,
                                                         const gchar    *text);
 
+  gboolean              (*make_gl_context_current)  (GdkDisplay        *display,
+                                                     GdkGLContext      *context);
+  void                  (*destroy_gl_context)       (GdkDisplay        *display,
+                                                     GdkGLContext      *context);
+
   /* Signals */
   void                   (*opened)                     (GdkDisplay     *display);
   void (*closed) (GdkDisplay *display,
@@ -303,6 +308,12 @@ void                _gdk_display_create_window_impl   (GdkDisplay       *display
                                                        gint              attributes_mask);
 GdkWindow *         _gdk_display_create_window        (GdkDisplay       *display);
 
+void                gdk_display_destroy_gl_context       (GdkDisplay        *display,
+                                                          GdkGLContext      *context);
+gboolean            gdk_display_make_gl_context_current  (GdkDisplay        *display,
+                                                          GdkGLContext      *context);
+GdkGLContext *      gdk_display_get_current_gl_context   (GdkDisplay        *display);
+
 G_END_DECLS
 
 #endif  /* __GDK_DISPLAY_PRIVATE_H__ */
diff --git a/gdk/gdkgl.c b/gdk/gdkgl.c
new file mode 100644
index 0000000..d9a93d9
--- /dev/null
+++ b/gdk/gdkgl.c
@@ -0,0 +1,459 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2005 Red Hat, Inc. 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gdkcairo.h"
+#include "gdkglcontextprivate.h"
+
+#include "gdkinternals.h"
+
+#include <epoxy/gl.h>
+#include <math.h>
+
+static cairo_user_data_key_t direct_key;
+
+void
+gdk_cairo_surface_mark_as_direct (cairo_surface_t *surface,
+                                  GdkWindow *window)
+{
+  cairo_surface_set_user_data (surface, &direct_key,
+                               g_object_ref (window),  g_object_unref);
+}
+
+/* x,y,width,height describes a rectangle in the gl render buffer
+   coordinate space, and its top left corner is drawn at the current
+   position according to the cairo translation. */
+/**
+ * gdk_cairo_draw_from_gl
+ * @cr: a cairo context
+ * @window: The window we're rendering for (not necessarily into)
+ * @source: The GL id of the source buffer
+ * @source_type: The type of the @source
+ * @buffer_scale: The scale-factor that the @source buffer is allocated for
+ * @x: The source x position in @source to start copying from in GL coordinates
+ * @y: The source y position in @source to start copying from in GL coordinates
+ * @width: The width of the region to draw
+ * @height: The height of the region to draw
+ *
+ * This is the main way to draw GL content in Gtk+. It takes a render buffer id
+ * (@source_type == #GL_RENDERBUFFER) or a texture id (@source_type == #GL_TEXTURE)
+ * and draws it onto @cr with an OVER operation, respecting the current clip.
+ *
+ * This will work for *all* cairo_t, as long as @window is realized, but the
+ * fallback implementation that reads back the pixels from the buffer may be
+ * used in the general case. In the case of direct drawing to a window with
+ * no special effects applied to @cr it will however use a more efficient
+ * approach.
+ *
+ * For #GL_RENDERBUFFER the code will always fall back to software for buffers
+ * with alpha components, so make sure you use #GL_TEXTURE if using alpha.
+ */
+void
+gdk_cairo_draw_from_gl (cairo_t              *cr,
+                        GdkWindow            *window,
+                        int                   source,
+                        int                   source_type,
+                        int                   buffer_scale,
+                        int                   x,
+                        int                   y,
+                        int                   width,
+                        int                   height)
+{
+  GdkGLContext *context;
+  cairo_surface_t *image;
+  cairo_matrix_t matrix;
+  int dx, dy, window_scale;
+  gboolean trivial_transform;
+  cairo_surface_t *group_target;
+  GdkWindow *direct_window, *impl_window;
+  GLuint framebuffer;
+  GLint alpha_size = 0;
+  cairo_region_t *clip_region;
+
+  impl_window = window->impl_window;
+
+  window_scale = gdk_window_get_scale_factor (impl_window);
+
+  context = gdk_window_get_paint_gl_context (window, NULL);
+  if (context == NULL)
+    {
+      g_warning ("gdk_cairo_draw_gl_render_buffer failed - no paint context");
+      return;
+    }
+
+  clip_region = gdk_cairo_region_from_clip (cr);
+
+  if (!gdk_gl_context_make_current (context))
+    g_error ("make current failed");
+
+  glGenFramebuffersEXT (1, &framebuffer);
+  glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, framebuffer);
+
+  if (source_type == GL_RENDERBUFFER)
+    {
+      glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_ALPHA_SIZE,  &alpha_size);
+
+      glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, source);
+      glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+                                    GL_RENDERBUFFER_EXT, source);
+      glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
+    }
+  else if (source_type == GL_TEXTURE)
+    {
+      glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_ALPHA_SIZE,  &alpha_size);
+
+      glBindTexture (GL_TEXTURE_2D, source);
+      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+      glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+                                 GL_TEXTURE_2D, source, 0);
+      glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
+    }
+  else
+    {
+      g_warning ("Unsupported gl source type %d\n", source_type);
+      return;
+    }
+
+  group_target = cairo_get_group_target (cr);
+  direct_window = cairo_surface_get_user_data (group_target, &direct_key);
+
+  cairo_get_matrix (cr, &matrix);
+
+  dx = matrix.x0;
+  dy = matrix.y0;
+
+  /* Trivial == integer-only translation */
+  trivial_transform =
+    (double)dx == matrix.x0 && (double)dy == matrix.y0 &&
+    matrix.xx == 1.0 && matrix.xy == 0.0 &&
+    matrix.yx == 0.0 && matrix.yy == 1.0;
+
+  /* For direct paint of non-alpha renderbuffer, we can
+     just do a bitblit */
+  if (source_type == GL_RENDERBUFFER &&
+      alpha_size == 0 &&
+      direct_window != NULL &&
+      direct_window->current_paint.use_gl &&
+      trivial_transform &&
+      clip_region != NULL)
+    {
+      int window_height;
+      int i;
+
+      /* Translate to impl coords */
+      cairo_region_translate (clip_region, dx, dy);
+
+      glEnable (GL_SCISSOR_TEST);
+
+      window_height = gdk_window_get_height (impl_window);
+      glDrawBuffer (GL_BACK);
+
+#define FLIP_Y(_y) (window_height*window_scale - (_y))
+
+      for (i = 0; i < cairo_region_num_rectangles (clip_region); i++)
+        {
+          cairo_rectangle_int_t clip_rect, dest;
+
+          cairo_region_get_rectangle (clip_region, i, &clip_rect);
+          clip_rect.x *= window_scale;
+          clip_rect.y *= window_scale;
+          clip_rect.width *= window_scale;
+          clip_rect.height *= window_scale;
+
+          glScissor (clip_rect.x, FLIP_Y (clip_rect.y + clip_rect.height),
+                     clip_rect.width, clip_rect.height);
+
+          dest.x = dx * window_scale;
+          dest.y = dy * window_scale;
+          dest.width = width * window_scale / buffer_scale;
+          dest.height = height * window_scale / buffer_scale;
+
+          if (gdk_rectangle_intersect (&clip_rect, &dest, &dest))
+            {
+              int clipped_src_x = x + (dest.x - dx * window_scale);
+              int clipped_src_y = y + (height - dest.height - (dest.y - dy * window_scale));
+              glBlitFramebufferEXT(clipped_src_x, clipped_src_y,
+                                   (clipped_src_x + dest.width), (clipped_src_y + dest.height),
+                                   dest.x, FLIP_Y(dest.y + dest.height),
+                                   dest.x + dest.width, FLIP_Y(dest.y),
+                                   GL_COLOR_BUFFER_BIT, GL_NEAREST);
+              if (impl_window->current_paint.flushed_region)
+                {
+                  cairo_rectangle_int_t flushed_rect;
+
+                  flushed_rect.x = dest.x / window_scale;
+                  flushed_rect.y = dest.y / window_scale;
+                  flushed_rect.width = (dest.x + dest.width + window_scale - 1) / window_scale - 
flushed_rect.x;
+                  flushed_rect.height = (dest.y + dest.height + window_scale - 1) / window_scale - 
flushed_rect.y;
+
+                  cairo_region_union_rectangle (impl_window->current_paint.flushed_region,
+                                                &flushed_rect);
+                  cairo_region_subtract_rectangle (impl_window->current_paint.need_blend_region,
+                                                   &flushed_rect);
+                }
+            }
+        }
+
+      glDisable (GL_SCISSOR_TEST);
+
+#undef FLIP_Y
+
+    }
+  /* For direct paint of alpha or non-alpha textures we can use texturing */
+  else if (source_type == GL_TEXTURE &&
+           direct_window != NULL &&
+           direct_window->current_paint.use_gl &&
+           trivial_transform &&
+           clip_region != NULL)
+    {
+      int window_height;
+      GLint texture_width;
+      GLint texture_height;
+      int i;
+
+      /* Translate to impl coords */
+      cairo_region_translate (clip_region, dx, dy);
+
+      glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &texture_width);
+      glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT,  &texture_height);
+
+      if (alpha_size != 0)
+        {
+          cairo_region_t *opaque_region, *blend_region;
+
+          opaque_region = cairo_region_copy (clip_region);
+          cairo_region_subtract (opaque_region, impl_window->current_paint.flushed_region);
+          cairo_region_subtract (opaque_region, impl_window->current_paint.need_blend_region);
+
+          if (!cairo_region_is_empty (opaque_region))
+            gdk_gl_texture_from_surface (impl_window->current_paint.surface,
+                                         opaque_region);
+
+          blend_region = cairo_region_copy (clip_region);
+          cairo_region_intersect (blend_region, impl_window->current_paint.need_blend_region);
+
+          glEnable (GL_BLEND);
+          if (!cairo_region_is_empty (blend_region))
+            gdk_gl_texture_from_surface (impl_window->current_paint.surface,
+                                         blend_region);
+
+          cairo_region_destroy (opaque_region);
+          cairo_region_destroy (blend_region);
+        }
+      glEnable (GL_SCISSOR_TEST);
+      glEnable (GL_TEXTURE_2D);
+
+      window_height = gdk_window_get_height (impl_window);
+
+#define FLIP_Y(_y) (window_height*window_scale - (_y))
+
+      for (i = 0; i < cairo_region_num_rectangles (clip_region); i++)
+        {
+          cairo_rectangle_int_t clip_rect, dest;
+
+          cairo_region_get_rectangle (clip_region, i, &clip_rect);
+
+          clip_rect.x *= window_scale;
+          clip_rect.y *= window_scale;
+          clip_rect.width *= window_scale;
+          clip_rect.height *= window_scale;
+
+          glScissor (clip_rect.x, FLIP_Y (clip_rect.y + clip_rect.height),
+                     clip_rect.width, clip_rect.height);
+
+          dest.x = dx * window_scale;
+          dest.y = dy * window_scale;
+          dest.width = width * window_scale / buffer_scale;
+          dest.height = height * window_scale / buffer_scale;
+
+          if (gdk_rectangle_intersect (&clip_rect, &dest, &dest))
+            {
+              int clipped_src_x = x + (dest.x - dx * window_scale);
+              int clipped_src_y = y + (height - dest.height - (dest.y - dy * window_scale));
+
+              glBegin (GL_QUADS);
+              glTexCoord2f (clipped_src_x / (float)texture_width, clipped_src_y / (float)texture_height);
+              glVertex2f (dest.x, FLIP_Y(dest.y + dest.height));
+
+              glTexCoord2f ((clipped_src_x + dest.width) / (float)texture_width, clipped_src_y / 
(float)texture_height);
+              glVertex2f (dest.x + dest.width, FLIP_Y(dest.y + dest.height));
+
+              glTexCoord2f ((clipped_src_x + dest.width) / (float)texture_width, (clipped_src_y + 
dest.height) / (float)texture_height);
+              glVertex2f (dest.x + dest.width, FLIP_Y(dest.y));
+
+              glTexCoord2f (clipped_src_x / (float)texture_width, (clipped_src_y + dest.height) / 
(float)texture_height);
+              glVertex2f (dest.x, FLIP_Y(dest.y));
+              glEnd();
+
+              if (impl_window->current_paint.flushed_region)
+                {
+                  cairo_rectangle_int_t flushed_rect;
+
+                  flushed_rect.x = dest.x / window_scale;
+                  flushed_rect.y = dest.y / window_scale;
+                  flushed_rect.width = (dest.x + dest.width + window_scale - 1) / window_scale - 
flushed_rect.x;
+                  flushed_rect.height = (dest.y + dest.height + window_scale - 1) / window_scale - 
flushed_rect.y;
+
+                  cairo_region_union_rectangle (impl_window->current_paint.flushed_region,
+                                                &flushed_rect);
+                  cairo_region_subtract_rectangle (impl_window->current_paint.need_blend_region,
+                                                   &flushed_rect);
+                }
+            }
+        }
+
+      if (alpha_size != 0)
+        glDisable (GL_BLEND);
+
+      glDisable (GL_TEXTURE_2D);
+      glDisable (GL_SCISSOR_TEST);
+
+#undef FLIP_Y
+
+    }
+  else
+    {
+      /* Software fallback */
+
+      /* TODO: avoid reading back non-required data due to dest clip */
+      image = cairo_surface_create_similar_image (cairo_get_target (cr),
+                                                  (alpha_size == 0) ? CAIRO_FORMAT_RGB24 : 
CAIRO_FORMAT_ARGB32,
+                                                  width, height);
+
+#ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE
+      cairo_surface_set_device_scale (image, buffer_scale, buffer_scale);
+#endif
+
+      glPixelStorei (GL_PACK_ALIGNMENT, 4);
+      glPixelStorei (GL_PACK_ROW_LENGTH, cairo_image_surface_get_stride (image) / 4);
+
+      glReadPixels (x, y, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
+                    cairo_image_surface_get_data (image));
+
+      glPixelStorei (GL_PACK_ROW_LENGTH, 0);
+
+      cairo_surface_mark_dirty (image);
+
+      /* Invert due to opengl having different origin */
+      cairo_scale (cr, 1, -1);
+      cairo_translate (cr, 0, -height / buffer_scale);
+
+      cairo_set_source_surface (cr, image, 0, 0);
+      cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+      cairo_paint (cr);
+
+      cairo_surface_destroy (image);
+    }
+
+  glDrawBuffer (GL_BACK);
+  glReadBuffer(GL_BACK);
+
+  glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
+  glDeleteFramebuffersEXT (1, &framebuffer);
+
+  if (clip_region)
+    cairo_region_destroy (clip_region);
+}
+
+void
+gdk_gl_texture_from_surface (cairo_surface_t *surface,
+                            cairo_region_t *region)
+{
+  GdkGLContext *current;
+  cairo_surface_t *image;
+  double device_x_offset, device_y_offset;
+  cairo_rectangle_int_t rect, e;
+  int n_rects, i;
+  GdkWindow *window;
+  int window_height;
+  unsigned int texture_id;
+  int window_scale;
+  double sx, sy;
+
+  current = gdk_gl_context_get_current ();
+  if (current &&
+      GDK_GL_CONTEXT_GET_CLASS (current)->texture_from_surface &&
+      GDK_GL_CONTEXT_GET_CLASS (current)->texture_from_surface (current, surface, region))
+    return;
+
+  /* Software fallback */
+
+  window = gdk_gl_context_get_window (gdk_gl_context_get_current ());
+  window_scale = gdk_window_get_scale_factor (window);
+  window_height = gdk_window_get_height (window);
+
+  sx = sy = 1;
+#ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE
+  cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy);
+#endif
+
+  cairo_surface_get_device_offset (surface,
+                                   &device_x_offset, &device_y_offset);
+
+  glGenTextures (1, &texture_id);
+  glBindTexture (GL_TEXTURE_RECTANGLE_ARB, texture_id);
+  glEnable (GL_TEXTURE_RECTANGLE_ARB);
+
+  n_rects = cairo_region_num_rectangles (region);
+  for (i = 0; i < n_rects; i++)
+    {
+      cairo_region_get_rectangle (region, i, &rect);
+
+      glScissor (rect.x * window_scale, (window_height - rect.y - rect.height) * window_scale,
+                 rect.width * window_scale, rect.height * window_scale);
+
+      e = rect;
+      e.x *= sx;
+      e.y *= sy;
+      e.x += (int)device_x_offset;
+      e.y += (int)device_y_offset;
+      e.width *= sx;
+      e.height *= sy;
+      image = cairo_surface_map_to_image (surface, &e);
+
+      glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
+      glPixelStorei (GL_UNPACK_ROW_LENGTH, cairo_image_surface_get_stride (image)/4);
+      glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 4, e.width, e.height, 0, GL_BGRA, 
GL_UNSIGNED_INT_8_8_8_8_REV,
+                    cairo_image_surface_get_data (image));
+      glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+
+      cairo_surface_unmap_image (surface, image);
+
+#define FLIP_Y(_y) (window_height - (_y))
+
+      glBegin (GL_QUADS);
+      glTexCoord2f (0.0f * sx, rect.height * sy);
+      glVertex2f (rect.x * window_scale, FLIP_Y(rect.y + rect.height) * window_scale);
+
+      glTexCoord2f (rect.width * sx, rect.height * sy);
+      glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y + rect.height) * window_scale);
+
+      glTexCoord2f (rect.width * sx, 0.0f * sy);
+      glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y) * window_scale);
+
+      glTexCoord2f (0.0f * sx, 0.0f * sy);
+      glVertex2f (rect.x * window_scale, FLIP_Y(rect.y) * window_scale);
+      glEnd();
+    }
+
+  glDisable (GL_TEXTURE_RECTANGLE_ARB);
+  glDeleteTextures (1, &texture_id);
+}
diff --git a/gdk/gdkglcontext.c b/gdk/gdkglcontext.c
new file mode 100644
index 0000000..58eaa0a
--- /dev/null
+++ b/gdk/gdkglcontext.c
@@ -0,0 +1,340 @@
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontext.c: GL context abstraction
+ * 
+ * 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/>.
+ */
+
+/**
+ * SECTION:gdkglcontext
+ * @Title: GdkGLContext
+ * @Short_description: OpenGL context
+ *
+ * #GdkGLContext is an object representing the platform-specific
+ * OpenGL drawing context.
+ *
+ * #GdkGLContexts are created for a #GdkWindow using gdk_window_create_gl_context(), and
+ * the context will be tied to the native window backing that window, matching the
+ * GdkVisual of the window.
+ *
+ * A #GdkGLContexts normal framebuffer draws directly on to the back buffer of the native
+ * window backing the #GdkWindow, so its not allowed to draw directly to that, as the
+ * gdk repaint system is in full control of that. Instead you can create render buffers
+ * or textures and use gdk_cairo_draw_from_gl() in the draw function of your widget
+ * to draw them. Then Gdk will handle the integration of your rendering with that of
+ * other widgets.
+ *
+ * Support for #GdkGLContext is platform specific, context creation can fail, returning
+ * a %NULL context.
+ *
+ * A #GdkGLContext has to be made "current" in order to start using
+ * it, otherwise any OpenGL call will be ignored.
+ *
+ * ## Creating a new OpenGL context ##
+ *
+ * In order to create a new #GdkGLContext instance you need a
+ * #GdkWindow, which you typically get during the realize call of a widget.
+ *
+ * ## Using a GdkGLContext ##
+ *
+ * You will need to make the #GdkGLContext the current context
+ * before issuing OpenGL calls; the system sends OpenGL commands to
+ * whichever context is current. It is possible to have multiple
+ * contexts, so you always need to ensure that the one which you
+ * want to draw with is the current one before issuing commands:
+ *
+ * |[<!-- language="C" -->
+ *   gdk_gl_context_make_current (context);
+ * ]|
+ *
+ * You can now perform your drawing using OpenGL commands.
+ *
+ * You can check which #GdkGLContext is the current one by using
+ * gdk_gl_context_get_current(); you can also unset any #GdkGLContext
+ * that is currently set by calling gdk_gl_context_clear_current().
+ */
+
+#include "config.h"
+
+#include "gdkglcontextprivate.h"
+#include "gdkdisplayprivate.h"
+#include "gdkvisual.h"
+#include "gdkinternals.h"
+
+#include "gdkintl.h"
+
+typedef struct {
+  GdkWindow *window;
+  GdkVisual *visual;
+} GdkGLContextPrivate;
+
+enum {
+  PROP_0,
+
+  PROP_WINDOW,
+  PROP_VISUAL,
+
+  LAST_PROP
+};
+
+static GParamSpec *obj_pspecs[LAST_PROP] = { NULL, };
+
+G_DEFINE_QUARK (gdk-gl-error-quark, gdk_gl_error)
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkGLContext, gdk_gl_context, G_TYPE_OBJECT)
+
+static void
+gdk_gl_context_dispose (GObject *gobject)
+{
+  GdkGLContext *context = GDK_GL_CONTEXT (gobject);
+  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
+
+  gdk_display_destroy_gl_context (gdk_window_get_display (priv->window), context);
+
+  g_clear_object (&priv->window);
+  g_clear_object (&priv->visual);
+
+  G_OBJECT_CLASS (gdk_gl_context_parent_class)->dispose (gobject);
+}
+
+static void
+gdk_gl_context_set_property (GObject      *gobject,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private ((GdkGLContext *) gobject);
+
+  switch (prop_id)
+    {
+    case PROP_WINDOW:
+      {
+        GdkWindow *window = g_value_get_object (value);
+
+        if (window)
+          g_object_ref (window);
+
+        if (priv->window)
+          g_object_unref (priv->window);
+
+        priv->window = window;
+      }
+      break;
+
+    case PROP_VISUAL:
+      {
+        GdkVisual *visual = g_value_get_object (value);
+
+        if (visual != NULL)
+          priv->visual = g_object_ref (visual);
+      }
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+gdk_gl_context_get_property (GObject    *gobject,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private ((GdkGLContext *) gobject);
+
+  switch (prop_id)
+    {
+    case PROP_WINDOW:
+      g_value_set_object (value, priv->window);
+      break;
+
+    case PROP_VISUAL:
+      g_value_set_object (value, priv->visual);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+gdk_gl_context_class_init (GdkGLContextClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  /**
+   * GdkGLContext:window:
+   *
+   * The #GdkWindow the gl context is bound to.
+   *
+   * Since: 3.16
+   */
+  obj_pspecs[PROP_WINDOW] =
+    g_param_spec_object ("window",
+                         P_("Window"),
+                         P_("The GDK window bound to the GL context"),
+                         GDK_TYPE_WINDOW,
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  /**
+   * GdkGLContext:visual:
+   *
+   * The #GdkVisual matching the pixel format used by the context.
+   *
+   * Since: 3.16
+   */
+  obj_pspecs[PROP_VISUAL] =
+    g_param_spec_object ("visual",
+                         P_("Visual"),
+                         P_("The GDK visual used by the GL context"),
+                         GDK_TYPE_VISUAL,
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  gobject_class->set_property = gdk_gl_context_set_property;
+  gobject_class->get_property = gdk_gl_context_get_property;
+  gobject_class->dispose = gdk_gl_context_dispose;
+
+  g_object_class_install_properties (gobject_class, LAST_PROP, obj_pspecs);
+}
+
+static void
+gdk_gl_context_init (GdkGLContext *self)
+{
+}
+
+/**
+ * gdk_gl_context_get_visual:
+ * @context: a #GdkGLContext
+ *
+ * Retrieves the #GdkVisual associated with the @context.
+ *
+ * Returns: (transfer none): the #GdkVisual
+ *
+ * Since: 3.16
+ */
+GdkVisual *
+gdk_gl_context_get_visual (GdkGLContext *context)
+{
+  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
+
+  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+
+  return priv->visual;
+}
+
+/*< private >
+ * gdk_gl_context_flush_buffer:
+ * @context: a #GdkGLContext
+ * @painted: The area that has been redrawn this frame
+ * @damage: The area that we know is actually different from the last frame
+ *
+ * Copies the back buffer to the front buffer.
+ *
+ * This function may call `glFlush()` implicitly before returning; it
+ * is not recommended to call `glFlush()` explicitly before calling
+ * this function.
+ *
+ * Since: 3.16
+ */
+void
+gdk_gl_context_flush_buffer (GdkGLContext *context,
+                             cairo_region_t *painted,
+                             cairo_region_t *damage)
+{
+  g_return_if_fail (GDK_IS_GL_CONTEXT (context));
+
+  GDK_GL_CONTEXT_GET_CLASS (context)->flush_buffer (context, painted, damage);
+}
+
+/**
+ * gdk_gl_context_make_current:
+ * @context: a #GdkGLContext
+ *
+ * Makes the @context the current one.
+ *
+ * Returns: %TRUE if the context is current
+ *
+ * Since: 3.16
+ */
+gboolean
+gdk_gl_context_make_current (GdkGLContext *context)
+{
+  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
+
+  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
+
+  return gdk_display_make_gl_context_current (gdk_window_get_display (priv->window), context);
+}
+
+/**
+ * gdk_gl_context_get_window:
+ * @context: a #GdkGLContext
+ *
+ * Retrieves the #GdkWindow used by the @context.
+ *
+ * Returns: (transfer none): a #GdkWindow or %NULL
+ *
+ * Since: 3.16
+ */
+GdkWindow *
+gdk_gl_context_get_window (GdkGLContext *context)
+{
+  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
+
+  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+
+  return priv->window;
+}
+
+/**
+ * gdk_gl_context_clear_current:
+ *
+ * Clears the current #GdkGLContext.
+ *
+ * Any OpenGL call after this function returns will be ignored
+ * until gdk_gl_context_make_current() is called.
+ *
+ * Since: 3.16
+ */
+void
+gdk_gl_context_clear_current (void)
+{
+  GdkDisplay *display = gdk_display_get_default ();
+
+  gdk_display_make_gl_context_current (display, NULL);
+}
+
+/**
+ * gdk_gl_context_get_current:
+ *
+ * Retrieves the current #GdkGLContext.
+ *
+ * Returns: (transfer none): the current #GdkGLContext, or %NULL
+ *
+ * Since: 3.16
+ */
+GdkGLContext *
+gdk_gl_context_get_current (void)
+{
+  GdkDisplay *display = gdk_display_get_default ();
+
+  return gdk_display_get_current_gl_context (display);
+}
diff --git a/gdk/gdkglcontext.h b/gdk/gdkglcontext.h
new file mode 100644
index 0000000..9447723
--- /dev/null
+++ b/gdk/gdkglcontext.h
@@ -0,0 +1,59 @@
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontext.h: GL context abstraction
+ * 
+ * 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/>.
+ */
+
+#ifndef __GDK_GL_CONTEXT_H__
+#define __GDK_GL_CONTEXT_H__
+
+#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#include <gdk/gdkversionmacros.h>
+#include <gdk/gdktypes.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_GL_CONTEXT             (gdk_gl_context_get_type ())
+#define GDK_GL_CONTEXT(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_GL_CONTEXT, 
GdkGLContext))
+#define GDK_IS_GL_CONTEXT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_GL_CONTEXT))
+
+#define GDK_GL_ERROR       (gdk_gl_error_quark ())
+
+GDK_AVAILABLE_IN_3_16
+GQuark gdk_gl_error_quark (void);
+
+GDK_AVAILABLE_IN_3_16
+GType gdk_gl_context_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_16
+GdkVisual *             gdk_gl_context_get_visual       (GdkGLContext *context);
+GDK_AVAILABLE_IN_3_16
+GdkWindow *             gdk_gl_context_get_window       (GdkGLContext *context);
+
+GDK_AVAILABLE_IN_3_16
+gboolean                gdk_gl_context_make_current     (GdkGLContext *context);
+GDK_AVAILABLE_IN_3_16
+GdkGLContext *          gdk_gl_context_get_current      (void);
+GDK_AVAILABLE_IN_3_16
+void                    gdk_gl_context_clear_current    (void);
+
+G_END_DECLS
+
+#endif /* __GDK_GL_CONTEXT_H__ */
diff --git a/gdk/gdkglcontextprivate.h b/gdk/gdkglcontextprivate.h
new file mode 100644
index 0000000..b973eb2
--- /dev/null
+++ b/gdk/gdkglcontextprivate.h
@@ -0,0 +1,58 @@
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontextprivate.h: GL context abstraction
+ * 
+ * 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/>.
+ */
+
+#ifndef __GDK_GL_CONTEXT_PRIVATE_H__
+#define __GDK_GL_CONTEXT_PRIVATE_H__
+
+#include "gdkglcontext.h"
+
+G_BEGIN_DECLS
+
+#define GDK_GL_CONTEXT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_GL_CONTEXT, 
GdkGLContextClass))
+#define GDK_IS_GL_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_GL_CONTEXT))
+#define GDK_GL_CONTEXT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_GL_CONTEXT, 
GdkGLContextClass))
+
+typedef struct _GdkGLContextClass       GdkGLContextClass;
+
+struct _GdkGLContext
+{
+  GObject parent_instance;
+};
+
+struct _GdkGLContextClass
+{
+  GObjectClass parent_class;
+
+  void (* update)          (GdkGLContext *context);
+  void (* flush_buffer)    (GdkGLContext *context,
+                            cairo_region_t *painted,
+                            cairo_region_t *damage);
+  gboolean (* texture_from_surface) (GdkGLContext    *context,
+                                    cairo_surface_t *surface,
+                                    cairo_region_t  *region);
+};
+
+void            gdk_gl_context_flush_buffer       (GdkGLContext *context,
+                                                   cairo_region_t *painted,
+                                                   cairo_region_t *damage);
+
+G_END_DECLS
+
+#endif /* __GDK_GL_CONTEXT_PRIVATE_H__ */
diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h
index d70b6c4..d1eb2b1 100644
--- a/gdk/gdkinternals.h
+++ b/gdk/gdkinternals.h
@@ -84,7 +84,8 @@ typedef enum {
   GDK_DEBUG_DRAW          = 1 <<  9,
   GDK_DEBUG_EVENTLOOP     = 1 << 10,
   GDK_DEBUG_FRAMES        = 1 << 11,
-  GDK_DEBUG_SETTINGS      = 1 << 12
+  GDK_DEBUG_SETTINGS      = 1 << 12,
+  GDK_DEBUG_OPENGL        = 1 << 13
 } GdkDebugFlag;
 
 typedef enum {
@@ -208,8 +209,18 @@ struct _GdkWindow
   struct {
     cairo_region_t *region;
     cairo_surface_t *surface;
+
+    /* Areas of region that have been copied to the back buffer already */
+    cairo_region_t *flushed_region;
+    /* Areas of region that have been copied to the back buffer but
+       needs furter blending of surface data. These two regions are
+       always non-intersecting. */
+    cairo_region_t *need_blend_region;
+
     gboolean surface_needs_composite;
+    gboolean use_gl;
   } current_paint;
+  GdkGLContext *gl_paint_context;
 
   cairo_region_t *update_area;
   guint update_freeze_count;
@@ -327,6 +338,10 @@ void _gdk_set_window_state (GdkWindow *window,
 
 gboolean        _gdk_cairo_surface_extents       (cairo_surface_t *surface,
                                                   GdkRectangle    *extents);
+void            gdk_gl_texture_from_surface      (cairo_surface_t *surface,
+                                                  cairo_region_t  *region);
+void            gdk_cairo_surface_mark_as_direct (cairo_surface_t *surface,
+                                                  GdkWindow       *window);
 cairo_region_t *gdk_cairo_region_from_clip       (cairo_t         *cr);
 
 
@@ -342,6 +357,9 @@ void       _gdk_window_destroy           (GdkWindow      *window,
 void       _gdk_window_clear_update_area (GdkWindow      *window);
 void       _gdk_window_update_size       (GdkWindow      *window);
 gboolean   _gdk_window_update_viewable   (GdkWindow      *window);
+GdkGLContext * gdk_window_get_paint_gl_context (GdkWindow *window,
+                                                GError   **error);
+
 
 void       _gdk_window_process_updates_recurse (GdkWindow *window,
                                                 cairo_region_t *expose_region);
diff --git a/gdk/gdktypes.h b/gdk/gdktypes.h
index be768a7..2b04c10 100644
--- a/gdk/gdktypes.h
+++ b/gdk/gdktypes.h
@@ -128,6 +128,8 @@ typedef struct _GdkWindow             GdkWindow;
 typedef struct _GdkKeymap             GdkKeymap;
 typedef struct _GdkAppLaunchContext   GdkAppLaunchContext;
 
+typedef struct _GdkGLContext          GdkGLContext;
+
 /**
  * GdkByteOrder:
  * @GDK_LSB_FIRST: The values are stored with the least-significant byte
@@ -429,8 +431,26 @@ struct _GdkPoint
   gint y;
 };
 
+/**
+ * GdkGLProfile:
+ * @GDK_GL_PROFILE_DEFAULT: ...
+ * @GDK_GL_PROFILE_LEGACY: ...
+ * @GDK_GL_PROFILE_3_2_CORE: ...
+ *
+ * ...
+ */
+typedef enum {
+  GDK_GL_PROFILE_DEFAULT,
+  GDK_GL_PROFILE_LEGACY,
+  GDK_GL_PROFILE_3_2_CORE
+} GdkGLProfile;
+
+typedef enum {
+  GDK_GL_ERROR_NOT_AVAILABLE,
+  GDK_GL_ERROR_UNSUPPORTED_FORMAT,
+  GDK_GL_ERROR_UNSUPPORTED_PROFILE
+} GdkGLError;
 
 G_END_DECLS
 
-
 #endif /* __GDK_TYPES_H__ */
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index cbf2fd6..62bc5cb 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -39,9 +39,12 @@
 #include "gdkmarshalers.h"
 #include "gdkframeclockidle.h"
 #include "gdkwindowimpl.h"
+#include "gdkglcontextprivate.h"
 
 #include <math.h>
 
+#include <epoxy/gl.h>
+
 /* for the use of round() */
 #include "fallback-c89.c"
 
@@ -185,6 +188,11 @@ static cairo_surface_t *gdk_window_ref_impl_surface (GdkWindow *window);
 static void gdk_window_set_frame_clock (GdkWindow      *window,
                                         GdkFrameClock  *clock);
 
+static void draw_ugly_color (GdkWindow       *window,
+                             const cairo_region_t *region,
+                             int color);
+
+
 static guint signals[LAST_SIGNAL] = { 0 };
 
 static gpointer parent_class = NULL;
@@ -1861,6 +1869,12 @@ gdk_window_free_current_paint (GdkWindow *window)
   cairo_region_destroy (window->current_paint.region);
   window->current_paint.region = NULL;
 
+  cairo_region_destroy (window->current_paint.flushed_region);
+  window->current_paint.flushed_region = NULL;
+
+  cairo_region_destroy (window->current_paint.need_blend_region);
+  window->current_paint.need_blend_region = NULL;
+
   window->current_paint.surface_needs_composite = FALSE;
 }
 
@@ -2688,6 +2702,55 @@ gdk_window_ref_impl_surface (GdkWindow *window)
   return GDK_WINDOW_IMPL_GET_CLASS (window->impl)->ref_cairo_surface (gdk_window_get_impl_window (window));
 }
 
+GdkGLContext *
+gdk_window_get_paint_gl_context (GdkWindow *window, GError **error)
+{
+  if (window->impl_window->gl_paint_context == NULL)
+    window->impl_window->gl_paint_context =
+      GDK_WINDOW_IMPL_GET_CLASS (window->impl)->create_gl_context (window,
+                                                                   GDK_GL_PROFILE_DEFAULT,
+                                                                   NULL,
+                                                                   error);
+
+  return window->impl_window->gl_paint_context;
+}
+
+/**
+ * gdk_window_create_gl_context:
+ * @window: a #GdkWindow
+ * @profile: the GL profile the context should target
+ * @error: return location for an error
+ *
+ * Creates a new #GdkGLContext for the given window, matching the
+ * framebuffer format to the visual of the #GdkWindow.
+ *
+ * If the creation of the #GdkGLContext failed, @error will be set.
+ *
+ * Returns: (transfer full): the newly created #GdkGLContext, or
+ * %NULL on error
+ *
+ * Since: 3.16
+ **/
+GdkGLContext *
+gdk_window_create_gl_context (GdkWindow    *window,
+                              GdkGLProfile  profile,
+                              GError      **error)
+{
+  GdkGLContext *paint_context;
+
+  g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  paint_context = gdk_window_get_paint_gl_context (window, error);
+  if (paint_context == NULL)
+    return NULL;
+
+  return GDK_WINDOW_IMPL_GET_CLASS (window->impl)->create_gl_context (window,
+                                                                      profile,
+                                                                      paint_context,
+                                                                      error);
+}
+
 /**
  * gdk_window_begin_paint_rect:
  * @window: a #GdkWindow
@@ -2764,6 +2827,7 @@ gdk_window_begin_paint_region (GdkWindow       *window,
   GdkWindowImplClass *impl_class;
   double sx, sy;
   gboolean needs_surface;
+  cairo_content_t surface_content;
 
   g_return_if_fail (GDK_IS_WINDOW (window));
 
@@ -2785,14 +2849,58 @@ gdk_window_begin_paint_region (GdkWindow       *window,
     needs_surface = impl_class->begin_paint_region (window, region);
 
   window->current_paint.region = cairo_region_copy (region);
-
   cairo_region_intersect (window->current_paint.region, window->clip_region);
   cairo_region_get_extents (window->current_paint.region, &clip_box);
 
+  window->current_paint.flushed_region = cairo_region_create ();
+  window->current_paint.need_blend_region = cairo_region_create ();
+
+  surface_content = gdk_window_get_content (window);
+
+  window->current_paint.use_gl = window->impl_window->gl_paint_context != NULL;
+
+  if (window->current_paint.use_gl)
+    {
+      GdkGLContext *context;
+
+      int ww = gdk_window_get_width (window) * gdk_window_get_scale_factor (window);
+      int wh = gdk_window_get_height (window) * gdk_window_get_scale_factor (window);
+
+      context = gdk_window_get_paint_gl_context (window, NULL);
+      if (context == NULL || !gdk_gl_context_make_current (context))
+        {
+          g_warning ("gl rendering failed, context: %p", context);
+          window->current_paint.use_gl = FALSE;
+        }
+      else
+        {
+          /* With gl we always need a surface to combine the gl
+             drawing with the native drawing. */
+          needs_surface = TRUE;
+          /* Also, we need the surface to include alpha */
+          surface_content = CAIRO_CONTENT_COLOR_ALPHA;
+
+          /* Initial setup */
+          glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
+          glDisable (GL_DEPTH_TEST);
+          glDisable(GL_BLEND);
+          glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+          glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+          glViewport (0, 0, ww, wh);
+
+          glMatrixMode (GL_PROJECTION);
+          glLoadIdentity ();
+          glOrtho (0.0f, ww, 0.0f, wh, -1.0f, 1.0f);
+
+          glMatrixMode (GL_MODELVIEW);
+          glLoadIdentity ();
+        }
+    }
+
   if (needs_surface)
     {
       window->current_paint.surface = gdk_window_create_similar_surface (window,
-                                                                         gdk_window_get_content (window),
+                                                                         surface_content,
                                                                          MAX (clip_box.width, 1),
                                                                          MAX (clip_box.height, 1));
       sx = sy = 1;
@@ -2800,6 +2908,7 @@ gdk_window_begin_paint_region (GdkWindow       *window,
       cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy);
 #endif
       cairo_surface_set_device_offset (window->current_paint.surface, -clip_box.x*sx, -clip_box.y*sy);
+      gdk_cairo_surface_mark_as_direct (window->current_paint.surface, window);
 
       window->current_paint.surface_needs_composite = TRUE;
     }
@@ -2829,8 +2938,55 @@ gdk_window_begin_paint_region (GdkWindow       *window,
  **/
 void
 gdk_window_mark_paint_from_clip (GdkWindow          *window,
-                                cairo_t            *cr)
+                                 cairo_t            *cr)
 {
+  cairo_region_t *clip_region;
+  GdkWindow *impl_window = window->impl_window;
+
+  if (impl_window->current_paint.surface == NULL ||
+      cairo_get_target (cr) != impl_window->current_paint.surface)
+    return;
+
+  if (cairo_region_is_empty (impl_window->current_paint.flushed_region))
+    return;
+
+  /* This here seems a bit weird, but basically, we're taking the current
+     clip and applying also the flushed region, and the result is that the
+     new clip is the intersection of these. This is the area where the newly
+     drawn region overlaps a previosly flushed area, which is an area of the
+     double buffer surface that need to be blended OVER the back buffer rather
+     than SRCed. */
+  cairo_save (cr);
+  /* We set the indentity matrix here so we get and apply regions in native
+     window coordinates. */
+  cairo_identity_matrix (cr);
+  gdk_cairo_region (cr, impl_window->current_paint.flushed_region);
+  cairo_clip (cr);
+
+  clip_region = gdk_cairo_region_from_clip (cr);
+  if (clip_region == NULL)
+    {
+      /* Failed to represent clip as region, mark all as requiring
+         blend */
+      cairo_region_union (impl_window->current_paint.need_blend_region,
+                          impl_window->current_paint.flushed_region);
+      cairo_region_destroy (impl_window->current_paint.flushed_region);
+      impl_window->current_paint.flushed_region = cairo_region_create ();
+    }
+  else
+    {
+      cairo_region_subtract (impl_window->current_paint.flushed_region, clip_region);
+      cairo_region_union (impl_window->current_paint.need_blend_region, clip_region);
+    }
+
+  /* Clear the area on the double buffer surface to transparent so we
+     can start drawing from scratch the area "above" the flushed
+     region */
+  cairo_set_source_rgba (cr, 0, 0, 0, 0);
+  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+  cairo_paint (cr);
+
+  cairo_restore (cr);
 }
 
 /**
@@ -2852,7 +3008,6 @@ gdk_window_end_paint (GdkWindow *window)
   GdkWindow *composited;
   GdkWindowImplClass *impl_class;
   GdkRectangle clip_box = { 0, };
-  cairo_region_t *full_clip;
   cairo_t *cr;
 
   g_return_if_fail (GDK_IS_WINDOW (window));
@@ -2872,41 +3027,57 @@ gdk_window_end_paint (GdkWindow *window)
   if (impl_class->end_paint)
     impl_class->end_paint (window);
 
+
   if (window->current_paint.surface_needs_composite)
     {
       cairo_surface_t *surface;
-      gboolean skip_alpha_blending;
 
       cairo_region_get_extents (window->current_paint.region, &clip_box);
-      full_clip = cairo_region_copy (window->clip_region);
-      cairo_region_intersect (full_clip, window->current_paint.region);
 
-      surface = gdk_window_ref_impl_surface (window);
-      cr = cairo_create (surface);
-      cairo_surface_destroy (surface);
+      if (window->current_paint.use_gl)
+        {
+          cairo_region_t *opaque_region = cairo_region_copy (window->current_paint.region);
+          cairo_region_subtract (opaque_region, window->current_paint.flushed_region);
+          cairo_region_subtract (opaque_region, window->current_paint.need_blend_region);
 
-      cairo_set_source_surface (cr, window->current_paint.surface, 0, 0);
-      gdk_cairo_region (cr, full_clip);
-      cairo_clip (cr);
+          if (!gdk_gl_context_make_current (window->gl_paint_context))
+            g_error ("make current failed");
 
-      /* We can skip alpha blending for a fast composite case
-       * if we have an impl window or we're a fully opaque window. */
-      skip_alpha_blending = (gdk_window_has_impl (window) ||
-                             window->alpha == 255);
+          if (!cairo_region_is_empty (opaque_region))
+            gdk_gl_texture_from_surface (window->current_paint.surface,
+                                         opaque_region);
+          if (!cairo_region_is_empty (window->current_paint.need_blend_region))
+            {
+              glEnable(GL_BLEND);
+              gdk_gl_texture_from_surface (window->current_paint.surface,
+                                           window->current_paint.need_blend_region);
+              glDisable(GL_BLEND);
+            }
 
-      if (skip_alpha_blending)
-        {
-          cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
-          cairo_paint (cr);
+          cairo_region_destroy (opaque_region);
+
+          gdk_gl_context_flush_buffer (window->gl_paint_context,
+                                       window->current_paint.region,
+                                       window->active_update_area);
+
+          if (epoxy_has_gl_extension ("GL_GREMEDY_frame_terminator"))
+            glFrameTerminatorGREMEDY();
         }
       else
         {
-          cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
-          cairo_paint_with_alpha (cr, window->alpha / 255.0);
-        }
+          surface = gdk_window_ref_impl_surface (window);
+          cr = cairo_create (surface);
+          cairo_surface_destroy (surface);
+
+          cairo_set_source_surface (cr, window->current_paint.surface, 0, 0);
+          gdk_cairo_region (cr, window->current_paint.region);
+          cairo_clip (cr);
+
+          cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+          cairo_paint (cr);
 
-      cairo_destroy (cr);
-      cairo_region_destroy (full_clip);
+          cairo_destroy (cr);
+        }
     }
 
   gdk_window_free_current_paint (window);
@@ -3432,11 +3603,22 @@ gdk_window_process_updates_internal (GdkWindow *window)
 
          expose_region = cairo_region_copy (window->active_update_area);
 
+          /* Sometimes we can't just paint only the new area, as the windowing system
+             requires more to be repainted. For instance, with opengl you typically
+             repaint all of each frame each time and then swap the buffer, although
+             there are extensions that allow us to reuse part of an old frame */
+          if (GDK_WINDOW_IMPL_GET_CLASS (window->impl)->invalidate_for_new_frame)
+            GDK_WINDOW_IMPL_GET_CLASS (window->impl)->invalidate_for_new_frame (window, expose_region);
+
          /* Clip to part visible in impl window */
          cairo_region_intersect (expose_region, window->clip_region);
 
          if (debug_updates)
            {
+              cairo_region_t *swap_region = cairo_region_copy (expose_region);
+              cairo_region_subtract (swap_region, window->active_update_area);
+              draw_ugly_color (window, swap_region, 1);
+
              /* Make sure we see the red invalid area before redrawing. */
              gdk_display_sync (gdk_window_get_display (window));
              g_usleep (70000);
@@ -3756,13 +3938,17 @@ gdk_window_set_invalidate_handler (GdkWindow                      *window,
 
 static void
 draw_ugly_color (GdkWindow       *window,
-                const cairo_region_t *region)
+                const cairo_region_t *region,
+                 int color)
 {
   cairo_t *cr;
 
   cr = gdk_cairo_create (window);
   /* Draw ugly color all over the newly-invalid region */
-  cairo_set_source_rgb (cr, 50000/65535., 10000/65535., 10000/65535.);
+  if (color == 0)
+    cairo_set_source_rgb (cr, 50000/65535., 10000/65535., 10000/65535.);
+  else
+    cairo_set_source_rgb (cr, 10000/65535., 50000/65535., 10000/65535.);
   gdk_cairo_region (cr, region);
   cairo_fill (cr);
 
@@ -3858,7 +4044,7 @@ gdk_window_invalidate_maybe_recurse_full (GdkWindow            *window,
   invalidate_impl_subwindows (window, region, child_func, user_data, 0, 0);
 
   if (debug_updates)
-    draw_ugly_color (window, visible_region);
+    draw_ugly_color (window, visible_region, 0);
 
   while (window != NULL && 
         !cairo_region_is_empty (visible_region))
diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h
index 5f2950e..f29a36d 100644
--- a/gdk/gdkwindow.h
+++ b/gdk/gdkwindow.h
@@ -1111,6 +1111,12 @@ GDK_AVAILABLE_IN_3_14
 gboolean  gdk_window_show_window_menu          (GdkWindow      *window,
                                                 GdkEvent       *event);
 
+GDK_AVAILABLE_IN_3_16
+GdkGLContext * gdk_window_create_gl_context        (GdkWindow    *window,
+                                                    GdkGLProfile  profile,
+                                                    GError      **error);
+
+
 G_END_DECLS
 
 #endif /* __GDK_WINDOW_H__ */
diff --git a/gdk/gdkwindowimpl.h b/gdk/gdkwindowimpl.h
index 85ae134..f6e9a63 100644
--- a/gdk/gdkwindowimpl.h
+++ b/gdk/gdkwindowimpl.h
@@ -289,6 +289,12 @@ struct _GdkWindowImplClass
                                            gint            bottom);
   gboolean     (* show_window_menu)       (GdkWindow      *window,
                                            GdkEvent       *event);
+  GdkGLContext *(*create_gl_context)      (GdkWindow      *window,
+                                           GdkGLProfile    profile,
+                                           GdkGLContext   *share,
+                                           GError        **error);
+  void         (*invalidate_for_new_frame)(GdkWindow      *window,
+                                           cairo_region_t *update_area);
 };
 
 /* Interface Functions */
diff --git a/gdk/x11/Makefile.am b/gdk/x11/Makefile.am
index 5930f7e..36e39f0 100644
--- a/gdk/x11/Makefile.am
+++ b/gdk/x11/Makefile.am
@@ -39,6 +39,8 @@ libgdk_x11_la_SOURCES =       \
        gdkeventtranslator.c    \
        gdkeventtranslator.h    \
        gdkgeometry-x11.c       \
+       gdkglcontext-x11.c      \
+       gdkglcontext-x11.h      \
        gdkkeys-x11.c           \
        gdkmain-x11.c           \
        gdkproperty-x11.c       \
@@ -71,6 +73,7 @@ libgdkx11include_HEADERS =    \
        gdkx11display.h         \
        gdkx11displaymanager.h  \
        gdkx11dnd.h             \
+       gdkx11glcontext.h       \
        gdkx11keys.h            \
        gdkx11property.h        \
        gdkx11screen.h          \
diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c
index 1db4742..064b56f 100644
--- a/gdk/x11/gdkdisplay-x11.c
+++ b/gdk/x11/gdkdisplay-x11.c
@@ -37,6 +37,7 @@
 #include "gdkdisplay-x11.h"
 #include "gdkprivate-x11.h"
 #include "gdkscreen-x11.h"
+#include "gdkglcontext-x11.h"
 
 #include <glib.h>
 #include <glib/gprintf.h>
@@ -2903,5 +2904,8 @@ gdk_x11_display_class_init (GdkX11DisplayClass * class)
   display_class->text_property_to_utf8_list = _gdk_x11_display_text_property_to_utf8_list;
   display_class->utf8_to_string_target = _gdk_x11_display_utf8_to_string_target;
 
+  display_class->destroy_gl_context = gdk_x11_display_destroy_gl_context;
+  display_class->make_gl_context_current = gdk_x11_display_make_gl_context_current;
+
   _gdk_x11_windowing_init ();
 }
diff --git a/gdk/x11/gdkdisplay-x11.h b/gdk/x11/gdkdisplay-x11.h
index 69ba890..b519445 100644
--- a/gdk/x11/gdkdisplay-x11.h
+++ b/gdk/x11/gdkdisplay-x11.h
@@ -124,6 +124,21 @@ struct _GdkX11Display
   GSList *error_traps;
 
   gint wm_moveresize_button;
+
+  /* GLX information */
+  gint glx_version;
+  gint glx_error_base;
+  gint glx_event_base;
+
+  guint have_glx : 1;
+
+  /* GLX extensions we check */
+  guint has_glx_swap_interval : 1;
+  guint has_glx_create_context : 1;
+  guint has_glx_texture_from_pixmap : 1;
+  guint has_glx_video_sync : 1;
+  guint has_glx_buffer_age : 1;
+  guint has_glx_sync_control : 1;
 };
 
 struct _GdkX11DisplayClass
diff --git a/gdk/x11/gdkglcontext-x11.c b/gdk/x11/gdkglcontext-x11.c
new file mode 100644
index 0000000..70ff6a3
--- /dev/null
+++ b/gdk/x11/gdkglcontext-x11.c
@@ -0,0 +1,886 @@
+/* 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 "gdkscreen-x11.h"
+
+#include "gdkx11display.h"
+#include "gdkx11glcontext.h"
+#include "gdkx11screen.h"
+#include "gdkx11window.h"
+#include "gdkx11visual.h"
+
+#include "gdkinternals.h"
+
+#include "gdkintl.h"
+
+#include <cairo/cairo-xlib.h>
+
+#include <GL/glx.h>
+
+G_DEFINE_TYPE (GdkX11GLContext, gdk_x11_gl_context, GDK_TYPE_GL_CONTEXT)
+
+typedef struct {
+  GLXDrawable drawable;
+
+  GdkDisplay *display;
+  GdkWindow *window;
+
+  guint32 last_frame_counter;
+} DrawableInfo;
+
+static void
+drawable_info_free (gpointer data_)
+{
+  DrawableInfo *data = data_;
+
+  gdk_x11_display_error_trap_push (data->display);
+
+  if (data->drawable)
+    glXDestroyWindow (gdk_x11_display_get_xdisplay (data->display), data->drawable);
+
+  gdk_x11_display_error_trap_pop_ignored (data->display);
+
+  g_slice_free (DrawableInfo, data);
+}
+
+static DrawableInfo *
+get_glx_drawable_info (GdkWindow *window)
+{
+  return g_object_get_data (G_OBJECT (window), "-gdk-x11-window-glx-info");
+}
+
+static void
+set_glx_drawable_info (GdkWindow    *window,
+                       DrawableInfo *info)
+{
+  g_object_set_data_full (G_OBJECT (window), "-gdk-x11-window-glx-info",
+                          info,
+                          drawable_info_free);
+}
+
+static void
+gdk_x11_gl_context_update (GdkGLContext *context)
+{
+  GdkWindow *window = gdk_gl_context_get_window (context);
+  int width, height;
+
+  if (!gdk_gl_context_make_current (context))
+    return;
+
+  width = gdk_window_get_width (window);
+  height = gdk_window_get_height (window);
+
+  GDK_NOTE (OPENGL, g_print ("Updating GL viewport size to { %d, %d } for window %lu (context: %p)\n",
+                             width, height,
+                             (unsigned long) gdk_x11_window_get_xid (window),
+                             context));
+
+  glViewport (0, 0, width, height);
+}
+
+static void
+maybe_wait_for_vblank (GdkDisplay  *display,
+                       GLXDrawable  drawable)
+{
+  GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+  Display *dpy = gdk_x11_display_get_xdisplay (display);
+
+  if (display_x11->has_glx_sync_control)
+    {
+      gint64 ust, msc, sbc;
+
+      glXGetSyncValuesOML (dpy, drawable, &ust, &msc, &sbc);
+      glXWaitForMscOML (dpy, drawable,
+                        0, 2, (msc + 1) % 2,
+                        &ust, &msc, &sbc);
+    }
+  else if (display_x11->has_glx_video_sync)
+    {
+      guint32 current_count;
+
+      glXGetVideoSyncSGI (&current_count);
+      glXWaitVideoSyncSGI (2, (current_count + 1) % 2, &current_count);
+    }
+}
+
+void
+gdk_x11_window_invalidate_for_new_frame (GdkWindow      *window,
+                                         cairo_region_t *update_area)
+{
+  cairo_rectangle_int_t window_rect;
+  GdkDisplay *display = gdk_window_get_display (window);
+  GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+  Display *dpy = gdk_x11_display_get_xdisplay (display);
+  GdkX11GLContext *context_x11;
+  unsigned int buffer_age;
+  gboolean invalidate_all;
+
+  /* Minimal update is ok if we're not drawing with gl */
+  if (window->gl_paint_context == NULL)
+    return;
+
+  context_x11 = GDK_X11_GL_CONTEXT (window->gl_paint_context);
+
+  buffer_age = 0;
+
+  if (display_x11->has_glx_buffer_age)
+    glXQueryDrawable(dpy, context_x11->drawable,
+                     GLX_BACK_BUFFER_AGE_EXT, &buffer_age);
+
+  invalidate_all = FALSE;
+  if (buffer_age == 0 || buffer_age >= 4)
+    invalidate_all = TRUE;
+  else
+    {
+      if (buffer_age >= 2)
+        {
+          if (window->old_updated_area[0])
+            cairo_region_union (update_area, window->old_updated_area[0]);
+          else
+            invalidate_all = TRUE;
+        }
+      if (buffer_age >= 3)
+        {
+          if (window->old_updated_area[1])
+            cairo_region_union (update_area, window->old_updated_area[1]);
+          else
+            invalidate_all = TRUE;
+        }
+    }
+
+  if (invalidate_all)
+    {
+      window_rect.x = 0;
+      window_rect.y = 0;
+      window_rect.width = gdk_window_get_width (window);
+      window_rect.height = gdk_window_get_height (window);
+
+      /* If nothing else is known, repaint everything so that the back
+         buffer is fully up-to-date for the swapbuffer */
+      cairo_region_union_rectangle (update_area, &window_rect);
+    }
+
+}
+
+static void
+gdk_x11_gl_context_flush_buffer (GdkGLContext *context,
+                                 cairo_region_t *painted,
+                                 cairo_region_t *damage)
+{
+  GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context);
+  GdkWindow *window = gdk_gl_context_get_window (context);
+  GdkDisplay *display = gdk_window_get_display (window);
+  Display *dpy = gdk_x11_display_get_xdisplay (display);
+  GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+  DrawableInfo *info;
+  GLXDrawable drawable;
+
+  gdk_gl_context_make_current (context);
+
+  info = get_glx_drawable_info (window);
+
+  drawable = context_x11->drawable;
+
+  GDK_NOTE (OPENGL,
+            g_print ("Flushing GLX buffers for drawable %lu (window: %lu), frame sync: %s\n",
+                     (unsigned long) drawable,
+                     (unsigned long) gdk_x11_window_get_xid (window),
+                     context_x11->do_frame_sync ? "yes" : "no"));
+
+  /* if we are going to wait for the vertical refresh manually
+   * we need to flush pending redraws, and we also need to wait
+   * for that to finish, otherwise we are going to tear.
+   *
+   * obviously, this condition should not be hit if we have
+   * GLX_SGI_swap_control, and we ask the driver to do the right
+   * thing.
+   */
+  if (context_x11->do_frame_sync)
+    {
+      guint32 end_frame_counter = 0;
+      gboolean has_counter = display_x11->has_glx_video_sync;
+      gboolean can_wait = display_x11->has_glx_video_sync || display_x11->has_glx_sync_control;
+
+      if (display_x11->has_glx_video_sync)
+        glXGetVideoSyncSGI (&end_frame_counter);
+
+      if (context_x11->do_frame_sync && !display_x11->has_glx_swap_interval)
+        {
+          glFinish ();
+
+          if (has_counter && can_wait)
+            {
+              guint32 last_counter = info != NULL ? info->last_frame_counter : 0;
+
+              if (last_counter == end_frame_counter)
+                maybe_wait_for_vblank (display, drawable);
+            }
+          else if (can_wait)
+            maybe_wait_for_vblank (display, drawable);
+        }
+    }
+
+  glXSwapBuffers (dpy, drawable);
+
+  if (context_x11->do_frame_sync && info != NULL && display_x11->has_glx_video_sync)
+    glXGetVideoSyncSGI (&info->last_frame_counter);
+}
+
+typedef struct {
+  Display *display;
+  GLXDrawable drawable;
+  gboolean y_inverted;
+} GdkGLXPixmap;
+
+static void
+glx_pixmap_destroy (void *data)
+{
+  GdkGLXPixmap *glx_pixmap = data;
+
+  glXDestroyPixmap (glx_pixmap->display, glx_pixmap->drawable);
+
+  g_slice_free (GdkGLXPixmap, glx_pixmap);
+}
+
+static GdkGLXPixmap *
+glx_pixmap_get (cairo_surface_t *surface)
+{
+  Display *display = cairo_xlib_surface_get_display (surface);
+  Screen *screen = cairo_xlib_surface_get_screen (surface);
+  Visual *visual = cairo_xlib_surface_get_visual (surface);;
+  GdkGLXPixmap *glx_pixmap;
+  GLXFBConfig *fbconfigs;
+  int nfbconfigs;
+  XVisualInfo *visinfo;
+  int i, value;
+  gboolean y_inverted;
+  const int pixmap_attributes[] = {
+    GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_RECTANGLE_EXT,
+    GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
+    None
+  };
+
+  y_inverted = FALSE;
+  fbconfigs = glXGetFBConfigs (display, XScreenNumberOfScreen (screen), &nfbconfigs);
+  for (i = 0; i < nfbconfigs; i++)
+    {
+      visinfo = glXGetVisualFromFBConfig (display, fbconfigs[i]);
+      if (!visinfo || visinfo->visualid != XVisualIDFromVisual (visual))
+       continue;
+
+      glXGetFBConfigAttrib (display, fbconfigs[i], GLX_DRAWABLE_TYPE, &value);
+      if (!(value & GLX_PIXMAP_BIT))
+       continue;
+
+      glXGetFBConfigAttrib (display, fbconfigs[i],
+                           GLX_BIND_TO_TEXTURE_TARGETS_EXT,
+                           &value);
+      if (!(value & GLX_TEXTURE_RECTANGLE_BIT_EXT))
+       continue;
+
+      glXGetFBConfigAttrib (display, fbconfigs[i],
+                           GLX_BIND_TO_TEXTURE_RGBA_EXT,
+                           &value);
+      if (value == FALSE)
+       {
+         glXGetFBConfigAttrib (display, fbconfigs[i],
+                               GLX_BIND_TO_TEXTURE_RGB_EXT,
+                               &value);
+         if (value == FALSE)
+           continue;
+       }
+
+      glXGetFBConfigAttrib (display, fbconfigs[i],
+                           GLX_Y_INVERTED_EXT,
+                           &value);
+      if (value == TRUE)
+       y_inverted = TRUE;
+
+      break;
+    }
+
+  if (i == nfbconfigs)
+    return NULL;
+
+  glx_pixmap = g_slice_new0 (GdkGLXPixmap);
+  glx_pixmap->y_inverted = y_inverted;
+  glx_pixmap->display = display;
+  glx_pixmap->drawable = glXCreatePixmap (display, fbconfigs[i],
+                                         cairo_xlib_surface_get_drawable (surface),
+                                         pixmap_attributes);
+
+  return glx_pixmap;
+}
+
+static gboolean
+gdk_x11_gl_context_texture_from_surface (GdkGLContext *context,
+                                        cairo_surface_t *surface,
+                                        cairo_region_t *region)
+{
+  GdkGLXPixmap *glx_pixmap;
+  double device_x_offset, device_y_offset;
+  cairo_rectangle_int_t rect;
+  int n_rects, i;
+  GdkWindow *window;
+  int window_height;
+  int window_scale;
+  unsigned int texture_id;
+  double sx, sy;
+
+  if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_XLIB)
+    return FALSE;
+
+  glx_pixmap = glx_pixmap_get (surface);
+  if (glx_pixmap == NULL)
+    return FALSE;
+
+  window = gdk_gl_context_get_window (gdk_gl_context_get_current ());
+  window_scale = gdk_window_get_scale_factor (window);
+  window_height = gdk_window_get_height (window);
+
+  sx = sy = 1;
+#ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE
+  cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy);
+#endif
+
+  cairo_surface_get_device_offset (surface,
+                                  &device_x_offset, &device_y_offset);
+
+  /* Ensure all the X stuff are synced before we read it back via texture-from-pixmap */
+  glXWaitX();
+
+  glGenTextures (1, &texture_id);
+  glBindTexture (GL_TEXTURE_RECTANGLE_ARB, texture_id);
+  glEnable (GL_TEXTURE_RECTANGLE_ARB);
+
+  glXBindTexImageEXT (glx_pixmap->display, glx_pixmap->drawable,
+                     GLX_FRONT_LEFT_EXT, NULL);
+
+  n_rects = cairo_region_num_rectangles (region);
+  for (i = 0; i < n_rects; i++)
+    {
+      int src_x, src_y, src_height, src_width;
+
+      cairo_region_get_rectangle (region, i, &rect);
+
+      glScissor (rect.x * window_scale, (window_height - rect.y - rect.height) * window_scale,
+                rect.width * window_scale, rect.height * window_scale);
+
+      src_x = rect.x * sx + device_x_offset;
+      src_y = rect.y * sy + device_y_offset;
+      src_width = rect.width * sx;
+      src_height = rect.height * sy;
+
+#define FLIP_Y(_y) (window_height - (_y))
+
+      glBegin (GL_QUADS);
+      glTexCoord2f (src_x, src_y + src_height);
+      glVertex2f (rect.x * window_scale, FLIP_Y(rect.y + rect.height) * window_scale);
+
+      glTexCoord2f (src_x + src_width, src_y + src_height);
+      glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y + rect.height) * window_scale);
+
+      glTexCoord2f (src_x + src_width, src_y);
+      glVertex2f ((rect.x + rect.width) * window_scale, FLIP_Y(rect.y) * window_scale);
+
+      glTexCoord2f (src_x, src_y);
+      glVertex2f (rect.x * window_scale, FLIP_Y(rect.y) * window_scale);
+      glEnd();
+    }
+
+  glXReleaseTexImageEXT (glx_pixmap->display, glx_pixmap->drawable,
+                        GLX_FRONT_LEFT_EXT);
+
+  glDisable (GL_TEXTURE_RECTANGLE_ARB);
+  glDeleteTextures (1, &texture_id);
+
+  glx_pixmap_destroy(glx_pixmap);
+
+  return TRUE;
+}
+
+static void
+gdk_x11_gl_context_class_init (GdkX11GLContextClass *klass)
+{
+  GdkGLContextClass *context_class = GDK_GL_CONTEXT_CLASS (klass);
+
+  context_class->update = gdk_x11_gl_context_update;
+  context_class->flush_buffer = gdk_x11_gl_context_flush_buffer;
+  context_class->texture_from_surface = gdk_x11_gl_context_texture_from_surface;
+}
+
+static void
+gdk_x11_gl_context_init (GdkX11GLContext *self)
+{
+}
+
+gboolean
+gdk_x11_display_init_gl (GdkDisplay *display)
+{
+  GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+  GdkScreen *screen;
+  Display *dpy;
+  int error_base, event_base;
+  int screen_num;
+
+  if (display_x11->have_glx)
+    return TRUE;
+
+  dpy = gdk_x11_display_get_xdisplay (display);
+
+  if (!glXQueryExtension (dpy, &error_base, &event_base))
+    return FALSE;
+
+  screen = gdk_display_get_default_screen (display);
+  screen_num = GDK_X11_SCREEN (screen)->screen_num;
+
+  display_x11->have_glx = TRUE;
+
+  display_x11->glx_version = epoxy_glx_version (dpy, screen_num);
+  display_x11->glx_error_base = error_base;
+  display_x11->glx_event_base = event_base;
+
+  display_x11->has_glx_create_context =
+    epoxy_has_glx_extension (dpy, screen_num, "GLX_ARB_create_context_profile");
+  display_x11->has_glx_swap_interval =
+    epoxy_has_glx_extension (dpy, screen_num, "GLX_SGI_swap_control");
+  display_x11->has_glx_texture_from_pixmap =
+    epoxy_has_glx_extension (dpy, screen_num, "GLX_EXT_texture_from_pixmap");
+  display_x11->has_glx_video_sync =
+    epoxy_has_glx_extension (dpy, screen_num, "GLX_SGI_video_sync");
+  display_x11->has_glx_buffer_age =
+    epoxy_has_glx_extension (dpy, screen_num, "GLX_EXT_buffer_age");
+  display_x11->has_glx_sync_control =
+    epoxy_has_glx_extension (dpy, screen_num, "GLX_OML_sync_control");
+
+  GDK_NOTE (OPENGL,
+            g_print ("GLX version %d.%d found\n"
+                     " - Vendor: %s\n"
+                     " - Checked extensions:\n"
+                     "\t* GLX_ARB_create_context_profile: %s\n"
+                     "\t* GLX_SGI_swap_control: %s\n"
+                     "\t* GLX_EXT_texture_from_pixmap: %s\n"
+                     "\t* GLX_SGI_video_sync: %s\n"
+                     "\t* GLX_EXT_buffer_age: %s\n"
+                     "\t* GLX_OML_sync_control: %s\n",
+                     display_x11->glx_version / 10,
+                     display_x11->glx_version % 10,
+                     glXGetClientString (dpy, GLX_VENDOR),
+                     display_x11->has_glx_create_context ? "yes" : "no",
+                     display_x11->has_glx_swap_interval ? "yes" : "no",
+                     display_x11->has_glx_texture_from_pixmap ? "yes" : "no",
+                     display_x11->has_glx_video_sync ? "yes" : "no",
+                     display_x11->has_glx_buffer_age ? "yes" : "no",
+                     display_x11->has_glx_sync_control ? "yes" : "no"));
+
+  return TRUE;
+}
+
+#define MAX_GLX_ATTRS   30
+
+static gboolean
+find_fbconfig_for_window (GdkWindow         *window,
+                          GLXFBConfig       *fb_config_out,
+                          XVisualInfo      **visinfo_out,
+                          GError           **error)
+{
+  static int attrs[MAX_GLX_ATTRS];
+  GdkVisual *visual = gdk_window_get_visual (window);
+  GdkDisplay *display = gdk_window_get_display (window);
+  Display *dpy = gdk_x11_display_get_xdisplay (display);
+  GLXFBConfig *configs;
+  int n_configs, i;
+  gboolean use_rgba;
+  gboolean retval = FALSE;
+
+  i = 0;
+  attrs[i++] = GLX_DRAWABLE_TYPE;
+  attrs[i++] = GLX_WINDOW_BIT;
+
+  attrs[i++] = GLX_RENDER_TYPE;
+  attrs[i++] = GLX_RGBA_BIT;
+
+  attrs[i++] = GLX_DOUBLEBUFFER;
+  attrs[i++] = GL_TRUE;
+
+  attrs[i++] = GLX_RED_SIZE;
+  attrs[i++] = gdk_visual_get_bits_per_rgb (visual);
+  attrs[i++] = GLX_GREEN_SIZE;
+  attrs[i++] = gdk_visual_get_bits_per_rgb (visual);;
+  attrs[i++] = GLX_BLUE_SIZE;
+  attrs[i++] = gdk_visual_get_bits_per_rgb (visual);;
+
+  use_rgba = (visual == gdk_screen_get_rgba_visual (gdk_display_get_default_screen (display)));
+
+  if (use_rgba)
+    {
+      attrs[i++] = GLX_ALPHA_SIZE;
+      attrs[i++] = 1;
+    }
+  else
+    {
+      attrs[i++] = GLX_ALPHA_SIZE;
+      attrs[i++] = GLX_DONT_CARE;
+    }
+
+  attrs[i++] = None;
+
+  g_assert (i < MAX_GLX_ATTRS);
+
+  configs = glXChooseFBConfig (dpy, DefaultScreen (dpy), attrs, &n_configs);
+  if (configs == NULL || n_configs == 0)
+    {
+      g_set_error_literal (error, GDK_GL_ERROR,
+                           GDK_GL_ERROR_UNSUPPORTED_FORMAT,
+                           _("No available configurations for the given pixel format"));
+      return FALSE;
+    }
+
+  /* if we don't care about an alpha channel, then the first
+   * valid configuration is the one we give back
+   */
+  if (!use_rgba)
+    {
+      if (fb_config_out != NULL)
+        *fb_config_out = configs[0];
+
+      if (visinfo_out != NULL)
+        *visinfo_out = glXGetVisualFromFBConfig (dpy, configs[0]);
+
+      retval = TRUE;
+      goto out;
+    }
+
+  for (i = 0; i < n_configs; i++)
+    {
+      XVisualInfo *visinfo;
+      unsigned long mask;
+
+      visinfo = glXGetVisualFromFBConfig (dpy, configs[i]);
+      if (visinfo == NULL)
+        continue;
+
+      mask = visinfo->red_mask | visinfo->green_mask | visinfo->blue_mask;
+      if (visinfo->depth == 32 && mask != 0xffffffff)
+        {
+          if (fb_config_out != NULL)
+            *fb_config_out = configs[i];
+
+          if (visinfo_out != NULL)
+            *visinfo_out = visinfo;
+
+          retval = TRUE;
+          goto out;
+        }
+
+      XFree (visinfo);
+    }
+
+  g_set_error (error, GDK_GL_ERROR,
+               GDK_GL_ERROR_UNSUPPORTED_FORMAT,
+               _("No available configurations for the given RGBA pixel format"));
+
+out:
+  XFree (configs);
+
+  return retval;
+}
+
+static GLXContext
+create_gl3_context (GdkDisplay   *display,
+                    GLXFBConfig   config,
+                    GdkGLContext *share)
+{
+  static const int attrib_list[] = {
+    GLX_CONTEXT_PROFILE_MASK_ARB,  GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
+    GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
+    GLX_CONTEXT_MAJOR_VERSION_ARB, 2,
+    None,
+  };
+
+  GdkX11GLContext *context_x11 = NULL;
+
+  if (share != NULL)
+    context_x11 = GDK_X11_GL_CONTEXT (share);
+
+  return glXCreateContextAttribsARB (gdk_x11_display_get_xdisplay (display),
+                                     config,
+                                     context_x11 != NULL ? context_x11->glx_context : NULL,
+                                     True,
+                                     attrib_list);
+}
+
+static GLXContext
+create_gl_context (GdkDisplay   *display,
+                   GLXFBConfig   config,
+                   GdkGLContext *share)
+{
+  GdkX11GLContext *context_x11 = NULL;
+
+  if (share != NULL)
+    context_x11 = GDK_X11_GL_CONTEXT (share);
+
+  return glXCreateNewContext (gdk_x11_display_get_xdisplay (display),
+                              config,
+                              GLX_RGBA_TYPE,
+                              context_x11 != NULL ? context_x11->glx_context : NULL,
+                              True);
+}
+
+GdkGLContext *
+gdk_x11_window_create_gl_context (GdkWindow    *window,
+                                  GdkGLProfile  profile,
+                                  GdkGLContext *share,
+                                  GError      **error)
+{
+  GdkDisplay *display = gdk_window_get_display (window);
+  GdkX11GLContext *context;
+  GdkVisual *gdk_visual;
+  GLXFBConfig config;
+  GLXContext glx_context;
+  GLXWindow drawable;
+  gboolean is_direct;
+  XVisualInfo *xvisinfo;
+  Display *dpy;
+  DrawableInfo *info;
+
+  if (!gdk_x11_display_init_gl (display))
+    {
+      g_set_error_literal (error, GDK_GL_ERROR,
+                           GDK_GL_ERROR_NOT_AVAILABLE,
+                           _("No GL implementation is available"));
+      return NULL;
+    }
+
+  if (profile == GDK_GL_PROFILE_3_2_CORE &&
+      !GDK_X11_DISPLAY (display)->has_glx_create_context)
+    {
+      g_set_error_literal (error, GDK_GL_ERROR,
+                           GDK_GL_ERROR_UNSUPPORTED_PROFILE,
+                           _("The GLX_ARB_create_context_profile extension "
+                             "needed to create 3.2 core profiles is not "
+                             "available"));
+      return NULL;
+    }
+
+  if (!find_fbconfig_for_window (window, &config, &xvisinfo, error))
+    return NULL;
+
+  dpy = gdk_x11_display_get_xdisplay (display);
+
+  /* we check for the GLX_ARB_create_context_profile extension
+   * while validating the PixelFormat.
+   */
+  if (profile == GDK_GL_PROFILE_3_2_CORE)
+    glx_context = create_gl3_context (display, config, share);
+  else
+    {
+      /* GDK_GL_PROFILE_DEFAULT is currently
+       * equivalent to the LEGACY profile
+       */
+      glx_context = create_gl_context (display, config, share);
+    }
+
+  if (glx_context == NULL)
+    {
+      g_set_error_literal (error, GDK_GL_ERROR,
+                           GDK_GL_ERROR_NOT_AVAILABLE,
+                           _("Unable to create a GL context"));
+      return NULL;
+    }
+
+  is_direct = glXIsDirect (dpy, glx_context);
+
+  gdk_x11_display_error_trap_push (display);
+
+  if (GDK_X11_DISPLAY (display)->glx_version >= 13)
+    {
+      info = get_glx_drawable_info (window->impl_window);
+
+      if (info == NULL)
+        {
+          info = g_slice_new (DrawableInfo);
+          info->window = window->impl_window;
+          info->display = display;
+          info->drawable = glXCreateWindow (dpy,
+                                            config,
+                                            gdk_x11_window_get_xid (window->impl_window),
+                                            NULL);
+          info->last_frame_counter = 0;
+
+          set_glx_drawable_info (window->impl_window, info);
+        }
+
+      drawable = info->drawable;
+    }
+  else
+    {
+      drawable = gdk_x11_window_get_xid (window);
+    }
+
+  gdk_visual = gdk_x11_screen_lookup_visual (gdk_display_get_default_screen (display),
+                                             xvisinfo->visualid);
+
+  XFree (xvisinfo);
+
+  if (gdk_x11_display_error_trap_pop (display))
+    {
+      g_set_error_literal (error, GDK_GL_ERROR,
+                           GDK_GL_ERROR_NOT_AVAILABLE,
+                           _("Unable to create a GL context"));
+
+      glXDestroyContext (dpy, glx_context);
+
+      return NULL;
+    }
+
+  GDK_NOTE (OPENGL,
+            g_print ("Created GLX context[%p], %s\n",
+                     glx_context,
+                     is_direct ? "direct" : "indirect"));
+
+  context = g_object_new (GDK_X11_TYPE_GL_CONTEXT,
+                          "window", window,
+                          "visual", gdk_visual,
+                          NULL);
+
+  context->glx_config = config;
+  context->glx_context = glx_context;
+  context->drawable = drawable;
+  context->is_direct = is_direct;
+
+  return GDK_GL_CONTEXT (context);
+}
+
+void
+gdk_x11_display_destroy_gl_context (GdkDisplay   *display,
+                                    GdkGLContext *context)
+{
+  GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context);
+  Display *dpy = gdk_x11_display_get_xdisplay (display);
+
+  if (context_x11->glx_context != NULL)
+    {
+      if (glXGetCurrentContext () == context_x11->glx_context)
+        glXMakeContextCurrent (dpy, None, None, NULL);
+
+      GDK_NOTE (OPENGL, g_print ("Destroying GLX context\n"));
+      glXDestroyContext (dpy, context_x11->glx_context);
+      context_x11->glx_context = NULL;
+    }
+}
+
+gboolean
+gdk_x11_display_make_gl_context_current (GdkDisplay   *display,
+                                         GdkGLContext *context)
+{
+  GdkX11GLContext *context_x11;
+  Display *dpy = gdk_x11_display_get_xdisplay (display);
+  GdkWindow *window;
+  GdkScreen *screen;
+  gboolean do_frame_sync = FALSE;
+
+  if (context == NULL)
+    {
+      glXMakeContextCurrent (dpy, None, None, NULL);
+      return TRUE;
+    }
+
+  context_x11 = GDK_X11_GL_CONTEXT (context);
+
+  if (context_x11->glx_context == NULL)
+    return FALSE;
+
+  window = gdk_gl_context_get_window (context);
+
+  // 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.
+  screen = gdk_window_get_screen (window);
+  do_frame_sync = ! gdk_screen_is_composited (screen);
+
+  context_x11->do_frame_sync = do_frame_sync;
+
+  GDK_NOTE (OPENGL,
+            g_print ("Making GLX context current to drawable %lu\n",
+                     (unsigned long) context_x11->drawable));
+
+  gdk_x11_display_error_trap_push (display);
+
+  glXMakeContextCurrent (dpy, context_x11->drawable, context_x11->drawable,
+                         context_x11->glx_context);
+
+  if (GDK_X11_DISPLAY (display)->has_glx_swap_interval)
+    {
+      if (context_x11->do_frame_sync)
+        glXSwapIntervalSGI (1);
+      else
+        glXSwapIntervalSGI (0);
+    }
+
+  /* TODO: Is this needed? */
+  XSync (dpy, False);
+
+  if (gdk_x11_display_error_trap_pop (display))
+    {
+      g_critical ("X Error received while calling glXMakeContextCurrent()");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+/**
+ * gdk_x11_display_get_glx_version:
+ * @display: a #GdkDisplay
+ * @major: (out): return location for the GLX major version
+ * @minor: (out): return location for the GLX minor version
+ *
+ * Retrieves the version of the GLX implementation.
+ *
+ * Returns: %TRUE if GLX is available
+ *
+ * Since: 3.14
+ */
+gboolean
+gdk_x11_display_get_glx_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;
+
+  if (!gdk_x11_display_init_gl (display))
+    return FALSE;
+
+  if (major != NULL)
+    *major = GDK_X11_DISPLAY (display)->glx_version / 10;
+  if (minor != NULL)
+    *minor = GDK_X11_DISPLAY (display)->glx_version % 10;
+
+  return TRUE;
+}
diff --git a/gdk/x11/gdkglcontext-x11.h b/gdk/x11/gdkglcontext-x11.h
new file mode 100644
index 0000000..9f7566c
--- /dev/null
+++ b/gdk/x11/gdkglcontext-x11.h
@@ -0,0 +1,71 @@
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontext-x11.h: Private 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/>.
+ */
+
+#ifndef __GDK_X11_GL_CONTEXT__
+#define __GDK_X11_GL_CONTEXT__
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+#include <epoxy/gl.h>
+#include <epoxy/glx.h>
+
+#include "gdkglcontextprivate.h"
+#include "gdkdisplayprivate.h"
+#include "gdkvisual.h"
+#include "gdkwindow.h"
+#include "gdkinternals.h"
+#include "gdkmain.h"
+
+G_BEGIN_DECLS
+
+struct _GdkX11GLContext
+{
+  GdkGLContext parent_instance;
+
+  GLXContext glx_context;
+  GLXFBConfig glx_config;
+  GLXDrawable drawable;
+
+  guint is_direct : 1;
+  guint do_frame_sync : 1;
+
+};
+
+struct _GdkX11GLContextClass
+{
+  GdkGLContextClass parent_class;
+};
+
+gboolean        gdk_x11_display_init_gl                         (GdkDisplay        *display);
+GdkGLContext *  gdk_x11_window_create_gl_context                (GdkWindow         *window,
+                                                                 GdkGLProfile       profile,
+                                                                 GdkGLContext      *share,
+                                                                 GError           **error);
+void            gdk_x11_window_invalidate_for_new_frame         (GdkWindow         *window,
+                                                                 cairo_region_t    *update_area);
+void            gdk_x11_display_destroy_gl_context              (GdkDisplay        *display,
+                                                                 GdkGLContext      *context);
+gboolean        gdk_x11_display_make_gl_context_current         (GdkDisplay        *display,
+                                                                 GdkGLContext      *context);
+
+G_END_DECLS
+
+#endif /* __GDK_X11_GL_CONTEXT__ */
diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c
index 3d6ff76..eeb7aa1 100644
--- a/gdk/x11/gdkwindow-x11.c
+++ b/gdk/x11/gdkwindow-x11.c
@@ -36,6 +36,7 @@
 #include "gdkasync.h"
 #include "gdkeventsource.h"
 #include "gdkdisplay-x11.h"
+#include "gdkglcontext-x11.h"
 #include "gdkprivate-x11.h"
 
 #include <stdlib.h>
@@ -5730,4 +5731,6 @@ gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
   impl_class->set_opaque_region = gdk_x11_window_set_opaque_region;
   impl_class->set_shadow_width = gdk_x11_window_set_shadow_width;
   impl_class->show_window_menu = gdk_x11_window_show_window_menu;
+  impl_class->create_gl_context = gdk_x11_window_create_gl_context;
+  impl_class->invalidate_for_new_frame = gdk_x11_window_invalidate_for_new_frame;
 }
diff --git a/gdk/x11/gdkx.h b/gdk/x11/gdkx.h
index b28580d..1aa5f7e 100644
--- a/gdk/x11/gdkx.h
+++ b/gdk/x11/gdkx.h
@@ -43,6 +43,7 @@
 #include <gdk/x11/gdkx11display.h>
 #include <gdk/x11/gdkx11displaymanager.h>
 #include <gdk/x11/gdkx11dnd.h>
+#include <gdk/x11/gdkx11glcontext.h>
 #include <gdk/x11/gdkx11keys.h>
 #include <gdk/x11/gdkx11property.h>
 #include <gdk/x11/gdkx11screen.h>
diff --git a/gdk/x11/gdkx11glcontext.h b/gdk/x11/gdkx11glcontext.h
new file mode 100644
index 0000000..962231c
--- /dev/null
+++ b/gdk/x11/gdkx11glcontext.h
@@ -0,0 +1,49 @@
+/* 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/>.
+ */
+
+#ifndef __GDK_X11_GL_CONTEXT_H__
+#define __GDK_X11_GL_CONTEXT_H__
+
+#if !defined (__GDKX_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdkx.h> can be included directly."
+#endif
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define GDK_X11_TYPE_GL_CONTEXT                (gdk_x11_gl_context_get_type ())
+#define GDK_X11_GL_CONTEXT(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_X11_TYPE_GL_CONTEXT, 
GdkX11GLContext))
+#define GDK_X11_IS_GL_CONTEXT(obj)     (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_X11_TYPE_GL_CONTEXT))
+
+typedef struct _GdkX11GLContext                GdkX11GLContext;
+typedef struct _GdkX11GLContextClass   GdkX11GLContextClass;
+
+GDK_AVAILABLE_IN_3_16
+GType gdk_x11_gl_context_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_16
+gboolean        gdk_x11_display_get_glx_version (GdkDisplay *display,
+                                                 int        *major,
+                                                 int        *minor);
+
+G_END_DECLS
+
+#endif /* __GDK_X11_GL_CONTEXT_H__ */


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