[gtk+/master.fcw: 255/648] Add OpenGL Support for the GDK Windows Backend



commit 726c248151d829b3169dc26cb87046c6d2b8a0d1
Author: Chun-wei Fan <fanchunwei src gnome org>
Date:   Tue Dec 16 14:52:45 2014 +0800

    Add OpenGL Support for the GDK Windows Backend
    
    This adds support for OpenGL to the GDK Windows backend using the WGL API
    calls, which enables programs that uses the GTK+ GLArea widgets to work on
    Windows as well.
    
    This also adds a simple utility function to query for the version of OpenGL
    that is supported by the Windows system, like the one provided by the X11
    backend.
    
    Many thanks to Alex (and Emmanuele, who started the OpenGL integration in
    GTK+) who offered advice and help along the way, as well as the X11 and
    Wayland backend for this work to refer to and to model upon.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=740795

 gdk/win32/Makefile.am          |    5 +
 gdk/win32/gdkdisplay-win32.c   |   15 +-
 gdk/win32/gdkdisplay-win32.h   |   48 +++
 gdk/win32/gdkglcontext-win32.c |  819 ++++++++++++++++++++++++++++++++++++++++
 gdk/win32/gdkglcontext-win32.h |   82 ++++
 gdk/win32/gdkwin32.h           |    1 +
 gdk/win32/gdkwin32glcontext.h  |   49 +++
 gdk/win32/gdkwindow-win32.c    |   24 +-
 8 files changed, 1025 insertions(+), 18 deletions(-)
---
diff --git a/gdk/win32/Makefile.am b/gdk/win32/Makefile.am
index 640a215..9366867 100644
--- a/gdk/win32/Makefile.am
+++ b/gdk/win32/Makefile.am
@@ -37,10 +37,13 @@ libgdk_win32_la_SOURCES = \
        gdkdevice-wintab.c \
        gdkdevice-wintab.h \
        gdkdisplay-win32.c \
+       gdkdisplay-win32.h \
        gdkdisplaymanager-win32.c \
        gdkdnd-win32.c \
        gdkevents-win32.c \
        gdkgeometry-win32.c \
+       gdkglcontext-win32.c \
+       gdkglcontext-win32.h \
        gdkglobals-win32.c \
        gdkinput.c \
        gdkkeys-win32.c \
@@ -55,6 +58,7 @@ libgdk_win32_la_SOURCES = \
        gdkwin32display.h \
        gdkwin32displaymanager.h \
        gdkwin32dnd.h \
+       gdkwin32glcontext.h             \
        gdkwin32.h \
        gdkwin32id.c \
        gdkwin32keys.h \
@@ -74,6 +78,7 @@ libgdkwin32include_HEADERS =  \
        gdkwin32display.h       \
        gdkwin32displaymanager.h\
        gdkwin32dnd.h           \
+       gdkwin32glcontext.h             \
        gdkwin32keys.h          \
        gdkwin32misc.h          \
        gdkwin32screen.h        \
diff --git a/gdk/win32/gdkdisplay-win32.c b/gdk/win32/gdkdisplay-win32.c
index 97d0e3e..60915c3 100644
--- a/gdk/win32/gdkdisplay-win32.c
+++ b/gdk/win32/gdkdisplay-win32.c
@@ -19,7 +19,8 @@
 #include "config.h"
 #include "gdk.h"
 #include "gdkprivate-win32.h"
-#include "gdkdisplayprivate.h"
+#include "gdkdisplay-win32.h"
+#include "gdkglcontext-win32.h"
 #include "gdkwin32display.h"
 #include "gdkwin32screen.h"
 #include "gdkwin32window.h"
@@ -216,16 +217,6 @@ _gdk_win32_display_open (const gchar *display_name)
   return _gdk_display;
 }
 
-struct _GdkWin32Display
-{
-  GdkDisplay display;
-};
-
-struct _GdkWin32DisplayClass
-{
-  GdkDisplayClass display_class;
-};
-
 G_DEFINE_TYPE (GdkWin32Display, gdk_win32_display, GDK_TYPE_DISPLAY)
 
 static const gchar *
@@ -581,6 +572,7 @@ gdk_win32_display_finalize (GObject *object)
 static void
 gdk_win32_display_init(GdkWin32Display *display)
 {
+  display->pixel_format = 0;
 }
 
 static void
@@ -664,6 +656,7 @@ gdk_win32_display_class_init (GdkWin32DisplayClass *klass)
   display_class->convert_selection = _gdk_win32_display_convert_selection;
   display_class->text_property_to_utf8_list = _gdk_win32_display_text_property_to_utf8_list;
   display_class->utf8_to_string_target = _gdk_win32_display_utf8_to_string_target;
+  display_class->make_gl_context_current = _gdk_win32_display_make_gl_context_current;
   
   _gdk_win32_windowing_init ();
 }
