Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gdkglcontext.c

  Sprache: C
 

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


/**
 * GdkGLContext:
 *
 * Represents a platform-specific OpenGL draw context.
 *
 * `GdkGLContext`s are created for a surface using
 * [method@Gdk.Surface.create_gl_context], and the context will match
 * the characteristics of the surface.
 *
 * A `GdkGLContext` is not tied to any particular normal framebuffer.
 * For instance, it cannot draw to the surface 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 [func@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.
 *
 * Support for `GdkGLContext` is platform-specific and context creation
 * can fail, returning %NULL context.
 *
 * 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 `GdkSurface`,
 * which you typically get during the realize call of a widget.
 *
 * A `GdkGLContext` is not realized until either [method@Gdk.GLContext.make_current]
 * or [method@Gdk.GLContext.realize] is called. 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
 * [method@Gdk.Surface.create_gl_context] by calling [method@Gdk.GLContext.realize].
 * If the realization fails you have the option to change the settings of
 * the `GdkGLContext` and try again.
 *
 * ## 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:
 *
 * ```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
 * [func@Gdk.GLContext.get_current]; you can also unset any `GdkGLContext`
 * that is currently set by calling [func@Gdk.GLContext.clear_current].
 */


#include "config.h"

#include "gdkglcontextprivate.h"

#include "gdkdebugprivate.h"
#include "gdkdisplayprivate.h"
#include "gdkdmabufeglprivate.h"
#include "gdkdmabuffourccprivate.h"
#include "gdkmemoryformatprivate.h"
#include "gdkmemorytextureprivate.h"
#include "gdkprofilerprivate.h"
#include "gdkglversionprivate.h"
#include "gdkdmabufformatsprivate.h"

#include "gdkprivate.h"

#include <glib/gi18n-lib.h>

#ifdef GDK_WINDOWING_WIN32
include "gdk/win32/gdkwin32.h"
#endif

#include <epoxy/gl.h>
#ifdef HAVE_EGL
#include <epoxy/egl.h>
#endif

#include <math.h>

#define DEFAULT_ALLOWED_APIS GDK_GL_API_GL | GDK_GL_API_GLES

const GdkDebugKey gdk_gl_feature_keys[] = {
  { "debug", GDK_GL_FEATURE_DEBUG, "GL_KHR_debug" },
  { "base-instance", GDK_GL_FEATURE_BASE_INSTANCE, "GL_ARB_base_instance" },
  { "buffer-storage", GDK_GL_FEATURE_BUFFER_STORAGE, "GL_EXT_buffer_storage" },
  { "external-objects", GDK_GL_FEATURE_EXTERNAL_OBJECTS, "GL_EXT_memory_object and GL_EXT_semaphore"},
  { "external-objects-win32", GDK_GL_FEATURE_EXTERNAL_OBJECTS_WIN32, "GL_EXT_memory_object_win32 and GL_EXT_semaphore_win32" },
  { "blend-func-extended", GDK_GL_FEATURE_BLEND_FUNC_EXTENDED, "GL_EXT_blend_func_extended" },
};

typedef struct _GdkGLContextPrivate GdkGLContextPrivate;

struct _GdkGLContextPrivate
{
  GdkGLVersion required;
  GdkGLVersion gl_version;

  GdkGLMemoryFlags memory_flags[GDK_MEMORY_N_FORMATS];

  GdkGLFeatures features;
  guint surface_attached : 1;
  guint use_khr_debug : 1;
  guint has_debug_output : 1;
  guint extensions_checked : 1;
  guint debug_enabled : 1;
  guint forward_compatible : 1;
  guint is_legacy : 1;

  GdkGLAPI allowed_apis;
  GdkGLAPI api;

  int max_debug_label_length;

#ifdef HAVE_EGL
  gpointer egl_native_window;
  EGLSurface egl_surface;
  GdkMemoryDepth egl_surface_depth;
  EGLContext egl_context;
  EGLBoolean (*eglSwapBuffersWithDamage) (EGLDisplay, EGLSurface, const EGLint *, EGLint);
#endif
};

enum {
  PROP_0,

  PROP_ALLOWED_APIS,
  PROP_API,
  PROP_SHARED_CONTEXT,

  LAST_PROP
};

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

/**
 * gdk_gl_error_quark:
 *
 * Registers an error quark for [class@Gdk.GLContext] errors.
 *
 * Returns: the error quark
 **/

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

G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkGLContext, gdk_gl_context, GDK_TYPE_DRAW_CONTEXT)

typedef struct _MaskedContext MaskedContext;

static inline MaskedContext *
mask_context (GdkGLContext *context,
              gboolean      surfaceless)
{
  return (MaskedContext *) GSIZE_TO_POINTER (GPOINTER_TO_SIZE (context) | (surfaceless ? 1 : 0));
}

static inline GdkGLContext *
unmask_context (MaskedContext *mask)
{
  return GDK_GL_CONTEXT (GSIZE_TO_POINTER (GPOINTER_TO_SIZE (mask) & ~(gsize) 1));
}

static inline gboolean
mask_is_surfaceless (MaskedContext *mask)
{
  return GPOINTER_TO_SIZE (mask) & (gsize) 1;
}

static void
unref_unmasked (gpointer data)
{
  g_object_unref (unmask_context (data));
}

static GPrivate thread_current_context = G_PRIVATE_INIT (unref_unmasked);

static void
gdk_gl_context_clear_old_updated_area (GdkGLContext *context)
{
  for (unsigned int i = 0; i < GDK_GL_MAX_TRACKED_BUFFERS; i++)
    g_clear_pointer (&context->old_updated_area[i], cairo_region_destroy);
}

static void
gdk_gl_context_dispose (GObject *gobject)
{
  GdkGLContext *context = GDK_GL_CONTEXT (gobject);
#ifdef HAVE_EGL
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  if (priv->egl_context != NULL)
    {
      GdkDisplay *display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
      EGLDisplay *egl_display = gdk_display_get_egl_display (display);

      if (eglGetCurrentContext () == priv->egl_context)
        eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);

      GDK_DISPLAY_DEBUG (display, OPENGL, "Destroying EGL context");

      eglDestroyContext (egl_display, priv->egl_context);
      priv->egl_context = NULL;
    }
#endif

  gdk_gl_context_clear_old_updated_area (context);

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

