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:
parent
385e0f84c6
commit
f834596fd4
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 ();
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user