journal: Support per-framebuffer journals

Instead of having a single journal per context, we now have a
CoglJournal object for each CoglFramebuffer. This means we now don't
have to flush the journal when switching/pushing/popping between
different framebuffers so for example a Clutter scene that involves some
ClutterEffect actors that transiently redirect to an FBO can still be
batched.

This also allows us to track state in the journal that relates to the
current frame of its associated framebuffer which we'll need for our
optimization for using the CPU to handle reading a single pixel back
from a framebuffer when we know the whole scene is currently comprised
of simple rectangles in a journal.
This commit is contained in:
Robert Bragg
2011-01-06 13:25:45 +00:00
parent 5f35bd7b67
commit 1a5a4df326
21 changed files with 375 additions and 101 deletions

View File

@ -139,6 +139,8 @@ _cogl_framebuffer_init (CoglFramebuffer *framebuffer,
int width,
int height)
{
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
framebuffer->type = type;
framebuffer->width = width;
framebuffer->height = height;
@ -155,11 +157,45 @@ _cogl_framebuffer_init (CoglFramebuffer *framebuffer,
/* Initialise the clip stack */
_cogl_clip_state_init (&framebuffer->clip_state);
framebuffer->journal = _cogl_journal_new ();
/* XXX: We have to maintain a central list of all framebuffers
* because at times we need to be able to flush all known journals.
*
* Examples where we need to flush all journals are:
* - because journal entries can reference OpenGL texture
* coordinates that may not survive texture-atlas reorganization
* so we need the ability to flush those entries.
* - because although we generally advise against modifying
* pipelines after construction we have to handle that possibility
* and since pipelines may be referenced in journal entries we
* need to be able to flush them before allowing the pipelines to
* be changed.
*
* Note we don't maintain a list of journals and associate
* framebuffers with journals by e.g. having a journal->framebuffer
* reference since that would introduce a circular reference.
*
* Note: As a future change to try and remove the need to index all
* journals it might be possible to defer resolving of OpenGL
* texture coordinates for rectangle primitives until we come to
* flush a journal. This would mean for instance that a single
* rectangle entry in a journal could later be expanded into
* multiple quad primitives to handle sliced textures but would mean
* we don't have to worry about retaining references to OpenGL
* texture coordinates that may later become invalid.
*/
ctx->framebuffers = g_list_prepend (ctx->framebuffers, framebuffer);
}
void
_cogl_framebuffer_free (CoglFramebuffer *framebuffer)
{
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
ctx->framebuffers = g_list_remove (ctx->framebuffers, framebuffer);
_cogl_clip_state_destroy (&framebuffer->clip_state);
cogl_object_unref (framebuffer->modelview_stack);
@ -167,6 +203,8 @@ _cogl_framebuffer_free (CoglFramebuffer *framebuffer)
cogl_object_unref (framebuffer->projection_stack);
framebuffer->projection_stack = NULL;
cogl_object_unref (framebuffer->journal);
}
/* This version of cogl_clear can be used internally as an alternative
@ -220,7 +258,10 @@ _cogl_framebuffer_clear4f (CoglFramebuffer *framebuffer,
{
COGL_NOTE (DRAW, "Clear begin");
_cogl_journal_flush ();
/* XXX: in the case where it's the color buffer being cleared and
* the current clip-stack is empty we could instead discard the
* journal here instead of flushing it. */
_cogl_framebuffer_flush_journal (framebuffer);
/* NB: _cogl_framebuffer_flush_state may disrupt various state (such
* as the pipeline state) when flushing the clip stack, so should
@ -305,7 +346,7 @@ _cogl_framebuffer_set_viewport (CoglFramebuffer *framebuffer,
framebuffer->viewport_height == height)
return;
_cogl_journal_flush ();
_cogl_framebuffer_flush_journal (framebuffer);
framebuffer->viewport_x = x;
framebuffer->viewport_y = y;
@ -361,6 +402,50 @@ _cogl_framebuffer_get_projection_stack (CoglFramebuffer *framebuffer)
return framebuffer->projection_stack;
}
void
_cogl_framebuffer_add_dependency (CoglFramebuffer *framebuffer,
CoglFramebuffer *dependency)
{
GList *l;
for (l = framebuffer->deps; l; l = l->next)
{
CoglFramebuffer *existing_dep = l->data;
if (existing_dep == dependency)
return;
}
/* TODO: generalize the primed-array type structure we e.g. use for
* cogl_object_set_user_data or for pipeline children as a way to
* avoid quite a lot of mid-scene micro allocations here... */
framebuffer->deps =
g_list_prepend (framebuffer->deps, cogl_object_ref (dependency));
}
void
_cogl_framebuffer_remove_all_dependencies (CoglFramebuffer *framebuffer)
{
GList *l;
for (l = framebuffer->deps; l; l = l->next)
cogl_object_unref (l->data);
g_list_free (framebuffer->deps);
framebuffer->deps = NULL;
}
void
_cogl_framebuffer_flush_journal (CoglFramebuffer *framebuffer)
{
_cogl_journal_flush (framebuffer->journal, framebuffer);
}
void
_cogl_framebuffer_flush_dependency_journals (CoglFramebuffer *framebuffer)
{
GList *l;
for (l = framebuffer->deps; l; l = l->next)
_cogl_framebuffer_flush_journal (l->data);
}
static inline void
_cogl_framebuffer_init_bits (CoglFramebuffer *framebuffer)
{
@ -459,11 +544,9 @@ try_creating_fbo (CoglOffscreen *offscreen,
)
return FALSE;
/* We are about to generate and bind a new fbo, so we pretend to change framebuffer
* state so that the old framebuffer will be rebound again before drawing.
* The framebuffer state can't be changed while their are active entries, so flush
* first. */
_cogl_journal_flush ();
/* We are about to generate and bind a new fbo, so we pretend to
* change framebuffer state so that the old framebuffer will be
* rebound again before drawing. */
ctx->dirty_bound_framebuffer = 1;
/* Generate framebuffer */
@ -639,13 +722,17 @@ _cogl_offscreen_new_to_texture_full (CoglHandle texhandle,
if (fbo_created)
{
CoglOffscreen *ret;
_cogl_framebuffer_init (COGL_FRAMEBUFFER (offscreen),
COGL_FRAMEBUFFER_TYPE_OFFSCREEN,
cogl_texture_get_format (texhandle),
data.level_width,
data.level_height);
return _cogl_offscreen_object_new (offscreen);
ret = _cogl_offscreen_object_new (offscreen);
_cogl_texture_associate_framebuffer (texhandle, COGL_FRAMEBUFFER (ret));
return ret;
}
else
{
@ -782,16 +869,14 @@ _cogl_set_framebuffer_real (CoglFramebuffer *framebuffer)
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
cogl_flush ();
entry = (CoglFramebuffer **)&ctx->framebuffer_stack->data;
ctx->dirty_bound_framebuffer = 1;
ctx->dirty_gl_viewport = 1;
if (framebuffer != COGL_INVALID_HANDLE)
if (framebuffer)
cogl_object_ref (framebuffer);
if (*entry != COGL_INVALID_HANDLE)
if (*entry)
cogl_object_unref (*entry);
*entry = framebuffer;
@ -844,8 +929,6 @@ cogl_push_framebuffer (CoglFramebuffer *buffer)
g_return_if_fail (_cogl_is_framebuffer (buffer));
g_assert (ctx->framebuffer_stack);
cogl_flush ();
ctx->framebuffer_stack =
g_slist_prepend (ctx->framebuffer_stack, COGL_INVALID_HANDLE);