static void
gdk_gl_context_set_property (GObject      *object,
                             guint         prop_id,
                             const GValue *value,
                             GParamSpec   *pspec)
{
  GdkGLContext *self = GDK_GL_CONTEXT (object);

  switch (prop_id)
    {
    case PROP_ALLOWED_APIS:
      gdk_gl_context_set_allowed_apis (self, g_value_get_flags (value));
      break;

    case PROP_SHARED_CONTEXT:
      g_assert (g_value_get_object (value) == NULL);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

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

  switch (prop_id)
    {
    case PROP_ALLOWED_APIS:
      g_value_set_flags (value, priv->allowed_apis);
      break;

    case PROP_API:
      g_value_set_flags (value, priv->api);
      break;

    case PROP_SHARED_CONTEXT:
      g_value_set_object (value, NULL);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

#define N_EGL_ATTRS 16

#ifdef HAVE_EGL
static inline EGLenum
gdk_api_to_egl_api (GdkGLAPI api)
{
  switch (api)
    {
    case GDK_GL_API_GLES:
      return EGL_OPENGL_ES_API;
    case GDK_GL_API_GL:
    default:
      return EGL_OPENGL_API;
    }
}

static GdkGLAPI
gdk_gl_context_create_egl_context (GdkGLContext *context,
                                   GdkGLAPI      api,
                                   gboolean      legacy)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
  GdkDisplay *display = gdk_gl_context_get_display (context);
  EGLDisplay egl_display = gdk_display_get_egl_display (display);
  GdkGLContext *share = gdk_display_get_gl_context (display);
  GdkGLContextPrivate *share_priv = share ? gdk_gl_context_get_instance_private (share) : NULL;
  EGLConfig egl_config;
  EGLContext ctx;
  EGLint context_attribs[N_EGL_ATTRS], i = 0, flags = 0;
  gsize major_idx, minor_idx;
  gboolean debug_bit, forward_bit;
  GdkGLVersion version;
  const GdkGLVersion* supported_versions;
  gsize j;
  G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;

  if (!gdk_gl_context_is_api_allowed (context, api, NULL))
    return 0;

  /* We will use the default version matching the context status
   * unless the user requested a version which makes sense */

  gdk_gl_context_get_matching_version (context, api, legacy, &version);

  if (!eglBindAPI (gdk_api_to_egl_api (api)))
    return 0;

  debug_bit = gdk_gl_context_get_debug_enabled (context);
  forward_bit = gdk_gl_context_get_forward_compatible (context);

  if (display->have_egl_no_config_context)
    egl_config = NULL;
  else
    egl_config = gdk_display_get_egl_config (display, GDK_MEMORY_U8);

  if (debug_bit)
    flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
  if (forward_bit)
    flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;

  if (api == GDK_GL_API_GL)
    {
      /* We want a core profile, unless in legacy mode */
      context_attribs[i++] = EGL_CONTEXT_OPENGL_PROFILE_MASK;
      context_attribs[i++] = legacy
        ? EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT
        : EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT;
    }

  if (legacy || api == GDK_GL_API_GLES)
    flags &= ~EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;

  context_attribs[i++] = EGL_CONTEXT_MAJOR_VERSION;
  major_idx = i++;
  context_attribs[i++] = EGL_CONTEXT_MINOR_VERSION;
  minor_idx = i++;
  context_attribs[i++] = EGL_CONTEXT_FLAGS_KHR;
  context_attribs[i++] = flags;

  context_attribs[i++] = EGL_NONE;
  g_assert (i < N_EGL_ATTRS);

  GDK_DISPLAY_DEBUG (display, OPENGL,
                     "Creating EGL context version %d.%d (debug:%s, forward:%s, legacy:%s, es:%s)",
                     gdk_gl_version_get_major (&version), gdk_gl_version_get_minor (&version),
                     debug_bit ? "yes" : "no",
                     forward_bit ? "yes" : "no",
                     legacy ? "yes" : "no",
                     api == GDK_GL_API_GLES ? "yes" : "no");

  supported_versions = gdk_gl_versions_get_for_api (api);
  ctx = EGL_NO_CONTEXT;
  for (j = 0; gdk_gl_version_greater_equal (&supported_versions[j], &version); j++)
    {
      context_attribs [major_idx] = gdk_gl_version_get_major (&supported_versions[j]);
      context_attribs [minor_idx] = gdk_gl_version_get_minor (&supported_versions[j]);

      ctx = eglCreateContext (egl_display,
                              egl_config,
                              share ? share_priv->egl_context : EGL_NO_CONTEXT,
                              context_attribs);
      if (ctx != EGL_NO_CONTEXT)
        break;
    }

  if (ctx == EGL_NO_CONTEXT)
    return 0;

  priv->egl_context = ctx;
  gdk_gl_context_set_version (context, &supported_versions[j]);
  gdk_gl_context_set_is_legacy (context, legacy);

  if (epoxy_has_egl_extension (egl_display, "EGL_KHR_swap_buffers_with_damage"))
    priv->eglSwapBuffersWithDamage = (gpointer) epoxy_eglGetProcAddress ("eglSwapBuffersWithDamageKHR");
  else if (epoxy_has_egl_extension (egl_display, "EGL_EXT_swap_buffers_with_damage"))
    priv->eglSwapBuffersWithDamage = (gpointer) epoxy_eglGetProcAddress ("eglSwapBuffersWithDamageEXT");

  gdk_profiler_end_mark (start_time, "Create EGL context", NULL);

  return api;
}

static GdkGLAPI
gdk_gl_context_realize_egl (GdkGLContext  *context,
                            GError       **error)
{
  GdkDisplay *display = gdk_gl_context_get_display (context);
  GdkGLContext *share = gdk_display_get_gl_context (display);
  GdkDebugFlags flags;
  GdkGLAPI api, preferred_api;
  gboolean prefer_legacy;

  flags = gdk_display_get_debug_flags(display);

  if (share && gdk_gl_context_is_api_allowed (context,
                                              gdk_gl_context_get_api (share),
                                              NULL))
    preferred_api = gdk_gl_context_get_api (share);
  else if ((flags & GDK_DEBUG_GL_PREFER_GL) != 0 &&
           gdk_gl_context_is_api_allowed (context, GDK_GL_API_GL, NULL))
    preferred_api = GDK_GL_API_GL;
  else if (gdk_gl_context_is_api_allowed (context, GDK_GL_API_GLES, NULL))
    preferred_api = GDK_GL_API_GLES;
  else if ((flags & GDK_DEBUG_GL_PREFER_GL) == 0 &&
            gdk_gl_context_is_api_allowed (context, GDK_GL_API_GL, NULL))
    preferred_api = GDK_GL_API_GL;
  else
    {
      g_set_error_literal (error, GDK_GL_ERROR,
                           GDK_GL_ERROR_NOT_AVAILABLE,
                           _("No GL API allowed."));
      return 0;
    }

  prefer_legacy = share != NULL && gdk_gl_context_is_legacy (share);

  if (preferred_api == GDK_GL_API_GL)
    {
      if ((api = gdk_gl_context_create_egl_context (context, GDK_GL_API_GL, prefer_legacy)) ||
          (api = gdk_gl_context_create_egl_context (context, GDK_GL_API_GLES, FALSE)) ||
          (api = gdk_gl_context_create_egl_context (context, GDK_GL_API_GL, TRUE)))
        return api;
    }
  else
    {
      if ((api = gdk_gl_context_create_egl_context (context, GDK_GL_API_GLES, FALSE)) ||
          (api = gdk_gl_context_create_egl_context (context, GDK_GL_API_GL, prefer_legacy)) ||
          (api = gdk_gl_context_create_egl_context (context, GDK_GL_API_GL, TRUE)))
        return api;
    }

  g_set_error_literal (error, GDK_GL_ERROR,
                       GDK_GL_ERROR_NOT_AVAILABLE,
                       _("Unable to create a GL context"));
  return 0;
}
#endif /* HAVE_EGL */

static GdkGLAPI
gdk_gl_context_default_realize (GdkGLContext  *context,
                                GError       **error)
{
#ifdef HAVE_EGL
  GdkDisplay *display = gdk_gl_context_get_display (context);

  if (gdk_display_get_egl_display (display))
    return gdk_gl_context_realize_egl (context, error);
#endif

  g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
                       _("The current backend does not support OpenGL"));
  return 0;
}

#undef N_EGL_ATTRS

static cairo_region_t *
gdk_gl_context_real_get_damage (GdkGLContext *context)
{
  GdkDrawContext *draw_context = GDK_DRAW_CONTEXT (context);
  guint buffer_width, buffer_height;
#ifdef HAVE_EGL
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
  GdkDisplay *display = gdk_draw_context_get_display (draw_context);

  if (priv->egl_context && display->have_egl_buffer_age)
    {
      int buffer_age = 0;
      gdk_gl_context_make_current (context);
      eglQuerySurface (gdk_display_get_egl_display (display), priv->egl_surface,
                       EGL_BUFFER_AGE_EXT, &buffer_age);

      if (buffer_age > 0 && buffer_age <= GDK_GL_MAX_TRACKED_BUFFERS)
        {
          cairo_region_t *damage = cairo_region_create ();
          int i;

          for (i = 0; i < buffer_age - 1; i++)
            {
              if (context->old_updated_area[i] == NULL)
                {
                  gdk_draw_context_get_buffer_size (draw_context, &buffer_width, &buffer_height);
                  cairo_region_create_rectangle (&(GdkRectangle) {
                                                     00,
                                                     buffer_width,
                                                     buffer_height
                                                 });
                  break;
                }
              cairo_region_union (damage, context->old_updated_area[i]);
            }

          return damage;
        }
    }
#endif

  gdk_draw_context_get_buffer_size (draw_context, &buffer_width, &buffer_height);
  return cairo_region_create_rectangle (&(GdkRectangle) {
                                            00,
                                            buffer_width,
                                            buffer_height
                                        });
}

static gboolean
gdk_gl_context_real_is_shared (GdkGLContext *self,
                               GdkGLContext *other)
{
  if (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (self)) != gdk_draw_context_get_display (GDK_DRAW_CONTEXT (other)))
    return FALSE;

  /* XXX: Should we check es or legacy here? */

  return TRUE;
}

static gboolean
gdk_gl_context_real_is_current (GdkGLContext *self)
{
#ifdef HAVE_EGL
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);

  return priv->egl_context == eglGetCurrentContext ();
#else
  return TRUE;
#endif
}

static gboolean
gdk_gl_context_real_clear_current (GdkGLContext *context)
{
#ifdef HAVE_EGL
  GdkDisplay *display = gdk_gl_context_get_display (context);
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  if (priv->egl_context == NULL)
    return FALSE;

  return eglMakeCurrent (gdk_display_get_egl_display (display),
                         EGL_NO_SURFACE,
                         EGL_NO_SURFACE,
                         EGL_NO_CONTEXT);
#else
  return FALSE;
#endif
}

static gboolean
gdk_gl_context_real_make_current (GdkGLContext *context,
                                  gboolean      surfaceless)
{
#ifdef HAVE_EGL
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
  GdkDisplay *display = gdk_gl_context_get_display (context);
  EGLSurface egl_surface;

  if (priv->egl_context == NULL)
    return FALSE;

  if (!surfaceless)
    egl_surface = priv->egl_surface;
  else
    egl_surface = EGL_NO_SURFACE;

  return eglMakeCurrent (gdk_display_get_egl_display (display),
                         egl_surface,
                         egl_surface,
                         priv->egl_context);
#else
  return FALSE;
#endif
}

#ifdef HAVE_EGL
void
gdk_gl_context_set_egl_native_window (GdkGLContext *self,
                                      gpointer      native_window)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
  GdkGLContext *current = NULL;

  /* This checks that all EGL platforms we support conform to the same struct sizes.
   * When this ever fails, there will be some fun times happening for whoever tries
   * this weird EGL backend... */

  G_STATIC_ASSERT (sizeof (gpointer) == sizeof (EGLNativeWindowType));

  if (priv->egl_surface != NULL)
    {
      GdkDrawContext *draw_context = GDK_DRAW_CONTEXT (self);
      GdkDisplay *display = gdk_draw_context_get_display (draw_context);

      current = gdk_gl_context_clear_current_if_surface (gdk_draw_context_get_surface (draw_context));

      eglDestroySurface (gdk_display_get_egl_display (display), priv->egl_surface);
      priv->egl_surface = NULL;
    }

  priv->egl_native_window = native_window;

  if (current)
    {
      gdk_gl_context_make_current (current);
      g_object_unref (current);
    }
}

