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

@ -56,9 +56,10 @@ struct _CoglBufferVtable
typedef enum _CoglBufferFlags typedef enum _CoglBufferFlags
{ {
COGL_BUFFER_FLAG_NONE = 0, COGL_BUFFER_FLAG_NONE = 0,
COGL_BUFFER_FLAG_BUFFER_OBJECT = 1UL << 0, /* real openGL buffer object */ 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; } CoglBufferFlags;
typedef enum { typedef enum {
@ -145,6 +146,18 @@ _cogl_buffer_immutable_ref (CoglBuffer *buffer);
void void
_cogl_buffer_immutable_unref (CoglBuffer *buffer); _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 G_END_DECLS
#endif /* __COGL_BUFFER_PRIVATE_H__ */ #endif /* __COGL_BUFFER_PRIVATE_H__ */

View File

@ -474,6 +474,57 @@ cogl_buffer_unmap (CoglBuffer *buffer)
buffer->vtable.unmap (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 gboolean
cogl_buffer_set_data (CoglBuffer *buffer, cogl_buffer_set_data (CoglBuffer *buffer,
gsize offset, gsize offset,

View File

@ -287,6 +287,9 @@ cogl_create_context (void)
_context->atlases = NULL; _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 /* As far as I can tell, GL_POINT_SPRITE doesn't have any effect
unless GL_COORD_REPLACE is enabled for an individual unless GL_COORD_REPLACE is enabled for an individual
layer. Therefore it seems like it should be ok to just leave it 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); g_hash_table_unref (_context->arbfp_cache);
#endif #endif
g_byte_array_free (_context->buffer_map_fallback_array, TRUE);
g_free (_context); g_free (_context);
} }

View File

@ -229,6 +229,12 @@ typedef struct
stencil buffer */ stencil buffer */
gboolean current_clip_stack_uses_stencil; 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; CoglContextDriver drv;
CoglContextWinsys winsys; CoglContextWinsys winsys;
} CoglContext; } CoglContext;

View File

@ -1058,8 +1058,7 @@ upload_vertices (const CoglJournalEntry *entries,
buffer = COGL_BUFFER (array); buffer = COGL_BUFFER (array);
cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_STATIC); cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_STATIC);
vout = cogl_buffer_map (buffer, COGL_BUFFER_ACCESS_WRITE, vout = _cogl_buffer_map_for_fill_or_fallback (buffer);
COGL_BUFFER_MAP_HINT_DISCARD);
vin = &g_array_index (vertices, float, 0); vin = &g_array_index (vertices, float, 0);
/* Expand the number of vertices from 2 to 4 while uploading */ /* Expand the number of vertices from 2 to 4 while uploading */
@ -1128,7 +1127,7 @@ upload_vertices (const CoglJournalEntry *entries,
vout += vb_stride * 4; vout += vb_stride * 4;
} }
cogl_buffer_unmap (buffer); _cogl_buffer_unmap_for_fill_or_fallback (buffer);
return array; return array;
} }

View File

@ -1484,9 +1484,8 @@ _cogl_path_build_stroke_vbo (CoglPath *path)
sizeof (floatVec2), sizeof (floatVec2),
NULL); NULL);
vbo_p = cogl_buffer_map (COGL_BUFFER (data->stroke_vbo), vbo_p =
COGL_BUFFER_ACCESS_WRITE, _cogl_buffer_map_for_fill_or_fallback (COGL_BUFFER (data->stroke_vbo));
COGL_BUFFER_MAP_HINT_DISCARD);
/* Copy the vertices in and count the number of sub paths. Each sub /* 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 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++; 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); data->stroke_vbo_attributes = g_new (CoglVertexAttribute *, n_attributes);