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.
This commit is contained in:
Neil Roberts 2011-01-13 15:35:30 +00:00
parent ac81f3b936
commit a8216aff2f
6 changed files with 83 additions and 10 deletions

View File

@ -58,7 +58,8 @@ 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_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__ */

View File

@ -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,

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);