static void
gdk_gl_context_ensure_egl_surface (GdkGLContext   *self,
                                   GdkMemoryDepth  depth)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
  GdkDrawContext *draw_context = GDK_DRAW_CONTEXT (self);
  GdkSurface *surface = gdk_draw_context_get_surface (draw_context);
  GdkDisplay *display = gdk_draw_context_get_display (draw_context);

  g_return_if_fail (priv->egl_native_window != NULL);

  if (depth == GDK_MEMORY_NONE)
    {
      if (priv->egl_surface_depth == GDK_MEMORY_NONE)
        depth = GDK_MEMORY_U8;
      else
        depth = priv->egl_surface_depth;
    }

  if (priv->egl_surface == NULL ||
      (priv->egl_surface != NULL &&
       gdk_display_get_egl_config (display, priv->egl_surface_depth) != gdk_display_get_egl_config (display, depth)))
    {
      GdkGLContext *cleared;
      EGLint attribs[4];
      EGLDisplay egl_display;
      EGLConfig egl_config;
      int i;

      cleared = gdk_gl_context_clear_current_if_surface (surface);
      if (priv->egl_surface != NULL)
        eglDestroySurface (gdk_display_get_egl_display (display), priv->egl_surface);

      egl_display = gdk_display_get_egl_display (display),
      egl_config = gdk_display_get_egl_config (display, depth),

      i = 0;
/*
      if (display->have_egl_gl_colorspace)
        {
          attribs[i++] = EGL_GL_COLORSPACE_KHR;
          attribs[i++] = EGL_GL_COLORSPACE_SRGB_KHR;
          surface->is_srgb = TRUE;
        }
*/

      g_assert (i < G_N_ELEMENTS (attribs));
      attribs[i++] = EGL_NONE;

      priv->egl_surface = eglCreateWindowSurface (egl_display,
                                                  egl_config,
                                                  (EGLNativeWindowType) priv->egl_native_window,
                                                  attribs);
      if (priv->egl_surface == EGL_NO_SURFACE)
        {
          /* just assume the error is no srgb support and try again without */
          surface->is_srgb = FALSE;
          priv->egl_surface = eglCreateWindowSurface (egl_display,
                                                      egl_config,
                                                      (EGLNativeWindowType) priv->egl_native_window,
                                                      NULL);
        }
      priv->egl_surface_depth = depth;

      if (cleared)
        {
          gdk_gl_context_make_current (cleared);
          g_object_unref (cleared);
        }
    }
}
#endif

static void
gdk_gl_context_real_begin_frame (GdkDrawContext  *draw_context,
                                 gpointer         context_data,
                                 GdkMemoryDepth   depth,
                                 cairo_region_t  *region,
                                 GdkColorState  **out_color_state,
                                 GdkMemoryDepth  *out_depth)
{
  GdkGLContext *context = GDK_GL_CONTEXT (draw_context);
#ifdef HAVE_EGL
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
#endif
  GdkSurface *surface = gdk_draw_context_get_surface (draw_context);
  GdkColorState *color_state;
  cairo_region_t *damage;
  guint ww, wh;
  int i;

  color_state = gdk_surface_get_color_state (surface);

  depth = gdk_memory_depth_merge (depth, gdk_color_state_get_depth (color_state));

#ifdef HAVE_EGL
  if (priv->egl_context)
    gdk_gl_context_ensure_egl_surface (context, depth);
  
  *out_depth = priv->egl_surface_depth;
  *out_color_state = color_state;
#else
  *out_color_state = gdk_color_state_get_srgb ();
  *out_depth = GDK_MEMORY_U8;
#endif

  damage = GDK_GL_CONTEXT_GET_CLASS (context)->get_damage (context);

  g_clear_pointer (&context->old_updated_area[GDK_GL_MAX_TRACKED_BUFFERS - 1], cairo_region_destroy);
  for (i = GDK_GL_MAX_TRACKED_BUFFERS - 1; i > 0; i--)
    {
      context->old_updated_area[i] = context->old_updated_area[i - 1];
    }
  context->old_updated_area[0] = cairo_region_copy (region);

  cairo_region_union (region, damage);
  cairo_region_destroy (damage);

  gdk_draw_context_get_buffer_size (draw_context, &ww, &wh);

  gdk_gl_context_make_current (context);

  /* Initial setup */
  glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
  glDisable (GL_DEPTH_TEST);
  glDisable (GL_BLEND);
  glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

  glViewport (00, ww, wh);

#ifdef HAVE_EGL
  if (priv->egl_context && gdk_gl_context_check_version (context, NULL, "3.0"))
    glDrawBuffers (1, (GLenum[1]) { gdk_gl_context_get_use_es (context) ? GL_BACK : GL_BACK_LEFT });
#endif
}

static void
gdk_gl_context_real_end_frame (GdkDrawContext *draw_context,
                               gpointer        context_data,
                               cairo_region_t *painted)
{
#ifdef HAVE_EGL
  GdkGLContext *context = GDK_GL_CONTEXT (draw_context);
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
  GdkSurface *surface = gdk_gl_context_get_surface (context);
  GdkDisplay *display = gdk_surface_get_display (surface);
  G_GNUC_UNUSED gint64 begin_time = GDK_PROFILER_CURRENT_TIME;
  guint buffer_width, buffer_height;

  if (priv->egl_context == NULL)
    return;

  gdk_gl_context_make_current (context);

  gdk_draw_context_get_buffer_size (draw_context, &buffer_width, &buffer_height);

  if (priv->eglSwapBuffersWithDamage &&
      cairo_region_contains_rectangle (painted,
                                       &(cairo_rectangle_int_t) {
                                           00,
                                           buffer_width, buffer_height
                                       }) != CAIRO_REGION_OVERLAP_IN)
    {
      EGLint stack_rects[4 * 4]; /* 4 rects */
      EGLint *heap_rects = NULL;
      int i, j, n_rects = cairo_region_num_rectangles (painted);
      EGLint *rects;

      if (n_rects < G_N_ELEMENTS (stack_rects) / 4)
        rects = (EGLint *)&stack_rects;
      else
        heap_rects = rects = g_new (EGLint, n_rects * 4);

      for (i = 0, j = 0; i < n_rects; i++)
        {
          cairo_rectangle_int_t rect;

          cairo_region_get_rectangle (painted, i, &rect);
          rects[j++] = rect.x;
          rects[j++] = buffer_height - rect.height - rect.y;
          rects[j++] = rect.width;
          rects[j++] = rect.height;
        }
      priv->eglSwapBuffersWithDamage (gdk_display_get_egl_display (display), priv->egl_surface, rects, n_rects);
      g_free (heap_rects);
    }
  else
    eglSwapBuffers (gdk_display_get_egl_display (display), priv->egl_surface);
#endif

  gdk_profiler_add_mark (begin_time, GDK_PROFILER_CURRENT_TIME - begin_time, "EGL swap buffers", NULL);
}

static void
gdk_gl_context_surface_detach (GdkDrawContext *draw_context)
{
#ifdef HAVE_EGL
  GdkGLContext *self = GDK_GL_CONTEXT (draw_context);
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);

  gdk_gl_context_set_egl_native_window (self, NULL);
  g_assert (priv->egl_native_window == NULL);
  g_assert (priv->egl_surface == NULL);

  priv->egl_surface_depth = GDK_MEMORY_NONE;
#endif
}

static void
gdk_gl_context_surface_resized (GdkDrawContext *draw_context)
{
  GdkGLContext *context = GDK_GL_CONTEXT (draw_context);

  gdk_gl_context_clear_old_updated_area (context);
}

static guint
gdk_gl_context_real_get_default_framebuffer (GdkGLContext *self)
{
  return 0;
}

static void
gdk_gl_context_class_init (GdkGLContextClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass);

  klass->realize = gdk_gl_context_default_realize;
  klass->get_damage = gdk_gl_context_real_get_damage;
  klass->is_shared = gdk_gl_context_real_is_shared;
  klass->make_current = gdk_gl_context_real_make_current;
  klass->clear_current = gdk_gl_context_real_clear_current;
  klass->is_current = gdk_gl_context_real_is_current;
  klass->get_default_framebuffer = gdk_gl_context_real_get_default_framebuffer;

  draw_context_class->begin_frame = gdk_gl_context_real_begin_frame;
  draw_context_class->end_frame = gdk_gl_context_real_end_frame;
  draw_context_class->surface_detach = gdk_gl_context_surface_detach;
  draw_context_class->surface_resized = gdk_gl_context_surface_resized;

  /**
   * GdkGLContext:shared-context:
   *
   * Always %NULL
   *
   * As many contexts can share data now and no single shared context exists
   * anymore, this function has been deprecated and now always returns %NULL.
   *
   * Deprecated: 4.4: Use [method@Gdk.GLContext.is_shared] to check if contexts
   *   can be shared.
   */

  properties[PROP_SHARED_CONTEXT] =
    g_param_spec_object ("shared-context", NULL, NULL,
                         GDK_TYPE_GL_CONTEXT,
                         G_PARAM_READWRITE |
                         G_PARAM_CONSTRUCT_ONLY |
                         G_PARAM_STATIC_NAME |
                         G_PARAM_DEPRECATED);

  /**
   * GdkGLContext:allowed-apis:
   *
   * The allowed APIs.
   *
   * Since: 4.6
   */

  properties[PROP_ALLOWED_APIS] =
    g_param_spec_flags ("allowed-apis", NULL, NULL,
                        GDK_TYPE_GL_API,
                        DEFAULT_ALLOWED_APIS,
                        G_PARAM_READWRITE |
                        G_PARAM_STATIC_NAME |
                        G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GdkGLContext:api:
   *
   * The API currently in use.
   *
   * Since: 4.6
   */

  properties[PROP_API] =
    g_param_spec_flags ("api", NULL, NULL,
                        GDK_TYPE_GL_API,
                        0,
                        G_PARAM_READABLE |
                        G_PARAM_STATIC_NAME |
                        G_PARAM_EXPLICIT_NOTIFY);

  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;

  g_object_class_install_properties (gobject_class, LAST_PROP, properties);
}

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

  priv->allowed_apis = DEFAULT_ALLOWED_APIS;
}

/* Must have called gdk_display_prepare_gl() before */
GdkGLContext *
gdk_gl_context_new (GdkDisplay *display,
                    GdkSurface *surface,
                    gboolean    surface_attached)
{
  GdkGLContextPrivate *priv;
  GdkGLContext *shared, *result;

  g_assert (surface == NULL || display == gdk_surface_get_display (surface));
  g_assert (!surface_attached || surface != NULL);

  /* assert gdk_display_prepare_gl() had been called */
  shared = gdk_display_get_gl_context (display);
  g_assert (shared);

  result = g_object_new (G_OBJECT_TYPE (shared),
                         "display", display,
                         "surface", surface,
                         NULL);

  priv = gdk_gl_context_get_instance_private (result);
  priv->surface_attached = surface_attached;

  return result;
}

