1 /* GDK - The GIMP Drawing Kit
2  *
3  * gdkglcontext-win32.c: Win32 specific OpenGL wrappers
4  *
5  * Copyright © 2014 Emmanuele Bassi
6  * Copyright © 2014 Alexander Larsson
7  * Copyright © 2014 Chun-wei Fan
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "config.h"
24 
25 #include "gdkprivate-win32.h"
26 #include "gdkwindow-win32.h"
27 #include "gdkglcontext-win32.h"
28 #include "gdkdisplay-win32.h"
29 
30 #include "gdkwin32display.h"
31 #include "gdkwin32glcontext.h"
32 #include "gdkwin32misc.h"
33 #include "gdkwin32screen.h"
34 #include "gdkwin32window.h"
35 
36 #include "gdkglcontext.h"
37 #include "gdkwindow.h"
38 #include "gdkinternals.h"
39 #include "gdkintl.h"
40 
41 #include <cairo.h>
42 #include <epoxy/wgl.h>
43 
44 #ifdef GDK_WIN32_ENABLE_EGL
45 # include <epoxy/egl.h>
46 #endif
47 
48 struct _GdkWin32GLContext
49 {
50   GdkGLContext parent_instance;
51 
52   HDC gl_hdc;
53   guint need_alpha_bits : 1;
54   guint is_attached : 1;
55   guint do_frame_sync : 1;
56   guint do_blit_swap : 1;
57 };
58 
59 struct _GdkWin32GLContextClass
60 {
61   GdkGLContextClass parent_class;
62 };
63 
G_DEFINE_TYPE(GdkWin32GLContext,gdk_win32_gl_context,GDK_TYPE_GL_CONTEXT)64 G_DEFINE_TYPE (GdkWin32GLContext, gdk_win32_gl_context, GDK_TYPE_GL_CONTEXT)
65 
66 static void
67 gdk_win32_gl_context_cleanup (GdkGLContext *context)
68 {
69   GdkWindow *window = gdk_gl_context_get_window (context);
70 
71   if (window != NULL)
72     {
73       GdkWindowImplWin32 *impl = NULL;
74 
75       ReleaseDC (GDK_WINDOW_HWND (window), GDK_WIN32_GL_CONTEXT(context)->gl_hdc);
76       impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
77 
78       if (impl->suppress_layered > 0)
79         impl->suppress_layered--;
80 
81       /* If we don't have any window that forces layered windows off,
82        * trigger update_style_bits() to enable layered windows again
83        */
84       if (impl->suppress_layered == 0)
85         _gdk_win32_window_update_style_bits (window);
86     }
87 }
88 
89 static void
gdk_gl_blit_region(GdkWindow * window,cairo_region_t * region,gboolean use_intel_workaround)90 gdk_gl_blit_region (GdkWindow      *window,
91                     cairo_region_t *region,
92                     gboolean        use_intel_workaround)
93 {
94   int n_rects, i, j;
95   int scale = gdk_window_get_scale_factor (window);
96   int wh = gdk_window_get_height (window);
97   cairo_rectangle_int_t rect;
98   int retries = 0;
99 
100   if (use_intel_workaround)
101     retries = 1;
102 
103   n_rects = cairo_region_num_rectangles (region);
104 
105   for (i = 0; i <= retries; i ++)
106     {
107       for (j = 0; j < n_rects; j++)
108         {
109           cairo_region_get_rectangle (region, j, &rect);
110           glScissor (rect.x * scale, (wh - rect.y - rect.height) * scale, rect.width * scale, rect.height * scale);
111           glBlitFramebuffer (rect.x * scale, (wh - rect.y - rect.height) * scale, (rect.x + rect.width) * scale, (wh - rect.y) * scale,
112                              rect.x * scale, (wh - rect.y - rect.height) * scale, (rect.x + rect.width) * scale, (wh - rect.y) * scale,
113                              GL_COLOR_BUFFER_BIT, GL_NEAREST);
114         }
115 
116       if (retries > 0 && i < retries)
117         glFlush ();
118     }
119 }
120 
121 /* Section on WGL usage */
122 struct _GdkWin32GLContextWGL
123 {
124   GdkWin32GLContext parent_instance;
125 
126   HGLRC wgl_context;
127 };
128 
129 typedef struct _GdkWin32GLContextClass GdkWin32GLContextWGLClass;
130 typedef struct _GdkWin32GLContextWGL   GdkWin32GLContextWGL;
131 
132 G_DEFINE_TYPE (GdkWin32GLContextWGL, gdk_win32_gl_context_wgl, GDK_TYPE_WIN32_GL_CONTEXT)
133 
134 #define GDK_TYPE_WIN32_GL_CONTEXT_WGL     (gdk_win32_gl_context_wgl_get_type())
135 #define GDK_WIN32_GL_CONTEXT_WGL(obj)     (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_WIN32_GL_CONTEXT_WGL, GdkWin32GLContextWGL))
136 #define GDK_IS_WIN32_GL_CONTEXT_WGL(obj)  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_WIN32_GL_CONTEXT_WGL))
137 
138 typedef struct
139 {
140   ATOM wc_atom;
141   HWND hwnd;
142   HDC hdc;
143   HGLRC hglrc;
144   gboolean inited;
145 } GdkWGLDummy;
146 
147 static void
destroy_dummy_gl_context(GdkWGLDummy dummy)148 destroy_dummy_gl_context (GdkWGLDummy dummy)
149 {
150   if (dummy.hglrc != NULL)
151     {
152       wglDeleteContext (dummy.hglrc);
153       dummy.hglrc = NULL;
154     }
155   if (dummy.hdc != NULL)
156     {
157       DeleteDC (dummy.hdc);
158       dummy.hdc = NULL;
159     }
160   if (dummy.hwnd != NULL)
161     {
162       DestroyWindow (dummy.hwnd);
163       dummy.hwnd = NULL;
164     }
165   if (dummy.wc_atom != 0)
166     {
167       UnregisterClass (MAKEINTATOM (dummy.wc_atom), GetModuleHandle (NULL));
168       dummy.wc_atom = 0;
169     }
170   dummy.inited = FALSE;
171 }
172 
173 /* Yup, we need to create a dummy window for the dummy WGL context */
174 static void
get_dummy_window_hwnd(GdkWGLDummy * dummy)175 get_dummy_window_hwnd (GdkWGLDummy *dummy)
176 {
177   WNDCLASSEX dummy_wc;
178 
179   memset (&dummy_wc, 0, sizeof (WNDCLASSEX));
180 
181   dummy_wc.cbSize = sizeof( WNDCLASSEX );
182   dummy_wc.style = CS_OWNDC;
183   dummy_wc.lpfnWndProc = (WNDPROC) DefWindowProc;
184   dummy_wc.cbClsExtra = 0;
185   dummy_wc.cbWndExtra = 0;
186   dummy_wc.hInstance = GetModuleHandle( NULL );
187   dummy_wc.hIcon = 0;
188   dummy_wc.hCursor = NULL;
189   dummy_wc.hbrBackground = 0;
190   dummy_wc.lpszMenuName = 0;
191   dummy_wc.lpszClassName = "dummy";
192   dummy_wc.hIconSm = 0;
193 
194   dummy->wc_atom = RegisterClassEx (&dummy_wc);
195 
196   dummy->hwnd =
197     CreateWindowEx (WS_EX_APPWINDOW,
198                     MAKEINTATOM (dummy->wc_atom),
199                     "",
200                     WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
201                     0,
202                     0,
203                     0,
204                     0,
205                     NULL,
206                     NULL,
207                     GetModuleHandle (NULL),
208                     NULL);
209 }
210 
211 static gint
212 gdk_init_dummy_context (GdkWGLDummy    *dummy,
213                         const gboolean  need_alpha_bits);
214 
215 #define PIXEL_ATTRIBUTES 19
216 
217 static gint
get_wgl_pfd(HDC hdc,const gboolean need_alpha_bits,PIXELFORMATDESCRIPTOR * pfd,GdkWin32Display * display)218 get_wgl_pfd (HDC                    hdc,
219              const gboolean         need_alpha_bits,
220              PIXELFORMATDESCRIPTOR *pfd,
221              GdkWin32Display       *display)
222 {
223   gint best_pf = 0;
224 
225   pfd->nSize = sizeof (PIXELFORMATDESCRIPTOR);
226 
227   if (display != NULL && display->hasWglARBPixelFormat)
228     {
229       GdkWGLDummy dummy;
230       UINT num_formats;
231       gint colorbits = GetDeviceCaps (hdc, BITSPIXEL);
232       guint extra_fields = 1;
233       gint i = 0;
234       int pixelAttribs[PIXEL_ATTRIBUTES];
235       int alpha_idx = 0;
236 
237       /* Save up the HDC and HGLRC that we are currently using, to restore back to it when we are done here */
238       HDC hdc_current = wglGetCurrentDC ();
239       HGLRC hglrc_current = wglGetCurrentContext ();
240 
241       if (display->hasWglARBmultisample)
242         {
243           /* 2 pairs of values needed for multisampling/AA support */
244           extra_fields += 2 * 2;
245         }
246 
247       /* Update PIXEL_ATTRIBUTES above if any groups are added here! */
248       /* one group contains a value pair for both pixelAttribs and pixelAttribsNoAlpha */
249       pixelAttribs[i] = WGL_DRAW_TO_WINDOW_ARB;
250       pixelAttribs[i++] = GL_TRUE;
251 
252       pixelAttribs[i++] = WGL_SUPPORT_OPENGL_ARB;
253       pixelAttribs[i++] = GL_TRUE;
254 
255       pixelAttribs[i++] = WGL_DOUBLE_BUFFER_ARB;
256       pixelAttribs[i++] = GL_TRUE;
257 
258       pixelAttribs[i++] = WGL_ACCELERATION_ARB;
259       pixelAttribs[i++] = WGL_FULL_ACCELERATION_ARB;
260 
261       pixelAttribs[i++] = WGL_PIXEL_TYPE_ARB;
262       pixelAttribs[i++] = WGL_TYPE_RGBA_ARB;
263 
264       pixelAttribs[i++] = WGL_COLOR_BITS_ARB;
265       pixelAttribs[i++] = colorbits;
266 
267       /* end of "Update PIXEL_ATTRIBUTES above if any groups are added here!" */
268 
269       if (display->hasWglARBmultisample)
270         {
271           pixelAttribs[i++] = WGL_SAMPLE_BUFFERS_ARB;
272           pixelAttribs[i++] = 1;
273 
274           pixelAttribs[i++] = WGL_SAMPLES_ARB;
275           pixelAttribs[i++] = 8;
276         }
277 
278       pixelAttribs[i++] = WGL_ALPHA_BITS_ARB;
279 
280       /* track the spot where the alpha bits are, so that we can clear it if needed */
281       alpha_idx = i;
282 
283       pixelAttribs[i++] = 8;
284       pixelAttribs[i++] = 0; /* end of pixelAttribs */
285 
286       memset (&dummy, 0, sizeof (GdkWGLDummy));
287 
288       /* acquire and cache dummy Window (HWND & HDC) and
289        * dummy GL Context, we need it for wglChoosePixelFormatARB()
290        */
291       best_pf = gdk_init_dummy_context (&dummy, need_alpha_bits);
292 
293       if (best_pf == 0 || !wglMakeCurrent (dummy.hdc, dummy.hglrc))
294         {
295           wglMakeCurrent (hdc_current, hglrc_current);
296           return 0;
297         }
298 
299       wglChoosePixelFormatARB (hdc,
300                                pixelAttribs,
301                                NULL,
302                                1,
303                                &best_pf,
304                                &num_formats);
305 
306       if (best_pf == 0)
307         {
308           if (!need_alpha_bits)
309             {
310               pixelAttribs[alpha_idx] = 0;
311               pixelAttribs[alpha_idx + 1] = 0;
312 
313               /* give another chance if need_alpha_bits is FALSE,
314                * meaning we prefer to have an alpha channel anyways
315                */
316               wglChoosePixelFormatARB (hdc,
317                                        pixelAttribs,
318                                        NULL,
319                                        1,
320                                        &best_pf,
321                                        &num_formats);
322 
323             }
324         }
325 
326       /* Go back to the HDC that we were using, since we are done with the dummy HDC and GL Context */
327       wglMakeCurrent (hdc_current, hglrc_current);
328       destroy_dummy_gl_context (dummy);
329     }
330   else
331     {
332       pfd->nVersion = 1;
333       pfd->dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
334       pfd->iPixelType = PFD_TYPE_RGBA;
335       pfd->cColorBits = GetDeviceCaps (hdc, BITSPIXEL);
336       pfd->cAlphaBits = 8;
337       pfd->dwLayerMask = PFD_MAIN_PLANE;
338 
339       best_pf = ChoosePixelFormat (hdc, pfd);
340 
341       if (best_pf == 0)
342         /* give another chance if need_alpha_bits is FALSE,
343          * meaning we prefer to have an alpha channel anyways
344          */
345         if (!need_alpha_bits)
346           {
347             pfd->cAlphaBits = 0;
348             best_pf = ChoosePixelFormat (hdc, pfd);
349           }
350     }
351 
352   return best_pf;
353 }
354 
355 /* in WGL, for many OpenGL items, we need a dummy WGL context, so create
356  * one and cache it for later use
357  */
358 static gint
gdk_init_dummy_context(GdkWGLDummy * dummy,const gboolean need_alpha_bits)359 gdk_init_dummy_context (GdkWGLDummy    *dummy,
360                          const gboolean  need_alpha_bits)
361 {
362   PIXELFORMATDESCRIPTOR pfd;
363   gboolean set_pixel_format_result = FALSE;
364   gint best_idx = 0;
365 
366   get_dummy_window_hwnd (dummy);
367 
368   dummy->hdc = GetDC (dummy->hwnd);
369   memset (&pfd, 0, sizeof (PIXELFORMATDESCRIPTOR));
370 
371   best_idx = get_wgl_pfd (dummy->hdc, need_alpha_bits, &pfd, NULL);
372 
373   if (best_idx != 0)
374     set_pixel_format_result = SetPixelFormat (dummy->hdc,
375                                               best_idx,
376                                               &pfd);
377 
378   if (best_idx == 0 || !set_pixel_format_result)
379     return 0;
380 
381   dummy->hglrc = wglCreateContext (dummy->hdc);
382   if (dummy->hglrc == NULL)
383     return 0;
384 
385   dummy->inited = TRUE;
386 
387   return best_idx;
388 }
389 
390 static void
gdk_win32_gl_context_dispose_wgl(GObject * gobject)391 gdk_win32_gl_context_dispose_wgl (GObject *gobject)
392 {
393   GdkGLContext *context = GDK_GL_CONTEXT (gobject);
394   GdkWin32GLContextWGL *context_wgl = GDK_WIN32_GL_CONTEXT_WGL (context);
395 
396   if (context_wgl->wgl_context != NULL)
397     {
398       GdkWindow *window = gdk_gl_context_get_window (context);
399 
400       if (wglGetCurrentContext () == context_wgl->wgl_context)
401         wglMakeCurrent (NULL, NULL);
402 
403       GDK_NOTE (OPENGL, g_print ("Destroying WGL context\n"));
404 
405       wglDeleteContext (context_wgl->wgl_context);
406       context_wgl->wgl_context = NULL;
407 
408       gdk_win32_gl_context_cleanup (context);
409     }
410 
411   G_OBJECT_CLASS (gdk_win32_gl_context_parent_class)->dispose (gobject);
412 }
413 
414 static void
gdk_win32_gl_context_end_frame_wgl(GdkGLContext * context,cairo_region_t * painted,cairo_region_t * damage)415 gdk_win32_gl_context_end_frame_wgl (GdkGLContext   *context,
416                                     cairo_region_t *painted,
417                                     cairo_region_t *damage)
418 {
419   GdkWin32Display *display_win32 = (GDK_WIN32_DISPLAY (gdk_gl_context_get_display (context)));
420   GdkWin32GLContext *context_win32 = GDK_WIN32_GL_CONTEXT (context);
421 
422   gdk_gl_context_make_current (context);
423 
424   if (context_win32->do_frame_sync)
425     {
426       glFinish ();
427 
428       if (display_win32->hasWglOMLSyncControl)
429         {
430           gint64 ust, msc, sbc;
431 
432           wglGetSyncValuesOML (context_win32->gl_hdc, &ust, &msc, &sbc);
433           wglWaitForMscOML (context_win32->gl_hdc,
434                             0,
435                             2,
436                             (msc + 1) % 2,
437                             &ust, &msc, &sbc);
438         }
439     }
440 
441   if (context_win32->do_blit_swap)
442     {
443       GdkWindow *window = gdk_gl_context_get_window (context);
444 
445       glDrawBuffer(GL_FRONT);
446       glReadBuffer(GL_BACK);
447       gdk_gl_blit_region (window, painted, display_win32->needIntelGLWorkaround);
448       glDrawBuffer(GL_BACK);
449       glFlush();
450 
451       if (gdk_gl_context_has_frame_terminator (context))
452         glFrameTerminatorGREMEDY ();
453     }
454   else
455     SwapBuffers (context_win32->gl_hdc);
456 }
457 
458 /*
459  * We need to check whether the OpenGL driver is capable of delvering
460  * what we need to use in GdkGLContext.  This function in the future
461  * will also check for blacklisted drivers, if needed.  If things fail
462  * here, we can try falling back to OpenGL/ES (libANGLE) if that is enabled.
463  */
464 static gboolean
gdk_win32_gl_context_wgl_check_capabilities(GdkWin32Display * display_win32,GdkWGLDummy dummy)465 gdk_win32_gl_context_wgl_check_capabilities (GdkWin32Display *display_win32,
466                                              GdkWGLDummy      dummy)
467 {
468   if (!wglMakeCurrent (dummy.hdc, dummy.hglrc))
469     return FALSE;
470 
471   display_win32->gl_version = epoxy_gl_version ();
472 
473   /* We must have OpenGL/WGL 2.0 or later, or have the GL_ARB_shader_objects extension */
474   if (display_win32->gl_version < 20 && !epoxy_has_gl_extension ("GL_ARB_shader_objects"))
475     return FALSE;
476 
477   return TRUE;
478 }
479 
480 static void
gdk_win32_display_init_wgl(GdkWin32Display * display_win32,const gboolean need_alpha_bits)481 gdk_win32_display_init_wgl (GdkWin32Display *display_win32,
482                             const gboolean   need_alpha_bits)
483 {
484   /* acquire and cache dummy Window (HWND & HDC) and
485    * dummy GL Context, it is used to query functions
486    * and used for other stuff as well
487    */
488   GdkWGLDummy dummy;
489   gint best_idx = 0;
490   gboolean proceed = TRUE;
491 
492   memset (&dummy, 0, sizeof (GdkWGLDummy));
493 
494   best_idx = gdk_init_dummy_context (&dummy, need_alpha_bits);
495 
496   if (best_idx != 0)
497     proceed = gdk_win32_gl_context_wgl_check_capabilities (display_win32, dummy);
498 
499   if (best_idx != 0 && proceed)
500     {
501       display_win32->hasWglARBCreateContext =
502         epoxy_has_wgl_extension (dummy.hdc, "WGL_ARB_create_context");
503       display_win32->hasWglEXTSwapControl =
504         epoxy_has_wgl_extension (dummy.hdc, "WGL_EXT_swap_control");
505       display_win32->hasWglOMLSyncControl =
506         epoxy_has_wgl_extension (dummy.hdc, "WGL_OML_sync_control");
507       display_win32->hasWglARBPixelFormat =
508         epoxy_has_wgl_extension (dummy.hdc, "WGL_ARB_pixel_format");
509       display_win32->hasWglARBmultisample =
510         epoxy_has_wgl_extension (dummy.hdc, "WGL_ARB_multisample");
511       display_win32->needIntelGLWorkaround =
512         (g_ascii_strcasecmp (glGetString (GL_VENDOR), "intel") == 0);
513 
514       GDK_NOTE (OPENGL,
515                 g_print ("WGL API version %d.%d found\n"
516                          " - Vendor: %s\n"
517                          " - Intel OpenGL workaround: %s\n"
518                          " - Checked extensions:\n"
519                          "\t* WGL_ARB_pixel_format: %s\n"
520                          "\t* WGL_ARB_create_context: %s\n"
521                          "\t* WGL_EXT_swap_control: %s\n"
522                          "\t* WGL_OML_sync_control: %s\n"
523                          "\t* WGL_ARB_multisample: %s\n",
524                          display_win32->gl_version / 10,
525                          display_win32->gl_version % 10,
526                          glGetString (GL_VENDOR),
527                          display_win32->needIntelGLWorkaround ? "yes" : "no",
528                          display_win32->hasWglARBPixelFormat ? "yes" : "no",
529                          display_win32->hasWglARBCreateContext ? "yes" : "no",
530                          display_win32->hasWglEXTSwapControl ? "yes" : "no",
531                          display_win32->hasWglOMLSyncControl ? "yes" : "no",
532                          display_win32->hasWglARBmultisample ? "yes" : "no"));
533 
534       display_win32->gl_type = GDK_WIN32_GL_WGL;
535     }
536   else
537     display_win32->gl_type = GDK_WIN32_GL_NONE;
538 
539   /* clean up things if we indeed created our dummy WGL context */
540   if (best_idx != 0)
541     {
542       wglMakeCurrent (NULL, NULL);
543       destroy_dummy_gl_context (dummy);
544     }
545 }
546 
547 /* Setup the legacy WGL context after creating it */
548 static gboolean
ensure_legacy_wgl_context(HDC hdc,HGLRC hglrc_legacy,GdkGLContext * share)549 ensure_legacy_wgl_context (HDC           hdc,
550                            HGLRC         hglrc_legacy,
551                            GdkGLContext *share)
552 {
553   if (!wglMakeCurrent (hdc, hglrc_legacy))
554     return FALSE;
555 
556   if (share != NULL)
557     {
558       GdkWin32GLContextWGL *context_wgl = GDK_WIN32_GL_CONTEXT_WGL (share);
559 
560       return wglShareLists (hglrc_legacy, context_wgl->wgl_context);
561     }
562 
563   return TRUE;
564 }
565 
566 static HGLRC
create_wgl_context_with_attribs(HDC hdc,HGLRC hglrc_base,GdkGLContext * share,int flags,int major,int minor,gboolean * is_legacy)567 create_wgl_context_with_attribs (HDC           hdc,
568                                  HGLRC         hglrc_base,
569                                  GdkGLContext *share,
570                                  int           flags,
571                                  int           major,
572                                  int           minor,
573                                  gboolean     *is_legacy)
574 {
575   HGLRC hglrc;
576   GdkWin32GLContextWGL *context_wgl;
577 
578   /* if we have wglCreateContextAttribsARB(), create a
579   * context with the compatibility profile if a legacy
580   * context is requested, or when we go into fallback mode
581   */
582   int profile = *is_legacy ? WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :
583                              WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
584 
585   int attribs[] = {
586     WGL_CONTEXT_PROFILE_MASK_ARB,   profile,
587     WGL_CONTEXT_MAJOR_VERSION_ARB, *is_legacy ? 3 : major,
588     WGL_CONTEXT_MINOR_VERSION_ARB, *is_legacy ? 0 : minor,
589     WGL_CONTEXT_FLAGS_ARB,          flags,
590     0
591   };
592 
593   if (share != NULL)
594     context_wgl= GDK_WIN32_GL_CONTEXT_WGL (share);
595 
596   hglrc = wglCreateContextAttribsARB (hdc,
597                                       share != NULL ? context_wgl->wgl_context : NULL,
598                                       attribs);
599 
600   return hglrc;
601 }
602 
603 static HGLRC
create_wgl_context(HDC hdc,GdkGLContext * share,int flags,int major,int minor,gboolean * is_legacy,gboolean hasWglARBCreateContext)604 create_wgl_context (HDC           hdc,
605                     GdkGLContext *share,
606                     int           flags,
607                     int           major,
608                     int           minor,
609                     gboolean     *is_legacy,
610                     gboolean      hasWglARBCreateContext)
611 {
612   /* We need a legacy WGL context for *all* cases */
613   HGLRC hglrc_base = wglCreateContext (hdc);
614   gboolean success = TRUE;
615 
616   /* Save up the HDC and HGLRC that we are currently using, to restore back to it when we are done here  */
617   HDC hdc_current = wglGetCurrentDC ();
618   HGLRC hglrc_current = wglGetCurrentContext ();
619 
620   /* if we have no wglCreateContextAttribsARB(), return the legacy context when all is set */
621   if (*is_legacy && !hasWglARBCreateContext)
622     {
623       if (ensure_legacy_wgl_context (hdc, hglrc_base, share))
624         {
625           wglMakeCurrent (hdc_current, hglrc_current);
626           return hglrc_base;
627         }
628 
629       success = FALSE;
630       goto gl_fail;
631     }
632   else
633     {
634       HGLRC hglrc;
635 
636       if (!wglMakeCurrent (hdc, hglrc_base))
637         {
638           success = FALSE;
639           goto gl_fail;
640         }
641 
642       hglrc = create_wgl_context_with_attribs (hdc,
643                                                hglrc_base,
644                                                share,
645                                                flags,
646                                                major,
647                                                minor,
648                                                is_legacy);
649 
650       /* return the legacy context we have if it could be setup properly, in case the 3.0+ context creation failed */
651       if (hglrc == NULL)
652         {
653           if (!(*is_legacy))
654             {
655               /* If we aren't using a legacy context in the beginning, try again with a compatibility profile 3.0 context */
656               hglrc = create_wgl_context_with_attribs (hdc,
657                                                        hglrc_base,
658                                                        share,
659                                                        flags,
660                                                        0, 0,
661                                                        is_legacy);
662 
663               *is_legacy = TRUE;
664             }
665 
666           if (hglrc == NULL)
667             {
668               if (!ensure_legacy_wgl_context (hdc, hglrc_base, share))
669                 success = FALSE;
670             }
671 
672           if (success)
673             GDK_NOTE (OPENGL, g_print ("Using legacy context as fallback\n"));
674         }
675 
676 gl_fail:
677 
678       if (!success)
679         {
680           wglMakeCurrent (NULL, NULL);
681           wglDeleteContext (hglrc_base);
682           return NULL;
683         }
684 
685       wglMakeCurrent (hdc_current, hglrc_current);
686 
687       if (hglrc != NULL)
688         {
689           wglDeleteContext (hglrc_base);
690           return hglrc;
691         }
692 
693       return hglrc_base;
694   }
695 }
696 
697 static gboolean
set_pixformat_for_hdc(HDC hdc,gint * best_idx,const gboolean need_alpha_bits,GdkWin32Display * display)698 set_pixformat_for_hdc (HDC              hdc,
699                        gint            *best_idx,
700                        const gboolean   need_alpha_bits,
701                        GdkWin32Display *display)
702 {
703   gboolean already_checked = TRUE;
704   *best_idx = GetPixelFormat (hdc);
705 
706   /* one is only allowed to call SetPixelFormat(), and so ChoosePixelFormat()
707    * one single time per window HDC
708    */
709   if (*best_idx == 0)
710     {
711       PIXELFORMATDESCRIPTOR pfd;
712       gboolean set_pixel_format_result = FALSE;
713 
714       GDK_NOTE (OPENGL, g_print ("requesting pixel format...\n"));
715 	  already_checked = FALSE;
716       *best_idx = get_wgl_pfd (hdc, need_alpha_bits, &pfd, display);
717 
718       if (*best_idx != 0)
719         set_pixel_format_result = SetPixelFormat (hdc, *best_idx, &pfd);
720 
721       /* ChoosePixelFormat() or SetPixelFormat() failed, bail out */
722       if (*best_idx == 0 || !set_pixel_format_result)
723         return FALSE;
724     }
725 
726   GDK_NOTE (OPENGL, g_print ("%s""requested and set pixel format: %d\n", already_checked ? "already " : "", *best_idx));
727 
728   return TRUE;
729 }
730 
731 static gboolean
gdk_win32_gl_context_realize_wgl(GdkGLContext * context,GError ** error)732 gdk_win32_gl_context_realize_wgl (GdkGLContext  *context,
733                                   GError       **error)
734 {
735   GdkGLContext *share = gdk_gl_context_get_shared_context (context);
736   GdkWin32GLContext *context_win32 = GDK_WIN32_GL_CONTEXT (context);
737   GdkWindow *window = gdk_gl_context_get_window (context);
738   GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
739   GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (gdk_window_get_display (window));
740 
741   /* These are the real WGL context items that we will want to use later */
742   gboolean debug_bit, compat_bit;
743   gboolean legacy_bit = FALSE;
744   HGLRC hglrc;
745   gint pixel_format;
746 
747   /* request flags and specific versions for core (3.2+) WGL context */
748   gint flags = 0;
749   gint major = 0;
750   gint minor = 0;
751 
752   /*
753    * A legacy context cannot be shared with core profile ones, this means we
754    * must stick to a legacy context if the shared context is a legacy context.  We
755    * also use a legacy context if we use GDK_GL=legacy or if we do not have
756    * the WGL_ARB_create_context extension
757    */
758   if ((_gdk_gl_flags & GDK_GL_LEGACY) != 0 ||
759       !display_win32->hasWglARBCreateContext ||
760       share != NULL && gdk_gl_context_is_legacy (share))
761     legacy_bit = TRUE;
762 
763   /* say early enough that we are not using GLES, so that we acquire the correct versions */
764   gdk_gl_context_set_use_es (context, FALSE);
765 
766   gdk_gl_context_get_required_version (context, &major, &minor);
767   debug_bit = gdk_gl_context_get_debug_enabled (context);
768   compat_bit = gdk_gl_context_get_forward_compatible (context);
769 
770   if (!set_pixformat_for_hdc (context_win32->gl_hdc,
771                              &pixel_format,
772                               context_win32->need_alpha_bits,
773                               display_win32))
774     {
775       g_set_error_literal (error, GDK_GL_ERROR,
776                            GDK_GL_ERROR_UNSUPPORTED_FORMAT,
777                            _("No available configurations for the given pixel format"));
778 
779       return FALSE;
780     }
781 
782   if (debug_bit)
783     flags |= WGL_CONTEXT_DEBUG_BIT_ARB;
784   if (compat_bit)
785     flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
786 
787   GDK_NOTE (OPENGL,
788             g_print ("Creating %s WGL context (version:%d.%d, debug:%s, forward:%s, legacy: %s)\n",
789                      compat_bit ? "core" : "compat",
790                      major,
791                      minor,
792                      debug_bit ? "yes" : "no",
793                      compat_bit ? "yes" : "no",
794                      legacy_bit ? "yes" : "no"));
795 
796   hglrc = create_wgl_context (context_win32->gl_hdc,
797                               share,
798                               flags,
799                               major,
800                               minor,
801                              &legacy_bit,
802                               display_win32->hasWglARBCreateContext);
803 
804   if (hglrc == NULL)
805     {
806       g_set_error_literal (error, GDK_GL_ERROR,
807                            GDK_GL_ERROR_NOT_AVAILABLE,
808                            _("Unable to create a GL context"));
809       return FALSE;
810     }
811 
812   GDK_NOTE (OPENGL,
813             g_print ("Created WGL context[%p], pixel_format=%d\n",
814                      hglrc,
815                      pixel_format));
816 
817   GDK_WIN32_GL_CONTEXT_WGL (context)->wgl_context = hglrc;
818 
819   /* OpenGL does not work with WS_EX_LAYERED enabled, so we need to
820    * disable WS_EX_LAYERED when we acquire a valid HGLRC
821    */
822   impl->suppress_layered++;
823 
824   /* if this is the first time a GL context is acquired for the window,
825    * disable layered windows by triggering update_style_bits()
826    */
827   if (impl->suppress_layered == 1)
828     _gdk_win32_window_update_style_bits (window);
829 
830   /* Ensure that any other context is created with a legacy bit set */
831   gdk_gl_context_set_is_legacy (context, legacy_bit);
832 
833   return TRUE;
834 }
835 
836 static gboolean
gdk_win32_display_make_wgl_context_current(GdkDisplay * display,GdkGLContext * context)837 gdk_win32_display_make_wgl_context_current (GdkDisplay   *display,
838                                             GdkGLContext *context)
839 {
840   GdkWin32GLContext *context_win32;
841   GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (display);
842   GdkWindow *window;
843 
844   if (context == NULL)
845     {
846       wglMakeCurrent(NULL, NULL);
847 
848       return TRUE;
849     }
850 
851   context_win32 = GDK_WIN32_GL_CONTEXT (context);
852   window = gdk_gl_context_get_window (context);
853 
854   if (!wglMakeCurrent (context_win32->gl_hdc,
855                        GDK_WIN32_GL_CONTEXT_WGL (context)->wgl_context))
856     {
857       GDK_NOTE (OPENGL, g_print ("Making WGL context current failed\n"));
858       return FALSE;
859     }
860 
861   if (context_win32->is_attached)
862     {
863       if (display_win32->hasWglEXTSwapControl)
864         {
865           /* If there is compositing there is no particular need to delay
866            * the swap when drawing on the offscreen, rendering to the screen
867            * happens later anyway, and its up to the compositor to sync that
868            * to the vblank. */
869           gboolean do_frame_sync = !gdk_screen_is_composited (gdk_window_get_screen (window));
870 
871           if (do_frame_sync != context_win32->do_frame_sync)
872             {
873               context_win32->do_frame_sync = do_frame_sync;
874 
875               if (do_frame_sync)
876                 wglSwapIntervalEXT (1);
877               else
878                 wglSwapIntervalEXT (0);
879             }
880         }
881     }
882 
883   return TRUE;
884 }
885 
886 static void
gdk_win32_gl_context_wgl_class_init(GdkWin32GLContextWGLClass * klass)887 gdk_win32_gl_context_wgl_class_init (GdkWin32GLContextWGLClass *klass)
888 {
889   GdkGLContextClass *context_class = GDK_GL_CONTEXT_CLASS (klass);
890   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
891 
892   context_class->end_frame = gdk_win32_gl_context_end_frame_wgl;
893   context_class->realize = gdk_win32_gl_context_realize_wgl;
894 
895   gobject_class->dispose = gdk_win32_gl_context_dispose_wgl;
896 }
897 
898 static void
gdk_win32_gl_context_wgl_init(GdkWin32GLContextWGL * wgl_context)899 gdk_win32_gl_context_wgl_init (GdkWin32GLContextWGL *wgl_context)
900 {
901 }
902 
903 /* End section on WGL support */
904 /* Section on EGL/libANGLE support */
905 #ifdef GDK_WIN32_ENABLE_EGL
906 
907 #ifndef EGL_PLATFORM_ANGLE_ANGLE
908 #define EGL_PLATFORM_ANGLE_ANGLE          0x3202
909 #endif
910 
911 #ifndef EGL_PLATFORM_ANGLE_TYPE_ANGLE
912 #define EGL_PLATFORM_ANGLE_TYPE_ANGLE     0x3203
913 #endif
914 
915 #ifndef EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE
916 #define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208
917 #endif
918 
919 struct _GdkWin32GLContextEGL
920 {
921   GdkWin32GLContext parent_instance;
922 
923   EGLContext egl_context;
924 };
925 
926 #define GDK_TYPE_WIN32_GL_CONTEXT_EGL     (gdk_win32_gl_context_egl_get_type())
927 #define GDK_WIN32_GL_CONTEXT_EGL(obj)     (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_WIN32_GL_CONTEXT_EGL, GdkWin32GLContextEGL))
928 #define GDK_IS_WIN32_GL_CONTEXT_EGL(obj)  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_WIN32_GL_CONTEXT_EGL))
929 
930 typedef struct _GdkWin32GLContextClass GdkWin32GLContextEGLClass;
931 typedef struct _GdkWin32GLContextEGL      GdkWin32GLContextEGL;
932 
G_DEFINE_TYPE(GdkWin32GLContextEGL,gdk_win32_gl_context_egl,GDK_TYPE_WIN32_GL_CONTEXT)933 G_DEFINE_TYPE (GdkWin32GLContextEGL, gdk_win32_gl_context_egl, GDK_TYPE_WIN32_GL_CONTEXT)
934 
935 static EGLDisplay
936 gdk_win32_get_egl_display (GdkWin32Display *display)
937 {
938   EGLDisplay disp;
939   gboolean success = FALSE;
940 
941   if (epoxy_has_egl_extension (NULL, "EGL_EXT_platform_base"))
942     {
943       PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplay = (void *) eglGetProcAddress ("eglGetPlatformDisplayEXT");
944       if (getPlatformDisplay)
945         {
946           EGLint disp_attr[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, EGL_NONE};
947 
948           disp = getPlatformDisplay (EGL_PLATFORM_ANGLE_ANGLE, display->hdc_egl_temp, disp_attr);
949 
950           if (disp != EGL_NO_DISPLAY)
951             return disp;
952         }
953     }
954 
955   return eglGetDisplay (display->hdc_egl_temp);
956 }
957 
958 static void
gdk_win32_gl_context_dispose_egl(GObject * gobject)959 gdk_win32_gl_context_dispose_egl (GObject *gobject)
960 {
961   GdkGLContext *context = GDK_GL_CONTEXT (gobject);
962   GdkWin32GLContextEGL *context_egl = GDK_WIN32_GL_CONTEXT_EGL (context);
963 
964   if (context_egl->egl_context != EGL_NO_CONTEXT)
965     {
966       GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (gdk_gl_context_get_display (context));
967 
968       if (eglGetCurrentContext () == context_egl->egl_context)
969         eglMakeCurrent(display_win32->egl_disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
970 
971       GDK_NOTE (OPENGL, g_message ("Destroying EGL (ANGLE) context"));
972 
973       eglDestroyContext (display_win32->egl_disp, context_egl->egl_context);
974       context_egl->egl_context = EGL_NO_CONTEXT;
975 
976       gdk_win32_gl_context_cleanup (context);
977     }
978 
979   G_OBJECT_CLASS (gdk_win32_gl_context_parent_class)->dispose (gobject);
980 }
981 
982 static gboolean
get_is_egl_force_redraw(GdkWindow * window)983 get_is_egl_force_redraw (GdkWindow *window)
984 {
985   /* We only need to call gdk_window_invalidate_rect () if necessary */
986   if (window->gl_paint_context != NULL && gdk_gl_context_get_use_es (window->gl_paint_context))
987     {
988       GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
989 
990       return impl->egl_force_redraw_all;
991     }
992 
993   return FALSE;
994 }
995 
996 static void
reset_egl_force_redraw(GdkWindow * window)997 reset_egl_force_redraw (GdkWindow *window)
998 {
999   if (window->gl_paint_context != NULL && gdk_gl_context_get_use_es (window->gl_paint_context))
1000     {
1001       GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
1002 
1003       if (impl->egl_force_redraw_all)
1004         impl->egl_force_redraw_all = FALSE;
1005     }
1006 }
1007 
1008 static void
gdk_win32_gl_context_end_frame_egl(GdkGLContext * context,cairo_region_t * painted,cairo_region_t * damage)1009 gdk_win32_gl_context_end_frame_egl (GdkGLContext *context,
1010                                     cairo_region_t *painted,
1011                                     cairo_region_t *damage)
1012 {
1013   GdkWindow *window = gdk_gl_context_get_window (context);
1014   GdkWin32Display *display_win32 = (GDK_WIN32_DISPLAY (gdk_gl_context_get_display (context)));
1015   GdkWin32GLContext *context_win32 = GDK_WIN32_GL_CONTEXT (context);
1016 
1017   EGLSurface egl_surface = _gdk_win32_window_get_egl_surface (window, display_win32->egl_config, FALSE);
1018   gboolean force_egl_redraw_all = get_is_egl_force_redraw (window);
1019 
1020   gdk_gl_context_make_current (context);
1021 
1022   if (context_win32->do_blit_swap && !force_egl_redraw_all)
1023     gdk_gl_blit_region (window, painted, FALSE);
1024   else if (force_egl_redraw_all)
1025     {
1026       GdkRectangle rect = {0, 0, gdk_window_get_width (window), gdk_window_get_height (window)};
1027 
1028       /* We need to do gdk_window_invalidate_rect() so that we don't get glitches after maximizing or
1029        *  restoring or using aerosnap
1030        */
1031       gdk_window_invalidate_rect (window, &rect, TRUE);
1032       reset_egl_force_redraw (window);
1033     }
1034 
1035   eglSwapBuffers (display_win32->egl_disp, egl_surface);
1036 }
1037 
1038 static void
gdk_win32_display_init_egl(GdkWin32Display * display_win32)1039 gdk_win32_display_init_egl (GdkWin32Display *display_win32)
1040 {
1041   EGLDisplay egl_disp;
1042 
1043   if (display_win32->gl_type == GDK_WIN32_GL_NONE)
1044     GDK_NOTE (OPENGL, g_message ("Falling back to GLES..."));
1045 
1046   egl_disp = gdk_win32_get_egl_display (display_win32);
1047 
1048   if (egl_disp == EGL_NO_DISPLAY ||
1049      !eglInitialize (egl_disp, NULL, NULL))
1050     {
1051       if (egl_disp != EGL_NO_DISPLAY)
1052         {
1053           eglTerminate (egl_disp);
1054           egl_disp = EGL_NO_DISPLAY;
1055         }
1056 
1057       display_win32->gl_type = GDK_WIN32_GL_NONE;
1058       return;
1059     }
1060 
1061   display_win32->egl_disp = egl_disp;
1062   display_win32->gl_version = epoxy_egl_version (egl_disp);
1063 
1064   eglBindAPI(EGL_OPENGL_ES_API);
1065 
1066   display_win32->hasEglSurfacelessContext =
1067     epoxy_has_egl_extension (egl_disp, "EGL_KHR_surfaceless_context");
1068 
1069   GDK_NOTE (OPENGL,
1070             g_print ("EGL API version %d.%d found\n"
1071                      " - Vendor: %s\n"
1072                      " - Checked extensions:\n"
1073                      "\t* EGL_KHR_surfaceless_context: %s\n",
1074                      display_win32->gl_version / 10,
1075                      display_win32->gl_version % 10,
1076                      eglQueryString (display_win32->egl_disp, EGL_VENDOR),
1077                      display_win32->hasEglSurfacelessContext ? "yes" : "no"));
1078 
1079   display_win32->gl_type = GDK_WIN32_GL_EGL;
1080 }
1081 
1082 #define MAX_EGL_ATTRS   30
1083 
1084 static gboolean
find_eglconfig_for_window(GdkWin32Display * display_win32,gboolean need_alpha_bits,GError ** error)1085 find_eglconfig_for_window (GdkWin32Display  *display_win32,
1086                            gboolean          need_alpha_bits,
1087                            GError          **error)
1088 {
1089   EGLint attrs[MAX_EGL_ATTRS];
1090   EGLint count;
1091   EGLConfig *configs, chosen_config;
1092 
1093   int i = 0;
1094 
1095   EGLDisplay egl_disp;
1096 
1097   if (display_win32->egl_min_swap_interval != 0 &&
1098       display_win32->egl_config != NULL)
1099     return TRUE;
1100 
1101   egl_disp = display_win32->egl_disp;
1102 
1103   attrs[i++] = EGL_CONFORMANT;
1104   attrs[i++] = EGL_OPENGL_ES2_BIT;
1105   attrs[i++] = EGL_SURFACE_TYPE;
1106   attrs[i++] = EGL_WINDOW_BIT;
1107 
1108   attrs[i++] = EGL_COLOR_BUFFER_TYPE;
1109   attrs[i++] = EGL_RGB_BUFFER;
1110 
1111   attrs[i++] = EGL_RED_SIZE;
1112   attrs[i++] = 1;
1113   attrs[i++] = EGL_GREEN_SIZE;
1114   attrs[i++] = 1;
1115   attrs[i++] = EGL_BLUE_SIZE;
1116   attrs[i++] = 1;
1117 
1118   if (need_alpha_bits)
1119     {
1120       attrs[i++] = EGL_ALPHA_SIZE;
1121       attrs[i++] = 1;
1122     }
1123   else
1124     {
1125       attrs[i++] = EGL_ALPHA_SIZE;
1126       attrs[i++] = EGL_DONT_CARE;
1127     }
1128 
1129   attrs[i++] = EGL_NONE;
1130   g_assert (i < MAX_EGL_ATTRS);
1131 
1132   if (!eglChooseConfig (display_win32->egl_disp, attrs, NULL, 0, &count) || count < 1)
1133     {
1134       g_set_error_literal (error, GDK_GL_ERROR,
1135                            GDK_GL_ERROR_UNSUPPORTED_FORMAT,
1136                            _("No available configurations for the given pixel format"));
1137       return FALSE;
1138     }
1139 
1140   configs = g_new (EGLConfig, count);
1141 
1142   if (!eglChooseConfig (display_win32->egl_disp, attrs, configs, count, &count) || count < 1)
1143     {
1144       g_set_error_literal (error, GDK_GL_ERROR,
1145                            GDK_GL_ERROR_UNSUPPORTED_FORMAT,
1146                            _("No available configurations for the given pixel format"));
1147       return FALSE;
1148     }
1149 
1150   /* Pick first valid configuration i guess? */
1151   chosen_config = configs[0];
1152 
1153   if (!eglGetConfigAttrib (display_win32->egl_disp, chosen_config,
1154                            EGL_MIN_SWAP_INTERVAL, &display_win32->egl_min_swap_interval))
1155     {
1156       g_set_error_literal (error, GDK_GL_ERROR,
1157                            GDK_GL_ERROR_NOT_AVAILABLE,
1158                            "Could not retrieve the minimum swap interval");
1159       g_free (configs);
1160       return FALSE;
1161     }
1162 
1163   display_win32->egl_config = chosen_config;
1164 
1165   g_free (configs);
1166 
1167   return TRUE;
1168 }
1169 
1170 #define N_EGL_ATTRS     16
1171 
1172 static EGLContext
create_egl_context(EGLDisplay display,EGLConfig config,GdkGLContext * share,int flags,int major,int minor)1173 create_egl_context (EGLDisplay    display,
1174                     EGLConfig     config,
1175                     GdkGLContext *share,
1176                     int           flags,
1177                     int           major,
1178                     int           minor)
1179 {
1180   EGLContext ctx;
1181   EGLint context_attribs[N_EGL_ATTRS];
1182   int i = 0;
1183 
1184   /*
1185    * ANGLE does not support the GL_OES_vertex_array_object extension, so we need to use ES3 directly
1186    * if we do not request for ES3 or later
1187    */
1188   if (major < 3)
1189     {
1190       context_attribs[i++] = EGL_CONTEXT_CLIENT_VERSION;
1191       context_attribs[i++] = 3;
1192     }
1193 
1194   /* Specify the flags */
1195   context_attribs[i++] = EGL_CONTEXT_FLAGS_KHR;
1196   context_attribs[i++] = flags;
1197 
1198   context_attribs[i++] = EGL_NONE;
1199   g_assert (i < N_EGL_ATTRS);
1200 
1201   ctx = eglCreateContext (display,
1202                           config,
1203                           share != NULL ? GDK_WIN32_GL_CONTEXT_EGL (share)->egl_context
1204                                         : EGL_NO_CONTEXT,
1205                           context_attribs);
1206 
1207   return ctx;
1208 }
1209 
1210 static gboolean
gdk_win32_gl_context_realize_egl(GdkGLContext * context,GError ** error)1211 gdk_win32_gl_context_realize_egl (GdkGLContext  *context,
1212                                   GError       **error)
1213 {
1214   GdkGLContext *share = gdk_gl_context_get_shared_context (context);
1215   GdkWin32GLContext *context_win32 = GDK_WIN32_GL_CONTEXT (context);
1216   GdkWindow *window = gdk_gl_context_get_window (context);
1217   GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
1218   GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (gdk_window_get_display (window));
1219 
1220   /* These are the real EGL context items that we will want to use later */
1221   EGLContext ctx;
1222   gboolean debug_bit, compat_bit;
1223   gboolean legacy_bit = FALSE;
1224 
1225   /* request flags and specific versions for EGL context */
1226   gint flags = 0;
1227   gint major = 0;
1228   gint minor = 0;
1229 
1230   /* we are using GLES, so set early so that we get the correct GLES versions to request */
1231   gdk_gl_context_set_use_es (context, TRUE);
1232 
1233   gdk_gl_context_get_required_version (context, &major, &minor);
1234   debug_bit = gdk_gl_context_get_debug_enabled (context);
1235   compat_bit = gdk_gl_context_get_forward_compatible (context);
1236 
1237   if (debug_bit)
1238     flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
1239   if (compat_bit)
1240     flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;
1241 
1242   GDK_NOTE (OPENGL, g_message ("Creating EGL context version %d.%d (debug:%s, forward:%s, legacy:%s)",
1243                                major, minor,
1244                                debug_bit ? "yes" : "no",
1245                                compat_bit ? "yes" : "no"));
1246 
1247   ctx = create_egl_context (display_win32->egl_disp,
1248                             display_win32->egl_config,
1249                             share,
1250                             flags,
1251                             major,
1252                             minor);
1253 
1254   if (ctx == EGL_NO_CONTEXT)
1255     {
1256       g_set_error_literal (error, GDK_GL_ERROR,
1257                            GDK_GL_ERROR_NOT_AVAILABLE,
1258                            _("Unable to create a GL context"));
1259 
1260       return FALSE;
1261     }
1262 
1263   GDK_NOTE (OPENGL,
1264             g_print ("Created EGL context[%p]\n",
1265                      ctx));
1266 
1267   GDK_WIN32_GL_CONTEXT_EGL (context)->egl_context = ctx;
1268 
1269   /* OpenGL does not work with WS_EX_LAYERED enabled, so we need to
1270    * disable WS_EX_LAYERED when we acquire a valid HGLRC
1271    */
1272   impl->suppress_layered++;
1273 
1274   /* if this is the first time a GL context is acquired for the window,
1275    * disable layered windows by triggering update_style_bits()
1276    */
1277   if (impl->suppress_layered == 1)
1278     _gdk_win32_window_update_style_bits (window);
1279 
1280   /* Ensure that any other context is created with a legacy bit set */
1281   gdk_gl_context_set_is_legacy (context, legacy_bit);
1282 
1283   return TRUE;
1284 }
1285 
1286 void
gdk_win32_window_invalidate_egl_framebuffer(GdkWindow * window)1287 gdk_win32_window_invalidate_egl_framebuffer (GdkWindow *window)
1288 {
1289 /* If we are using ANGLE, we need to force redraw of the whole Window and its child windows
1290  *  as we need to re-acquire the EGL surfaces that we rendered to upload to Cairo explicitly,
1291  *  using gdk_window_invalidate_rect (), when we maximize or restore or use aerosnap
1292  */
1293   if (window->gl_paint_context != NULL && gdk_gl_context_get_use_es (window->gl_paint_context))
1294     {
1295       GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
1296 
1297       impl->egl_force_redraw_all = TRUE;
1298     }
1299 }
1300 
1301 static gboolean
gdk_win32_display_make_egl_context_current(GdkDisplay * display,GdkGLContext * context)1302 gdk_win32_display_make_egl_context_current (GdkDisplay   *display,
1303                                             GdkGLContext *context)
1304 {
1305   GdkWin32GLContext *context_win32;
1306   GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (display);
1307   GdkWindow *window;
1308   EGLSurface egl_surface;
1309 
1310   if (context == NULL)
1311     {
1312       eglMakeCurrent (display_win32->egl_disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1313 
1314       return TRUE;
1315     }
1316 
1317   context_win32 = GDK_WIN32_GL_CONTEXT (context);
1318   window = gdk_gl_context_get_window (context);
1319 
1320   if (context_win32->is_attached || !display_win32->hasEglSurfacelessContext)
1321     egl_surface = _gdk_win32_window_get_egl_surface (window,
1322                                                      display_win32->egl_config,
1323                                                     !context_win32->is_attached);
1324   else
1325     egl_surface = EGL_NO_SURFACE;
1326 
1327   if (!eglMakeCurrent (display_win32->egl_disp,
1328                        egl_surface,
1329                        egl_surface,
1330                        GDK_WIN32_GL_CONTEXT_EGL (context)->egl_context))
1331     {
1332       g_warning ("eglMakeCurrent failed");
1333       return FALSE;
1334     }
1335 
1336   if (display_win32->egl_min_swap_interval == 0)
1337     eglSwapInterval (display_win32->egl_disp, 0);
1338   else
1339     g_debug ("Can't disable GL swap interval");
1340 
1341   return TRUE;
1342 }
1343 
1344 static void
gdk_win32_gl_context_egl_class_init(GdkWin32GLContextEGLClass * klass)1345 gdk_win32_gl_context_egl_class_init (GdkWin32GLContextEGLClass *klass)
1346 {
1347   GdkGLContextClass *context_class = GDK_GL_CONTEXT_CLASS (klass);
1348   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1349 
1350   context_class->end_frame = gdk_win32_gl_context_end_frame_egl;
1351   context_class->realize = gdk_win32_gl_context_realize_egl;
1352 
1353   gobject_class->dispose = gdk_win32_gl_context_dispose_egl;
1354 }
1355 
1356 static void
gdk_win32_gl_context_egl_init(GdkWin32GLContextEGL * egl_context)1357 gdk_win32_gl_context_egl_init (GdkWin32GLContextEGL *egl_context)
1358 {
1359 }
1360 #else /* GDK_WIN32_ENABLE_EGL */
1361 /* define EGL stuff as no-op macros or functions if it is not enabled */
1362 
1363 static void
gdk_win32_display_init_egl(GdkWin32Display * display_win32)1364 gdk_win32_display_init_egl (GdkWin32Display *display_win32)
1365 {
1366   GDK_NOTE (OPENGL, g_message ("Cannot %s GLES contexts: no GLES support",
1367                                display_win32->gl_type == GDK_WIN32_GL_PENDING ?
1368                                "create" : "fallback to"));
1369   display_win32->gl_type = GDK_WIN32_GL_NONE;
1370 }
1371 
1372 #define gdk_win32_display_make_egl_context_current(disp,ctx) FALSE
1373 
1374 void
gdk_win32_window_invalidate_egl_framebuffer(GdkWindow * window)1375 gdk_win32_window_invalidate_egl_framebuffer (GdkWindow *window)
1376 {
1377 }
1378 
1379 static gboolean
find_eglconfig_for_window(GdkWin32Display * display_win32,gboolean need_alpha_bits,GError ** error)1380 find_eglconfig_for_window (GdkWin32Display  *display_win32,
1381                            gboolean          need_alpha_bits,
1382                            GError          **error)
1383 {
1384   return FALSE;
1385 }
1386 #endif /* !GDK_WIN32_ENABLE_EGL */
1387 
1388 static void
gdk_win32_gl_context_class_init(GdkWin32GLContextClass * klass)1389 gdk_win32_gl_context_class_init (GdkWin32GLContextClass *klass)
1390 {
1391   GdkGLContextClass *context_class = GDK_GL_CONTEXT_CLASS (klass);
1392   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1393 }
1394 
1395 static void
gdk_win32_gl_context_init(GdkWin32GLContext * self)1396 gdk_win32_gl_context_init (GdkWin32GLContext *self)
1397 {
1398 }
1399 
1400 void
gdk_win32_window_invalidate_for_new_frame(GdkWindow * window,cairo_region_t * update_area)1401 gdk_win32_window_invalidate_for_new_frame (GdkWindow      *window,
1402                                            cairo_region_t *update_area)
1403 {
1404   cairo_rectangle_int_t window_rect;
1405   gboolean invalidate_all = FALSE;
1406   GdkWin32GLContext *context_win32;
1407   cairo_rectangle_int_t whole_window = { 0, 0, gdk_window_get_width (window), gdk_window_get_height (window) };
1408 
1409   /* Minimal update is ok if we're not drawing with gl */
1410   if (window->gl_paint_context == NULL)
1411     return;
1412 
1413   context_win32 = GDK_WIN32_GL_CONTEXT (window->gl_paint_context);
1414   context_win32->do_blit_swap = FALSE;
1415 
1416   if (gdk_gl_context_has_framebuffer_blit (window->gl_paint_context) &&
1417       cairo_region_contains_rectangle (update_area, &whole_window) != CAIRO_REGION_OVERLAP_IN)
1418     {
1419       context_win32->do_blit_swap = TRUE;
1420     }
1421   else
1422     invalidate_all = TRUE;
1423 
1424   if (invalidate_all)
1425     {
1426       window_rect.x = 0;
1427       window_rect.y = 0;
1428       window_rect.width = gdk_window_get_width (window);
1429       window_rect.height = gdk_window_get_height (window);
1430 
1431       /* If nothing else is known, repaint everything so that the back
1432          buffer is fully up-to-date for the swapbuffer */
1433       cairo_region_union_rectangle (update_area, &window_rect);
1434     }
1435 }
1436 
1437 static void
gdk_win32_display_init_gl(GdkDisplay * display,GdkGLContext * share,const gboolean need_alpha_bits)1438 gdk_win32_display_init_gl (GdkDisplay     *display,
1439                            GdkGLContext   *share,
1440                            const gboolean  need_alpha_bits)
1441 {
1442   GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (display);
1443   gint best_idx = 0;
1444   gboolean disable_wgl = FALSE;
1445 
1446   if (display_win32->gl_type == GDK_WIN32_GL_WGL ||
1447       display_win32->gl_type == GDK_WIN32_GL_EGL)
1448     return;
1449 
1450   /*
1451    * We must disable WGL if we are using GDK_GL=gles or if the
1452    * existing shared GLContext is a GLES context
1453    */
1454   disable_wgl = ((_gdk_gl_flags & GDK_GL_GLES) != 0) ||
1455                 (share != NULL && gdk_gl_context_get_use_es (share));
1456 
1457   if (!disable_wgl)
1458     gdk_win32_display_init_wgl (display_win32, need_alpha_bits);
1459 
1460   if (display_win32->gl_type == GDK_WIN32_GL_PENDING ||
1461       display_win32->gl_type == GDK_WIN32_GL_NONE)
1462     gdk_win32_display_init_egl (display_win32);
1463 }
1464 
1465 GdkGLContext *
gdk_win32_window_create_gl_context(GdkWindow * window,gboolean attached,GdkGLContext * share,GError ** error)1466 gdk_win32_window_create_gl_context (GdkWindow *window,
1467                                     gboolean attached,
1468                                     GdkGLContext *share,
1469                                     GError **error)
1470 {
1471   GdkDisplay *display = gdk_window_get_display (window);
1472   GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (gdk_window_get_display (window));
1473   GdkWin32GLContext *context_win32 = NULL;
1474   GdkVisual *visual = gdk_window_get_visual (window);
1475 
1476   gboolean need_alpha_bits = (visual == gdk_screen_get_rgba_visual (gdk_display_get_default_screen (display)));
1477 
1478   /* Acquire and store up the Windows-specific HWND and HDC */
1479   HDC hdc = GetDC (GDK_WINDOW_HWND (window));
1480 
1481   /* display_win32->hdc_egl_temp should *not* be destroyed here!  It is destroyed at dispose()! */
1482   display_win32->hdc_egl_temp = hdc;
1483   gdk_win32_display_init_gl (display,
1484                              share,
1485                              need_alpha_bits);
1486 
1487   if (display_win32->gl_type == GDK_WIN32_GL_NONE)
1488     {
1489       g_set_error_literal (error, GDK_GL_ERROR,
1490                            GDK_GL_ERROR_NOT_AVAILABLE,
1491                            _("No GL implementation is available"));
1492       return NULL;
1493     }
1494 
1495   if (display_win32->gl_type == GDK_WIN32_GL_EGL &&
1496      !find_eglconfig_for_window (display_win32, need_alpha_bits, error))
1497     {
1498       display_win32->gl_type = GDK_WIN32_GL_NONE;
1499       return NULL;
1500     }
1501 
1502   if (display_win32->gl_type == GDK_WIN32_GL_WGL)
1503     context_win32 = GDK_WIN32_GL_CONTEXT (g_object_new (GDK_TYPE_WIN32_GL_CONTEXT_WGL,
1504                                           "display", display,
1505                                           "window", window,
1506                                           "shared-context", share,
1507                                           NULL));
1508 #ifdef GDK_WIN32_ENABLE_EGL
1509   else if (display_win32->gl_type == GDK_WIN32_GL_EGL)
1510     context_win32 = GDK_WIN32_GL_CONTEXT (g_object_new (GDK_TYPE_WIN32_GL_CONTEXT_EGL,
1511                                           "display", display,
1512                                           "window", window,
1513                                           "shared-context", share,
1514                                           NULL));
1515 #endif
1516 
1517   context_win32->need_alpha_bits = need_alpha_bits;
1518   context_win32->gl_hdc = hdc;
1519   context_win32->is_attached = attached;
1520 
1521   return GDK_GL_CONTEXT (context_win32);
1522 }
1523 
1524 gboolean
gdk_win32_display_make_gl_context_current(GdkDisplay * display,GdkGLContext * context)1525 gdk_win32_display_make_gl_context_current (GdkDisplay   *display,
1526                                             GdkGLContext *context)
1527 {
1528   GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (display);
1529 
1530   if (display_win32->gl_type == GDK_WIN32_GL_WGL)
1531     return gdk_win32_display_make_wgl_context_current (display, context);
1532 
1533   if (display_win32->gl_type == GDK_WIN32_GL_EGL)
1534     return gdk_win32_display_make_egl_context_current (display, context);
1535 
1536   g_assert_not_reached ();
1537   return FALSE;
1538 }
1539 
1540 /**
1541  * gdk_win32_display_get_wgl_version:
1542  * @display: a #GdkDisplay
1543  * @major: (out): return location for the WGL major version
1544  * @minor: (out): return location for the WGL minor version
1545  *
1546  * Retrieves the version of the WGL implementation.
1547  *
1548  * Returns: %TRUE if WGL is available
1549  *
1550  * Since: 3.16
1551  */
1552 gboolean
gdk_win32_display_get_wgl_version(GdkDisplay * display,gint * major,gint * minor)1553 gdk_win32_display_get_wgl_version (GdkDisplay *display,
1554                                    gint *major,
1555                                    gint *minor)
1556 {
1557   GdkWin32Display *display_win32 = NULL;
1558   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
1559 
1560   if (!GDK_IS_WIN32_DISPLAY (display))
1561     return FALSE;
1562 
1563   display_win32 = GDK_WIN32_DISPLAY (display);
1564 
1565   if (display_win32->gl_type == GDK_WIN32_GL_PENDING)
1566     gdk_win32_display_init_gl (display, NULL, FALSE);
1567 
1568   if (display_win32->gl_type == GDK_WIN32_GL_NONE)
1569     return FALSE;
1570 
1571   if (major != NULL)
1572     *major = GDK_WIN32_DISPLAY (display)->gl_version / 10;
1573   if (minor != NULL)
1574     *minor = GDK_WIN32_DISPLAY (display)->gl_version % 10;
1575 
1576   return TRUE;
1577 }
1578