gdkglcontext.c 28.6 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.
 *
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
 *
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
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

95 96
  int major;
  int minor;
97
  int gl_version;
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;
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
}

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);

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;
}

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.
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
/**
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
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);

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.
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);

518 519 520 521 522
  compatible = !!compatible;

  priv->forward_compatible = compatible;
}

523
/**
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
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);

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.
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;
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;
592 593
}

594
/**
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
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;
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;
629
  else
Emmanuele Bassi's avatar
Emmanuele Bassi committed
630
    maj = default_major;
631

632 633
  if (priv->minor > 0)
    min = priv->minor;
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;
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 796 797 798 799 800
  if (priv->use_es)
    {
      has_npot = priv->gl_version >= 20;
      has_texture_rectangle = FALSE;

      /* This should check for GL_NV_framebuffer_blit - see extension at:
       *
       * https://www.khronos.org/registry/gles/extensions/NV/NV_framebuffer_blit.txt
       */
      priv->has_gl_framebuffer_blit = FALSE;
801

802 803 804 805 806 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
    {
      has_npot = epoxy_has_gl_extension ("GL_ARB_texture_non_power_of_two");
      has_texture_rectangle = epoxy_has_gl_extension ("GL_ARB_texture_rectangle");

      priv->has_gl_framebuffer_blit = epoxy_has_gl_extension ("GL_EXT_framebuffer_blit");
      priv->has_frame_terminator = epoxy_has_gl_extension ("GL_GREMEDY_frame_terminator");
      priv->has_unpack_subimage = TRUE;
815 816 817 818

      /* 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;
819
    }
820

821
  if (!priv->use_es && G_UNLIKELY (_gdk_gl_flags & GDK_GL_TEXTURE_RECTANGLE))
822 823
    priv->use_texture_rectangle = TRUE;
  else if (has_npot)
824 825 826 827
    priv->use_texture_rectangle = FALSE;
  else if (has_texture_rectangle)
    priv->use_texture_rectangle = TRUE;
  else
828
    g_warning ("GL implementation doesn't support any form of non-power-of-two textures");
829

830
  GDK_NOTE (OPENGL,
831 832
            g_message ("%s version: %d.%d (%s)\n"
                       "* GLSL version: %s\n"
833
                       "* Extensions checked:\n"
834 835 836 837
                       " - 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"
838 839
                       "* Using texture rectangle: %s",
                       priv->use_es ? "OpenGL ES" : "OpenGL",
840
                       priv->gl_version / 10, priv->gl_version % 10,
841 842
                       priv->is_legacy ? "legacy" : "core",
                       glGetString (GL_SHADING_LANGUAGE_VERSION),
843 844 845 846 847
                       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"));
848

849
  priv->extensions_checked = TRUE;
850 851
}

852 853 854 855 856 857 858 859
/**
 * gdk_gl_context_make_current:
 * @context: a #GdkGLContext
 *
 * Makes the @context the current one.
 *
 * Since: 3.16
 */
860
void
861 862 863
gdk_gl_context_make_current (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
864
  GdkGLContext *current;
865

866
  g_return_if_fail (GDK_IS_GL_CONTEXT (context));
867

868 869 870
  current = g_private_get (&thread_current_context);
  if (current == context)
    return;
871

872 873 874 875 876 877 878 879 880 881 882 883 884 885
  /* 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;
        }
    }

886
  if (gdk_display_make_gl_context_current (priv->display, context))
887 888
    {
      g_private_replace (&thread_current_context, g_object_ref (context));
889
      gdk_gl_context_check_extensions (context);
890
    }
891 892
}

893 894 895 896 897 898
/**
 * gdk_gl_context_get_display:
 * @context: a #GdkGLContext
 *
 * Retrieves the #GdkDisplay the @context is created for
 *
899
 * Returns: (nullable) (transfer none): a #GdkDisplay or %NULL
900 901 902 903 904 905 906 907 908 909 910 911 912
 *
 * 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;
}

913 914 915 916 917 918
/**
 * gdk_gl_context_get_window:
 * @context: a #GdkGLContext
 *
 * Retrieves the #GdkWindow used by the @context.
 *
919
 * Returns: (nullable) (transfer none): a #GdkWindow or %NULL
920 921 922 923 924 925 926 927 928 929 930 931 932
 *
 * 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;
}

933 934 935 936 937 938
/**
 * gdk_gl_context_get_shared_context:
 * @context: a #GdkGLContext
 *
 * Retrieves the #GdkGLContext that this @context share data with.
 *
939
 * Returns: (nullable) (transfer none): a #GdkGLContext or %NULL
940 941 942 943 944 945 946 947 948 949 950 951 952
 *
 * 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;
}

953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
/**
 * 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;
}

981 982 983 984 985 986 987 988 989 990 991 992 993
/**
 * 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)
{
994
  GdkGLContext *current;
995

996 997 998 999 1000
  current = g_private_get (&thread_current_context);
  if (current != NULL)
    {
      GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (current);

1001
      if (gdk_display_make_gl_context_current (priv->display, NULL))
1002 1003
        g_private_replace (&thread_current_context, NULL);
    }
1004 1005 1006 1007 1008 1009 1010
}

/**
 * gdk_gl_context_get_current:
 *
 * Retrieves the current #GdkGLContext.
 *
1011
 * Returns: (nullable) (transfer none): the current #GdkGLContext, or %NULL
1012 1013 1014 1015 1016 1017
 *
 * Since: 3.16
 */
GdkGLContext *
gdk_gl_context_get_current (void)
{
1018 1019 1020
  GdkGLContext *current;

  current = g_private_get (&thread_current_context);
1021

1022
  return current;
1023
}
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052

/**
 * 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;
}