framebuffer: Support texture based depth buffers

This commit introduces some new framebuffer api to be able to
enable texture based depth buffers for a framebuffer (currently
only supported for offscreen framebuffers) and once allocated
to be able to retrieve the depth buffer as a texture for further
usage, say, to implement shadow mapping.

The API works as follow:
  * Before the framebuffer is allocated, you can request that a depth
    texture is created with
    cogl_framebuffer_set_depth_texture_enabled()
  * cogl_framebuffer_get_depth_texture() can then be used to grab a
    CoglTexture once the framebuffer has been allocated.
This commit is contained in:
Damien Lespiau
2012-05-23 18:19:29 +01:00
committed by Robert Bragg
parent 58bbf8499a
commit 87bc616d34
11 changed files with 319 additions and 15 deletions

View File

@ -817,9 +817,83 @@ _cogl_offscreen_free (CoglOffscreen *offscreen)
if (offscreen->texture != NULL)
cogl_object_unref (offscreen->texture);
if (offscreen->depth_texture != NULL)
cogl_object_unref (offscreen->depth_texture);
g_free (offscreen);
}
static CoglTexture *
create_depth_texture (CoglContext *ctx,
int width,
int height)
{
CoglPixelFormat format;
CoglTexture2D *depth_texture;
if (ctx->private_feature_flags &
(COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL |
COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL))
{
format = COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8;
}
else
format = COGL_PIXEL_FORMAT_DEPTH_16;
depth_texture = cogl_texture_2d_new_with_size (ctx,
width, height,
format,
NULL);
return COGL_TEXTURE (depth_texture);
}
static CoglTexture *
attach_depth_texture (CoglContext *ctx,
CoglTexture *depth_texture,
CoglOffscreenAllocateFlags flags)
{
GLuint tex_gl_handle;
GLenum tex_gl_target;
if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL)
{
/* attach a GL_DEPTH_STENCIL texture to the GL_DEPTH_ATTACHMENT and
* GL_STENCIL_ATTACHMENT attachement points */
g_assert (cogl_texture_get_format (depth_texture) ==
COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8);
cogl_texture_get_gl_texture (depth_texture,
&tex_gl_handle, &tex_gl_target);
GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
tex_gl_target, tex_gl_handle,
0));
GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER,
GL_STENCIL_ATTACHMENT,
tex_gl_target, tex_gl_handle,
0));
}
else if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH)
{
/* attach a newly created GL_DEPTH_COMPONENT16 texture to the
* GL_DEPTH_ATTACHMENT attachement point */
g_assert (cogl_texture_get_format (depth_texture) ==
COGL_PIXEL_FORMAT_DEPTH_16);
cogl_texture_get_gl_texture (COGL_TEXTURE (depth_texture),
&tex_gl_handle, &tex_gl_target);
GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
tex_gl_target, tex_gl_handle,
0));
}
return COGL_TEXTURE (depth_texture);
}
static GList *
try_creating_renderbuffers (CoglContext *ctx,
int width,
@ -937,6 +1011,7 @@ try_creating_fbo (CoglContext *ctx,
int texture_level,
int texture_level_width,
int texture_level_height,
CoglTexture *depth_texture,
CoglFramebufferConfig *config,
CoglOffscreenAllocateFlags flags,
CoglGLFramebuffer *gl_framebuffer)
@ -987,12 +1062,31 @@ try_creating_fbo (CoglContext *ctx,
tex_gl_target, tex_gl_handle,
texture_level));
gl_framebuffer->renderbuffers =
try_creating_renderbuffers (ctx,
texture_level_width,
texture_level_height,
flags,
n_samples);
/* attach either a depth/stencil texture, a depth texture or render buffers
* depending on what we've been asked to provide */
if (depth_texture &&
flags & (COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL |
COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH))
{
attach_depth_texture (ctx, depth_texture, flags);
/* Let's clear the flags that are now fulfilled as we might need to
* create renderbuffers (for the ALLOCATE_FLAG_DEPTH |
* ALLOCATE_FLAG_STENCIL case) */
flags &= ~(COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL |
COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH);
}
if (flags)
{
gl_framebuffer->renderbuffers =
try_creating_renderbuffers (ctx,
texture_level_width,
texture_level_height,
flags,
n_samples);
}
/* Make sure it's complete */
status = ctx->glCheckFramebufferStatus (GL_FRAMEBUFFER);
@ -1031,6 +1125,7 @@ _cogl_framebuffer_try_creating_gl_fbo (CoglContext *ctx,
int texture_level,
int texture_level_width,
int texture_level_height,
CoglTexture *depth_texture,
CoglFramebufferConfig *config,
CoglOffscreenAllocateFlags flags,
CoglGLFramebuffer *gl_framebuffer)
@ -1040,6 +1135,7 @@ _cogl_framebuffer_try_creating_gl_fbo (CoglContext *ctx,
texture_level,
texture_level_width,
texture_level_height,
depth_texture,
config,
flags,
gl_framebuffer);
@ -1054,6 +1150,24 @@ _cogl_offscreen_allocate (CoglOffscreen *offscreen,
CoglOffscreenAllocateFlags flags;
CoglGLFramebuffer *gl_framebuffer = &offscreen->gl_framebuffer;
if (fb->config.depth_texture_enabled &&
offscreen->depth_texture == NULL)
{
offscreen->depth_texture =
create_depth_texture (ctx,
offscreen->texture_level_width,
offscreen->texture_level_height);
if (offscreen->depth_texture)
_cogl_texture_associate_framebuffer (offscreen->depth_texture, fb);
else
{
g_set_error (error, COGL_FRAMEBUFFER_ERROR,
COGL_FRAMEBUFFER_ERROR_ALLOCATE,
"Failed to allocate depth texture for framebuffer");
}
}
/* 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
@ -1072,6 +1186,7 @@ _cogl_offscreen_allocate (CoglOffscreen *offscreen,
offscreen->texture_level,
offscreen->texture_level_width,
offscreen->texture_level_height,
offscreen->depth_texture,
&fb->config,
flags = 0,
gl_framebuffer)) ||
@ -1082,6 +1197,7 @@ _cogl_offscreen_allocate (CoglOffscreen *offscreen,
offscreen->texture_level,
offscreen->texture_level_width,
offscreen->texture_level_height,
offscreen->depth_texture,
&fb->config,
flags = ctx->last_offscreen_allocate_flags,
gl_framebuffer)) ||
@ -1094,6 +1210,7 @@ _cogl_offscreen_allocate (CoglOffscreen *offscreen,
offscreen->texture_level,
offscreen->texture_level_width,
offscreen->texture_level_height,
offscreen->depth_texture,
&fb->config,
flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL,
gl_framebuffer)) ||
@ -1103,6 +1220,7 @@ _cogl_offscreen_allocate (CoglOffscreen *offscreen,
offscreen->texture_level,
offscreen->texture_level_width,
offscreen->texture_level_height,
offscreen->depth_texture,
&fb->config,
flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH |
COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL,
@ -1113,6 +1231,7 @@ _cogl_offscreen_allocate (CoglOffscreen *offscreen,
offscreen->texture_level,
offscreen->texture_level_width,
offscreen->texture_level_height,
offscreen->depth_texture,
&fb->config,
flags = COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL,
gl_framebuffer) ||
@ -1122,6 +1241,7 @@ _cogl_offscreen_allocate (CoglOffscreen *offscreen,
offscreen->texture_level,
offscreen->texture_level_width,
offscreen->texture_level_height,
offscreen->depth_texture,
&fb->config,
flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH,
gl_framebuffer) ||
@ -1131,6 +1251,7 @@ _cogl_offscreen_allocate (CoglOffscreen *offscreen,
offscreen->texture_level,
offscreen->texture_level_width,
offscreen->texture_level_height,
offscreen->depth_texture,
&fb->config,
flags = 0,
gl_framebuffer))
@ -1145,7 +1266,7 @@ _cogl_offscreen_allocate (CoglOffscreen *offscreen,
ctx->have_last_offscreen_allocate_flags = TRUE;
}
/* Save the flags we managed so successfully allocate the
/* Save the flags we managed to successfully allocate the
* renderbuffers with in case we need to make renderbuffers for a
* GLES2 context later */
offscreen->allocation_flags = flags;
@ -1173,6 +1294,15 @@ cogl_framebuffer_allocate (CoglFramebuffer *framebuffer,
if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
{
if (framebuffer->config.depth_texture_enabled)
{
g_set_error (error, COGL_FRAMEBUFFER_ERROR,
COGL_FRAMEBUFFER_ERROR_ALLOCATE,
"Can't allocate onscreen framebuffer with a "
"texture based depth buffer");
return FALSE;
}
if (!winsys->onscreen_init (onscreen, error))
return FALSE;
}
@ -1928,6 +2058,32 @@ cogl_framebuffer_get_color_format (CoglFramebuffer *framebuffer)
return framebuffer->format;
}
void
cogl_framebuffer_set_depth_texture_enabled (CoglFramebuffer *framebuffer,
CoglBool enabled)
{
_COGL_RETURN_IF_FAIL (!framebuffer->allocated);
framebuffer->config.depth_texture_enabled = enabled;
}
CoglBool
cogl_framebuffer_get_depth_texture_enabled (CoglFramebuffer *framebuffer)
{
return framebuffer->config.depth_texture_enabled;
}
CoglTexture *
cogl_framebuffer_get_depth_texture (CoglFramebuffer *framebuffer)
{
/* lazily allocate the framebuffer... */
if (!cogl_framebuffer_allocate (framebuffer, NULL))
return NULL;
_COGL_RETURN_VAL_IF_FAIL (cogl_is_offscreen (framebuffer), NULL);
return COGL_OFFSCREEN(framebuffer)->depth_texture;
}
int
cogl_framebuffer_get_samples_per_pixel (CoglFramebuffer *framebuffer)
{