summaryrefslogtreecommitdiff
path: root/cogl/cogl/winsys/cogl-winsys-egl.c
diff options
context:
space:
mode:
Diffstat (limited to 'cogl/cogl/winsys/cogl-winsys-egl.c')
-rw-r--r--cogl/cogl/winsys/cogl-winsys-egl.c1085
1 files changed, 1085 insertions, 0 deletions
diff --git a/cogl/cogl/winsys/cogl-winsys-egl.c b/cogl/cogl/winsys/cogl-winsys-egl.c
new file mode 100644
index 000000000..a53c0c7d7
--- /dev/null
+++ b/cogl/cogl/winsys/cogl-winsys-egl.c
@@ -0,0 +1,1085 @@
+/*
+ * Cogl
+ *
+ * A Low Level GPU Graphics and Utilities API
+ *
+ * Copyright (C) 2007,2008,2009,2010,2011,2013 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ *
+ * Authors:
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-i18n-private.h"
+#include "cogl-util.h"
+#include "cogl-winsys-egl-private.h"
+#include "cogl-winsys-private.h"
+#include "cogl-feature-private.h"
+#include "cogl-context-private.h"
+#include "cogl-framebuffer.h"
+#include "cogl-onscreen-private.h"
+#include "cogl-swap-chain-private.h"
+#include "cogl-renderer-private.h"
+#include "cogl-onscreen-template-private.h"
+#include "cogl-gles2-context-private.h"
+#include "cogl-error-private.h"
+#include "cogl-egl.h"
+
+#include "cogl-private.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+#ifndef EGL_KHR_create_context
+#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098
+#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB
+#define EGL_CONTEXT_FLAGS_KHR 0x30FC
+#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD
+#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31BD
+#define EGL_OPENGL_ES3_BIT_KHR 0x0040
+#define EGL_NO_RESET_NOTIFICATION_KHR 0x31BE
+#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31BF
+#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001
+#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002
+#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004
+#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001
+#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002
+#endif
+
+
+#define MAX_EGL_CONFIG_ATTRIBS 30
+
+/* Define a set of arrays containing the functions required from GL
+ for each winsys feature */
+#define COGL_WINSYS_FEATURE_BEGIN(name, namespaces, extension_names, \
+ egl_private_flags) \
+ static const CoglFeatureFunction \
+ cogl_egl_feature_ ## name ## _funcs[] = {
+#define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) \
+ { G_STRINGIFY (name), G_STRUCT_OFFSET (CoglRendererEGL, pf_ ## name) },
+#define COGL_WINSYS_FEATURE_END() \
+ { NULL, 0 }, \
+ };
+#include "cogl-winsys-egl-feature-functions.h"
+
+/* Define an array of features */
+#undef COGL_WINSYS_FEATURE_BEGIN
+#define COGL_WINSYS_FEATURE_BEGIN(name, namespaces, extension_names, \
+ egl_private_flags) \
+ { 255, 255, 0, namespaces, extension_names, \
+ 0, egl_private_flags, \
+ 0, \
+ cogl_egl_feature_ ## name ## _funcs },
+#undef COGL_WINSYS_FEATURE_FUNCTION
+#define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args)
+#undef COGL_WINSYS_FEATURE_END
+#define COGL_WINSYS_FEATURE_END()
+
+static const CoglFeatureData winsys_feature_data[] =
+ {
+#include "cogl-winsys-egl-feature-functions.h"
+ };
+
+static const char *
+get_error_string (void)
+{
+ switch (eglGetError()){
+ case EGL_BAD_DISPLAY:
+ return "Invalid display";
+ case EGL_NOT_INITIALIZED:
+ return "Display not initialized";
+ case EGL_BAD_ALLOC:
+ return "Not enough resources to allocate context";
+ case EGL_BAD_ATTRIBUTE:
+ return "Invalid attribute";
+ case EGL_BAD_CONFIG:
+ return "Invalid config";
+ case EGL_BAD_CONTEXT:
+ return "Invalid context";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "Invalid current surface";
+ case EGL_BAD_MATCH:
+ return "Bad match";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "Invalid native pixmap";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "Invalid native window";
+ case EGL_BAD_PARAMETER:
+ return "Invalid parameter";
+ case EGL_BAD_SURFACE:
+ return "Invalid surface";
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static CoglFuncPtr
+_cogl_winsys_renderer_get_proc_address (CoglRenderer *renderer,
+ const char *name,
+ CoglBool in_core)
+{
+ void *ptr = NULL;
+
+ if (!in_core)
+ ptr = eglGetProcAddress (name);
+
+ /* eglGetProcAddress doesn't support fetching core API so we need to
+ get that separately with GModule */
+ if (ptr == NULL)
+ g_module_symbol (renderer->libgl_module, name, &ptr);
+
+ return ptr;
+}
+
+static void
+_cogl_winsys_renderer_disconnect (CoglRenderer *renderer)
+{
+ /* This function must be overridden by a platform winsys */
+ g_assert_not_reached ();
+}
+
+/* Updates all the function pointers */
+static void
+check_egl_extensions (CoglRenderer *renderer)
+{
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+ const char *egl_extensions;
+ char **split_extensions;
+ int i;
+
+ egl_extensions = eglQueryString (egl_renderer->edpy, EGL_EXTENSIONS);
+ split_extensions = g_strsplit (egl_extensions, " ", 0 /* max_tokens */);
+
+ COGL_NOTE (WINSYS, " EGL Extensions: %s", egl_extensions);
+
+ egl_renderer->private_features = 0;
+ for (i = 0; i < G_N_ELEMENTS (winsys_feature_data); i++)
+ if (_cogl_feature_check (renderer,
+ "EGL", winsys_feature_data + i, 0, 0,
+ COGL_DRIVER_GL, /* the driver isn't used */
+ split_extensions,
+ egl_renderer))
+ {
+ egl_renderer->private_features |=
+ winsys_feature_data[i].feature_flags_private;
+ }
+
+ g_strfreev (split_extensions);
+}
+
+CoglBool
+_cogl_winsys_egl_renderer_connect_common (CoglRenderer *renderer,
+ CoglError **error)
+{
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+
+ if (!eglInitialize (egl_renderer->edpy,
+ &egl_renderer->egl_version_major,
+ &egl_renderer->egl_version_minor))
+ {
+ _cogl_set_error (error, COGL_WINSYS_ERROR,
+ COGL_WINSYS_ERROR_INIT,
+ "Couldn't initialize EGL");
+ return FALSE;
+ }
+
+ check_egl_extensions (renderer);
+
+ return TRUE;
+}
+
+static CoglBool
+_cogl_winsys_renderer_connect (CoglRenderer *renderer,
+ CoglError **error)
+{
+ /* This function must be overridden by a platform winsys */
+ g_assert_not_reached ();
+}
+
+static void
+egl_attributes_from_framebuffer_config (CoglDisplay *display,
+ CoglFramebufferConfig *config,
+ EGLint *attributes)
+{
+ CoglRenderer *renderer = display->renderer;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+ int i = 0;
+
+ /* Let the platform add attributes first */
+ if (egl_renderer->platform_vtable->add_config_attributes)
+ i = egl_renderer->platform_vtable->add_config_attributes (display,
+ config,
+ attributes);
+
+ if (config->need_stencil)
+ {
+ attributes[i++] = EGL_STENCIL_SIZE;
+ attributes[i++] = 2;
+ }
+
+ attributes[i++] = EGL_RED_SIZE;
+ attributes[i++] = 1;
+ attributes[i++] = EGL_GREEN_SIZE;
+ attributes[i++] = 1;
+ attributes[i++] = EGL_BLUE_SIZE;
+ attributes[i++] = 1;
+
+ attributes[i++] = EGL_ALPHA_SIZE;
+ attributes[i++] = config->swap_chain->has_alpha ? 1 : EGL_DONT_CARE;
+
+ attributes[i++] = EGL_DEPTH_SIZE;
+ attributes[i++] = 1;
+
+ attributes[i++] = EGL_BUFFER_SIZE;
+ attributes[i++] = EGL_DONT_CARE;
+
+ attributes[i++] = EGL_RENDERABLE_TYPE;
+ attributes[i++] = ((renderer->driver == COGL_DRIVER_GL ||
+ renderer->driver == COGL_DRIVER_GL3) ?
+ EGL_OPENGL_BIT :
+ renderer->driver == COGL_DRIVER_GLES1 ?
+ EGL_OPENGL_ES_BIT :
+ EGL_OPENGL_ES2_BIT);
+
+ attributes[i++] = EGL_SURFACE_TYPE;
+ attributes[i++] = EGL_WINDOW_BIT;
+
+ if (config->samples_per_pixel)
+ {
+ attributes[i++] = EGL_SAMPLE_BUFFERS;
+ attributes[i++] = 1;
+ attributes[i++] = EGL_SAMPLES;
+ attributes[i++] = config->samples_per_pixel;
+ }
+
+ attributes[i++] = EGL_NONE;
+
+ g_assert (i < MAX_EGL_CONFIG_ATTRIBS);
+}
+
+EGLBoolean
+_cogl_winsys_egl_make_current (CoglDisplay *display,
+ EGLSurface draw,
+ EGLSurface read,
+ EGLContext context)
+{
+ CoglDisplayEGL *egl_display = display->winsys;
+ CoglRendererEGL *egl_renderer = display->renderer->winsys;
+ EGLBoolean ret;
+
+ if (egl_display->current_draw_surface == draw &&
+ egl_display->current_read_surface == read &&
+ egl_display->current_context == context)
+ return EGL_TRUE;
+
+ ret = eglMakeCurrent (egl_renderer->edpy,
+ draw,
+ read,
+ context);
+
+ egl_display->current_draw_surface = draw;
+ egl_display->current_read_surface = read;
+ egl_display->current_context = context;
+
+ return ret;
+}
+
+static void
+cleanup_context (CoglDisplay *display)
+{
+ CoglRenderer *renderer = display->renderer;
+ CoglDisplayEGL *egl_display = display->winsys;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+
+ if (egl_display->egl_context != EGL_NO_CONTEXT)
+ {
+ _cogl_winsys_egl_make_current (display,
+ EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ eglDestroyContext (egl_renderer->edpy, egl_display->egl_context);
+ egl_display->egl_context = EGL_NO_CONTEXT;
+ }
+
+ if (egl_renderer->platform_vtable->cleanup_context)
+ egl_renderer->platform_vtable->cleanup_context (display);
+}
+
+static CoglBool
+try_create_context (CoglDisplay *display,
+ CoglError **error)
+{
+ CoglRenderer *renderer = display->renderer;
+ CoglDisplayEGL *egl_display = display->winsys;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+ EGLDisplay edpy;
+ EGLConfig config;
+ EGLint config_count = 0;
+ EGLBoolean status;
+ EGLint attribs[9];
+ EGLint cfg_attribs[MAX_EGL_CONFIG_ATTRIBS];
+ const char *error_message;
+
+ _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context == NULL, TRUE);
+
+ if (renderer->driver == COGL_DRIVER_GL ||
+ renderer->driver == COGL_DRIVER_GL3)
+ eglBindAPI (EGL_OPENGL_API);
+
+ egl_attributes_from_framebuffer_config (display,
+ &display->onscreen_template->config,
+ cfg_attribs);
+
+ edpy = egl_renderer->edpy;
+
+ status = eglChooseConfig (edpy,
+ cfg_attribs,
+ &config, 1,
+ &config_count);
+ if (status != EGL_TRUE || config_count == 0)
+ {
+ error_message = "Unable to find a usable EGL configuration";
+ goto fail;
+ }
+
+ egl_display->egl_config = config;
+
+ if (display->renderer->driver == COGL_DRIVER_GL3)
+ {
+ if (!(egl_renderer->private_features &
+ COGL_EGL_WINSYS_FEATURE_CREATE_CONTEXT))
+ {
+ error_message = "Driver does not support GL 3 contexts";
+ goto fail;
+ }
+
+ /* Try to get a core profile 3.1 context with no deprecated features */
+ attribs[0] = EGL_CONTEXT_MAJOR_VERSION_KHR;
+ attribs[1] = 3;
+ attribs[2] = EGL_CONTEXT_MINOR_VERSION_KHR;
+ attribs[3] = 1;
+ attribs[4] = EGL_CONTEXT_FLAGS_KHR;
+ attribs[5] = EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;
+ attribs[6] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
+ attribs[7] = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
+ attribs[8] = EGL_NONE;
+ }
+ else if (display->renderer->driver == COGL_DRIVER_GLES2)
+ {
+ attribs[0] = EGL_CONTEXT_CLIENT_VERSION;
+ attribs[1] = 2;
+ attribs[2] = EGL_NONE;
+ }
+ else
+ attribs[0] = EGL_NONE;
+
+ egl_display->egl_context = eglCreateContext (edpy,
+ config,
+ EGL_NO_CONTEXT,
+ attribs);
+
+ if (egl_display->egl_context == EGL_NO_CONTEXT)
+ {
+ error_message = "Unable to create a suitable EGL context";
+ goto fail;
+ }
+
+ if (egl_renderer->platform_vtable->context_created &&
+ !egl_renderer->platform_vtable->context_created (display, error))
+ return FALSE;
+
+ return TRUE;
+
+fail:
+ _cogl_set_error (error, COGL_WINSYS_ERROR,
+ COGL_WINSYS_ERROR_CREATE_CONTEXT,
+ "%s", error_message);
+
+ cleanup_context (display);
+
+ return FALSE;
+}
+
+static void
+_cogl_winsys_display_destroy (CoglDisplay *display)
+{
+ CoglRendererEGL *egl_renderer = display->renderer->winsys;
+ CoglDisplayEGL *egl_display = display->winsys;
+
+ _COGL_RETURN_IF_FAIL (egl_display != NULL);
+
+ cleanup_context (display);
+
+ if (egl_renderer->platform_vtable->display_destroy)
+ egl_renderer->platform_vtable->display_destroy (display);
+
+ g_slice_free (CoglDisplayEGL, display->winsys);
+ display->winsys = NULL;
+}
+
+static CoglBool
+_cogl_winsys_display_setup (CoglDisplay *display,
+ CoglError **error)
+{
+ CoglDisplayEGL *egl_display;
+ CoglRenderer *renderer = display->renderer;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+
+ _COGL_RETURN_VAL_IF_FAIL (display->winsys == NULL, FALSE);
+
+ egl_display = g_slice_new0 (CoglDisplayEGL);
+ display->winsys = egl_display;
+
+#ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT
+ if (display->wayland_compositor_display)
+ {
+ struct wl_display *wayland_display = display->wayland_compositor_display;
+ CoglRendererEGL *egl_renderer = display->renderer->winsys;
+
+ if (egl_renderer->pf_eglBindWaylandDisplay)
+ egl_renderer->pf_eglBindWaylandDisplay (egl_renderer->edpy,
+ wayland_display);
+ }
+#endif
+
+ if (egl_renderer->platform_vtable->display_setup &&
+ !egl_renderer->platform_vtable->display_setup (display, error))
+ goto error;
+
+ if (!try_create_context (display, error))
+ goto error;
+
+ egl_display->found_egl_config = TRUE;
+
+ return TRUE;
+
+error:
+ _cogl_winsys_display_destroy (display);
+ return FALSE;
+}
+
+static CoglBool
+_cogl_winsys_context_init (CoglContext *context, CoglError **error)
+{
+ CoglRenderer *renderer = context->display->renderer;
+ CoglDisplayEGL *egl_display = context->display->winsys;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+
+ context->winsys = g_new0 (CoglContextEGL, 1);
+
+ _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE);
+
+ memset (context->winsys_features, 0, sizeof (context->winsys_features));
+
+ check_egl_extensions (renderer);
+
+ if (!_cogl_context_update_features (context, error))
+ return FALSE;
+
+ if (egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_SWAP_REGION)
+ {
+ COGL_FLAGS_SET (context->winsys_features,
+ COGL_WINSYS_FEATURE_SWAP_REGION, TRUE);
+ COGL_FLAGS_SET (context->winsys_features,
+ COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE);
+ }
+
+ if ((egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_FENCE_SYNC) &&
+ _cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_OES_EGL_SYNC))
+ COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_FENCE, TRUE);
+
+ if (egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_BUFFER_AGE)
+ {
+ COGL_FLAGS_SET (context->winsys_features,
+ COGL_WINSYS_FEATURE_BUFFER_AGE,
+ TRUE);
+ COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_BUFFER_AGE, TRUE);
+ }
+
+ /* NB: We currently only support creating standalone GLES2 contexts
+ * for offscreen rendering and so we need a dummy (non-visible)
+ * surface to be able to bind those contexts */
+ if (egl_display->dummy_surface != EGL_NO_SURFACE &&
+ context->driver == COGL_DRIVER_GLES2)
+ COGL_FLAGS_SET (context->features,
+ COGL_FEATURE_ID_GLES2_CONTEXT, TRUE);
+
+ if (egl_renderer->platform_vtable->context_init &&
+ !egl_renderer->platform_vtable->context_init (context, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+_cogl_winsys_context_deinit (CoglContext *context)
+{
+ CoglRenderer *renderer = context->display->renderer;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+
+ if (egl_renderer->platform_vtable->context_deinit)
+ egl_renderer->platform_vtable->context_deinit (context);
+
+ g_free (context->winsys);
+}
+
+typedef struct _CoglGLES2ContextEGL
+{
+ EGLContext egl_context;
+ EGLSurface dummy_surface;
+} CoglGLES2ContextEGL;
+
+static void *
+_cogl_winsys_context_create_gles2_context (CoglContext *ctx, CoglError **error)
+{
+ CoglRendererEGL *egl_renderer = ctx->display->renderer->winsys;
+ CoglDisplayEGL *egl_display = ctx->display->winsys;
+ EGLint attribs[3];
+ EGLContext egl_context;
+
+ attribs[0] = EGL_CONTEXT_CLIENT_VERSION;
+ attribs[1] = 2;
+ attribs[2] = EGL_NONE;
+
+ egl_context = eglCreateContext (egl_renderer->edpy,
+ egl_display->egl_config,
+ egl_display->egl_context,
+ attribs);
+ if (egl_context == EGL_NO_CONTEXT)
+ {
+ _cogl_set_error (error, COGL_WINSYS_ERROR,
+ COGL_WINSYS_ERROR_CREATE_GLES2_CONTEXT,
+ "%s", get_error_string ());
+ return NULL;
+ }
+
+ return (void *)egl_context;
+}
+
+static void
+_cogl_winsys_destroy_gles2_context (CoglGLES2Context *gles2_ctx)
+{
+ CoglContext *context = gles2_ctx->context;
+ CoglDisplay *display = context->display;
+ CoglDisplayEGL *egl_display = display->winsys;
+ CoglRenderer *renderer = display->renderer;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+ EGLContext egl_context = gles2_ctx->winsys;
+
+ _COGL_RETURN_IF_FAIL (egl_display->current_context != egl_context);
+
+ eglDestroyContext (egl_renderer->edpy, egl_context);
+}
+
+static CoglBool
+_cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
+ CoglError **error)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = framebuffer->context;
+ CoglDisplay *display = context->display;
+ CoglDisplayEGL *egl_display = display->winsys;
+ CoglRenderer *renderer = display->renderer;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+ EGLint attributes[MAX_EGL_CONFIG_ATTRIBS];
+ EGLConfig egl_config;
+ EGLint config_count = 0;
+ EGLBoolean status;
+
+ _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE);
+
+ egl_attributes_from_framebuffer_config (display,
+ &framebuffer->config,
+ attributes);
+
+ status = eglChooseConfig (egl_renderer->edpy,
+ attributes,
+ &egl_config, 1,
+ &config_count);
+ if (status != EGL_TRUE || config_count == 0)
+ {
+ _cogl_set_error (error, COGL_WINSYS_ERROR,
+ COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+ "Failed to find a suitable EGL configuration");
+ return FALSE;
+ }
+
+ /* Update the real number of samples_per_pixel now that we have
+ * found an egl_config... */
+ if (framebuffer->config.samples_per_pixel)
+ {
+ EGLint samples;
+ status = eglGetConfigAttrib (egl_renderer->edpy,
+ egl_config,
+ EGL_SAMPLES, &samples);
+ g_return_val_if_fail (status == EGL_TRUE, TRUE);
+ framebuffer->samples_per_pixel = samples;
+ }
+
+ onscreen->winsys = g_slice_new0 (CoglOnscreenEGL);
+
+ if (egl_renderer->platform_vtable->onscreen_init &&
+ !egl_renderer->platform_vtable->onscreen_init (onscreen,
+ egl_config,
+ error))
+ {
+ g_slice_free (CoglOnscreenEGL, onscreen->winsys);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+_cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = framebuffer->context;
+ CoglDisplayEGL *egl_display = context->display->winsys;
+ CoglRenderer *renderer = context->display->renderer;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+ CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+
+ /* If we never successfully allocated then there's nothing to do */
+ if (egl_onscreen == NULL)
+ return;
+
+ if (egl_onscreen->egl_surface != EGL_NO_SURFACE)
+ {
+ /* Cogl always needs a valid context bound to something so if we
+ * are destroying the onscreen that is currently bound we'll
+ * switch back to the dummy drawable. */
+ if ((egl_display->dummy_surface != EGL_NO_SURFACE ||
+ (egl_renderer->private_features &
+ COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT) != 0) &&
+ (egl_display->current_draw_surface == egl_onscreen->egl_surface ||
+ egl_display->current_read_surface == egl_onscreen->egl_surface))
+ {
+ _cogl_winsys_egl_make_current (context->display,
+ egl_display->dummy_surface,
+ egl_display->dummy_surface,
+ egl_display->current_context);
+ }
+
+ if (eglDestroySurface (egl_renderer->edpy, egl_onscreen->egl_surface)
+ == EGL_FALSE)
+ g_warning ("Failed to destroy EGL surface");
+ egl_onscreen->egl_surface = EGL_NO_SURFACE;
+ }
+
+ if (egl_renderer->platform_vtable->onscreen_deinit)
+ egl_renderer->platform_vtable->onscreen_deinit (onscreen);
+
+ g_slice_free (CoglOnscreenEGL, onscreen->winsys);
+ onscreen->winsys = NULL;
+}
+
+static CoglBool
+bind_onscreen_with_context (CoglOnscreen *onscreen,
+ EGLContext egl_context)
+{
+ CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = fb->context;
+ CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+
+ CoglBool status = _cogl_winsys_egl_make_current (context->display,
+ egl_onscreen->egl_surface,
+ egl_onscreen->egl_surface,
+ egl_context);
+ if (status)
+ {
+ CoglRenderer *renderer = context->display->renderer;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+
+ if (fb->config.swap_throttled)
+ eglSwapInterval (egl_renderer->edpy, 1);
+ else
+ eglSwapInterval (egl_renderer->edpy, 0);
+ }
+
+ return status;
+}
+
+static CoglBool
+bind_onscreen (CoglOnscreen *onscreen)
+{
+ CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = fb->context;
+ CoglDisplayEGL *egl_display = context->display->winsys;
+
+ return bind_onscreen_with_context (onscreen, egl_display->egl_context);
+}
+
+static void
+_cogl_winsys_onscreen_bind (CoglOnscreen *onscreen)
+{
+ bind_onscreen (onscreen);
+}
+
+#ifndef EGL_BUFFER_AGE_EXT
+#define EGL_BUFFER_AGE_EXT 0x313D
+#endif
+
+static int
+_cogl_winsys_onscreen_get_buffer_age (CoglOnscreen *onscreen)
+{
+ CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
+ CoglRenderer *renderer = context->display->renderer;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+ CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+ EGLSurface surface = egl_onscreen->egl_surface;
+ int age;
+
+ if (!(egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_BUFFER_AGE))
+ return 0;
+
+ eglQuerySurface (egl_renderer->edpy, surface, EGL_BUFFER_AGE_EXT, &age);
+
+ return age;
+}
+
+static void
+_cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
+ const int *user_rectangles,
+ int n_rectangles)
+{
+ CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
+ CoglRenderer *renderer = context->display->renderer;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+ CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ int framebuffer_height = cogl_framebuffer_get_height (framebuffer);
+ int *rectangles = g_alloca (sizeof (int) * n_rectangles * 4);
+ int i;
+
+ /* eglSwapBuffersRegion expects rectangles relative to the
+ * bottom left corner but we are given rectangles relative to
+ * the top left so we need to flip them... */
+ memcpy (rectangles, user_rectangles, sizeof (int) * n_rectangles * 4);
+ for (i = 0; i < n_rectangles; i++)
+ {
+ int *rect = &rectangles[4 * i];
+ rect[1] = framebuffer_height - rect[1] - rect[3];
+ }
+
+ /* At least for eglSwapBuffers the EGL spec says that the surface to
+ swap must be bound to the current context. It looks like Mesa
+ also validates that this is the case for eglSwapBuffersRegion so
+ we must bind here too */
+ _cogl_framebuffer_flush_state (COGL_FRAMEBUFFER (onscreen),
+ COGL_FRAMEBUFFER (onscreen),
+ COGL_FRAMEBUFFER_STATE_BIND);
+
+ if (egl_renderer->pf_eglSwapBuffersRegion (egl_renderer->edpy,
+ egl_onscreen->egl_surface,
+ n_rectangles,
+ rectangles) == EGL_FALSE)
+ g_warning ("Error reported by eglSwapBuffersRegion");
+}
+
+static void
+_cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
+ const int *rectangles,
+ int n_rectangles)
+{
+ CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
+ CoglRenderer *renderer = context->display->renderer;
+ CoglRendererEGL *egl_renderer = renderer->winsys;
+ CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+
+ /* The specification for EGL (at least in 1.4) says that the surface
+ needs to be bound to the current context for the swap to work
+ although it may change in future. Mesa explicitly checks for this
+ and just returns an error if this is not the case so we can't
+ just pretend this isn't in the spec. */
+ _cogl_framebuffer_flush_state (COGL_FRAMEBUFFER (onscreen),
+ COGL_FRAMEBUFFER (onscreen),
+ COGL_FRAMEBUFFER_STATE_BIND);
+
+ if (n_rectangles && egl_renderer->pf_eglSwapBuffersWithDamage)
+ {
+ CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen);
+ size_t size = n_rectangles * sizeof (int) * 4;
+ int *flipped = alloca (size);
+ int i;
+
+ memcpy (flipped, rectangles, size);
+ for (i = 0; i < n_rectangles; i++)
+ {
+ const int *rect = rectangles + 4 * i;
+ int *flip_rect = flipped + 4 * i;
+ flip_rect[1] = fb->height - rect[1] - rect[3];
+ }
+
+ if (egl_renderer->pf_eglSwapBuffersWithDamage (egl_renderer->edpy,
+ egl_onscreen->egl_surface,
+ flipped,
+ n_rectangles) == EGL_FALSE)
+ g_warning ("Error reported by eglSwapBuffersWithDamage");
+ }
+ else
+ eglSwapBuffers (egl_renderer->edpy, egl_onscreen->egl_surface);
+}
+
+static void
+_cogl_winsys_onscreen_update_swap_throttled (CoglOnscreen *onscreen)
+{
+ CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
+ CoglDisplayEGL *egl_display = context->display->winsys;
+ CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+
+ if (egl_display->current_draw_surface != egl_onscreen->egl_surface)
+ return;
+
+ egl_display->current_draw_surface = EGL_NO_SURFACE;
+
+ _cogl_winsys_onscreen_bind (onscreen);
+}
+
+static void
+_cogl_winsys_save_context (CoglContext *ctx)
+{
+ CoglContextEGL *egl_context = ctx->winsys;
+ CoglDisplayEGL *egl_display = ctx->display->winsys;
+
+ egl_context->saved_draw_surface = egl_display->current_draw_surface;
+ egl_context->saved_read_surface = egl_display->current_read_surface;
+}
+
+static CoglBool
+_cogl_winsys_set_gles2_context (CoglGLES2Context *gles2_ctx, CoglError **error)
+{
+ CoglContext *ctx = gles2_ctx->context;
+ CoglDisplayEGL *egl_display = ctx->display->winsys;
+ CoglBool status;
+
+ if (gles2_ctx->write_buffer &&
+ cogl_is_onscreen (gles2_ctx->write_buffer))
+ status =
+ bind_onscreen_with_context (COGL_ONSCREEN (gles2_ctx->write_buffer),
+ gles2_ctx->winsys);
+ else
+ status = _cogl_winsys_egl_make_current (ctx->display,
+ egl_display->dummy_surface,
+ egl_display->dummy_surface,
+ gles2_ctx->winsys);
+
+ if (!status)
+ {
+ _cogl_set_error (error,
+ COGL_WINSYS_ERROR,
+ COGL_WINSYS_ERROR_MAKE_CURRENT,
+ "Failed to make gles2 context current");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+_cogl_winsys_restore_context (CoglContext *ctx)
+{
+ CoglContextEGL *egl_context = ctx->winsys;
+ CoglDisplayEGL *egl_display = ctx->display->winsys;
+
+ _cogl_winsys_egl_make_current (ctx->display,
+ egl_context->saved_draw_surface,
+ egl_context->saved_read_surface,
+ egl_display->egl_context);
+}
+
+#if defined(EGL_KHR_fence_sync) || defined(EGL_KHR_reusable_sync)
+static void *
+_cogl_winsys_fence_add (CoglContext *context)
+{
+ CoglRendererEGL *renderer = context->display->renderer->winsys;
+ void *ret;
+
+ if (renderer->pf_eglCreateSync)
+ ret = renderer->pf_eglCreateSync (renderer->edpy,
+ EGL_SYNC_FENCE_KHR,
+ NULL);
+ else
+ ret = NULL;
+
+ return ret;
+}
+
+static CoglBool
+_cogl_winsys_fence_is_complete (CoglContext *context, void *fence)
+{
+ CoglRendererEGL *renderer = context->display->renderer->winsys;
+ EGLint ret;
+
+ ret = renderer->pf_eglClientWaitSync (renderer->edpy,
+ fence,
+ EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
+ 0);
+ return (ret == EGL_CONDITION_SATISFIED_KHR);
+}
+
+static void
+_cogl_winsys_fence_destroy (CoglContext *context, void *fence)
+{
+ CoglRendererEGL *renderer = context->display->renderer->winsys;
+
+ renderer->pf_eglDestroySync (renderer->edpy, fence);
+}
+#endif
+
+static CoglWinsysVtable _cogl_winsys_vtable =
+ {
+ .constraints = COGL_RENDERER_CONSTRAINT_USES_EGL |
+ COGL_RENDERER_CONSTRAINT_SUPPORTS_COGL_GLES2,
+
+ /* This winsys is only used as a base for the EGL-platform
+ winsys's so it does not have an ID or a name */
+
+ .renderer_get_proc_address = _cogl_winsys_renderer_get_proc_address,
+ .renderer_connect = _cogl_winsys_renderer_connect,
+ .renderer_disconnect = _cogl_winsys_renderer_disconnect,
+ .display_setup = _cogl_winsys_display_setup,
+ .display_destroy = _cogl_winsys_display_destroy,
+ .context_init = _cogl_winsys_context_init,
+ .context_deinit = _cogl_winsys_context_deinit,
+ .context_create_gles2_context =
+ _cogl_winsys_context_create_gles2_context,
+ .destroy_gles2_context = _cogl_winsys_destroy_gles2_context,
+ .onscreen_init = _cogl_winsys_onscreen_init,
+ .onscreen_deinit = _cogl_winsys_onscreen_deinit,
+ .onscreen_bind = _cogl_winsys_onscreen_bind,
+ .onscreen_swap_buffers_with_damage =
+ _cogl_winsys_onscreen_swap_buffers_with_damage,
+ .onscreen_swap_region = _cogl_winsys_onscreen_swap_region,
+ .onscreen_get_buffer_age = _cogl_winsys_onscreen_get_buffer_age,
+ .onscreen_update_swap_throttled =
+ _cogl_winsys_onscreen_update_swap_throttled,
+
+ /* CoglGLES2Context related methods */
+ .save_context = _cogl_winsys_save_context,
+ .set_gles2_context = _cogl_winsys_set_gles2_context,
+ .restore_context = _cogl_winsys_restore_context,
+
+#if defined(EGL_KHR_fence_sync) || defined(EGL_KHR_reusable_sync)
+ .fence_add = _cogl_winsys_fence_add,
+ .fence_is_complete = _cogl_winsys_fence_is_complete,
+ .fence_destroy = _cogl_winsys_fence_destroy,
+#endif
+ };
+
+/* XXX: we use a function because no doubt someone will complain
+ * about using c99 member initializers because they aren't portable
+ * to windows. We want to avoid having to rigidly follow the real
+ * order of members since some members are #ifdefd and we'd have
+ * to mirror the #ifdefing to add padding etc. For any winsys that
+ * can assume the platform has a sane compiler then we can just use
+ * c99 initializers for insane platforms they can initialize
+ * the members by name in a function.
+ */
+const CoglWinsysVtable *
+_cogl_winsys_egl_get_vtable (void)
+{
+ return &_cogl_winsys_vtable;
+}
+
+#ifdef EGL_KHR_image_base
+EGLImageKHR
+_cogl_egl_create_image (CoglContext *ctx,
+ EGLenum target,
+ EGLClientBuffer buffer,
+ const EGLint *attribs)
+{
+ CoglDisplayEGL *egl_display = ctx->display->winsys;
+ CoglRendererEGL *egl_renderer = ctx->display->renderer->winsys;
+ EGLContext egl_ctx;
+
+ _COGL_RETURN_VAL_IF_FAIL (egl_renderer->pf_eglCreateImage, EGL_NO_IMAGE_KHR);
+
+ /* The EGL_KHR_image_pixmap spec explicitly states that EGL_NO_CONTEXT must
+ * always be used in conjunction with the EGL_NATIVE_PIXMAP_KHR target */
+#ifdef EGL_KHR_image_pixmap
+ if (target == EGL_NATIVE_PIXMAP_KHR)
+ egl_ctx = EGL_NO_CONTEXT;
+ else
+#endif
+ egl_ctx = egl_display->egl_context;
+
+ return egl_renderer->pf_eglCreateImage (egl_renderer->edpy,
+ egl_ctx,
+ target,
+ buffer,
+ attribs);
+}
+
+void
+_cogl_egl_destroy_image (CoglContext *ctx,
+ EGLImageKHR image)
+{
+ CoglRendererEGL *egl_renderer = ctx->display->renderer->winsys;
+
+ _COGL_RETURN_IF_FAIL (egl_renderer->pf_eglDestroyImage);
+
+ egl_renderer->pf_eglDestroyImage (egl_renderer->edpy, image);
+}
+#endif
+
+#ifdef EGL_WL_bind_wayland_display
+CoglBool
+_cogl_egl_query_wayland_buffer (CoglContext *ctx,
+ struct wl_resource *buffer,
+ int attribute,
+ int *value)
+{
+ CoglRendererEGL *egl_renderer = ctx->display->renderer->winsys;
+
+ _COGL_RETURN_VAL_IF_FAIL (egl_renderer->pf_eglQueryWaylandBuffer, FALSE);
+
+ return egl_renderer->pf_eglQueryWaylandBuffer (egl_renderer->edpy,
+ buffer,
+ attribute,
+ value);
+}
+#endif
+
+EGLDisplay
+cogl_egl_context_get_egl_display (CoglContext *context)
+{
+ CoglRendererEGL *egl_renderer = context->display->renderer->winsys;
+
+ return egl_renderer->edpy;
+}
+
+EGLContext
+cogl_egl_context_get_egl_context (CoglContext *context)
+{
+ CoglDisplayEGL *egl_display = context->display->winsys;
+
+ return egl_display->egl_context;
+}