cogl: Support multiple fallbacks in cogl_offscreen_new_to_texture()
The Intel drivers in Mesa 7.6 (and possibly earlier versions) don't support creating FBOs with a stencil buffer but without a depth buffer. This reworks framebuffer allocation so that we try a number of fallback options before failing. The options we try in order are: - the same options that were sucessful last time if available - combined depth and stencil - separate depth and stencil - just stencil, no depth - just depth, no stencil - neither depth or stencil
This commit is contained in:
parent
05ce533fc8
commit
9690913fef
2 changed files with 158 additions and 86 deletions
|
@ -56,7 +56,7 @@ typedef struct _CoglOffscreen
|
|||
{
|
||||
CoglFramebuffer _parent;
|
||||
GLuint fbo_handle;
|
||||
GLuint gl_stencil_handle;
|
||||
GSList *renderbuffers;
|
||||
CoglHandle texture;
|
||||
} CoglOffscreen;
|
||||
|
||||
|
|
|
@ -71,6 +71,15 @@
|
|||
#ifndef GL_STENCIL_INDEX8
|
||||
#define GL_STENCIL_INDEX8 0x8D48
|
||||
#endif
|
||||
#ifndef GL_DEPTH_STENCIL
|
||||
#define GL_DEPTH_STENCIL 0x84F9
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
_TRY_DEPTH_STENCIL = 1L<<0,
|
||||
_TRY_DEPTH = 1L<<1,
|
||||
_TRY_STENCIL = 1L<<2
|
||||
} TryFBOFlags;
|
||||
|
||||
static void _cogl_framebuffer_free (CoglFramebuffer *framebuffer);
|
||||
static void _cogl_onscreen_free (CoglOnscreen *onscreen);
|
||||
|
@ -232,17 +241,123 @@ _cogl_framebuffer_get_projection_stack (CoglHandle handle)
|
|||
return framebuffer->projection_stack;
|
||||
}
|
||||
|
||||
static TryFBOFlags
|
||||
try_creating_fbo (CoglOffscreen *offscreen,
|
||||
TryFBOFlags flags,
|
||||
CoglHandle texture)
|
||||
{
|
||||
GLuint gl_depth_stencil_handle;
|
||||
GLuint gl_depth_handle;
|
||||
GLuint gl_stencil_handle;
|
||||
GLuint tex_gl_handle;
|
||||
GLenum tex_gl_target;
|
||||
GLuint fbo_gl_handle;
|
||||
GLenum status;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, FALSE);
|
||||
|
||||
if (!cogl_texture_get_gl_texture (texture, &tex_gl_handle, &tex_gl_target))
|
||||
return 0;
|
||||
|
||||
if (tex_gl_target != GL_TEXTURE_2D
|
||||
#ifdef HAVE_COGL_GL
|
||||
&& tex_gl_target != GL_TEXTURE_RECTANGLE_ARB
|
||||
#endif
|
||||
)
|
||||
return 0;
|
||||
|
||||
/* We are about to generate and bind a new fbo, so when next flushing the
|
||||
* journal, we will need to rebind the current framebuffer... */
|
||||
ctx->dirty_bound_framebuffer = 1;
|
||||
|
||||
/* Generate framebuffer */
|
||||
glGenFramebuffers (1, &fbo_gl_handle);
|
||||
GE (glBindFramebuffer (GL_FRAMEBUFFER, fbo_gl_handle));
|
||||
offscreen->fbo_handle = fbo_gl_handle;
|
||||
|
||||
GE (glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
tex_gl_target, tex_gl_handle, 0));
|
||||
|
||||
if (flags & _TRY_DEPTH_STENCIL)
|
||||
{
|
||||
/* Create a renderbuffer for depth and stenciling */
|
||||
GE (glGenRenderbuffers (1, &gl_depth_stencil_handle));
|
||||
GE (glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_stencil_handle));
|
||||
GE (glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_STENCIL,
|
||||
cogl_texture_get_width (texture),
|
||||
cogl_texture_get_height (texture)));
|
||||
GE (glBindRenderbuffer (GL_RENDERBUFFER, 0));
|
||||
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
|
||||
GL_STENCIL_ATTACHMENT,
|
||||
GL_RENDERBUFFER, gl_depth_stencil_handle));
|
||||
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
|
||||
GL_DEPTH_ATTACHMENT,
|
||||
GL_RENDERBUFFER, gl_depth_stencil_handle));
|
||||
offscreen->renderbuffers =
|
||||
g_slist_prepend (offscreen->renderbuffers,
|
||||
GUINT_TO_POINTER (gl_depth_stencil_handle));
|
||||
}
|
||||
|
||||
if (flags & _TRY_DEPTH)
|
||||
{
|
||||
GE (glGenRenderbuffers (1, &gl_depth_handle));
|
||||
GE (glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_handle));
|
||||
/* For now we just ask for GL_DEPTH_COMPONENT16 since this is all that's
|
||||
* available under GLES */
|
||||
GE (glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
|
||||
cogl_texture_get_width (texture),
|
||||
cogl_texture_get_height (texture)));
|
||||
GE (glBindRenderbuffer (GL_RENDERBUFFER, 0));
|
||||
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
|
||||
GL_DEPTH_ATTACHMENT,
|
||||
GL_RENDERBUFFER, gl_depth_handle));
|
||||
offscreen->renderbuffers =
|
||||
g_slist_prepend (offscreen->renderbuffers,
|
||||
GUINT_TO_POINTER (gl_depth_handle));
|
||||
}
|
||||
|
||||
if (flags & _TRY_STENCIL)
|
||||
{
|
||||
GE (glGenRenderbuffers (1, &gl_stencil_handle));
|
||||
GE (glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle));
|
||||
GE (glRenderbufferStorage (GL_RENDERBUFFER, GL_STENCIL_INDEX8,
|
||||
cogl_texture_get_width (texture),
|
||||
cogl_texture_get_height (texture)));
|
||||
GE (glBindRenderbuffer (GL_RENDERBUFFER, 0));
|
||||
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
|
||||
GL_STENCIL_ATTACHMENT,
|
||||
GL_RENDERBUFFER, gl_stencil_handle));
|
||||
offscreen->renderbuffers =
|
||||
g_slist_prepend (offscreen->renderbuffers,
|
||||
GUINT_TO_POINTER (gl_stencil_handle));
|
||||
}
|
||||
|
||||
/* Make sure it's complete */
|
||||
status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
GE (glDeleteFramebuffers (1, &fbo_gl_handle));
|
||||
|
||||
for (l = offscreen->renderbuffers; l; l = l->next)
|
||||
{
|
||||
GLuint renderbuffer = GPOINTER_TO_UINT (l->data);
|
||||
GE (glDeleteRenderbuffers (1, &renderbuffer));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
CoglHandle
|
||||
cogl_offscreen_new_to_texture (CoglHandle texhandle)
|
||||
{
|
||||
CoglOffscreen *offscreen;
|
||||
int width;
|
||||
int height;
|
||||
GLuint tex_gl_handle;
|
||||
GLenum tex_gl_target;
|
||||
GLuint fbo_gl_handle;
|
||||
GLuint gl_stencil_handle;
|
||||
GLenum status;
|
||||
CoglOffscreen *offscreen;
|
||||
TryFBOFlags flags;
|
||||
static TryFBOFlags last_working_flags = 0;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
|
||||
|
||||
|
@ -257,42 +372,6 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle)
|
|||
if (cogl_texture_is_sliced (texhandle))
|
||||
return COGL_INVALID_HANDLE;
|
||||
|
||||
/* Pick the single texture slice width, height and GL id */
|
||||
|
||||
width = cogl_texture_get_width (texhandle);
|
||||
height = cogl_texture_get_height (texhandle);
|
||||
|
||||
if (!cogl_texture_get_gl_texture (texhandle, &tex_gl_handle, &tex_gl_target))
|
||||
return COGL_INVALID_HANDLE;
|
||||
|
||||
if (tex_gl_target != GL_TEXTURE_2D
|
||||
#ifdef HAVE_COGL_GL
|
||||
&& tex_gl_target != GL_TEXTURE_RECTANGLE_ARB
|
||||
#endif
|
||||
)
|
||||
return COGL_INVALID_HANDLE;
|
||||
|
||||
/* Create a renderbuffer for stenciling */
|
||||
GE (glGenRenderbuffers (1, &gl_stencil_handle));
|
||||
GE (glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle));
|
||||
GE (glRenderbufferStorage (GL_RENDERBUFFER, GL_STENCIL_INDEX8,
|
||||
cogl_texture_get_width (texhandle),
|
||||
cogl_texture_get_height (texhandle)));
|
||||
GE (glBindRenderbuffer (GL_RENDERBUFFER, 0));
|
||||
|
||||
/* We are about to generate and bind a new fbo, so when next flushing the
|
||||
* journal, we will need to rebind the current framebuffer... */
|
||||
ctx->dirty_bound_framebuffer = 1;
|
||||
|
||||
/* Generate framebuffer */
|
||||
glGenFramebuffers (1, &fbo_gl_handle);
|
||||
GE (glBindFramebuffer (GL_FRAMEBUFFER, fbo_gl_handle));
|
||||
GE (glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
tex_gl_target, tex_gl_handle, 0));
|
||||
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
|
||||
GL_STENCIL_ATTACHMENT,
|
||||
GL_RENDERBUFFER, gl_stencil_handle));
|
||||
|
||||
/* XXX: The framebuffer_object spec isn't clear in defining whether attaching
|
||||
* a texture as a renderbuffer with mipmap filtering enabled while the
|
||||
* mipmaps have not been uploaded should result in an incomplete framebuffer
|
||||
|
@ -305,50 +384,34 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle)
|
|||
*/
|
||||
_cogl_texture_set_filters (texhandle, GL_NEAREST, GL_NEAREST);
|
||||
|
||||
/* Make sure it's complete */
|
||||
status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
|
||||
offscreen = g_new0 (CoglOffscreen, 1);
|
||||
offscreen->texture = cogl_handle_ref (texhandle);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
if (!(last_working_flags &&
|
||||
(flags = try_creating_fbo (offscreen, last_working_flags,
|
||||
texhandle))) &&
|
||||
!(flags = try_creating_fbo (offscreen, _TRY_DEPTH_STENCIL,
|
||||
texhandle)) &&
|
||||
!(flags = try_creating_fbo (offscreen, _TRY_DEPTH | _TRY_STENCIL,
|
||||
texhandle)) &&
|
||||
!(flags = try_creating_fbo (offscreen, _TRY_STENCIL, texhandle)) &&
|
||||
!(flags = try_creating_fbo (offscreen, _TRY_DEPTH, texhandle)) &&
|
||||
!(flags = try_creating_fbo (offscreen, 0, texhandle)))
|
||||
{
|
||||
/* Stencil renderbuffers aren't always supported. Try again
|
||||
without the stencil buffer */
|
||||
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
|
||||
GL_STENCIL_ATTACHMENT,
|
||||
GL_RENDERBUFFER,
|
||||
0));
|
||||
GE (glDeleteRenderbuffers (1, &gl_stencil_handle));
|
||||
gl_stencil_handle = 0;
|
||||
|
||||
status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
/* Still failing, so give up */
|
||||
GE (glDeleteFramebuffers (1, &fbo_gl_handle));
|
||||
GE (glBindFramebuffer (GL_FRAMEBUFFER, 0));
|
||||
return COGL_INVALID_HANDLE;
|
||||
}
|
||||
g_free (offscreen);
|
||||
last_working_flags = 0;
|
||||
/* XXX: This API should probably have been defined to take a GError */
|
||||
g_warning ("%s: Failed to create an OpenGL framebuffer", G_STRLOC);
|
||||
return COGL_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
offscreen = g_new0 (CoglOffscreen, 1);
|
||||
/* Save the final set of flags that worked so we can hopefully construct
|
||||
* subsequent buffers faster. */
|
||||
last_working_flags = flags;
|
||||
|
||||
_cogl_framebuffer_init (COGL_FRAMEBUFFER (offscreen),
|
||||
COGL_FRAMEBUFFER_TYPE_OFFSCREEN,
|
||||
width,
|
||||
height);
|
||||
|
||||
offscreen->fbo_handle = fbo_gl_handle;
|
||||
offscreen->gl_stencil_handle = gl_stencil_handle;
|
||||
offscreen->texture = cogl_handle_ref (texhandle);
|
||||
|
||||
/* XXX: Can we get a away with removing this? It wasn't documented, and most
|
||||
* users of the API are hopefully setting up the modelview from scratch
|
||||
* anyway */
|
||||
#if 0
|
||||
cogl_matrix_translate (&framebuffer->modelview, -1.0f, -1.0f, 0.0f);
|
||||
cogl_matrix_scale (&framebuffer->modelview,
|
||||
2.0f / framebuffer->width, 2.0f / framebuffer->height, 1.0f);
|
||||
#endif
|
||||
cogl_texture_get_width (texhandle),
|
||||
cogl_texture_get_height (texhandle));
|
||||
|
||||
return _cogl_offscreen_handle_new (offscreen);
|
||||
}
|
||||
|
@ -356,16 +419,25 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle)
|
|||
static void
|
||||
_cogl_offscreen_free (CoglOffscreen *offscreen)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
/* Chain up to parent */
|
||||
_cogl_framebuffer_free (COGL_FRAMEBUFFER (offscreen));
|
||||
|
||||
if (offscreen->gl_stencil_handle)
|
||||
GE (glDeleteRenderbuffers (1, &offscreen->gl_stencil_handle));
|
||||
for (l = offscreen->renderbuffers; l; l = l->next)
|
||||
{
|
||||
GLuint renderbuffer = GPOINTER_TO_UINT (l->data);
|
||||
GE (glDeleteRenderbuffers (1, &renderbuffer));
|
||||
}
|
||||
g_slist_free (offscreen->renderbuffers);
|
||||
|
||||
GE (glDeleteFramebuffers (1, &offscreen->fbo_handle));
|
||||
|
||||
if (offscreen->texture != COGL_INVALID_HANDLE)
|
||||
cogl_handle_unref (offscreen->texture);
|
||||
GE (glDeleteFramebuffers (1, &offscreen->fbo_handle));
|
||||
|
||||
g_free (offscreen);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue