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 * SECTION:gdkglcontext
23 * @Title: GdkGLContext
24 * @Short_description: OpenGL context
25 *
26 * #GdkGLContext is an object representing the platform-specific
27 * OpenGL drawing context.
28 *
29 * #GdkGLContexts are created for a #GdkWindow using
30 * gdk_window_create_gl_context(), and the context will match
31 * the #GdkVisual of the window.
32 *
33 * A #GdkGLContext is not tied to any particular normal framebuffer.
34 * For instance, it cannot draw to the #GdkWindow back buffer. The GDK
35 * repaint system is in full control of the painting to that. Instead,
36 * you can create render buffers or textures and use gdk_cairo_draw_from_gl()
37 * in the draw function of your widget to draw them. Then GDK will handle
38 * the integration of your rendering with that of other widgets.
39 *
40 * Support for #GdkGLContext is platform-specific, context creation
41 * can fail, returning %NULL context.
42 *
43 * A #GdkGLContext has to be made "current" in order to start using
44 * it, otherwise any OpenGL call will be ignored.
45 *
46 * ## Creating a new OpenGL context ##
47 *
48 * In order to create a new #GdkGLContext instance you need a
49 * #GdkWindow, which you typically get during the realize call
50 * of a widget.
51 *
52 * A #GdkGLContext is not realized until either gdk_gl_context_make_current(),
53 * or until it is realized using gdk_gl_context_realize(). It is possible to
54 * specify details of the GL context like the OpenGL version to be used, or
55 * whether the GL context should have extra state validation enabled after
56 * calling gdk_window_create_gl_context() by calling gdk_gl_context_realize().
57 * If the realization fails you have the option to change the settings of the
58 * #GdkGLContext and try again.
59 *
60 * ## Using a GdkGLContext ##
61 *
62 * You will need to make the #GdkGLContext the current context
63 * before issuing OpenGL calls; the system sends OpenGL commands to
64 * whichever context is current. It is possible to have multiple
65 * contexts, so you always need to ensure that the one which you
66 * want to draw with is the current one before issuing commands:
67 *
68 * |[<!-- language="C" -->
69 * gdk_gl_context_make_current (context);
70 * ]|
71 *
72 * You can now perform your drawing using OpenGL commands.
73 *
74 * You can check which #GdkGLContext is the current one by using
75 * gdk_gl_context_get_current(); you can also unset any #GdkGLContext
76 * that is currently set by calling gdk_gl_context_clear_current().
77 */
78
79#include "config.h"
80
81#include "gdkglcontextprivate.h"
82#include "gdkdisplayprivate.h"
83#include "gdkinternals.h"
84
85#include "gdkintl.h"
86#include "gdk-private.h"
87
88#include <epoxy/gl.h>
89
90typedef struct {
91 GdkDisplay *display;
92 GdkWindow *window;
93 GdkGLContext *shared_context;
94
95 int major;
96 int minor;
97 int gl_version;
98
99 guint realized : 1;
100 guint use_texture_rectangle : 1;
101 guint has_gl_framebuffer_blit : 1;
102 guint has_frame_terminator : 1;
103 guint has_unpack_subimage : 1;
104 guint extensions_checked : 1;
105 guint debug_enabled : 1;
106 guint forward_compatible : 1;
107 guint is_legacy : 1;
108 guint use_es : 1;
109
110 GdkGLContextPaintData *paint_data;
111} GdkGLContextPrivate;
112
113enum {
114 PROP_0,
115
116 PROP_DISPLAY,
117 PROP_WINDOW,
118 PROP_SHARED_CONTEXT,
119
120 LAST_PROP
121};
122
123static GParamSpec *obj_pspecs[LAST_PROP] = { NULL, };
124
125G_DEFINE_QUARK (gdk-gl-error-quark, gdk_gl_error)
126
127G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkGLContext, gdk_gl_context, G_TYPE_OBJECT)
128
129static GPrivate thread_current_context = G_PRIVATE_INIT (g_object_unref);
130
131static void
132gdk_gl_context_dispose (GObject *gobject)
133{
134 GdkGLContext *context = GDK_GL_CONTEXT (gobject);
135 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
136 GdkGLContext *current;
137
138 current = g_private_get (&thread_current_context);
139 if (current == context)
140 g_private_replace (&thread_current_context, NULL);
141
142 g_clear_object (&priv->display);
143 g_clear_object (&priv->window);
144 g_clear_object (&priv->shared_context);
145
146 G_OBJECT_CLASS (gdk_gl_context_parent_class)->dispose (gobject);
147}
148
149static void
150gdk_gl_context_finalize (GObject *gobject)
151{
152 GdkGLContext *context = GDK_GL_CONTEXT (gobject);
153 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
154
155 g_clear_pointer (&priv->paint_data, g_free);
156}
157
158static void
159gdk_gl_context_set_property (GObject *gobject,
160 guint prop_id,
161 const GValue *value,
162 GParamSpec *pspec)
163{
164 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private ((GdkGLContext *) gobject);
165
166 switch (prop_id)
167 {
168 case PROP_DISPLAY:
169 {
170 GdkDisplay *display = g_value_get_object (value);
171
172 if (display)
173 g_object_ref (display);
174
175 if (priv->display)
176 g_object_unref (priv->display);
177
178 priv->display = display;
179 }
180 break;
181
182 case PROP_WINDOW:
183 {
184 GdkWindow *window = g_value_get_object (value);
185
186 if (window)
187 g_object_ref (window);
188
189 if (priv->window)
190 g_object_unref (priv->window);
191
192 priv->window = window;
193 }
194 break;
195
196 case PROP_SHARED_CONTEXT:
197 {
198 GdkGLContext *context = g_value_get_object (value);
199
200 if (context != NULL)
201 priv->shared_context = g_object_ref (context);
202 }
203 break;
204
205 default:
206 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
207 }
208}
209
210static void
211gdk_gl_context_get_property (GObject *gobject,
212 guint prop_id,
213 GValue *value,
214 GParamSpec *pspec)
215{
216 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private ((GdkGLContext *) gobject);
217
218 switch (prop_id)
219 {
220 case PROP_DISPLAY:
221 g_value_set_object (value, priv->display);
222 break;
223
224 case PROP_WINDOW:
225 g_value_set_object (value, priv->window);
226 break;
227
228 case PROP_SHARED_CONTEXT:
229 g_value_set_object (value, priv->shared_context);
230 break;
231
232 default:
233 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
234 }
235}
236
237void
238gdk_gl_context_upload_texture (GdkGLContext *context,
239 cairo_surface_t *image_surface,
240 int width,
241 int height,
242 guint texture_target)
243{
244 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
245
246 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
247
248 /* GL_UNPACK_ROW_LENGTH is available on desktop GL, OpenGL ES >= 3.0, or if
249 * the GL_EXT_unpack_subimage extension for OpenGL ES 2.0 is available
250 */
251 if (!priv->use_es ||
252 (priv->use_es && (priv->gl_version >= 30 || priv->has_unpack_subimage)))
253 {
254 glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
255 glPixelStorei (GL_UNPACK_ROW_LENGTH, cairo_image_surface_get_stride (image_surface) / 4);
256
257 if (priv->use_es)
258 glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
259 cairo_image_surface_get_data (image_surface));
260 else
261 glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
262 cairo_image_surface_get_data (image_surface));
263
264 glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
265 }
266 else
267 {
268 GLvoid *data = cairo_image_surface_get_data (image_surface);
269 int stride = cairo_image_surface_get_stride (image_surface);
270 int i;
271
272 if (priv->use_es)
273 {
274 glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
275
276 for (i = 0; i < height; i++)
277 glTexSubImage2D (texture_target, 0, 0, i, width, 1, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char*) data + (i * stride));
278 }
279 else
280 {
281 glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
282
283 for (i = 0; i < height; i++)
284 glTexSubImage2D (texture_target, 0, 0, i, width, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (unsigned char*) data + (i * stride));
285 }
286 }
287}
288
289static gboolean
290gdk_gl_context_real_realize (GdkGLContext *self,
291 GError **error)
292{
293 g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
294 "The current backend does not support OpenGL");
295
296 return FALSE;
297}
298
299static void
300gdk_gl_context_class_init (GdkGLContextClass *klass)
301{
302 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
303
304 klass->realize = gdk_gl_context_real_realize;
305
306 /**
307 * GdkGLContext:display:
308 *
309 * The #GdkDisplay used to create the #GdkGLContext.
310 *
311 * Since: 3.16
312 */
313 obj_pspecs[PROP_DISPLAY] =
314 g_param_spec_object ("display",
315 P_("Display"),
316 P_("The GDK display used to create the GL context"),
317 GDK_TYPE_DISPLAY,
318 G_PARAM_READWRITE |
319 G_PARAM_CONSTRUCT_ONLY |
320 G_PARAM_STATIC_STRINGS);
321
322 /**
323 * GdkGLContext:window:
324 *
325 * The #GdkWindow the gl context is bound to.
326 *
327 * Since: 3.16
328 */
329 obj_pspecs[PROP_WINDOW] =
330 g_param_spec_object ("window",
331 P_("Window"),
332 P_("The GDK window bound to the GL context"),
333 GDK_TYPE_WINDOW,
334 G_PARAM_READWRITE |
335 G_PARAM_CONSTRUCT_ONLY |
336 G_PARAM_STATIC_STRINGS);
337
338 /**
339 * GdkGLContext:shared-context:
340 *
341 * The #GdkGLContext that this context is sharing data with, or #NULL
342 *
343 * Since: 3.16
344 */
345 obj_pspecs[PROP_SHARED_CONTEXT] =
346 g_param_spec_object ("shared-context",
347 P_("Shared context"),
348 P_("The GL context this context shares data with"),
349 GDK_TYPE_GL_CONTEXT,
350 G_PARAM_READWRITE |
351 G_PARAM_CONSTRUCT_ONLY |
352 G_PARAM_STATIC_STRINGS);
353
354 gobject_class->set_property = gdk_gl_context_set_property;
355 gobject_class->get_property = gdk_gl_context_get_property;
356 gobject_class->dispose = gdk_gl_context_dispose;
357 gobject_class->finalize = gdk_gl_context_finalize;
358
359 g_object_class_install_properties (gobject_class, LAST_PROP, obj_pspecs);
360}
361
362static void
363gdk_gl_context_init (GdkGLContext *self)
364{
365}
366
367/*< private >
368 * gdk_gl_context_end_frame:
369 * @context: a #GdkGLContext
370 * @painted: The area that has been redrawn this frame
371 * @damage: The area that we know is actually different from the last frame
372 *
373 * Copies the back buffer to the front buffer.
374 *
375 * This function may call `glFlush()` implicitly before returning; it
376 * is not recommended to call `glFlush()` explicitly before calling
377 * this function.
378 *
379 * Since: 3.16
380 */
381void
382gdk_gl_context_end_frame (GdkGLContext *context,
383 cairo_region_t *painted,
384 cairo_region_t *damage)
385{
386 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
387
388 GDK_GL_CONTEXT_GET_CLASS (context)->end_frame (context, painted, damage);
389}
390
391GdkGLContextPaintData *
392gdk_gl_context_get_paint_data (GdkGLContext *context)
393{
394
395 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
396
397 if (priv->paint_data == NULL)
398 {
399 priv->paint_data = g_new0 (GdkGLContextPaintData, 1);
400 priv->paint_data->is_legacy = priv->is_legacy;
401 priv->paint_data->use_es = priv->use_es;
402 }
403
404 return priv->paint_data;
405}
406
407gboolean
408gdk_gl_context_use_texture_rectangle (GdkGLContext *context)
409{
410 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
411
412 return priv->use_texture_rectangle;
413}
414
415gboolean
416gdk_gl_context_has_framebuffer_blit (GdkGLContext *context)
417{
418 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
419
420 return priv->has_gl_framebuffer_blit;
421}
422
423gboolean
424gdk_gl_context_has_frame_terminator (GdkGLContext *context)
425{
426 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
427
428 return priv->has_frame_terminator;
429}
430
431gboolean
432gdk_gl_context_has_unpack_subimage (GdkGLContext *context)
433{
434 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
435
436 return priv->has_unpack_subimage;
437}
438
439/**
440 * gdk_gl_context_set_debug_enabled:
441 * @context: a #GdkGLContext
442 * @enabled: whether to enable debugging in the context
443 *
444 * Sets whether the #GdkGLContext should perform extra validations and
445 * run time checking. This is useful during development, but has
446 * additional overhead.
447 *
448 * The #GdkGLContext must not be realized or made current prior to
449 * calling this function.
450 *
451 * Since: 3.16
452 */
453void
454gdk_gl_context_set_debug_enabled (GdkGLContext *context,
455 gboolean enabled)
456{
457 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
458
459 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
460 g_return_if_fail (!priv->realized);
461
462 enabled = !!enabled;
463
464 priv->debug_enabled = enabled;
465}
466
467/**
468 * gdk_gl_context_get_debug_enabled:
469 * @context: a #GdkGLContext
470 *
471 * Retrieves the value set using gdk_gl_context_set_debug_enabled().
472 *
473 * Returns: %TRUE if debugging is enabled
474 *
475 * Since: 3.16
476 */
477gboolean
478gdk_gl_context_get_debug_enabled (GdkGLContext *context)
479{
480 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
481
482 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
483
484 return priv->debug_enabled;
485}
486
487/**
488 * gdk_gl_context_set_forward_compatible:
489 * @context: a #GdkGLContext
490 * @compatible: whether the context should be forward compatible
491 *
492 * Sets whether the #GdkGLContext should be forward compatible.
493 *
494 * Forward compatibile contexts must not support OpenGL functionality that
495 * has been marked as deprecated in the requested version; non-forward
496 * compatible contexts, on the other hand, must support both deprecated and
497 * non deprecated functionality.
498 *
499 * The #GdkGLContext must not be realized or made current prior to calling
500 * this function.
501 *
502 * Since: 3.16
503 */
504void
505gdk_gl_context_set_forward_compatible (GdkGLContext *context,
506 gboolean compatible)
507{
508 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
509
510 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
511 g_return_if_fail (!priv->realized);
512
513 compatible = !!compatible;
514
515 priv->forward_compatible = compatible;
516}
517
518/**
519 * gdk_gl_context_get_forward_compatible:
520 * @context: a #GdkGLContext
521 *
522 * Retrieves the value set using gdk_gl_context_set_forward_compatible().
523 *
524 * Returns: %TRUE if the context should be forward compatible
525 *
526 * Since: 3.16
527 */
528gboolean
529gdk_gl_context_get_forward_compatible (GdkGLContext *context)
530{
531 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
532
533 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
534
535 return priv->forward_compatible;
536}
537
538/**
539 * gdk_gl_context_set_required_version:
540 * @context: a #GdkGLContext
541 * @major: the major version to request
542 * @minor: the minor version to request
543 *
544 * Sets the major and minor version of OpenGL to request.
545 *
546 * Setting @major and @minor to zero will use the default values.
547 *
548 * The #GdkGLContext must not be realized or made current prior to calling
549 * this function.
550 *
551 * Since: 3.16
552 */
553void
554gdk_gl_context_set_required_version (GdkGLContext *context,
555 int major,
556 int minor)
557{
558 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
559 int version, min_ver;
560
561 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
562 g_return_if_fail (!priv->realized);
563
564 /* this will take care of the default */
565 if (major == 0 && minor == 0)
566 {
567 priv->major = 0;
568 priv->minor = 0;
569 return;
570 }
571
572 /* Enforce a minimum context version number of 3.2 */
573 version = (major * 100) + minor;
574
575 if (priv->use_es || (_gdk_gl_flags & GDK_GL_GLES) != 0)
576 min_ver = 200;
577 else
578 min_ver = 302;
579
580 if (version < min_ver)
581 {
582 g_warning ("gdk_gl_context_set_required_version - GL context versions less than 3.2 are not supported.");
583 version = min_ver;
584 }
585 priv->major = version / 100;
586 priv->minor = version % 100;
587}
588
589/**
590 * gdk_gl_context_get_required_version:
591 * @context: a #GdkGLContext
592 * @major: (out) (nullable): return location for the major version to request
593 * @minor: (out) (nullable): return location for the minor version to request
594 *
595 * Retrieves the major and minor version requested by calling
596 * gdk_gl_context_set_required_version().
597 *
598 * Since: 3.16
599 */
600void
601gdk_gl_context_get_required_version (GdkGLContext *context,
602 int *major,
603 int *minor)
604{
605 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
606 int default_major, default_minor;
607 int maj, min;
608
609 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
610
611 if (priv->use_es || (_gdk_gl_flags & GDK_GL_GLES) != 0)
612 {
613 default_major = 2;
614 default_minor = 0;
615 }
616 else
617 {
618 default_major = 3;
619 default_minor = 2;
620 }
621
622 if (priv->major > 0)
623 maj = priv->major;
624 else
625 maj = default_major;
626
627 if (priv->minor > 0)
628 min = priv->minor;
629 else
630 min = default_minor;
631
632 if (major != NULL)
633 *major = maj;
634 if (minor != NULL)
635 *minor = min;
636}
637
638/**
639 * gdk_gl_context_is_legacy:
640 * @context: a #GdkGLContext
641 *
642 * Whether the #GdkGLContext is in legacy mode or not.
643 *
644 * The #GdkGLContext must be realized before calling this function.
645 *
646 * When realizing a GL context, GDK will try to use the OpenGL 3.2 core
647 * profile; this profile removes all the OpenGL API that was deprecated
648 * prior to the 3.2 version of the specification. If the realization is
649 * successful, this function will return %FALSE.
650 *
651 * If the underlying OpenGL implementation does not support core profiles,
652 * GDK will fall back to a pre-3.2 compatibility profile, and this function
653 * will return %TRUE.
654 *
655 * You can use the value returned by this function to decide which kind
656 * of OpenGL API to use, or whether to do extension discovery, or what
657 * kind of shader programs to load.
658 *
659 * Returns: %TRUE if the GL context is in legacy mode
660 *
661 * Since: 3.20
662 */
663gboolean
664gdk_gl_context_is_legacy (GdkGLContext *context)
665{
666 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
667
668 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
669 g_return_val_if_fail (priv->realized, FALSE);
670
671 return priv->is_legacy;
672}
673
674void
675gdk_gl_context_set_is_legacy (GdkGLContext *context,
676 gboolean is_legacy)
677{
678 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
679
680 priv->is_legacy = !!is_legacy;
681}
682
683/**
684 * gdk_gl_context_set_use_es:
685 * @context: a #GdkGLContext:
686 * @use_es: whether the context should use OpenGL ES instead of OpenGL
687 *
688 * Requests that GDK create a OpenGL ES context instead of an OpenGL one,
689 * if the platform and windowing system allows it.
690 *
691 * The @context must not have been realized.
692 *
693 * You should check the return value of gdk_gl_context_get_use_es() after
694 * calling gdk_gl_context_realize() to decide whether to use the OpenGL or
695 * OpenGL ES API, extensions, or shaders.
696 *
697 * Since: 3.22
698 */
699void
700gdk_gl_context_set_use_es (GdkGLContext *context,
701 gboolean use_es)
702{
703 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
704
705 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
706 g_return_if_fail (!priv->realized);
707
708 priv->use_es = !!use_es;
709}
710
711/**
712 * gdk_gl_context_get_use_es:
713 * @context: a #GdkGLContext
714 *
715 * Checks whether the @context is using an OpenGL or OpenGL ES profile.
716 *
717 * Returns: %TRUE if the #GdkGLContext is using an OpenGL ES profile
718 *
719 * Since: 3.22
720 */
721gboolean
722gdk_gl_context_get_use_es (GdkGLContext *context)
723{
724 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
725
726 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
727
728 return priv->use_es;
729}
730
731/**
732 * gdk_gl_context_realize:
733 * @context: a #GdkGLContext
734 * @error: return location for a #GError
735 *
736 * Realizes the given #GdkGLContext.
737 *
738 * It is safe to call this function on a realized #GdkGLContext.
739 *
740 * Returns: %TRUE if the context is realized
741 *
742 * Since: 3.16
743 */
744gboolean
745gdk_gl_context_realize (GdkGLContext *context,
746 GError **error)
747{
748 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
749
750 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
751
752 if (priv->realized)
753 return TRUE;
754
755 priv->realized = GDK_GL_CONTEXT_GET_CLASS (context)->realize (context, error);
756
757 return priv->realized;
758}
759
760static void
761gdk_gl_context_check_extensions (GdkGLContext *context)
762{
763 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
764 gboolean has_npot, has_texture_rectangle;
765
766 if (!priv->realized)
767 return;
768
769 if (priv->extensions_checked)
770 return;
771
772 priv->use_es = !epoxy_is_desktop_gl ();
773 priv->gl_version = epoxy_gl_version ();
774
775 if (priv->use_es)
776 {
777 has_npot = priv->gl_version >= 20;
778 has_texture_rectangle = FALSE;
779
780 /* This should check for GL_NV_framebuffer_blit - see extension at:
781 *
782 * https://www.khronos.org/registry/gles/extensions/NV/NV_framebuffer_blit.txt
783 */
784 priv->has_gl_framebuffer_blit = FALSE;
785
786 /* No OES version */
787 priv->has_frame_terminator = FALSE;
788
789 priv->has_unpack_subimage = epoxy_has_gl_extension ("GL_EXT_unpack_subimage");
790 }
791 else
792 {
793 has_npot = epoxy_has_gl_extension ("GL_ARB_texture_non_power_of_two");
794 has_texture_rectangle = epoxy_has_gl_extension ("GL_ARB_texture_rectangle");
795
796 priv->has_gl_framebuffer_blit = epoxy_has_gl_extension ("GL_EXT_framebuffer_blit");
797 priv->has_frame_terminator = epoxy_has_gl_extension ("GL_GREMEDY_frame_terminator");
798 priv->has_unpack_subimage = TRUE;
799 }
800
801 if (!priv->use_es && G_UNLIKELY (_gdk_gl_flags & GDK_GL_TEXTURE_RECTANGLE))
802 priv->use_texture_rectangle = TRUE;
803 else if (has_npot)
804 priv->use_texture_rectangle = FALSE;
805 else if (has_texture_rectangle)
806 priv->use_texture_rectangle = TRUE;
807 else
808 g_warning ("GL implementation doesn't support any form of non-power-of-two textures");
809
810 GDK_NOTE (OPENGL,
811 g_message ("%s version: %d.%d\n"
812 "* Extensions checked:\n"
813 " - GL_ARB_texture_non_power_of_two: %s\n"
814 " - GL_ARB_texture_rectangle: %s\n"
815 " - GL_EXT_framebuffer_blit: %s\n"
816 " - GL_GREMEDY_frame_terminator: %s\n"
817 "* Using texture rectangle: %s",
818 priv->use_es ? "OpenGL ES" : "OpenGL",
819 priv->gl_version / 10, priv->gl_version % 10,
820 has_npot ? "yes" : "no",
821 has_texture_rectangle ? "yes" : "no",
822 priv->has_gl_framebuffer_blit ? "yes" : "no",
823 priv->has_frame_terminator ? "yes" : "no",
824 priv->use_texture_rectangle ? "yes" : "no"));
825
826 priv->extensions_checked = TRUE;
827}
828
829/**
830 * gdk_gl_context_make_current:
831 * @context: a #GdkGLContext
832 *
833 * Makes the @context the current one.
834 *
835 * Since: 3.16
836 */
837void
838gdk_gl_context_make_current (GdkGLContext *context)
839{
840 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
841 GdkGLContext *current;
842
843 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
844
845 current = g_private_get (&thread_current_context);
846 if (current == context)
847 return;
848
849 /* we need to realize the GdkGLContext if it wasn't explicitly realized */
850 if (!priv->realized)
851 {
852 GError *error = NULL;
853
854 gdk_gl_context_realize (context, &error);
855 if (error != NULL)
856 {
857 g_critical ("Could not realize the GL context: %s", error->message);
858 g_error_free (error);
859 return;
860 }
861 }
862
863 if (gdk_display_make_gl_context_current (priv->display, context))
864 {
865 g_private_replace (&thread_current_context, g_object_ref (context));
866 gdk_gl_context_check_extensions (context);
867 }
868}
869
870/**
871 * gdk_gl_context_get_display:
872 * @context: a #GdkGLContext
873 *
874 * Retrieves the #GdkDisplay the @context is created for
875 *
876 * Returns: (transfer none): a #GdkDisplay or %NULL
877 *
878 * Since: 3.16
879 */
880GdkDisplay *
881gdk_gl_context_get_display (GdkGLContext *context)
882{
883 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
884
885 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
886
887 return priv->display;
888}
889
890/**
891 * gdk_gl_context_get_window:
892 * @context: a #GdkGLContext
893 *
894 * Retrieves the #GdkWindow used by the @context.
895 *
896 * Returns: (transfer none): a #GdkWindow or %NULL
897 *
898 * Since: 3.16
899 */
900GdkWindow *
901gdk_gl_context_get_window (GdkGLContext *context)
902{
903 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
904
905 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
906
907 return priv->window;
908}
909
910/**
911 * gdk_gl_context_get_shared_context:
912 * @context: a #GdkGLContext
913 *
914 * Retrieves the #GdkGLContext that this @context share data with.
915 *
916 * Returns: (transfer none): a #GdkGLContext or %NULL
917 *
918 * Since: 3.16
919 */
920GdkGLContext *
921gdk_gl_context_get_shared_context (GdkGLContext *context)
922{
923 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
924
925 g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
926
927 return priv->shared_context;
928}
929
930/**
931 * gdk_gl_context_get_version:
932 * @context: a #GdkGLContext
933 * @major: (out): return location for the major version
934 * @minor: (out): return location for the minor version
935 *
936 * Retrieves the OpenGL version of the @context.
937 *
938 * The @context must be realized prior to calling this function.
939 *
940 * Since: 3.16
941 */
942void
943gdk_gl_context_get_version (GdkGLContext *context,
944 int *major,
945 int *minor)
946{
947 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
948
949 g_return_if_fail (GDK_IS_GL_CONTEXT (context));
950 g_return_if_fail (priv->realized);
951
952 if (major != NULL)
953 *major = priv->gl_version / 10;
954 if (minor != NULL)
955 *minor = priv->gl_version % 10;
956}
957
958/**
959 * gdk_gl_context_clear_current:
960 *
961 * Clears the current #GdkGLContext.
962 *
963 * Any OpenGL call after this function returns will be ignored
964 * until gdk_gl_context_make_current() is called.
965 *
966 * Since: 3.16
967 */
968void
969gdk_gl_context_clear_current (void)
970{
971 GdkGLContext *current;
972
973 current = g_private_get (&thread_current_context);
974 if (current != NULL)
975 {
976 GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (current);
977
978 if (gdk_display_make_gl_context_current (priv->display, NULL))
979 g_private_replace (&thread_current_context, NULL);
980 }
981}
982
983/**
984 * gdk_gl_context_get_current:
985 *
986 * Retrieves the current #GdkGLContext.
987 *
988 * Returns: (transfer none): the current #GdkGLContext, or %NULL
989 *
990 * Since: 3.16
991 */
992GdkGLContext *
993gdk_gl_context_get_current (void)
994{
995 GdkGLContext *current;
996
997 current = g_private_get (&thread_current_context);
998
999 return current;
1000}
1001
1002/**
1003 * gdk_gl_get_flags:
1004 *
1005 * Returns the currently active GL flags.
1006 *
1007 * Returns: the GL flags
1008 *
1009 * Since: 3.16
1010 */
1011GdkGLFlags
1012gdk_gl_get_flags (void)
1013{
1014 return _gdk_gl_flags;
1015}
1016
1017/**
1018 * gdk_gl_set_flags:
1019 * @flags: #GdkGLFlags to set
1020 *
1021 * Sets GL flags.
1022 *
1023 * Since: 3.16
1024 */
1025void
1026gdk_gl_set_flags (GdkGLFlags flags)
1027{
1028 _gdk_gl_flags = flags;
1029}
1030