Add cogl_buffer_map_range()

This adds a buffer method to map a subregion of the buffer. This works
using the GL_ARB_map_buffer_range extension. If the extension is not
available then it will fallback to using glMapBuffer to map the entire
buffer and then just add the offset to the returned pointer.

cogl_buffer_map() is now just a wrapper which maps the entire range of
the buffer. The driver backend functions have been renamed to
map_range and they now all take the offset and size arguments.

When the COGL_BUFFER_MAP_HINT_DISCARD hint is used and the map range
extension is available instead of using glBufferData to invalidate the
buffer it will instead pass the new GL_MAP_HINT_INVALIDATE_BUFFER
flag. There is now additionally a COGL_BUFFER_MAP_HINT_DISCARD_REGION
hint which can be used if the application only wants to discard the
small region that is mapped. glMapBufferRange is always used if it is
available even if the entire buffer is being mapped because it seems
more robust to pass those flags then to call glBufferData.

Reviewed-by: Robert Bragg <robert@linux.intel.com>

(cherry picked from commit 55ca02b5ca9cafc750251ec974e0d6a536cb80b8)
This commit is contained in:
Neil Roberts 2012-10-17 21:28:45 +01:00 committed by Robert Bragg
parent 8a3fb7a10d
commit ec03357e88
10 changed files with 168 additions and 32 deletions

View File

