gdkglcontext.c 29 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* GDK - The GIMP Drawing Kit
 *
 * gdkglcontext.c: GL context abstraction
 * 
 * Copyright © 2014  Emmanuele Bassi
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * SECTION:gdkglcontext
 * @Title: GdkGLContext
 * @Short_description: OpenGL context
 *
 * #GdkGLContext is an object representing the platform-specific
 * OpenGL drawing context.
 *
Matthias Clasen's avatar
Matthias Clasen committed
29
30
31
 * #GdkGLContexts are created for a #GdkWindow using
 * gdk_window_create_gl_context(), and the context will match
 * the #GdkVisual of the window.
32
 *
33
34
35
36
37
38
 * A #GdkGLContext is not tied to any particular normal framebuffer.
 * For instance, it cannot draw to the #GdkWindow back buffer. The GDK
 * repaint system is in full control of the painting to that. Instead,
 * you can create render buffers or textures and use gdk_cairo_draw_from_gl()
 * in the draw function of your widget to draw them. Then GDK will handle
 * the integration of your rendering with that of other widgets.
39
 *
Matthias Clasen's avatar
Matthias Clasen committed
40
41
 * Support for #GdkGLContext is platform-specific, context creation
 * can fail, returning %NULL context.
42
43
44
45
46
47
48
 *
 * A #GdkGLContext has to be made "current" in order to start using
 * it, otherwise any OpenGL call will be ignored.
 *
 * ## Creating a new OpenGL context ##
 *
 * In order to create a new #GdkGLContext instance you need a
Matthias Clasen's avatar
Matthias Clasen committed
49
50
 * #GdkWindow, which you typically get during the realize call
 * of a widget.
51
 *
52
53
54
55
56
57
58
59
 * A #GdkGLContext is not realized until either gdk_gl_context_make_current(),
 * or until it is realized using gdk_gl_context_realize(). It is possible to
 * specify details of the GL context like the OpenGL version to be used, or
 * whether the GL context should have extra state validation enabled after
 * calling gdk_window_create_gl_context() by calling gdk_gl_context_realize().
 * If the realization fails you have the option to change the settings of the
 * #GdkGLContext and try again.
 *
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
 * ## Using a GdkGLContext ##
 *
 * You will need to make the #GdkGLContext the current context
 * before issuing OpenGL calls; the system sends OpenGL commands to
 * whichever context is current. It is possible to have multiple
 * contexts, so you always need to ensure that the one which you
 * want to draw with is the current one before issuing commands:
 *
 * |[<!-- language="C" -->
 *   gdk_gl_context_make_current (context);
 * ]|
 *
 * You can now perform your drawing using OpenGL commands.
 *
 * You can check which #GdkGLContext is the current one by using
 * gdk_gl_context_get_current(); you can also unset any #GdkGLContext
 * that is currently set by calling gdk_gl_context_clear_current().
 */

#include "config.h"

#include "gdkglcontextprivate.h"
#include "gdkdisplayprivate.h"
#include "gdkinternals.h"

#include "gdkintl.h"
86
#include "gdk-private.h"
87

88
89
#include <epoxy/gl.h>

90
typedef struct {
91
  GdkDisplay *display;
92
  GdkWindow *window;
93
  GdkGLContext *shared_context;
94

Emmanuele Bassi's avatar
Emmanuele Bassi committed
95
96
  int major;
  int minor;
97
  int gl_version;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
98

99
100
  guint realized : 1;
  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;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
105
106
  guint debug_enabled : 1;
  guint forward_compatible : 1;
107
  guint is_legacy : 1;
108
109

  int use_es;
110

111
  GdkGLContextPaintData *paint_data;
112
113
114
115
116
} GdkGLContextPrivate;

enum {
  PROP_0,

117
  PROP_DISPLAY,
118
  PROP_WINDOW,
119
  PROP_SHARED_CONTEXT,
120
121
122
123
124
125
126
127
128
129

  LAST_PROP
};

static GParamSpec *obj_pspecs[LAST_PROP] = { NULL, };

G_DEFINE_QUARK (gdk-gl-error-quark, gdk_gl_error)

G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkGLContext, gdk_gl_context, G_TYPE_OBJECT)

