mutter/cogl/winsys/cogl-winsys-egl.c

899 lines
28 KiB
C
Raw Normal View History

/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2007,2008,2009,2010,2011 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
*
* Authors:
* Robert Bragg <robert@linux.intel.com>
*/
Intial Re-layout of the Cogl source code and introduction of a Cogl Winsys As part of an incremental process to have Cogl be a standalone project we want to re-consider how we organise the Cogl source code. Currently this is the structure I'm aiming for: cogl/ cogl/ <put common source here> winsys/ cogl-glx.c cogl-wgl.c driver/ gl/ gles/ os/ ? utils/ cogl-fixed cogl-matrix-stack? cogl-journal? cogl-primitives? pango/ The new winsys component is a starting point for migrating window system code (i.e. x11,glx,wgl,osx,egl etc) from Clutter to Cogl. The utils/ and pango/ directories aren't added by this commit, but they are noted because I plan to add them soon. Overview of the planned structure: * The winsys/ API is the API that binds OpenGL to a specific window system, be that X11 or win32 etc. Example are glx, wgl and egl. Much of the logic under clutter/{glx,osx,win32 etc} should migrate here. * Note there is also the idea of a winsys-base that may represent a window system for which there are multiple winsys APIs. An example of this is x11, since glx and egl may both be used with x11. (currently only Clutter has the idea of a winsys-base) * The driver/ represents a specific varient of OpenGL. Currently we have "gl" representing OpenGL 1.4-2.1 (mostly fixed function) and "gles" representing GLES 1.1 (fixed funciton) and 2.0 (fully shader based) * Everything under cogl/ should fundamentally be supporting access to the GPU. Essentially Cogl's most basic requirement is to provide a nice GPU Graphics API and drawing a line between this and the utility functionality we add to support Clutter should help keep this lean and maintainable. * Code under utils/ as suggested builds on cogl/ adding more convenient APIs or mechanism to optimize special cases. Broadly speaking you can compare cogl/ to OpenGL and utils/ to GLU. * clutter/pango will be moved to clutter/cogl/pango How some of the internal configure.ac/pkg-config terminology has changed: backendextra -> CLUTTER_WINSYS_BASE # e.g. "x11" backendextralib -> CLUTTER_WINSYS_BASE_LIB # e.g. "x11/libclutter-x11.la" clutterbackend -> {CLUTTER,COGL}_WINSYS # e.g. "glx" CLUTTER_FLAVOUR -> {CLUTTER,COGL}_WINSYS clutterbackendlib -> CLUTTER_WINSYS_LIB CLUTTER_COGL -> COGL_DRIVER # e.g. "gl" Note: The CLUTTER_FLAVOUR and CLUTTER_COGL defines are kept for apps As the first thing to take advantage of the new winsys component in Cogl; cogl_get_proc_address() has been moved from cogl/{gl,gles}/cogl.c into cogl/common/cogl.c and this common implementation first trys _cogl_winsys_get_proc_address() but if that fails then it falls back to gmodule.
2009-07-28 01:02:02 +00:00
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#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-private.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <glib/gi18n-lib.h>
#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)
Intial Re-layout of the Cogl source code and introduction of a Cogl Winsys As part of an incremental process to have Cogl be a standalone project we want to re-consider how we organise the Cogl source code. Currently this is the structure I'm aiming for: cogl/ cogl/ <put common source here> winsys/ cogl-glx.c cogl-wgl.c driver/ gl/ gles/ os/ ? utils/ cogl-fixed cogl-matrix-stack? cogl-journal? cogl-primitives? pango/ The new winsys component is a starting point for migrating window system code (i.e. x11,glx,wgl,osx,egl etc) from Clutter to Cogl. The utils/ and pango/ directories aren't added by this commit, but they are noted because I plan to add them soon. Overview of the planned structure: * The winsys/ API is the API that binds OpenGL to a specific window system, be that X11 or win32 etc. Example are glx, wgl and egl. Much of the logic under clutter/{glx,osx,win32 etc} should migrate here. * Note there is also the idea of a winsys-base that may represent a window system for which there are multiple winsys APIs. An example of this is x11, since glx and egl may both be used with x11. (currently only Clutter has the idea of a winsys-base) * The driver/ represents a specific varient of OpenGL. Currently we have "gl" representing OpenGL 1.4-2.1 (mostly fixed function) and "gles" representing GLES 1.1 (fixed funciton) and 2.0 (fully shader based) * Everything under cogl/ should fundamentally be supporting access to the GPU. Essentially Cogl's most basic requirement is to provide a nice GPU Graphics API and drawing a line between this and the utility functionality we add to support Clutter should help keep this lean and maintainable. * Code under utils/ as suggested builds on cogl/ adding more convenient APIs or mechanism to optimize special cases. Broadly speaking you can compare cogl/ to OpenGL and utils/ to GLU. * clutter/pango will be moved to clutter/cogl/pango How some of the internal configure.ac/pkg-config terminology has changed: backendextra -> CLUTTER_WINSYS_BASE # e.g. "x11" backendextralib -> CLUTTER_WINSYS_BASE_LIB # e.g. "x11/libclutter-x11.la" clutterbackend -> {CLUTTER,COGL}_WINSYS # e.g. "glx" CLUTTER_FLAVOUR -> {CLUTTER,COGL}_WINSYS clutterbackendlib -> CLUTTER_WINSYS_LIB CLUTTER_COGL -> COGL_DRIVER # e.g. "gl" Note: The CLUTTER_FLAVOUR and CLUTTER_COGL defines are kept for apps As the first thing to take advantage of the new winsys component in Cogl; cogl_get_proc_address() has been moved from cogl/{gl,gles}/cogl.c into cogl/common/cogl.c and this common implementation first trys _cogl_winsys_get_proc_address() but if that fails then it falls back to gmodule.
2009-07-28 01:02:02 +00:00
{
void *ptr;
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;
Intial Re-layout of the Cogl source code and introduction of a Cogl Winsys As part of an incremental process to have Cogl be a standalone project we want to re-consider how we organise the Cogl source code. Currently this is the structure I'm aiming for: cogl/ cogl/ <put common source here> winsys/ cogl-glx.c cogl-wgl.c driver/ gl/ gles/ os/ ? utils/ cogl-fixed cogl-matrix-stack? cogl-journal? cogl-primitives? pango/ The new winsys component is a starting point for migrating window system code (i.e. x11,glx,wgl,osx,egl etc) from Clutter to Cogl. The utils/ and pango/ directories aren't added by this commit, but they are noted because I plan to add them soon. Overview of the planned structure: * The winsys/ API is the API that binds OpenGL to a specific window system, be that X11 or win32 etc. Example are glx, wgl and egl. Much of the logic under clutter/{glx,osx,win32 etc} should migrate here. * Note there is also the idea of a winsys-base that may represent a window system for which there are multiple winsys APIs. An example of this is x11, since glx and egl may both be used with x11. (currently only Clutter has the idea of a winsys-base) * The driver/ represents a specific varient of OpenGL. Currently we have "gl" representing OpenGL 1.4-2.1 (mostly fixed function) and "gles" representing GLES 1.1 (fixed funciton) and 2.0 (fully shader based) * Everything under cogl/ should fundamentally be supporting access to the GPU. Essentially Cogl's most basic requirement is to provide a nice GPU Graphics API and drawing a line between this and the utility functionality we add to support Clutter should help keep this lean and maintainable. * Code under utils/ as suggested builds on cogl/ adding more convenient APIs or mechanism to optimize special cases. Broadly speaking you can compare cogl/ to OpenGL and utils/ to GLU. * clutter/pango will be moved to clutter/cogl/pango How some of the internal configure.ac/pkg-config terminology has changed: backendextra -> CLUTTER_WINSYS_BASE # e.g. "x11" backendextralib -> CLUTTER_WINSYS_BASE_LIB # e.g. "x11/libclutter-x11.la" clutterbackend -> {CLUTTER,COGL}_WINSYS # e.g. "glx" CLUTTER_FLAVOUR -> {CLUTTER,COGL}_WINSYS clutterbackendlib -> CLUTTER_WINSYS_LIB CLUTTER_COGL -> COGL_DRIVER # e.g. "gl" Note: The CLUTTER_FLAVOUR and CLUTTER_COGL defines are kept for apps As the first thing to take advantage of the new winsys component in Cogl; cogl_get_proc_address() has been moved from cogl/{gl,gles}/cogl.c into cogl/common/cogl.c and this common implementation first trys _cogl_winsys_get_proc_address() but if that fails then it falls back to gmodule.
2009-07-28 01:02:02 +00:00
}
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;
int i;
egl_extensions = eglQueryString (egl_renderer->edpy, EGL_EXTENSIONS);
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,
Dynamically load the GL or GLES library The GL or GLES library is now dynamically loaded by the CoglRenderer so that it can choose between GL, GLES1 and GLES2 at runtime. The library is loaded by the renderer because it needs to be done before calling eglInitialize. There is a new environment variable called COGL_DRIVER to choose between gl, gles1 or gles2. The #ifdefs for HAVE_COGL_GL, HAVE_COGL_GLES and HAVE_COGL_GLES2 have been changed so that they don't assume the ifdefs are mutually exclusive. They haven't been removed entirely so that it's possible to compile the GLES backends without the the enums from the GL headers. When using GLX the winsys additionally dynamically loads libGL because that also contains the GLX API. It can't be linked in directly because that would probably conflict with the GLES API if the EGL is selected. When compiling with EGL support the library links directly to libEGL because it doesn't contain any GL API so it shouldn't have any conflicts. When building for WGL or OSX Cogl still directly links against the GL API so there is a #define in config.h so that Cogl won't try to dlopen the library. Cogl-pango previously had a #ifdef to detect when the GL backend is used so that it can sneakily pass GL_QUADS to cogl_vertex_buffer_draw. This is now changed so that it queries the CoglContext for the backend. However to get this to work Cogl now needs to export the _cogl_context_get_default symbol and cogl-pango needs some extra -I flags to so that it can include cogl-context-private.h
2011-07-07 19:44:56 +00:00
"EGL", winsys_feature_data + i, 0, 0,
COGL_DRIVER_GL, /* the driver isn't used */
egl_extensions,
egl_renderer))
{
egl_renderer->private_features |=
winsys_feature_data[i].feature_flags_private;
}
}
CoglBool
_cogl_winsys_egl_renderer_connect_common (CoglRenderer *renderer,
GError **error)
{
CoglRendererEGL *egl_renderer = renderer->winsys;
if (!eglInitialize (egl_renderer->edpy,
&egl_renderer->egl_version_major,
&egl_renderer->egl_version_minor))
{
g_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,
GError **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,
CoglBool needs_stencil_override,
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);
attributes[i++] = EGL_STENCIL_SIZE;
attributes[i++] = needs_stencil_override ? 2 : 0;
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 ?
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);
}
static CoglBool
try_create_context (CoglDisplay *display,
CoglBool with_stencil_buffer,
GError **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;
Dynamically load the GL or GLES library The GL or GLES library is now dynamically loaded by the CoglRenderer so that it can choose between GL, GLES1 and GLES2 at runtime. The library is loaded by the renderer because it needs to be done before calling eglInitialize. There is a new environment variable called COGL_DRIVER to choose between gl, gles1 or gles2. The #ifdefs for HAVE_COGL_GL, HAVE_COGL_GLES and HAVE_COGL_GLES2 have been changed so that they don't assume the ifdefs are mutually exclusive. They haven't been removed entirely so that it's possible to compile the GLES backends without the the enums from the GL headers. When using GLX the winsys additionally dynamically loads libGL because that also contains the GLX API. It can't be linked in directly because that would probably conflict with the GLES API if the EGL is selected. When compiling with EGL support the library links directly to libEGL because it doesn't contain any GL API so it shouldn't have any conflicts. When building for WGL or OSX Cogl still directly links against the GL API so there is a #define in config.h so that Cogl won't try to dlopen the library. Cogl-pango previously had a #ifdef to detect when the GL backend is used so that it can sneakily pass GL_QUADS to cogl_vertex_buffer_draw. This is now changed so that it queries the CoglContext for the backend. However to get this to work Cogl now needs to export the _cogl_context_get_default symbol and cogl-pango needs some extra -I flags to so that it can include cogl-context-private.h
2011-07-07 19:44:56 +00:00
EGLint attribs[3];
EGLint cfg_attribs[MAX_EGL_CONFIG_ATTRIBS];
const char *error_message;
_COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context == NULL, TRUE);
Dynamically load the GL or GLES library The GL or GLES library is now dynamically loaded by the CoglRenderer so that it can choose between GL, GLES1 and GLES2 at runtime. The library is loaded by the renderer because it needs to be done before calling eglInitialize. There is a new environment variable called COGL_DRIVER to choose between gl, gles1 or gles2. The #ifdefs for HAVE_COGL_GL, HAVE_COGL_GLES and HAVE_COGL_GLES2 have been changed so that they don't assume the ifdefs are mutually exclusive. They haven't been removed entirely so that it's possible to compile the GLES backends without the the enums from the GL headers. When using GLX the winsys additionally dynamically loads libGL because that also contains the GLX API. It can't be linked in directly because that would probably conflict with the GLES API if the EGL is selected. When compiling with EGL support the library links directly to libEGL because it doesn't contain any GL API so it shouldn't have any conflicts. When building for WGL or OSX Cogl still directly links against the GL API so there is a #define in config.h so that Cogl won't try to dlopen the library. Cogl-pango previously had a #ifdef to detect when the GL backend is used so that it can sneakily pass GL_QUADS to cogl_vertex_buffer_draw. This is now changed so that it queries the CoglContext for the backend. However to get this to work Cogl now needs to export the _cogl_context_get_default symbol and cogl-pango needs some extra -I flags to so that it can include cogl-context-private.h
2011-07-07 19:44:56 +00:00
if (renderer->driver == COGL_DRIVER_GL)
Dynamically load the GL or GLES library The GL or GLES library is now dynamically loaded by the CoglRenderer so that it can choose between GL, GLES1 and GLES2 at runtime. The library is loaded by the renderer because it needs to be done before calling eglInitialize. There is a new environment variable called COGL_DRIVER to choose between gl, gles1 or gles2. The #ifdefs for HAVE_COGL_GL, HAVE_COGL_GLES and HAVE_COGL_GLES2 have been changed so that they don't assume the ifdefs are mutually exclusive. They haven't been removed entirely so that it's possible to compile the GLES backends without the the enums from the GL headers. When using GLX the winsys additionally dynamically loads libGL because that also contains the GLX API. It can't be linked in directly because that would probably conflict with the GLES API if the EGL is selected. When compiling with EGL support the library links directly to libEGL because it doesn't contain any GL API so it shouldn't have any conflicts. When building for WGL or OSX Cogl still directly links against the GL API so there is a #define in config.h so that Cogl won't try to dlopen the library. Cogl-pango previously had a #ifdef to detect when the GL backend is used so that it can sneakily pass GL_QUADS to cogl_vertex_buffer_draw. This is now changed so that it queries the CoglContext for the backend. However to get this to work Cogl now needs to export the _cogl_context_get_default symbol and cogl-pango needs some extra -I flags to so that it can include cogl-context-private.h
2011-07-07 19:44:56 +00:00
eglBindAPI (EGL_OPENGL_API);
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_attributes_from_framebuffer_config (display,
&display->onscreen_template->config,
with_stencil_buffer,
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;
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:
g_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_CREATE_CONTEXT,
"%s", error_message);
return FALSE;
}
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
create_context (CoglDisplay *display, GError **error)
{
CoglDisplayEGL *egl_display = display->winsys;
/* Note: we don't just rely on eglChooseConfig to correctly
* report that the driver doesn't support a stencil buffer
* because we've seen PVR drivers that claim stencil buffer
* support according to the EGLConfig but then later fail
* when trying to create a context with such a config.
*/
if (try_create_context (display, TRUE, error))
{
egl_display->stencil_disabled = FALSE;
return TRUE;
}
else
{
g_clear_error (error);
cleanup_context (display);
egl_display->stencil_disabled = TRUE;
return try_create_context (display, FALSE, error);
}
}
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,
GError **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;
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 (!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, GError **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);
}
/* 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)
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, GError **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)
{
g_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,
GError **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;
CoglBool need_stencil =
egl_display->stencil_disabled ? FALSE : framebuffer->config.need_stencil;
_COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE);
egl_attributes_from_framebuffer_config (display,
&framebuffer->config,
need_stencil,
attributes);
status = eglChooseConfig (egl_renderer->edpy,
attributes,
&egl_config, 1,
&config_count);
if (status != EGL_TRUE || config_count == 0)
{
g_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;
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)
{
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 (CoglOnscreen *onscreen)
{
CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen);
CoglContext *context = fb->context;
CoglDisplayEGL *egl_display = context->display->winsys;
CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
CoglBool status = _cogl_winsys_egl_make_current (context->display,
egl_onscreen->egl_surface,
egl_onscreen->egl_surface,
egl_display->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 void
_cogl_winsys_onscreen_bind (CoglOnscreen *onscreen)
{
bind_onscreen (onscreen);
}
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 (CoglOnscreen *onscreen)
{
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);
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 EGLDisplay
_cogl_winsys_context_egl_get_egl_display (CoglContext *context)
{
CoglRendererEGL *egl_renderer = context->display->renderer->winsys;
return egl_renderer->edpy;
}
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, GError **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 (COGL_ONSCREEN (gles2_ctx->write_buffer));
}
else
status = _cogl_winsys_egl_make_current (ctx->display,
egl_display->dummy_surface,
egl_display->dummy_surface,
gles2_ctx->winsys);
if (!status)
{
g_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);
}
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_egl_get_egl_display =
_cogl_winsys_context_egl_get_egl_display,
.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 = _cogl_winsys_onscreen_swap_buffers,
.onscreen_swap_region = _cogl_winsys_onscreen_swap_region,
.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,
};
/* 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