void
gdk_gl_context_push_debug_group (GdkGLContext *context,
                                 const char   *message)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  if (priv->use_khr_debug)
    glPushDebugGroupKHR (GL_DEBUG_SOURCE_APPLICATION, 0, -1, message);
}

void
gdk_gl_context_push_debug_group_printf (GdkGLContext *context,
                                        const char   *format,
                                        ...)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
  char *message;
  va_list args;

  if (priv->use_khr_debug)
    {
      int msg_len;

      va_start (args, format);
      message = g_strdup_vprintf (format, args);
      va_end (args);

      msg_len = MIN (priv->max_debug_label_length, strlen (message) - 1);
      glPushDebugGroupKHR (GL_DEBUG_SOURCE_APPLICATION, 0, msg_len, message);
      g_free (message);
    }
}

void
gdk_gl_context_pop_debug_group (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  if (priv->use_khr_debug)
    glPopDebugGroupKHR ();
}

void
gdk_gl_context_label_object (GdkGLContext *context,
                             guint         identifier,
                             guint         name,
                             const char   *label)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  if (priv->use_khr_debug)
    glObjectLabel (identifier, name, -1, label);
}

void
gdk_gl_context_label_object_printf  (GdkGLContext *context,
                                     guint         identifier,
                                     guint         name,
                                     const char   *format,
                                     ...)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
  char *message;
  va_list args;

  if (priv->use_khr_debug)
    {
      int msg_len;

      va_start (args, format);
      message = g_strdup_vprintf (format, args);
      va_end (args);

      msg_len = MIN (priv->max_debug_label_length, strlen (message) - 1);

      glObjectLabel (identifier, name, msg_len, message);
      g_free (message);
    }
}


gboolean
gdk_gl_context_has_feature (GdkGLContext  *self,
                            GdkGLFeatures  feature)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);

  return (priv->features & feature) == feature;
}

static gboolean
gdk_gl_context_is_realized (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  return priv->api != 0;
}

/**
 * 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
 * runtime checking.
 *
 * This is useful during development, but has additional overhead.
 *
 * The `GdkGLContext` must not be realized or made current prior to
 * calling this function.
 */

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 (!gdk_gl_context_is_realized (context));

  enabled = !!enabled;

  priv->debug_enabled = enabled;
}

/**
 * gdk_gl_context_get_debug_enabled:
 * @context: a `GdkGLContext`
 *
 * Retrieves whether the context is doing extra validations and runtime checking.
 *
 * See [method@Gdk.GLContext.set_debug_enabled].
 *
 * Returns: %TRUE if debugging is enabled
 */

gboolean
gdk_gl_context_get_debug_enabled (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);

  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-compatible 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.
 *
 * The `GdkGLContext` must not be realized or made current prior to calling
 * this function.
 */

void
gdk_gl_context_set_forward_compatible (GdkGLContext *context,
                                       gboolean      compatible)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  g_return_if_fail (GDK_IS_GL_CONTEXT (context));
  g_return_if_fail (!gdk_gl_context_is_realized (context));

  compatible = !!compatible;

  priv->forward_compatible = compatible;
}

/**
 * gdk_gl_context_get_forward_compatible:
 * @context: a `GdkGLContext`
 *
 * Retrieves whether the context is forward-compatible.
 *
 * See [method@Gdk.GLContext.set_forward_compatible].
 *
 * Returns: %TRUE if the context should be forward-compatible
 */

gboolean
gdk_gl_context_get_forward_compatible (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);

  return priv->forward_compatible;
}

void
gdk_gl_context_get_matching_version (GdkGLContext *context,
                                     GdkGLAPI      api,
                                     gboolean      legacy,
                                     GdkGLVersion *out_version)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
  GdkGLVersion min_version;

  g_return_if_fail (GDK_IS_GL_CONTEXT (context));

  if (api == GDK_GL_API_GL)
    min_version = GDK_GL_MIN_GL_VERSION;
  else
    min_version = GDK_GL_MIN_GLES_VERSION;

  if (gdk_gl_version_greater_equal (&priv->required, &min_version))
    *out_version = priv->required;
  else
    *out_version = min_version;

}

/**
 * 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.
 *
 * Setting @major and @minor lower than the minimum versions required
 * by GTK will result in the context choosing the minimum version.
 *
 * The @context must not be realized or made current prior to calling
 * this function.
 */

void
gdk_gl_context_set_required_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 (!gdk_gl_context_is_realized (context));

  priv->required = GDK_GL_VERSION_INIT (major, minor);
}

gboolean
gdk_gl_context_check_gl_version (GdkGLContext       *self,
                                 const GdkGLVersion *required_gl,
                                 const GdkGLVersion *required_gles)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);

  g_return_val_if_fail (GDK_IS_GL_CONTEXT (self), FALSE);

  if (!gdk_gl_context_is_realized (self))
    return FALSE;

  switch (priv->api)
    {
    case GDK_GL_API_GL:
      return required_gl == NULL || gdk_gl_version_greater_equal (&priv->gl_version, required_gl);

    case GDK_GL_API_GLES:
      return required_gles == NULL || gdk_gl_version_greater_equal (&priv->gl_version, required_gles);

    default:
      g_return_val_if_reached (FALSE);

    }
}

/**
 * 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 required OpenGL version set as a requirement for the @context
 * realization. It will not change even if a greater OpenGL version is supported
 * and used after the @context is realized. See
 * [method@Gdk.GLContext.get_version] for the real version in use.
 *
 * See [method@Gdk.GLContext.set_required_version].
 */

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

  if (major != NULL)
    *major = gdk_gl_version_get_major (&priv->required);
  if (minor != NULL)
    *minor = gdk_gl_version_get_minor (&priv->required);
}

/**
 * gdk_gl_context_is_legacy:
 * @context: a `GdkGLContext`
 *
 * Whether the `GdkGLContext` is in legacy mode or not.
 *
 * 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.
 *
 * Returns: %TRUE if the GL context is in legacy mode
 */

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 (gdk_gl_context_is_realized (context), FALSE);

  return priv->is_legacy;
}

void
gdk_gl_context_set_version (GdkGLContext       *context,
                            const GdkGLVersion *version)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  priv->gl_version = *version;
}

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

/**
 * gdk_gl_context_is_shared:
 * @self: a `GdkGLContext`
 * @other: the `GdkGLContext` that should be compatible with @self
 *
 * Checks if the two GL contexts can share resources.
 *
 * When they can, the texture IDs from @other can be used in @self. This
 * is particularly useful when passing `GdkGLTexture` objects between
 * different contexts.
 *
 * Contexts created for the same display with the same properties will
 * always be compatible, even if they are created for different surfaces.
 * For other contexts it depends on the GL backend.
 *
 * Both contexts must be realized for this check to succeed. If either one
 * is not, this function will return %FALSE.
 *
 * Returns: %TRUE if the two GL contexts are compatible.
 *
 * Since: 4.4
 */

gboolean
gdk_gl_context_is_shared (GdkGLContext *self,
                          GdkGLContext *other)
{
  g_return_val_if_fail (GDK_IS_GL_CONTEXT (self), FALSE);
  g_return_val_if_fail (GDK_IS_GL_CONTEXT (other), FALSE);

  if (!gdk_gl_context_is_realized (self) ||
      !gdk_gl_context_is_realized (other))
    return FALSE;

  return GDK_GL_CONTEXT_GET_CLASS (self)->is_shared (self, other);
}

/**
 * gdk_gl_context_set_allowed_apis:
 * @self: a GL context
 * @apis: the allowed APIs
 *
 * Sets the allowed APIs. When gdk_gl_context_realize() is called, only the
 * allowed APIs will be tried. If you set this to 0, realizing will always fail.
 *
 * If you set it on a realized context, the property will not have any effect.
 * It is only relevant during gdk_gl_context_realize().
 *
 * By default, all APIs are allowed.
 *
 * Since: 4.6
 **/

void
gdk_gl_context_set_allowed_apis (GdkGLContext *self,
                                 GdkGLAPI      apis)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);

  g_return_if_fail (GDK_IS_GL_CONTEXT (self));

  if (priv->allowed_apis == apis)
    return;

  priv->allowed_apis = apis;

  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ALLOWED_APIS]);
}

/**
 * gdk_gl_context_get_allowed_apis:
 * @self: a GL context
 *
 * Gets the allowed APIs set via gdk_gl_context_set_allowed_apis().
 *
 * Returns: the allowed APIs
 *
 * Since: 4.6
 **/

GdkGLAPI
gdk_gl_context_get_allowed_apis (GdkGLContext *self)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);

  g_return_val_if_fail (GDK_IS_GL_CONTEXT (self), 0);

  return priv->allowed_apis;
}

/**
 * gdk_gl_context_get_api:
 * @self: a GL context
 *
 * Gets the API currently in use.
 *
 * If the renderer has not been realized yet, 0 is returned.
 *
 * Returns: the currently used API
 *
 * Since: 4.6
 **/

GdkGLAPI
gdk_gl_context_get_api (GdkGLContext *self)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);

  g_return_val_if_fail (GDK_IS_GL_CONTEXT (self), 0);

  return priv->api;
}

gboolean
gdk_gl_context_is_api_allowed (GdkGLContext  *self,
                               GdkGLAPI       api,
                               GError       **error)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
  GdkGLAPI allowed_apis;

  allowed_apis = priv->allowed_apis;

  if (!gdk_has_feature (GDK_FEATURE_GLES_API))
    {
      if (api == GDK_GL_API_GLES)
        {
          g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
                               _("OpenGL ES API disabled via GDK_DISABLE"));
          return FALSE;
        }

      allowed_apis &= ~GDK_GL_API_GLES;
    }

  if (!gdk_has_feature (GDK_FEATURE_GL_API))
    {
      if (api == GDK_GL_API_GL)
        {
          g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
                               _("OpenGL API disabled via GDK_DISABLE"));
          return FALSE;
        }

      allowed_apis &= ~GDK_GL_API_GL;
    }

  if (allowed_apis & api)
    return TRUE;

  g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
               _("Application does not support %s API"),
               api == GDK_GL_API_GL ? "OpenGL" : "OpenGL ES");

  return FALSE;
}