130
131
static GPrivate thread_current_context = G_PRIVATE_INIT (g_object_unref);

132
133
134
135
136
static void
gdk_gl_context_dispose (GObject *gobject)
{
  GdkGLContext *context = GDK_GL_CONTEXT (gobject);
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
137
  GdkGLContext *current;
138

139
140
141
  current = g_private_get (&thread_current_context);
  if (current == context)
    g_private_replace (&thread_current_context, NULL);
142

143
  g_clear_object (&priv->display);
144
  g_clear_object (&priv->window);
145
  g_clear_object (&priv->shared_context);
146
147
148
149

  G_OBJECT_CLASS (gdk_gl_context_parent_class)->dispose (gobject);
}

150
151
152
153
154
155
156
static void
gdk_gl_context_finalize (GObject *gobject)
{
  GdkGLContext *context = GDK_GL_CONTEXT (gobject);
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  g_clear_pointer (&priv->paint_data, g_free);
157
  G_OBJECT_CLASS (gdk_gl_context_parent_class)->finalize (gobject);
158
159
}

160
161
162
163
164
165
166
167
168
169
static void
gdk_gl_context_set_property (GObject      *gobject,
                             guint         prop_id,
                             const GValue *value,
                             GParamSpec   *pspec)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private ((GdkGLContext *) gobject);

  switch (prop_id)
    {
170
171
172
173
174
175
176
177
178
179
180
181
182
183
    case PROP_DISPLAY:
      {
        GdkDisplay *display = g_value_get_object (value);

        if (display)
          g_object_ref (display);

        if (priv->display)
          g_object_unref (priv->display);

        priv->display = display;
      }
      break;

184
185
186
187
188
189
190
191
192
193
194
195
196
197
    case PROP_WINDOW:
      {
        GdkWindow *window = g_value_get_object (value);

        if (window)
          g_object_ref (window);

        if (priv->window)
          g_object_unref (priv->window);

        priv->window = window;
      }
      break;

198
199
200
201
202
203
204
205
206
    case PROP_SHARED_CONTEXT:
      {
        GdkGLContext *context = g_value_get_object (value);

        if (context != NULL)
          priv->shared_context = g_object_ref (context);
      }
      break;

207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
    }
}

static void
gdk_gl_context_get_property (GObject    *gobject,
                             guint       prop_id,
                             GValue     *value,
                             GParamSpec *pspec)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private ((GdkGLContext *) gobject);

  switch (prop_id)
    {
222
223
224
225
    case PROP_DISPLAY:
      g_value_set_object (value, priv->display);
      break;

226
227
228
229
    case PROP_WINDOW:
      g_value_set_object (value, priv->window);
      break;

230
231
232
233
    case PROP_SHARED_CONTEXT:
      g_value_set_object (value, priv->shared_context);
      break;

234
235
236
237
238
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
    }
}

239
void
240
241
242
243
244
245
gdk_gl_context_upload_texture (GdkGLContext    *context,
                               cairo_surface_t *image_surface,
                               int              width,
                               int              height,
                               guint            texture_target)
{
246
247
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

248
249
  g_return_if_fail (GDK_IS_GL_CONTEXT (context));

250
251
252
  /* GL_UNPACK_ROW_LENGTH is available on desktop GL, OpenGL ES >= 3.0, or if
   * the GL_EXT_unpack_subimage extension for OpenGL ES 2.0 is available
   */
253
254
  if (!priv->use_es ||
      (priv->use_es && (priv->gl_version >= 30 || priv->has_unpack_subimage)))
255
256
257
    {
      glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
      glPixelStorei (GL_UNPACK_ROW_LENGTH, cairo_image_surface_get_stride (image_surface) / 4);
258

259
      if (priv->use_es)
260
261
262
263
264
265
        glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                      cairo_image_surface_get_data (image_surface));
      else
        glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
                      cairo_image_surface_get_data (image_surface));

266
267
268
      glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
    }
  else
269
270
271
272
273
274
275
276
277
278
    {
      GLvoid *data = cairo_image_surface_get_data (image_surface);
      int stride = cairo_image_surface_get_stride (image_surface);
      int i;

      if (priv->use_es)
        {
          glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

          for (i = 0; i < height; i++)
279
            glTexSubImage2D (texture_target, 0, 0, i, width, 1, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char*) data + (i * stride));
280
281
282
283
284
285
        }
      else
        {
          glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);

          for (i = 0; i < height; i++)
