4 * An object oriented GL/GLES Abstraction/Utility Layer
6 * Copyright (C) 2010 Intel Corporation.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
24 * Damien Lespiau <damien.lespiau@intel.com>
25 * Robert Bragg <robert@linux.intel.com>
28 /* For an overview of the functionality implemented here, please see
29 * cogl-buffer.h, which contains the gtk-doc section overview for the
41 #include "cogl-internal.h"
42 #include "cogl-util.h"
43 #include "cogl-context-private.h"
44 #include "cogl-handle.h"
45 #include "cogl-pixel-buffer-private.h"
48 * GL/GLES compatibility defines for the buffer API:
51 #ifndef GL_PIXEL_PACK_BUFFER
52 #define GL_PIXEL_PACK_BUFFER 0x88EB
54 #ifndef GL_PIXEL_UNPACK_BUFFER
55 #define GL_PIXEL_UNPACK_BUFFER 0x88EC
57 #ifndef GL_ARRAY_BUFFER
58 #define GL_ARRAY_BUFFER 0x8892
60 #ifndef GL_ELEMENT_ARRAY_BUFFER
61 #define GL_ARRAY_BUFFER 0x8893
64 #define GL_READ_ONLY 0x88B8
67 #define GL_WRITE_ONLY 0x88B9
70 #define GL_READ_WRITE 0x88BA
74 * The CoglHandle macros don't support any form of inheritance, so for
75 * now we implement the CoglObject support for the CoglBuffer
76 * abstract class manually.
79 static GSList *_cogl_buffer_types;
82 _cogl_buffer_register_buffer_type (const CoglObjectClass *klass)
84 _cogl_buffer_types = g_slist_prepend (_cogl_buffer_types, (void *) klass);
88 cogl_is_buffer (void *object)
90 const CoglHandleObject *obj = object;
96 for (l = _cogl_buffer_types; l; l = l->next)
97 if (l->data == obj->klass)
104 convert_bind_target_to_gl_target (CoglBufferBindTarget target)
108 case COGL_BUFFER_BIND_TARGET_PIXEL_PACK:
109 return GL_PIXEL_PACK_BUFFER;
110 case COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK:
111 return GL_PIXEL_UNPACK_BUFFER;
112 case COGL_BUFFER_BIND_TARGET_ATTRIBUTE_BUFFER:
113 return GL_ARRAY_BUFFER;
114 case COGL_BUFFER_BIND_TARGET_INDEX_BUFFER:
115 return GL_ELEMENT_ARRAY_BUFFER;
117 g_return_val_if_reached (COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK);
122 _cogl_buffer_bind_no_create (CoglBuffer *buffer,
123 CoglBufferBindTarget target)
125 CoglContext *ctx = buffer->context;
127 _COGL_RETURN_VAL_IF_FAIL (buffer != NULL, NULL);
129 /* Don't allow binding the buffer to multiple targets at the same time */
130 _COGL_RETURN_VAL_IF_FAIL (ctx->current_buffer[buffer->last_target] != buffer,
133 /* Don't allow nesting binds to the same target */
134 _COGL_RETURN_VAL_IF_FAIL (ctx->current_buffer[target] == NULL, NULL);
136 buffer->last_target = target;
137 ctx->current_buffer[target] = buffer;
139 if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT)
141 GLenum gl_target = convert_bind_target_to_gl_target (buffer->last_target);
142 GE( ctx, glBindBuffer (gl_target, buffer->gl_handle) );
150 _cogl_buffer_hints_to_gl_enum (CoglBufferUsageHint usage_hint,
151 CoglBufferUpdateHint update_hint)
153 /* usage hint is always TEXTURE for now */
154 if (update_hint == COGL_BUFFER_UPDATE_HINT_STATIC)
155 return GL_STATIC_DRAW;
156 if (update_hint == COGL_BUFFER_UPDATE_HINT_DYNAMIC)
157 return GL_DYNAMIC_DRAW;
158 /* OpenGL ES 1.1 and 2 only know about STATIC_DRAW and DYNAMIC_DRAW */
160 if (update_hint == COGL_BUFFER_UPDATE_HINT_STREAM)
161 return GL_STREAM_DRAW;
164 return GL_STATIC_DRAW;
168 bo_recreate_store (CoglBuffer *buffer)
173 /* This assumes the buffer is already bound */
175 gl_target = convert_bind_target_to_gl_target (buffer->last_target);
176 gl_enum = _cogl_buffer_hints_to_gl_enum (buffer->usage_hint,
177 buffer->update_hint);
179 GE( buffer->context, glBufferData (gl_target,
183 buffer->store_created = TRUE;
187 bo_map (CoglBuffer *buffer,
188 CoglBufferAccess access,
189 CoglBufferMapHint hints)
192 CoglBufferBindTarget target;
194 CoglContext *ctx = buffer->context;
196 if ((access & COGL_BUFFER_ACCESS_READ) &&
197 !cogl_has_feature (ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_READ))
199 if ((access & COGL_BUFFER_ACCESS_WRITE) &&
200 !cogl_has_feature (ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE))
203 target = buffer->last_target;
204 _cogl_buffer_bind_no_create (buffer, target);
206 gl_target = convert_bind_target_to_gl_target (target);
208 /* create an empty store if we don't have one yet. creating the store
209 * lazily allows the user of the CoglBuffer to set a hint before the
210 * store is created. */
211 if (!buffer->store_created || (hints & COGL_BUFFER_MAP_HINT_DISCARD))
212 bo_recreate_store (buffer);
214 GE_RET( data, ctx, glMapBuffer (gl_target,
215 _cogl_buffer_access_to_gl_enum (access)) );
217 buffer->flags |= COGL_BUFFER_FLAG_MAPPED;
219 _cogl_buffer_unbind (buffer);
225 bo_unmap (CoglBuffer *buffer)
227 CoglContext *ctx = buffer->context;
229 _cogl_buffer_bind_no_create (buffer, buffer->last_target);
231 GE( ctx, glUnmapBuffer (convert_bind_target_to_gl_target
232 (buffer->last_target)) );
233 buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED;
235 _cogl_buffer_unbind (buffer);
239 bo_set_data (CoglBuffer *buffer,
244 CoglBufferBindTarget target;
246 CoglContext *ctx = buffer->context;
248 target = buffer->last_target;
249 _cogl_buffer_bind (buffer, target);
251 gl_target = convert_bind_target_to_gl_target (target);
253 GE( ctx, glBufferSubData (gl_target, offset, size, data) );
255 _cogl_buffer_unbind (buffer);
261 * Fallback path, buffer->data points to a malloc'ed buffer.
265 malloc_map (CoglBuffer *buffer,
266 CoglBufferAccess access,
267 CoglBufferMapHint hints)
269 buffer->flags |= COGL_BUFFER_FLAG_MAPPED;
274 malloc_unmap (CoglBuffer *buffer)
276 buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED;
280 malloc_set_data (CoglBuffer *buffer,
285 memcpy (buffer->data + offset, data, size);
290 _cogl_buffer_initialize (CoglBuffer *buffer,
291 CoglContext *context,
294 CoglBufferBindTarget default_target,
295 CoglBufferUsageHint usage_hint,
296 CoglBufferUpdateHint update_hint)
298 buffer->context = cogl_object_ref (context);
299 buffer->flags = COGL_BUFFER_FLAG_NONE;
300 buffer->store_created = FALSE;
302 buffer->last_target = default_target;
303 buffer->usage_hint = usage_hint;
304 buffer->update_hint = update_hint;
306 buffer->immutable_ref = 0;
310 buffer->vtable.map = malloc_map;
311 buffer->vtable.unmap = malloc_unmap;
312 buffer->vtable.set_data = malloc_set_data;
314 buffer->data = g_malloc (size);
318 buffer->vtable.map = bo_map;
319 buffer->vtable.unmap = bo_unmap;
320 buffer->vtable.set_data = bo_set_data;
322 GE( context, glGenBuffers (1, &buffer->gl_handle) );
323 buffer->flags |= COGL_BUFFER_FLAG_BUFFER_OBJECT;
328 _cogl_buffer_fini (CoglBuffer *buffer)
330 _COGL_RETURN_IF_FAIL (!(buffer->flags & COGL_BUFFER_FLAG_MAPPED));
331 _COGL_RETURN_IF_FAIL (buffer->immutable_ref == 0);
333 if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT)
334 GE( buffer->context, glDeleteBuffers (1, &buffer->gl_handle) );
336 g_free (buffer->data);
338 cogl_object_unref (buffer->context);
342 _cogl_buffer_access_to_gl_enum (CoglBufferAccess access)
344 if ((access & COGL_BUFFER_ACCESS_READ_WRITE) == COGL_BUFFER_ACCESS_READ_WRITE)
345 return GL_READ_WRITE;
346 else if (access & COGL_BUFFER_ACCESS_WRITE)
347 return GL_WRITE_ONLY;
353 _cogl_buffer_bind (CoglBuffer *buffer, CoglBufferBindTarget target)
357 ret = _cogl_buffer_bind_no_create (buffer, target);
359 /* create an empty store if we don't have one yet. creating the store
360 * lazily allows the user of the CoglBuffer to set a hint before the
361 * store is created. */
362 if ((buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT) &&
363 !buffer->store_created)
364 bo_recreate_store (buffer);
370 _cogl_buffer_unbind (CoglBuffer *buffer)
372 CoglContext *ctx = buffer->context;
374 _COGL_RETURN_IF_FAIL (buffer != NULL);
376 /* the unbind should pair up with a previous bind */
377 _COGL_RETURN_IF_FAIL (ctx->current_buffer[buffer->last_target] == buffer);
379 if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT)
381 GLenum gl_target = convert_bind_target_to_gl_target (buffer->last_target);
382 GE( ctx, glBindBuffer (gl_target, 0) );
385 ctx->current_buffer[buffer->last_target] = NULL;
389 cogl_buffer_get_size (CoglBuffer *buffer)
391 if (!cogl_is_buffer (buffer))
394 return COGL_BUFFER (buffer)->size;
398 cogl_buffer_set_update_hint (CoglBuffer *buffer,
399 CoglBufferUpdateHint hint)
401 if (!cogl_is_buffer (buffer))
404 if (G_UNLIKELY (hint > COGL_BUFFER_UPDATE_HINT_STREAM))
405 hint = COGL_BUFFER_UPDATE_HINT_STATIC;
407 buffer->update_hint = hint;
411 cogl_buffer_get_update_hint (CoglBuffer *buffer)
413 if (!cogl_is_buffer (buffer))
416 return buffer->update_hint;
420 warn_about_midscene_changes (void)
422 static gboolean seen = FALSE;
425 g_warning ("Mid-scene modification of buffers has "
426 "undefined results\n");
432 cogl_buffer_map (CoglBuffer *buffer,
433 CoglBufferAccess access,
434 CoglBufferMapHint hints)
436 _COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), NULL);
438 if (G_UNLIKELY (buffer->immutable_ref))
439 warn_about_midscene_changes ();
441 if (buffer->flags & COGL_BUFFER_FLAG_MAPPED)
444 buffer->data = buffer->vtable.map (buffer, access, hints);
449 cogl_buffer_unmap (CoglBuffer *buffer)
451 if (!cogl_is_buffer (buffer))
454 if (!(buffer->flags & COGL_BUFFER_FLAG_MAPPED))
457 buffer->vtable.unmap (buffer);
461 _cogl_buffer_map_for_fill_or_fallback (CoglBuffer *buffer)
463 CoglContext *ctx = buffer->context;
466 _COGL_RETURN_VAL_IF_FAIL (!ctx->buffer_map_fallback_in_use, NULL);
468 ctx->buffer_map_fallback_in_use = TRUE;
470 ret = cogl_buffer_map (buffer,
471 COGL_BUFFER_ACCESS_WRITE,
472 COGL_BUFFER_MAP_HINT_DISCARD);
478 /* If the map fails then we'll use a temporary buffer to fill
479 the data and then upload it using cogl_buffer_set_data when
480 the buffer is unmapped. The temporary buffer is shared to
481 avoid reallocating it every time */
482 g_byte_array_set_size (ctx->buffer_map_fallback_array, buffer->size);
484 buffer->flags |= COGL_BUFFER_FLAG_MAPPED_FALLBACK;
486 return ctx->buffer_map_fallback_array->data;
491 _cogl_buffer_unmap_for_fill_or_fallback (CoglBuffer *buffer)
493 CoglContext *ctx = buffer->context;
495 _COGL_RETURN_IF_FAIL (ctx->buffer_map_fallback_in_use);
497 ctx->buffer_map_fallback_in_use = FALSE;
499 if ((buffer->flags & COGL_BUFFER_FLAG_MAPPED_FALLBACK))
501 cogl_buffer_set_data (buffer, 0,
502 ctx->buffer_map_fallback_array->data,
504 buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED_FALLBACK;
507 cogl_buffer_unmap (buffer);
511 cogl_buffer_set_data (CoglBuffer *buffer,
516 _COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), FALSE);
517 _COGL_RETURN_VAL_IF_FAIL ((offset + size) <= buffer->size, FALSE);
519 if (G_UNLIKELY (buffer->immutable_ref))
520 warn_about_midscene_changes ();
522 return buffer->vtable.set_data (buffer, offset, data, size);
526 _cogl_buffer_immutable_ref (CoglBuffer *buffer)
528 _COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), NULL);
530 buffer->immutable_ref++;
535 _cogl_buffer_immutable_unref (CoglBuffer *buffer)
537 _COGL_RETURN_IF_FAIL (cogl_is_buffer (buffer));
538 _COGL_RETURN_IF_FAIL (buffer->immutable_ref > 0);
540 buffer->immutable_ref--;