Add an internal _cogl_offscreen_new_to_texture_full function

This function is the same as cogl_offscreen_new_to_texture but it
takes a level parameter and a set of flags so that FBOs can be used to
render to higher mipmap levels and to disable the depth and stencil
buffers. cogl_offscreen_new_to_texture now just calls the new function
with the level set to zero. This function could be useful in a few
places in Cogl where we want to use FBOs as an implementation detail
such as when copying between textures.

http://bugzilla.clutter-project.org/show_bug.cgi?id=2414
This commit is contained in:
Neil Roberts 2010-11-11 15:28:44 +00:00
parent d52bd995a6
commit b4016f64fa
2 changed files with 101 additions and 25 deletions

View File

@ -66,6 +66,12 @@ typedef struct _CoglOffscreen
CoglHandle texture;
} CoglOffscreen;
/* Flags to pass to _cogl_offscreen_new_to_texture_full */
typedef enum
{
COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL = 1
} CoglOffscreenFlags;
#define COGL_OFFSCREEN(X) ((CoglOffscreen *)(X))
typedef struct _CoglOnscreen
@ -143,5 +149,23 @@ _cogl_create_framebuffer_stack (void);
void
_cogl_free_framebuffer_stack (GSList *stack);
/*
* _cogl_offscreen_new_to_texture_full:
* @texhandle: A handle to the texture to target
* @create_flags: Flags specifying how to create the FBO
* @level: The mipmap level within the texture to target
*
* Creates a new offscreen buffer which will target the given
* texture. By default the buffer will have a depth and stencil
* buffer. This can be disabled by passing
* %COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL in @create_flags.
*
* Return value: the new CoglOffscreen object.
*/
CoglHandle
_cogl_offscreen_new_to_texture_full (CoglHandle texhandle,
CoglOffscreenFlags create_flags,
unsigned int level);
#endif /* __COGL_FRAMEBUFFER_PRIVATE_H */

View File

