Bug 945 - Clipping+fbo cloning bugs

* clutter/cogl/gl/cogl.c: 
	* clutter/cogl/gles/cogl.c: 
	* clutter/cogl/cogl.h.in: Add cogl_clip_stack_save,
	cogl_clip_stack_restore, cogl_viewport and cogl_frustum.

	* clutter/cogl/gl/cogl-fbo.h: 
	* clutter/cogl/gl/cogl-fbo.c: Try to attach a stencil buffer when
	creating an FBO.

	* clutter/cogl/common/cogl-clip-stack.c: Add functions to save and
	restore the whole state of the stack.

	* clutter/clutter-texture.c (clutter_texture_paint): When
	rendering the FBO source, setup a temporary asymmetric perspective
	projection matrix to render it as it would appear on screen.

	* clutter/clutter-private.h: 
	* clutter/clutter-actor.c
	(_clutter_actor_apply_modelview_transform_recursive): No longer
	static and exported in clutter-private.h
This commit is contained in:
Neil Roberts 2008-08-01 12:23:57 +00:00
parent 88a561fa01
commit ef76d8e5e2
11 changed files with 307 additions and 33 deletions

View File

@ -338,14 +338,35 @@ void cogl_get_bitmasks (gint *red,
* @z_near: Nearest visible point
* @z_far: Furthest visible point along the z-axis
*
* Multiplies the current set matrix with a projection matrix based
* on the provided values.
* Replaces the current projection matrix with a perspective matrix
* based on the provided values.
*/
void cogl_perspective (ClutterFixed fovy,
ClutterFixed aspect,
ClutterFixed z_near,
ClutterFixed z_far);
/**
* cogl_frustum:
* @left: Left clipping plane
* @right: Right clipping plane
* @bottom: Bottom clipping plane
* @top: Top clipping plane
* @z_near: Nearest visible point
* @z_far: Furthest visible point along the z-axis
*
* Replaces the current projection matrix with a perspective matrix
* for the given viewing frustum.
*
* Since: 0.8.2
*/
void cogl_frustum (ClutterFixed left,
ClutterFixed right,
ClutterFixed bottom,
ClutterFixed top,
ClutterFixed z_near,
ClutterFixed z_far);
/**
* cogl_setup_viewport:
* @width: Width of the viewport
@ -369,6 +390,18 @@ void cogl_setup_viewport (guint width,
ClutterFixed z_near,
ClutterFixed z_far);
/**
* cogl_viewport:
* @width: Width of the viewport
* @height: Height of the viewport
*
* Replace the current viewport with the given values.
*
* Since: 0.8.2
*/
void cogl_viewport (guint width,
guint height);
/**
* cogl_push_matrix:
*
@ -511,6 +544,29 @@ void cogl_clip_set (ClutterFixed x_offset,
*/
void cogl_clip_unset (void);
/**
* cogl_clip_stack_save:
*
* Save the entire state of the clipping stack and then clear all
* clipping. The previous state can be returned to with
* cogl_clip_stack_restore(). Each call to cogl_clip_set() after this
* must be matched by a call to cogl_clip_unset() before calling
* cogl_clip_stack_restore().
*
* Since: 0.8.2
*/
void cogl_clip_stack_save (void);
/**
* cogl_clip_stack_restore:
*
* Restore the state of the clipping stack that was previously saved
* by cogl_clip_stack_save().
*
* Since: 0.8.2
*/
void cogl_clip_stack_restore (void);
/**
* cogl_enable_depth_test:
* @setting: %TRUE to enable depth testing or %FALSE to disable.

View File

@ -50,6 +50,12 @@ typedef struct _CoglClipStackEntry CoglClipStackEntry;
struct _CoglClipStackEntry
{
/* If this is set then this entry clears the clip stack. This is
used to clear the stack when drawing an FBO put to keep the
entries so they can be restored when the FBO drawing is
completed */
gboolean clear;
/* The rectangle for this clip */
ClutterFixed x_offset;
ClutterFixed y_offset;
@ -61,7 +67,6 @@ struct _CoglClipStackEntry
};
static GList *cogl_clip_stack_top = NULL;
static GList *cogl_clip_stack_bottom = NULL;
static int cogl_clip_stack_depth = 0;
static void
@ -93,6 +98,7 @@ cogl_clip_set (ClutterFixed x_offset,
CoglClipStackEntry *entry = g_slice_new (CoglClipStackEntry);
/* Make a new entry */
entry->clear = FALSE;
entry->x_offset = x_offset;
entry->y_offset = y_offset;
entry->width = width;
@ -105,8 +111,6 @@ cogl_clip_set (ClutterFixed x_offset,
/* Store it in the stack */
cogl_clip_stack_top = g_list_prepend (cogl_clip_stack_top, entry);
if (cogl_clip_stack_bottom == NULL)
cogl_clip_stack_bottom = cogl_clip_stack_top;
}
void
@ -118,8 +122,6 @@ cogl_clip_unset (void)
g_slice_free (CoglClipStackEntry, cogl_clip_stack_top->data);
cogl_clip_stack_top = g_list_delete_link (cogl_clip_stack_top,
cogl_clip_stack_top);
if (cogl_clip_stack_top == NULL)
cogl_clip_stack_bottom = NULL;
cogl_clip_stack_depth--;
/* Rebuild the clip */
@ -131,7 +133,7 @@ _cogl_clip_stack_rebuild (gboolean just_stencil)
{
int has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES);
GList *node;
int depth = 1;
int depth = 0;
/* Disable clip planes if the stack is empty */
if (has_clip_planes && cogl_clip_stack_depth < 1)
@ -141,8 +143,14 @@ _cogl_clip_stack_rebuild (gboolean just_stencil)
if (cogl_clip_stack_depth < (has_clip_planes ? 2 : 1))
_cogl_disable_stencil_buffer ();
/* Find the bottom of the stack */
for (node = cogl_clip_stack_top; depth < cogl_clip_stack_depth - 1;
node = node->next)
depth++;
/* Re-add every entry from the bottom of the stack up */
for (node = cogl_clip_stack_bottom; node; node = node->prev, depth++)
depth = 1;
for (; depth <= cogl_clip_stack_depth; node = node->prev, depth++)
if (!just_stencil || !has_clip_planes || depth > 1)
{
const CoglClipStackEntry *entry = (CoglClipStackEntry *) node->data;
@ -156,12 +164,16 @@ _cogl_clip_stack_rebuild (gboolean just_stencil)
void
_cogl_clip_stack_merge (void)
{
GList *node = cogl_clip_stack_bottom;
GList *node = cogl_clip_stack_top;
int i;
/* Merge the current clip stack on top of whatever is in the stencil
buffer */
if (node)
if (cogl_clip_stack_depth)
{
for (i = 0; i < cogl_clip_stack_depth - 1; i++)
node = node->next;
/* Skip the first entry if we have clipping planes */
if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES))
node = node->prev;
@ -178,3 +190,43 @@ _cogl_clip_stack_merge (void)
}
}
}
void
cogl_clip_stack_save (void)
{
CoglClipStackEntry *entry = g_slice_new (CoglClipStackEntry);
/* Push an entry into the stack to mark that it should be cleared */
entry->clear = TRUE;
cogl_clip_stack_top = g_list_prepend (cogl_clip_stack_top, entry);
/* Reset the depth to zero */
cogl_clip_stack_depth = 0;
/* Rebuilding the stack will now disabling all clipping */
_cogl_clip_stack_rebuild (FALSE);
}
void
cogl_clip_stack_restore (void)
{
GList *node;
/* The top of the stack should be a clear marker */
g_assert (cogl_clip_stack_top);
g_assert (((CoglClipStackEntry *) cogl_clip_stack_top->data)->clear);
/* Remove the top entry */
g_slice_free (CoglClipStackEntry, cogl_clip_stack_top->data);
cogl_clip_stack_top = g_list_delete_link (cogl_clip_stack_top,
cogl_clip_stack_top);
/* Recalculate the depth of the stack */
cogl_clip_stack_depth = 0;
for (node = cogl_clip_stack_top;
node && !((CoglClipStackEntry *) node->data)->clear;
node = node->next)
cogl_clip_stack_depth++;
_cogl_clip_stack_rebuild (FALSE);
}

View File

@ -26,11 +26,6 @@
#ifndef __COGL_CLIP_STACK_H
#define __COGL_CLIP_STACK_H
void cogl_clip_set (ClutterFixed x_offset,
ClutterFixed y_offset,
ClutterFixed width,
ClutterFixed height);
void cogl_clip_unset (void);
void _cogl_clip_stack_rebuild (gboolean just_stencil);
void _cogl_clip_stack_merge (void);

View File

@ -13,7 +13,9 @@ CoglPixelFormat
CoglBufferTarget
<SUBSECTION>
cogl_perspective
cogl_frustum
cogl_setup_viewport
cogl_viewport
cogl_get_modelview_matrix
cogl_get_projection_matrix
cogl_get_viewport
@ -28,6 +30,8 @@ cogl_rotate
<SUBSECTION>
cogl_clip_set
cogl_clip_unset
cogl_clip_stack_save
cogl_clip_stack_restore
<SUBSECTION>
cogl_enable_depth_test
cogl_alpha_func

View File

@ -68,6 +68,7 @@ typedef struct
/* Relying on glext.h to define these */
COGL_PFNGLGENRENDERBUFFERSEXTPROC pf_glGenRenderbuffersEXT;
COGL_PFNGLDELETERENDERBUFFERSEXTPROC pf_glDeleteRenderbuffersEXT;
COGL_PFNGLBINDRENDERBUFFEREXTPROC pf_glBindRenderbufferEXT;
COGL_PFNGLRENDERBUFFERSTORAGEEXTPROC pf_glRenderbufferStorageEXT;
COGL_PFNGLGENFRAMEBUFFERSEXTPROC pf_glGenFramebuffersEXT;