/**
 * gdk_gl_context_set_use_es:
 * @context: a `GdkGLContext`
 * @use_es: whether the context should use OpenGL ES instead of OpenGL,
 *   or -1 to allow auto-detection
 *
 * Requests that GDK create an OpenGL ES context instead of an OpenGL one.
 *
 * Not all platforms support OpenGL ES.
 *
 * The @context must not have been realized.
 *
 * By default, GDK will attempt to automatically detect whether the
 * underlying GL implementation is OpenGL or OpenGL ES once the @context
 * is realized.
 *
 * You should check the return value of [method@Gdk.GLContext.get_use_es]
 * after calling [method@Gdk.GLContext.realize] to decide whether to use
 * the OpenGL or OpenGL ES API, extensions, or shaders.
 *
 * Deprecated: 4.6
 */

void
gdk_gl_context_set_use_es (GdkGLContext *context,
                           int           use_es)
{
  g_return_if_fail (GDK_IS_GL_CONTEXT (context));
  g_return_if_fail (!gdk_gl_context_is_realized (context));

  switch (use_es)
  {
    case -1:
      gdk_gl_context_set_allowed_apis (context, DEFAULT_ALLOWED_APIS);
      break;
    case 0:
      gdk_gl_context_set_allowed_apis (context, GDK_GL_API_GL);
      break;
    case 1:
      gdk_gl_context_set_allowed_apis (context, GDK_GL_API_GLES);
      break;
    default:
      /* Just ignore the call */
      break;
  }
}

/**
 * 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;
 * %FALSE if other profile is in use of if the @context has not yet
 * been realized.
 */

gboolean
gdk_gl_context_get_use_es (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);

  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);

  return priv->api == GDK_GL_API_GLES;
}

static void APIENTRY
gl_debug_message_callback (GLenum        source,
                           GLenum        type,
                           GLuint        id,
                           GLenum        severity,
                           GLsizei       length,
                           const GLchar *message,
                           const void   *user_data)
{
  const char *message_source;
  const char *message_type;
  const char *message_severity;
  GLogLevelFlags log_level;

  if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
    return;

  switch (source)
    {
    case GL_DEBUG_SOURCE_API:
      message_source = "API";
      break;
    case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
      message_source = "Window System";
      break;
    case GL_DEBUG_SOURCE_SHADER_COMPILER:
      message_source = "Shader Compiler";
      break;
    case GL_DEBUG_SOURCE_THIRD_PARTY:
      message_source = "Third Party";
      break;
    case GL_DEBUG_SOURCE_APPLICATION:
      message_source = "Application";
      break;
    case GL_DEBUG_SOURCE_OTHER:
    default:
      message_source = "Other";
    }

  switch (type)
    {
    case GL_DEBUG_TYPE_ERROR:
      message_type = "Error";
      break;
    case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
      message_type = "Deprecated Behavior";
      break;
    case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
      message_type = "Undefined Behavior";
      break;
    case GL_DEBUG_TYPE_PORTABILITY:
      message_type = "Portability";
      break;
    case GL_DEBUG_TYPE_PERFORMANCE:
      message_type = "Performance";
      break;
    case GL_DEBUG_TYPE_MARKER:
      message_type = "Marker";
      break;
    case GL_DEBUG_TYPE_PUSH_GROUP:
      message_type = "Push Group";
      break;
    case GL_DEBUG_TYPE_POP_GROUP:
      message_type = "Pop Group";
      break;
    case GL_DEBUG_TYPE_OTHER:
    default:
      message_type = "Other";
    }

  switch (severity)
    {
    case GL_DEBUG_SEVERITY_HIGH:
      message_severity = "High";
      log_level = G_LOG_LEVEL_CRITICAL;
      break;
    case GL_DEBUG_SEVERITY_MEDIUM:
      message_severity = "Medium";
      log_level = G_LOG_LEVEL_WARNING;
      break;
    case GL_DEBUG_SEVERITY_LOW:
      message_severity = "Low";
      log_level = G_LOG_LEVEL_MESSAGE;
      break;
    case GL_DEBUG_SEVERITY_NOTIFICATION:
      message_severity = "Notification";
      log_level = G_LOG_LEVEL_INFO;
      break;
    default:
      message_severity = "Unknown";
      log_level = G_LOG_LEVEL_MESSAGE;
    }

  /* There's no higher level function taking a log level argument... */
  g_log_structured_standard (G_LOG_DOMAIN, log_level,
                             __FILE__, G_STRINGIFY (__LINE__),
                             G_STRFUNC,
                             "OPENGL:\n    Source: %s\n    Type: %s\n    Severity: %s\n    Message: %s",
                             message_source, message_type, message_severity, message);
}

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

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->api)
    return TRUE;

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

  if (priv->api)
    {
      g_assert (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (00)));

      g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_API]);
    }

  return priv->api;
}

static void
gdk_gl_context_init_memory_flags (GdkGLContext *self)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
  gsize i;

  if (!gdk_gl_context_get_use_es (self))
    {
      for (i = 0; i < G_N_ELEMENTS (priv->memory_flags); i++)
        {
          priv->memory_flags[i] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
        }
      return;
    }

  /* GLES 2.0 spec, tables 3.2 and 3.3 */
  priv->memory_flags[GDK_MEMORY_R8G8B8] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_R8G8B8A8_PREMULTIPLIED] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_R8G8B8A8] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE;
#if 0
  /* GLES2 can do these, but GTK can't */
  priv->memory_flags[GDK_MEMORY_A8] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_G8] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_G8A8_PREMULTIPLIED] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_G8A8] = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE;
#endif

  /* GLES 3.0.6 spec, table 3.13 */
  priv->memory_flags[GDK_MEMORY_G8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_A8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_G8A8_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_G8A8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_R8G8B8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_R8G8B8A8_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_R8G8B8A8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_R8G8B8X8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_R16G16B16_FLOAT] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_R16G16B16A16_FLOAT] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_A16_FLOAT] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_R32G32B32_FLOAT] |= GDK_GL_FORMAT_USABLE;
  priv->memory_flags[GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE;
  priv->memory_flags[GDK_MEMORY_R32G32B32A32_FLOAT] |= GDK_GL_FORMAT_USABLE;
  priv->memory_flags[GDK_MEMORY_A32_FLOAT] |= GDK_GL_FORMAT_USABLE;
  priv->memory_flags[GDK_MEMORY_ABGR2101010_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_ABGR2101010] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
  priv->memory_flags[GDK_MEMORY_XBGR2101010] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;

  /* no changes in GLES 3.1 spec, table 8.13 */

  if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (32)))
    {
      /* GLES 3.2 spec, table 8.10 */
      priv->memory_flags[GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED] |= GDK_GL_FORMAT_RENDERABLE;
      priv->memory_flags[GDK_MEMORY_R16G16B16A16_FLOAT] |= GDK_GL_FORMAT_RENDERABLE;
      priv->memory_flags[GDK_MEMORY_A16_FLOAT] |= GDK_GL_FORMAT_RENDERABLE;
      priv->memory_flags[GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED] |= GDK_GL_FORMAT_RENDERABLE;
      priv->memory_flags[GDK_MEMORY_R32G32B32A32_FLOAT] |= GDK_GL_FORMAT_RENDERABLE;
      priv->memory_flags[GDK_MEMORY_A32_FLOAT] |= GDK_GL_FORMAT_RENDERABLE;
    }

  if (epoxy_has_gl_extension ("GL_OES_rgb8_rgba8"))
    {
      priv->memory_flags[GDK_MEMORY_R8G8B8A8_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_R8G8B8A8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_R8G8B8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_R8G8B8X8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
    }
  if (epoxy_has_gl_extension ("GL_EXT_abgr"))
    {
      priv->memory_flags[GDK_MEMORY_A8B8G8R8_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_A8B8G8R8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_X8B8G8R8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
    }
  if (epoxy_has_gl_extension ("GL_EXT_texture_format_BGRA8888"))
    {
      priv->memory_flags[GDK_MEMORY_B8G8R8A8_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_B8G8R8A8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_B8G8R8X8] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
    }

  if (epoxy_has_gl_extension ("GL_EXT_texture_norm16"))
    {
      priv->memory_flags[GDK_MEMORY_R16G16B16A16_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_R16G16B16A16] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_R16G16B16] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_G16A16_PREMULTIPLIED] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_G16A16] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_G16] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_A16] |= GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_RENDERABLE | GDK_GL_FORMAT_FILTERABLE;
    }
  if (epoxy_has_gl_extension ("GL_OES_texture_half_float"))
    {
      GdkGLMemoryFlags flags = GDK_GL_FORMAT_USABLE;
      if (epoxy_has_gl_extension ("GL_EXT_color_buffer_half_float"))
        flags |= GDK_GL_FORMAT_RENDERABLE;
      if (epoxy_has_gl_extension ("GL_OES_texture_half_float_linear"))
        flags |= GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED] |= flags;
      priv->memory_flags[GDK_MEMORY_R16G16B16A16_FLOAT] |= flags;
      /* disabled for now, see https://gitlab.freedesktop.org/mesa/mesa/-/issues/10378 */
      priv->memory_flags[GDK_MEMORY_R16G16B16_FLOAT] |= flags & ~GDK_GL_FORMAT_RENDERABLE;
      priv->memory_flags[GDK_MEMORY_A16_FLOAT] |= flags;
    }
  if (epoxy_has_gl_extension ("GL_OES_texture_float"))
    {
      GdkGLMemoryFlags flags = GDK_GL_FORMAT_USABLE;
      if (epoxy_has_gl_extension ("GL_EXT_color_buffer_float"))
        flags |= GDK_GL_FORMAT_RENDERABLE;
      if (epoxy_has_gl_extension ("GL_OES_texture_float_linear"))
        flags |= GDK_GL_FORMAT_FILTERABLE;
      priv->memory_flags[GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED] |= flags;
      priv->memory_flags[GDK_MEMORY_R32G32B32A32_FLOAT] |= flags;
      priv->memory_flags[GDK_MEMORY_R32G32B32_FLOAT] |= flags & ~GDK_GL_FORMAT_RENDERABLE;
      priv->memory_flags[GDK_MEMORY_A32_FLOAT] |= flags;
    }
}