@ -324,10 +324,18 @@ _cogl_framebuffer_init_bits (CoglFramebuffer *framebuffer)
framebuffer->dirty_bitmasks = FALSE;
}
typedef struct
{
CoglHandle texture;
unsigned int level;
unsigned int level_width;
unsigned int level_height;
} CoglFramebufferTryFBOData;
static gboolean
try_creating_fbo (CoglOffscreen *offscreen,
TryFBOFlags flags,
CoglHandle texture)
CoglFramebufferTryFBOData *data)
{
GLuint gl_depth_stencil_handle;
GLuint gl_depth_handle;
@ -339,7 +347,8 @@ try_creating_fbo (CoglOffscreen *offscreen,
_COGL_GET_CONTEXT (ctx, FALSE);
if (!cogl_texture_get_gl_texture (texture, &tex_gl_handle, &tex_gl_target))
if (!cogl_texture_get_gl_texture (data->texture,
&tex_gl_handle, &tex_gl_target))
return FALSE;
if (tex_gl_target != GL_TEXTURE_2D
@ -362,7 +371,7 @@ try_creating_fbo (CoglOffscreen *offscreen,
offscreen->fbo_handle = fbo_gl_handle;
GE (glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
tex_gl_target, tex_gl_handle, 0));
tex_gl_target, tex_gl_handle, data->level));
if (flags & _TRY_DEPTH_STENCIL)
{
@ -370,8 +379,8 @@ try_creating_fbo (CoglOffscreen *offscreen,
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)));
data->level_width,
data->level_height));
GE (glBindRenderbuffer (GL_RENDERBUFFER, 0));
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
GL_STENCIL_ATTACHMENT,
@ -391,8 +400,8 @@ try_creating_fbo (CoglOffscreen *offscreen,
/* 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)));
data->level_width,
data->level_height));
GE (glBindRenderbuffer (GL_RENDERBUFFER, 0));
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
@ -407,8 +416,8 @@ try_creating_fbo (CoglOffscreen *offscreen,
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)));
data->level_width,
data->level_height));
GE (glBindRenderbuffer (GL_RENDERBUFFER, 0));
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
GL_STENCIL_ATTACHMENT,
@ -443,11 +452,16 @@ try_creating_fbo (CoglOffscreen *offscreen,
}
CoglHandle
cogl_offscreen_new_to_texture (CoglHandle texhandle)
_cogl_offscreen_new_to_texture_full (CoglHandle texhandle,
CoglOffscreenFlags create_flags,
unsigned int level)
{
CoglOffscreen *offscreen;
static TryFBOFlags flags;
static gboolean have_working_flags = FALSE;
unsigned int i;
CoglFramebufferTryFBOData data;
gboolean fbo_created;
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
@ -462,6 +476,27 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle)
if (cogl_texture_is_sliced (texhandle))
return COGL_INVALID_HANDLE;
data.texture = texhandle;
data.level = level;
/* Calculate the size of the texture at this mipmap level to ensure
that it's a valid level */
data.level_width = cogl_texture_get_width (texhandle);
data.level_height = cogl_texture_get_height (texhandle);
for (i = 0; i < level; i++)
{
/* If neither dimension can be further divided then the level is
invalid */
if (data.level_width == 1 && data.level_height == 1)
return COGL_INVALID_HANDLE;
if (data.level_width > 1)
data.level_width >>= 1;
if (data.level_height > 1)
data.level_height >>= 1;
}
/* 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
@ -477,25 +512,36 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle)
offscreen = g_new0 (CoglOffscreen, 1);
offscreen->texture = cogl_handle_ref (texhandle);
if ((have_working_flags &&
try_creating_fbo (offscreen, flags, texhandle)) ||
#ifdef HAVE_COGL_GL
try_creating_fbo (offscreen, flags = _TRY_DEPTH_STENCIL, texhandle) ||
#endif
try_creating_fbo (offscreen, flags = _TRY_DEPTH | _TRY_STENCIL,
texhandle) ||
try_creating_fbo (offscreen, flags = _TRY_STENCIL, texhandle) ||
try_creating_fbo (offscreen, flags = _TRY_DEPTH, texhandle) ||
try_creating_fbo (offscreen, flags = 0, texhandle))
if ((create_flags & COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL))
fbo_created = try_creating_fbo (offscreen, 0, &data);
else
{
/* Record that the last set of flags succeeded so that we can
try that set first next time */
have_working_flags = TRUE;
if ((have_working_flags &&
try_creating_fbo (offscreen, flags, &data)) ||
#ifdef HAVE_COGL_GL
try_creating_fbo (offscreen, flags = _TRY_DEPTH_STENCIL, &data) ||
#endif
try_creating_fbo (offscreen, flags = _TRY_DEPTH | _TRY_STENCIL,
&data) ||
try_creating_fbo (offscreen, flags = _TRY_STENCIL, &data) ||
try_creating_fbo (offscreen, flags = _TRY_DEPTH, &data) ||
try_creating_fbo (offscreen, flags = 0, &data))
{
/* Record that the last set of flags succeeded so that we can
try that set first next time */
have_working_flags = TRUE;
fbo_created = TRUE;
}
else
fbo_created = FALSE;
}
if (fbo_created)
{
_cogl_framebuffer_init (COGL_FRAMEBUFFER (offscreen),
COGL_FRAMEBUFFER_TYPE_OFFSCREEN,
cogl_texture_get_width (texhandle),
cogl_texture_get_height (texhandle));
data.level_width,
data.level_height);
return _cogl_offscreen_object_new (offscreen);
}
@ -508,6 +554,12 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle)
}
}
CoglHandle
cogl_offscreen_new_to_texture (CoglHandle texhandle)
{
return _cogl_offscreen_new_to_texture_full (texhandle, 0, 0);
}
static void
_cogl_offscreen_free (CoglOffscreen *offscreen)
{