From 1a5a4df3261e1674efb2b53aa738d33fdb53af6c Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 6 Jan 2011 13:25:45 +0000 Subject: [PATCH] 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. --- cogl/cogl-atlas-texture.c | 22 ++--- cogl/cogl-clip-state.c | 5 +- cogl/cogl-context.c | 6 -- cogl/cogl-context.h | 12 ++- cogl/cogl-framebuffer-private.h | 23 ++++++ cogl/cogl-framebuffer.c | 111 +++++++++++++++++++++---- cogl/cogl-journal-private.h | 19 ++++- cogl/cogl-journal.c | 140 ++++++++++++++++++++++---------- cogl/cogl-pipeline-private.h | 3 + cogl/cogl-pipeline.c | 7 +- cogl/cogl-primitives.c | 9 +- cogl/cogl-sub-texture.c | 3 +- cogl/cogl-texture-2d-sliced.c | 2 +- cogl/cogl-texture-2d.c | 2 +- cogl/cogl-texture-3d.c | 2 +- cogl/cogl-texture-private.h | 15 ++++ cogl/cogl-texture-rectangle.c | 2 +- cogl/cogl-texture.c | 58 +++++++++++++ cogl/cogl-vertex-attribute.c | 7 +- cogl/cogl.c | 20 ++++- cogl/cogl2-path.c | 8 +- 21 files changed, 375 insertions(+), 101 deletions(-) diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c index 51b2ad42e..aa025b99b 100644 --- a/cogl/cogl-atlas-texture.c +++ b/cogl/cogl-atlas-texture.c @@ -98,14 +98,14 @@ _cogl_atlas_texture_reorganize_cb (void *data) { CoglAtlas *atlas = data; - /* We don't know if any pipelines may currently be referenced in - * the journal that depend on the current underlying GL texture - * storage so we flush the journal before migrating. + /* We don't know if any journal entries currently depend on OpenGL + * texture coordinates that would be invalidated by reorganizing + * this atlas so we flush all journals before migrating. * * We are assuming that texture atlas migration never happens * during a flush so we don't have to consider recursion here. */ - _cogl_journal_flush (); + cogl_flush (); if (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"); - /* We don't know if any pipelines may currently be referenced in - * the journal that depend on the current underlying GL texture - * storage so we flush the journal before migrating. + /* We don't know if any journal entries currently depend on + * OpenGL texture coordinates that would be invalidated by + * migrating textures in this atlas so we flush all journals + * before migrating. * * We are assuming that texture atlas migration never happens * 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 * 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 */ atlas_tex = g_new (CoglAtlasTexture, 1); + _cogl_texture_init (COGL_TEXTURE (atlas_tex), + &cogl_atlas_texture_vtable); + atlas_tex->sub_texture = COGL_INVALID_HANDLE; /* 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); return COGL_INVALID_HANDLE; } - - atlas_tex->_parent.vtable = &cogl_atlas_texture_vtable; atlas_tex->format = internal_format; atlas_tex->atlas = atlas; diff --git a/cogl/cogl-clip-state.c b/cogl/cogl-clip-state.c index 1fc31f1d4..2efe2ec98 100644 --- a/cogl/cogl-clip-state.c +++ b/cogl/cogl-clip-state.c @@ -169,14 +169,15 @@ _cogl_clip_state_flush (CoglClipState *clip_state) void cogl_clip_ensure (void) { + CoglFramebuffer *framebuffer = _cogl_get_framebuffer (); 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 flushed. This function may be being called by an external application however so it makes sense to flush the journal here */ - _cogl_journal_flush (); + _cogl_framebuffer_flush_journal (framebuffer); _cogl_clip_state_flush (clip_state); } diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index c2492beaa..28eac36b9 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -166,8 +166,6 @@ cogl_create_context (void) _context->default_gl_texture_2d_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 = g_array_new (TRUE, FALSE, sizeof (CoglVertexAttribute *)); _context->journal_clip_bounds = NULL; @@ -333,10 +331,6 @@ _cogl_destroy_context (void) if (_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) g_array_free (_context->journal_flush_attributes_array, TRUE); if (_context->journal_clip_bounds) diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h index 31f129fc2..8c0161fab 100644 --- a/cogl/cogl-context.h +++ b/cogl/cogl-context.h @@ -109,14 +109,12 @@ typedef struct CoglHandle default_gl_texture_2d_tex; CoglHandle default_gl_texture_rect_tex; - /* Batching geometry... */ - /* We journal the texture rectangles we want to submit to OpenGL so - * we have an oppertunity to optimise the final order so that we - * can batch things together. */ - GArray *journal; - GArray *logged_vertices; + /* Central list of all framebuffers so all journals can be flushed + * at any time. */ + GList *framebuffers; + + /* Global journal buffers */ GArray *journal_flush_attributes_array; - size_t journal_needed_vbo_len; GArray *journal_clip_bounds; GArray *polygon_vertices; diff --git a/cogl/cogl-framebuffer-private.h b/cogl/cogl-framebuffer-private.h index 2e3b1995e..05199a8e1 100644 --- a/cogl/cogl-framebuffer-private.h +++ b/cogl/cogl-framebuffer-private.h @@ -27,6 +27,7 @@ #include "cogl-handle.h" #include "cogl-matrix-stack.h" #include "cogl-clip-state.h" +#include "cogl-journal-private.h" typedef enum _CoglFramebufferType { COGL_FRAMEBUFFER_TYPE_ONSCREEN, @@ -57,6 +58,15 @@ struct _CoglFramebuffer int blue_bits; int green_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)) @@ -144,6 +154,19 @@ _cogl_framebuffer_get_modelview_stack (CoglFramebuffer *framebuffer); CoglMatrixStack * _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 { /* XXX: When using this, that imples you are going to manually load the diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c index 9d310c128..7a6a564e7 100644 --- a/cogl/cogl-framebuffer.c +++ b/cogl/cogl-framebuffer.c @@ -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); diff --git a/cogl/cogl-journal-private.h b/cogl/cogl-journal-private.h index a34a5263e..754a2ecd7 100644 --- a/cogl/cogl-journal-private.h +++ b/cogl/cogl-journal-private.h @@ -27,6 +27,16 @@ #include "cogl-handle.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 * 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. */ @@ -43,8 +53,12 @@ typedef struct _CoglJournalEntry * later. */ } CoglJournalEntry; +CoglJournal * +_cogl_journal_new (void); + void -_cogl_journal_log_quad (const float *position, +_cogl_journal_log_quad (CoglJournal *journal, + const float *position, CoglPipeline *pipeline, int n_layers, CoglHandle layer0_override_texture, @@ -52,6 +66,7 @@ _cogl_journal_log_quad (const float *position, unsigned int tex_coords_len); void -_cogl_journal_flush (void); +_cogl_journal_flush (CoglJournal *journal, + CoglFramebuffer *framebuffer); #endif /* __COGL_JOURNAL_PRIVATE_H */ diff --git a/cogl/cogl-journal.c b/cogl/cogl-journal.c index bccae24c7..783cdbda7 100644 --- a/cogl/cogl-journal.c +++ b/cogl/cogl-journal.c @@ -92,6 +92,8 @@ typedef struct _CoglJournalFlushState { + CoglJournal *journal; + CoglVertexArray *vertex_array; GArray *attributes; int current_attribute; @@ -115,6 +117,31 @@ typedef void (*CoglJournalBatchCallback) (CoglJournalEntry *start, typedef gboolean (*CoglJournalBatchTest) (CoglJournalEntry *entry0, 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 _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, CoglJournalFlushState *state) { + CoglJournal *journal = state->journal; CoglClipStack *clip_stack, *clip_entry; 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++) { 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); ClipBounds *clip_bounds = &g_array_index (ctx->journal_clip_bounds, ClipBounds, entry_num); @@ -1137,11 +1165,11 @@ upload_vertices (const CoglJournalEntry *entries, * is undefined. */ void -_cogl_journal_flush (void) +_cogl_journal_flush (CoglJournal *journal, + CoglFramebuffer *framebuffer) { CoglJournalFlushState state; int i; - CoglFramebuffer *framebuffer; CoglMatrixStack *modelview_stack; COGL_STATIC_TIMER (flush_timer, "Mainloop", /* parent */ @@ -1151,17 +1179,32 @@ _cogl_journal_flush (void) _COGL_GET_CONTEXT (ctx, NO_RETVAL); - if (ctx->journal->len == 0) + if (journal->entries->len == 0) return; 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)) - 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; - framebuffer = _cogl_get_framebuffer (); modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); state.modelview_stack = modelview_stack; 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 this may end up joining together clip stack batches in the next iteration. */ - batch_and_call ((CoglJournalEntry *)ctx->journal->data, /* first entry */ - ctx->journal->len, /* max number of entries to consider */ + batch_and_call ((CoglJournalEntry *)journal->entries->data, /* first entry */ + journal->entries->len, /* max number of entries to consider */ compare_entry_clip_stacks, _cogl_journal_check_software_clip, /* callback */ &state); /* data */ @@ -1182,11 +1225,11 @@ _cogl_journal_flush (void) /* We upload the vertices after the clip stack pass in case it 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), - ctx->journal->len, - ctx->journal_needed_vbo_len, - ctx->logged_vertices); + journal->entries->len, + journal->needed_vbo_len, + journal->vertices); state.array_offset = 0; /* 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 * vertex transformation in software at log time. */ - batch_and_call ((CoglJournalEntry *)ctx->journal->data, /* first entry */ - ctx->journal->len, /* max number of entries to consider */ + batch_and_call ((CoglJournalEntry *)journal->entries->data, /* first entry */ + journal->entries->len, /* max number of entries to consider */ compare_entry_clip_stacks, _cogl_journal_flush_clip_stacks_and_entries, /* callback */ &state); /* data */ @@ -1225,40 +1268,42 @@ _cogl_journal_flush (void) cogl_object_unref (state.vertex_array); - for (i = 0; i < ctx->journal->len; i++) + for (i = 0; i < journal->entries->len; i++) { CoglJournalEntry *entry = - &g_array_index (ctx->journal, CoglJournalEntry, i); + &g_array_index (journal->entries, CoglJournalEntry, i); _cogl_pipeline_journal_unref (entry->pipeline); _cogl_clip_stack_unref (entry->clip_stack); } - g_array_set_size (ctx->journal, 0); - g_array_set_size (ctx->logged_vertices, 0); + g_array_set_size (journal->entries, 0); + g_array_set_size (journal->vertices, 0); + journal->needed_vbo_len = 0; + + cogl_pop_framebuffer (); COGL_TIMER_STOP (_cogl_uprof_context, flush_timer); } -static void -_cogl_journal_init (void) +static gboolean +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 - * next the the journal is flushed. Note: This lets up flush things - * that themselves depend on the journal, such as clip state. */ + if (!texture) + return TRUE; - /* NB: the journal deals with flushing the modelview stack and clip - state manually */ - _cogl_framebuffer_flush_state (_cogl_get_framebuffer (), - COGL_FRAMEBUFFER_FLUSH_SKIP_MODELVIEW | - COGL_FRAMEBUFFER_FLUSH_SKIP_CLIP_STATE); + for (l = _cogl_texture_get_associated_framebuffers (texture); l; l = l->next) + _cogl_framebuffer_add_dependency (framebuffer, l->data); - ctx->journal_needed_vbo_len = 0; + return TRUE; } void -_cogl_journal_log_quad (const float *position, +_cogl_journal_log_quad (CoglJournal *journal, + const float *position, CoglPipeline *pipeline, int n_layers, CoglHandle layer0_override_texture, @@ -1267,7 +1312,7 @@ _cogl_journal_log_quad (const float *position, { gsize stride; int next_vert; - GLfloat *v; + float *v; int i; int next_entry; guint32 disable_layers; @@ -1284,9 +1329,6 @@ _cogl_journal_log_quad (const float *position, 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 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 @@ -1296,15 +1338,14 @@ _cogl_journal_log_quad (const float *position, * about how we pack our vertex data */ stride = GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (n_layers); - next_vert = ctx->logged_vertices->len; - g_array_set_size (ctx->logged_vertices, next_vert + 2 * stride + 1); - v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert); + next_vert = journal->vertices->len; + g_array_set_size (journal->vertices, next_vert + 2 * stride + 1); + v = &g_array_index (journal->vertices, float, next_vert); /* 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 calculate based on the length of the logged vertices array */ - ctx->journal_needed_vbo_len += - GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4; + journal->needed_vbo_len += GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4; /* XXX: All the jumping around to fill in this strided buffer doesn't * seem ideal. */ @@ -1330,13 +1371,13 @@ _cogl_journal_log_quad (const float *position, if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_JOURNAL)) { 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); } - next_entry = ctx->journal->len; - g_array_set_size (ctx->journal, next_entry + 1); - entry = &g_array_index (ctx->journal, CoglJournalEntry, next_entry); + next_entry = journal->entries->len; + g_array_set_size (journal->entries, next_entry + 1); + entry = &g_array_index (journal->entries, CoglJournalEntry, next_entry); entry->n_layers = n_layers; entry->array_offset = next_vert; @@ -1379,8 +1420,17 @@ _cogl_journal_log_quad (const float *position, 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)) - _cogl_journal_flush (); + _cogl_framebuffer_flush_journal (_cogl_get_framebuffer ()); COGL_TIMER_STOP (_cogl_uprof_context, log_timer); } diff --git a/cogl/cogl-pipeline-private.h b/cogl/cogl-pipeline-private.h index bbfbc5a26..47007fb38 100644 --- a/cogl/cogl-pipeline-private.h +++ b/cogl/cogl-pipeline-private.h @@ -1204,6 +1204,9 @@ _cogl_pipeline_layer_get_type (CoglPipelineLayer *layer); CoglHandle _cogl_pipeline_layer_get_texture (CoglPipelineLayer *layer); +CoglHandle +_cogl_pipeline_layer_get_texture_real (CoglPipelineLayer *layer); + CoglPipelineFilter _cogl_pipeline_layer_get_min_filter (CoglPipelineLayer *layer); diff --git a/cogl/cogl-pipeline.c b/cogl/cogl-pipeline.c index 91f0fbf1e..98c94ff76 100644 --- a/cogl/cogl-pipeline.c +++ b/cogl/cogl-pipeline.c @@ -1240,7 +1240,12 @@ _cogl_pipeline_pre_change_notify (CoglPipeline *pipeline, } 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 diff --git a/cogl/cogl-primitives.c b/cogl/cogl-primitives.c index 392a02b28..c88470cb9 100644 --- a/cogl/cogl-primitives.c +++ b/cogl/cogl-primitives.c @@ -78,6 +78,7 @@ log_quad_sub_textures_cb (CoglHandle texture_handle, void *user_data) { TextureSlicedQuadState *state = user_data; + CoglFramebuffer *framebuffer = _cogl_get_framebuffer (); CoglHandle texture_override; float quad_coords[4]; @@ -120,7 +121,8 @@ log_quad_sub_textures_cb (CoglHandle texture_handle, else texture_override = texture_handle; - _cogl_journal_log_quad (quad_coords, + _cogl_journal_log_quad (framebuffer->journal, + quad_coords, state->pipeline, 1, /* one layer */ 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); ValidateTexCoordsState state; float *final_tex_coords = alloca (sizeof (float) * 4 * n_layers); + CoglFramebuffer *framebuffer; _COGL_GET_CONTEXT (ctx, FALSE); @@ -539,7 +542,9 @@ _cogl_multitexture_quad_single_primitive (const float *position, if (state.override_pipeline) pipeline = state.override_pipeline; - _cogl_journal_log_quad (position, + framebuffer = _cogl_get_framebuffer (); + _cogl_journal_log_quad (framebuffer->journal, + position, pipeline, n_layers, COGL_INVALID_HANDLE, /* no texture override */ diff --git a/cogl/cogl-sub-texture.c b/cogl/cogl-sub-texture.c index 1730c871e..04aca444f 100644 --- a/cogl/cogl-sub-texture.c +++ b/cogl/cogl-sub-texture.c @@ -267,7 +267,8 @@ _cogl_sub_texture_new (CoglHandle next_texture, sub_tex = g_new (CoglSubTexture, 1); 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 of indirection by referencing the full texture of that texture diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c index 5e3f65ad6..51590f40f 100644 --- a/cogl/cogl-texture-2d-sliced.c +++ b/cogl/cogl-texture-2d-sliced.c @@ -907,7 +907,7 @@ _cogl_texture_2d_sliced_init_base (CoglTexture2DSliced *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_y_spans = NULL; diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c index 85ddd284a..80ec5c7bd 100644 --- a/cogl/cogl-texture-2d.c +++ b/cogl/cogl-texture-2d.c @@ -197,7 +197,7 @@ _cogl_texture_2d_create_base (unsigned int width, CoglTexture2D *tex_2d = g_new (CoglTexture2D, 1); 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->height = height; diff --git a/cogl/cogl-texture-3d.c b/cogl/cogl-texture-3d.c index bd2cc4af5..19ddb8fcd 100644 --- a/cogl/cogl-texture-3d.c +++ b/cogl/cogl-texture-3d.c @@ -178,7 +178,7 @@ _cogl_texture_3d_create_base (unsigned int width, CoglTexture3D *tex_3d = g_new (CoglTexture3D, 1); 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->height = height; diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h index 0e45e33a2..57093cb7b 100644 --- a/cogl/cogl-texture-private.h +++ b/cogl/cogl-texture-private.h @@ -135,6 +135,7 @@ struct _CoglTextureVtable struct _CoglTexture { CoglHandleObject _parent; + GList *framebuffers; const CoglTextureVtable *vtable; }; @@ -165,6 +166,10 @@ struct _CoglTexturePixel guint8 data[4]; }; +void +_cogl_texture_init (CoglTexture *texture, + const CoglTextureVtable *vtable); + void _cogl_texture_free (CoglTexture *texture); @@ -285,4 +290,14 @@ _cogl_texture_set_region_from_bitmap (CoglHandle handle, unsigned int dst_height, 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 */ diff --git a/cogl/cogl-texture-rectangle.c b/cogl/cogl-texture-rectangle.c index 5d6e5cdd7..cdf9bdfb8 100644 --- a/cogl/cogl-texture-rectangle.c +++ b/cogl/cogl-texture-rectangle.c @@ -222,7 +222,7 @@ _cogl_texture_rectangle_create_base (unsigned int width, CoglTextureRectangle *tex_rect = g_new (CoglTextureRectangle, 1); 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->height = height; diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index bedde9091..41aa68b95 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -49,6 +49,7 @@ #include "cogl-pipeline.h" #include "cogl-context.h" #include "cogl-handle.h" +#include "cogl-object-private.h" #include "cogl-primitives.h" #include "cogl-framebuffer-private.h" @@ -118,6 +119,14 @@ cogl_texture_unref (CoglHandle handle) cogl_handle_unref (handle); } +void +_cogl_texture_init (CoglTexture *texture, + const CoglTextureVtable *vtable) +{ + texture->vtable = vtable; + texture->framebuffers = NULL; +} + void _cogl_texture_free (CoglTexture *texture) { @@ -1473,3 +1482,52 @@ cogl_texture_get_data (CoglHandle handle, 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); +} diff --git a/cogl/cogl-vertex-attribute.c b/cogl/cogl-vertex-attribute.c index 715a176fe..d7821a13d 100644 --- a/cogl/cogl-vertex-attribute.c +++ b/cogl/cogl-vertex-attribute.c @@ -386,6 +386,8 @@ validate_layer_cb (CoglPipeline *pipeline, if (texture == COGL_INVALID_HANDLE) goto validated; + _cogl_texture_flush_journal_rendering (texture); + /* Give the texture a chance to know that we're rendering non-quad shaped primitives. If the texture is in an atlas it will be migrated */ @@ -1050,7 +1052,10 @@ flush_state (CoglDrawFlags flags, ValidateLayerState *state) { 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->options.flags = 0; diff --git a/cogl/cogl.c b/cogl/cogl.c index 4da18f52d..c2cee24ae 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -282,7 +282,7 @@ cogl_set_backface_culling_enabled (gboolean setting) return; /* 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; } @@ -479,7 +479,12 @@ cogl_disable_fog (void) 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 @@ -505,8 +510,15 @@ _cogl_read_pixels_with_rowstride (int x, g_return_if_fail (source == COGL_READ_PIXELS_COLOR_BUFFER); - /* make sure any batched primitives get emitted to the GL driver before - * issuing our read pixels... */ + /* make sure any batched primitives get emitted to the GL driver + * 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 (); framebuffer = _cogl_get_framebuffer (); diff --git a/cogl/cogl2-path.c b/cogl/cogl2-path.c index 0cfcaecb8..6ab91dd1c 100644 --- a/cogl/cogl2-path.c +++ b/cogl/cogl2-path.c @@ -459,17 +459,21 @@ _cogl_add_path_to_stencil_buffer (CoglPath *path, void cogl2_path_fill (CoglPath *path) { + CoglFramebuffer *framebuffer; + g_return_if_fail (cogl_is_path (path)); if (path->data->path_nodes->len == 0) return; - _cogl_journal_flush (); + framebuffer = _cogl_get_framebuffer (); + + _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 * 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); }