View File

@ -37,6 +37,7 @@
/* Expecting EXT functions not to be defined - redirect to pointers in context */
#define glGenRenderbuffersEXT ctx->pf_glGenRenderbuffersEXT
#define glDeleteRenderbuffersEXT ctx->pf_glDeleteRenderbuffersEXT
#define glBindRenderbufferEXT ctx->pf_glBindRenderbufferEXT
#define glRenderbufferStorageEXT ctx->pf_glRenderbufferStorageEXT
#define glGenFramebuffersEXT ctx->pf_glGenFramebuffersEXT
@ -68,6 +69,7 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle)
CoglTexSliceSpan *y_span;
GLuint tex_gl_handle;
GLuint fbo_gl_handle;
GLuint gl_stencil_handle;
GLenum status;
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
@ -92,21 +94,47 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle)
x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0);
y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0);
tex_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0);
/* Create a renderbuffer for stenciling */
GE( glGenRenderbuffersEXT (1, &gl_stencil_handle) );
GE( glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, gl_stencil_handle) );
GE( glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX8_EXT,
cogl_texture_get_width (texhandle),
cogl_texture_get_height (texhandle)) );
GE( glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, 0) );
/* Generate framebuffer */
glGenFramebuffersEXT (1, &fbo_gl_handle);
GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fbo_gl_handle) );
GE( glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
tex->gl_target, tex_gl_handle, 0) );
GE( glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT,
GL_STENCIL_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, gl_stencil_handle) );
/* Make sure it's complete */
status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
GE( glDeleteFramebuffersEXT (1, &fbo_gl_handle) );
GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) );
return COGL_INVALID_HANDLE;
/* Stencil renderbuffers aren't always supported. Try again
without the stencil buffer */
GE( glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT,
GL_STENCIL_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT,
0) );
GE( glDeleteRenderbuffersEXT (1, &gl_stencil_handle) );
gl_stencil_handle = 0;
status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
/* Still failing, so give up */
GE( glDeleteFramebuffersEXT (1, &fbo_gl_handle) );
GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) );
return COGL_INVALID_HANDLE;
}
}
GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) );
@ -114,10 +142,11 @@ cogl_offscreen_new_to_texture (CoglHandle texhandle)
/* Allocate and init a CoglFbo object (store non-wasted size
for subsequent blits and viewport setup) */
fbo = (CoglFbo*) g_malloc (sizeof (CoglFbo));
fbo->ref_count = 1;
fbo->width = x_span->size - x_span->waste;
fbo->height = y_span->size - y_span->waste;
fbo->gl_handle = fbo_gl_handle;
fbo->ref_count = 1;
fbo->width = x_span->size - x_span->waste;
fbo->height = y_span->size - y_span->waste;
fbo->gl_handle = fbo_gl_handle;
fbo->gl_stencil_handle = gl_stencil_handle;
COGL_HANDLE_DEBUG_NEW (offscreen, fbo);
@ -140,7 +169,8 @@ _cogl_offscreen_free (CoglFbo *fbo)
/* Frees FBO resources but its handle is not
released! Do that separately before this! */
if (fbo->gl_stencil_handle)
GE( glDeleteRenderbuffersEXT (1, &fbo->gl_stencil_handle) );
GE( glDeleteFramebuffersEXT (1, &fbo->gl_handle) );
g_free (fbo);
}