void
gdk_gl_version_init_epoxy (GdkGLVersion *version)
{
  int epoxy_version = epoxy_gl_version ();

  *version = GDK_GL_VERSION_INIT (epoxy_version / 10, epoxy_version % 10);
}

static GdkGLFeatures
gdk_gl_context_check_features (GdkGLContext *context)
{
  GdkGLFeatures features = 0;

  if (epoxy_has_gl_extension ("GL_KHR_debug"))
    features |= GDK_GL_FEATURE_DEBUG;

  if (gdk_gl_context_check_version (context, "4.2""9.9") ||
      epoxy_has_gl_extension ("GL_EXT_base_instance") ||
      epoxy_has_gl_extension ("GL_ARB_base_instance"))
    features |= GDK_GL_FEATURE_BASE_INSTANCE;

  if (gdk_gl_context_check_version (context, "4.4""9.9") ||
      epoxy_has_gl_extension ("GL_EXT_buffer_storage") ||
      epoxy_has_gl_extension ("GL_ARB_buffer_storage"))
    features |= GDK_GL_FEATURE_BUFFER_STORAGE;

  if (epoxy_has_gl_extension ("GL_EXT_memory_object") &&
      epoxy_has_gl_extension ("GL_EXT_semaphore"))
    {
      features |= GDK_GL_FEATURE_EXTERNAL_OBJECTS;

      if (epoxy_has_gl_extension ("GL_EXT_memory_object_win32") &&
          epoxy_has_gl_extension ("GL_EXT_semaphore_win32"))
        features |= GDK_GL_FEATURE_EXTERNAL_OBJECTS_WIN32;
    }

  if (gdk_gl_context_check_version (context, "3.3""9.9") ||
      epoxy_has_gl_extension ("GL_ARB_blend_func_extended") ||
      epoxy_has_gl_extension ("GL_EXT_blend_func_extended"))
    features |= GDK_GL_FEATURE_BLEND_FUNC_EXTENDED;

  return features;
}

static void
gdk_gl_context_check_extensions (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
  GdkGLFeatures supported_features, disabled_features;
  gboolean gl_debug = FALSE;
  GdkDisplay *display;

  if (!gdk_gl_context_is_realized (context))
    return;

  if (priv->extensions_checked)
    return;

  priv->has_debug_output = epoxy_has_gl_extension ("GL_ARB_debug_output") ||
                           epoxy_has_gl_extension ("GL_KHR_debug");

  display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
  gl_debug = (gdk_display_get_debug_flags (display) & GDK_DEBUG_GL_DEBUG) != 0;

  if (priv->has_debug_output && gl_debug)
    {
      glEnable (GL_DEBUG_OUTPUT);
      glEnable (GL_DEBUG_OUTPUT_SYNCHRONOUS);
      glDebugMessageCallback (gl_debug_message_callback, NULL);
    }

  /* If we asked for a core profile, but didn't get one, we're in legacy mode */
  if (!gdk_gl_context_get_use_es (context) &&
      !gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (32)))
    priv->is_legacy = TRUE;

  supported_features = gdk_gl_context_check_features (context);
  disabled_features = gdk_parse_debug_var ("GDK_GL_DISABLE",
      "GDK_GL_DISABLE can be set to values which cause GDK to disable\n"
      "certain OpenGL extensions.\n",
      gdk_gl_feature_keys,
      G_N_ELEMENTS (gdk_gl_feature_keys));

  /* handle feature dependencies */
  if (disabled_features & GDK_GL_FEATURE_EXTERNAL_OBJECTS)
    disabled_features |= GDK_GL_FEATURE_EXTERNAL_OBJECTS_WIN32;

  priv->features = supported_features & ~disabled_features;

  gdk_gl_context_init_memory_flags (context);

  if ((priv->features & GDK_GL_FEATURE_DEBUG) && gl_debug)
    {
      priv->use_khr_debug = TRUE;
      glGetIntegerv (GL_MAX_LABEL_LENGTH, &priv->max_debug_label_length);
    }

  if (GDK_DISPLAY_DEBUG_CHECK (display, OPENGL))
    {
      int i, max_texture_size;
      glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size);
      gdk_debug_message ("%s version: %d.%d (%s)",
                         gdk_gl_context_get_use_es (context) ? "OpenGL ES" : "OpenGL",
                         gdk_gl_version_get_major (&priv->gl_version), gdk_gl_version_get_minor (&priv->gl_version),
                         priv->is_legacy ? "legacy" : "core");
      gdk_debug_message ("GLSL version: %s", glGetString (GL_SHADING_LANGUAGE_VERSION));
      gdk_debug_message ("Max texture size: %d", max_texture_size);
      gdk_debug_message ("Enabled features (use GDK_GL_DISABLE env var to disable):");
      for (i = 0; i < G_N_ELEMENTS (gdk_gl_feature_keys); i++)
        {
          gdk_debug_message ("    %s: %s",
                             gdk_gl_feature_keys[i].key,
                             (priv->features & gdk_gl_feature_keys[i].value) ? "✓" :
                             ((disabled_features & gdk_gl_feature_keys[i].value) ? "disabled via env var" :
                             (((supported_features & gdk_gl_feature_keys[i].value) == 0) ? "✗" :
                             "Hum, what? This should not happen.")));
        }
    }

  priv->extensions_checked = TRUE;
}

static gboolean
gdk_gl_context_check_is_current (GdkGLContext *context)
{
  return GDK_GL_CONTEXT_GET_CLASS (context)->is_current (context);
}

/**
 * gdk_gl_context_make_current:
 * @context: a `GdkGLContext`
 *
 * Makes the @context the current one.
 */

void
gdk_gl_context_make_current (GdkGLContext *context)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
  MaskedContext *current, *masked_context;
  gboolean surfaceless;

  g_return_if_fail (GDK_IS_GL_CONTEXT (context));

  if (priv->surface_attached)
    {
      surfaceless = FALSE;
    }
  else
    {
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
      surfaceless = !gdk_draw_context_is_in_frame (GDK_DRAW_CONTEXT (context));
G_GNUC_END_IGNORE_DEPRECATIONS
    }
  masked_context = mask_context (context, surfaceless);

  current = g_private_get (&thread_current_context);
  if (current == masked_context && gdk_gl_context_check_is_current (context))
    return;

  /* we need to realize the GdkGLContext if it wasn't explicitly realized */
  if (!gdk_gl_context_is_realized (context))
    {
      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;
        }
    }

  if (!GDK_GL_CONTEXT_GET_CLASS (context)->make_current (context, surfaceless))
    {
      g_warning ("gdk_gl_context_make_current() failed");
      return;
    }

  g_object_ref (context);
  g_private_replace (&thread_current_context, masked_context);
  gdk_gl_context_check_extensions (context);
}

/**
 * gdk_gl_context_get_display:
 * @context: a `GdkGLContext`
 *
 * Retrieves the display the @context is created for
 *
 * Returns: (nullable) (transfer none): a `GdkDisplay`
 */

GdkDisplay *
gdk_gl_context_get_display (GdkGLContext *context)
{
  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);

  return gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context));
}

/**
 * gdk_gl_context_get_surface:
 * @context: a `GdkGLContext`
 *
 * Retrieves the surface used by the @context.
 *
 * Returns: (nullable) (transfer none): a `GdkSurface`
 */

GdkSurface *
gdk_gl_context_get_surface (GdkGLContext *context)
{
  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);

  return gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
}

/**
 * gdk_gl_context_get_shared_context:
 * @context: a `GdkGLContext`
 *
 * Used to retrieves the `GdkGLContext` that this @context share data with.
 *
 * As many contexts can share data now and no single shared context exists
 * anymore, this function has been deprecated and now always returns %NULL.
 *
 * Returns: (nullable) (transfer none): %NULL
 *
 * Deprecated: 4.4: Use [method@Gdk.GLContext.is_shared] to check if contexts
 *   can be shared.
 */

GdkGLContext *
gdk_gl_context_get_shared_context (GdkGLContext *context)
{
  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);

  return NULL;
}

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

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 (gdk_gl_context_is_realized (context));

  if (major != NULL)
    *major = gdk_gl_version_get_major (&priv->gl_version);
  if (minor != NULL)
    *minor = gdk_gl_version_get_minor (&priv->gl_version);
}

const char *
gdk_gl_context_get_glsl_version_string (GdkGLContext *self)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);

  if (priv->api == GDK_GL_API_GL)
    {
      if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (46)))
        return "#version 460";
      else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (45)))
        return "#version 450";
      else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (44)))
        return "#version 440";
      else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (43)))
        return "#version 430";
      else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (42)))
        return "#version 420";
      else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (41)))
        return "#version 410";
      else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (40)))
        return "#version 400";
      else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (33)))
        return "#version 330";
      else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (32)))
        return "#version 150";
      else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (31)))
        return "#version 140";
      else
        return "#version 130";
    }
  else if (priv->api == GDK_GL_API_GLES)
    {
      if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (32)))
        return "#version 320 es";
      else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (31)))
        return "#version 310 es";
      else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (30)))
        return "#version 300 es";
      else
        return "#version 100";
    }
  else
    {
      /* must be realized to be called */
      g_assert_not_reached ();
    }
}

/**
 * gdk_gl_context_clear_current:
 *
 * Clears the current `GdkGLContext`.
 *
 * Any OpenGL call after this function returns will be ignored
 * until [method@Gdk.GLContext.make_current] is called.
 */