286
            glTexSubImage2D (texture_target, 0, 0, i, width, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (unsigned char*) data + (i * stride));
287
288
        }
    }
289
290
}

Emmanuele Bassi's avatar
Emmanuele Bassi committed
291
292
293
294
295
296
297
298
299
300
static gboolean
gdk_gl_context_real_realize (GdkGLContext  *self,
                             GError       **error)
{
  g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
                       "The current backend does not support OpenGL");

  return FALSE;
}

301
302
303
304
305
static void
gdk_gl_context_class_init (GdkGLContextClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

Emmanuele Bassi's avatar
Emmanuele Bassi committed
306
307
  klass->realize = gdk_gl_context_real_realize;

308
309
310
  /**
   * GdkGLContext:display:
   *
311
   * The #GdkDisplay used to create the #GdkGLContext.
312
313
314
315
316
317
   *
   * Since: 3.16
   */
  obj_pspecs[PROP_DISPLAY] =
    g_param_spec_object ("display",
                         P_("Display"),
318
                         P_("The GDK display used to create the GL context"),
319
320
321
322
323
                         GDK_TYPE_DISPLAY,
                         G_PARAM_READWRITE |
                         G_PARAM_CONSTRUCT_ONLY |
                         G_PARAM_STATIC_STRINGS);

324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
  /**
   * GdkGLContext:window:
   *
   * The #GdkWindow the gl context is bound to.
   *
   * Since: 3.16
   */
  obj_pspecs[PROP_WINDOW] =
    g_param_spec_object ("window",
                         P_("Window"),
                         P_("The GDK window bound to the GL context"),
                         GDK_TYPE_WINDOW,
                         G_PARAM_READWRITE |
                         G_PARAM_CONSTRUCT_ONLY |
                         G_PARAM_STATIC_STRINGS);

340
341
342
  /**
   * GdkGLContext:shared-context:
   *
343
   * The #GdkGLContext that this context is sharing data with, or %NULL
344
345
346
347
348
349
   *
   * Since: 3.16
   */
  obj_pspecs[PROP_SHARED_CONTEXT] =
    g_param_spec_object ("shared-context",
                         P_("Shared context"),
350
                         P_("The GL context this context shares data with"),
351
352
353
354
355
                         GDK_TYPE_GL_CONTEXT,
                         G_PARAM_READWRITE |
                         G_PARAM_CONSTRUCT_ONLY |
                         G_PARAM_STATIC_STRINGS);

356
357
358
  gobject_class->set_property = gdk_gl_context_set_property;
  gobject_class->get_property = gdk_gl_context_get_property;
  gobject_class->dispose = gdk_gl_context_dispose;
359
  gobject_class->finalize = gdk_gl_context_finalize;
360
361
362
363
364
365
366

  g_object_class_install_properties (gobject_class, LAST_PROP, obj_pspecs);
}

static void
gdk_gl_context_init (GdkGLContext *self)
{
367
368
369
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);

  priv->use_es = -1;
370
371
372
}

/*< private >
373
 * gdk_gl_context_end_frame:
374
375
376
377
378
379
380
381
382
383
384
385
386
 * @context: a #GdkGLContext
 * @painted: The area that has been redrawn this frame
 * @damage: The area that we know is actually different from the last frame
 *
 * Copies the back buffer to the front buffer.
 *
 * This function may call `glFlush()` implicitly before returning; it
 * is not recommended to call `glFlush()` explicitly before calling
 * this function.
 *
 * Since: 3.16
 */
void
387
388
389
gdk_gl_context_end_frame (GdkGLContext   *context,
                          cairo_region_t *painted,
                          cairo_region_t *damage)
390
391
392
{
  g_return_if_fail (GDK_IS_GL_CONTEXT (context));

393
  GDK_GL_CONTEXT_GET_CLASS (context)->end_frame (context, painted, damage);
394
395
}

396
397
398
399
400
401
402
GdkGLContextPaintData *
gdk_gl_context_get_paint_data (GdkGLContext *context)
{

  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  if (priv->paint_data == NULL)
403
404
405
    {
      priv->paint_data = g_new0 (GdkGLContextPaintData, 1);
      priv->paint_data->is_legacy = priv->is_legacy;
406
      priv->paint_data->use_es = priv->use_es;
407
    }
408
409
410
411

  return priv->paint_data;
}

412
413
414
415
416
417
418
419
gboolean
gdk_gl_context_use_texture_rectangle (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  return priv->use_texture_rectangle;
}

420
421
422
423
424
425
426
427
gboolean
gdk_gl_context_has_framebuffer_blit (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  return priv->has_gl_framebuffer_blit;
}

428
429
430
431
432
433
434
435
gboolean
gdk_gl_context_has_frame_terminator (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  return priv->has_frame_terminator;
}

436
437
438
439
440
441
442
443
gboolean
gdk_gl_context_has_unpack_subimage (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  return priv->has_unpack_subimage;
}

Emmanuele Bassi's avatar
Emmanuele Bassi committed
444
445
446
447
448
449
450
451
452
/**
 * gdk_gl_context_set_debug_enabled:
 * @context: a #GdkGLContext
 * @enabled: whether to enable debugging in the context
 *
 * Sets whether the #GdkGLContext should perform extra validations and
 * run time checking. This is useful during development, but has
 * additional overhead.
 *
453
454
 * The #GdkGLContext must not be realized or made current prior to
 * calling this function.
Emmanuele Bassi's avatar
Emmanuele Bassi committed
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
 *
 * Since: 3.16
 */
void
gdk_gl_context_set_debug_enabled (GdkGLContext *context,
                                  gboolean      enabled)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  g_return_if_fail (GDK_IS_GL_CONTEXT (context));
  g_return_if_fail (!priv->realized);

  enabled = !!enabled;

  priv->debug_enabled = enabled;
}

472
/**
Emmanuele Bassi's avatar
Emmanuele Bassi committed
473
474
475
476
477
478
 * gdk_gl_context_get_debug_enabled:
 * @context: a #GdkGLContext
 *
 * Retrieves the value set using gdk_gl_context_set_debug_enabled().
 *
 * Returns: %TRUE if debugging is enabled
479
480
 *
 * Since: 3.16
Emmanuele Bassi's avatar
Emmanuele Bassi committed
481
482
483
484
485
486
 */
gboolean
gdk_gl_context_get_debug_enabled (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

487
488
  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);

Emmanuele Bassi's avatar
Emmanuele Bassi committed
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
  return priv->debug_enabled;
}

/**
 * gdk_gl_context_set_forward_compatible:
 * @context: a #GdkGLContext
 * @compatible: whether the context should be forward compatible
 *
 * Sets whether the #GdkGLContext should be forward compatible.
 *
 * Forward compatibile contexts must not support OpenGL functionality that
 * has been marked as deprecated in the requested version; non-forward
 * compatible contexts, on the other hand, must support both deprecated and
 * non deprecated functionality.
 *
504
505
 * The #GdkGLContext must not be realized or made current prior to calling
 * this function.
Emmanuele Bassi's avatar
Emmanuele Bassi committed
506
507
508
509
510
511
512
513
514
 *
 * Since: 3.16
 */
void
gdk_gl_context_set_forward_compatible (GdkGLContext *context,
                                       gboolean      compatible)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

515
516
517
  g_return_if_fail (GDK_IS_GL_CONTEXT (context));
  g_return_if_fail (!priv->realized);

Emmanuele Bassi's avatar
Emmanuele Bassi committed
518
519
520
521
522
  compatible = !!compatible;

  priv->forward_compatible = compatible;
}

523
/**
Emmanuele Bassi's avatar
Emmanuele Bassi committed
524
525
526
527
528
529
 * gdk_gl_context_get_forward_compatible:
 * @context: a #GdkGLContext
 *
 * Retrieves the value set using gdk_gl_context_set_forward_compatible().
 *
 * Returns: %TRUE if the context should be forward compatible
530
531
 *
 * Since: 3.16
Emmanuele Bassi's avatar
Emmanuele Bassi committed
532
533
534
535
536
537
 */
gboolean
gdk_gl_context_get_forward_compatible (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

538
539
  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);