View File

@ -32,6 +32,7 @@ typedef struct
int width;
int height;
GLuint gl_handle;
GLuint gl_stencil_handle;
} CoglFbo;

View File

@ -746,6 +746,9 @@ cogl_perspective (ClutterFixed fovy,
memset (&m[0], 0, sizeof (m));
GE( glMatrixMode (GL_PROJECTION) );
GE( glLoadIdentity () );
/*
* Based on the original algorithm in perspective():
*
@ -773,6 +776,8 @@ cogl_perspective (ClutterFixed fovy,
GE( glMultMatrixf (m) );
GE( glMatrixMode (GL_MODELVIEW) );
/* Calculate and store the inverse of the matrix */
memset (ctx->inverse_projection, 0, sizeof (GLfloat) * 16);
@ -786,6 +791,60 @@ cogl_perspective (ClutterFixed fovy,
#undef M
}
void
cogl_frustum (ClutterFixed left,
ClutterFixed right,
ClutterFixed bottom,
ClutterFixed top,
ClutterFixed z_near,
ClutterFixed z_far)
{
GLfloat c, d;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
GE( glMatrixMode (GL_PROJECTION) );
GE( glLoadIdentity () );
GE( glFrustum (CLUTTER_FIXED_TO_DOUBLE (left),
CLUTTER_FIXED_TO_DOUBLE (right),
CLUTTER_FIXED_TO_DOUBLE (bottom),
CLUTTER_FIXED_TO_DOUBLE (top),
CLUTTER_FIXED_TO_DOUBLE (z_near),
CLUTTER_FIXED_TO_DOUBLE (z_far)) );
GE( glMatrixMode (GL_MODELVIEW) );
/* Calculate and store the inverse of the matrix */
memset (ctx->inverse_projection, 0, sizeof (GLfloat) * 16);
c = -CLUTTER_FIXED_TO_FLOAT (z_far + z_near)
/ CLUTTER_FIXED_TO_FLOAT (z_far - z_near);
d = -CLUTTER_FIXED_TO_FLOAT (2 * CFX_QMUL (z_far, z_near))
/ CLUTTER_FIXED_TO_FLOAT (z_far - z_near);
#define M(row,col) ctx->inverse_projection[col*4+row]
M(0,0) = CLUTTER_FIXED_TO_FLOAT (right - left)
/ CLUTTER_FIXED_TO_FLOAT (2 * z_near);
M(0,3) = CLUTTER_FIXED_TO_FLOAT (right + left)
/ CLUTTER_FIXED_TO_FLOAT (2 * z_near);
M(1,1) = CLUTTER_FIXED_TO_FLOAT (top - bottom)
/ CLUTTER_FIXED_TO_FLOAT (2 * z_near);
M(1,3) = CLUTTER_FIXED_TO_FLOAT (top + bottom)
/ CLUTTER_FIXED_TO_FLOAT (2 * z_near);
M(2,3) = -1.0f;
M(3,2) = 1.0f / d;
M(3,3) = c / d;
#undef M
}
void
cogl_viewport (guint width,
guint height)
{
GE( glViewport (0, 0, width, height) );
}
void
cogl_setup_viewport (guint width,
guint height,
@ -798,12 +857,8 @@ cogl_setup_viewport (guint width,
GE( glViewport (0, 0, width, height) );
GE( glMatrixMode (GL_PROJECTION) );
GE( glLoadIdentity () );
cogl_perspective (fovy, aspect, z_near, z_far);
GE( glMatrixMode (GL_MODELVIEW) );
GE( glLoadIdentity () );
/*
@ -969,6 +1024,10 @@ _cogl_features_init ()
(COGL_PFNGLGENRENDERBUFFERSEXTPROC)
cogl_get_proc_address ("glGenRenderbuffersEXT");
ctx->pf_glDeleteRenderbuffersEXT =
(COGL_PFNGLDELETERENDERBUFFERSEXTPROC)
cogl_get_proc_address ("glDeleteRenderbuffersEXT");
ctx->pf_glBindRenderbufferEXT =
(COGL_PFNGLBINDRENDERBUFFEREXTPROC)
cogl_get_proc_address ("glBindRenderbufferEXT");

View File

@ -666,6 +666,31 @@ cogl_wrap_glMultMatrixx (const GLfixed *m)
cogl_wrap_glMultMatrix (new_matrix);
}
void
cogl_wrap_glFrustumx (GLfixed left, GLfixed right,
GLfixed bottom, GLfixed top,
GLfixed z_near, GLfixed z_far)
{
float matrix[16];
float two_near = CLUTTER_FIXED_TO_FLOAT (2 * z_near);
memset (matrix, 0, sizeof (matrix));
matrix[0] = two_near / CLUTTER_FIXED_TO_FLOAT (right - left);
matrix[5] = two_near / CLUTTER_FIXED_TO_FLOAT (top - bottom);
matrix[8] = CLUTTER_FIXED_TO_FLOAT (right + left)
/ CLUTTER_FIXED_TO_FLOAT (right - left);
matrix[9] = CLUTTER_FIXED_TO_FLOAT (top + bottom)
/ CLUTTER_FIXED_TO_FLOAT (top - bottom);
matrix[10] = -CLUTTER_FIXED_TO_FLOAT (z_far + z_near)
/ CLUTTER_FIXED_TO_FLOAT (z_far - z_near);
matrix[11] = -1.0f;
matrix[14] = -two_near * CLUTTER_FIXED_TO_FLOAT (z_far)
/ CLUTTER_FIXED_TO_FLOAT (z_far - z_near);
cogl_wrap_glMultMatrix (matrix);
}
void
cogl_wrap_glScalex (GLfixed x, GLfixed y, GLfixed z)
{

View File

@ -204,6 +204,9 @@ void cogl_wrap_glPopMatrix ();
void cogl_wrap_glMatrixMode (GLenum mode);
void cogl_wrap_glLoadIdentity ();
void cogl_wrap_glMultMatrixx (const GLfixed *m);
void cogl_wrap_glFrustumx (GLfixed left, GLfixed right,
GLfixed bottom, GLfixed top,
GLfixed z_near, GLfixed z_far);
void cogl_wrap_glScalex (GLfixed x, GLfixed y, GLfixed z);
void cogl_wrap_glTranslatex (GLfixed x, GLfixed y, GLfixed z);
void cogl_wrap_glRotatex (GLfixed angle, GLfixed x, GLfixed y, GLfixed z);
@ -268,6 +271,7 @@ void _cogl_gles2_clear_cache_for_program (CoglHandle program);
#define cogl_wrap_glMatrixMode glMatrixMode
#define cogl_wrap_glLoadIdentity glLoadIdentity
#define cogl_wrap_glMultMatrixx glMultMatrixx
#define cogl_wrap_glFrustumx glFrustumx
#define cogl_wrap_glScalex glScalex
#define cogl_wrap_glTranslatex glTranslatex
#define cogl_wrap_glRotatex glRotatex

View File

@ -661,7 +661,10 @@ cogl_perspective (ClutterFixed fovy,
memset (&m[0], 0, sizeof (m));
/*
GE( cogl_wrap_glMatrixMode (GL_PROJECTION) );
GE( cogl_wrap_glLoadIdentity () );
/*
* Based on the original algorithm in perspective():
*
* 1) xmin = -xmax => xmax + xmin == 0 && xmax - xmin == 2 * xmax
@ -688,6 +691,8 @@ cogl_perspective (ClutterFixed fovy,
GE( cogl_wrap_glMultMatrixx (m) );
GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) );
/* Calculate and store the inverse of the matrix */
memset (ctx->inverse_projection, 0, sizeof (ClutterFixed) * 16);
@ -702,6 +707,51 @@ cogl_perspective (ClutterFixed fovy,
#undef M
}
void
cogl_frustum (ClutterFixed left,
ClutterFixed right,
ClutterFixed bottom,
ClutterFixed top,
ClutterFixed z_near,
ClutterFixed z_far)
{
ClutterFixed c, d;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
GE( cogl_wrap_glMatrixMode (GL_PROJECTION) );
GE( cogl_wrap_glLoadIdentity () );
GE( cogl_wrap_glFrustumx (left, right,
bottom, top,
z_near, z_far) );
GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) );
/* Calculate and store the inverse of the matrix */
memset (ctx->inverse_projection, 0, sizeof (ClutterFixed) * 16);
c = -CFX_QDIV (z_far + z_near, z_far - z_near);
d = -CFX_QDIV (2 * CFX_QMUL (z_far, z_near), z_far - z_near);
#define M(row,col) ctx->inverse_projection[col*4+row]
M(0,0) = CFX_QDIV (right - left, 2 * z_near);
M(0,3) = CFX_QDIV (right + left, 2 * z_near);
M(1,1) = CFX_QDIV (top - bottom, 2 * z_near);
M(1,3) = CFX_QDIV (top + bottom, 2 * z_near);
M(2,3) = -CFX_ONE;
M(3,2) = CFX_QDIV (CFX_ONE, d);
M(3,3) = CFX_QDIV (c, d);
#undef M
}
void
cogl_viewport (guint width,
guint height)
{
GE( glViewport (0, 0, width, height) );
}
void
cogl_setup_viewport (guint w,
guint h,
@ -715,8 +765,6 @@ cogl_setup_viewport (guint w,
ClutterFixed z_camera;
GE( glViewport (0, 0, width, height) );
GE( cogl_wrap_glMatrixMode (GL_PROJECTION) );
GE( cogl_wrap_glLoadIdentity () );
/* For Ortho projection.
* cogl_wrap_glOrthox (0, width << 16, 0, height << 16, -1 << 16, 1 << 16);
@ -724,7 +772,6 @@ cogl_setup_viewport (guint w,
cogl_perspective (fovy, aspect, z_near, z_far);
GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) );
GE( cogl_wrap_glLoadIdentity () );
/*