diff --git a/cogl/cogl-framebuffer-private.h b/cogl/cogl-framebuffer-private.h index 27a91e3df..8d4bbc3d1 100644 --- a/cogl/cogl-framebuffer-private.h +++ b/cogl/cogl-framebuffer-private.h @@ -47,11 +47,21 @@ typedef enum _CoglFramebufferType { COGL_FRAMEBUFFER_TYPE_OFFSCREEN } CoglFramebufferType; +typedef struct +{ + CoglSwapChain *swap_chain; + gboolean need_stencil; +} CoglFramebufferConfig; + struct _CoglFramebuffer { CoglObject _parent; CoglContext *context; CoglFramebufferType type; + + /* The user configuration before allocation... */ + CoglFramebufferConfig config; + int width; int height; /* Format of the pixels in the framebuffer (including the expected @@ -108,7 +118,8 @@ typedef struct _CoglOffscreen CoglFramebuffer _parent; GLuint fbo_handle; GSList *renderbuffers; - CoglTexture *texture; + + CoglTexture *texture; } CoglOffscreen; /* Flags to pass to _cogl_offscreen_new_to_texture_full */ diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c index 7a7d50a2b..f3a03da46 100644 --- a/cogl/cogl-framebuffer.c +++ b/cogl/cogl-framebuffer.c @@ -36,6 +36,7 @@ #include "cogl-util.h" #include "cogl-texture-private.h" #include "cogl-framebuffer-private.h" +#include "cogl-onscreen-template-private.h" #include "cogl-clip-stack.h" #include "cogl-journal-private.h" #include "cogl-winsys-private.h" @@ -957,6 +958,16 @@ _cogl_offscreen_free (CoglOffscreen *offscreen) g_free (offscreen); } +static void +_cogl_onscreen_init_from_template (CoglOnscreen *onscreen, + CoglOnscreenTemplate *onscreen_template) +{ + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + + framebuffer->config = onscreen_template->config; + cogl_object_ref (framebuffer->config.swap_chain); +} + /* XXX: While we still have backend in Clutter we need a dummy object * to represent the CoglOnscreen framebuffer that the backend * creates... */ @@ -976,6 +987,8 @@ _cogl_onscreen_new (void) /* NB: make sure to pass positive width/height numbers here * because otherwise we'll hit input validation assertions!*/ + _cogl_onscreen_init_from_template (onscreen, ctx->display->onscreen_template); + COGL_FRAMEBUFFER (onscreen)->allocated = TRUE; /* XXX: Note we don't initialize onscreen->winsys in this case. */ @@ -1007,6 +1020,9 @@ cogl_onscreen_new (CoglContext *ctx, int width, int height) width, /* width */ height); /* height */ + _cogl_onscreen_init_from_template (onscreen, ctx->display->onscreen_template); + + /* FIXME: This should be configurable via the template too */ onscreen->swap_throttled = TRUE; return _cogl_onscreen_object_new (onscreen); diff --git a/cogl/cogl-onscreen-template-private.h b/cogl/cogl-onscreen-template-private.h index 0f8d2533b..212787978 100644 --- a/cogl/cogl-onscreen-template-private.h +++ b/cogl/cogl-onscreen-template-private.h @@ -31,7 +31,7 @@ struct _CoglOnscreenTemplate { CoglObject _parent; - CoglSwapChain *swap_chain; + CoglFramebufferConfig config; }; #endif /* __COGL_ONSCREEN_TEMPLATE_PRIVATE_H */ diff --git a/cogl/cogl-onscreen-template.c b/cogl/cogl-onscreen-template.c index ff400e4e6..b9fbd2356 100644 --- a/cogl/cogl-onscreen-template.c +++ b/cogl/cogl-onscreen-template.c @@ -31,8 +31,11 @@ #include "cogl.h" #include "cogl-object.h" +#include "cogl-framebuffer-private.h" #include "cogl-onscreen-template-private.h" +#include + static void _cogl_onscreen_template_free (CoglOnscreenTemplate *onscreen_template); COGL_OBJECT_DEFINE (OnscreenTemplate, onscreen_template); @@ -53,10 +56,15 @@ CoglOnscreenTemplate * cogl_onscreen_template_new (CoglSwapChain *swap_chain) { CoglOnscreenTemplate *onscreen_template = g_slice_new0 (CoglOnscreenTemplate); + char *user_config; - onscreen_template->swap_chain = swap_chain; + onscreen_template->config.swap_chain = swap_chain; if (swap_chain) cogl_object_ref (swap_chain); + else + onscreen_template->config.swap_chain = cogl_swap_chain_new (); + + onscreen_template->config.need_stencil = TRUE; return _cogl_onscreen_template_object_new (onscreen_template); } diff --git a/cogl/winsys/cogl-winsys-egl.c b/cogl/winsys/cogl-winsys-egl.c index c4e8170e8..6b40fc387 100644 --- a/cogl/winsys/cogl-winsys-egl.c +++ b/cogl/winsys/cogl-winsys-egl.c @@ -72,6 +72,8 @@ #define COGL_ONSCREEN_X11_EVENT_MASK StructureNotifyMask #endif +#define MAX_EGL_CONFIG_ATTRIBS 30 + typedef enum _CoglEGLWinsysFeature { COGL_EGL_WINSYS_FEATURE_SWAP_REGION =1L<<0, @@ -143,6 +145,7 @@ typedef struct _CoglDisplayEGL EGLConfig egl_config; gboolean found_egl_config; + gboolean stencil_disabled; } CoglDisplayEGL; typedef struct _CoglContextEGL @@ -596,10 +599,59 @@ get_visual_info (CoglDisplay *display, EGLConfig egl_config) } #endif +static void +egl_attributes_from_framebuffer_config (CoglDisplay *display, + CoglFramebufferConfig *config, + gboolean needs_stencil_override, + EGLint *attributes) +{ + int i = 0; + + 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; + + /* XXX: Why does the GDL platform choose these by default? */ +#ifdef COGL_HAS_EGL_PLATFORM_GDL_SUPPORT + attributes[i++] = EGL_BIND_TO_TEXTURE_RGBA; + attributes[i++] = EGL_TRUE; + attributes[i++] = EGL_BIND_TO_TEXTURE_RGB; + attributes[i++] = EGL_TRUE; +#endif + + attributes[i++] = EGL_BUFFER_SIZE; + attributes[i++] = EGL_DONT_CARE; + + attributes[i++] = EGL_RENDERABLE_TYPE; + attributes[i++] = (display->renderer->driver == COGL_DRIVER_GL ? + EGL_OPENGL_BIT : + display->renderer->driver == COGL_DRIVER_GLES1 ? + EGL_OPENGL_ES_BIT : + EGL_OPENGL_ES2_BIT); + + attributes[i++] = EGL_SURFACE_TYPE; + attributes[i++] = EGL_WINDOW_BIT; + + attributes[i++] = EGL_NONE; + + g_assert (i < MAX_EGL_CONFIG_ATTRIBS); +} + static gboolean try_create_context (CoglDisplay *display, - int retry_cookie, - gboolean *try_fallback, + gboolean with_stencil_buffer, GError **error) { CoglDisplayEGL *egl_display = display->winsys; @@ -612,38 +664,8 @@ try_create_context (CoglDisplay *display, EGLConfig config; EGLint config_count = 0; EGLBoolean status; - EGLint cfg_attribs[] = { - /* NB: This must be the first attribute, since we may - * try and fallback to no stencil buffer */ - EGL_STENCIL_SIZE, 2, - - EGL_RED_SIZE, 1, - EGL_GREEN_SIZE, 1, - EGL_BLUE_SIZE, 1, - EGL_ALPHA_SIZE, EGL_DONT_CARE, - - EGL_DEPTH_SIZE, 1, - - /* XXX: Why does the GDL platform choose these by default? */ -#ifdef COGL_HAS_EGL_PLATFORM_GDL_SUPPORT - EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE, - EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE, -#endif - - EGL_BUFFER_SIZE, EGL_DONT_CARE, - - EGL_RENDERABLE_TYPE, (display->renderer->driver == COGL_DRIVER_GL ? - EGL_OPENGL_BIT : - display->renderer->driver == COGL_DRIVER_GLES1 ? - EGL_OPENGL_ES_BIT : - EGL_OPENGL_ES2_BIT), - - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - - EGL_NONE - }; + EGLint cfg_attribs[MAX_EGL_CONFIG_ATTRIBS]; EGLint attribs[3]; - #ifdef COGL_HAS_EGL_PLATFORM_POWERVR_X11_SUPPORT XVisualInfo *xvisinfo; XSetWindowAttributes attrs; @@ -653,28 +675,18 @@ try_create_context (CoglDisplay *display, #endif const char *error_message; - 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); + + g_return_val_if_fail (egl_display->egl_context == NULL, TRUE); edpy = egl_renderer->edpy; if (display->renderer->driver == COGL_DRIVER_GL) eglBindAPI (EGL_OPENGL_API); - /* 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 */ - status = eglChooseConfig (edpy, cfg_attribs, &config, 1, @@ -687,6 +699,15 @@ try_create_context (CoglDisplay *display, egl_display->egl_config = config; + 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, @@ -919,20 +940,10 @@ try_create_context (CoglDisplay *display, fail: - /* Currently we only have one fallback path... */ - if (retry_cookie == 0) - { - *try_fallback = TRUE; - return FALSE; - } - else - { - *try_fallback = FALSE; - g_set_error (error, COGL_WINSYS_ERROR, - COGL_WINSYS_ERROR_CREATE_CONTEXT, - "%s", error_message); - return FALSE; - } + g_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "%s", error_message); + return FALSE; } static void @@ -997,35 +1008,25 @@ static gboolean create_context (CoglDisplay *display, GError **error) { CoglDisplayEGL *egl_display = display->winsys; - gboolean support_transparent_windows; - int retry_cookie = 0; - gboolean status; - gboolean try_fallback; - GError *try_error = NULL; - g_return_val_if_fail (egl_display->egl_context == NULL, TRUE); - - if (display->onscreen_template->swap_chain && - display->onscreen_template->swap_chain->has_alpha) - support_transparent_windows = TRUE; + /* 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 - support_transparent_windows = FALSE; - - retry_cookie = 0; - while (!(status = try_create_context (display, - retry_cookie, - &try_fallback, - &try_error)) && - try_fallback) { g_clear_error (error); cleanup_context (display); - retry_cookie++; + egl_display->stencil_disabled = TRUE; + return try_create_context (display, FALSE, error); } - if (!status) - g_propagate_error (error, try_error); - - return status; } static void @@ -1212,9 +1213,32 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen, #ifdef COGL_HAS_EGL_PLATFORM_WAYLAND_SUPPORT struct wl_visual *wayland_visual; #endif + EGLint attributes[MAX_EGL_CONFIG_ATTRIBS]; + EGLConfig egl_config; + EGLint config_count = 0; + EGLBoolean status; + gboolean need_stencil = + egl_display->stencil_disabled ? FALSE : framebuffer->config.need_stencil; g_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; + } + #ifdef COGL_HAS_EGL_PLATFORM_POWERVR_X11_SUPPORT /* FIXME: We need to explicitly Select for ConfigureNotify events. @@ -1274,7 +1298,7 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen, _cogl_xlib_renderer_trap_errors (display->renderer, &state); - xvisinfo = get_visual_info (display, egl_display->egl_config); + xvisinfo = get_visual_info (display, egl_config); if (xvisinfo == NULL) { g_set_error (error, COGL_WINSYS_ERROR, @@ -1336,7 +1360,7 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen, egl_onscreen->egl_surface = eglCreateWindowSurface (egl_renderer->edpy, - egl_display->egl_config, + egl_config, (NativeWindowType) xlib_onscreen->xwin, NULL); #elif defined (COGL_HAS_EGL_PLATFORM_WAYLAND_SUPPORT) @@ -1369,7 +1393,7 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen, egl_onscreen->egl_surface = eglCreateWindowSurface (egl_renderer->edpy, - egl_display->egl_config, + egl_config, (EGLNativeWindowType) egl_onscreen->wayland_egl_native_window, NULL); diff --git a/cogl/winsys/cogl-winsys-glx.c b/cogl/winsys/cogl-winsys-glx.c index c5558f217..1a42605e1 100644 --- a/cogl/winsys/cogl-winsys-glx.c +++ b/cogl/winsys/cogl-winsys-glx.c @@ -44,6 +44,8 @@ #include "cogl-texture-2d-private.h" #include "cogl-texture-rectangle-private.h" #include "cogl-pipeline-opengl-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-swap-chain-private.h" #include #include @@ -63,6 +65,7 @@ #endif #define COGL_ONSCREEN_X11_EVENT_MASK StructureNotifyMask +#define MAX_GLX_CONFIG_ATTRIBS 30 typedef struct _CoglContextGLX { @@ -452,34 +455,60 @@ update_winsys_features (CoglContext *context, GError **error) return TRUE; } +static void +glx_attributes_from_framebuffer_config (CoglDisplay *display, + CoglFramebufferConfig *config, + int *attributes) +{ + CoglGLXRenderer *glx_renderer = display->renderer->winsys; + int i = 0; + + attributes[i++] = GLX_DRAWABLE_TYPE; + attributes[i++] = GLX_WINDOW_BIT; + + attributes[i++] = GLX_RENDER_TYPE; + attributes[i++] = GLX_RGBA_BIT; + + attributes[i++] = GLX_DOUBLEBUFFER; + attributes[i++] = GL_TRUE; + + attributes[i++] = GLX_RED_SIZE; + attributes[i++] = 1; + attributes[i++] = GLX_GREEN_SIZE; + attributes[i++] = 1; + attributes[i++] = GLX_BLUE_SIZE; + attributes[i++] = 1; + attributes[i++] = GLX_ALPHA_SIZE; + attributes[i++] = config->swap_chain->has_alpha ? 1 : GLX_DONT_CARE; + attributes[i++] = GLX_DEPTH_SIZE; + attributes[i++] = 1; + attributes[i++] = GLX_STENCIL_SIZE; + attributes[i++] = config->need_stencil ? 1: GLX_DONT_CARE; + + attributes[i++] = None; + + g_assert (i < MAX_GLX_CONFIG_ATTRIBS); +} + /* It seems the GLX spec never defined an invalid GLXFBConfig that * we could overload as an indication of error, so we have to return * an explicit boolean status. */ static gboolean find_fbconfig (CoglDisplay *display, - gboolean with_alpha, + CoglFramebufferConfig *config, GLXFBConfig *config_ret, GError **error) { CoglXlibRenderer *xlib_renderer = display->renderer->winsys; CoglGLXRenderer *glx_renderer = display->renderer->winsys; GLXFBConfig *configs = NULL; - int n_configs, i; - static const int attributes[] = { - GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, - GLX_RENDER_TYPE, GLX_RGBA_BIT, - GLX_DOUBLEBUFFER, GL_TRUE, - GLX_RED_SIZE, 1, - GLX_GREEN_SIZE, 1, - GLX_BLUE_SIZE, 1, - GLX_ALPHA_SIZE, 1, - GLX_DEPTH_SIZE, 1, - GLX_STENCIL_SIZE, 1, - None - }; + int n_configs; + static int attributes[MAX_GLX_CONFIG_ATTRIBS]; gboolean ret = TRUE; int xscreen_num = DefaultScreen (xlib_renderer->xdpy); + glx_attributes_from_framebuffer_config (display, config, attributes); + configs = glx_renderer->glXChooseFBConfig (xlib_renderer->xdpy, xscreen_num, attributes, @@ -493,8 +522,10 @@ find_fbconfig (CoglDisplay *display, goto done; } - if (with_alpha) + if (config->swap_chain->has_alpha) { + int i; + for (i = 0; i < n_configs; i++) { XVisualInfo *vinfo; @@ -540,7 +571,8 @@ create_context (CoglDisplay *display, GError **error) CoglXlibDisplay *xlib_display = display->winsys; CoglXlibRenderer *xlib_renderer = display->renderer->winsys; CoglGLXRenderer *glx_renderer = display->renderer->winsys; - gboolean support_transparent_windows; + gboolean support_transparent_windows = + display->onscreen_template->config.swap_chain->has_alpha; GLXFBConfig config; GError *fbconfig_error = NULL; XSetWindowAttributes attrs; @@ -550,14 +582,8 @@ create_context (CoglDisplay *display, GError **error) g_return_val_if_fail (glx_display->glx_context == NULL, TRUE); - if (display->onscreen_template->swap_chain && - display->onscreen_template->swap_chain->has_alpha) - support_transparent_windows = TRUE; - else - support_transparent_windows = FALSE; - glx_display->found_fbconfig = - find_fbconfig (display, support_transparent_windows, &config, + find_fbconfig (display, &display->onscreen_template->config, &config, &fbconfig_error); if (!glx_display->found_fbconfig) { @@ -762,9 +788,23 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen, Window xwin; CoglOnscreenXlib *xlib_onscreen; CoglOnscreenGLX *glx_onscreen; + GLXFBConfig fbconfig; + GError *fbconfig_error = NULL; g_return_val_if_fail (glx_display->glx_context, FALSE); + if (!find_fbconfig (display, &framebuffer->config, + &fbconfig, + &fbconfig_error)) + { + g_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "Unable to find suitable fbconfig for the GLX context: %s", + fbconfig_error->message); + g_error_free (fbconfig_error); + return FALSE; + } + /* FIXME: We need to explicitly Select for ConfigureNotify events. * For foreign windows we need to be careful not to mess up any * existing event mask. @@ -823,7 +863,7 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen, _cogl_xlib_renderer_trap_errors (display->renderer, &state); xvisinfo = glx_renderer->glXGetVisualFromFBConfig (xlib_renderer->xdpy, - glx_display->fbconfig); + fbconfig); if (xvisinfo == NULL) { g_set_error (error, COGL_WINSYS_ERROR, @@ -887,7 +927,7 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen, { glx_onscreen->glxwin = glx_renderer->glXCreateWindow (xlib_renderer->xdpy, - glx_display->fbconfig, + fbconfig, xlib_onscreen->xwin, NULL); } diff --git a/cogl/winsys/cogl-winsys-wgl.c b/cogl/winsys/cogl-winsys-wgl.c index f3bc8ba6a..8306f2784 100644 --- a/cogl/winsys/cogl-winsys-wgl.c +++ b/cogl/winsys/cogl-winsys-wgl.c @@ -316,7 +316,8 @@ pixel_format_is_better (const PIXELFORMATDESCRIPTOR *pfa, } static int -choose_pixel_format (HDC dc, PIXELFORMATDESCRIPTOR *pfd) +choose_pixel_format (CoglFramebufferConfig *config, + HDC dc, PIXELFORMATDESCRIPTOR *pfd) { int i, num_formats, best_pf = 0; PIXELFORMATDESCRIPTOR best_pfd; @@ -341,6 +342,11 @@ choose_pixel_format (HDC dc, PIXELFORMATDESCRIPTOR *pfd) already found */ (best_pf == 0 || pixel_format_is_better (&best_pfd, pfd))) { + if (config->swap_chain->has_alpha && pfd->cAlphaBits == 0) + continue; + if (config->need_stencil && pfd->cStencilBits == 0) + continue; + best_pf = i; best_pfd = *pfd; } @@ -445,7 +451,8 @@ create_context (CoglDisplay *display, GError **error) wgl_display->dummy_dc = GetDC (wgl_display->dummy_hwnd); - pf = choose_pixel_format (wgl_display->dummy_dc, &pfd); + pf = choose_pixel_format (&display->onscreen_template->config, + wgl_display->dummy_dc, &pfd); if (pf == 0 || !SetPixelFormat (wgl_display->dummy_dc, pf, &pfd)) { @@ -784,9 +791,10 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen, wgl_onscreen->client_dc = GetDC (hwnd); /* Use the same pixel format as the dummy DC from the renderer */ - pf = GetPixelFormat (wgl_display->dummy_dc); - DescribePixelFormat (wgl_display->dummy_dc, pf, sizeof (pfd), &pfd); - if (!SetPixelFormat (wgl_onscreen->client_dc, pf, &pfd)) + pf = choose_pixel_format (&framebuffer->config, + wgl_onscreen->client_dc, &pfd); + + if (pf == 0 || !SetPixelFormat (wgl_onscreen->client_dc, pf, &pfd)) { g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_ONSCREEN,