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