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 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 "gdkinternals.h"
82 #include "gdkintl.h"
83 #include "gdkmemorytextureprivate.h"
84 
85 #include "gdk-private.h"
86 
87 #ifdef GDK_WINDOWING_WIN32
88 # include "gdk/win32/gdkwin32.h"
89 #endif
90 
91 #include <epoxy/gl.h>
92 
93 typedef struct {
94   int major;
95   int minor;
96   int gl_version;
97 
98   guint realized : 1;
99   guint use_texture_rectangle : 1;
100   guint has_khr_debug : 1;
101   guint use_khr_debug : 1;
102   guint has_unpack_subimage : 1;
103   guint has_debug_output : 1;
104   guint extensions_checked : 1;
105   guint debug_enabled : 1;
106   guint forward_compatible : 1;
107   guint is_legacy : 1;
108 
109   int use_es;
110 
111   int max_debug_label_length;
112 
113   GdkGLContextPaintData *paint_data;
114 } GdkGLContextPrivate;
115 
116 enum {
117   PROP_0,
118 
119   PROP_SHARED_CONTEXT,
120 
121   LAST_PROP
122 };
123 
124 static GParamSpec *obj_pspecs[LAST_PROP] = { NULL, };
125 
126 G_DEFINE_QUARK (gdk-gl-error-quark, gdk_gl_error)
127 
128 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkGLContext, gdk_gl_context, GDK_TYPE_DRAW_CONTEXT)
129 
130 typedef struct _MaskedContext MaskedContext;
131 
132 static inline MaskedContext *
mask_context(GdkGLContext * context,gboolean surfaceless)133 mask_context (GdkGLContext *context,
134               gboolean      surfaceless)
135 {
136   return (MaskedContext *) GSIZE_TO_POINTER (GPOINTER_TO_SIZE (context) | (surfaceless ? 1 : 0));
137 }
138 
139 static inline GdkGLContext *
unmask_context(MaskedContext * mask)140 unmask_context (MaskedContext *mask)
141 {
142   return GDK_GL_CONTEXT (GSIZE_TO_POINTER (GPOINTER_TO_SIZE (mask) & ~(gsize) 1));
143 }
144 
145 static void
unref_unmasked(gpointer data)146 unref_unmasked (gpointer data)
147 {
148   g_object_unref (unmask_context (data));
149 }
150 
151 static GPrivate thread_current_context = G_PRIVATE_INIT (unref_unmasked);
152 
153 static void
gdk_gl_context_clear_old_updated_area(GdkGLContext * context)154 gdk_gl_context_clear_old_updated_area (GdkGLContext *context)
155 {
156   int i;
157 
158   for (i = 0; i < 2; i++)
159     {
160       g_clear_pointer (&context->old_updated_area[i], cairo_region_destroy);
161     }
162 }
163 
164 static void
gdk_gl_context_dispose(GObject * gobject)165 gdk_gl_context_dispose (GObject *gobject)
166 {
167   GdkGLContext *context = GDK_GL_CONTEXT (gobject);
168   MaskedContext *current;
169 
170   gdk_gl_context_clear_old_updated_area (context);
171 
172   current = g_private_get (&thread_current_context);
173   if (unmask_context (current) == context)
174     g_private_replace (&thread_current_context, NULL);
175 
176   G_OBJECT_CLASS (gdk_gl_context_parent_class)->dispose (gobject);
177 }
178 
179 static void
gdk_gl_context_finalize(GObject * gobject)180 gdk_gl_context_finalize (GObject *gobject)
181 {
182   GdkGLContext *context = GDK_GL_CONTEXT (gobject);
183   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
184 
185   g_clear_pointer (&priv->paint_data, g_free);
186   G_OBJECT_CLASS (gdk_gl_context_parent_class)->finalize (gobject);
187 }
188 
189 static void
gdk_gl_context_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * pspec)190 gdk_gl_context_set_property (GObject      *gobject,
191                              guint         prop_id,
192                              const GValue *value,
193                              GParamSpec   *pspec)
194 {
195   switch (prop_id)
196     {
197     case PROP_SHARED_CONTEXT:
198       g_assert (g_value_get_object (value) == NULL);
199       break;
200 
201     default:
202       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
203     }
204 }
205 
206 static void
gdk_gl_context_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * pspec)207 gdk_gl_context_get_property (GObject    *gobject,
208                              guint       prop_id,
209                              GValue     *value,
210                              GParamSpec *pspec)
211 {
212   switch (prop_id)
213     {
214     case PROP_SHARED_CONTEXT:
215       g_value_set_object (value, NULL);
216       break;
217 
218     default:
219       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
220     }
221 }
222 
223 void
gdk_gl_context_upload_texture(GdkGLContext * context,const guchar * data,int width,int height,int stride,GdkMemoryFormat data_format,guint texture_target)224 gdk_gl_context_upload_texture (GdkGLContext    *context,
225                                const guchar    *data,
226                                int              width,
227                                int              height,
228                                int              stride,
229                                GdkMemoryFormat  data_format,
230                                guint            texture_target)
231 {
232   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
233   guchar *copy = NULL;
234   guint gl_format;
235   guint gl_type;
236   guint bpp;
237 
238   g_return_if_fail (GDK_IS_GL_CONTEXT (context));
239 
240   if (priv->use_es)
241     {
242       /* GLES only supports rgba, so convert if necessary */
243       if (data_format != GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
244         {
245           copy = g_malloc (width * height * 4);
246           gdk_memory_convert (copy, width * 4,
247                               GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
248                               data, stride, data_format,
249                               width, height);
250           stride = width * 4;
251           data = copy;
252         }
253 
254       bpp = 4;
255       gl_format = GL_RGBA;
256       gl_type = GL_UNSIGNED_BYTE;
257     }
258   else
259     {
260       if (data_format == GDK_MEMORY_DEFAULT) /* Cairo surface format */
261         {
262           gl_format = GL_BGRA;
263           gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
264           bpp = 4;
265         }
266       else if (data_format == GDK_MEMORY_R8G8B8) /* Pixmap non-alpha data */
267         {
268           gl_format = GL_RGB;
269           gl_type = GL_UNSIGNED_BYTE;
270           bpp = 3;
271         }
272       else if (data_format == GDK_MEMORY_B8G8R8)
273         {
274           gl_format = GL_BGR;
275           gl_type = GL_UNSIGNED_BYTE;
276           bpp = 3;
277         }
278       else /* Fall-back, convert to cairo-surface-format */
279         {
280           copy = g_malloc (width * height * 4);
281           gdk_memory_convert (copy, width * 4,
282                               GDK_MEMORY_DEFAULT,
283                               data, stride, data_format,
284                               width, height);
285           stride = width * 4;
286           bpp = 4;
287           data = copy;
288           gl_format = GL_BGRA;
289           gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
290         }
291     }
292 
293   /* GL_UNPACK_ROW_LENGTH is available on desktop GL, OpenGL ES >= 3.0, or if
294    * the GL_EXT_unpack_subimage extension for OpenGL ES 2.0 is available
295    */
296   if (stride == width * bpp)
297     {
298       glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
299 
300       glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data);
301       glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
302     }
303   else if ((!priv->use_es ||
304             (priv->use_es && (priv->gl_version >= 30 || priv->has_unpack_subimage))))
305     {
306       glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp);
307 
308       glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data);
309 
310       glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
311     }
312   else
313     {
314       int i;
315       glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, NULL);
316       for (i = 0; i < height; i++)
317         glTexSubImage2D (texture_target, 0, 0, i, width, 1, gl_format, gl_type, data + (i * stride));
318     }
319 
320   g_free (copy);
321 }
322 
323 static gboolean
gdk_gl_context_real_realize(GdkGLContext * self,GError ** error)324 gdk_gl_context_real_realize (GdkGLContext  *self,
325                              GError       **error)
326 {
327   g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
328                        "The current backend does not support OpenGL");
329 
330   return FALSE;
331 }
332 
333 static cairo_region_t *
gdk_gl_context_real_get_damage(GdkGLContext * context)334 gdk_gl_context_real_get_damage (GdkGLContext *context)
335 {
336   GdkSurface *surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
337 
338   return cairo_region_create_rectangle (&(GdkRectangle) {
339                                             0, 0,
340                                             gdk_surface_get_width (surface),
341                                             gdk_surface_get_height (surface)
342                                         });
343 }
344 
345 static gboolean
gdk_gl_context_real_is_shared(GdkGLContext * self,GdkGLContext * other)346 gdk_gl_context_real_is_shared (GdkGLContext *self,
347                                GdkGLContext *other)
348 {
349   if (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (self)) != gdk_draw_context_get_display (GDK_DRAW_CONTEXT (other)))
350     return FALSE;
351 
352   /* XXX: Should we check es or legacy here? */
353 
354   return TRUE;
355 }
356 
357 static void
gdk_gl_context_real_begin_frame(GdkDrawContext * draw_context,cairo_region_t * region)358 gdk_gl_context_real_begin_frame (GdkDrawContext *draw_context,
359                                  cairo_region_t *region)
360 {
361   GdkGLContext *context = GDK_GL_CONTEXT (draw_context);
362   GdkSurface *surface;
363   cairo_region_t *damage;
364   int ww, wh;
365 
366   damage = GDK_GL_CONTEXT_GET_CLASS (context)->get_damage (context);
367 
368   if (context->old_updated_area[1])
369     cairo_region_destroy (context->old_updated_area[1]);
370   context->old_updated_area[1] = context->old_updated_area[0];
371   context->old_updated_area[0] = cairo_region_copy (region);
372 
373   cairo_region_union (region, damage);
374   cairo_region_destroy (damage);
375 
376   surface = gdk_draw_context_get_surface (draw_context);
377   ww = gdk_surface_get_width (surface) * gdk_surface_get_scale_factor (surface);
378   wh = gdk_surface_get_height (surface) * gdk_surface_get_scale_factor (surface);
379 
380   gdk_gl_context_make_current (context);
381 
382   /* Initial setup */
383   glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
384   glDisable (GL_DEPTH_TEST);
385   glDisable (GL_BLEND);
386   glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
387 
388   glViewport (0, 0, ww, wh);
389 }
390 
391 static void
gdk_gl_context_real_end_frame(GdkDrawContext * draw_context,cairo_region_t * painted)392 gdk_gl_context_real_end_frame (GdkDrawContext *draw_context,
393                                cairo_region_t *painted)
394 {
395 }
396 
397 static void
gdk_gl_context_surface_resized(GdkDrawContext * draw_context)398 gdk_gl_context_surface_resized (GdkDrawContext *draw_context)
399 {
400   GdkGLContext *context = GDK_GL_CONTEXT (draw_context);
401 
402   gdk_gl_context_clear_old_updated_area (context);
403 }
404 
405 static void
gdk_gl_context_class_init(GdkGLContextClass * klass)406 gdk_gl_context_class_init (GdkGLContextClass *klass)
407 {
408   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
409   GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass);
410 
411   klass->realize = gdk_gl_context_real_realize;
412   klass->get_damage = gdk_gl_context_real_get_damage;
413   klass->is_shared = gdk_gl_context_real_is_shared;
414 
415   draw_context_class->begin_frame = gdk_gl_context_real_begin_frame;
416   draw_context_class->end_frame = gdk_gl_context_real_end_frame;
417   draw_context_class->surface_resized = gdk_gl_context_surface_resized;
418 
419   /**
420    * GdkGLContext:shared-context: (attributes org.gtk.Property.get=gdk_gl_context_get_shared_context)
421    *
422    * Always %NULL
423    *
424    * As many contexts can share data now and no single shared context exists
425    * anymore, this function has been deprecated and now always returns %NULL.
426    *
427    * Deprecated: 4.4: Use [method@Gdk.GLContext.is_shared] to check if contexts
428    *   can be shared.
429    */
430   obj_pspecs[PROP_SHARED_CONTEXT] =
431     g_param_spec_object ("shared-context",
432                          P_("Shared context"),
433                          P_("The GL context this context shares data with"),
434                          GDK_TYPE_GL_CONTEXT,
435                          G_PARAM_READWRITE |
436                          G_PARAM_CONSTRUCT_ONLY |
437                          G_PARAM_STATIC_STRINGS |
438                          G_PARAM_DEPRECATED);
439 
440   gobject_class->set_property = gdk_gl_context_set_property;
441   gobject_class->get_property = gdk_gl_context_get_property;
442   gobject_class->dispose = gdk_gl_context_dispose;
443   gobject_class->finalize = gdk_gl_context_finalize;
444 
445   g_object_class_install_properties (gobject_class, LAST_PROP, obj_pspecs);
446 }
447 
448 static void
gdk_gl_context_init(GdkGLContext * self)449 gdk_gl_context_init (GdkGLContext *self)
450 {
451   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
452 
453   priv->use_es = -1;
454 }
455 
456 /* Must have called gdk_display_prepare_gl() before */
457 GdkGLContext *
gdk_gl_context_new_for_surface(GdkSurface * surface)458 gdk_gl_context_new_for_surface (GdkSurface *surface)
459 {
460   GdkDisplay *display = gdk_surface_get_display (surface);
461   GdkGLContext *shared = gdk_display_get_gl_context (display);
462 
463   /* assert gdk_display_prepare_gl() had been called */
464   g_assert (shared);
465 
466   return g_object_new (G_OBJECT_TYPE (shared),
467                        "surface", surface,
468                        NULL);
469 }
470 
471 GdkGLContextPaintData *
gdk_gl_context_get_paint_data(GdkGLContext * context)472 gdk_gl_context_get_paint_data (GdkGLContext *context)
473 {
474   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
475 
476   if (priv->paint_data == NULL)
477     {
478       priv->paint_data = g_new0 (GdkGLContextPaintData, 1);
479       priv->paint_data->is_legacy = priv->is_legacy;
480       priv->paint_data->use_es = priv->use_es;
481     }
482 
483   return priv->paint_data;
484 }
485 
486 gboolean
gdk_gl_context_use_texture_rectangle(GdkGLContext * context)487 gdk_gl_context_use_texture_rectangle (GdkGLContext *context)
488 {
489   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
490 
491   return priv->use_texture_rectangle;
492 }
493 
494 void
gdk_gl_context_push_debug_group(GdkGLContext * context,const char * message)495 gdk_gl_context_push_debug_group (GdkGLContext *context,
496                                  const char   *message)
497 {
498   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
499 
500   if (priv->use_khr_debug)
501     glPushDebugGroupKHR (GL_DEBUG_SOURCE_APPLICATION, 0, -1, message);
502 }
503 
504 void
gdk_gl_context_push_debug_group_printf(GdkGLContext * context,const char * format,...)505 gdk_gl_context_push_debug_group_printf (GdkGLContext *context,
506                                         const char   *format,
507                                         ...)
508 {
509   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
510   char *message;
511   va_list args;
512 
513   if (priv->use_khr_debug)
514     {
515       int msg_len;
516 
517       va_start (args, format);
518       message = g_strdup_vprintf (format, args);
519       va_end (args);
520 
521       msg_len = MIN (priv->max_debug_label_length, strlen (message) - 1);
522       glPushDebugGroupKHR (GL_DEBUG_SOURCE_APPLICATION, 0, msg_len, message);
523       g_free (message);
524     }
525 }
526 
527 void
gdk_gl_context_pop_debug_group(GdkGLContext * context)528 gdk_gl_context_pop_debug_group (GdkGLContext *context)
529 {
530   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
531 
532   if (priv->use_khr_debug)
533     glPopDebugGroupKHR ();
534 }
535 
536 void
gdk_gl_context_label_object(GdkGLContext * context,guint identifier,guint name,const char * label)537 gdk_gl_context_label_object (GdkGLContext *context,
538                              guint         identifier,
539                              guint         name,
540                              const char   *label)
541 {
542   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
543 
544   if (priv->use_khr_debug)
545     glObjectLabel (identifier, name, -1, label);
546 }
547 
548 void
gdk_gl_context_label_object_printf(GdkGLContext * context,guint identifier,guint name,const char * format,...)549 gdk_gl_context_label_object_printf  (GdkGLContext *context,
550                                      guint         identifier,
551                                      guint         name,
552                                      const char   *format,
553                                      ...)
554 {
555   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
556   char *message;
557   va_list args;
558 
559   if (priv->use_khr_debug)
560     {
561       int msg_len;
562 
563       va_start (args, format);
564       message = g_strdup_vprintf (format, args);
565       va_end (args);
566 
567       msg_len = MIN (priv->max_debug_label_length, strlen (message) - 1);
568 
569       glObjectLabel (identifier, name, msg_len, message);
570       g_free (message);
571     }
572 }
573 
574 
575 gboolean
gdk_gl_context_has_unpack_subimage(GdkGLContext * context)576 gdk_gl_context_has_unpack_subimage (GdkGLContext *context)
577 {
578   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
579 
580   return priv->has_unpack_subimage;
581 }
582 
583 /**
584  * gdk_gl_context_set_debug_enabled:
585  * @context: a `GdkGLContext`
586  * @enabled: whether to enable debugging in the context
587  *
588  * Sets whether the `GdkGLContext` should perform extra validations and
589  * runtime checking.
590  *
591  * This is useful during development, but has additional overhead.
592  *
593  * The `GdkGLContext` must not be realized or made current prior to
594  * calling this function.
595  */
596 void
gdk_gl_context_set_debug_enabled(GdkGLContext * context,gboolean enabled)597 gdk_gl_context_set_debug_enabled (GdkGLContext *context,
598                                   gboolean      enabled)
599 {
600   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
601 
602   g_return_if_fail (GDK_IS_GL_CONTEXT (context));
603   g_return_if_fail (!priv->realized);
604 
605   enabled = !!enabled;
606 
607   priv->debug_enabled = enabled;
608 }
609 
610 /**
611  * gdk_gl_context_get_debug_enabled:
612  * @context: a `GdkGLContext`
613  *
614  * Retrieves whether the context is doing extra validations and runtime checking.
615  *
616  * See [method@Gdk.GLContext.set_debug_enabled].
617  *
618  * Returns: %TRUE if debugging is enabled
619  */
620 gboolean
gdk_gl_context_get_debug_enabled(GdkGLContext * context)621 gdk_gl_context_get_debug_enabled (GdkGLContext *context)
622 {
623   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
624 
625   g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
626 
627   return priv->debug_enabled;
628 }
629 
630 /**
631  * gdk_gl_context_set_forward_compatible:
632  * @context: a `GdkGLContext`
633  * @compatible: whether the context should be forward-compatible
634  *
635  * Sets whether the `GdkGLContext` should be forward-compatible.
636  *
637  * Forward-compatible contexts must not support OpenGL functionality that
638  * has been marked as deprecated in the requested version; non-forward
639  * compatible contexts, on the other hand, must support both deprecated and
640  * non deprecated functionality.
641  *
642  * The `GdkGLContext` must not be realized or made current prior to calling
643  * this function.
644  */
645 void
gdk_gl_context_set_forward_compatible(GdkGLContext * context,gboolean compatible)646 gdk_gl_context_set_forward_compatible (GdkGLContext *context,
647                                        gboolean      compatible)
648 {
649   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
650 
651   g_return_if_fail (GDK_IS_GL_CONTEXT (context));
652   g_return_if_fail (!priv->realized);
653 
654   compatible = !!compatible;
655 
656   priv->forward_compatible = compatible;
657 }
658 
659 /**
660  * gdk_gl_context_get_forward_compatible:
661  * @context: a `GdkGLContext`
662  *
663  * Retrieves whether the context is forward-compatible.
664  *
665  * See [method@Gdk.GLContext.set_forward_compatible].
666  *
667  * Returns: %TRUE if the context should be forward-compatible
668  */
669 gboolean
gdk_gl_context_get_forward_compatible(GdkGLContext * context)670 gdk_gl_context_get_forward_compatible (GdkGLContext *context)
671 {
672   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
673 
674   g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
675 
676   return priv->forward_compatible;
677 }
678 
679 /**
680  * gdk_gl_context_set_required_version:
681  * @context: a `GdkGLContext`
682  * @major: the major version to request
683  * @minor: the minor version to request
684  *
685  * Sets the major and minor version of OpenGL to request.
686  *
687  * Setting @major and @minor to zero will use the default values.
688  *
689  * The `GdkGLContext` must not be realized or made current prior to calling
690  * this function.
691  */
692 void
gdk_gl_context_set_required_version(GdkGLContext * context,int major,int minor)693 gdk_gl_context_set_required_version (GdkGLContext *context,
694                                      int           major,
695                                      int           minor)
696 {
697   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
698   gboolean force_gles = FALSE;
699   int version, min_ver;
700 #ifdef G_ENABLE_DEBUG
701   GdkDisplay *display;
702 #endif
703 
704   g_return_if_fail (GDK_IS_GL_CONTEXT (context));
705   g_return_if_fail (!priv->realized);
706 
707   /* this will take care of the default */
708   if (major == 0 && minor == 0)
709     {
710       priv->major = 0;
711       priv->minor = 0;
712       return;
713     }
714 
715   version = (major * 100) + minor;
716 
717 #ifdef G_ENABLE_DEBUG
718   display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
719   force_gles = GDK_DISPLAY_DEBUG_CHECK (display, GL_GLES);
720 #endif
721   /* Enforce a minimum context version number of 3.2 for desktop GL,
722    * and 2.0 for GLES
723    */
724   if (priv->use_es > 0 || force_gles)
725     min_ver = 200;
726   else
727     min_ver = 302;
728 
729   if (version < min_ver)
730     {
731       g_warning ("gdk_gl_context_set_required_version - GL context versions less than 3.2 are not supported.");
732       version = min_ver;
733     }
734   priv->major = version / 100;
735   priv->minor = version % 100;
736 }
737 
738 /**
739  * gdk_gl_context_get_required_version:
740  * @context: a `GdkGLContext`
741  * @major: (out) (nullable): return location for the major version to request
742  * @minor: (out) (nullable): return location for the minor version to request
743  *
744  * Retrieves required OpenGL version.
745  *
746  * See [method@Gdk.GLContext.set_required_version].
747  */
748 void
gdk_gl_context_get_required_version(GdkGLContext * context,int * major,int * minor)749 gdk_gl_context_get_required_version (GdkGLContext *context,
750                                      int          *major,
751                                      int          *minor)
752 {
753   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
754   gboolean force_gles = FALSE;
755 #ifdef G_ENABLE_DEBUG
756   GdkDisplay *display;
757 #endif
758   int default_major, default_minor;
759   int maj, min;
760 
761   g_return_if_fail (GDK_IS_GL_CONTEXT (context));
762 
763 #ifdef G_ENABLE_DEBUG
764   display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
765   force_gles = GDK_DISPLAY_DEBUG_CHECK (display, GL_GLES);
766 #endif
767 
768   /* Default fallback values for uninitialised contexts; we
769    * enforce a context version number of 3.2 for desktop GL,
770    * and 2.0 for GLES
771    */
772   if (priv->use_es > 0 || force_gles)
773     {
774       default_major = 2;
775       default_minor = 0;
776     }
777   else
778     {
779       default_major = 3;
780       default_minor = 2;
781     }
782 
783   if (priv->major > 0)
784     maj = priv->major;
785   else
786     maj = default_major;
787 
788   if (priv->minor > 0)
789     min = priv->minor;
790   else
791     min = default_minor;
792 
793   if (major != NULL)
794     *major = maj;
795   if (minor != NULL)
796     *minor = min;
797 }
798 
799 /**
800  * gdk_gl_context_is_legacy:
801  * @context: a `GdkGLContext`
802  *
803  * Whether the `GdkGLContext` is in legacy mode or not.
804  *
805  * The `GdkGLContext` must be realized before calling this function.
806  *
807  * When realizing a GL context, GDK will try to use the OpenGL 3.2 core
808  * profile; this profile removes all the OpenGL API that was deprecated
809  * prior to the 3.2 version of the specification. If the realization is
810  * successful, this function will return %FALSE.
811  *
812  * If the underlying OpenGL implementation does not support core profiles,
813  * GDK will fall back to a pre-3.2 compatibility profile, and this function
814  * will return %TRUE.
815  *
816  * You can use the value returned by this function to decide which kind
817  * of OpenGL API to use, or whether to do extension discovery, or what
818  * kind of shader programs to load.
819  *
820  * Returns: %TRUE if the GL context is in legacy mode
821  */
822 gboolean
gdk_gl_context_is_legacy(GdkGLContext * context)823 gdk_gl_context_is_legacy (GdkGLContext *context)
824 {
825   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
826 
827   g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
828   g_return_val_if_fail (priv->realized, FALSE);
829 
830   return priv->is_legacy;
831 }
832 
833 void
gdk_gl_context_set_is_legacy(GdkGLContext * context,gboolean is_legacy)834 gdk_gl_context_set_is_legacy (GdkGLContext *context,
835                               gboolean      is_legacy)
836 {
837   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
838 
839   priv->is_legacy = !!is_legacy;
840 }
841 
842 /**
843  * gdk_gl_context_is_shared:
844  * @self: a `GdkGLContext`
845  * @other: the `GdkGLContext` that should be compatible with @self
846  *
847  * Checks if the two GL contexts can share resources.
848  *
849  * When they can, the texture IDs from @other can be used in @self. This
850  * is particularly useful when passing `GdkGLTexture` objects between
851  * different contexts.
852  *
853  * Contexts created for the same display with the same properties will
854  * always be compatible, even if they are created for different surfaces.
855  * For other contexts it depends on the GL backend.
856  *
857  * Both contexts must be realized for this check to succeed. If either one
858  * is not, this function will return %FALSE.
859  *
860  * Returns: %TRUE if the two GL contexts are compatible.
861  *
862  * Since: 4.4
863  */
864 gboolean
gdk_gl_context_is_shared(GdkGLContext * self,GdkGLContext * other)865 gdk_gl_context_is_shared (GdkGLContext *self,
866                           GdkGLContext *other)
867 {
868   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
869   GdkGLContextPrivate *priv_other = gdk_gl_context_get_instance_private (other);
870 
871   g_return_val_if_fail (GDK_IS_GL_CONTEXT (self), FALSE);
872   g_return_val_if_fail (GDK_IS_GL_CONTEXT (other), FALSE);
873 
874   if (!priv->realized || !priv_other->realized)
875     return FALSE;
876 
877   return GDK_GL_CONTEXT_GET_CLASS (self)->is_shared (self, other);
878 }
879 
880 /**
881  * gdk_gl_context_set_use_es:
882  * @context: a `GdkGLContext`
883  * @use_es: whether the context should use OpenGL ES instead of OpenGL,
884  *   or -1 to allow auto-detection
885  *
886  * Requests that GDK create an OpenGL ES context instead of an OpenGL one.
887  *
888  * Not all platforms support OpenGL ES.
889  *
890  * The @context must not have been realized.
891  *
892  * By default, GDK will attempt to automatically detect whether the
893  * underlying GL implementation is OpenGL or OpenGL ES once the @context
894  * is realized.
895  *
896  * You should check the return value of [method@Gdk.GLContext.get_use_es]
897  * after calling [method@Gdk.GLContext.realize] to decide whether to use
898  * the OpenGL or OpenGL ES API, extensions, or shaders.
899  */
900 void
gdk_gl_context_set_use_es(GdkGLContext * context,int use_es)901 gdk_gl_context_set_use_es (GdkGLContext *context,
902                            int           use_es)
903 {
904   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
905 
906   g_return_if_fail (GDK_IS_GL_CONTEXT (context));
907   g_return_if_fail (!priv->realized);
908 
909   if (priv->use_es != use_es)
910     priv->use_es = use_es;
911 }
912 
913 /**
914  * gdk_gl_context_get_use_es:
915  * @context: a `GdkGLContext`
916  *
917  * Checks whether the @context is using an OpenGL or OpenGL ES profile.
918  *
919  * Returns: %TRUE if the `GdkGLContext` is using an OpenGL ES profile
920  */
921 gboolean
gdk_gl_context_get_use_es(GdkGLContext * context)922 gdk_gl_context_get_use_es (GdkGLContext *context)
923 {
924   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
925 
926   g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
927 
928   if (!priv->realized)
929     return FALSE;
930 
931   return priv->use_es > 0;
932 }
933 
934 static void APIENTRY
gl_debug_message_callback(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar * message,const void * user_data)935 gl_debug_message_callback (GLenum        source,
936                            GLenum        type,
937                            GLuint        id,
938                            GLenum        severity,
939                            GLsizei       length,
940                            const GLchar *message,
941                            const void   *user_data)
942 {
943   const char *message_source;
944   const char *message_type;
945   const char *message_severity;
946 
947   if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
948     return;
949 
950   switch (source)
951     {
952     case GL_DEBUG_SOURCE_API:
953       message_source = "API";
954       break;
955     case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
956       message_source = "Window System";
957       break;
958     case GL_DEBUG_SOURCE_SHADER_COMPILER:
959       message_source = "Shader Compiler";
960       break;
961     case GL_DEBUG_SOURCE_THIRD_PARTY:
962       message_source = "Third Party";
963       break;
964     case GL_DEBUG_SOURCE_APPLICATION:
965       message_source = "Application";
966       break;
967     case GL_DEBUG_SOURCE_OTHER:
968     default:
969       message_source = "Other";
970     }
971 
972   switch (type)
973     {
974     case GL_DEBUG_TYPE_ERROR:
975       message_type = "Error";
976       break;
977     case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
978       message_type = "Deprecated Behavior";
979       break;
980     case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
981       message_type = "Undefined Behavior";
982       break;
983     case GL_DEBUG_TYPE_PORTABILITY:
984       message_type = "Portability";
985       break;
986     case GL_DEBUG_TYPE_PERFORMANCE:
987       message_type = "Performance";
988       break;
989     case GL_DEBUG_TYPE_MARKER:
990       message_type = "Marker";
991       break;
992     case GL_DEBUG_TYPE_PUSH_GROUP:
993       message_type = "Push Group";
994       break;
995     case GL_DEBUG_TYPE_POP_GROUP:
996       message_type = "Pop Group";
997       break;
998     case GL_DEBUG_TYPE_OTHER:
999     default:
1000       message_type = "Other";
1001     }
1002 
1003   switch (severity)
1004     {
1005     case GL_DEBUG_SEVERITY_HIGH:
1006       message_severity = "High";
1007       break;
1008     case GL_DEBUG_SEVERITY_MEDIUM:
1009       message_severity = "Medium";
1010       break;
1011     case GL_DEBUG_SEVERITY_LOW:
1012       message_severity = "Low";
1013       break;
1014     case GL_DEBUG_SEVERITY_NOTIFICATION:
1015       message_severity = "Notification";
1016       break;
1017     default:
1018       message_severity = "Unknown";
1019     }
1020 
1021   g_warning ("OPENGL:\n    Source: %s\n    Type: %s\n    Severity: %s\n    Message: %s",
1022              message_source, message_type, message_severity, message);
1023 }
1024 
1025 /**
1026  * gdk_gl_context_realize:
1027  * @context: a `GdkGLContext`
1028  * @error: return location for a `GError`
1029  *
1030  * Realizes the given `GdkGLContext`.
1031  *
1032  * It is safe to call this function on a realized `GdkGLContext`.
1033  *
1034  * Returns: %TRUE if the context is realized
1035  */
1036 gboolean
gdk_gl_context_realize(GdkGLContext * context,GError ** error)1037 gdk_gl_context_realize (GdkGLContext  *context,
1038                         GError       **error)
1039 {
1040   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
1041 
1042   g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
1043 
1044   if (priv->realized)
1045     return TRUE;
1046 
1047   priv->realized = GDK_GL_CONTEXT_GET_CLASS (context)->realize (context, error);
1048 
1049   return priv->realized;
1050 }
1051 
1052 static void
gdk_gl_context_check_extensions(GdkGLContext * context)1053 gdk_gl_context_check_extensions (GdkGLContext *context)
1054 {
1055   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
1056   gboolean has_npot, has_texture_rectangle;
1057   gboolean gl_debug = FALSE;
1058 #ifdef G_ENABLE_DEBUG
1059   GdkDisplay *display;
1060 #endif
1061 
1062   if (!priv->realized)
1063     return;
1064 
1065   if (priv->extensions_checked)
1066     return;
1067 
1068   priv->gl_version = epoxy_gl_version ();
1069 
1070   if (priv->use_es < 0)
1071     priv->use_es = !epoxy_is_desktop_gl ();
1072 
1073   priv->has_debug_output = epoxy_has_gl_extension ("GL_ARB_debug_output") ||
1074                            epoxy_has_gl_extension ("GL_KHR_debug");
1075 
1076 #ifdef G_ENABLE_DEBUG
1077   display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
1078   gl_debug = GDK_DISPLAY_DEBUG_CHECK (display, GL_DEBUG);
1079 #endif
1080 
1081   if (priv->has_debug_output
1082 #ifndef G_ENABLE_CONSISTENCY_CHECKS
1083       && gl_debug
1084 #endif
1085       )
1086     {
1087       gdk_gl_context_make_current (context);
1088       glEnable (GL_DEBUG_OUTPUT);
1089       glEnable (GL_DEBUG_OUTPUT_SYNCHRONOUS);
1090       glDebugMessageCallback (gl_debug_message_callback, NULL);
1091     }
1092 
1093   if (priv->use_es)
1094     {
1095       has_npot = priv->gl_version >= 20;
1096       has_texture_rectangle = FALSE;
1097 
1098       priv->has_unpack_subimage = epoxy_has_gl_extension ("GL_EXT_unpack_subimage");
1099       priv->has_khr_debug = epoxy_has_gl_extension ("GL_KHR_debug");
1100     }
1101   else
1102     {
1103       has_npot = priv->gl_version >= 20 || epoxy_has_gl_extension ("GL_ARB_texture_non_power_of_two");
1104       has_texture_rectangle = priv->gl_version >= 31 || epoxy_has_gl_extension ("GL_ARB_texture_rectangle");
1105 
1106       priv->has_unpack_subimage = TRUE;
1107       priv->has_khr_debug = epoxy_has_gl_extension ("GL_KHR_debug");
1108 
1109       /* We asked for a core profile, but we didn't get one, so we're in legacy mode */
1110       if (priv->gl_version < 32)
1111         priv->is_legacy = TRUE;
1112     }
1113 
1114   if (priv->has_khr_debug && gl_debug)
1115     {
1116       priv->use_khr_debug = TRUE;
1117       glGetIntegerv (GL_MAX_LABEL_LENGTH, &priv->max_debug_label_length);
1118     }
1119   if (!priv->use_es && GDK_DISPLAY_DEBUG_CHECK (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)), GL_TEXTURE_RECT))
1120     priv->use_texture_rectangle = TRUE;
1121   else if (has_npot)
1122     priv->use_texture_rectangle = FALSE;
1123   else if (has_texture_rectangle)
1124     priv->use_texture_rectangle = TRUE;
1125   else
1126     g_warning ("GL implementation doesn't support any form of non-power-of-two textures");
1127 
1128   GDK_DISPLAY_NOTE (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)), OPENGL,
1129     g_message ("%s version: %d.%d (%s)\n"
1130                        "* GLSL version: %s\n"
1131                        "* Extensions checked:\n"
1132                        " - GL_ARB_texture_non_power_of_two: %s\n"
1133                        " - GL_ARB_texture_rectangle: %s\n"
1134                        " - GL_KHR_debug: %s\n"
1135                        " - GL_EXT_unpack_subimage: %s\n"
1136                        "* Using texture rectangle: %s",
1137                        priv->use_es ? "OpenGL ES" : "OpenGL",
1138                        priv->gl_version / 10, priv->gl_version % 10,
1139                        priv->is_legacy ? "legacy" : "core",
1140                        glGetString (GL_SHADING_LANGUAGE_VERSION),
1141                        has_npot ? "yes" : "no",
1142                        has_texture_rectangle ? "yes" : "no",
1143                        priv->has_khr_debug ? "yes" : "no",
1144                        priv->has_unpack_subimage ? "yes" : "no",
1145                        priv->use_texture_rectangle ? "yes" : "no"));
1146 
1147   priv->extensions_checked = TRUE;
1148 }
1149 
1150 /**
1151  * gdk_gl_context_make_current:
1152  * @context: a `GdkGLContext`
1153  *
1154  * Makes the @context the current one.
1155  */
1156 void
gdk_gl_context_make_current(GdkGLContext * context)1157 gdk_gl_context_make_current (GdkGLContext *context)
1158 {
1159   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
1160   MaskedContext *current, *masked_context;
1161   gboolean surfaceless;
1162 
1163   g_return_if_fail (GDK_IS_GL_CONTEXT (context));
1164 
1165   surfaceless = !gdk_draw_context_is_in_frame (GDK_DRAW_CONTEXT (context));
1166   masked_context = mask_context (context, surfaceless);
1167 
1168   current = g_private_get (&thread_current_context);
1169   if (current == masked_context)
1170     return;
1171 
1172   /* we need to realize the GdkGLContext if it wasn't explicitly realized */
1173   if (!priv->realized)
1174     {
1175       GError *error = NULL;
1176 
1177       gdk_gl_context_realize (context, &error);
1178       if (error != NULL)
1179         {
1180           g_critical ("Could not realize the GL context: %s", error->message);
1181           g_error_free (error);
1182           return;
1183         }
1184     }
1185 
1186   if (!GDK_GL_CONTEXT_GET_CLASS (context)->make_current (context, surfaceless))
1187     {
1188       g_warning ("gdk_gl_context_make_current() failed");
1189       return;
1190     }
1191 
1192   g_object_ref (context);
1193   g_private_replace (&thread_current_context, masked_context);
1194   gdk_gl_context_check_extensions (context);
1195 }
1196 
1197 /**
1198  * gdk_gl_context_get_display:
1199  * @context: a `GdkGLContext`
1200  *
1201  * Retrieves the display the @context is created for
1202  *
1203  * Returns: (nullable) (transfer none): a `GdkDisplay`
1204  */
1205 GdkDisplay *
gdk_gl_context_get_display(GdkGLContext * context)1206 gdk_gl_context_get_display (GdkGLContext *context)
1207 {
1208   g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
1209 
1210   return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
1211 }
1212 
1213 /**
1214  * gdk_gl_context_get_surface:
1215  * @context: a `GdkGLContext`
1216  *
1217  * Retrieves the surface used by the @context.
1218  *
1219  * Returns: (nullable) (transfer none): a `GdkSurface`
1220  */
1221 GdkSurface *
gdk_gl_context_get_surface(GdkGLContext * context)1222 gdk_gl_context_get_surface (GdkGLContext *context)
1223 {
1224   g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
1225 
1226   return gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
1227 }
1228 
1229 /**
1230  * gdk_gl_context_get_shared_context: (attributes org.gtk.Method.get_property=shared-context)
1231  * @context: a `GdkGLContext`
1232  *
1233  * Used to retrieves the `GdkGLContext` that this @context share data with.
1234  *
1235  * As many contexts can share data now and no single shared context exists
1236  * anymore, this function has been deprecated and now always returns %NULL.
1237  *
1238  * Returns: (nullable) (transfer none): %NULL
1239  *
1240  * Deprecated: 4.4: Use [method@Gdk.GLContext.is_shared] to check if contexts
1241  *   can be shared.
1242  */
1243 GdkGLContext *
gdk_gl_context_get_shared_context(GdkGLContext * context)1244 gdk_gl_context_get_shared_context (GdkGLContext *context)
1245 {
1246   g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
1247 
1248   return NULL;
1249 }
1250 
1251 /**
1252  * gdk_gl_context_get_version:
1253  * @context: a `GdkGLContext`
1254  * @major: (out): return location for the major version
1255  * @minor: (out): return location for the minor version
1256  *
1257  * Retrieves the OpenGL version of the @context.
1258  *
1259  * The @context must be realized prior to calling this function.
1260  */
1261 void
gdk_gl_context_get_version(GdkGLContext * context,int * major,int * minor)1262 gdk_gl_context_get_version (GdkGLContext *context,
1263                             int          *major,
1264                             int          *minor)
1265 {
1266   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
1267 
1268   g_return_if_fail (GDK_IS_GL_CONTEXT (context));
1269   g_return_if_fail (priv->realized);
1270 
1271   if (major != NULL)
1272     *major = priv->gl_version / 10;
1273   if (minor != NULL)
1274     *minor = priv->gl_version % 10;
1275 }
1276 
1277 /**
1278  * gdk_gl_context_clear_current:
1279  *
1280  * Clears the current `GdkGLContext`.
1281  *
1282  * Any OpenGL call after this function returns will be ignored
1283  * until [method@Gdk.GLContext.make_current] is called.
1284  */
1285 void
gdk_gl_context_clear_current(void)1286 gdk_gl_context_clear_current (void)
1287 {
1288   MaskedContext *current;
1289 
1290   current = g_private_get (&thread_current_context);
1291   if (current != NULL)
1292     {
1293       GdkGLContext *context = unmask_context (current);
1294 
1295       if (GDK_GL_CONTEXT_GET_CLASS (context)->clear_current (context))
1296         g_private_replace (&thread_current_context, NULL);
1297     }
1298 }
1299 
1300 /**
1301  * gdk_gl_context_get_current:
1302  *
1303  * Retrieves the current `GdkGLContext`.
1304  *
1305  * Returns: (nullable) (transfer none): the current `GdkGLContext`
1306  */
1307 GdkGLContext *
gdk_gl_context_get_current(void)1308 gdk_gl_context_get_current (void)
1309 {
1310   MaskedContext *current;
1311 
1312   current = g_private_get (&thread_current_context);
1313 
1314   return unmask_context (current);
1315 }
1316 
1317 gboolean
gdk_gl_context_has_debug(GdkGLContext * self)1318 gdk_gl_context_has_debug (GdkGLContext *self)
1319 {
1320   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
1321 
1322   return priv->debug_enabled || priv->use_khr_debug;
1323 }
1324 
1325 /* This is currently private! */
1326 /* When using GL/ES, don't flip the 'R' and 'B' bits on Windows/ANGLE for glReadPixels() */
1327 gboolean
gdk_gl_context_use_es_bgra(GdkGLContext * context)1328 gdk_gl_context_use_es_bgra (GdkGLContext *context)
1329 {
1330   if (!gdk_gl_context_get_use_es (context))
1331     return FALSE;
1332 
1333 #ifdef GDK_WINDOWING_WIN32
1334   if (GDK_WIN32_IS_GL_CONTEXT (context))
1335     return TRUE;
1336 #endif
1337 
1338   return FALSE;
1339 }
1340 
1341 static GdkGLBackend the_gl_backend_type = GDK_GL_NONE;
1342 
1343 static const char *gl_backend_names[] = {
1344   [GDK_GL_NONE] = "No GL (You should never read this)",
1345   [GDK_GL_EGL] = "EGL",
1346   [GDK_GL_GLX] = "X11 GLX",
1347   [GDK_GL_WGL] = "Windows WGL",
1348   [GDK_GL_CGL] = "Apple CGL"
1349 };
1350 
1351 /*<private>
1352  * gdk_gl_backend_can_be_used:
1353  * @backend_type: Type of backend to check
1354  * @error: Return location for an error
1355  *
1356  * Checks if this backend type can be used. When multiple displays
1357  * are opened that use different GL backends, conflicts can arise,
1358  * so this function checks that all displays use compatible GL
1359  * backends.
1360  *
1361  * Returns: %TRUE if the backend can still be used
1362  */
1363 gboolean
gdk_gl_backend_can_be_used(GdkGLBackend backend_type,GError ** error)1364 gdk_gl_backend_can_be_used (GdkGLBackend   backend_type,
1365                             GError       **error)
1366 {
1367   if (the_gl_backend_type == GDK_GL_NONE ||
1368       the_gl_backend_type == backend_type)
1369     return TRUE;
1370 
1371   g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
1372                /* translators: This is about OpenGL backend names, like
1373                 * "Trying to use X11 GLX, but EGL is already in use" */
1374                _("Trying to use %s, but %s is already in use"),
1375                gl_backend_names[backend_type],
1376                gl_backend_names[the_gl_backend_type]);
1377   return FALSE;
1378 }
1379 
1380 /*<private>
1381  * gdk_gl_backend_use:
1382  * @backend_type: Type of backend
1383  *
1384  * Ensures that the backend in use is the given one. If another backend
1385  * is already in use, this function will abort the program. It should
1386  * have previously checked via gdk_gl_backend_can_be_used().
1387  **/
1388 void
gdk_gl_backend_use(GdkGLBackend backend_type)1389 gdk_gl_backend_use (GdkGLBackend backend_type)
1390 {
1391   /* Check that the context class is properly initializing its backend type */
1392   g_assert (backend_type != GDK_GL_NONE);
1393 
1394   if (the_gl_backend_type == GDK_GL_NONE)
1395     {
1396       the_gl_backend_type = backend_type;
1397       /* This is important!!!11eleven
1398        * (But really: How do I print a message in 2 categories?) */
1399       GDK_NOTE (OPENGL, g_print ("Using OpenGL backend %s\n", gl_backend_names[the_gl_backend_type]));
1400       GDK_NOTE (MISC, g_message ("Using Opengl backend %s", gl_backend_names[the_gl_backend_type]));
1401     }
1402 
1403   g_assert (the_gl_backend_type == backend_type);
1404 }
1405