diff --git a/cogl/cogl-buffer-private.h b/cogl/cogl-buffer-private.h index 59794cd20..cf2d9b920 100644 --- a/cogl/cogl-buffer-private.h +++ b/cogl/cogl-buffer-private.h @@ -41,9 +41,11 @@ typedef struct _CoglBufferVtable CoglBufferVtable; struct _CoglBufferVtable { - void * (* map) (CoglBuffer *buffer, - CoglBufferAccess access, - CoglBufferMapHint hints); + void * (* map_range) (CoglBuffer *buffer, + size_t offset, + size_t size, + CoglBufferAccess access, + CoglBufferMapHint hints); void (* unmap) (CoglBuffer *buffer); diff --git a/cogl/cogl-buffer.c b/cogl/cogl-buffer.c index 573db0fed..7a1a08463 100644 --- a/cogl/cogl-buffer.c +++ b/cogl/cogl-buffer.c @@ -79,12 +79,14 @@ cogl_is_buffer (void *object) */ static void * -malloc_map (CoglBuffer *buffer, - CoglBufferAccess access, - CoglBufferMapHint hints) +malloc_map_range (CoglBuffer *buffer, + size_t offset, + size_t size, + CoglBufferAccess access, + CoglBufferMapHint hints) { buffer->flags |= COGL_BUFFER_FLAG_MAPPED; - return buffer->data; + return buffer->data + offset; } static void @@ -138,7 +140,7 @@ _cogl_buffer_initialize (CoglBuffer *buffer, if (use_malloc) { - buffer->vtable.map = malloc_map; + buffer->vtable.map_range = malloc_map_range; buffer->vtable.unmap = malloc_unmap; buffer->vtable.set_data = malloc_set_data; @@ -146,7 +148,7 @@ _cogl_buffer_initialize (CoglBuffer *buffer, } 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.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); + 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)) warn_about_midscene_changes (); if (buffer->flags & COGL_BUFFER_FLAG_MAPPED) 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; } diff --git a/cogl/cogl-buffer.h b/cogl/cogl-buffer.h index 5ad24b967..217de11bf 100644 --- a/cogl/cogl-buffer.h +++ b/cogl/cogl-buffer.h @@ -161,7 +161,13 @@ typedef enum { /*< prefix=COGL_BUFFER_ACCESS >*/ /** * CoglBufferMapHint: * @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 * is mapped. @@ -170,7 +176,8 @@ typedef enum { /*< prefix=COGL_BUFFER_ACCESS >*/ * Stability: unstable */ 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; /** @@ -180,7 +187,9 @@ typedef enum { /*< prefix=COGL_BUFFER_MAP_HINT >*/ * @hints: A mask of #CoglBufferMapHints that tell Cogl how * 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 * %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, 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 #CoglBufferMapHints 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: * @buffer: a buffer object diff --git a/cogl/cogl-driver.h b/cogl/cogl-driver.h index f92211100..36f4c7adf 100644 --- a/cogl/cogl-driver.h +++ b/cogl/cogl-driver.h @@ -240,9 +240,11 @@ struct _CoglDriverVtable /* Maps a buffer into the CPU */ void * - (* buffer_map) (CoglBuffer *buffer, - CoglBufferAccess access, - CoglBufferMapHint hints); + (* buffer_map_range) (CoglBuffer *buffer, + size_t offset, + size_t size, + CoglBufferAccess access, + CoglBufferMapHint hints); /* Unmaps a buffer */ void diff --git a/cogl/driver/gl/cogl-buffer-gl-private.h b/cogl/driver/gl/cogl-buffer-gl-private.h index 570e36cca..583e0c119 100644 --- a/cogl/driver/gl/cogl-buffer-gl-private.h +++ b/cogl/driver/gl/cogl-buffer-gl-private.h @@ -40,9 +40,11 @@ void _cogl_buffer_gl_destroy (CoglBuffer *buffer); void * -_cogl_buffer_gl_map (CoglBuffer *buffer, - CoglBufferAccess access, - CoglBufferMapHint hints); +_cogl_buffer_gl_map_range (CoglBuffer *buffer, + size_t offset, + size_t size, + CoglBufferAccess access, + CoglBufferMapHint hints); void _cogl_buffer_gl_unmap (CoglBuffer *buffer); diff --git a/cogl/driver/gl/cogl-buffer-gl.c b/cogl/driver/gl/cogl-buffer-gl.c index 94f693636..673dbf1ad 100644 --- a/cogl/driver/gl/cogl-buffer-gl.c +++ b/cogl/driver/gl/cogl-buffer-gl.c @@ -57,7 +57,18 @@ #ifndef GL_READ_WRITE #define GL_READ_WRITE 0x88BA #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 _cogl_buffer_gl_create (CoglBuffer *buffer) @@ -173,9 +184,11 @@ _cogl_buffer_bind_no_create (CoglBuffer *buffer, } void * -_cogl_buffer_gl_map (CoglBuffer *buffer, - CoglBufferAccess access, - CoglBufferMapHint hints) +_cogl_buffer_gl_map_range (CoglBuffer *buffer, + size_t offset, + size_t size, + CoglBufferAccess access, + CoglBufferMapHint hints) { uint8_t *data; CoglBufferBindTarget target; @@ -194,14 +207,54 @@ _cogl_buffer_gl_map (CoglBuffer *buffer, gl_target = convert_bind_target_to_gl_target (target); - /* 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 - * store is created. */ - if (!buffer->store_created || (hints & COGL_BUFFER_MAP_HINT_DISCARD)) - recreate_store (buffer); + /* 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 + * lazily allows the user of the CoglBuffer to set a hint before the + * store is created. */ + 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); + + GE_RET( data, + ctx, + glMapBuffer (gl_target, + _cogl_buffer_access_to_gl_enum (access)) ); + + if (data) + data += offset; + } - GE_RET( data, ctx, glMapBuffer (gl_target, - _cogl_buffer_access_to_gl_enum (access)) ); if (data) buffer->flags |= COGL_BUFFER_FLAG_MAPPED; diff --git a/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/driver/gl/gl/cogl-driver-gl.c index 7600db26a..2597161fe 100644 --- a/cogl/driver/gl/gl/cogl-driver-gl.c +++ b/cogl/driver/gl/gl/cogl-driver-gl.c @@ -618,7 +618,7 @@ _cogl_driver_gl = _cogl_clip_stack_gl_flush, _cogl_buffer_gl_create, _cogl_buffer_gl_destroy, - _cogl_buffer_gl_map, + _cogl_buffer_gl_map_range, _cogl_buffer_gl_unmap, _cogl_buffer_gl_set_data, }; diff --git a/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/driver/gl/gles/cogl-driver-gles.c index dd4955b33..ddd9b530b 100644 --- a/cogl/driver/gl/gles/cogl-driver-gles.c +++ b/cogl/driver/gl/gles/cogl-driver-gles.c @@ -392,7 +392,7 @@ _cogl_driver_gles = _cogl_clip_stack_gl_flush, _cogl_buffer_gl_create, _cogl_buffer_gl_destroy, - _cogl_buffer_gl_map, + _cogl_buffer_gl_map_range, _cogl_buffer_gl_unmap, _cogl_buffer_gl_set_data, }; diff --git a/cogl/gl-prototypes/cogl-all-functions.h b/cogl/gl-prototypes/cogl-all-functions.h index eeef4e7da..870722e40 100644 --- a/cogl/gl-prototypes/cogl-all-functions.h +++ b/cogl/gl-prototypes/cogl-all-functions.h @@ -286,3 +286,14 @@ COGL_EXT_FUNCTION (void, glGenVertexArrays, (GLsizei n, GLuint *arrays)) 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 () diff --git a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt index 5f6f959de..194495185 100644 --- a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt +++ b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt @@ -731,6 +731,7 @@ cogl_buffer_set_update_hint cogl_buffer_get_update_hint CoglBufferAccess cogl_buffer_map +cogl_buffer_map_range cogl_buffer_unmap cogl_buffer_set_data