Emmanuele Bassi's avatar
Emmanuele Bassi committed
540
541
542
543
544
545
546
547
548
549
550
551
552
  return priv->forward_compatible;
}

/**
 * gdk_gl_context_set_required_version:
 * @context: a #GdkGLContext
 * @major: the major version to request
 * @minor: the minor version to request
 *
 * Sets the major and minor version of OpenGL to request.
 *
 * Setting @major and @minor to zero will use the default values.
 *
553
554
 * The #GdkGLContext must not be realized or made current prior to calling
 * this function.
Emmanuele Bassi's avatar
Emmanuele Bassi committed
555
556
557
558
559
560
561
562
563
 *
 * Since: 3.16
 */
void
gdk_gl_context_set_required_version (GdkGLContext *context,
                                     int           major,
                                     int           minor)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
Emmanuele Bassi's avatar
Emmanuele Bassi committed
564
  int version, min_ver;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
565
566
567
568

  g_return_if_fail (GDK_IS_GL_CONTEXT (context));
  g_return_if_fail (!priv->realized);

569
570
571
572
573
574
575
576
  /* this will take care of the default */
  if (major == 0 && minor == 0)
    {
      priv->major = 0;
      priv->minor = 0;
      return;
    }

577
578
  /* Enforce a minimum context version number of 3.2 */
  version = (major * 100) + minor;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
579

580
  if (priv->use_es > 0 || (_gdk_gl_flags & GDK_GL_GLES) != 0)
Emmanuele Bassi's avatar
Emmanuele Bassi committed
581
    min_ver = 200;
582
583
  else
    min_ver = 302;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
584
585

  if (version < min_ver)
586
587
    {
      g_warning ("gdk_gl_context_set_required_version - GL context versions less than 3.2 are not supported.");
Emmanuele Bassi's avatar
Emmanuele Bassi committed
588
      version = min_ver;
589
590
591
    }
  priv->major = version / 100;
  priv->minor = version % 100;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
592
593
}

594
/**
Emmanuele Bassi's avatar
Emmanuele Bassi committed
595
596
597
598
599
600
601
 * gdk_gl_context_get_required_version:
 * @context: a #GdkGLContext
 * @major: (out) (nullable): return location for the major version to request
 * @minor: (out) (nullable): return location for the minor version to request
 *
 * Retrieves the major and minor version requested by calling
 * gdk_gl_context_set_required_version().
602
603
 *
 * Since: 3.16
Emmanuele Bassi's avatar
Emmanuele Bassi committed
604
605
606
607
608
609
610
 */
void
gdk_gl_context_get_required_version (GdkGLContext *context,
                                     int          *major,
                                     int          *minor)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
Emmanuele Bassi's avatar
Emmanuele Bassi committed
611
  int default_major, default_minor;
612
  int maj, min;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
613

614
615
  g_return_if_fail (GDK_IS_GL_CONTEXT (context));

616
  if (priv->use_es > 0 || (_gdk_gl_flags & GDK_GL_GLES) != 0)
Emmanuele Bassi's avatar
Emmanuele Bassi committed
617
    {
618
619
      default_major = 2;
      default_minor = 0;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
620
621
622
    }
  else
    {
623
624
      default_major = 3;
      default_minor = 2;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
625
626
    }

627
628
  if (priv->major > 0)
    maj = priv->major;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
629
  else
Emmanuele Bassi's avatar
Emmanuele Bassi committed
630
    maj = default_major;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
631

632
633
  if (priv->minor > 0)
    min = priv->minor;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
634
  else
Emmanuele Bassi's avatar
Emmanuele Bassi committed
635
    min = default_minor;
636
637
638
639
640

  if (major != NULL)
    *major = maj;
  if (minor != NULL)
    *minor = min;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
641
642
}

643
644
645
646
647
648
/**
 * gdk_gl_context_is_legacy:
 * @context: a #GdkGLContext
 *
 * Whether the #GdkGLContext is in legacy mode or not.
 *
649
650
651
652
653
654
655
656
657
658
659
660
661
662
 * The #GdkGLContext must be realized before calling this function.
 *
 * When realizing a GL context, GDK will try to use the OpenGL 3.2 core
 * profile; this profile removes all the OpenGL API that was deprecated
 * prior to the 3.2 version of the specification. If the realization is
 * successful, this function will return %FALSE.
 *
 * If the underlying OpenGL implementation does not support core profiles,
 * GDK will fall back to a pre-3.2 compatibility profile, and this function
 * will return %TRUE.
 *
 * You can use the value returned by this function to decide which kind
 * of OpenGL API to use, or whether to do extension discovery, or what
 * kind of shader programs to load.
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
 *
 * Returns: %TRUE if the GL context is in legacy mode
 *
 * Since: 3.20
 */
