1/* GDK - The GIMP Drawing Kit
2 *
3 * gdkglcontext.c: GL context abstraction
4 *
5 * Copyright © 2014 Emmanuele Bassi
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21/**
22 * GdkGLContext:
23 *
24 * `GdkGLContext` is an object representing a platform-specific
25 * OpenGL draw context.
26 *
27 * `GdkGLContext`s are created for a surface using
28 * [method@Gdk.Surface.create_gl_context], and the context will match
29 * the characteristics of the surface.
30 *
31 * A `GdkGLContext` is not tied to any particular normal framebuffer.
32 * For instance, it cannot draw to the surface back buffer. The GDK
33 * repaint system is in full control of the painting to that. Instead,
34 * you can create render buffers or textures and use [func@cairo_draw_from_gl]
35 * in the draw function of your widget to draw them. Then GDK will handle
36 * the integration of your rendering with that of other widgets.
37 *
38 * Support for `GdkGLContext` is platform-specific and context creation
39 * can fail, returning %NULL context.
40 *
41 * A `GdkGLContext` has to be made "current" in order to start using
42 * it, otherwise any OpenGL call will be ignored.
43 *
44 * ## Creating a new OpenGL context
45 *
46 * In order to create a new `GdkGLContext` instance you need a `GdkSurface`,
47 * which you typically get during the realize call of a widget.
48 *
49 * A `GdkGLContext` is not realized until either [method@Gdk.GLContext.make_current]
50 * or [method@Gdk.GLContext.realize] is called. It is possible to specify
51 * details of the GL context like the OpenGL version to be used, or whether
52 * the GL context should have extra state validation enabled after calling
53 * [method@Gdk.Surface.create_gl_context] by calling [method@Gdk.GLContext.realize].
54 * If the realization fails you have the option to change the settings of
55 * the `GdkGLContext` and try again.
56 *
57 * ## Using a GdkGLContext
58 *
59 * You will need to make the `GdkGLContext` the current context before issuing
60 * OpenGL calls; the system sends OpenGL commands to whichever context is current.
61 * It is possible to have multiple contexts, so you always need to ensure that
62 * the one which you want to draw with is the current one before issuing commands:
63 *
64 * ```c
65 * gdk_gl_context_make_current (context);
66 * ```
67 *
68 * You can now perform your drawing using OpenGL commands.
69 *
70 * You can check which `GdkGLContext` is the current one by using
71 * [func@Gdk.GLContext.get_current]; you can also unset any `GdkGLContext`
72 * that is currently set by calling [func@Gdk.GLContext.clear_current].
73 */
74
75#include "config.h"
76
77#include "gdkglcontextprivate.h"
78
79#include "gdkdebug.h"
80#include "gdkdisplayprivate.h"
81#include "gdkintl.h"
82#include "gdkmemoryformatprivate.h"
83#include "gdkmemorytextureprivate.h"
84#include "gdkprofilerprivate.h"
85
86#include "gdk-private.h"
87
88#ifdef GDK_WINDOWING_WIN32
89# include "gdk/win32/gdkwin32.h"
90#endif
91
92#include <epoxy/gl.h>
93#ifdef HAVE_EGL
94#include <epoxy/egl.h>
95#endif
96
97#define DEFAULT_ALLOWED_APIS GDK_GL_API_GL | GDK_GL_API_GLES
98
99typedef struct {
100 int major;
101 int minor;
102 int gl_version;
103
104 guint has_khr_debug : 1;
105 guint use_khr_debug : 1;
106 guint has_half_float : 1;
107 guint has_unpack_subimage : 1;
108 guint has_debug_output : 1;
109 guint extensions_checked : 1;
110 guint debug_enabled : 1;
111 guint forward_compatible : 1;
112 guint is_legacy : 1;
113
114 GdkGLAPI allowed_apis;
115 GdkGLAPI api;
116
117 int max_debug_label_length;
118
119#ifdef HAVE_EGL
120 EGLContext egl_context;
121 EGLBoolean (*eglSwapBuffersWithDamage) (EGLDisplay, EGLSurface, const EGLint *, EGLint);
122#endif
123} GdkGLContextPrivate;
124
125enum {
126 PROP_0,
127
128 PROP_ALLOWED_APIS,
129 PROP_API,
130 PROP_SHARED_CONTEXT,
131
132 LAST_PROP
133};
134
135static GParamSpec *properties[LAST_PROP] = { NULL, };
136
137G_DEFINE_QUARK (gdk-gl-error-quark, gdk_gl_error)
138
139G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkGLContext, gdk_gl_context, GDK_TYPE_DRAW_CONTEXT)
140
141typedef struct _MaskedContext MaskedContext;
142
143static inline MaskedContext *
144mask_context (GdkGLContext *context,
145 gboolean surfaceless)
146{
147 return (MaskedContext *) GSIZE_TO_POINTER (GPOINTER_TO_SIZE (context) | (surfaceless ? 1 : 0));
148}
149
150static inline GdkGLContext *
151unmask_context (MaskedContext *mask)
152{
153 return GDK_GL_CONTEXT (GSIZE_TO_POINTER (GPOINTER_TO_SIZE (mask) & ~(gsize) 1));
154}
155
156static inline gboolean
157mask_is_surfaceless (MaskedContext *mask)
158{
159 return GPOINTER_TO_SIZE (mask) & (gsize) 1;
160}
161
162static void
163unref_unmasked (gpointer data)
164{
165 g_object_unref (object: unmask_context (mask: data));
166}
167
168static GPrivate thread_current_context = G_PRIVATE_INIT (unref_unmasked);
169
170static void
171gdk_gl_context_clear_old_updated_area (GdkGLContext *context)
172{
173 int i;
174
175 for (i = 0; i < 2; i++)
176 {
177 g_clear_pointer (&context->old_updated_area[i], cairo_region_destroy);
178 }
179}
180
181static void
182gdk_gl_context_dispose (GObject *gobject)
183{
184 GdkGLContext *context = GDK_GL_CONTEXT (gobject);
185#ifdef HAVE_EGL
186 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
187
188 if (priv->egl_context != NULL)
189 {
190 GdkDisplay *display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
191 EGLDisplay *egl_display = gdk_display_get_egl_display (display);
192
193 if (eglGetCurrentContext () == priv->egl_context)
194 eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
195
196 GDK_DISPLAY_NOTE (display, OPENGL, g_message ("Destroying EGL context"));
197
198 eglDestroyContext (egl_display, priv->egl_context);
199 priv->egl_context = NULL;
200 }
201#endif
202
203 gdk_gl_context_clear_old_updated_area (context);
204
205 G_OBJECT_CLASS (gdk_gl_context_parent_class)->dispose (gobject);
206}
207
208static void
209gdk_gl_context_set_property (GObject *object,
210 guint prop_id,
211 const GValue *value,
212 GParamSpec *pspec)
213{
214 GdkGLContext *self = GDK_GL_CONTEXT (object);
215
216 switch (prop_id)
217 {
218 case PROP_ALLOWED_APIS:
219 gdk_gl_context_set_allowed_apis (self, apis: g_value_get_flags (value));
220 break;
221
222 case PROP_SHARED_CONTEXT:
223 g_assert (g_value_get_object (value) == NULL);
224 break;
225
226 default:
227 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
228 }
229}
230
231static void
232gdk_gl_context_get_property (GObject *object,
233 guint prop_id,
234 GValue *value,
235 GParamSpec *pspec)
236{
237 GdkGLContext *self = GDK_GL_CONTEXT (object);
238 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
239
240 switch (prop_id)
241 {
242 case PROP_ALLOWED_APIS:
243 g_value_set_flags (value, v_flags: priv->allowed_apis);
244 break;
245
246 case PROP_API:
247 g_value_set_flags (value, v_flags: priv->api);
248 break;
249
250 case PROP_SHARED_CONTEXT:
251 g_value_set_object (value, NULL);
252 break;
253
254 default:
255 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
256 }
257}
258
259#define N_EGL_ATTRS 16
260
261static GdkGLAPI
262gdk_gl_context_real_realize (GdkGLContext *context,
263 GError **error)
264{
265#ifdef HAVE_EGL
266 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
267 GdkDisplay *display = gdk_gl_context_get_display (context);
268 EGLDisplay egl_display = gdk_display_get_egl_display (display);
269
270 if (egl_display)
271 {
272 EGLConfig egl_config;
273 GdkGLContext *share = gdk_display_get_gl_context (display);
274 GdkGLContextPrivate *share_priv = gdk_gl_context_get_instance_private (self: share);
275 EGLContext ctx;
276 EGLint context_attribs[N_EGL_ATTRS];
277 int major, minor, flags;
278 gboolean debug_bit, forward_bit, legacy_bit;
279 GdkGLAPI api;
280 int i = 0;
281 G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
282
283 if (share != NULL)
284 gdk_gl_context_get_required_version (context: share, major: &major, minor: &minor);
285 else
286 gdk_gl_context_get_required_version (context, major: &major, minor: &minor);
287
288 debug_bit = gdk_gl_context_get_debug_enabled (context);
289 forward_bit = gdk_gl_context_get_forward_compatible (context);
290 legacy_bit = GDK_DISPLAY_DEBUG_CHECK (display, GL_LEGACY) ||
291 (share != NULL && gdk_gl_context_is_legacy (context: share));
292
293 if (display->have_egl_no_config_context)
294 egl_config = NULL;
295 else
296 egl_config = gdk_display_get_egl_config (display);
297
298 flags = 0;
299
300 if (debug_bit)
301 flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
302 if (forward_bit)
303 flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;
304
305 if (gdk_gl_context_is_api_allowed (self: context, api: GDK_GL_API_GL, NULL) &&
306 eglBindAPI (EGL_OPENGL_API))
307 {
308 /* We want a core profile, unless in legacy mode */
309 context_attribs[i++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
310 context_attribs[i++] = legacy_bit
311 ? EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR
312 : EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
313
314 /* Specify the version */
315 context_attribs[i++] = EGL_CONTEXT_MAJOR_VERSION_KHR;
316 context_attribs[i++] = legacy_bit ? 3 : major;
317 context_attribs[i++] = EGL_CONTEXT_MINOR_VERSION_KHR;
318 context_attribs[i++] = legacy_bit ? 0 : minor;
319 api = GDK_GL_API_GL;
320 }
321 else if (gdk_gl_context_is_api_allowed (self: context, api: GDK_GL_API_GLES, NULL) &&
322 eglBindAPI (EGL_OPENGL_ES_API))
323 {
324 context_attribs[i++] = EGL_CONTEXT_CLIENT_VERSION;
325 if (major == 3)
326 context_attribs[i++] = 3;
327 else
328 context_attribs[i++] = 2;
329 api = GDK_GL_API_GLES;
330 }
331 else
332 {
333 g_set_error_literal (err: error, GDK_GL_ERROR, code: GDK_GL_ERROR_NOT_AVAILABLE,
334 _("The EGL implementation does not support any allowed APIs"));
335 return 0;
336 }
337
338 /* Specify the flags */
339 context_attribs[i++] = EGL_CONTEXT_FLAGS_KHR;
340 context_attribs[i++] = flags;
341
342 context_attribs[i++] = EGL_NONE;
343 g_assert (i < N_EGL_ATTRS);
344
345 GDK_DISPLAY_NOTE (display, OPENGL,
346 g_message ("Creating EGL context version %d.%d (debug:%s, forward:%s, legacy:%s, es:%s)",
347 major, minor,
348 debug_bit ? "yes" : "no",
349 forward_bit ? "yes" : "no",
350 legacy_bit ? "yes" : "no",
351 api == GDK_GL_API_GLES ? "yes" : "no"));
352
353 ctx = eglCreateContext (egl_display,
354 egl_config,
355 share != NULL ? share_priv->egl_context
356 : EGL_NO_CONTEXT,
357 context_attribs);
358
359 /* If context creation failed without the ES bit, let's try again with it */
360 if (ctx == NULL && gdk_gl_context_is_api_allowed (self: context, api: GDK_GL_API_GLES, NULL) && eglBindAPI (EGL_OPENGL_ES_API))
361 {
362 i = 0;
363 context_attribs[i++] = EGL_CONTEXT_MAJOR_VERSION;
364 context_attribs[i++] = 2;
365 context_attribs[i++] = EGL_CONTEXT_MINOR_VERSION;
366 context_attribs[i++] = 0;
367 context_attribs[i++] = EGL_CONTEXT_FLAGS_KHR;
368 context_attribs[i++] = flags & ~EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;
369 context_attribs[i++] = EGL_NONE;
370 g_assert (i < N_EGL_ATTRS);
371
372 legacy_bit = FALSE;
373 api = GDK_GL_API_GLES;
374
375 GDK_DISPLAY_NOTE (display, OPENGL,
376 g_message ("eglCreateContext failed, switching to OpenGL ES"));
377 ctx = eglCreateContext (egl_display,
378 egl_config,
379 share != NULL ? share_priv->egl_context
380 : EGL_NO_CONTEXT,
381 context_attribs);
382 }
383
384 /* If context creation failed without the legacy bit, let's try again with it */
385 if (ctx == NULL && gdk_gl_context_is_api_allowed (self: context, api: GDK_GL_API_GL, NULL) && eglBindAPI (EGL_OPENGL_API))
386 {
387 i = 0;
388 context_attribs[i++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
389 context_attribs[i++] = EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR;
390 context_attribs[i++] = EGL_CONTEXT_MAJOR_VERSION;
391 context_attribs[i++] = 3;
392 context_attribs[i++] = EGL_CONTEXT_MINOR_VERSION;
393 context_attribs[i++] = 0;
394 context_attribs[i++] = EGL_CONTEXT_FLAGS_KHR;
395 context_attribs[i++] = flags & ~EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;
396 context_attribs[i++] = EGL_NONE;
397 g_assert (i < N_EGL_ATTRS);
398
399 legacy_bit = TRUE;
400 api = GDK_GL_API_GL;
401
402 GDK_DISPLAY_NOTE (display, OPENGL,
403 g_message ("eglCreateContext failed, switching to legacy"));
404 ctx = eglCreateContext (egl_display,
405 egl_config,
406 share != NULL ? share_priv->egl_context
407 : EGL_NO_CONTEXT,
408 context_attribs);
409 }
410
411 if (ctx == NULL)
412 {
413 g_set_error_literal (err: error, GDK_GL_ERROR,
414 code: GDK_GL_ERROR_NOT_AVAILABLE,
415 _("Unable to create a GL context"));
416 return 0;
417 }
418
419 GDK_DISPLAY_NOTE (display, OPENGL, g_message ("Created EGL context[%p]", ctx));
420
421 priv->egl_context = ctx;
422
423 gdk_gl_context_set_is_legacy (context, is_legacy: legacy_bit);
424
425 if (epoxy_has_egl_extension (dpy: egl_display, extension: "EGL_KHR_swap_buffers_with_damage"))
426 priv->eglSwapBuffersWithDamage = (gpointer)epoxy_eglGetProcAddress ("eglSwapBuffersWithDamageKHR");
427 else if (epoxy_has_egl_extension (dpy: egl_display, extension: "EGL_EXT_swap_buffers_with_damage"))
428 priv->eglSwapBuffersWithDamage = (gpointer)epoxy_eglGetProcAddress ("eglSwapBuffersWithDamageEXT");
429
430 gdk_profiler_end_mark (start_time, "realize GdkWaylandGLContext", NULL);
431
432 return api;
433 }
434#endif
435
436 g_set_error_literal (err: error, GDK_GL_ERROR, code: GDK_GL_ERROR_NOT_AVAILABLE,
437 _("The current backend does not support OpenGL"));
438 return 0;
439}
440
441#undef N_EGL_ATTRS
442
443static cairo_region_t *
444gdk_gl_context_real_get_damage (GdkGLContext *context)
445{
446 GdkSurface *surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
447#ifdef HAVE_EGL
448 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
449 GdkDisplay *display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
450
451 if (priv->egl_context && display->have_egl_buffer_age)
452 {
453 EGLSurface egl_surface;
454 int buffer_age = 0;
455 egl_surface = gdk_surface_get_egl_surface (self: surface);
456 gdk_gl_context_make_current (context);
457 eglQuerySurface (gdk_display_get_egl_display (display), egl_surface,
458 EGL_BUFFER_AGE_EXT, &buffer_age);
459
460 switch (buffer_age)
461 {
462 case 1:
463 return cairo_region_create ();
464 break;
465
466 case 2:
467 if (context->old_updated_area[0])
468 return cairo_region_copy (original: context->old_updated_area[0]);
469 break;
470
471 case 3:
472 if (context->old_updated_area[0] &&
473 context->old_updated_area[1])
474 {
475 cairo_region_t *damage = cairo_region_copy (original: context->old_updated_area[0]);
476 cairo_region_union (dst: damage, other: context->old_updated_area[1]);
477 return damage;
478 }
479 break;
480
481 default:
482 ;
483 }
484 }
485#endif
486
487 return cairo_region_create_rectangle (rectangle: &(GdkRectangle) {
488 0, 0,
489 gdk_surface_get_width (surface),
490 gdk_surface_get_height (surface)
491 });
492}
493
494static gboolean
495gdk_gl_context_real_is_shared (GdkGLContext *self,
496 GdkGLContext *other)
497{
498 if (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (self)) != gdk_draw_context_get_display (GDK_DRAW_CONTEXT (other)))
499 return FALSE;
500
501 /* XXX: Should we check es or legacy here? */
502
503 return TRUE;
504}
505
506static gboolean
507gdk_gl_context_real_clear_current (GdkGLContext *context)
508{
509 GdkDisplay *display = gdk_gl_context_get_display (context);
510#ifdef HAVE_EGL
511 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
512
513 if (priv->egl_context == NULL)
514 return FALSE;
515
516 return eglMakeCurrent (gdk_display_get_egl_display (display),
517 EGL_NO_SURFACE,
518 EGL_NO_SURFACE,
519 EGL_NO_CONTEXT);
520#else
521 return FALSE;
522#endif
523}
524
525static gboolean
526gdk_gl_context_real_make_current (GdkGLContext *context,
527 gboolean surfaceless)
528{
529#ifdef HAVE_EGL
530 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
531 GdkDisplay *display = gdk_gl_context_get_display (context);
532 EGLSurface egl_surface;
533
534 if (priv->egl_context == NULL)
535 return FALSE;
536
537 if (!surfaceless)
538 egl_surface = gdk_surface_get_egl_surface (self: gdk_gl_context_get_surface (context));
539 else
540 egl_surface = EGL_NO_SURFACE;
541
542 return eglMakeCurrent (gdk_display_get_egl_display (display),
543 egl_surface,
544 egl_surface,
545 priv->egl_context);
546#else
547 return FALSE;
548#endif
549}
550
551static void
552gdk_gl_context_real_begin_frame (GdkDrawContext *draw_context,
553 gboolean prefers_high_depth,
554 cairo_region_t *region)
555{
556 GdkGLContext *context = GDK_GL_CONTEXT (draw_context);
557 G_GNUC_UNUSED GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
558 GdkSurface *surface;
559 cairo_region_t *damage;
560 int ww, wh;
561
562 surface = gdk_draw_context_get_surface (context: draw_context);
563
564#ifdef HAVE_EGL
565 if (priv->egl_context)
566 gdk_surface_ensure_egl_surface (self: surface, hdr: prefers_high_depth);
567#endif
568
569 damage = GDK_GL_CONTEXT_GET_CLASS (context)->get_damage (context);
570
571 if (context->old_updated_area[1])
572 cairo_region_destroy (region: context->old_updated_area[1]);
573 context->old_updated_area[1] = context->old_updated_area[0];
574 context->old_updated_area[0] = cairo_region_copy (original: region);
575
576 cairo_region_union (dst: region, other: damage);
577 cairo_region_destroy (region: damage);
578
579 ww = gdk_surface_get_width (surface) * gdk_surface_get_scale_factor (surface);
580 wh = gdk_surface_get_height (surface) * gdk_surface_get_scale_factor (surface);
581
582 gdk_gl_context_make_current (context);
583
584 /* Initial setup */
585 glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
586 glDisable (GL_DEPTH_TEST);
587 glDisable (GL_BLEND);
588 glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
589
590 glViewport (0, 0, ww, wh);
591
592#ifdef HAVE_EGL
593 if (priv->egl_context && gdk_gl_context_check_version (context, required_gl_major: 0, required_gl_minor: 0, required_gles_major: 3, required_gles_minor: 0))
594 glDrawBuffers (1, (GLenum[1]) { gdk_gl_context_get_use_es (context) ? GL_BACK : GL_BACK_LEFT });
595#endif
596}
597
598static void
599gdk_gl_context_real_end_frame (GdkDrawContext *draw_context,
600 cairo_region_t *painted)
601{
602#ifdef HAVE_EGL
603 GdkGLContext *context = GDK_GL_CONTEXT (draw_context);
604 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
605 GdkSurface *surface = gdk_gl_context_get_surface (context);
606 GdkDisplay *display = gdk_surface_get_display (surface);
607 EGLSurface egl_surface;
608
609 if (priv->egl_context == NULL)
610 return;
611
612 gdk_gl_context_make_current (context);
613
614 egl_surface = gdk_surface_get_egl_surface (self: surface);
615
616 gdk_profiler_add_mark (GDK_PROFILER_CURRENT_TIME, 0, "EGL", "swap buffers");
617
618 if (priv->eglSwapBuffersWithDamage)
619 {
620 EGLint stack_rects[4 * 4]; /* 4 rects */
621 EGLint *heap_rects = NULL;
622 int i, j, n_rects = cairo_region_num_rectangles (region: painted);
623 int surface_height = gdk_surface_get_height (surface);
624 int scale = gdk_surface_get_scale_factor (surface);
625 EGLint *rects;
626
627 if (n_rects < G_N_ELEMENTS (stack_rects) / 4)
628 rects = (EGLint *)&stack_rects;
629 else
630 heap_rects = rects = g_new (EGLint, n_rects * 4);
631
632 for (i = 0, j = 0; i < n_rects; i++)
633 {
634 cairo_rectangle_int_t rect;
635
636 cairo_region_get_rectangle (region: painted, nth: i, rectangle: &rect);
637 rects[j++] = rect.x * scale;
638 rects[j++] = (surface_height - rect.height - rect.y) * scale;
639 rects[j++] = rect.width * scale;
640 rects[j++] = rect.height * scale;
641 }
642 priv->eglSwapBuffersWithDamage (gdk_display_get_egl_display (display), egl_surface, rects, n_rects);
643 g_free (mem: heap_rects);
644 }
645 else
646 eglSwapBuffers (gdk_display_get_egl_display (display), egl_surface);
647#endif
648}
649
650static void
651gdk_gl_context_surface_resized (GdkDrawContext *draw_context)
652{
653 GdkGLContext *context = GDK_GL_CONTEXT (draw_context);
654
655 gdk_gl_context_clear_old_updated_area (context);
656}
657
658static guint
659gdk_gl_context_real_get_default_framebuffer (GdkGLContext *self)
660{
661 return 0;
662}
663
664static void
665gdk_gl_context_class_init (GdkGLContextClass *klass)
666{
667 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
668 GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass);
669
670 klass->realize = gdk_gl_context_real_realize;
671 klass->get_damage = gdk_gl_context_real_get_damage;
672 klass->is_shared = gdk_gl_context_real_is_shared;
673 klass->make_current = gdk_gl_context_real_make_current;
674 klass->clear_current = gdk_gl_context_real_clear_current;
675 klass->get_default_framebuffer = gdk_gl_context_real_get_default_framebuffer;
676
677 draw_context_class->begin_frame = gdk_gl_context_real_begin_frame;
678 draw_context_class->end_frame = gdk_gl_context_real_end_frame;
679 draw_context_class->surface_resized = gdk_gl_context_surface_resized;
680
681 /**
682 * GdkGLContext:shared-context: (attributes org.gtk.Property.get=gdk_gl_context_get_shared_context)
683 *
684 * Always %NULL
685 *
686 * As many contexts can share data now and no single shared context exists
687 * anymore, this function has been deprecated and now always returns %NULL.
688 *
689 * Deprecated: 4.4: Use [method@Gdk.GLContext.is_shared] to check if contexts
690 * can be shared.
691 */
692 properties[PROP_SHARED_CONTEXT] =
693 g_param_spec_object (name: "shared-context",
694 P_("Shared context"),
695 P_("The GL context this context shares data with"),
696 GDK_TYPE_GL_CONTEXT,
697 flags: G_PARAM_READWRITE |
698 G_PARAM_CONSTRUCT_ONLY |
699 G_PARAM_STATIC_STRINGS |
700 G_PARAM_DEPRECATED);
701
702 /**
703 * GdkGLContext:allowed-apis: (attributes org.gtk.Property.get=gdk_gl_context_get_allowed_apis org.gtk.Property.gdk_gl_context_set_allowed_apis)
704 *
705 * The allowed APIs.
706 *
707 * Since: 4.6
708 */
709 properties[PROP_ALLOWED_APIS] =
710 g_param_spec_flags (name: "allowed-apis",
711 P_("Allowed APIs"),
712 P_("The list of allowed APIs for this context"),
713 flags_type: GDK_TYPE_GL_API,
714 DEFAULT_ALLOWED_APIS,
715 flags: G_PARAM_READWRITE |
716 G_PARAM_STATIC_STRINGS |
717 G_PARAM_EXPLICIT_NOTIFY);
718
719 /**
720 * GdkGLContext:api: (attributes org.gtk.Property.get=gdk_gl_context_get_api)
721 *
722 * The API currently in use.
723 *
724 * Since: 4.6
725 */
726 properties[PROP_API] =
727 g_param_spec_flags (name: "api",
728 P_("API"),
729 P_("The API currently in use"),
730 flags_type: GDK_TYPE_GL_API,
731 default_value: 0,
732 flags: G_PARAM_READABLE |
733 G_PARAM_STATIC_STRINGS |
734 G_PARAM_EXPLICIT_NOTIFY);
735
736 gobject_class->set_property = gdk_gl_context_set_property;
737 gobject_class->get_property = gdk_gl_context_get_property;
738 gobject_class->dispose = gdk_gl_context_dispose;
739
740 g_object_class_install_properties (oclass: gobject_class, n_pspecs: LAST_PROP, pspecs: properties);
741}
742
743static void
744gdk_gl_context_init (GdkGLContext *self)
745{
746 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
747
748 priv->allowed_apis = DEFAULT_ALLOWED_APIS;
749}
750
751/* Must have called gdk_display_prepare_gl() before */
752GdkGLContext *
753gdk_gl_context_new (GdkDisplay *display,
754 GdkSurface *surface)
755{
756 GdkGLContext *shared;
757
758 g_assert (surface == NULL || display == gdk_surface_get_display (surface));
759
760 /* assert gdk_display_prepare_gl() had been called */
761 shared = gdk_display_get_gl_context (display);
762 g_assert (shared);
763
764 return g_object_new (G_OBJECT_TYPE (shared),
765 first_property_name: "display", display,
766 "surface", surface,
767 NULL);
768}
769
770void
771gdk_gl_context_push_debug_group (GdkGLContext *context,
772 const char *message)
773{
774 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
775
776 if (priv->use_khr_debug)
777 glPushDebugGroupKHR (GL_DEBUG_SOURCE_APPLICATION, 0, -1, message);
778}
779
780void
781gdk_gl_context_push_debug_group_printf (GdkGLContext *context,
782 const char *format,
783 ...)
784{
785 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
786 char *message;
787 va_list args;
788
789 if (priv->use_khr_debug)
790 {
791 int msg_len;
792
793 va_start (args, format);
794 message = g_strdup_vprintf (format, args);
795 va_end (args);
796
797 msg_len = MIN (priv->max_debug_label_length, strlen (message) - 1);
798 glPushDebugGroupKHR (GL_DEBUG_SOURCE_APPLICATION, 0, msg_len, message);
799 g_free (mem: message);
800 }
801}
802
803void
804gdk_gl_context_pop_debug_group (GdkGLContext *context)
805{
806 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
807
808 if (priv->use_khr_debug)
809 glPopDebugGroupKHR ();
810}
811
812void
813gdk_gl_context_label_object (GdkGLContext *context,
814 guint identifier,
815 guint name,
816 const char *label)
817{
818 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
819
820 if (priv->use_khr_debug)
821 glObjectLabel (identifier, name, -1, label);
822}
823
824void
825gdk_gl_context_label_object_printf (GdkGLContext *context,
826 guint identifier,
827 guint name,
828 const char *format,
829 ...)
830{
831 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
832 char *message;
833 va_list args;
834
835 if (priv->use_khr_debug)
836 {
837 int msg_len;
838
839 va_start (args, format);
840 message = g_strdup_vprintf (format, args);
841 va_end (args);
842
843 msg_len = MIN (priv->max_debug_label_length, strlen (message) - 1);
844
845 glObjectLabel (identifier, name, msg_len, message);
846 g_free (mem: message);
847 }
848}
849
850
851gboolean
852gdk_gl_context_has_unpack_subimage (GdkGLContext *context)
853{
854 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
855
856 return priv->has_unpack_subimage;
857}
858
859static gboolean
860gdk_gl_context_is_realized (GdkGLContext *context)
861{
862 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
863
864 return priv->api != 0;
865}
866
867/**
868 * gdk_gl_context_set_debug_enabled:
869 * @context: a `GdkGLContext`
870 * @enabled: whether to enable debugging in the context
871 *
872 * Sets whether the `GdkGLContext` should perform extra validations and
873 * runtime checking.
874 *
875 * This is useful during development, but has additional overhead.
876 *
877 * The `GdkGLContext` must not be realized or made current prior to
878 * calling this function.
879 */
880void
881gdk_gl_context_set_debug_enabled (GdkGLContext *context,
882 gboolean enabled)
883{
884 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
885
886 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
887 g_return_if_fail (!gdk_gl_context_is_realized (context));
888
889 enabled = !!enabled;
890
891 priv->debug_enabled = enabled;
892}
893
894/**
895 * gdk_gl_context_get_debug_enabled:
896 * @context: a `GdkGLContext`
897 *
898 * Retrieves whether the context is doing extra validations and runtime checking.
899 *
900 * See [method@Gdk.GLContext.set_debug_enabled].
901 *
902 * Returns: %TRUE if debugging is enabled
903 */
904gboolean
905gdk_gl_context_get_debug_enabled (GdkGLContext *context)
906{
907 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
908
909 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
910
911 return priv->debug_enabled;
912}
913
914/**
915 * gdk_gl_context_set_forward_compatible:
916 * @context: a `GdkGLContext`
917 * @compatible: whether the context should be forward-compatible
918 *
919 * Sets whether the `GdkGLContext` should be forward-compatible.
920 *
921 * Forward-compatible contexts must not support OpenGL functionality that
922 * has been marked as deprecated in the requested version; non-forward
923 * compatible contexts, on the other hand, must support both deprecated and
924 * non deprecated functionality.
925 *
926 * The `GdkGLContext` must not be realized or made current prior to calling
927 * this function.
928 */
929void
930gdk_gl_context_set_forward_compatible (GdkGLContext *context,
931 gboolean compatible)
932{
933 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
934
935 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
936 g_return_if_fail (!gdk_gl_context_is_realized (context));
937
938 compatible = !!compatible;
939
940 priv->forward_compatible = compatible;
941}
942
943/**
944 * gdk_gl_context_get_forward_compatible:
945 * @context: a `GdkGLContext`
946 *
947 * Retrieves whether the context is forward-compatible.
948 *
949 * See [method@Gdk.GLContext.set_forward_compatible].
950 *
951 * Returns: %TRUE if the context should be forward-compatible
952 */
953gboolean
954gdk_gl_context_get_forward_compatible (GdkGLContext *context)
955{
956 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
957
958 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
959
960 return priv->forward_compatible;
961}
962
963/**
964 * gdk_gl_context_set_required_version:
965 * @context: a `GdkGLContext`
966 * @major: the major version to request
967 * @minor: the minor version to request
968 *
969 * Sets the major and minor version of OpenGL to request.
970 *
971 * Setting @major and @minor to zero will use the default values.
972 *
973 * The `GdkGLContext` must not be realized or made current prior to calling
974 * this function.
975 */
976void
977gdk_gl_context_set_required_version (GdkGLContext *context,
978 int major,
979 int minor)
980{
981 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
982 gboolean force_gles = FALSE;
983 int version, min_ver;
984#ifdef G_ENABLE_DEBUG
985 GdkDisplay *display;
986#endif
987
988 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
989 g_return_if_fail (!gdk_gl_context_is_realized (context));
990
991 /* this will take care of the default */
992 if (major == 0 && minor == 0)
993 {
994 priv->major = 0;
995 priv->minor = 0;
996 return;
997 }
998
999 version = (major * 100) + minor;
1000
1001#ifdef G_ENABLE_DEBUG
1002 display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
1003 force_gles = GDK_DISPLAY_DEBUG_CHECK (display, GL_GLES);
1004#endif
1005 /* Enforce a minimum context version number of 3.2 for desktop GL,
1006 * and 2.0 for GLES
1007 */
1008 if (gdk_gl_context_get_use_es (context) || force_gles)
1009 min_ver = 200;
1010 else
1011 min_ver = 302;
1012
1013 if (version < min_ver)
1014 {
1015 g_warning ("gdk_gl_context_set_required_version - GL context versions less than 3.2 are not supported.");
1016 version = min_ver;
1017 }
1018 priv->major = version / 100;
1019 priv->minor = version % 100;
1020}
1021
1022gboolean
1023gdk_gl_context_check_version (GdkGLContext *self,
1024 int required_gl_major,
1025 int required_gl_minor,
1026 int required_gles_major,
1027 int required_gles_minor)
1028{
1029 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
1030
1031 g_return_val_if_fail (GDK_IS_GL_CONTEXT (self), FALSE);
1032 g_return_val_if_fail (required_gl_minor < 10, FALSE);
1033 g_return_val_if_fail (required_gles_minor < 10, FALSE);
1034
1035 if (!gdk_gl_context_is_realized (context: self))
1036 return FALSE;
1037
1038 switch (priv->api)
1039 {
1040 case GDK_GL_API_GL:
1041 return priv->gl_version >= required_gl_major * 10 + required_gl_minor;
1042
1043 case GDK_GL_API_GLES:
1044 return priv->gl_version >= required_gles_major * 10 + required_gles_minor;
1045
1046 default:
1047 g_return_val_if_reached (FALSE);
1048
1049 }
1050}
1051
1052/**
1053 * gdk_gl_context_get_required_version:
1054 * @context: a `GdkGLContext`
1055 * @major: (out) (nullable): return location for the major version to request
1056 * @minor: (out) (nullable): return location for the minor version to request
1057 *
1058 * Retrieves required OpenGL version.
1059 *
1060 * See [method@Gdk.GLContext.set_required_version].
1061 */
1062void
1063gdk_gl_context_get_required_version (GdkGLContext *context,
1064 int *major,
1065 int *minor)
1066{
1067 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
1068 gboolean force_gles = FALSE;
1069 GdkDisplay *display;
1070 int default_major, default_minor;
1071 int maj, min;
1072
1073 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
1074
1075 display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
1076
1077#ifdef G_ENABLE_DEBUG
1078 force_gles = GDK_DISPLAY_DEBUG_CHECK (display, GL_GLES);
1079#endif
1080
1081 /* libANGLE on Windows at least requires GLES 3.0+ */
1082 if (display->have_egl_win32_libangle)
1083 force_gles = TRUE;
1084
1085 /* Default fallback values for uninitialised contexts; we
1086 * enforce a context version number of 3.2 for desktop GL,
1087 * and 2.0 for GLES
1088 */
1089 if (gdk_gl_context_get_use_es (context) || force_gles)
1090 {
1091 default_major = display->have_egl_win32_libangle ? 3 : 2;
1092 default_minor = 0;
1093 }
1094 else
1095 {
1096 default_major = 3;
1097 default_minor = 2;
1098 }
1099
1100 if (priv->major > 0)
1101 maj = priv->major;
1102 else
1103 maj = default_major;
1104
1105 if (priv->minor > 0)
1106 min = priv->minor;
1107 else
1108 min = default_minor;
1109
1110 if (major != NULL)
1111 *major = maj;
1112 if (minor != NULL)
1113 *minor = min;
1114}
1115
1116/**
1117 * gdk_gl_context_is_legacy:
1118 * @context: a `GdkGLContext`
1119 *
1120 * Whether the `GdkGLContext` is in legacy mode or not.
1121 *
1122 * The `GdkGLContext` must be realized before calling this function.
1123 *
1124 * When realizing a GL context, GDK will try to use the OpenGL 3.2 core
1125 * profile; this profile removes all the OpenGL API that was deprecated
1126 * prior to the 3.2 version of the specification. If the realization is
1127 * successful, this function will return %FALSE.
1128 *
1129 * If the underlying OpenGL implementation does not support core profiles,
1130 * GDK will fall back to a pre-3.2 compatibility profile, and this function
1131 * will return %TRUE.
1132 *
1133 * You can use the value returned by this function to decide which kind
1134 * of OpenGL API to use, or whether to do extension discovery, or what
1135 * kind of shader programs to load.
1136 *
1137 * Returns: %TRUE if the GL context is in legacy mode
1138 */
1139gboolean
1140gdk_gl_context_is_legacy (GdkGLContext *context)
1141{
1142 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
1143
1144 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
1145 g_return_val_if_fail (gdk_gl_context_is_realized (context), FALSE);
1146
1147 return priv->is_legacy;
1148}
1149
1150void
1151gdk_gl_context_set_is_legacy (GdkGLContext *context,
1152 gboolean is_legacy)
1153{
1154 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
1155
1156 priv->is_legacy = !!is_legacy;
1157}
1158
1159/**
1160 * gdk_gl_context_is_shared:
1161 * @self: a `GdkGLContext`
1162 * @other: the `GdkGLContext` that should be compatible with @self
1163 *
1164 * Checks if the two GL contexts can share resources.
1165 *
1166 * When they can, the texture IDs from @other can be used in @self. This
1167 * is particularly useful when passing `GdkGLTexture` objects between
1168 * different contexts.
1169 *
1170 * Contexts created for the same display with the same properties will
1171 * always be compatible, even if they are created for different surfaces.
1172 * For other contexts it depends on the GL backend.
1173 *
1174 * Both contexts must be realized for this check to succeed. If either one
1175 * is not, this function will return %FALSE.
1176 *
1177 * Returns: %TRUE if the two GL contexts are compatible.
1178 *
1179 * Since: 4.4
1180 */
1181gboolean
1182gdk_gl_context_is_shared (GdkGLContext *self,
1183 GdkGLContext *other)
1184{
1185 g_return_val_if_fail (GDK_IS_GL_CONTEXT (self), FALSE);
1186 g_return_val_if_fail (GDK_IS_GL_CONTEXT (other), FALSE);
1187
1188 if (!gdk_gl_context_is_realized (context: self) ||
1189 !gdk_gl_context_is_realized (context: other))
1190 return FALSE;
1191
1192 return GDK_GL_CONTEXT_GET_CLASS (self)->is_shared (self, other);
1193}
1194
1195/**
1196 * gdk_gl_context_set_allowed_apis: (attributes org.gtk.Method.set_property=allowed-apis)
1197 * @self: a GL context
1198 * @apis: the allowed APIs
1199 *
1200 * Sets the allowed APIs. When gdk_gl_context_realize() is called, only the
1201 * allowed APIs will be tried. If you set this to 0, realizing will always fail.
1202 *
1203 * If you set it on a realized context, the property will not have any effect.
1204 * It is only relevant during gdk_gl_context_realize().
1205 *
1206 * By default, all APIs are allowed.
1207 *
1208 * Since: 4.6
1209 **/
1210void
1211gdk_gl_context_set_allowed_apis (GdkGLContext *self,
1212 GdkGLAPI apis)
1213{
1214 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
1215
1216 g_return_if_fail (GDK_IS_GL_CONTEXT (self));
1217
1218 if (priv->allowed_apis == apis)
1219 return;
1220
1221 priv->allowed_apis = apis;
1222
1223 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_ALLOWED_APIS]);
1224}
1225
1226/**
1227 * gdk_gl_context_get_allowed_apis: (attributes org.gtk.Method.get_property=allowed-apis)
1228 * @self: a GL context
1229 *
1230 * Gets the allowed APIs set via gdk_gl_context_set_allowed_apis().
1231 *
1232 * Returns: the allowed APIs
1233 *
1234 * Since: 4.6
1235 **/
1236GdkGLAPI
1237gdk_gl_context_get_allowed_apis (GdkGLContext *self)
1238{
1239 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
1240
1241 g_return_val_if_fail (GDK_IS_GL_CONTEXT (self), 0);
1242
1243 return priv->allowed_apis;
1244}
1245
1246/**
1247 * gdk_gl_context_get_api: (attributes org.gtk.Method.get_property=api)
1248 * @self: a GL context
1249 *
1250 * Gets the API currently in use.
1251 *
1252 * If the renderer has not been realized yet, 0 is returned.
1253 *
1254 * Returns: the currently used API
1255 *
1256 * Since: 4.6
1257 **/
1258GdkGLAPI
1259gdk_gl_context_get_api (GdkGLContext *self)
1260{
1261 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
1262
1263 g_return_val_if_fail (GDK_IS_GL_CONTEXT (self), 0);
1264
1265 return priv->api;
1266}
1267
1268gboolean
1269gdk_gl_context_is_api_allowed (GdkGLContext *self,
1270 GdkGLAPI api,
1271 GError **error)
1272{
1273 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
1274
1275 if (GDK_DISPLAY_DEBUG_CHECK (gdk_gl_context_get_display (self), GL_GLES))
1276 {
1277 if (!(api & GDK_GL_API_GLES))
1278 {
1279 g_set_error_literal (err: error, GDK_GL_ERROR, code: GDK_GL_ERROR_NOT_AVAILABLE,
1280 _("Anything but OpenGL ES disabled via GDK_DEBUG"));
1281 return FALSE;
1282 }
1283 }
1284
1285 if (priv->allowed_apis & api)
1286 return TRUE;
1287
1288 g_set_error (err: error, GDK_GL_ERROR, code: GDK_GL_ERROR_NOT_AVAILABLE,
1289 _("Application does not support %s API"),
1290 api == GDK_GL_API_GL ? "OpenGL" : "OpenGL ES");
1291
1292 return FALSE;
1293}
1294
1295/**
1296 * gdk_gl_context_set_use_es:
1297 * @context: a `GdkGLContext`
1298 * @use_es: whether the context should use OpenGL ES instead of OpenGL,
1299 * or -1 to allow auto-detection
1300 *
1301 * Requests that GDK create an OpenGL ES context instead of an OpenGL one.
1302 *
1303 * Not all platforms support OpenGL ES.
1304 *
1305 * The @context must not have been realized.
1306 *
1307 * By default, GDK will attempt to automatically detect whether the
1308 * underlying GL implementation is OpenGL or OpenGL ES once the @context
1309 * is realized.
1310 *
1311 * You should check the return value of [method@Gdk.GLContext.get_use_es]
1312 * after calling [method@Gdk.GLContext.realize] to decide whether to use
1313 * the OpenGL or OpenGL ES API, extensions, or shaders.
1314 */
1315void
1316gdk_gl_context_set_use_es (GdkGLContext *context,
1317 int use_es)
1318{
1319 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
1320 g_return_if_fail (!gdk_gl_context_is_realized (context));
1321
1322 switch (use_es)
1323 {
1324 case -1:
1325 gdk_gl_context_set_allowed_apis (self: context, DEFAULT_ALLOWED_APIS);
1326 break;
1327 case 0:
1328 gdk_gl_context_set_allowed_apis (self: context, apis: GDK_GL_API_GL);
1329 break;
1330 case 1:
1331 gdk_gl_context_set_allowed_apis (self: context, apis: GDK_GL_API_GLES);
1332 break;
1333 default:
1334 /* Just ignore the call */
1335 break;
1336 }
1337}
1338
1339/**
1340 * gdk_gl_context_get_use_es:
1341 * @context: a `GdkGLContext`
1342 *
1343 * Checks whether the @context is using an OpenGL or OpenGL ES profile.
1344 *
1345 * Returns: %TRUE if the `GdkGLContext` is using an OpenGL ES profile
1346 */
1347gboolean
1348gdk_gl_context_get_use_es (GdkGLContext *context)
1349{
1350 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
1351
1352 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
1353
1354 return priv->api == GDK_GL_API_GLES;
1355}
1356
1357static void APIENTRY
1358gl_debug_message_callback (GLenum source,
1359 GLenum type,
1360 GLuint id,
1361 GLenum severity,
1362 GLsizei length,
1363 const GLchar *message,
1364 const void *user_data)
1365{
1366 const char *message_source;
1367 const char *message_type;
1368 const char *message_severity;
1369 GLogLevelFlags log_level;
1370
1371 if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
1372 return;
1373
1374 switch (source)
1375 {
1376 case GL_DEBUG_SOURCE_API:
1377 message_source = "API";
1378 break;
1379 case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
1380 message_source = "Window System";
1381 break;
1382 case GL_DEBUG_SOURCE_SHADER_COMPILER:
1383 message_source = "Shader Compiler";
1384 break;
1385 case GL_DEBUG_SOURCE_THIRD_PARTY:
1386 message_source = "Third Party";
1387 break;
1388 case GL_DEBUG_SOURCE_APPLICATION:
1389 message_source = "Application";
1390 break;
1391 case GL_DEBUG_SOURCE_OTHER:
1392 default:
1393 message_source = "Other";
1394 }
1395
1396 switch (type)
1397 {
1398 case GL_DEBUG_TYPE_ERROR:
1399 message_type = "Error";
1400 break;
1401 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
1402 message_type = "Deprecated Behavior";
1403 break;
1404 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
1405 message_type = "Undefined Behavior";
1406 break;
1407 case GL_DEBUG_TYPE_PORTABILITY:
1408 message_type = "Portability";
1409 break;
1410 case GL_DEBUG_TYPE_PERFORMANCE:
1411 message_type = "Performance";
1412 break;
1413 case GL_DEBUG_TYPE_MARKER:
1414 message_type = "Marker";
1415 break;
1416 case GL_DEBUG_TYPE_PUSH_GROUP:
1417 message_type = "Push Group";
1418 break;
1419 case GL_DEBUG_TYPE_POP_GROUP:
1420 message_type = "Pop Group";
1421 break;
1422 case GL_DEBUG_TYPE_OTHER:
1423 default:
1424 message_type = "Other";
1425 }
1426
1427 switch (severity)
1428 {
1429 case GL_DEBUG_SEVERITY_HIGH:
1430 message_severity = "High";
1431 log_level = G_LOG_LEVEL_CRITICAL;
1432 break;
1433 case GL_DEBUG_SEVERITY_MEDIUM:
1434 message_severity = "Medium";
1435 log_level = G_LOG_LEVEL_WARNING;
1436 break;
1437 case GL_DEBUG_SEVERITY_LOW:
1438 message_severity = "Low";
1439 log_level = G_LOG_LEVEL_MESSAGE;
1440 break;
1441 case GL_DEBUG_SEVERITY_NOTIFICATION:
1442 message_severity = "Notification";
1443 log_level = G_LOG_LEVEL_INFO;
1444 break;
1445 default:
1446 message_severity = "Unknown";
1447 log_level = G_LOG_LEVEL_MESSAGE;
1448 }
1449
1450 /* There's no higher level function taking a log level argument... */
1451 g_log_structured_standard (G_LOG_DOMAIN, log_level,
1452 __FILE__, G_STRINGIFY (__LINE__),
1453 G_STRFUNC,
1454 message_format: "OPENGL:\n Source: %s\n Type: %s\n Severity: %s\n Message: %s",
1455 message_source, message_type, message_severity, message);
1456}
1457
1458/**
1459 * gdk_gl_context_realize:
1460 * @context: a `GdkGLContext`
1461 * @error: return location for a `GError`
1462 *
1463 * Realizes the given `GdkGLContext`.
1464 *
1465 * It is safe to call this function on a realized `GdkGLContext`.
1466 *
1467 * Returns: %TRUE if the context is realized
1468 */
1469gboolean
1470gdk_gl_context_realize (GdkGLContext *context,
1471 GError **error)
1472{
1473 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
1474
1475 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
1476
1477 if (priv->api)
1478 return TRUE;
1479
1480 priv->api = GDK_GL_CONTEXT_GET_CLASS (context)->realize (context, error);
1481
1482 if (priv->api)
1483 g_object_notify_by_pspec (G_OBJECT (context), pspec: properties[PROP_API]);
1484
1485 return priv->api;
1486}
1487
1488static void
1489gdk_gl_context_check_extensions (GdkGLContext *context)
1490{
1491 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
1492 gboolean gl_debug = FALSE;
1493#ifdef G_ENABLE_DEBUG
1494 GdkDisplay *display;
1495#endif
1496
1497 if (!gdk_gl_context_is_realized (context))
1498 return;
1499
1500 if (priv->extensions_checked)
1501 return;
1502
1503 priv->gl_version = epoxy_gl_version ();
1504
1505 priv->has_debug_output = epoxy_has_gl_extension (extension: "GL_ARB_debug_output") ||
1506 epoxy_has_gl_extension (extension: "GL_KHR_debug");
1507
1508#ifdef G_ENABLE_DEBUG
1509 display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
1510 gl_debug = GDK_DISPLAY_DEBUG_CHECK (display, GL_DEBUG);
1511#endif
1512
1513 if (priv->has_debug_output
1514#ifndef G_ENABLE_CONSISTENCY_CHECKS
1515 && gl_debug
1516#endif
1517 )
1518 {
1519 gdk_gl_context_make_current (context);
1520 glEnable (GL_DEBUG_OUTPUT);
1521 glEnable (GL_DEBUG_OUTPUT_SYNCHRONOUS);
1522 glDebugMessageCallback (gl_debug_message_callback, NULL);
1523 }
1524
1525 if (gdk_gl_context_get_use_es (context))
1526 {
1527 priv->has_unpack_subimage = epoxy_has_gl_extension (extension: "GL_EXT_unpack_subimage");
1528 priv->has_khr_debug = epoxy_has_gl_extension (extension: "GL_KHR_debug");
1529 }
1530 else
1531 {
1532 priv->has_unpack_subimage = TRUE;
1533 priv->has_khr_debug = epoxy_has_gl_extension (extension: "GL_KHR_debug");
1534
1535 /* We asked for a core profile, but we didn't get one, so we're in legacy mode */
1536 if (priv->gl_version < 32)
1537 priv->is_legacy = TRUE;
1538 }
1539
1540 if (priv->has_khr_debug && gl_debug)
1541 {
1542 priv->use_khr_debug = TRUE;
1543 glGetIntegerv (GL_MAX_LABEL_LENGTH, &priv->max_debug_label_length);
1544 }
1545
1546 priv->has_half_float = gdk_gl_context_check_version (self: context, required_gl_major: 3, required_gl_minor: 0, required_gles_major: 3, required_gles_minor: 0) ||
1547 epoxy_has_gl_extension (extension: "OES_vertex_half_float");
1548
1549 GDK_DISPLAY_NOTE (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)), OPENGL,
1550 g_message ("%s version: %d.%d (%s)\n"
1551 "* GLSL version: %s\n"
1552 "* Extensions checked:\n"
1553 " - GL_KHR_debug: %s\n"
1554 " - GL_EXT_unpack_subimage: %s\n"
1555 " - OES_vertex_half_float: %s",
1556 gdk_gl_context_get_use_es (context) ? "OpenGL ES" : "OpenGL",
1557 priv->gl_version / 10, priv->gl_version % 10,
1558 priv->is_legacy ? "legacy" : "core",
1559 glGetString (GL_SHADING_LANGUAGE_VERSION),
1560 priv->has_khr_debug ? "yes" : "no",
1561 priv->has_unpack_subimage ? "yes" : "no",
1562 priv->has_half_float ? "yes" : "no"));
1563
1564 priv->extensions_checked = TRUE;
1565}
1566
1567/**
1568 * gdk_gl_context_make_current:
1569 * @context: a `GdkGLContext`
1570 *
1571 * Makes the @context the current one.
1572 */
1573void
1574gdk_gl_context_make_current (GdkGLContext *context)
1575{
1576 MaskedContext *current, *masked_context;
1577 gboolean surfaceless;
1578
1579 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
1580
1581 surfaceless = !gdk_draw_context_is_in_frame (GDK_DRAW_CONTEXT (context));
1582 masked_context = mask_context (context, surfaceless);
1583
1584 current = g_private_get (key: &thread_current_context);
1585 if (current == masked_context)
1586 return;
1587
1588 /* we need to realize the GdkGLContext if it wasn't explicitly realized */
1589 if (!gdk_gl_context_is_realized (context))
1590 {
1591 GError *error = NULL;
1592
1593 gdk_gl_context_realize (context, error: &error);
1594 if (error != NULL)
1595 {
1596 g_critical ("Could not realize the GL context: %s", error->message);
1597 g_error_free (error);
1598 return;
1599 }
1600 }
1601
1602 if (!GDK_GL_CONTEXT_GET_CLASS (context)->make_current (context, surfaceless))
1603 {
1604 g_warning ("gdk_gl_context_make_current() failed");
1605 return;
1606 }
1607
1608 g_object_ref (context);
1609 g_private_replace (key: &thread_current_context, value: masked_context);
1610 gdk_gl_context_check_extensions (context);
1611}
1612
1613/**
1614 * gdk_gl_context_get_display:
1615 * @context: a `GdkGLContext`
1616 *
1617 * Retrieves the display the @context is created for
1618 *
1619 * Returns: (nullable) (transfer none): a `GdkDisplay`
1620 */
1621GdkDisplay *
1622gdk_gl_context_get_display (GdkGLContext *context)
1623{
1624 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
1625
1626 return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
1627}
1628
1629/**
1630 * gdk_gl_context_get_surface:
1631 * @context: a `GdkGLContext`
1632 *
1633 * Retrieves the surface used by the @context.
1634 *
1635 * Returns: (nullable) (transfer none): a `GdkSurface`
1636 */
1637GdkSurface *
1638gdk_gl_context_get_surface (GdkGLContext *context)
1639{
1640 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
1641
1642 return gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
1643}
1644
1645/**
1646 * gdk_gl_context_get_shared_context: (attributes org.gtk.Method.get_property=shared-context)
1647 * @context: a `GdkGLContext`
1648 *
1649 * Used to retrieves the `GdkGLContext` that this @context share data with.
1650 *
1651 * As many contexts can share data now and no single shared context exists
1652 * anymore, this function has been deprecated and now always returns %NULL.
1653 *
1654 * Returns: (nullable) (transfer none): %NULL
1655 *
1656 * Deprecated: 4.4: Use [method@Gdk.GLContext.is_shared] to check if contexts
1657 * can be shared.
1658 */
1659GdkGLContext *
1660gdk_gl_context_get_shared_context (GdkGLContext *context)
1661{
1662 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
1663
1664 return NULL;
1665}
1666
1667/**
1668 * gdk_gl_context_get_version:
1669 * @context: a `GdkGLContext`
1670 * @major: (out): return location for the major version
1671 * @minor: (out): return location for the minor version
1672 *
1673 * Retrieves the OpenGL version of the @context.
1674 *
1675 * The @context must be realized prior to calling this function.
1676 */
1677void
1678gdk_gl_context_get_version (GdkGLContext *context,
1679 int *major,
1680 int *minor)
1681{
1682 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self: context);
1683
1684 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
1685 g_return_if_fail (gdk_gl_context_is_realized (context));
1686
1687 if (major != NULL)
1688 *major = priv->gl_version / 10;
1689 if (minor != NULL)
1690 *minor = priv->gl_version % 10;
1691}
1692
1693/**
1694 * gdk_gl_context_clear_current:
1695 *
1696 * Clears the current `GdkGLContext`.
1697 *
1698 * Any OpenGL call after this function returns will be ignored
1699 * until [method@Gdk.GLContext.make_current] is called.
1700 */
1701void
1702gdk_gl_context_clear_current (void)
1703{
1704 MaskedContext *current;
1705
1706 current = g_private_get (key: &thread_current_context);
1707 if (current != NULL)
1708 {
1709 GdkGLContext *context = unmask_context (mask: current);
1710
1711 if (GDK_GL_CONTEXT_GET_CLASS (context)->clear_current (context))
1712 g_private_replace (key: &thread_current_context, NULL);
1713 }
1714}
1715
1716/*<private>
1717 * gdk_gl_context_clear_current_if_surface:
1718 * @surface: surface to clear for
1719 *
1720 * Does a gdk_gl_context_clear_current() if the current context is attached
1721 * to @surface, leaves the current context alone otherwise.
1722 **/
1723void
1724gdk_gl_context_clear_current_if_surface (GdkSurface *surface)
1725{
1726 MaskedContext *current;
1727
1728 current = g_private_get (key: &thread_current_context);
1729 if (current != NULL && !mask_is_surfaceless (mask: current))
1730 {
1731 GdkGLContext *context = unmask_context (mask: current);
1732
1733 if (gdk_gl_context_get_surface (context) != surface)
1734 return;
1735
1736 if (GDK_GL_CONTEXT_GET_CLASS (context)->clear_current (context))
1737 g_private_replace (key: &thread_current_context, NULL);
1738 }
1739}
1740
1741/**
1742 * gdk_gl_context_get_current:
1743 *
1744 * Retrieves the current `GdkGLContext`.
1745 *
1746 * Returns: (nullable) (transfer none): the current `GdkGLContext`
1747 */
1748GdkGLContext *
1749gdk_gl_context_get_current (void)
1750{
1751 MaskedContext *current;
1752
1753 current = g_private_get (key: &thread_current_context);
1754
1755 return unmask_context (mask: current);
1756}
1757
1758gboolean
1759gdk_gl_context_has_debug (GdkGLContext *self)
1760{
1761 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
1762
1763 return priv->debug_enabled || priv->use_khr_debug;
1764}
1765
1766gboolean
1767gdk_gl_context_has_vertex_half_float (GdkGLContext *self)
1768{
1769 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
1770
1771 return priv->has_half_float;
1772}
1773
1774/* This is currently private! */
1775/* When using GL/ES, don't flip the 'R' and 'B' bits on Windows/ANGLE for glReadPixels() */
1776gboolean
1777gdk_gl_context_use_es_bgra (GdkGLContext *context)
1778{
1779 if (!gdk_gl_context_get_use_es (context))
1780 return FALSE;
1781
1782#ifdef GDK_WINDOWING_WIN32
1783 if (GDK_WIN32_IS_GL_CONTEXT (context))
1784 return TRUE;
1785#endif
1786
1787 return FALSE;
1788}
1789
1790static GdkGLBackend the_gl_backend_type = GDK_GL_NONE;
1791
1792static const char *gl_backend_names[] = {
1793 [GDK_GL_NONE] = "No GL (You should never read this)",
1794 [GDK_GL_EGL] = "EGL",
1795 [GDK_GL_GLX] = "X11 GLX",
1796 [GDK_GL_WGL] = "Windows WGL",
1797 [GDK_GL_CGL] = "Apple CGL"
1798};
1799
1800/*<private>
1801 * gdk_gl_backend_can_be_used:
1802 * @backend_type: Type of backend to check
1803 * @error: Return location for an error
1804 *
1805 * Checks if this backend type can be used. When multiple displays
1806 * are opened that use different GL backends, conflicts can arise,
1807 * so this function checks that all displays use compatible GL
1808 * backends.
1809 *
1810 * Returns: %TRUE if the backend can still be used
1811 */
1812gboolean
1813gdk_gl_backend_can_be_used (GdkGLBackend backend_type,
1814 GError **error)
1815{
1816 if (the_gl_backend_type == GDK_GL_NONE ||
1817 the_gl_backend_type == backend_type)
1818 return TRUE;
1819
1820 g_set_error (err: error, GDK_GL_ERROR, code: GDK_GL_ERROR_NOT_AVAILABLE,
1821 /* translators: This is about OpenGL backend names, like
1822 * "Trying to use X11 GLX, but EGL is already in use" */
1823 _("Trying to use %s, but %s is already in use"),
1824 gl_backend_names[backend_type],
1825 gl_backend_names[the_gl_backend_type]);
1826 return FALSE;
1827}
1828
1829/*<private>
1830 * gdk_gl_backend_use:
1831 * @backend_type: Type of backend
1832 *
1833 * Ensures that the backend in use is the given one. If another backend
1834 * is already in use, this function will abort the program. It should
1835 * have previously checked via gdk_gl_backend_can_be_used().
1836 **/
1837void
1838gdk_gl_backend_use (GdkGLBackend backend_type)
1839{
1840 /* Check that the context class is properly initializing its backend type */
1841 g_assert (backend_type != GDK_GL_NONE);
1842
1843 if (the_gl_backend_type == GDK_GL_NONE)
1844 {
1845 the_gl_backend_type = backend_type;
1846 /* This is important!!!11eleven
1847 * (But really: How do I print a message in 2 categories?) */
1848 GDK_NOTE (OPENGL, g_print ("Using OpenGL backend %s\n", gl_backend_names[the_gl_backend_type]));
1849 GDK_NOTE (MISC, g_message ("Using Opengl backend %s", gl_backend_names[the_gl_backend_type]));
1850 }
1851
1852 g_assert (the_gl_backend_type == backend_type);
1853}
1854
1855

source code of gtk/gdk/gdkglcontext.c