diff --git a/gdk/win32/gdkdisplay-win32.h b/gdk/win32/gdkdisplay-win32.h
new file mode 100644
index 0000000..b633425
--- /dev/null
+++ b/gdk/win32/gdkdisplay-win32.h
@@ -0,0 +1,48 @@
+/*
+ * gdkdisplay-win32.h
+ *
+ * Copyright 2014 Chun-wei Fan <fanc999 yahoo com tw>
+ *
+ * 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 "gdkdisplayprivate.h"
+
+#ifndef __GDK_DISPLAY__WIN32_H__
+#define __GDK_DISPLAY__WIN32_H__
+
+struct _GdkWin32Display
+{
+  GdkDisplay display;
+
+  /* WGL/OpenGL Items */
+  guint have_wgl : 1;
+  gint gl_version;
+  HDC gl_hdc;
+  HWND gl_hwnd;
+  guint pixel_format;
+
+  gint hasWglARBCreateContext : 1;
+  gint hasWglARBPbuffer : 1;
+  gint hasWglARBRenderTexture : 1;
+  gint hasWglEXTSwapControl : 1;
+  gint hasWglOMLSyncControl : 1;
+};
+
+struct _GdkWin32DisplayClass
+{
+  GdkDisplayClass display_class;
+};
+
+#endif /* __GDK_DISPLAY__WIN32_H__ */
diff --git a/gdk/win32/gdkglcontext-win32.c b/gdk/win32/gdkglcontext-win32.c
new file mode 100644
index 0000000..66c28b1
--- /dev/null
+++ b/gdk/win32/gdkglcontext-win32.c
@@ -0,0 +1,819 @@
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontext-win32.c: Win32 specific OpenGL wrappers
+ *
+ * Copyright © 2014 Emmanuele Bassi
+ * Copyright © 2014 Alexander Larsson
+ * Copyright © 2014 Chun-wei Fan
+ *
+ * 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 "gdkprivate-win32.h"
+#include "gdkwindow-win32.h"
+#include "gdkglcontext-win32.h"
+#include "gdkdisplay-win32.h"
+
+#include "gdkwin32display.h"
+#include "gdkwin32glcontext.h"
+#include "gdkwin32misc.h"
+#include "gdkwin32screen.h"
+#include "gdkwin32window.h"
+
+#include "gdkglcontext.h"
+#include "gdkwindow.h"
+#include "gdkinternals.h"
+#include "gdkintl.h"
+
+#include <cairo.h>
+#include <epoxy/wgl.h>
+
+G_DEFINE_TYPE (GdkWin32GLContext, gdk_win32_gl_context, GDK_TYPE_GL_CONTEXT)
+
+static void
+_gdk_win32_gl_context_dispose (GObject *gobject)
+{
+  GdkGLContext *context = GDK_GL_CONTEXT (gobject);
+  GdkWin32GLContext *context_win32 = GDK_WIN32_GL_CONTEXT (gobject);
+  GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (gdk_gl_context_get_display (context));
+
+  if (context_win32->hglrc != NULL)
+    {
+      if (wglGetCurrentContext () == context_win32->hglrc)
+        wglMakeCurrent (NULL, NULL);
+
+      GDK_NOTE (OPENGL, g_print ("Destroying WGL context\n"));
+
+      wglDeleteContext (context_win32->hglrc);
+      context_win32->hglrc = NULL;
+
+      ReleaseDC (display_win32->gl_hwnd, context_win32->gl_hdc);
+    }
+
+  G_OBJECT_CLASS (gdk_win32_gl_context_parent_class)->dispose (gobject);
+}
+
+static void
+gdk_win32_gl_context_class_init (GdkWin32GLContextClass *klass)
+{
+  GdkGLContextClass *context_class = GDK_GL_CONTEXT_CLASS (klass);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  context_class->end_frame = _gdk_win32_gl_context_end_frame;
+  context_class->texture_from_surface = _gdk_win32_gl_context_texture_from_surface;
+
+  gobject_class->dispose = _gdk_win32_gl_context_dispose;
+}
+
+static void
+gdk_win32_gl_context_init (GdkWin32GLContext *self)
+{
+}
+
+static void
+gdk_gl_blit_region (GdkWindow *window, cairo_region_t *region)
+{
+  int n_rects, i;
+  int scale = gdk_window_get_scale_factor (window);
+  int wh = gdk_window_get_height (window);
+  cairo_rectangle_int_t rect;
+
+  n_rects = cairo_region_num_rectangles (region);
+  for (i = 0; i < n_rects; i++)
+    {
+      cairo_region_get_rectangle (region, i, &rect);
+      glScissor (rect.x * scale, (wh - rect.y - rect.height) * scale, rect.width * scale, rect.height * 
scale);
+      glBlitFramebuffer (rect.x * scale, (wh - rect.y - rect.height) * scale, (rect.x + rect.width) * scale, 
(wh - rect.y) * scale,
+                         rect.x * scale, (wh - rect.y - rect.height) * scale, (rect.x + rect.width) * scale, 
(wh - rect.y) * scale,
+                         GL_COLOR_BUFFER_BIT, GL_NEAREST);
+    }
+}
+
+void
+_gdk_win32_gl_context_end_frame (GdkGLContext *context,
+                                 cairo_region_t *painted,
+                                 cairo_region_t *damage)
+{
+  GdkWin32GLContext *context_win32 = GDK_WIN32_GL_CONTEXT (context);
+  GdkWindow *window = gdk_gl_context_get_window (context);
+  GdkWin32Display *display = (GDK_WIN32_DISPLAY (gdk_gl_context_get_display (context)));
+
+  gboolean can_wait = display->hasWglOMLSyncControl;
+  gdk_gl_context_make_current (context);
+
+  if (context_win32->do_frame_sync)
+    {
+      guint32 end_frame_counter = 0;
+
+      if (context_win32->do_frame_sync)
+        {
+          glFinish ();
+
+          if (can_wait)
+            {
+              gint64 ust, msc, sbc;
+
+              wglGetSyncValuesOML (context_win32->gl_hdc, &ust, &msc, &sbc);
+              wglWaitForMscOML (context_win32->gl_hdc,
+                                0,
+                                2,
+                                (msc + 1) % 2,
+                                &ust, &msc, &sbc);
+            }
+        }
+    }
+
+  if (context_win32->do_blit_swap)
+    {
+      glDrawBuffer(GL_FRONT);
+      glReadBuffer(GL_BACK);
+      gdk_gl_blit_region (window, painted);
+      glDrawBuffer(GL_BACK);
+      glFlush();
+
+      if (gdk_gl_context_has_frame_terminator (context))
+        glFrameTerminatorGREMEDY ();
+    }
+  else
+    SwapBuffers (context_win32->gl_hdc);
+}
+
+void
+_gdk_win32_window_invalidate_for_new_frame (GdkWindow *window,
+                                            cairo_region_t *update_area)
+{
+  cairo_rectangle_int_t window_rect;
+  unsigned int buffer_age;
+  gboolean invalidate_all = FALSE;
+  GdkWin32GLContext *context_win32;
+  cairo_rectangle_int_t whole_window = { 0, 0, gdk_window_get_width (window), gdk_window_get_height (window) 
};
+
+  /* Minimal update is ok if we're not drawing with gl */
+  if (window->gl_paint_context == NULL)
+    return;
+
+  context_win32 = GDK_WIN32_GL_CONTEXT (window->gl_paint_context);
+  context_win32->do_blit_swap = FALSE;
+
+  if (gdk_gl_context_has_framebuffer_blit (window->gl_paint_context) &&
+      cairo_region_contains_rectangle (update_area, &whole_window) != CAIRO_REGION_OVERLAP_IN)
+    {
+      context_win32->do_blit_swap = TRUE;
+    }
+  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);
+    }
+}
+
+/* This is currently largely carried over from the stock implementation
+ * from gdkgl.c, but we need to use different formats for glTexImage2D(),
+ * meaning that this is using a software fallback.
+ *
+ * TODO: See whether it is possible to use a WGL extension to use the GPU
+ *       to render the items offscreen (pBuffers would probably not be good
+ *       due to overheads in context creations and multiple switchings).  We
+ *       can't use CreateDIBSection() for this as CreateDIBSection() will punt
+ *       use back into the stock Microsoft OpenGL 1.1 implementation, which is
+ *       not enough for our use cases here.
+ */
+static gboolean
+_gdk_win32_gl_context_texture_from_surface (GdkGLContext *paint_context,
+                                            cairo_surface_t *surface,
+                                            cairo_region_t  *region)
+{
+  cairo_surface_t *image;
+  double device_x_offset, device_y_offset;
+  cairo_rectangle_int_t rect, e;
+  int n_rects, i;
+  GdkWindow *window;
+  int unscaled_window_height;
+  unsigned int texture_id;
+  int window_scale;
+  double sx, sy;
+  float umax, vmax;
+  gboolean use_texture_rectangle;
+  guint target;
+
+  if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_WIN32)
+    return FALSE;
+
+  use_texture_rectangle = gdk_gl_context_use_texture_rectangle (paint_context);
+
+  window = gdk_gl_context_get_window (paint_context);
+  window_scale = gdk_window_get_scale_factor (window);
+  gdk_window_get_unscaled_size (window, NULL, &unscaled_window_height);
+
+  sx = sy = 1;
+  cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy);
+
+  cairo_surface_get_device_offset (surface,
+                                   &device_x_offset, &device_y_offset);
+  if (use_texture_rectangle)
+    target = GL_TEXTURE_RECTANGLE_ARB;
+  else
+    target = GL_TEXTURE_2D;
+
+  glGenTextures (1, &texture_id);
+  glBindTexture (target, texture_id);
+  glEnable (target);
+  glEnable (GL_SCISSOR_TEST);
+
+  glTexParameteri (target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  glTexParameteri (target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+  glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+  glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+  n_rects = cairo_region_num_rectangles (region);
+
+#define FLIP_Y(_y) (unscaled_window_height - (_y))
+
+  for (i = 0; i < n_rects; i++)
+    {
+      cairo_region_get_rectangle (region, i, &rect);
+
+      glScissor (rect.x * window_scale, FLIP_Y ((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 (target, 0, GL_RGBA, e.width, e.height, 0, GL_BGRA, GL_UNSIGNED_BYTE,
+                    cairo_image_surface_get_data (image));
+      glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+
+      cairo_surface_unmap_image (surface, image);
+
+      if (use_texture_rectangle)
+        {
+          umax = rect.width * sx;
+          vmax = rect.height * sy;
+        }
+      else
+        {
+          umax = 1.0;
+          vmax = 1.0;
+        }
+
+      {
+        GdkTexturedQuad quad = {
+          rect.x * window_scale, FLIP_Y(rect.y * window_scale),
+          (rect.x + rect.width) * window_scale, FLIP_Y((rect.y + rect.height) * window_scale),
+          0, 0,
+          umax, vmax,
+        };
+
+        /* We don't want to combine the quads here, because they have different textures.
+         * And we don't want to upload the unused source areas to make it one texture. */
+        gdk_gl_texture_quads (paint_context, target, 1, &quad);
+      }
+    }
+
+  glDisable (GL_SCISSOR_TEST);
+  glDisable (target);
+  glDeleteTextures (1, &texture_id);
+
+  return TRUE;
+}
+
+typedef struct
+{
+  ATOM wc_atom;
+  HWND hwnd;
+  HDC hdc;
+  HGLRC hglrc;
+  gboolean inited;
+} GdkWGLDummy;
+
+static void
+_destroy_dummy_gl_context (GdkWGLDummy dummy)
+{
+  if (dummy.hglrc != NULL)
+    {
+      if (wglGetCurrentContext () == dummy.hglrc)
+        wglMakeCurrent (NULL, NULL);
+      wglDeleteContext (dummy.hglrc);
+      dummy.hglrc = NULL;
+    }
+  if (dummy.hdc != NULL)
+    {
+      DeleteDC (dummy.hdc);
+      dummy.hdc = NULL;
+    }
+  if (dummy.hwnd != NULL)
+    {
+      DestroyWindow (dummy.hwnd);
+      dummy.hwnd = NULL;
+    }
+  if (dummy.wc_atom != 0)
+    {
+      UnregisterClass (MAKEINTATOM (dummy.wc_atom), GetModuleHandle (NULL));
+      dummy.wc_atom = 0;
+    }
+  dummy.inited = FALSE;
+}
+
+/* Yup, we need to create a dummy window for the dummy WGL context */
+static void
+_get_dummy_window_hwnd (GdkWGLDummy *dummy)
+{
+  WNDCLASSEX dummy_wc;
+  HWND dummy_hwnd;
+
+  memset (&dummy_wc, 0, sizeof (WNDCLASSEX));
+
+  dummy_wc.cbSize = sizeof( WNDCLASSEX );
+  dummy_wc.style = CS_OWNDC;
+  dummy_wc.lpfnWndProc = (WNDPROC) DefWindowProc;
+  dummy_wc.cbClsExtra = 0;
+  dummy_wc.cbWndExtra = 0;
+  dummy_wc.hInstance = GetModuleHandle( NULL );
+  dummy_wc.hIcon = 0;
+  dummy_wc.hCursor = NULL;
+  dummy_wc.hbrBackground = 0;
+  dummy_wc.lpszMenuName = 0;
+  dummy_wc.lpszClassName = "dummy";
+  dummy_wc.hIconSm = 0;
+
+  dummy->wc_atom = RegisterClassEx (&dummy_wc);
+
+  dummy->hwnd =
+    CreateWindowEx (WS_EX_APPWINDOW,
+                    MAKEINTATOM (dummy->wc_atom),
+                    "",
+                    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+                    0,
+                    0,
+                    0,
+                    0,
+                    NULL,
+                    NULL,
+                    GetModuleHandle (NULL),
+                    NULL);
+}
+
+static gboolean
+_pixel_format_is_better (const PIXELFORMATDESCRIPTOR *pfa,
+                        const PIXELFORMATDESCRIPTOR *pfb)
+{
+  /* prefer a format that has an alpha channel */
+  if (pfa->cAlphaBits == 0)
+    if (pfb->cAlphaBits > 0)
+      return TRUE;
+  else if (pfb->cAlphaBits == 0)
+    return FALSE;
+
+  /* Prefer a bigger color buffer */
+  if (pfb->cColorBits > pfa->cColorBits)
+    return TRUE;
+  else if (pfb->cColorBits < pfa->cColorBits)
+    return FALSE;
+
+  return TRUE;
+}
+
+static gint
+_get_wgl_pfd (HDC hdc,
+             const gboolean need_alpha_bits,
+             PIXELFORMATDESCRIPTOR *pfd)
+{
+  PIXELFORMATDESCRIPTOR best_pfd;
+  gint configs;
+  gint i;
+  gint best_pf = 0;
+  gboolean alpha_check;
+
+  /* whether an alpha channel is required or preferred */
+  if (need_alpha_bits)
+    alpha_check = pfd->cAlphaBits > 0 && pfd->cAlphaBits <= 32;
+  else
+    alpha_check = pfd->cAlphaBits >= 0 && pfd->cAlphaBits <= 32;
+
+  configs = DescribePixelFormat (hdc, 0, sizeof (best_pfd), NULL);
+  for (i = 1; i <= configs; i++)
+    {
+      gint config;
+
+      memset (pfd, 0, sizeof (best_pfd));
+      config = DescribePixelFormat (hdc, i, sizeof (best_pfd), pfd);
+
+      if (config != 0)
+        {
+          /* Check whether this is a better format than one we've already found */
+          if (((pfd->dwFlags &(PFD_SUPPORT_OPENGL |
+                            PFD_DRAW_TO_WINDOW |
+                            PFD_DOUBLEBUFFER |
+                            PFD_GENERIC_FORMAT)) ==
+          (PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW)) &&
+
+          pfd->iPixelType == PFD_TYPE_RGBA &&
+          pfd->cColorBits >= 16 && pfd->cColorBits <= 32 &&
+          alpha_check &&
+          (best_pf == 0 || _pixel_format_is_better (&best_pfd, pfd)))
+            {
+              best_pf = i;
+              best_pfd = *pfd;
+            }
+        }
+    }
+
+  return best_pf;
+}
+
+/* in WGL, for many OpenGL items, we need a dummy WGL context, so create
+ * one and cache it for later use
+ */
+static gint
+_gdk_init_dummy_context (GdkWGLDummy *dummy,
+                         const gboolean need_alpha_bits)
+{
+  gboolean set_pixel_format_result = FALSE;
+  gint best_idx = 0;
+  PIXELFORMATDESCRIPTOR pfd;
+
+  memset (&pfd, 0, sizeof (PIXELFORMATDESCRIPTOR));
+
+  _get_dummy_window_hwnd (dummy);
+
+  dummy->hdc = GetDC (dummy->hwnd);
+
+  best_idx = _get_wgl_pfd (dummy->hdc, need_alpha_bits, &pfd);
+
+  if (best_idx != 0)
+    set_pixel_format_result = SetPixelFormat (dummy->hdc,
+                                              best_idx,
+                                              &pfd);
+
+  if (best_idx == 0 || !set_pixel_format_result)
+    return 0;
+
+  dummy->hglrc = wglCreateContext (dummy->hdc);
+  if (dummy->hglrc == NULL)
+    return 0;
+
+  dummy->inited = TRUE;
+  return best_idx;
+}
+
+gboolean
+_gdk_win32_display_init_gl (GdkDisplay *display,
+                            const gboolean need_alpha_bits,
+                            GdkWGLDummy *dummy)
+{
+  GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (display);
+  gint glMajMinVersion;
+  GdkWindowImplWin32 *impl;
+  gint best_idx = 0;
+
+  if (display_win32->have_wgl)
+    return TRUE;
+
+  /* acquire and cache dummy Window (HWND & HDC) and
+   * dummy GL Context, it is used to query functions
+   * and used for other stuff as well
+   */
+  best_idx = _gdk_init_dummy_context (dummy, need_alpha_bits);
+
+  if (best_idx == 0 || !wglMakeCurrent (dummy->hdc, dummy->hglrc))
+    return FALSE;
+
+  display_win32->have_wgl = TRUE;
+  display_win32->gl_version = epoxy_gl_version ();
+
+  display_win32->hasWglARBCreateContext =
+    epoxy_has_wgl_extension (dummy->hdc, "WGL_ARB_create_context");
+  display_win32->hasWglEXTSwapControl =
+    epoxy_has_wgl_extension (dummy->hdc, "WGL_EXT_swap_control");
+  display_win32->hasWglOMLSyncControl =
+    epoxy_has_wgl_extension (dummy->hdc, "WGL_OML_sync_control");
+
+  GDK_NOTE (OPENGL,
+            g_print ("WGL API version %d.%d found\n"
+                     " - Vendor: %s\n"
+                     " - Checked extensions:\n"
+                     "\t* WGL_ARB_create_context: %s\n"
+                     "\t* WGL_EXT_swap_control: %s\n"
+                     "\t* WGL_OML_sync_control: %s\n",
+                     display_win32->gl_version / 10,
+                     display_win32->gl_version % 10,
+                     glGetString (GL_VENDOR),
+                     display_win32->hasWglARBCreateContext ? "yes" : "no",
+                     display_win32->hasWglEXTSwapControl ? "yes" : "no",
+                     display_win32->hasWglOMLSyncControl ? "yes" : "no"));
+
+  wglMakeCurrent (NULL, NULL);
+
+  return TRUE;
+}
+
+static HGLRC
+_create_legacy_gl_context (HDC hdc, GdkGLContext *share)
+{
+  HGLRC hglrc, hglrc2;
+
+  hglrc = wglCreateContext (hdc);
+
+  /* for legacy WGL, we can't share lists during context creation,
+   * so do so immediately afterwards
+   */
+  if (share != NULL)
+    {
+      hglrc2 = GDK_WIN32_GL_CONTEXT (share)->hglrc;
+      wglShareLists (hglrc2, hglrc);
+    }
+
+  return hglrc;
+}
+
+static HGLRC
+_create_gl3_context (HDC hdc,
+                     GdkGLContext *share,
+                     const gboolean need_alpha_bits,
+                     GdkWGLDummy *dummy)
+{
+  HGLRC hglrc;
+  GdkWin32GLContext *context_win32;
+
+  gint attribs[] = {
+    WGL_CONTEXT_PROFILE_MASK_ARB,  WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
+    WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
+    WGL_CONTEXT_MINOR_VERSION_ARB, 2,
+    0
+  };
+
+  /* Use the dummy GL Context we had earlier, if available,
+   * otherwise get a new dummy context as a dummy context is
+   * required for successfully using wglCreateContextAttribsARB()
+   */
+  if (!dummy->inited)
+    if (_gdk_init_dummy_context (dummy, need_alpha_bits) == 0)
+      return NULL;
+
+  if (!wglMakeCurrent (hdc, dummy->hglrc))
+    return NULL;
+
+  if (share != NULL)
+    context_win32 = GDK_WIN32_GL_CONTEXT (share);
+
+  hglrc = wglCreateContextAttribsARB (hdc,
+                                      share != NULL ? context_win32->hglrc : NULL,
+                                      attribs);
+
+  wglMakeCurrent (NULL, NULL);
+
+  return hglrc;
+}
+
+static gboolean
+_set_pixformat_for_hdc (HDC hdc,
+                       gint *best_idx,
+                       const gboolean need_alpha_bits)
+{
+  PIXELFORMATDESCRIPTOR pfd;
+  gboolean set_pixel_format_result = FALSE;
+
+  /* one is only allowed to call SetPixelFormat(), and so ChoosePixelFormat()
+   * one single time per window HDC
+   */
+  *best_idx = _get_wgl_pfd (hdc, need_alpha_bits, &pfd);
+  if (*best_idx != 0)
+    set_pixel_format_result = SetPixelFormat (hdc, *best_idx, &pfd);
+
+  /* ChoosePixelFormat() or SetPixelFormat() failed, bail out */
+  if (*best_idx == 0 || !set_pixel_format_result)
+    return FALSE;
+  return TRUE;
+}
+
+GdkGLContext *
+_gdk_win32_window_create_gl_context (GdkWindow *window,
+                                     gboolean attached,
+                                     GdkGLProfile profile,
+                                     GdkGLContext *share,
+                                     GError **error)
+{
+  GdkDisplay *display = gdk_window_get_display (window);
+  GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (gdk_window_get_display (window));
+  GdkWin32GLContext *context = NULL;
+  GdkVisual *visual = gdk_window_get_visual (window);
+
+  /* XXX: gdk_screen_get_rgba_visual() is not implemented on Windows, so
+   * need_alpha_bits will always be FALSE for now.
+   *
+   * Please see bug https://bugzilla.gnome.org/show_bug.cgi?id=727316
+   */
+  gboolean need_alpha_bits = (visual == gdk_screen_get_rgba_visual (gdk_display_get_default_screen 
(display)));
+
+  /* Real GL Context and Window items */
+  WNDCLASSEX wc;
+  ATOM wc_atom;
+  HWND hwnd;
+  HDC hdc;
+  HGLRC hglrc;
+  gint pixel_format;
+
+  /* for the dummy WGL contexts that is needed
+   * for acquiring function pointers for GL/WGL extensions
+   * and for creating a OpenGL 3.2+ WGL context
+   */
+  GdkWGLDummy dummy;
+  memset (&dummy, 0, sizeof (GdkWGLDummy));
+
+  if (!_gdk_win32_display_init_gl (display, need_alpha_bits, &dummy))
+    {
+      g_set_error_literal (error, GDK_GL_ERROR,
+                           GDK_GL_ERROR_NOT_AVAILABLE,
+                           _("No GL implementation is available"));
+      _destroy_dummy_gl_context (dummy);
+      return NULL;
+    }
+
+  if (profile == GDK_GL_PROFILE_3_2_CORE &&
+      !display_win32->hasWglARBCreateContext)
+    {
+      g_set_error_literal (error, GDK_GL_ERROR,
+                           GDK_GL_ERROR_UNSUPPORTED_PROFILE,
+                           _("The WGL_ARB_create_context extension "
+                             "needed to create 3.2 core profiles is not "
+                             "available"));
+      _destroy_dummy_gl_context (dummy);
+      return NULL;
+    }
+
+  hwnd = GDK_WINDOW_HWND (window);
+  hdc = GetDC (hwnd);
+
+  if (!_set_pixformat_for_hdc (hdc, &pixel_format, need_alpha_bits))
+    {
+      g_set_error_literal (error, GDK_GL_ERROR,
+                           GDK_GL_ERROR_UNSUPPORTED_FORMAT,
+                           _("No available configurations for the given pixel format"));
+      _destroy_dummy_gl_context (dummy);
+      return NULL;
+    }
+
+  if (profile == GDK_GL_PROFILE_3_2_CORE)
+    hglrc = _create_gl3_context (hdc, share, need_alpha_bits, &dummy);
+  else
+    {
+      profile = GDK_GL_PROFILE_LEGACY;
+      hglrc = _create_legacy_gl_context (hdc, share);
+    }
+
+  _destroy_dummy_gl_context (dummy);
+
+  if (hglrc == NULL)
+    {
+      g_set_error_literal (error, GDK_GL_ERROR,
+                           GDK_GL_ERROR_NOT_AVAILABLE,
+                           _("Unable to create a GL context"));
+      return NULL;
+    }
+
+  display_win32->pixel_format = pixel_format;
+  display_win32->gl_hdc = hdc;
+  display_win32->gl_hwnd = hwnd;
+
+  GDK_NOTE (OPENGL,
+            g_print ("Created WGL context[%p], pixel_format=%d\n",
+                     hglrc,
+                     pixel_format));
+
+  context = g_object_new (GDK_TYPE_WIN32_GL_CONTEXT,
+                          "display", display,
+                          "window", window,
+                          "profile", profile,
+                          "shared-context", share,
+                          NULL);
+
+  context->hglrc = hglrc;
+  context->gl_hdc = hdc;
+  context->is_attached = attached;
+
+  return GDK_GL_CONTEXT (context);
+}
+
+gboolean
+_gdk_win32_display_make_gl_context_current (GdkDisplay *display,
+                                            GdkGLContext *context)
+{
+  GdkWin32GLContext *context_win32;
+  GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (display);
+  GdkWindow *window;
+  GdkScreen *screen;
+
+  gboolean do_frame_sync = FALSE;
+
+  if (context == NULL)
+    {
+      wglMakeCurrent(NULL, NULL);
+      return TRUE;
+    }
+
+  context_win32 = GDK_WIN32_GL_CONTEXT (context);
+
+  if (!wglMakeCurrent (display_win32->gl_hdc, context_win32->hglrc))
+    {
+      GDK_NOTE (OPENGL,
+                g_print ("Making WGL context current failed\n"));
+      return FALSE;
+    }
+
+  context_win32->gl_hdc = display_win32->gl_hdc;
+
+  if (context_win32->is_attached && display_win32->hasWglEXTSwapControl)
+    {
+      window = gdk_gl_context_get_window (context);
+
+      /* If there 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);
+
+      /* XXX: gdk_screen_is_composited () is always FALSE on Windows at the moment */
+      do_frame_sync = ! gdk_screen_is_composited (screen);
+
+      if (do_frame_sync != context_win32->do_frame_sync)
+        {
+          context_win32->do_frame_sync = do_frame_sync;
+
+          if (do_frame_sync)
+            wglSwapIntervalEXT (1);
+          else
+            wglSwapIntervalEXT (0);
+        }
+    }
+
+  return TRUE;
+}
+
+/**
+ * gdk_win32_display_get_wgl_version:
+ * @display: a #GdkDisplay
+ * @major: (out): return location for the WGL major version
+ * @minor: (out): return location for the WGL minor version
+ *
+ * Retrieves the version of the WGL implementation.
+ *
+ * Returns: %TRUE if WGL is available
+ *
+ * Since: 3.16
+ */
+gboolean
+gdk_win32_display_get_wgl_version (GdkDisplay *display,
+                                   gint *major,
+                                   gint *minor)
+{
+  GdkWGLDummy dummy;
+
+  g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+
+  memset (&dummy, 0, sizeof (GdkWGLDummy));
+
+  if (!GDK_IS_WIN32_DISPLAY (display))
+    return FALSE;
+
+  if (!_gdk_win32_display_init_gl (display, FALSE, &dummy))
+    return FALSE;
+
+  _destroy_dummy_gl_context (dummy);
+
+  if (major != NULL)
+    *major = GDK_WIN32_DISPLAY (display)->gl_version / 10;
+  if (minor != NULL)
+    *minor = GDK_WIN32_DISPLAY (display)->gl_version % 10;
+
+  return TRUE;
+}
diff --git a/gdk/win32/gdkglcontext-win32.h b/gdk/win32/gdkglcontext-win32.h
new file mode 100644
index 0000000..e87d44d
--- /dev/null
+++ b/gdk/win32/gdkglcontext-win32.h
@@ -0,0 +1,82 @@
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontext-win32.h: Private Win32 specific OpenGL wrappers
+ *
+ * Copyright © 2014 Chun-wei Fan
+ *
+ * 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_WIN32_GL_CONTEXT__
+#define __GDK_WIN32_GL_CONTEXT__
+
+#include <epoxy/gl.h>
+#include <epoxy/wgl.h>
+
+#include "gdkglcontextprivate.h"
+#include "gdkdisplayprivate.h"
+#include "gdkvisual.h"
+#include "gdkwindow.h"
+#include "gdkinternals.h"
+#include "gdkmain.h"
+
+G_BEGIN_DECLS
+
+struct _GdkWin32GLContext
+{
+  GdkGLContext parent_instance;
+
+  /* WGL Context Items */
+  HGLRC hglrc;
+  HDC gl_hdc;
+
+  /* other items */
+  gint is_attached : 1;
+  gint do_frame_sync : 1;
+  gint do_blit_swap : 1;
+};
+
+struct _GdkWin32GLContextClass
+{
+  GdkGLContextClass parent_class;
+};
+
+GdkGLContext *
+_gdk_win32_window_create_gl_context (GdkWindow *window,
+                                     gboolean attached,
+                                     GdkGLProfile profile,
+                                     GdkGLContext *share,
+                                     GError **error);
+
+void
+_gdk_win32_window_invalidate_for_new_frame (GdkWindow *window,
+                                            cairo_region_t *update_area);
+
+void
+_gdk_win32_gl_context_end_frame (GdkGLContext *context,
+                                 cairo_region_t *painted,
+                                 cairo_region_t *damage);
+
+gboolean
+_gdk_win32_gl_context_texture_from_surface (GdkGLContext *paint_context,
+                                            cairo_surface_t *surface,
+                                            cairo_region_t *region);
+
+gboolean
+_gdk_win32_display_make_gl_context_current (GdkDisplay *display,
+                                            GdkGLContext *context);
+
+G_END_DECLS
+
+#endif /* __GDK_WIN32_GL_CONTEXT__ */
diff --git a/gdk/win32/gdkwin32.h b/gdk/win32/gdkwin32.h
index 5256392..4550dc7 100644
--- a/gdk/win32/gdkwin32.h
+++ b/gdk/win32/gdkwin32.h
@@ -35,6 +35,7 @@
 #include <gdk/win32/gdkwin32screen.h>
 #include <gdk/win32/gdkwin32window.h>
 #include <gdk/win32/gdkwin32misc.h>
+#include <gdk/win32/gdkwin32glcontext.h>
 
 #undef __GDKWIN32_H_INSIDE__
 
diff --git a/gdk/win32/gdkwin32glcontext.h b/gdk/win32/gdkwin32glcontext.h
new file mode 100644
index 0000000..fc6e94c
--- /dev/null
+++ b/gdk/win32/gdkwin32glcontext.h
@@ -0,0 +1,49 @@
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkglcontext-win32.c: Win32 specific OpenGL wrappers
+ *
+ * Copyright © 2014  Chun-wei Fan
+ *
+ * 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_WIN32_GL_CONTEXT_H__
+#define __GDK_WIN32_GL_CONTEXT_H__
+
+#if !defined (__GDKWIN32_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdkwin32.h> can be included directly."
+#endif
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_WIN32_GL_CONTEXT              (gdk_win32_gl_context_get_type ())
+#define GDK_WIN32_GL_CONTEXT(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GDK_TYPE_WIN32_GL_CONTEXT, GdkWin32GLContext))
+#define GDK_WIN32_IS_GL_CONTEXT(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_WIN32_GL_CONTEXT))
+
+typedef struct _GdkWin32GLContext              GdkWin32GLContext;
+typedef struct _GdkWin32GLContextClass GdkWin32GLContextClass;
+
+GDK_AVAILABLE_IN_3_16
+GType gdk_win32_gl_context_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_16
+gboolean        gdk_win32_display_get_wgl_version (GdkDisplay *display,
+                                                   gint       *major,
+                                                   gint       *minor);
+
+G_END_DECLS
+
+#endif /* __GDK_WIN32_GL_CONTEXT_H__ */
diff --git a/gdk/win32/gdkwindow-win32.c b/gdk/win32/gdkwindow-win32.c
index 7b4bd79..3e568a4 100644
--- a/gdk/win32/gdkwindow-win32.c
+++ b/gdk/win32/gdkwindow-win32.c
@@ -38,6 +38,7 @@
 #include "gdkdisplayprivate.h"
 #include "gdkvisualprivate.h"
 #include "gdkwin32window.h"
+#include "gdkglcontext-win32.h"
 
 #include <cairo-win32.h>
 
@@ -336,13 +337,15 @@ RegisterGdkClass (GdkWindowType wtype, GdkWindowTypeHint wtype_hint)
   switch (wtype)
     {
     case GDK_WINDOW_TOPLEVEL:
+      /* MSDN: CS_OWNDC is needed for OpenGL contexts */
+      wcl.style |= CS_OWNDC;
       if (0 == klassTOPLEVEL)
-       {
-         wcl.lpszClassName = L"gdkWindowToplevel";
-         
-         ONCE_PER_CLASS ();
-         klassTOPLEVEL = RegisterClassExW (&wcl);
-       }
+        {
+          wcl.lpszClassName = L"gdkWindowToplevel";
+
+          ONCE_PER_CLASS ();
+          klassTOPLEVEL = RegisterClassExW (&wcl);
+        }
       klass = klassTOPLEVEL;
       break;
       
@@ -351,6 +354,10 @@ RegisterGdkClass (GdkWindowType wtype, GdkWindowTypeHint wtype_hint)
        {
          wcl.lpszClassName = L"gdkWindowChild";
          
+         /* XXX: Find out whether GL Widgets are done for GDK_WINDOW_CHILD
+          *      MSDN says CS_PARENTDC should not be used for GL Context
+          *      creation
+          */
          wcl.style |= CS_PARENTDC; /* MSDN: ... enhances system performance. */
          ONCE_PER_CLASS ();
          klassCHILD = RegisterClassExW (&wcl);
@@ -524,8 +531,9 @@ _gdk_win32_display_create_window_impl (GdkDisplay    *display,
        }
       else
        {
+         /* MSDN: We need WS_CLIPCHILDREN and WS_CLIPSIBLINGS for GL Context Creation */
          if (window->window_type == GDK_WINDOW_TOPLEVEL)
-           dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;
+           dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
          else
            dwStyle = WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU | WS_CAPTION | WS_THICKFRAME | 
WS_CLIPCHILDREN;
 
@@ -3440,6 +3448,8 @@ gdk_window_impl_win32_class_init (GdkWindowImplWin32Class *klass)
   impl_class->get_property = _gdk_win32_window_get_property;
   impl_class->change_property = _gdk_win32_window_change_property;
   impl_class->delete_property = _gdk_win32_window_delete_property;
+  impl_class->create_gl_context = _gdk_win32_window_create_gl_context;
+  impl_class->invalidate_for_new_frame = _gdk_win32_window_invalidate_for_new_frame;
 }
 
 HGDIOBJ



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