gboolean
gdk_gl_context_is_legacy (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
  g_return_val_if_fail (priv->realized, FALSE);

  return priv->is_legacy;
}

void
gdk_gl_context_set_is_legacy (GdkGLContext *context,
                              gboolean      is_legacy)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  priv->is_legacy = !!is_legacy;
}

688
689
690
/**
 * gdk_gl_context_set_use_es:
 * @context: a #GdkGLContext:
691
692
 * @use_es: whether the context should use OpenGL ES instead of OpenGL,
 *   or -1 to allow auto-detection
693
 *
694
695
 * Requests that GDK create a OpenGL ES context instead of an OpenGL one,
 * if the platform and windowing system allows it.
696
697
698
 *
 * The @context must not have been realized.
 *
699
700
701
702
 * By default, GDK will attempt to automatically detect whether the
 * underlying GL implementation is OpenGL or OpenGL ES once the @context
 * is realized.
 *
703
704
705
 * You should check the return value of gdk_gl_context_get_use_es() after
 * calling gdk_gl_context_realize() to decide whether to use the OpenGL or
 * OpenGL ES API, extensions, or shaders.
706
707
708
 *
 * Since: 3.22
 */
Emmanuele Bassi's avatar
Emmanuele Bassi committed
709
710
void
gdk_gl_context_set_use_es (GdkGLContext *context,
711
                           int           use_es)
Emmanuele Bassi's avatar
Emmanuele Bassi committed
712
713
714
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

715
716
717
  g_return_if_fail (GDK_IS_GL_CONTEXT (context));
  g_return_if_fail (!priv->realized);

718
719
  if (priv->use_es != use_es)
    priv->use_es = use_es;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
720
721
}

722
723
724
725
726
727
728
729
730
731
/**
 * gdk_gl_context_get_use_es:
 * @context: a #GdkGLContext
 *
 * Checks whether the @context is using an OpenGL or OpenGL ES profile.
 *
 * Returns: %TRUE if the #GdkGLContext is using an OpenGL ES profile
 *
 * Since: 3.22
 */
Emmanuele Bassi's avatar
Emmanuele Bassi committed
732
733
734
735
736
gboolean
gdk_gl_context_get_use_es (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

737
738
  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);

739
740
741
742
  if (!priv->realized)
    return FALSE;

  return priv->use_es > 0;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
743
744
}

745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
/**
 * gdk_gl_context_realize:
 * @context: a #GdkGLContext
 * @error: return location for a #GError
 *
 * Realizes the given #GdkGLContext.
 *
 * It is safe to call this function on a realized #GdkGLContext.
 *
 * Returns: %TRUE if the context is realized
 *
 * Since: 3.16
 */
gboolean
gdk_gl_context_realize (GdkGLContext  *context,
                        GError       **error)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);

  if (priv->realized)
767
    return TRUE;
768
769
770
771
772
773

  priv->realized = GDK_GL_CONTEXT_GET_CLASS (context)->realize (context, error);

  return priv->realized;
}

774
static void
775
gdk_gl_context_check_extensions (GdkGLContext *context)
776
777
778
779
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
  gboolean has_npot, has_texture_rectangle;

780
781
782
783
784
785
  if (!priv->realized)
    return;

  if (priv->extensions_checked)
    return;

786
787
  priv->gl_version = epoxy_gl_version ();

788
789
790
  if (priv->use_es < 0)
    priv->use_es = !epoxy_is_desktop_gl ();