void
gdk_gl_context_clear_current (void)
{
  MaskedContext *current;

  current = g_private_get (&thread_current_context);
  if (current != NULL)
    {
      GdkGLContext *context = unmask_context (current);

      if (GDK_GL_CONTEXT_GET_CLASS (context)->clear_current (context))
        g_private_replace (&thread_current_context, NULL);
    }
}

/*<private>
 * gdk_gl_context_clear_current_if_surface:
 * @surface: surface to clear for
 *
 * Does a gdk_gl_context_clear_current() if the current context is attached
 * to @surface, leaves the current context alone otherwise.
 * 
 * Returns: (nullable) (transfer full): The context that was cleared, so that it can be
 *   re-made current later
 **/

GdkGLContext *
gdk_gl_context_clear_current_if_surface (GdkSurface *surface)
{
  MaskedContext *current;

  current = g_private_get (&thread_current_context);
  if (current != NULL && !mask_is_surfaceless (current))
    {
      GdkGLContext *context = unmask_context (current);

      if (gdk_gl_context_get_surface (context) != surface)
        return NULL;

      g_object_ref (context);

      if (GDK_GL_CONTEXT_GET_CLASS (context)->clear_current (context))
        {
          g_private_replace (&thread_current_context, NULL);
          return context;
        }

      g_object_unref (context);
    }

  return NULL;
}

/**
 * gdk_gl_context_get_current:
 *
 * Retrieves the current `GdkGLContext`.
 *
 * Returns: (nullable) (transfer none): the current `GdkGLContext`
 */

GdkGLContext *
gdk_gl_context_get_current (void)
{
  MaskedContext *current;
  GdkGLContext *context;

  current = g_private_get (&thread_current_context);
  context = unmask_context (current);

  if (context && !gdk_gl_context_check_is_current (context))
    {
      g_private_replace (&thread_current_context, NULL);
      context = NULL;
    }

  return context;
}

GdkGLMemoryFlags
gdk_gl_context_get_format_flags (GdkGLContext    *self,
                                 GdkMemoryFormat  format)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);

  return priv->memory_flags[format];
}

/* Return if glGenVertexArrays, glBindVertexArray and glDeleteVertexArrays
 * can be used
 */

gboolean
gdk_gl_context_has_vertex_arrays (GdkGLContext *self)
{
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);

  switch (priv->api)
    {
    case GDK_GL_API_GL:
      return TRUE;

    case GDK_GL_API_GLES:
      return gdk_gl_version_get_major (&priv->gl_version) >= 3;

    default:
      g_return_val_if_reached (FALSE);
    }
}

/* This is currently private! */
/* When using GL/ES, don't flip the 'R' and 'B' bits on Windows/ANGLE for glReadPixels() */
gboolean
gdk_gl_context_use_es_bgra (GdkGLContext *context)
{
  if (!gdk_gl_context_get_use_es (context))
    return FALSE;

#ifdef GDK_WINDOWING_WIN32
  if (GDK_WIN32_IS_GL_CONTEXT (context))
    return TRUE;
#endif

  return FALSE;
}

static GdkGLBackend the_gl_backend_type = GDK_GL_NONE;

static const char *gl_backend_names[] = {
  [GDK_GL_NONE] = "No GL (You should never read this)",
  [GDK_GL_EGL] = "EGL",
  [GDK_GL_GLX] = "X11 GLX",
  [GDK_GL_WGL] = "Windows WGL",
  [GDK_GL_CGL] = "Apple CGL"
};

/*<private>
 * gdk_gl_backend_can_be_used:
 * @backend_type: Type of backend to check
 * @error: Return location for an error
 *
 * Checks if this backend type can be used. When multiple displays
 * are opened that use different GL backends, conflicts can arise,
 * so this function checks that all displays use compatible GL
 * backends.
 *
 * Returns: %TRUE if the backend can still be used
 */

gboolean
gdk_gl_backend_can_be_used (GdkGLBackend   backend_type,
                            GError       **error)
{
  if (the_gl_backend_type != GDK_GL_NONE &&
      the_gl_backend_type != backend_type)
    {
      g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
                   /* translators: This is about OpenGL backend names, like
                    * "Trying to use X11 GLX, but EGL is already in use"
                    */

                   _("Trying to use %s, but %s is already in use"),
                   gl_backend_names[backend_type],
                   gl_backend_names[the_gl_backend_type]);
      return FALSE;
    }

  if ((backend_type == GDK_GL_EGL && !gdk_has_feature (GDK_FEATURE_EGL)) ||
      (backend_type == GDK_GL_GLX && !gdk_has_feature (GDK_FEATURE_GLX)) ||
      (backend_type == GDK_GL_WGL && !gdk_has_feature (GDK_FEATURE_WGL)))
    {
      g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE,
                   _("Trying to use %s, but it is disabled via GDK_DISABLE"),
                   gl_backend_names[backend_type]);
      return FALSE;
    }

  return TRUE;
}

/*<private>
 * gdk_gl_backend_use:
 * @backend_type: Type of backend
 *
 * Ensures that the backend in use is the given one. If another backend
 * is already in use, this function will abort the program. It should
 * have previously checked via gdk_gl_backend_can_be_used().
 **/

void
gdk_gl_backend_use (GdkGLBackend backend_type)
{
  /* Check that the context class is properly initializing its backend type */
  g_assert (backend_type != GDK_GL_NONE);

  if (the_gl_backend_type == GDK_GL_NONE)
    {
      the_gl_backend_type = backend_type;
      /* This is important!!!11eleven
       * (But really: How do I print a message in 2 categories?) */

      GDK_DEBUG (OPENGL, "Using OpenGL backend %s", gl_backend_names[the_gl_backend_type]);
      GDK_DEBUG (MISC, "Using OpenGL backend %s", gl_backend_names[the_gl_backend_type]);
    }

  g_assert (the_gl_backend_type == backend_type);
}

gboolean
gdk_gl_context_export_dmabuf (GdkGLContext *self,
                              unsigned int  texture_id,
                              GdkDmabuf    *dmabuf)
{
#if defined(HAVE_EGL) && defined(HAVE_DMABUF)
  GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
  GdkDisplay *display = gdk_gl_context_get_display (self);
  EGLDisplay egl_display = gdk_display_get_egl_display (display);
  EGLContext egl_context = priv->egl_context;
  EGLint attribs[10];
  EGLImage image;
  gboolean result = FALSE;
  int i;
  int fourcc;
  int n_planes;
  EGLuint64KHR modifiers[GDK_DMABUF_MAX_PLANES];
  int fds[GDK_DMABUF_MAX_PLANES];
  int strides[GDK_DMABUF_MAX_PLANES];
  int offsets[GDK_DMABUF_MAX_PLANES];

  g_return_val_if_fail (GDK_IS_GL_CONTEXT (self), FALSE);
  g_return_val_if_fail (texture_id > 0FALSE);
  g_return_val_if_fail (dmabuf != NULL, FALSE);

  if (egl_display == EGL_NO_DISPLAY || !display->have_egl_dma_buf_export)
    {
      GDK_DISPLAY_DEBUG (display, DMABUF,
                         "Can't export dmabufs from GL, missing EGL or EGL_EXT_image_dma_buf_export");
      return 0;
    }

  GDK_DISPLAY_DEBUG (display, DMABUF, "Exporting GL texture to dmabuf");

  i = 0;
  attribs[i++] = EGL_IMAGE_PRESERVED_KHR;
  attribs[i++] = EGL_TRUE;

  attribs[i++] = EGL_NONE;

  image = eglCreateImageKHR (egl_display,
                             egl_context,
                             EGL_GL_TEXTURE_2D_KHR,
                             (EGLClientBuffer)GUINT_TO_POINTER (texture_id),
                             attribs);

  if (image == EGL_NO_IMAGE)
    {
      GDK_DISPLAY_DEBUG (display, DMABUF,
                         "Creating EGLImage for dmabuf failed: %#x", eglGetError ());
      return FALSE;
    }

  if (!eglExportDMABUFImageQueryMESA (egl_display,
                                      image,
                                      &fourcc,
                                      &n_planes,
                                      NULL))
    {
      GDK_DISPLAY_DEBUG (display, DMABUF,
                         "eglExportDMABUFImageQueryMESA failed: %#x", eglGetError ());
      goto out;
    }

  if (n_planes < 1 || n_planes > GDK_DMABUF_MAX_PLANES)
    {
      GDK_DISPLAY_DEBUG (display, DMABUF,
                         "dmabufs with %d planes are not supported", n_planes);
      goto out;
    }

  if (!eglExportDMABUFImageQueryMESA (egl_display,
                                      image,
                                      &fourcc,
                                      &n_planes,
                                      modifiers))
    {
      GDK_DISPLAY_DEBUG (display, DMABUF,
                         "eglExportDMABUFImageQueryMESA for modifiers failed: %#x", eglGetError ());
      goto out;
    }

  if (!eglExportDMABUFImageMESA (egl_display,
                                 image,
                                 fds,
                                 strides,
                                 offsets))
    {
      g_warning ("eglExportDMABUFImage failed: %#x", eglGetError ());
      goto out;
    }

  dmabuf->fourcc = (guint32)fourcc;
  dmabuf->modifier = modifiers[0];
  dmabuf->n_planes = n_planes;

  for (i = 0; i < n_planes; i++)
    {
      dmabuf->planes[i].fd = fds[i];
      dmabuf->planes[i].stride = (int) strides[i];
      dmabuf->planes[i].offset = (int) offsets[i];
    }

  GDK_DISPLAY_DEBUG (display, DMABUF,
                     "Exported GL texture to dmabuf (format: %.4s:%#" G_GINT64_MODIFIER "x, planes: %d)",
                     (char *)&fourcc, modifiers[0], n_planes);

  result = TRUE;

out:
  eglDestroyImageKHR (egl_display, image);

  return result;
#else
  return FALSE;
#endif
}

