From a8216aff2f3daa56225aec56ff27317af6d0eba8 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 13 Jan 2011 15:35:30 +0000 Subject: [PATCH] cogl: Fallback to set_data when mapping a buffer to fill it In the journal code and when generating the stroke path the vertices are generated on the fly and stored in a CoglBuffer using cogl_buffer_map. However cogl_buffer_map is allowed to fail but it wasn't checking for a NULL return value. In particular on GLES it will always fail because glMapBuffer is only provided by an extension. This adds a new pair of internal functions called _cogl_buffer_{un,}map_for_fill_or_fallback which wrap cogl_buffer_map. If the map fails then it will instead return a pointer into a GByteArray attached to the context. When the buffer is unmapped the array is copied into the buffer using cogl_buffer_set_data. --- cogl/cogl-buffer-private.h | 19 +++++++++++--- cogl/cogl-buffer.c | 51 ++++++++++++++++++++++++++++++++++++++ cogl/cogl-context.c | 5 ++++ cogl/cogl-context.h | 6 +++++ cogl/cogl-journal.c | 5 ++-- cogl/cogl2-path.c | 7 +++--- 6 files changed, 83 insertions(+), 10 deletions(-) diff --git a/cogl/cogl-buffer-private.h b/cogl/cogl-buffer-private.h index 28d1a25d6..fc79e7d5f 100644 --- a/cogl/cogl-buffer-private.h +++ b/cogl/cogl-buffer-private.h @@ -56,9 +56,10 @@ struct _CoglBufferVtable typedef enum _CoglBufferFlags { - COGL_BUFFER_FLAG_NONE = 0, - COGL_BUFFER_FLAG_BUFFER_OBJECT = 1UL << 0, /* real openGL buffer object */ - COGL_BUFFER_FLAG_MAPPED = 1UL << 1 + COGL_BUFFER_FLAG_NONE = 0, + COGL_BUFFER_FLAG_BUFFER_OBJECT = 1UL << 0, /* real openGL buffer object */ + COGL_BUFFER_FLAG_MAPPED = 1UL << 1, + COGL_BUFFER_FLAG_MAPPED_FALLBACK = 1UL << 2 } CoglBufferFlags; typedef enum { @@ -145,6 +146,18 @@ _cogl_buffer_immutable_ref (CoglBuffer *buffer); void _cogl_buffer_immutable_unref (CoglBuffer *buffer); +/* This is a wrapper around cogl_buffer_map for internal use when we + want to map the buffer for write only to replace the entire + contents. If the map fails then it will fallback to writing to a + temporary buffer. When _cogl_buffer_unmap_for_fill_or_fallback is + called the temporary buffer will be copied into the array. Note + that these calls share a global array so they can not be nested. */ +void * +_cogl_buffer_map_for_fill_or_fallback (CoglBuffer *buffer); + +void +_cogl_buffer_unmap_for_fill_or_fallback (CoglBuffer *buffer); + G_END_DECLS #endif /* __COGL_BUFFER_PRIVATE_H__ */ diff --git a/cogl/cogl-buffer.c b/cogl/cogl-buffer.c index ab5043ae6..21ede706c 100644 --- a/cogl/cogl-buffer.c +++ b/cogl/cogl-buffer.c @@ -474,6 +474,57 @@ cogl_buffer_unmap (CoglBuffer *buffer) buffer->vtable.unmap (buffer); } +void * +_cogl_buffer_map_for_fill_or_fallback (CoglBuffer *buffer) +{ + void *ret; + + _COGL_GET_CONTEXT (ctx, NULL); + + g_return_val_if_fail (!ctx->buffer_map_fallback_in_use, NULL); + + ctx->buffer_map_fallback_in_use = TRUE; + + ret = cogl_buffer_map (buffer, + COGL_BUFFER_ACCESS_WRITE, + COGL_BUFFER_MAP_HINT_DISCARD); + + if (ret) + return ret; + else + { + /* If the map fails then we'll use a temporary buffer to fill + the data and then upload it using cogl_buffer_set_data when + the buffer is unmapped. The temporary buffer is shared to + avoid reallocating it every time */ + g_byte_array_set_size (ctx->buffer_map_fallback_array, buffer->size); + + buffer->flags |= COGL_BUFFER_FLAG_MAPPED_FALLBACK; + + return ctx->buffer_map_fallback_array->data; + } +} + +void +_cogl_buffer_unmap_for_fill_or_fallback (CoglBuffer *buffer) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + g_return_if_fail (ctx->buffer_map_fallback_in_use); + + ctx->buffer_map_fallback_in_use = FALSE; + + if ((buffer->flags & COGL_BUFFER_FLAG_MAPPED_FALLBACK)) + { + cogl_buffer_set_data (buffer, 0, + ctx->buffer_map_fallback_array->data, + buffer->size); + buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED_FALLBACK; + } + else + cogl_buffer_unmap (buffer); +} + gboolean cogl_buffer_set_data (CoglBuffer *buffer, gsize offset, diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index d246dcfb0..c2492beaa 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -287,6 +287,9 @@ cogl_create_context (void) _context->atlases = NULL; + _context->buffer_map_fallback_array = g_byte_array_new (); + _context->buffer_map_fallback_in_use = FALSE; + /* As far as I can tell, GL_POINT_SPRITE doesn't have any effect unless GL_COORD_REPLACE is enabled for an individual layer. Therefore it seems like it should be ok to just leave it @@ -385,6 +388,8 @@ _cogl_destroy_context (void) g_hash_table_unref (_context->arbfp_cache); #endif + g_byte_array_free (_context->buffer_map_fallback_array, TRUE); + g_free (_context); } diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h index 1d55923df..31f129fc2 100644 --- a/cogl/cogl-context.h +++ b/cogl/cogl-context.h @@ -229,6 +229,12 @@ typedef struct stencil buffer */ gboolean current_clip_stack_uses_stencil; + /* This is used as a temporary buffer to fill a CoglBuffer when + cogl_buffer_map fails and we only want to map to fill it with new + data */ + GByteArray *buffer_map_fallback_array; + gboolean buffer_map_fallback_in_use; + CoglContextDriver drv; CoglContextWinsys winsys; } CoglContext; diff --git a/cogl/cogl-journal.c b/cogl/cogl-journal.c index 1ccf1dbe8..bccae24c7 100644 --- a/cogl/cogl-journal.c +++ b/cogl/cogl-journal.c @@ -1058,8 +1058,7 @@ upload_vertices (const CoglJournalEntry *entries, buffer = COGL_BUFFER (array); cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_STATIC); - vout = cogl_buffer_map (buffer, COGL_BUFFER_ACCESS_WRITE, - COGL_BUFFER_MAP_HINT_DISCARD); + vout = _cogl_buffer_map_for_fill_or_fallback (buffer); vin = &g_array_index (vertices, float, 0); /* Expand the number of vertices from 2 to 4 while uploading */ @@ -1128,7 +1127,7 @@ upload_vertices (const CoglJournalEntry *entries, vout += vb_stride * 4; } - cogl_buffer_unmap (buffer); + _cogl_buffer_unmap_for_fill_or_fallback (buffer); return array; } diff --git a/cogl/cogl2-path.c b/cogl/cogl2-path.c index 5e7d6a2cb..d8dc6c613 100644 --- a/cogl/cogl2-path.c +++ b/cogl/cogl2-path.c @@ -1484,9 +1484,8 @@ _cogl_path_build_stroke_vbo (CoglPath *path) sizeof (floatVec2), NULL); - vbo_p = cogl_buffer_map (COGL_BUFFER (data->stroke_vbo), - COGL_BUFFER_ACCESS_WRITE, - COGL_BUFFER_MAP_HINT_DISCARD); + vbo_p = + _cogl_buffer_map_for_fill_or_fallback (COGL_BUFFER (data->stroke_vbo)); /* Copy the vertices in and count the number of sub paths. Each sub path will form a separate attribute so we can paint the disjoint @@ -1506,7 +1505,7 @@ _cogl_path_build_stroke_vbo (CoglPath *path) n_attributes++; } - cogl_buffer_unmap (COGL_BUFFER (data->stroke_vbo)); + _cogl_buffer_unmap_for_fill_or_fallback (COGL_BUFFER (data->stroke_vbo)); data->stroke_vbo_attributes = g_new (CoglVertexAttribute *, n_attributes);