791
792
793
794
795
  if (priv->use_es)
    {
      has_npot = priv->gl_version >= 20;
      has_texture_rectangle = FALSE;

796
      /* This should check for GL_NV_framebuffer_blit as well - see extension at:
797
798
       *
       * https://www.khronos.org/registry/gles/extensions/NV/NV_framebuffer_blit.txt
799
800
801
       *
       * for ANGLE, we can enable bit blitting if we have the
       * GL_ANGLE_framebuffer_blit extension
802
       */
803
804
805
806
      if (epoxy_has_gl_extension ("GL_ANGLE_framebuffer_blit"))
        priv->has_gl_framebuffer_blit = TRUE;
      else
        priv->has_gl_framebuffer_blit = FALSE;
807

808
809
810
811
812
813
814
      /* No OES version */
      priv->has_frame_terminator = FALSE;

      priv->has_unpack_subimage = epoxy_has_gl_extension ("GL_EXT_unpack_subimage");
    }
  else
    {
815
816
      has_npot = priv->gl_version >= 20 || epoxy_has_gl_extension ("GL_ARB_texture_non_power_of_two");
      has_texture_rectangle = priv->gl_version >= 31 || epoxy_has_gl_extension ("GL_ARB_texture_rectangle");
817

818
      priv->has_gl_framebuffer_blit = priv->gl_version >= 30 || epoxy_has_gl_extension ("GL_EXT_framebuffer_blit");
819
820
      priv->has_frame_terminator = epoxy_has_gl_extension ("GL_GREMEDY_frame_terminator");
      priv->has_unpack_subimage = TRUE;
821
822
823
824

      /* We asked for a core profile, but we didn't get one, so we're in legacy mode */
      if (priv->gl_version < 32)
        priv->is_legacy = TRUE;
825
    }
826

827
  if (!priv->use_es && G_UNLIKELY (_gdk_gl_flags & GDK_GL_TEXTURE_RECTANGLE))
828
829
    priv->use_texture_rectangle = TRUE;
  else if (has_npot)
830
831
832
833
    priv->use_texture_rectangle = FALSE;
  else if (has_texture_rectangle)
    priv->use_texture_rectangle = TRUE;
  else
Matthias Clasen's avatar
Matthias Clasen committed
834
    g_warning ("GL implementation doesn't support any form of non-power-of-two textures");
835

Emmanuele Bassi's avatar
Emmanuele Bassi committed
836
  GDK_NOTE (OPENGL,
837
838
            g_message ("%s version: %d.%d (%s)\n"
                       "* GLSL version: %s\n"
839
                       "* Extensions checked:\n"
840
841
842
843
                       " - GL_ARB_texture_non_power_of_two: %s\n"
                       " - GL_ARB_texture_rectangle: %s\n"
                       " - GL_EXT_framebuffer_blit: %s\n"
                       " - GL_GREMEDY_frame_terminator: %s\n"
844
845
                       "* Using texture rectangle: %s",
                       priv->use_es ? "OpenGL ES" : "OpenGL",
846
                       priv->gl_version / 10, priv->gl_version % 10,
847
848
                       priv->is_legacy ? "legacy" : "core",
                       glGetString (GL_SHADING_LANGUAGE_VERSION),
849
850
851
852
853
                       has_npot ? "yes" : "no",
                       has_texture_rectangle ? "yes" : "no",
                       priv->has_gl_framebuffer_blit ? "yes" : "no",
                       priv->has_frame_terminator ? "yes" : "no",
                       priv->use_texture_rectangle ? "yes" : "no"));
Emmanuele Bassi's avatar
Emmanuele Bassi committed
854

855
  priv->extensions_checked = TRUE;
856
857
}

858
859
860
861
862
863
864
865
/**
 * gdk_gl_context_make_current:
 * @context: a #GdkGLContext
 *
 * Makes the @context the current one.
 *
 * Since: 3.16
 */
866
void
867
868
869
gdk_gl_context_make_current (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
870
  GdkGLContext *current;
871

872
  g_return_if_fail (GDK_IS_GL_CONTEXT (context));
873

874
875
876
  current = g_private_get (&thread_current_context);
  if (current == context)
    return;
877

878
879
880
881
882
883
884
885
886
887
888
889
890
891
  /* we need to realize the GdkGLContext if it wasn't explicitly realized */
  if (!priv->realized)
    {
      GError *error = NULL;

      gdk_gl_context_realize (context, &error);
      if (error != NULL)
        {
          g_critical ("Could not realize the GL context: %s", error->message);
          g_error_free (error);
          return;
        }
    }

892
  if (gdk_display_make_gl_context_current (priv->display, context))
893
894
    {
      g_private_replace (&thread_current_context, g_object_ref (context));
895
      gdk_gl_context_check_extensions (context);
896
    }
897
898
}