static gboolean
gdk_gl_context_find_format (GdkGLContext    *self,
                            GdkMemoryAlpha   alpha,
                            GLint            gl_format,
                            GLint            gl_type,
                            GdkMemoryFormat *out_format)
{
  GdkMemoryFormat format;

  for (format = 0; format < GDK_MEMORY_N_FORMATS; format++)
    {
      GLint q_internal_format, q_internal_srgb_format;
      GLenum q_format, q_type;
      GdkSwizzle q_swizzle;

      if (gdk_memory_format_alpha (format) != alpha)
        continue;

      if (!(gdk_gl_context_get_format_flags (self, format) & GDK_GL_FORMAT_RENDERABLE))
        continue;

      if (!gdk_memory_format_gl_format (format,
                                        0,
                                        gdk_gl_context_get_use_es (self),
                                        &q_internal_format,
                                        &q_internal_srgb_format,
                                        &q_format,
                                        &q_type,
                                        &q_swizzle))
        continue;

      if (q_format != gl_format || q_type != gl_type)
        continue;

      *out_format = format;
      return TRUE;
    }

  return FALSE;
}

void
gdk_gl_context_download (GdkGLContext          *self,
                         GLuint                 tex_id,
                         GdkMemoryFormat        tex_format,
                         GdkColorState         *tex_color_state,
                         guchar                *dest_data,
                         const GdkMemoryLayout *dest_layout,
                         GdkColorState         *dest_color_state)
{
  gsize expected_stride;
  GLint gl_internal_format, gl_internal_srgb_format;
  GLenum gl_format, gl_type;
  GdkSwizzle gl_swizzle;

  g_assert (gdk_memory_format_get_n_planes (tex_format) == 1);

  expected_stride = dest_layout->width / gdk_memory_format_get_plane_block_width (dest_layout->format, 0)
                                       * gdk_memory_format_get_plane_block_bytes (dest_layout->format, 0);
  expected_stride = (expected_stride + 3) & ~3;

  if (!gdk_gl_context_get_use_es (self) &&
      ((gdk_gl_context_get_format_flags (self, tex_format) & GDK_GL_FORMAT_USABLE) == GDK_GL_FORMAT_USABLE))
    {
      if (!gdk_memory_format_gl_format (tex_format,
                                        0,
                                        gdk_gl_context_get_use_es (self),
                                        &gl_internal_format, &gl_internal_srgb_format,
                                        &gl_format, &gl_type, &gl_swizzle))
        {
          g_assert_not_reached ();
        }

      glBindTexture (GL_TEXTURE_2D, tex_id);

      if (dest_layout->planes[0].stride == expected_stride &&
          dest_layout->format == tex_format)
        {
          glGetTexImage (GL_TEXTURE_2D,
                         0,
                         gl_format,
                         gl_type,
                         dest_data + gdk_memory_layout_offset (dest_layout, 000));

          gdk_memory_convert_color_state (dest_data,
                                          dest_layout,
                                          dest_color_state,
                                          tex_color_state);
        }
      else
        {
          GdkMemoryLayout pixel_layout;
          guchar *pixels;

          gdk_memory_layout_init (&pixel_layout, tex_format, dest_layout->width, dest_layout->height, 1);
          pixels = g_malloc (pixel_layout.size);

          glPixelStorei (GL_PACK_ALIGNMENT, 1);
          glGetTexImage (GL_TEXTURE_2D,
                         0,
                         gl_format,
                         gl_type,
                         pixels);

          gdk_memory_convert (dest_data,
                              dest_layout,
                              dest_color_state,
                              pixels,
                              &pixel_layout,
                              tex_color_state);

          g_free (pixels);
        }
    }
  else
    {
      GdkMemoryFormat actual_format;
      GLenum gl_read_format, gl_read_type;
      GLuint fbo;

      glGenFramebuffers (1, &fbo);
      glBindFramebuffer (GL_FRAMEBUFFER, fbo);
      glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_id, 0);
      if (gdk_gl_context_check_version (self, "4.3""3.1"))
        {
          GLint read_format, read_type;
          glGetFramebufferParameteriv (GL_FRAMEBUFFER, GL_IMPLEMENTATION_COLOR_READ_FORMAT, &read_format);
          glGetFramebufferParameteriv (GL_FRAMEBUFFER, GL_IMPLEMENTATION_COLOR_READ_TYPE, &read_type);
          if (gdk_gl_context_find_format (self, gdk_memory_format_alpha (tex_format), read_format, read_type, &actual_format))
            {
              gl_read_format = read_format;
              gl_read_type = read_type;
            }
          else
            {
              actual_format = gdk_memory_depth_get_format (gdk_memory_format_get_depth (tex_format));
              if (gdk_memory_format_alpha (tex_format) == GDK_MEMORY_ALPHA_STRAIGHT)
                actual_format = gdk_memory_format_get_straight (actual_format);

              if (!gdk_memory_format_gl_format (actual_format,
                                                0,
                                                gdk_gl_context_get_use_es (self),
                                                &gl_internal_format, &gl_internal_srgb_format,
                                                &gl_read_format, &gl_read_type, &gl_swizzle))
                {
                  g_assert_not_reached ();
                }
            }
        }
      else
        {
          actual_format = gdk_memory_depth_get_format (gdk_memory_format_get_depth (tex_format));
          if (gdk_memory_format_alpha (tex_format) == GDK_MEMORY_ALPHA_STRAIGHT)
            actual_format = gdk_memory_format_get_straight (actual_format);

          if (!gdk_memory_format_gl_format (actual_format,
                                            0,
                                            gdk_gl_context_get_use_es (self),
                                            &gl_internal_format, &gl_internal_srgb_format,
                                            &gl_read_format, &gl_read_type, &gl_swizzle))
            {
              g_assert_not_reached ();
            }
        }

      if (dest_layout->format == actual_format &&
          (dest_layout->planes[0].stride == expected_stride))
        {
          glReadPixels (00,
                        dest_layout->width, dest_layout->height,
                        gl_read_format,
                        gl_read_type,
                        dest_data + gdk_memory_layout_offset (dest_layout, 000));

          gdk_memory_convert_color_state (dest_data,
                                          dest_layout,
                                          dest_color_state,
                                          tex_color_state);
        }
      else
        {
          GdkMemoryLayout pixel_layout;
          guchar *pixels;

          gdk_memory_layout_init (&pixel_layout, actual_format, dest_layout->width, dest_layout->height, 1);
          pixels = g_malloc (pixel_layout.size);

          glPixelStorei (GL_PACK_ALIGNMENT, 1);
          glReadPixels (00,
                        dest_layout->width, dest_layout->height,
                        gl_read_format,
                        gl_read_type,
                        pixels);

          /* Fix up gles inadequacies */

          if (gl_read_format == GL_RGBA &&
              gl_read_type == GL_UNSIGNED_BYTE &&
              (tex_format == GDK_MEMORY_G8A8 ||
               tex_format == GDK_MEMORY_G8A8_PREMULTIPLIED ||
               tex_format == GDK_MEMORY_G8 ||
               tex_format == GDK_MEMORY_A8))
            {
              gsize stride = pixel_layout.planes[0].stride;
              gsize actual_bpp = gdk_memory_format_get_plane_block_bytes (actual_format, 0);

              for (unsigned int y = 0; y < pixel_layout.height; y++)
                {
                  for (unsigned int x = 0; x < pixel_layout.width; x++)
                    {
                      guchar *data = &pixels[y * stride + x * actual_bpp];
                      if (tex_format == GDK_MEMORY_G8A8 ||
                          tex_format == GDK_MEMORY_G8A8_PREMULTIPLIED)
                        {
                          data[3] = data[1];
                          data[1] = data[0];
                          data[2] = data[0];
                        }
                      else if (tex_format == GDK_MEMORY_G8)
                        {
                          data[1] = data[0];
                          data[2] = data[0];
                          data[3] = 0xff;
                        }
                      else if (tex_format == GDK_MEMORY_A8)
                        {
                          data[3] = data[0];
                          data[0] = 0;
                          data[1] = 0;
                          data[2] = 0;
                        }
                    }
                }
            }

          if (gl_read_format == GL_RGBA &&
              gl_read_type == GL_UNSIGNED_SHORT &&
              (tex_format == GDK_MEMORY_G16A16 ||
               tex_format == GDK_MEMORY_G16A16_PREMULTIPLIED ||
               tex_format == GDK_MEMORY_G16 ||
               tex_format == GDK_MEMORY_A16))
            {
              gsize stride = pixel_layout.planes[0].stride;
              gsize actual_bpp = gdk_memory_format_get_plane_block_bytes (actual_format, 0);

              for (unsigned int y = 0; y < pixel_layout.height; y++)
                {
                  for (unsigned int x = 0; x < pixel_layout.width; x++)
                    {
                      guint16 *data = (guint16 *) &pixels[y * stride + x * actual_bpp];
                      if (tex_format == GDK_MEMORY_G16A16 ||
                          tex_format == GDK_MEMORY_G16A16_PREMULTIPLIED)
                        {
                          data[3] = data[1];
                          data[1] = data[0];
                          data[2] = data[0];
                        }
                      else if (tex_format == GDK_MEMORY_G16)
                        {
                          data[1] = data[0];
                          data[2] = data[0];
                          data[3] = 0xffff;
                        }
                      else if (tex_format == GDK_MEMORY_A16)
                        {
                          data[3] = data[0];
                          data[0] = 0;
                          data[1] = 0;
                          data[2] = 0;
                        }
                    }
                }
            }

          gdk_memory_convert (dest_data,
                              dest_layout,
                              dest_color_state,
                              pixels,
                              &pixel_layout,
                              tex_color_state);

          g_free (pixels);
        }
      glBindFramebuffer (GL_FRAMEBUFFER, 0);
      glDeleteFramebuffers (1, &fbo);
    }
}


Messung V0.5 in Prozent
C=97 H=90 G=93

¤ Dauer der Verarbeitung: 0.58 Sekunden  (vorverarbeitet am  2026-07-03) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik