mutter/clutter/eglx/clutter-stage-egl.c
Robert Bragg fe01ec6323 [stage-egl] support fallbacks in clutter_stage_egl_realize
Most of clutter_stage_egl_realize was renamed to
_clutter_stage_egl_try_realize which now takes a cookie indicating which
fallback number should tried next.  clutter_stage_egl_realize now keeps
trying to realize with successive fallback numbers until it succeeds or runs
out of fallbacks.

The only fallback supported for now is for hardware with no stencil buffer
support.
2009-10-16 18:58:49 +01:00

327 lines
9.7 KiB
C

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "clutter-backend-egl.h"
#include "clutter-stage-egl.h"
#include "clutter-eglx.h"
#include "../clutter-main.h"
#include "../clutter-feature.h"
#include "../clutter-color.h"
#include "../clutter-util.h"
#include "../clutter-event.h"
#include "../clutter-enum-types.h"
#include "../clutter-private.h"
#include "../clutter-debug.h"
#include "../clutter-units.h"
#include "../clutter-container.h"
#include "../clutter-stage.h"
#include "../clutter-stage-window.h"
static ClutterStageWindowIface *clutter_stage_egl_parent_iface = NULL;
static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);
G_DEFINE_TYPE_WITH_CODE (ClutterStageEGL,
clutter_stage_egl,
CLUTTER_TYPE_STAGE_X11,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
clutter_stage_window_iface_init));
static void
clutter_stage_egl_unrealize (ClutterStageWindow *stage_window)
{
ClutterBackend *backend = clutter_get_default_backend ();
ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (stage_window);
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
CLUTTER_NOTE (BACKEND, "Unrealizing stage");
clutter_x11_trap_x_errors ();
if (!stage_x11->is_foreign_xwin && stage_x11->xwin != None)
{
XDestroyWindow (backend_x11->xdpy, stage_x11->xwin);
stage_x11->xwin = None;
}
else
stage_x11->xwin = None;
if (stage_egl->egl_surface)
{
eglDestroySurface (clutter_eglx_display (), stage_egl->egl_surface);
stage_egl->egl_surface = EGL_NO_SURFACE;
}
XSync (backend_x11->xdpy, False);
clutter_x11_untrap_x_errors ();
}
static gboolean
_clutter_stage_egl_try_realize (ClutterStageWindow *stage_window, int *retry_cookie)
{
ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (stage_window);
ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window);
ClutterBackend *backend;
ClutterBackendEGL *backend_egl;
ClutterBackendX11 *backend_x11;
EGLConfig config;
EGLint config_count;
EGLBoolean status;
int i;
int num_configs;
EGLConfig *all_configs;
EGLint cfg_attribs[] = {
/* NB: This must be the first attribute, since we may
* try and fallback to no stencil buffer */
EGL_STENCIL_SIZE, 8,
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
EGL_BUFFER_SIZE, EGL_DONT_CARE,
#ifdef HAVE_COGL_GLES2
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
#else /* HAVE_COGL_GLES2 */
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
#endif /* HAVE_COGL_GLES2 */
EGL_NONE
};
EGLDisplay edpy;
/* Here we can change the attributes depending on the fallback count... */
/* Some GLES hardware can't support a stencil buffer: */
if (*retry_cookie == 1)
{
g_warning ("Trying with stencil buffer disabled...");
cfg_attribs[1 /* EGL_STENCIL_SIZE */] = 0;
}
/* XXX: at this point we only have one fallback */
backend = clutter_get_default_backend ();
backend_egl = CLUTTER_BACKEND_EGL (backend);
backend_x11 = CLUTTER_BACKEND_X11 (backend);
edpy = clutter_eglx_display ();
eglGetConfigs (edpy, NULL, 0, &num_configs);
all_configs = g_malloc (num_configs * sizeof (EGLConfig));
eglGetConfigs (clutter_eglx_display (),
all_configs,
num_configs,
&num_configs);
for (i = 0; i < num_configs; ++i)
{
EGLint red = -1, green = -1, blue = -1, alpha = -1, stencil = -1;
eglGetConfigAttrib (edpy,
all_configs[i],
EGL_RED_SIZE, &red);
eglGetConfigAttrib (edpy,
all_configs[i],
EGL_GREEN_SIZE, &green);
eglGetConfigAttrib (edpy,
all_configs[i],
EGL_BLUE_SIZE, &blue);
eglGetConfigAttrib (edpy,
all_configs[i],
EGL_ALPHA_SIZE, &alpha);
eglGetConfigAttrib (edpy,
all_configs[i],
EGL_STENCIL_SIZE, &stencil);
CLUTTER_NOTE (BACKEND, "EGLConfig == R:%d G:%d B:%d A:%d S:%d \n",
red, green, blue, alpha, stencil);
}
g_free (all_configs);
status = eglChooseConfig (edpy,
cfg_attribs,
&config, 1,
&config_count);
if (status != EGL_TRUE)
{
g_warning ("eglChooseConfig failed");
goto fail;
}
if (stage_x11->xwin == None)
stage_x11->xwin =
XCreateSimpleWindow (backend_x11->xdpy,
backend_x11->xwin_root,
0, 0,
stage_x11->xwin_width,
stage_x11->xwin_height,
0, 0,
WhitePixel (backend_x11->xdpy,
backend_x11->xscreen_num));
if (clutter_x11_has_event_retrieval ())
{
if (clutter_x11_has_xinput ())
{
XSelectInput (backend_x11->xdpy, stage_x11->xwin,
StructureNotifyMask |
FocusChangeMask |
ExposureMask |
EnterWindowMask | LeaveWindowMask |
PropertyChangeMask);
#ifdef USE_XINPUT
_clutter_x11_select_events (stage_x11->xwin);
#endif
}
else
XSelectInput (backend_x11->xdpy, stage_x11->xwin,
StructureNotifyMask |
FocusChangeMask |
ExposureMask |
PointerMotionMask |
KeyPressMask | KeyReleaseMask |
ButtonPressMask | ButtonReleaseMask |
EnterWindowMask | LeaveWindowMask |
PropertyChangeMask);
}
clutter_stage_x11_fix_window_size (stage_x11, -1, -1);
clutter_stage_x11_set_wm_protocols (stage_x11);
if (stage_egl->egl_surface != EGL_NO_SURFACE)
{
eglDestroySurface (edpy, stage_egl->egl_surface);
stage_egl->egl_surface = EGL_NO_SURFACE;
}
stage_egl->egl_surface =
eglCreateWindowSurface (edpy,
config,
(NativeWindowType) stage_x11->xwin,
NULL);
if (stage_egl->egl_surface == EGL_NO_SURFACE)
{
g_warning ("Unable to create an EGL surface");
goto fail;
}
if (G_UNLIKELY (backend_egl->egl_context == None))
{
#ifdef HAVE_COGL_GLES2
static const EGLint attribs[3]
= { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
backend_egl->egl_context = eglCreateContext (edpy,
config,
EGL_NO_CONTEXT,
attribs);
#else
/* Seems some GLES implementations 1.x do not like attribs... */
backend_egl->egl_context = eglCreateContext (edpy,
config,
EGL_NO_CONTEXT,
NULL);
#endif
if (backend_egl->egl_context == EGL_NO_CONTEXT)
{
g_warning ("Unable to create a suitable EGL context");
goto fail;
}
backend_egl->egl_config = config;
CLUTTER_NOTE (GL, "Created EGL Context");
}
*retry_cookie = 0;
return TRUE;
fail:
if (stage_egl->egl_surface != EGL_NO_SURFACE)
{
eglDestroySurface (backend_egl->edpy, stage_egl->egl_surface);
stage_egl->egl_surface = EGL_NO_SURFACE;
}
if (stage_x11->xwin != None)
{
XDestroyWindow (backend_x11->xdpy, stage_x11->xwin);
stage_x11->xwin = None;
}
/* NB: We currently only support a single fallback option */
if (*retry_cookie == 0)
*retry_cookie = 1; /* tell the caller to try again */
else
*retry_cookie = 0; /* tell caller not to try again! */
return FALSE;
}
static gboolean
clutter_stage_egl_realize (ClutterStageWindow *stage_window)
{
int retry_cookie = 0;
CLUTTER_NOTE (BACKEND, "Realizing main stage");
while (1)
{
/* _clutter_stage_egl_try_realize supports fallbacks, and the number of
* fallbacks already tried is tracked in the retry_cookie, so what we are
* doing here is re-trying until we get told there are no more fallback
* options... */
if (_clutter_stage_egl_try_realize (stage_window, &retry_cookie))
{
gboolean ret = clutter_stage_egl_parent_iface->realize (stage_window);
if (G_LIKELY (ret))
CLUTTER_NOTE (BACKEND, "Successfully realized stage");
return ret;
}
if (retry_cookie == 0)
return FALSE; /* we've been told not to try again! */
g_warning ("%s: Trying fallback", G_STRFUNC);
}
g_return_val_if_reached (FALSE);
}
static void
clutter_stage_egl_dispose (GObject *gobject)
{
G_OBJECT_CLASS (clutter_stage_egl_parent_class)->dispose (gobject);
}
static void
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
{
clutter_stage_egl_parent_iface = g_type_interface_peek_parent (iface);
iface->realize = clutter_stage_egl_realize;
iface->unrealize = clutter_stage_egl_unrealize;
/* the rest is inherited from ClutterStageX11 */
}
static void
clutter_stage_egl_class_init (ClutterStageEGLClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = clutter_stage_egl_dispose;
}
static void
clutter_stage_egl_init (ClutterStageEGL *stage)
{
}