@ -41,7 +41,9 @@ typedef struct _CoglBufferVtable CoglBufferVtable;
struct _CoglBufferVtable struct _CoglBufferVtable
{ {
void * (* map) (CoglBuffer *buffer, void * (* map_range) (CoglBuffer *buffer,
size_t offset,
size_t size,
CoglBufferAccess access, CoglBufferAccess access,
CoglBufferMapHint hints); CoglBufferMapHint hints);

View File

@ -79,12 +79,14 @@ cogl_is_buffer (void *object)
*/ */
static void * static void *
malloc_map (CoglBuffer *buffer, malloc_map_range (CoglBuffer *buffer,
size_t offset,
size_t size,
CoglBufferAccess access, CoglBufferAccess access,
CoglBufferMapHint hints) CoglBufferMapHint hints)
{ {
buffer->flags |= COGL_BUFFER_FLAG_MAPPED; buffer->flags |= COGL_BUFFER_FLAG_MAPPED;
return buffer->data; return buffer->data + offset;
} }
static void static void
@ -138,7 +140,7 @@ _cogl_buffer_initialize (CoglBuffer *buffer,
if (use_malloc) if (use_malloc)
{ {
buffer->vtable.map = malloc_map; buffer->vtable.map_range = malloc_map_range;
buffer->vtable.unmap = malloc_unmap; buffer->vtable.unmap = malloc_unmap;
buffer->vtable.set_data = malloc_set_data; buffer->vtable.set_data = malloc_set_data;
@ -146,7 +148,7 @@ _cogl_buffer_initialize (CoglBuffer *buffer,
} }
else else
{ {
buffer->vtable.map = ctx->driver_vtable->buffer_map; buffer->vtable.map_range = ctx->driver_vtable->buffer_map_range;
buffer->vtable.unmap = ctx->driver_vtable->buffer_unmap; buffer->vtable.unmap = ctx->driver_vtable->buffer_unmap;
buffer->vtable.set_data = ctx->driver_vtable->buffer_set_data; buffer->vtable.set_data = ctx->driver_vtable->buffer_set_data;
@ -218,13 +220,30 @@ cogl_buffer_map (CoglBuffer *buffer,
{ {
_COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), NULL); _COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), NULL);
return cogl_buffer_map_range (buffer, 0, buffer->size, access, hints);
}
void *
cogl_buffer_map_range (CoglBuffer *buffer,
size_t offset,
size_t size,
CoglBufferAccess access,
CoglBufferMapHint hints)
{
_COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), NULL);
if (G_UNLIKELY (buffer->immutable_ref)) if (G_UNLIKELY (buffer->immutable_ref))
warn_about_midscene_changes (); warn_about_midscene_changes ();
if (buffer->flags & COGL_BUFFER_FLAG_MAPPED) if (buffer->flags & COGL_BUFFER_FLAG_MAPPED)
return buffer->data; return buffer->data;
buffer->data = buffer->vtable.map (buffer, access, hints); buffer->data = buffer->vtable.map_range (buffer,
offset,
size,
access,
hints);
return buffer->data; return buffer->data;
} }

View File

@ -161,7 +161,13 @@ typedef enum { /*< prefix=COGL_BUFFER_ACCESS >*/
/** /**
* CoglBufferMapHint: * CoglBufferMapHint:
* @COGL_BUFFER_MAP_HINT_DISCARD: Tells Cogl that you plan to replace * @COGL_BUFFER_MAP_HINT_DISCARD: Tells Cogl that you plan to replace
* all the buffer's contents. * all the buffer's contents. When this flag is used to map a
* buffer, the entire contents of the buffer become undefined, even
* if only a subregion of the buffer is mapped.
* @COGL_BUFFER_MAP_HINT_DISCARD_RANGE: Tells Cogl that you plan to
* replace all the contents of the mapped region. The contents of
* the region specified are undefined after this flag is used to
* map a buffer.
* *
* Hints to Cogl about how you are planning to modify the data once it * Hints to Cogl about how you are planning to modify the data once it
* is mapped. * is mapped.
@ -170,7 +176,8 @@ typedef enum { /*< prefix=COGL_BUFFER_ACCESS >*/
* Stability: unstable * Stability: unstable
*/ */
typedef enum { /*< prefix=COGL_BUFFER_MAP_HINT >*/ typedef enum { /*< prefix=COGL_BUFFER_MAP_HINT >*/
COGL_BUFFER_MAP_HINT_DISCARD = 1 << 0 COGL_BUFFER_MAP_HINT_DISCARD = 1 << 0,
COGL_BUFFER_MAP_HINT_DISCARD_RANGE = 1 << 1
} CoglBufferMapHint; } CoglBufferMapHint;
/** /**
@ -180,7 +187,9 @@ typedef enum { /*< prefix=COGL_BUFFER_MAP_HINT >*/
* @hints: A mask of #CoglBufferMapHint<!-- -->s that tell Cogl how * @hints: A mask of #CoglBufferMapHint<!-- -->s that tell Cogl how
* the data will be modified once mapped. * the data will be modified once mapped.
* *
* Maps the buffer into the application address space for direct access. * Maps the buffer into the application address space for direct
* access. This is equivalent to calling cogl_buffer_map_range() with
* zero as the offset and the size of the entire buffer as the size.
* *
* It is strongly recommended that you pass * It is strongly recommended that you pass
* %COGL_BUFFER_MAP_HINT_DISCARD as a hint if you are going to replace * %COGL_BUFFER_MAP_HINT_DISCARD as a hint if you are going to replace
@ -203,6 +212,43 @@ cogl_buffer_map (CoglBuffer *buffer,
CoglBufferAccess access, CoglBufferAccess access,
CoglBufferMapHint hints); CoglBufferMapHint hints);
/**
* cogl_buffer_map_range:
* @buffer: a buffer object
* @offset: Offset within the buffer to start the mapping
* @size: The size of data to map
* @access: how the mapped buffer will be used by the application
* @hints: A mask of #CoglBufferMapHint<!-- -->s that tell Cogl how
* the data will be modified once mapped.
*
* Maps a sub-region of the buffer into the application's address space
* for direct access.
*
* It is strongly recommended that you pass
* %COGL_BUFFER_MAP_HINT_DISCARD as a hint if you are going to replace
* all the buffer's data. This way if the buffer is currently being
* used by the GPU then the driver won't have to stall the CPU and
* wait for the hardware to finish because it can instead allocate a
* new buffer to map. You can pass
* %COGL_BUFFER_MAP_HINT_DISCARD_REGION instead if you want the
* regions outside of the mapping to be retained.
*
* The behaviour is undefined if you access the buffer in a way
* conflicting with the @access mask you pass. It is also an error to
* release your last reference while the buffer is mapped.
*
* Return value: A pointer to the mapped memory or %NULL is the call fails
*
* Since: 2.0
* Stability: unstable
*/
void *
cogl_buffer_map_range (CoglBuffer *buffer,
size_t offset,
size_t size,
CoglBufferAccess access,
CoglBufferMapHint hints);
/** /**
* cogl_buffer_unmap: * cogl_buffer_unmap:
* @buffer: a buffer object * @buffer: a buffer object

View File

@ -240,7 +240,9 @@ struct _CoglDriverVtable
/* Maps a buffer into the CPU */ /* Maps a buffer into the CPU */
void * void *
(* buffer_map) (CoglBuffer *buffer, (* buffer_map_range) (CoglBuffer *buffer,
size_t offset,
size_t size,
CoglBufferAccess access, CoglBufferAccess access,
CoglBufferMapHint hints); CoglBufferMapHint hints);

View File

@ -40,7 +40,9 @@ void
_cogl_buffer_gl_destroy (CoglBuffer *buffer); _cogl_buffer_gl_destroy (CoglBuffer *buffer);
void * void *
_cogl_buffer_gl_map (CoglBuffer *buffer, _cogl_buffer_gl_map_range (CoglBuffer *buffer,
size_t offset,
size_t size,
CoglBufferAccess access, CoglBufferAccess access,
CoglBufferMapHint hints); CoglBufferMapHint hints);

View File

@ -57,7 +57,18 @@
#ifndef GL_READ_WRITE #ifndef GL_READ_WRITE
#define GL_READ_WRITE 0x88BA #define GL_READ_WRITE 0x88BA
#endif #endif
#ifndef GL_MAP_READ_BIT
#define GL_MAP_READ_BIT 0x0001
#endif
#ifndef GL_MAP_WRITE_BIT
#define GL_MAP_WRITE_BIT 0x0002
#endif
#ifndef GL_MAP_INVALIDATE_RANGE_BIT
#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004
#endif
#ifndef GL_MAP_INVALIDATE_BUFFER_BIT
#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008
#endif
void void
_cogl_buffer_gl_create (CoglBuffer *buffer) _cogl_buffer_gl_create (CoglBuffer *buffer)
@ -173,7 +184,9 @@ _cogl_buffer_bind_no_create (CoglBuffer *buffer,
} }
void * void *
_cogl_buffer_gl_map (CoglBuffer *buffer, _cogl_buffer_gl_map_range (CoglBuffer *buffer,
size_t offset,
size_t size,
CoglBufferAccess access, CoglBufferAccess access,
CoglBufferMapHint hints) CoglBufferMapHint hints)
{ {
@ -194,14 +207,54 @@ _cogl_buffer_gl_map (CoglBuffer *buffer,
gl_target = convert_bind_target_to_gl_target (target); gl_target = convert_bind_target_to_gl_target (target);
/* If the map buffer range extension is supported then we will
* always use it even if we are mapping the full range because the
* normal mapping function doesn't support passing the discard
* hints */
if (ctx->glMapBufferRange)
{
GLbitfield gl_access = 0;
if ((access & COGL_BUFFER_ACCESS_READ))
gl_access |= GL_MAP_READ_BIT;
if ((access & COGL_BUFFER_ACCESS_WRITE))
gl_access |= GL_MAP_WRITE_BIT;
if ((hints & COGL_BUFFER_MAP_HINT_DISCARD))
gl_access |= GL_MAP_INVALIDATE_BUFFER_BIT;
if ((hints & COGL_BUFFER_MAP_HINT_DISCARD_RANGE))
gl_access |= GL_MAP_INVALIDATE_RANGE_BIT;
if (!buffer->store_created)
recreate_store (buffer);
GE_RET( data,
ctx,
glMapBufferRange (gl_target,
offset,
size,
gl_access) );
}
else
{
/* create an empty store if we don't have one yet. creating the store /* create an empty store if we don't have one yet. creating the store
* lazily allows the user of the CoglBuffer to set a hint before the * lazily allows the user of the CoglBuffer to set a hint before the
* store is created. */ * store is created. */
if (!buffer->store_created || (hints & COGL_BUFFER_MAP_HINT_DISCARD)) if (!buffer->store_created ||
(hints & COGL_BUFFER_MAP_HINT_DISCARD) ||
((hints & COGL_BUFFER_MAP_HINT_DISCARD_RANGE) &&
offset == 0 && size >= buffer->size))
recreate_store (buffer); recreate_store (buffer);
GE_RET( data, ctx, glMapBuffer (gl_target, GE_RET( data,
ctx,
glMapBuffer (gl_target,
_cogl_buffer_access_to_gl_enum (access)) ); _cogl_buffer_access_to_gl_enum (access)) );
if (data)
data += offset;
}
if (data) if (data)
buffer->flags |= COGL_BUFFER_FLAG_MAPPED; buffer->flags |= COGL_BUFFER_FLAG_MAPPED;

View File

@ -618,7 +618,7 @@ _cogl_driver_gl =
_cogl_clip_stack_gl_flush, _cogl_clip_stack_gl_flush,
_cogl_buffer_gl_create, _cogl_buffer_gl_create,
_cogl_buffer_gl_destroy, _cogl_buffer_gl_destroy,
_cogl_buffer_gl_map, _cogl_buffer_gl_map_range,
_cogl_buffer_gl_unmap, _cogl_buffer_gl_unmap,
_cogl_buffer_gl_set_data, _cogl_buffer_gl_set_data,
}; };

View File

@ -392,7 +392,7 @@ _cogl_driver_gles =
_cogl_clip_stack_gl_flush, _cogl_clip_stack_gl_flush,
_cogl_buffer_gl_create, _cogl_buffer_gl_create,
_cogl_buffer_gl_destroy, _cogl_buffer_gl_destroy,
_cogl_buffer_gl_map, _cogl_buffer_gl_map_range,
_cogl_buffer_gl_unmap, _cogl_buffer_gl_unmap,
_cogl_buffer_gl_set_data, _cogl_buffer_gl_set_data,
}; };

View File

@ -286,3 +286,14 @@ COGL_EXT_FUNCTION (void, glGenVertexArrays,
(GLsizei n, (GLsizei n,
GLuint *arrays)) GLuint *arrays))
COGL_EXT_END () COGL_EXT_END ()
COGL_EXT_BEGIN (map_region, 3, 0,
0, /* not in either GLES */
"ARB\0",
"map_buffer_range\0")
COGL_EXT_FUNCTION (GLvoid *, glMapBufferRange,
(GLenum target,
GLintptr offset,
GLsizeiptr length,
GLbitfield access))
COGL_EXT_END ()

View File

@ -731,6 +731,7 @@ cogl_buffer_set_update_hint
cogl_buffer_get_update_hint cogl_buffer_get_update_hint
CoglBufferAccess CoglBufferAccess
cogl_buffer_map cogl_buffer_map
cogl_buffer_map_range
cogl_buffer_unmap cogl_buffer_unmap
cogl_buffer_set_data cogl_buffer_set_data