1 /* GDK - The GIMP Drawing Kit
2  *
3  * gdkglcontext-egl.c: EGL-X11 specific wrappers
4  *
5  * SPDX-FileCopyrightText: 2014  Emmanuele Bassi
6  * SPDX-FileCopyrightText: 2021  GNOME Foundation
7  *
8  * SPDX-License-Identifier: LGPL-2.1-or-later
9  */
10 
11 #include "config.h"
12 
13 #include "gdkglcontext-x11.h"
14 #include "gdkdisplay-x11.h"
15 #include "gdkprivate-x11.h"
16 #include "gdkscreen-x11.h"
17 
18 #include "gdkx11display.h"
19 #include "gdkx11glcontext.h"
20 #include "gdkx11screen.h"
21 #include "gdkx11surface.h"
22 #include "gdkx11property.h"
23 #include <X11/Xatom.h>
24 
25 #include "gdkinternals.h"
26 #include "gdkprofilerprivate.h"
27 #include "gdkintl.h"
28 
29 #include <cairo-xlib.h>
30 
31 #include <epoxy/egl.h>
32 
33 struct _GdkX11GLContextEGL
34 {
35   GdkX11GLContext parent_instance;
36 
37   EGLContext egl_context;
38 
39   guint do_frame_sync : 1;
40 };
41 
42 typedef struct _GdkX11GLContextClass    GdkX11GLContextEGLClass;
43 
G_DEFINE_TYPE(GdkX11GLContextEGL,gdk_x11_gl_context_egl,GDK_TYPE_X11_GL_CONTEXT)44 G_DEFINE_TYPE (GdkX11GLContextEGL, gdk_x11_gl_context_egl, GDK_TYPE_X11_GL_CONTEXT)
45 
46 /**
47  * gdk_x11_display_get_egl_display:
48  * @display: (type GdkX11Display): an X11 display
49  *
50  * Retrieves the EGL display connection object for the given GDK display.
51  *
52  * This function returns `NULL` if GDK is using GLX.
53  *
54  * Returns: (nullable): the EGL display object
55  *
56  * Since: 4.4
57  */
58 gpointer
59 gdk_x11_display_get_egl_display (GdkDisplay *display)
60 {
61   GdkX11Display *self;
62 
63   g_return_val_if_fail (GDK_IS_X11_DISPLAY (display), NULL);
64 
65   self = GDK_X11_DISPLAY (display);
66 
67   return self->egl_display;
68 }
69 
70 static void
gdk_x11_display_create_egl_display(GdkX11Display * self)71 gdk_x11_display_create_egl_display (GdkX11Display *self)
72 {
73   Display *dpy;
74 
75   g_assert (self->egl_display == NULL);
76 
77   dpy = gdk_x11_display_get_xdisplay (GDK_DISPLAY (self));
78 
79   if (epoxy_has_egl_extension (NULL, "EGL_KHR_platform_base"))
80     {
81       PFNEGLGETPLATFORMDISPLAYPROC getPlatformDisplay =
82         (void *) eglGetProcAddress ("eglGetPlatformDisplay");
83 
84       if (getPlatformDisplay != NULL)
85         self->egl_display = getPlatformDisplay (EGL_PLATFORM_X11_KHR, dpy, NULL);
86 
87       if (self->egl_display != NULL)
88         return;
89     }
90 
91   if (epoxy_has_egl_extension (NULL, "EGL_EXT_platform_base"))
92     {
93       PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplay =
94         (void *) eglGetProcAddress ("eglGetPlatformDisplayEXT");
95 
96       if (getPlatformDisplay)
97         self->egl_display = getPlatformDisplay (EGL_PLATFORM_X11_EXT, dpy, NULL);
98 
99       if (self->egl_display != NULL)
100         return;
101     }
102 
103   self->egl_display = eglGetDisplay ((EGLNativeDisplayType) dpy);
104 }
105 
106 static XVisualInfo *
gdk_x11_display_get_visual_info_for_visual(GdkX11Display * self,VisualID visualid)107 gdk_x11_display_get_visual_info_for_visual (GdkX11Display  *self,
108                                             VisualID        visualid)
109 {
110   XVisualInfo template, *visinfo;
111   int nvisuals;
112 
113   template.screen = self->screen->screen_num;
114   template.visualid = visualid;
115 
116   visinfo = XGetVisualInfo (gdk_x11_display_get_xdisplay (GDK_DISPLAY (self)),
117                             VisualScreenMask | VisualIDMask,
118                             &template,
119                             &nvisuals);
120   g_warn_if_fail (nvisuals == 1);
121 
122   return visinfo;
123 }
124 
125 static gboolean
visual_is_rgba(XVisualInfo * visinfo)126 visual_is_rgba (XVisualInfo *visinfo)
127 {
128   return
129     visinfo->depth == 32 &&
130     visinfo->visual->red_mask   == 0xff0000 &&
131     visinfo->visual->green_mask == 0x00ff00 &&
132     visinfo->visual->blue_mask  == 0x0000ff;
133 }
134 
135 #define MAX_EGL_ATTRS   30
136 
137 static gboolean
gdk_x11_display_create_egl_config(GdkX11Display * display,gboolean force,Visual ** out_visual,int * out_depth,GError ** error)138 gdk_x11_display_create_egl_config (GdkX11Display  *display,
139                                    gboolean        force,
140                                    Visual        **out_visual,
141                                    int            *out_depth,
142                                    GError        **error)
143 {
144   GdkX11Display *self = GDK_X11_DISPLAY (display);
145   EGLint attrs[MAX_EGL_ATTRS];
146   EGLConfig *configs;
147   EGLint count, alloced;
148   enum {
149     NO_VISUAL_FOUND,
150     WITH_MULTISAMPLING,
151     WITH_STENCIL_AND_DEPTH_BUFFER,
152     NO_ALPHA,
153     NO_ALPHA_VISUAL,
154     PERFECT
155   } best_features;
156 
157   int i = 0;
158 
159   attrs[i++] = EGL_SURFACE_TYPE;
160   attrs[i++] = EGL_WINDOW_BIT;
161 
162   attrs[i++] = EGL_COLOR_BUFFER_TYPE;
163   attrs[i++] = EGL_RGB_BUFFER;
164 
165   attrs[i++] = EGL_RED_SIZE;
166   attrs[i++] = 8;
167   attrs[i++] = EGL_GREEN_SIZE;
168   attrs[i++] = 8;
169   attrs[i++] = EGL_BLUE_SIZE;
170   attrs[i++] = 8;
171   attrs[i++] = EGL_ALPHA_SIZE;
172   attrs[i++] = 8;
173 
174   attrs[i++] = EGL_NONE;
175   g_assert (i < MAX_EGL_ATTRS);
176 
177   if (!eglChooseConfig (self->egl_display, attrs, NULL, -1, &alloced) || alloced == 0)
178     {
179       g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
180                            _("No EGL configuration available"));
181       return FALSE;
182     }
183 
184   configs = g_new (EGLConfig, alloced);
185   if (!eglChooseConfig (self->egl_display, attrs, configs, alloced, &count))
186     {
187       g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
188                            _("Failed to get EGL configurations"));
189       return FALSE;
190     }
191   g_warn_if_fail (alloced == count);
192 
193   best_features = NO_VISUAL_FOUND;
194 
195   for (i = 0; i < count; i++)
196     {
197       XVisualInfo *visinfo;
198       int tmp, visualid;
199 
200       if (!eglGetConfigAttrib (self->egl_display, configs[i], EGL_NATIVE_VISUAL_ID, &visualid))
201         continue;
202 
203       visinfo = gdk_x11_display_get_visual_info_for_visual (self, visualid);
204       if (visinfo == NULL)
205         continue;
206 
207       if (!eglGetConfigAttrib (self->egl_display, configs[i], EGL_SAMPLE_BUFFERS, &tmp) || tmp != 0)
208         {
209           if (best_features < WITH_MULTISAMPLING)
210             {
211               GDK_NOTE (OPENGL, g_message ("Best EGL config is %u for visual 0x%lX with multisampling", i, visinfo->visualid));
212               best_features = WITH_MULTISAMPLING;
213               *out_visual = visinfo->visual;
214               *out_depth = visinfo->depth;
215               self->egl_config = configs[i];
216             }
217           XFree (visinfo);
218           continue;
219         }
220 
221       if (!eglGetConfigAttrib (self->egl_display, configs[i], EGL_DEPTH_SIZE, &tmp) || tmp != 0 ||
222           !eglGetConfigAttrib (self->egl_display, configs[i], EGL_STENCIL_SIZE, &tmp) || tmp != 0)
223         {
224           GDK_NOTE (OPENGL, g_message ("Best EGL config is %u for visual 0x%lX with stencil or depth buffer", i, visinfo->visualid));
225           if (best_features < WITH_STENCIL_AND_DEPTH_BUFFER)
226             {
227               best_features = WITH_STENCIL_AND_DEPTH_BUFFER;
228               *out_visual = visinfo->visual;
229               *out_depth = visinfo->depth;
230               self->egl_config = configs[i];
231             }
232           XFree (visinfo);
233           continue;
234         }
235 
236       if (!visual_is_rgba (visinfo))
237         {
238           GDK_NOTE (OPENGL, g_message ("Best EGL config is %u for visual 0x%lX without RGBA Visual", i, visinfo->visualid));
239           if (best_features < NO_ALPHA_VISUAL)
240             {
241               best_features = NO_ALPHA_VISUAL;
242               *out_visual = visinfo->visual;
243               *out_depth = visinfo->depth;
244               self->egl_config = configs[i];
245             }
246           XFree (visinfo);
247           continue;
248         }
249 
250       GDK_NOTE (OPENGL, g_message ("EGL Config %u for visual 0x%lX is the perfect choice", i, visinfo->visualid));
251       *out_visual = visinfo->visual;
252       *out_depth = visinfo->depth;
253       self->egl_config = configs[i];
254       XFree (visinfo);
255       /* everything is perfect */
256       best_features = PERFECT;
257       break;
258     }
259 
260   g_free (configs);
261 
262   if (best_features == NO_VISUAL_FOUND)
263     {
264       g_set_error_literal (error, GDK_GL_ERROR,
265                            GDK_GL_ERROR_NOT_AVAILABLE,
266                            _("No EGL configuration with required features found"));
267       return FALSE;
268     }
269   else if (!force && best_features != PERFECT)
270     {
271       g_set_error_literal (error, GDK_GL_ERROR,
272                            GDK_GL_ERROR_NOT_AVAILABLE,
273                            _("No perfect EGL configuration found"));
274       return FALSE;
275     }
276 
277   return TRUE;
278 }
279 
280 #undef MAX_EGL_ATTRS
281 
282 static EGLSurface
gdk_x11_surface_get_egl_surface(GdkSurface * surface)283 gdk_x11_surface_get_egl_surface (GdkSurface *surface)
284 {
285   GdkX11Surface *self = GDK_X11_SURFACE (surface);
286   GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (self));
287   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
288 
289   if (self->egl_surface)
290     return self->egl_surface;
291 
292   self->egl_surface =
293     eglCreateWindowSurface (display_x11->egl_display,
294                             display_x11->egl_config,
295                             (EGLNativeWindowType) gdk_x11_surface_get_xid (surface),
296                             NULL);
297 
298   return self->egl_surface;
299 }
300 
301 void
gdk_x11_surface_destroy_egl_surface(GdkX11Surface * self)302 gdk_x11_surface_destroy_egl_surface (GdkX11Surface *self)
303 {
304   GdkX11Display *display_x11;
305 
306   if (self->egl_surface == NULL)
307     return;
308 
309   display_x11 = GDK_X11_DISPLAY (gdk_surface_get_display (GDK_SURFACE (self)));
310 
311   eglDestroySurface (display_x11->egl_display, self->egl_surface);
312   self->egl_surface = NULL;
313 }
314 
315 static void
gdk_x11_gl_context_egl_begin_frame(GdkDrawContext * draw_context,cairo_region_t * region)316 gdk_x11_gl_context_egl_begin_frame (GdkDrawContext *draw_context,
317                                     cairo_region_t *region)
318 {
319   GDK_DRAW_CONTEXT_CLASS (gdk_x11_gl_context_egl_parent_class)->begin_frame (draw_context, region);
320 
321   glDrawBuffers (1, (GLenum[1]) { GL_BACK });
322 }
323 
324 static void
gdk_x11_gl_context_egl_end_frame(GdkDrawContext * draw_context,cairo_region_t * painted)325 gdk_x11_gl_context_egl_end_frame (GdkDrawContext *draw_context,
326                                   cairo_region_t *painted)
327 {
328   GdkGLContext *context = GDK_GL_CONTEXT (draw_context);
329   GdkSurface *surface = gdk_gl_context_get_surface (context);
330   GdkDisplay *display = gdk_surface_get_display (surface);
331   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
332   EGLSurface egl_surface;
333 
334   GDK_DRAW_CONTEXT_CLASS (gdk_x11_gl_context_egl_parent_class)->end_frame (draw_context, painted);
335 
336   gdk_gl_context_make_current (context);
337 
338   egl_surface = gdk_x11_surface_get_egl_surface (surface);
339 
340   gdk_profiler_add_mark (GDK_PROFILER_CURRENT_TIME, 0, "x11", "swap buffers");
341   if (display_x11->has_egl_swap_buffers_with_damage)
342     {
343       int i, j, n_rects = cairo_region_num_rectangles (painted);
344       int surface_height = gdk_surface_get_height (surface);
345       int scale = gdk_surface_get_scale_factor (surface);
346       EGLint stack_rects[4 * 4]; /* 4 rects */
347       EGLint *heap_rects = NULL;
348       EGLint *rects;
349 
350       if (n_rects < G_N_ELEMENTS (stack_rects) / 4)
351         rects = (EGLint *) &stack_rects;
352       else
353         rects = heap_rects = g_new (EGLint, n_rects * 4);
354 
355       for (i = 0, j = 0; i < n_rects; i++)
356         {
357           cairo_rectangle_int_t rect;
358 
359           cairo_region_get_rectangle (painted, i, &rect);
360 
361           rects[j++] = rect.x * scale;
362           rects[j++] = (surface_height - rect.height - rect.y) * scale;
363           rects[j++] = rect.width * scale;
364           rects[j++] = rect.height * scale;
365         }
366 
367       eglSwapBuffersWithDamageEXT (display_x11->egl_display, egl_surface, rects, n_rects);
368       g_free (heap_rects);
369     }
370   else
371     eglSwapBuffers (display_x11->egl_display, egl_surface);
372 }
373 
374 static gboolean
gdk_x11_gl_context_egl_clear_current(GdkGLContext * context)375 gdk_x11_gl_context_egl_clear_current (GdkGLContext *context)
376 {
377   GdkDisplay *display = gdk_gl_context_get_display (context);
378   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
379 
380   return eglMakeCurrent (display_x11->egl_display,
381                          EGL_NO_SURFACE,
382                          EGL_NO_SURFACE,
383                          EGL_NO_CONTEXT);
384 }
385 
386 static gboolean
gdk_x11_gl_context_egl_make_current(GdkGLContext * context,gboolean surfaceless)387 gdk_x11_gl_context_egl_make_current (GdkGLContext *context,
388                                      gboolean      surfaceless)
389 {
390   GdkX11GLContextEGL *self = GDK_X11_GL_CONTEXT_EGL (context);
391   GdkDisplay *display = gdk_gl_context_get_display (context);
392   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
393   GdkSurface *surface;
394   EGLSurface egl_surface;
395   gboolean do_frame_sync = FALSE;
396 
397   if (surfaceless)
398     {
399       return eglMakeCurrent (display_x11->egl_display,
400                              EGL_NO_SURFACE,
401                              EGL_NO_SURFACE,
402                              self->egl_context);
403     }
404 
405   surface = gdk_gl_context_get_surface (context);
406   egl_surface = gdk_x11_surface_get_egl_surface (surface);
407 
408   GDK_DISPLAY_NOTE (display, OPENGL,
409                     g_message ("Making EGL context %p current to surface %p",
410                                self->egl_context, egl_surface));
411 
412   if (!eglMakeCurrent (display_x11->egl_display,
413                        egl_surface,
414                        egl_surface,
415                        self->egl_context))
416     return FALSE;
417 
418   /* If the WM is compositing there is no particular need to delay
419    * the swap when drawing on the offscreen, rendering to the screen
420    * happens later anyway, and its up to the compositor to sync that
421    * to the vblank. */
422   do_frame_sync = ! gdk_display_is_composited (display);
423 
424   if (do_frame_sync != self->do_frame_sync)
425     {
426       self->do_frame_sync = do_frame_sync;
427 
428       if (do_frame_sync)
429         eglSwapInterval (display_x11->egl_display, 1);
430       else
431         eglSwapInterval (display_x11->egl_display, 0);
432     }
433 
434   return TRUE;
435 }
436 
437 static cairo_region_t *
gdk_x11_gl_context_egl_get_damage(GdkGLContext * context)438 gdk_x11_gl_context_egl_get_damage (GdkGLContext *context)
439 {
440   GdkDisplay *display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
441   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
442 
443   if (display_x11->has_egl_buffer_age)
444     {
445       GdkSurface *surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
446       EGLSurface egl_surface;
447       int buffer_age = 0;
448 
449       egl_surface = gdk_x11_surface_get_egl_surface (surface);
450       gdk_gl_context_make_current (context);
451 
452       eglQuerySurface (display_x11->egl_display,
453                        egl_surface,
454                        EGL_BUFFER_AGE_EXT,
455                        &buffer_age);
456 
457       switch (buffer_age)
458         {
459         case 1:
460           return cairo_region_create ();
461 
462         case 2:
463           if (context->old_updated_area[0])
464             return cairo_region_copy (context->old_updated_area[0]);
465           break;
466 
467         case 3:
468           if (context->old_updated_area[0] && context->old_updated_area[1])
469             {
470               cairo_region_t *damage = cairo_region_copy (context->old_updated_area[0]);
471               cairo_region_union (damage, context->old_updated_area[1]);
472               return damage;
473             }
474           break;
475 
476         default:
477           break;
478         }
479     }
480 
481   return GDK_GL_CONTEXT_CLASS (gdk_x11_gl_context_egl_parent_class)->get_damage (context);
482 }
483 
484 #define N_EGL_ATTRS 16
485 
486 static gboolean
gdk_x11_gl_context_egl_realize(GdkGLContext * context,GError ** error)487 gdk_x11_gl_context_egl_realize (GdkGLContext  *context,
488                                 GError       **error)
489 {
490   GdkX11Display *display_x11;
491   GdkDisplay *display;
492   GdkX11GLContextEGL *context_egl;
493   GdkGLContext *share;
494   gboolean debug_bit, forward_bit, legacy_bit, use_es;
495   int major, minor, i = 0;
496   EGLint context_attrs[N_EGL_ATTRS];
497 
498   display = gdk_gl_context_get_display (context);
499 
500   context_egl = GDK_X11_GL_CONTEXT_EGL (context);
501   display_x11 = GDK_X11_DISPLAY (display);
502   share = gdk_display_get_gl_context (display);
503 
504   gdk_gl_context_get_required_version (context, &major, &minor);
505   debug_bit = gdk_gl_context_get_debug_enabled (context);
506   forward_bit = gdk_gl_context_get_forward_compatible (context);
507   legacy_bit = GDK_DISPLAY_DEBUG_CHECK (display, GL_LEGACY) ||
508                (share != NULL && gdk_gl_context_is_legacy (share));
509   use_es = GDK_DISPLAY_DEBUG_CHECK (display, GL_GLES) ||
510            (share != NULL && gdk_gl_context_get_use_es (share));
511 
512   if (!use_es)
513     {
514       eglBindAPI (EGL_OPENGL_API);
515 
516       if (display_x11->has_egl_khr_create_context)
517         {
518           int flags = 0;
519 
520           if (debug_bit)
521             flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
522           if (forward_bit)
523             flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;
524 
525           context_attrs[i++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
526           context_attrs[i++] = legacy_bit
527                              ? EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR
528                              : EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
529           context_attrs[i++] = EGL_CONTEXT_MAJOR_VERSION_KHR;
530           context_attrs[i++] = legacy_bit ? 3 : major;
531           context_attrs[i++] = EGL_CONTEXT_MINOR_VERSION_KHR;
532           context_attrs[i++] = legacy_bit ? 0 : minor;
533           context_attrs[i++] = EGL_CONTEXT_FLAGS_KHR;
534           context_attrs[i++] = flags;
535           context_attrs[i++] = EGL_NONE;
536         }
537       else
538         {
539           context_attrs[i++] = EGL_NONE;
540         }
541     }
542   else
543     {
544       eglBindAPI (EGL_OPENGL_ES_API);
545 
546       context_attrs[i++] = EGL_CONTEXT_CLIENT_VERSION;
547       if (major == 3)
548         context_attrs[i++] = 3;
549       else
550         context_attrs[i++] = 2;
551     }
552 
553   context_attrs[i++] = EGL_NONE;
554   g_assert (i < N_EGL_ATTRS);
555 
556   GDK_DISPLAY_NOTE (display, OPENGL,
557                     g_message ("Creating EGL context version %d.%d (shared:%s, debug:%s, forward:%s, legacy:%s, es:%s)",
558                                major, minor,
559                                share != NULL ? "yes" : "no",
560                                debug_bit ? "yes" : "no",
561                                forward_bit ? "yes" : "no",
562                                legacy_bit ? "yes" : "no",
563                                use_es ? "yes" : "no"));
564 
565   context_egl->egl_context =
566     eglCreateContext (display_x11->egl_display,
567                       display_x11->egl_config,
568                       share != NULL
569                         ? GDK_X11_GL_CONTEXT_EGL (share)->egl_context
570                         : EGL_NO_CONTEXT,
571                       context_attrs);
572 
573   /* If we're not asking for a GLES context, and we don't have the legacy bit set
574    * already, try again with a legacy context
575    */
576   if (context_egl->egl_context == NULL && !use_es && !legacy_bit)
577     {
578       context_attrs[1] = EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR;
579       context_attrs[3] = 3;
580       context_attrs[5] = 0;
581 
582       legacy_bit = TRUE;
583       use_es = FALSE;
584 
585       GDK_NOTE (OPENGL,
586                 g_message ("Context creation failed; trying legacy EGL context"));
587 
588       context_egl->egl_context =
589         eglCreateContext (display_x11->egl_display,
590                           display_x11->egl_config,
591                           share != NULL
592                             ? GDK_X11_GL_CONTEXT_EGL (share)->egl_context
593                             : EGL_NO_CONTEXT,
594                           context_attrs);
595     }
596 
597   if (context_egl->egl_context == NULL)
598     {
599       g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
600                            _("Unable to create a GL context"));
601       return FALSE;
602     }
603 
604   gdk_gl_context_set_is_legacy (context, legacy_bit);
605   gdk_gl_context_set_use_es (context, use_es);
606 
607   GDK_NOTE (OPENGL,
608             g_message ("Realized EGL context[%p]",
609                        context_egl->egl_context));
610 
611   return TRUE;
612 }
613 
614 #undef N_EGL_ATTRS
615 
616 static void
gdk_x11_gl_context_egl_dispose(GObject * gobject)617 gdk_x11_gl_context_egl_dispose (GObject *gobject)
618 {
619   GdkX11GLContextEGL *context_egl = GDK_X11_GL_CONTEXT_EGL (gobject);
620 
621   if (context_egl->egl_context != NULL)
622     {
623       GdkGLContext *context = GDK_GL_CONTEXT (gobject);
624       GdkX11Display *display_x11 = GDK_X11_DISPLAY (gdk_gl_context_get_display (context));
625 
626       /* Unset the current context if we're disposing it */
627       if (eglGetCurrentContext () == context_egl->egl_context)
628         eglMakeCurrent (display_x11->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
629 
630       GDK_NOTE (OPENGL, g_message ("Destroying EGL context"));
631       eglDestroyContext (display_x11->egl_display, context_egl->egl_context);
632       context_egl->egl_context = NULL;
633     }
634 
635   G_OBJECT_CLASS (gdk_x11_gl_context_egl_parent_class)->dispose (gobject);
636 }
637 
638 static void
gdk_x11_gl_context_egl_class_init(GdkX11GLContextEGLClass * klass)639 gdk_x11_gl_context_egl_class_init (GdkX11GLContextEGLClass *klass)
640 {
641   GdkGLContextClass *context_class = GDK_GL_CONTEXT_CLASS (klass);
642   GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass);
643   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
644 
645   context_class->backend_type = GDK_GL_EGL;
646 
647   context_class->realize = gdk_x11_gl_context_egl_realize;
648   context_class->make_current = gdk_x11_gl_context_egl_make_current;
649   context_class->clear_current = gdk_x11_gl_context_egl_clear_current;
650   context_class->get_damage = gdk_x11_gl_context_egl_get_damage;
651 
652   draw_context_class->begin_frame = gdk_x11_gl_context_egl_begin_frame;
653   draw_context_class->end_frame = gdk_x11_gl_context_egl_end_frame;
654 
655   gobject_class->dispose = gdk_x11_gl_context_egl_dispose;
656 }
657 
658 static void
gdk_x11_gl_context_egl_init(GdkX11GLContextEGL * self)659 gdk_x11_gl_context_egl_init (GdkX11GLContextEGL *self)
660 {
661   self->do_frame_sync = TRUE;
662 }
663 
664 gboolean
gdk_x11_display_init_egl(GdkX11Display * self,gboolean force,Visual ** out_visual,int * out_depth,GError ** error)665 gdk_x11_display_init_egl (GdkX11Display  *self,
666                           gboolean        force,
667                           Visual        **out_visual,
668                           int            *out_depth,
669                           GError        **error)
670 {
671   GdkDisplay *display = GDK_DISPLAY (self);
672   Display *dpy;
673   int major, minor;
674 
675   if (!gdk_gl_backend_can_be_used (GDK_GL_EGL, error))
676     return FALSE;
677 
678   dpy = gdk_x11_display_get_xdisplay (display);
679 
680   if (!epoxy_has_egl ())
681     {
682       g_set_error_literal (error, GDK_GL_ERROR,
683                            GDK_GL_ERROR_NOT_AVAILABLE,
684                            _("EGL is not supported"));
685       return FALSE;
686     }
687 
688   gdk_x11_display_create_egl_display (self);
689   if (self->egl_display == NULL)
690     {
691       g_set_error_literal (error, GDK_GL_ERROR,
692                            GDK_GL_ERROR_NOT_AVAILABLE,
693                            _("Failed to create EGL display"));
694       return FALSE;
695     }
696 
697   if (!eglInitialize (self->egl_display, &major, &minor))
698     {
699       self->egl_display = NULL;
700       g_set_error_literal (error, GDK_GL_ERROR,
701                            GDK_GL_ERROR_NOT_AVAILABLE,
702                            _("Could not initialize EGL display"));
703       return FALSE;
704     }
705   if (major < GDK_EGL_MIN_VERSION_MAJOR ||
706       (major == GDK_EGL_MIN_VERSION_MAJOR && minor < GDK_EGL_MIN_VERSION_MINOR))
707     {
708       eglTerminate (dpy);
709       self->egl_display = NULL;
710       g_set_error (error, GDK_GL_ERROR,
711                    GDK_GL_ERROR_NOT_AVAILABLE,
712                    _("EGL version %d.%d is too old. GTK requires %d.%d"),
713                    major, minor, GDK_EGL_MIN_VERSION_MAJOR, GDK_EGL_MIN_VERSION_MINOR);
714       return FALSE;
715     }
716 
717   if (!epoxy_has_egl_extension (self->egl_display, "EGL_KHR_surfaceless_context"))
718     {
719       eglTerminate (dpy);
720       self->egl_display = NULL;
721       g_set_error_literal (error, GDK_GL_ERROR,
722                            GDK_GL_ERROR_UNSUPPORTED_PROFILE,
723                            _("Surfaceless contexts are not supported on this EGL implementation"));
724       return FALSE;
725     }
726 
727   if (!gdk_x11_display_create_egl_config (self, force, out_visual, out_depth, error))
728     {
729       eglTerminate (self->egl_display);
730       self->egl_display = NULL;
731       return FALSE;
732     }
733 
734   self->egl_version = epoxy_egl_version (dpy);
735 
736   self->has_egl_khr_create_context =
737     epoxy_has_egl_extension (self->egl_display, "EGL_KHR_create_context");
738   self->has_egl_buffer_age =
739     epoxy_has_egl_extension (self->egl_display, "EGL_EXT_buffer_age");
740   self->has_egl_swap_buffers_with_damage =
741     epoxy_has_egl_extension (self->egl_display, "EGL_EXT_swap_buffers_with_damage");
742 
743   GDK_DISPLAY_NOTE (display, OPENGL,
744                     g_message ("EGL found\n"
745                                " - Version: %s\n"
746                                " - Vendor: %s\n"
747                                " - Client API: %s\n"
748                                " - Checked extensions:\n"
749                                "\t* EGL_KHR_create_context: %s\n"
750                                "\t* EGL_EXT_buffer_age: %s\n"
751                                "\t* EGL_EXT_swap_buffers_with_damage: %s\n",
752                                eglQueryString (self->egl_display, EGL_VERSION),
753                                eglQueryString (self->egl_display, EGL_VENDOR),
754                                eglQueryString (self->egl_display, EGL_CLIENT_APIS),
755                                self->has_egl_khr_create_context ? "yes" : "no",
756                                self->has_egl_buffer_age ? "yes" : "no",
757                                self->has_egl_swap_buffers_with_damage ? "yes" : "no"));
758 
759   return TRUE;
760 }
761 
762 /**
763  * gdk_x11_display_get_egl_version:
764  * @display: (type GdkX11Display): a `GdkDisplay`
765  * @major: (out): return location for the EGL major version
766  * @minor: (out): return location for the EGL minor version
767  *
768  * Retrieves the version of the EGL implementation.
769  *
770  * Returns: %TRUE if EGL is available
771  *
772  * Since: 4.4
773  */
774 gboolean
gdk_x11_display_get_egl_version(GdkDisplay * display,int * major,int * minor)775 gdk_x11_display_get_egl_version (GdkDisplay *display,
776                                  int        *major,
777                                  int        *minor)
778 {
779   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
780 
781   if (!GDK_IS_X11_DISPLAY (display))
782     return FALSE;
783 
784   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
785 
786   if (display_x11->egl_display == NULL)
787     return FALSE;
788 
789   if (major != NULL)
790     *major = display_x11->egl_version / 10;
791   if (minor != NULL)
792     *minor = display_x11->egl_version % 10;
793 
794   return TRUE;
795 }
796