899
900
901
902
903
904
/**
 * gdk_gl_context_get_display:
 * @context: a #GdkGLContext
 *
 * Retrieves the #GdkDisplay the @context is created for
 *
905
 * Returns: (nullable) (transfer none): a #GdkDisplay or %NULL
906
907
908
909
910
911
912
913
914
915
916
917
918
 *
 * Since: 3.16
 */
GdkDisplay *
gdk_gl_context_get_display (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);

  return priv->display;
}

919
920
921
922
923
924
/**
 * gdk_gl_context_get_window:
 * @context: a #GdkGLContext
 *
 * Retrieves the #GdkWindow used by the @context.
 *
925
 * Returns: (nullable) (transfer none): a #GdkWindow or %NULL
926
927
928
929
930
931
932
933
934
935
936
937
938
 *
 * Since: 3.16
 */
GdkWindow *
gdk_gl_context_get_window (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);

  return priv->window;
}

939
940
941
942
943
944
/**
 * gdk_gl_context_get_shared_context:
 * @context: a #GdkGLContext
 *
 * Retrieves the #GdkGLContext that this @context share data with.
 *
945
 * Returns: (nullable) (transfer none): a #GdkGLContext or %NULL
946
947
948
949
950
951
952
953
954
955
956
957
958
 *
 * Since: 3.16
 */
GdkGLContext *
gdk_gl_context_get_shared_context (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);

  return priv->shared_context;
}

959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
/**
 * gdk_gl_context_get_version:
 * @context: a #GdkGLContext
 * @major: (out): return location for the major version
 * @minor: (out): return location for the minor version
 *
 * Retrieves the OpenGL version of the @context.
 *
 * The @context must be realized prior to calling this function.
 *
 * Since: 3.16
 */
void
gdk_gl_context_get_version (GdkGLContext *context,
                            int          *major,
                            int          *minor)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  g_return_if_fail (GDK_IS_GL_CONTEXT (context));
  g_return_if_fail (priv->realized);

  if (major != NULL)
    *major = priv->gl_version / 10;
  if (minor != NULL)
    *minor = priv->gl_version % 10;
}

987
988
989
990
991
992
993
994
995
996
997
998
999
/**
 * gdk_gl_context_clear_current:
 *
 * Clears the current #GdkGLContext.
 *
 * Any OpenGL call after this function returns will be ignored
 * until gdk_gl_context_make_current() is called.
 *
 * Since: 3.16
 */
void
gdk_gl_context_clear_current (void)
{
1000
  GdkGLContext *current;
1001

1002
1003
1004
1005
1006
  current = g_private_get (&thread_current_context);
  if (current != NULL)
    {
      GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (current);

1007
      if (gdk_display_make_gl_context_current (priv->display, NULL))
1008
1009
        g_private_replace (&thread_current_context, NULL);
    }
1010
1011
1012
1013
1014
1015
1016
}

/**
 * gdk_gl_context_get_current:
 *
 * Retrieves the current #GdkGLContext.
 *
1017
 * Returns: (nullable) (transfer none): the current #GdkGLContext, or %NULL
1018
1019
1020
1021
1022
1023
 *
 * Since: 3.16
 */
GdkGLContext *
gdk_gl_context_get_current (void)
{
1024
1025
1026
  GdkGLContext *current;

  current = g_private_get (&thread_current_context);
1027

1028
  return current;
1029
}
Matthias Clasen's avatar
Matthias Clasen committed
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058

/**
 * gdk_gl_get_flags:
 *
 * Returns the currently active GL flags.
 *
 * Returns: the GL flags
 *
 * Since: 3.16
 */
GdkGLFlags
gdk_gl_get_flags (void)
{
  return _gdk_gl_flags;
}

/**
 * gdk_gl_set_flags:
 * @flags: #GdkGLFlags to set
 *
 * Sets GL flags.
 *
 * Since: 3.16
 */
void
gdk_gl_set_flags (GdkGLFlags flags)
{
  _gdk_gl_flags = flags;
}