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

@ -98,14 +98,14 @@ _cogl_atlas_texture_reorganize_cb (void *data)
{ {
CoglAtlas *atlas = data; CoglAtlas *atlas = data;
/* We don't know if any pipelines may currently be referenced in /* We don't know if any journal entries currently depend on OpenGL
* the journal that depend on the current underlying GL texture * texture coordinates that would be invalidated by reorganizing
* storage so we flush the journal before migrating. * this atlas so we flush all journals before migrating.
* *
* We are assuming that texture atlas migration never happens * We are assuming that texture atlas migration never happens
* during a flush so we don't have to consider recursion here. * during a flush so we don't have to consider recursion here.
*/ */
_cogl_journal_flush (); cogl_flush ();
if (atlas->map) if (atlas->map)
_cogl_rectangle_map_foreach (atlas->map, _cogl_rectangle_map_foreach (atlas->map,
@ -294,14 +294,15 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex)
{ {
COGL_NOTE (ATLAS, "Migrating texture out of the atlas"); COGL_NOTE (ATLAS, "Migrating texture out of the atlas");
/* We don't know if any pipelines may currently be referenced in /* We don't know if any journal entries currently depend on
* the journal that depend on the current underlying GL texture * OpenGL texture coordinates that would be invalidated by
* storage so we flush the journal before migrating. * migrating textures in this atlas so we flush all journals
* before migrating.
* *
* We are assuming that texture atlas migration never happens * We are assuming that texture atlas migration never happens
* during a flush so we don't have to consider recursion here. * during a flush so we don't have to consider recursion here.
*/ */
_cogl_journal_flush (); cogl_flush ();
/* Notify cogl-pipeline.c that the texture's underlying GL texture /* Notify cogl-pipeline.c that the texture's underlying GL texture
* storage is changing so it knows it may need to bind a new texture * storage is changing so it knows it may need to bind a new texture
@ -575,6 +576,9 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp,
to set as the data for the rectangle in the atlas */ to set as the data for the rectangle in the atlas */
atlas_tex = g_new (CoglAtlasTexture, 1); atlas_tex = g_new (CoglAtlasTexture, 1);
_cogl_texture_init (COGL_TEXTURE (atlas_tex),
&cogl_atlas_texture_vtable);
atlas_tex->sub_texture = COGL_INVALID_HANDLE; atlas_tex->sub_texture = COGL_INVALID_HANDLE;
/* Look for an existing atlas that can hold the texture */ /* Look for an existing atlas that can hold the texture */
@ -620,8 +624,6 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp,
g_free (atlas_tex); g_free (atlas_tex);
return COGL_INVALID_HANDLE; return COGL_INVALID_HANDLE;
} }
atlas_tex->_parent.vtable = &cogl_atlas_texture_vtable;
atlas_tex->format = internal_format; atlas_tex->format = internal_format;
atlas_tex->atlas = atlas; atlas_tex->atlas = atlas;

View File

@ -169,14 +169,15 @@ _cogl_clip_state_flush (CoglClipState *clip_state)
void void
cogl_clip_ensure (void) cogl_clip_ensure (void)
{ {
CoglFramebuffer *framebuffer = _cogl_get_framebuffer ();
CoglClipState *clip_state; CoglClipState *clip_state;
clip_state = _cogl_framebuffer_get_clip_state (_cogl_get_framebuffer ()); clip_state = _cogl_framebuffer_get_clip_state (framebuffer);
/* Flushing the clip state doesn't cause the journal to be /* Flushing the clip state doesn't cause the journal to be
flushed. This function may be being called by an external flushed. This function may be being called by an external
application however so it makes sense to flush the journal application however so it makes sense to flush the journal
here */ here */
_cogl_journal_flush (); _cogl_framebuffer_flush_journal (framebuffer);
_cogl_clip_state_flush (clip_state); _cogl_clip_state_flush (clip_state);
} }

View File

@ -166,8 +166,6 @@ cogl_create_context (void)
_context->default_gl_texture_2d_tex = COGL_INVALID_HANDLE; _context->default_gl_texture_2d_tex = COGL_INVALID_HANDLE;
_context->default_gl_texture_rect_tex = COGL_INVALID_HANDLE; _context->default_gl_texture_rect_tex = COGL_INVALID_HANDLE;
_context->journal = g_array_new (FALSE, FALSE, sizeof (CoglJournalEntry));
_context->logged_vertices = g_array_new (FALSE, FALSE, sizeof (GLfloat));
_context->journal_flush_attributes_array = _context->journal_flush_attributes_array =
g_array_new (TRUE, FALSE, sizeof (CoglVertexAttribute *)); g_array_new (TRUE, FALSE, sizeof (CoglVertexAttribute *));
_context->journal_clip_bounds = NULL; _context->journal_clip_bounds = NULL;
@ -333,10 +331,6 @@ _cogl_destroy_context (void)
if (_context->texture_pipeline) if (_context->texture_pipeline)
cogl_handle_unref (_context->texture_pipeline); cogl_handle_unref (_context->texture_pipeline);
if (_context->journal)
g_array_free (_context->journal, TRUE);
if (_context->logged_vertices)
g_array_free (_context->logged_vertices, TRUE);
if (_context->journal_flush_attributes_array) if (_context->journal_flush_attributes_array)
g_array_free (_context->journal_flush_attributes_array, TRUE); g_array_free (_context->journal_flush_attributes_array, TRUE);
if (_context->journal_clip_bounds) if (_context->journal_clip_bounds)

View File

@ -109,14 +109,12 @@ typedef struct
CoglHandle default_gl_texture_2d_tex; CoglHandle default_gl_texture_2d_tex;
CoglHandle default_gl_texture_rect_tex; CoglHandle default_gl_texture_rect_tex;
/* Batching geometry... */ /* Central list of all framebuffers so all journals can be flushed
/* We journal the texture rectangles we want to submit to OpenGL so * at any time. */
* we have an oppertunity to optimise the final order so that we GList *framebuffers;
* can batch things together. */
GArray *journal; /* Global journal buffers */
GArray *logged_vertices;
GArray *journal_flush_attributes_array; GArray *journal_flush_attributes_array;
size_t journal_needed_vbo_len;
GArray *journal_clip_bounds; GArray *journal_clip_bounds;
GArray *polygon_vertices; GArray *polygon_vertices;

View File

@ -27,6 +27,7 @@
#include "cogl-handle.h" #include "cogl-handle.h"
#include "cogl-matrix-stack.h" #include "cogl-matrix-stack.h"
#include "cogl-clip-state.h" #include "cogl-clip-state.h"
#include "cogl-journal-private.h"
typedef enum _CoglFramebufferType { typedef enum _CoglFramebufferType {
COGL_FRAMEBUFFER_TYPE_ONSCREEN, COGL_FRAMEBUFFER_TYPE_ONSCREEN,
@ -57,6 +58,15 @@ struct _CoglFramebuffer
int blue_bits; int blue_bits;
int green_bits; int green_bits;
int alpha_bits; int alpha_bits;
/* We journal the textured rectangles we want to submit to OpenGL so
* we have an oppertunity to batch them together into less draw
* calls. */
CoglJournal *journal;
/* The scene of a given framebuffer may depend on images in other
* framebuffers... */
GList *deps;
}; };
#define COGL_FRAMEBUFFER(X) ((CoglFramebuffer *)(X)) #define COGL_FRAMEBUFFER(X) ((CoglFramebuffer *)(X))
@ -144,6 +154,19 @@ _cogl_framebuffer_get_modelview_stack (CoglFramebuffer *framebuffer);
CoglMatrixStack * CoglMatrixStack *
_cogl_framebuffer_get_projection_stack (CoglFramebuffer *framebuffer); _cogl_framebuffer_get_projection_stack (CoglFramebuffer *framebuffer);
void
_cogl_framebuffer_add_dependency (CoglFramebuffer *framebuffer,
CoglFramebuffer *dependency);
void
_cogl_framebuffer_remove_all_dependencies (CoglFramebuffer *framebuffer);
void
_cogl_framebuffer_flush_journal (CoglFramebuffer *framebuffer);
void
_cogl_framebuffer_flush_dependency_journals (CoglFramebuffer *framebuffer);
typedef enum _CoglFramebufferFlushFlags typedef enum _CoglFramebufferFlushFlags
{ {
/* XXX: When using this, that imples you are going to manually load the /* XXX: When using this, that imples you are going to manually load the

View File

@ -139,6 +139,8 @@ _cogl_framebuffer_init (CoglFramebuffer *framebuffer,
int width, int width,
int height) int height)
{ {
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
framebuffer->type = type; framebuffer->type = type;
framebuffer->width = width; framebuffer->width = width;
framebuffer->height = height; framebuffer->height = height;
@ -155,11 +157,45 @@ _cogl_framebuffer_init (CoglFramebuffer *framebuffer,
/* Initialise the clip stack */ /* Initialise the clip stack */
_cogl_clip_state_init (&framebuffer->clip_state); _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 void
_cogl_framebuffer_free (CoglFramebuffer *framebuffer) _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_clip_state_destroy (&framebuffer->clip_state);
cogl_object_unref (framebuffer->modelview_stack); cogl_object_unref (framebuffer->modelview_stack);
@ -167,6 +203,8 @@ _cogl_framebuffer_free (CoglFramebuffer *framebuffer)
cogl_object_unref (framebuffer->projection_stack); cogl_object_unref (framebuffer->projection_stack);
framebuffer->projection_stack = NULL; framebuffer->projection_stack = NULL;
cogl_object_unref (framebuffer->journal);
} }
/* This version of cogl_clear can be used internally as an alternative /* 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_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 /* NB: _cogl_framebuffer_flush_state may disrupt various state (such
* as the pipeline state) when flushing the clip stack, so should * 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) framebuffer->viewport_height == height)
return; return;
_cogl_journal_flush (); _cogl_framebuffer_flush_journal (framebuffer);
framebuffer->viewport_x = x; framebuffer->viewport_x = x;
framebuffer->viewport_y = y; framebuffer->viewport_y = y;
@ -361,6 +402,50 @@ _cogl_framebuffer_get_projection_stack (CoglFramebuffer *framebuffer)
return framebuffer->projection_stack; 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 static inline void
_cogl_framebuffer_init_bits (CoglFramebuffer *framebuffer) _cogl_framebuffer_init_bits (CoglFramebuffer *framebuffer)
{ {
@ -459,11 +544,9 @@ try_creating_fbo (CoglOffscreen *offscreen,
) )
return FALSE; return FALSE;
/* We are about to generate and bind a new fbo, so we pretend to change framebuffer /* We are about to generate and bind a new fbo, so we pretend to
* state so that the old framebuffer will be rebound again before drawing. * change framebuffer state so that the old framebuffer will be
* The framebuffer state can't be changed while their are active entries, so flush * rebound again before drawing. */
* first. */
_cogl_journal_flush ();
ctx->dirty_bound_framebuffer = 1; ctx->dirty_bound_framebuffer = 1;
/* Generate framebuffer */ /* Generate framebuffer */
@ -639,13 +722,17 @@ _cogl_offscreen_new_to_texture_full (CoglHandle texhandle,
if (fbo_created) if (fbo_created)
{ {
CoglOffscreen *ret;
_cogl_framebuffer_init (COGL_FRAMEBUFFER (offscreen), _cogl_framebuffer_init (COGL_FRAMEBUFFER (offscreen),
COGL_FRAMEBUFFER_TYPE_OFFSCREEN, COGL_FRAMEBUFFER_TYPE_OFFSCREEN,
cogl_texture_get_format (texhandle), cogl_texture_get_format (texhandle),
data.level_width, data.level_width,
data.level_height); 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 else
{ {
@ -782,16 +869,14 @@ _cogl_set_framebuffer_real (CoglFramebuffer *framebuffer)
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
cogl_flush ();
entry = (CoglFramebuffer **)&ctx->framebuffer_stack->data; entry = (CoglFramebuffer **)&ctx->framebuffer_stack->data;
ctx->dirty_bound_framebuffer = 1; ctx->dirty_bound_framebuffer = 1;
ctx->dirty_gl_viewport = 1; ctx->dirty_gl_viewport = 1;
if (framebuffer != COGL_INVALID_HANDLE) if (framebuffer)
cogl_object_ref (framebuffer); cogl_object_ref (framebuffer);
if (*entry != COGL_INVALID_HANDLE) if (*entry)
cogl_object_unref (*entry); cogl_object_unref (*entry);
*entry = framebuffer; *entry = framebuffer;
@ -844,8 +929,6 @@ cogl_push_framebuffer (CoglFramebuffer *buffer)
g_return_if_fail (_cogl_is_framebuffer (buffer)); g_return_if_fail (_cogl_is_framebuffer (buffer));
g_assert (ctx->framebuffer_stack); g_assert (ctx->framebuffer_stack);
cogl_flush ();
ctx->framebuffer_stack = ctx->framebuffer_stack =
g_slist_prepend (ctx->framebuffer_stack, COGL_INVALID_HANDLE); g_slist_prepend (ctx->framebuffer_stack, COGL_INVALID_HANDLE);

View File

@ -27,6 +27,16 @@
#include "cogl-handle.h" #include "cogl-handle.h"
#include "cogl-clip-stack.h" #include "cogl-clip-stack.h"
typedef struct _CoglJournal
{
CoglObject _parent;
GArray *entries;
GArray *vertices;
size_t needed_vbo_len;
} CoglJournal;
/* To improve batching of geometry when submitting vertices to OpenGL we /* To improve batching of geometry when submitting vertices to OpenGL we
* log the texture rectangles we want to draw to a journal, so when we * log the texture rectangles we want to draw to a journal, so when we
* later flush the journal we aim to batch data, and gl draw calls. */ * later flush the journal we aim to batch data, and gl draw calls. */
@ -43,8 +53,12 @@ typedef struct _CoglJournalEntry
* later. */ * later. */
} CoglJournalEntry; } CoglJournalEntry;
CoglJournal *
_cogl_journal_new (void);
void void
_cogl_journal_log_quad (const float *position, _cogl_journal_log_quad (CoglJournal *journal,
const float *position,
CoglPipeline *pipeline, CoglPipeline *pipeline,
int n_layers, int n_layers,
CoglHandle layer0_override_texture, CoglHandle layer0_override_texture,
@ -52,6 +66,7 @@ _cogl_journal_log_quad (const float *position,
unsigned int tex_coords_len); unsigned int tex_coords_len);
void void
_cogl_journal_flush (void); _cogl_journal_flush (CoglJournal *journal,
CoglFramebuffer *framebuffer);
#endif /* __COGL_JOURNAL_PRIVATE_H */ #endif /* __COGL_JOURNAL_PRIVATE_H */

View File

@ -92,6 +92,8 @@
typedef struct _CoglJournalFlushState typedef struct _CoglJournalFlushState
{ {
CoglJournal *journal;
CoglVertexArray *vertex_array; CoglVertexArray *vertex_array;
GArray *attributes; GArray *attributes;
int current_attribute; int current_attribute;
@ -115,6 +117,31 @@ typedef void (*CoglJournalBatchCallback) (CoglJournalEntry *start,
typedef gboolean (*CoglJournalBatchTest) (CoglJournalEntry *entry0, typedef gboolean (*CoglJournalBatchTest) (CoglJournalEntry *entry0,
CoglJournalEntry *entry1); CoglJournalEntry *entry1);
static void _cogl_journal_free (CoglJournal *journal);
COGL_OBJECT_DEFINE (Journal, journal);
static void
_cogl_journal_free (CoglJournal *journal)
{
if (journal->entries)
g_array_free (journal->entries, TRUE);
if (journal->vertices)
g_array_free (journal->vertices, TRUE);
g_slice_free (CoglJournal, journal);
}
CoglJournal *
_cogl_journal_new (void)
{
CoglJournal *journal = g_slice_new0 (CoglJournal);
journal->entries = g_array_new (FALSE, FALSE, sizeof (CoglJournalEntry));
journal->vertices = g_array_new (FALSE, FALSE, sizeof (float));
return _cogl_journal_object_new (journal);
}
static void static void
_cogl_journal_dump_logged_quad (guint8 *data, int n_layers) _cogl_journal_dump_logged_quad (guint8 *data, int n_layers)
{ {
@ -798,6 +825,7 @@ check_software_clip_for_batch (CoglJournalEntry *batch_start,
int batch_len, int batch_len,
CoglJournalFlushState *state) CoglJournalFlushState *state)
{ {
CoglJournal *journal = state->journal;
CoglClipStack *clip_stack, *clip_entry; CoglClipStack *clip_stack, *clip_entry;
int entry_num; int entry_num;
@ -914,7 +942,7 @@ check_software_clip_for_batch (CoglJournalEntry *batch_start,
for (entry_num = 0; entry_num < batch_len; entry_num++) for (entry_num = 0; entry_num < batch_len; entry_num++)
{ {
CoglJournalEntry *journal_entry = batch_start + entry_num; CoglJournalEntry *journal_entry = batch_start + entry_num;
float *verts = &g_array_index (ctx->logged_vertices, float, float *verts = &g_array_index (journal->vertices, float,
journal_entry->array_offset + 1); journal_entry->array_offset + 1);
ClipBounds *clip_bounds = &g_array_index (ctx->journal_clip_bounds, ClipBounds *clip_bounds = &g_array_index (ctx->journal_clip_bounds,
ClipBounds, entry_num); ClipBounds, entry_num);
@ -1137,11 +1165,11 @@ upload_vertices (const CoglJournalEntry *entries,
* is undefined. * is undefined.
*/ */
void void
_cogl_journal_flush (void) _cogl_journal_flush (CoglJournal *journal,
CoglFramebuffer *framebuffer)
{ {
CoglJournalFlushState state; CoglJournalFlushState state;
int i; int i;
CoglFramebuffer *framebuffer;
CoglMatrixStack *modelview_stack; CoglMatrixStack *modelview_stack;
COGL_STATIC_TIMER (flush_timer, COGL_STATIC_TIMER (flush_timer,
"Mainloop", /* parent */ "Mainloop", /* parent */
@ -1151,17 +1179,32 @@ _cogl_journal_flush (void)
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (ctx->journal->len == 0) if (journal->entries->len == 0)
return; return;
COGL_TIMER_START (_cogl_uprof_context, flush_timer); COGL_TIMER_START (_cogl_uprof_context, flush_timer);
/* The entries in this journal may depend on images in other
* framebuffers which may require that we flush the journals
* associated with those framebuffers before we can flush
* this journal... */
_cogl_framebuffer_flush_dependency_journals (framebuffer);
cogl_push_framebuffer (framebuffer);
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING)) if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING))
g_print ("BATCHING: journal len = %d\n", ctx->journal->len); g_print ("BATCHING: journal len = %d\n", journal->entries->len);
/* NB: the journal deals with flushing the modelview stack and clip
state manually */
_cogl_framebuffer_flush_state (framebuffer,
COGL_FRAMEBUFFER_FLUSH_SKIP_MODELVIEW |
COGL_FRAMEBUFFER_FLUSH_SKIP_CLIP_STATE);
state.journal = journal;
state.attributes = ctx->journal_flush_attributes_array; state.attributes = ctx->journal_flush_attributes_array;
framebuffer = _cogl_get_framebuffer ();
modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer);
state.modelview_stack = modelview_stack; state.modelview_stack = modelview_stack;
state.projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); state.projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer);
@ -1173,8 +1216,8 @@ _cogl_journal_flush (void)
separate walk of the journal because we can modify entries and separate walk of the journal because we can modify entries and
this may end up joining together clip stack batches in the next this may end up joining together clip stack batches in the next
iteration. */ iteration. */
batch_and_call ((CoglJournalEntry *)ctx->journal->data, /* first entry */ batch_and_call ((CoglJournalEntry *)journal->entries->data, /* first entry */
ctx->journal->len, /* max number of entries to consider */ journal->entries->len, /* max number of entries to consider */
compare_entry_clip_stacks, compare_entry_clip_stacks,
_cogl_journal_check_software_clip, /* callback */ _cogl_journal_check_software_clip, /* callback */
&state); /* data */ &state); /* data */
@ -1182,11 +1225,11 @@ _cogl_journal_flush (void)
/* We upload the vertices after the clip stack pass in case it /* We upload the vertices after the clip stack pass in case it
modifies the entries */ modifies the entries */
state.vertex_array = upload_vertices (&g_array_index (ctx->journal, state.vertex_array = upload_vertices (&g_array_index (journal->entries,
CoglJournalEntry, 0), CoglJournalEntry, 0),
ctx->journal->len, journal->entries->len,
ctx->journal_needed_vbo_len, journal->needed_vbo_len,
ctx->logged_vertices); journal->vertices);
state.array_offset = 0; state.array_offset = 0;
/* batch_and_call() batches a list of journal entries according to some /* batch_and_call() batches a list of journal entries according to some
@ -1212,8 +1255,8 @@ _cogl_journal_flush (void)
* Note: Splitting by modelview changes is skipped when are doing the * Note: Splitting by modelview changes is skipped when are doing the
* vertex transformation in software at log time. * vertex transformation in software at log time.
*/ */
batch_and_call ((CoglJournalEntry *)ctx->journal->data, /* first entry */ batch_and_call ((CoglJournalEntry *)journal->entries->data, /* first entry */
ctx->journal->len, /* max number of entries to consider */ journal->entries->len, /* max number of entries to consider */
compare_entry_clip_stacks, compare_entry_clip_stacks,
_cogl_journal_flush_clip_stacks_and_entries, /* callback */ _cogl_journal_flush_clip_stacks_and_entries, /* callback */
&state); /* data */ &state); /* data */
@ -1225,40 +1268,42 @@ _cogl_journal_flush (void)
cogl_object_unref (state.vertex_array); cogl_object_unref (state.vertex_array);
for (i = 0; i < ctx->journal->len; i++) for (i = 0; i < journal->entries->len; i++)
{ {
CoglJournalEntry *entry = CoglJournalEntry *entry =
&g_array_index (ctx->journal, CoglJournalEntry, i); &g_array_index (journal->entries, CoglJournalEntry, i);
_cogl_pipeline_journal_unref (entry->pipeline); _cogl_pipeline_journal_unref (entry->pipeline);
_cogl_clip_stack_unref (entry->clip_stack); _cogl_clip_stack_unref (entry->clip_stack);
} }
g_array_set_size (ctx->journal, 0); g_array_set_size (journal->entries, 0);
g_array_set_size (ctx->logged_vertices, 0); g_array_set_size (journal->vertices, 0);
journal->needed_vbo_len = 0;
cogl_pop_framebuffer ();
COGL_TIMER_STOP (_cogl_uprof_context, flush_timer); COGL_TIMER_STOP (_cogl_uprof_context, flush_timer);
} }
static void static gboolean
_cogl_journal_init (void) add_framebuffer_deps_cb (CoglPipelineLayer *layer, void *user_data)
{ {
_COGL_GET_CONTEXT (ctx, NO_RETVAL); CoglFramebuffer *framebuffer = user_data;
CoglHandle texture = _cogl_pipeline_layer_get_texture_real (layer);
const GList *l;
/* Here we flush anything that we know must remain constant until the if (!texture)
* next the the journal is flushed. Note: This lets up flush things return TRUE;
* that themselves depend on the journal, such as clip state. */
/* NB: the journal deals with flushing the modelview stack and clip for (l = _cogl_texture_get_associated_framebuffers (texture); l; l = l->next)
state manually */ _cogl_framebuffer_add_dependency (framebuffer, l->data);
_cogl_framebuffer_flush_state (_cogl_get_framebuffer (),
COGL_FRAMEBUFFER_FLUSH_SKIP_MODELVIEW |
COGL_FRAMEBUFFER_FLUSH_SKIP_CLIP_STATE);
ctx->journal_needed_vbo_len = 0; return TRUE;
} }
void void
_cogl_journal_log_quad (const float *position, _cogl_journal_log_quad (CoglJournal *journal,
const float *position,
CoglPipeline *pipeline, CoglPipeline *pipeline,
int n_layers, int n_layers,
CoglHandle layer0_override_texture, CoglHandle layer0_override_texture,
@ -1267,7 +1312,7 @@ _cogl_journal_log_quad (const float *position,
{ {
gsize stride; gsize stride;
int next_vert; int next_vert;
GLfloat *v; float *v;
int i; int i;
int next_entry; int next_entry;
guint32 disable_layers; guint32 disable_layers;
@ -1284,9 +1329,6 @@ _cogl_journal_log_quad (const float *position,
COGL_TIMER_START (_cogl_uprof_context, log_timer); COGL_TIMER_START (_cogl_uprof_context, log_timer);
if (ctx->logged_vertices->len == 0)
_cogl_journal_init ();
/* The vertex data is logged into a separate array. The data needs /* The vertex data is logged into a separate array. The data needs
to be copied into a vertex array before it's given to GL so we to be copied into a vertex array before it's given to GL so we
only store two vertices per quad and expand it to four while only store two vertices per quad and expand it to four while
@ -1296,15 +1338,14 @@ _cogl_journal_log_quad (const float *position,
* about how we pack our vertex data */ * about how we pack our vertex data */
stride = GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (n_layers); stride = GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (n_layers);
next_vert = ctx->logged_vertices->len; next_vert = journal->vertices->len;
g_array_set_size (ctx->logged_vertices, next_vert + 2 * stride + 1); g_array_set_size (journal->vertices, next_vert + 2 * stride + 1);
v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert); v = &g_array_index (journal->vertices, float, next_vert);
/* We calculate the needed size of the vbo as we go because it /* We calculate the needed size of the vbo as we go because it
depends on the number of layers in each entry and it's not easy depends on the number of layers in each entry and it's not easy
calculate based on the length of the logged vertices array */ calculate based on the length of the logged vertices array */
ctx->journal_needed_vbo_len += journal->needed_vbo_len += GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4;
GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4;
/* XXX: All the jumping around to fill in this strided buffer doesn't /* XXX: All the jumping around to fill in this strided buffer doesn't
* seem ideal. */ * seem ideal. */
@ -1330,13 +1371,13 @@ _cogl_journal_log_quad (const float *position,
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_JOURNAL)) if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_JOURNAL))
{ {
g_print ("Logged new quad:\n"); g_print ("Logged new quad:\n");
v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert); v = &g_array_index (journal->vertices, float, next_vert);
_cogl_journal_dump_logged_quad ((guint8 *)v, n_layers); _cogl_journal_dump_logged_quad ((guint8 *)v, n_layers);
} }
next_entry = ctx->journal->len; next_entry = journal->entries->len;
g_array_set_size (ctx->journal, next_entry + 1); g_array_set_size (journal->entries, next_entry + 1);
entry = &g_array_index (ctx->journal, CoglJournalEntry, next_entry); entry = &g_array_index (journal->entries, CoglJournalEntry, next_entry);
entry->n_layers = n_layers; entry->n_layers = n_layers;
entry->array_offset = next_vert; entry->array_offset = next_vert;
@ -1379,8 +1420,17 @@ _cogl_journal_log_quad (const float *position,
cogl_get_modelview_matrix (&entry->model_view); cogl_get_modelview_matrix (&entry->model_view);
_cogl_pipeline_foreach_layer_internal (pipeline,
add_framebuffer_deps_cb,
_cogl_get_framebuffer ());
/* XXX: It doesn't feel very nice that in this case we just assume
* that the journal is associated with the current framebuffer. I
* think a journal->framebuffer reference would seem nicer here but
* the reason we don't have that currently is that it would
* introduce a circular reference. */
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING)) if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING))
_cogl_journal_flush (); _cogl_framebuffer_flush_journal (_cogl_get_framebuffer ());
COGL_TIMER_STOP (_cogl_uprof_context, log_timer); COGL_TIMER_STOP (_cogl_uprof_context, log_timer);
} }

View File

@ -1204,6 +1204,9 @@ _cogl_pipeline_layer_get_type (CoglPipelineLayer *layer);
CoglHandle CoglHandle
_cogl_pipeline_layer_get_texture (CoglPipelineLayer *layer); _cogl_pipeline_layer_get_texture (CoglPipelineLayer *layer);
CoglHandle
_cogl_pipeline_layer_get_texture_real (CoglPipelineLayer *layer);
CoglPipelineFilter CoglPipelineFilter
_cogl_pipeline_layer_get_min_filter (CoglPipelineLayer *layer); _cogl_pipeline_layer_get_min_filter (CoglPipelineLayer *layer);

View File

@ -1240,7 +1240,12 @@ _cogl_pipeline_pre_change_notify (CoglPipeline *pipeline,
} }
if (!skip_journal_flush) if (!skip_journal_flush)
_cogl_journal_flush (); {
/* XXX: note we use cogl_flush() not _cogl_flush_journal() so
* we will flush *all* known journals that might reference the
* current pipeline. */
cogl_flush ();
}
} }
/* The fixed function backend has no private state and can't /* The fixed function backend has no private state and can't

View File

@ -78,6 +78,7 @@ log_quad_sub_textures_cb (CoglHandle texture_handle,
void *user_data) void *user_data)
{ {
TextureSlicedQuadState *state = user_data; TextureSlicedQuadState *state = user_data;
CoglFramebuffer *framebuffer = _cogl_get_framebuffer ();
CoglHandle texture_override; CoglHandle texture_override;
float quad_coords[4]; float quad_coords[4];
@ -120,7 +121,8 @@ log_quad_sub_textures_cb (CoglHandle texture_handle,
else else
texture_override = texture_handle; texture_override = texture_handle;
_cogl_journal_log_quad (quad_coords, _cogl_journal_log_quad (framebuffer->journal,
quad_coords,
state->pipeline, state->pipeline,
1, /* one layer */ 1, /* one layer */
texture_override, /* replace the layer0 texture */ texture_override, /* replace the layer0 texture */
@ -518,6 +520,7 @@ _cogl_multitexture_quad_single_primitive (const float *position,
int n_layers = cogl_pipeline_get_n_layers (pipeline); int n_layers = cogl_pipeline_get_n_layers (pipeline);
ValidateTexCoordsState state; ValidateTexCoordsState state;
float *final_tex_coords = alloca (sizeof (float) * 4 * n_layers); float *final_tex_coords = alloca (sizeof (float) * 4 * n_layers);
CoglFramebuffer *framebuffer;
_COGL_GET_CONTEXT (ctx, FALSE); _COGL_GET_CONTEXT (ctx, FALSE);
@ -539,7 +542,9 @@ _cogl_multitexture_quad_single_primitive (const float *position,
if (state.override_pipeline) if (state.override_pipeline)
pipeline = state.override_pipeline; pipeline = state.override_pipeline;
_cogl_journal_log_quad (position, framebuffer = _cogl_get_framebuffer ();
_cogl_journal_log_quad (framebuffer->journal,
position,
pipeline, pipeline,
n_layers, n_layers,
COGL_INVALID_HANDLE, /* no texture override */ COGL_INVALID_HANDLE, /* no texture override */

View File

@ -267,7 +267,8 @@ _cogl_sub_texture_new (CoglHandle next_texture,
sub_tex = g_new (CoglSubTexture, 1); sub_tex = g_new (CoglSubTexture, 1);
tex = COGL_TEXTURE (sub_tex); tex = COGL_TEXTURE (sub_tex);
tex->vtable = &cogl_sub_texture_vtable;
_cogl_texture_init (tex, &cogl_sub_texture_vtable);
/* If the next texture is also a sub texture we can avoid one level /* If the next texture is also a sub texture we can avoid one level
of indirection by referencing the full texture of that texture of indirection by referencing the full texture of that texture

View File

@ -907,7 +907,7 @@ _cogl_texture_2d_sliced_init_base (CoglTexture2DSliced *tex_2ds,
{ {
CoglTexture *tex = COGL_TEXTURE (tex_2ds); CoglTexture *tex = COGL_TEXTURE (tex_2ds);
tex->vtable = &cogl_texture_2d_sliced_vtable; _cogl_texture_init (tex, &cogl_texture_2d_sliced_vtable);
tex_2ds->slice_x_spans = NULL; tex_2ds->slice_x_spans = NULL;
tex_2ds->slice_y_spans = NULL; tex_2ds->slice_y_spans = NULL;

View File

@ -197,7 +197,7 @@ _cogl_texture_2d_create_base (unsigned int width,
CoglTexture2D *tex_2d = g_new (CoglTexture2D, 1); CoglTexture2D *tex_2d = g_new (CoglTexture2D, 1);
CoglTexture *tex = COGL_TEXTURE (tex_2d); CoglTexture *tex = COGL_TEXTURE (tex_2d);
tex->vtable = &cogl_texture_2d_vtable; _cogl_texture_init (tex, &cogl_texture_2d_vtable);
tex_2d->width = width; tex_2d->width = width;
tex_2d->height = height; tex_2d->height = height;

View File

@ -178,7 +178,7 @@ _cogl_texture_3d_create_base (unsigned int width,
CoglTexture3D *tex_3d = g_new (CoglTexture3D, 1); CoglTexture3D *tex_3d = g_new (CoglTexture3D, 1);
CoglTexture *tex = COGL_TEXTURE (tex_3d); CoglTexture *tex = COGL_TEXTURE (tex_3d);
tex->vtable = &cogl_texture_3d_vtable; _cogl_texture_init (tex, &cogl_texture_3d_vtable);
tex_3d->width = width; tex_3d->width = width;
tex_3d->height = height; tex_3d->height = height;

View File

@ -135,6 +135,7 @@ struct _CoglTextureVtable
struct _CoglTexture struct _CoglTexture
{ {
CoglHandleObject _parent; CoglHandleObject _parent;
GList *framebuffers;
const CoglTextureVtable *vtable; const CoglTextureVtable *vtable;
}; };
@ -165,6 +166,10 @@ struct _CoglTexturePixel
guint8 data[4]; guint8 data[4];
}; };
void
_cogl_texture_init (CoglTexture *texture,
const CoglTextureVtable *vtable);
void void
_cogl_texture_free (CoglTexture *texture); _cogl_texture_free (CoglTexture *texture);
@ -285,4 +290,14 @@ _cogl_texture_set_region_from_bitmap (CoglHandle handle,
unsigned int dst_height, unsigned int dst_height,
CoglBitmap *bmp); CoglBitmap *bmp);
void
_cogl_texture_associate_framebuffer (CoglHandle handle,
CoglFramebuffer *framebuffer);
const GList *
_cogl_texture_get_associated_framebuffers (CoglHandle handle);
void
_cogl_texture_flush_journal_rendering (CoglHandle handle);
#endif /* __COGL_TEXTURE_PRIVATE_H */ #endif /* __COGL_TEXTURE_PRIVATE_H */

View File

@ -222,7 +222,7 @@ _cogl_texture_rectangle_create_base (unsigned int width,
CoglTextureRectangle *tex_rect = g_new (CoglTextureRectangle, 1); CoglTextureRectangle *tex_rect = g_new (CoglTextureRectangle, 1);
CoglTexture *tex = COGL_TEXTURE (tex_rect); CoglTexture *tex = COGL_TEXTURE (tex_rect);
tex->vtable = &cogl_texture_rectangle_vtable; _cogl_texture_init (tex, &cogl_texture_rectangle_vtable);
tex_rect->width = width; tex_rect->width = width;
tex_rect->height = height; tex_rect->height = height;

View File

@ -49,6 +49,7 @@
#include "cogl-pipeline.h" #include "cogl-pipeline.h"
#include "cogl-context.h" #include "cogl-context.h"
#include "cogl-handle.h" #include "cogl-handle.h"
#include "cogl-object-private.h"
#include "cogl-primitives.h" #include "cogl-primitives.h"
#include "cogl-framebuffer-private.h" #include "cogl-framebuffer-private.h"
@ -118,6 +119,14 @@ cogl_texture_unref (CoglHandle handle)
cogl_handle_unref (handle); cogl_handle_unref (handle);
} }
void
_cogl_texture_init (CoglTexture *texture,
const CoglTextureVtable *vtable)
{
texture->vtable = vtable;
texture->framebuffers = NULL;
}
void void
_cogl_texture_free (CoglTexture *texture) _cogl_texture_free (CoglTexture *texture)
{ {
@ -1473,3 +1482,52 @@ cogl_texture_get_data (CoglHandle handle,
return byte_size; return byte_size;
} }
static void
_cogl_texture_framebuffer_destroy_cb (void *user_data,
void *instance)
{
CoglTexture *tex = user_data;
CoglFramebuffer *framebuffer = instance;
tex->framebuffers = g_list_remove (tex->framebuffers, framebuffer);
}
void
_cogl_texture_associate_framebuffer (CoglHandle handle,
CoglFramebuffer *framebuffer)
{
CoglTexture *tex = COGL_TEXTURE (handle);
static CoglUserDataKey framebuffer_destroy_notify_key;
/* Note: we don't take a reference on the framebuffer here because
* that would introduce a circular reference. */
tex->framebuffers = g_list_prepend (tex->framebuffers, framebuffer);
/* Since we haven't taken a reference on the framebuffer we setup
* some private data so we will be notified if it is destroyed... */
_cogl_object_set_user_data (COGL_OBJECT (framebuffer),
&framebuffer_destroy_notify_key,
tex,
_cogl_texture_framebuffer_destroy_cb);
}
const GList *
_cogl_texture_get_associated_framebuffers (CoglHandle handle)
{
CoglTexture *tex = COGL_TEXTURE (handle);
return tex->framebuffers;
}
void
_cogl_texture_flush_journal_rendering (CoglHandle handle)
{
CoglTexture *tex = COGL_TEXTURE (handle);
GList *l;
/* It could be that a referenced texture is part of a framebuffer
* which has an associated journal that must be flushed before it
* can be sampled from by the current primitive... */
for (l = tex->framebuffers; l; l = l->next)
_cogl_framebuffer_flush_journal (l->data);
}

View File

@ -386,6 +386,8 @@ validate_layer_cb (CoglPipeline *pipeline,
if (texture == COGL_INVALID_HANDLE) if (texture == COGL_INVALID_HANDLE)
goto validated; goto validated;
_cogl_texture_flush_journal_rendering (texture);
/* Give the texture a chance to know that we're rendering /* Give the texture a chance to know that we're rendering
non-quad shaped primitives. If the texture is in an atlas it non-quad shaped primitives. If the texture is in an atlas it
will be migrated */ will be migrated */
@ -1050,7 +1052,10 @@ flush_state (CoglDrawFlags flags,
ValidateLayerState *state) ValidateLayerState *state)
{ {
if (!(flags & COGL_DRAW_SKIP_JOURNAL_FLUSH)) if (!(flags & COGL_DRAW_SKIP_JOURNAL_FLUSH))
_cogl_journal_flush (); {
CoglFramebuffer *framebuffer = _cogl_get_framebuffer ();
_cogl_journal_flush (framebuffer->journal, framebuffer);
}
state->unit = 0; state->unit = 0;
state->options.flags = 0; state->options.flags = 0;

View File

@ -282,7 +282,7 @@ cogl_set_backface_culling_enabled (gboolean setting)
return; return;
/* Currently the journal can't track changes to backface culling state... */ /* Currently the journal can't track changes to backface culling state... */
_cogl_journal_flush (); _cogl_framebuffer_flush_journal (_cogl_get_framebuffer ());
ctx->enable_backface_culling = setting; ctx->enable_backface_culling = setting;
} }
@ -479,7 +479,12 @@ cogl_disable_fog (void)
void void
cogl_flush (void) cogl_flush (void)
{ {
_cogl_journal_flush (); GList *l;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
for (l = ctx->framebuffers; l; l = l->next)
_cogl_framebuffer_flush_journal (l->data);
} }
void void
@ -505,8 +510,15 @@ _cogl_read_pixels_with_rowstride (int x,
g_return_if_fail (source == COGL_READ_PIXELS_COLOR_BUFFER); g_return_if_fail (source == COGL_READ_PIXELS_COLOR_BUFFER);
/* make sure any batched primitives get emitted to the GL driver before /* make sure any batched primitives get emitted to the GL driver
* issuing our read pixels... */ * before issuing our read pixels...
*
* XXX: Note we currently use cogl_flush to ensure *all* journals
* are flushed here and not _cogl_journal_flush because we don't
* track the dependencies between framebuffers so we don't know if
* the current framebuffer depends on the contents of other
* framebuffers which could also have associated journal entries.
*/
cogl_flush (); cogl_flush ();
framebuffer = _cogl_get_framebuffer (); framebuffer = _cogl_get_framebuffer ();

View File

@ -459,17 +459,21 @@ _cogl_add_path_to_stencil_buffer (CoglPath *path,
void void
cogl2_path_fill (CoglPath *path) cogl2_path_fill (CoglPath *path)
{ {
CoglFramebuffer *framebuffer;
g_return_if_fail (cogl_is_path (path)); g_return_if_fail (cogl_is_path (path));
if (path->data->path_nodes->len == 0) if (path->data->path_nodes->len == 0)
return; return;
_cogl_journal_flush (); framebuffer = _cogl_get_framebuffer ();
_cogl_framebuffer_flush_journal (framebuffer);
/* NB: _cogl_framebuffer_flush_state may disrupt various state (such /* NB: _cogl_framebuffer_flush_state may disrupt various state (such
* as the pipeline state) when flushing the clip stack, so should * as the pipeline state) when flushing the clip stack, so should
* always be done first when preparing to draw. */ * always be done first when preparing to draw. */
_cogl_framebuffer_flush_state (_cogl_get_framebuffer (), 0); _cogl_framebuffer_flush_state (framebuffer, 0);
_cogl_path_fill_nodes (path); _cogl_path_fill_nodes (path);
} }