"Initial commit to Gerrit"
[profile/ivi/cogl.git] / cogl / cogl-buffer.c
1 /*
2  * Cogl
3  *
4  * An object oriented GL/GLES Abstraction/Utility Layer
5  *
6  * Copyright (C) 2010 Intel Corporation.
7  *
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.
12  *
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.
17  *
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/>.
20  *
21  *
22  *
23  * Authors:
24  *   Damien Lespiau <damien.lespiau@intel.com>
25  *   Robert Bragg <robert@linux.intel.com>
26  */
27
28 /* For an overview of the functionality implemented here, please see
29  * cogl-buffer.h, which contains the gtk-doc section overview for the
30  * Pixel Buffers API.
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <stdio.h>
38 #include <string.h>
39 #include <glib.h>
40
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"
46
47 /*
48  * GL/GLES compatibility defines for the buffer API:
49  */
50
51 #ifndef GL_PIXEL_PACK_BUFFER
52 #define GL_PIXEL_PACK_BUFFER 0x88EB
53 #endif
54 #ifndef GL_PIXEL_UNPACK_BUFFER
55 #define GL_PIXEL_UNPACK_BUFFER 0x88EC
56 #endif
57 #ifndef GL_ARRAY_BUFFER
58 #define GL_ARRAY_BUFFER 0x8892
59 #endif
60 #ifndef GL_ELEMENT_ARRAY_BUFFER
61 #define GL_ARRAY_BUFFER 0x8893
62 #endif
63 #ifndef GL_READ_ONLY
64 #define GL_READ_ONLY 0x88B8
65 #endif
66 #ifndef GL_WRITE_ONLY
67 #define GL_WRITE_ONLY 0x88B9
68 #endif
69 #ifndef GL_READ_WRITE
70 #define GL_READ_WRITE 0x88BA
71 #endif
72
73 /* XXX:
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.
77  */
78
79 static GSList *_cogl_buffer_types;
80
81 void
82 _cogl_buffer_register_buffer_type (const CoglObjectClass *klass)
83 {
84   _cogl_buffer_types = g_slist_prepend (_cogl_buffer_types, (void *) klass);
85 }
86
87 gboolean
88 cogl_is_buffer (void *object)
89 {
90   const CoglHandleObject *obj = object;
91   GSList *l;
92
93   if (object == NULL)
94     return FALSE;
95
96   for (l = _cogl_buffer_types; l; l = l->next)
97     if (l->data == obj->klass)
98       return TRUE;
99
100   return FALSE;
101 }
102
103 static GLenum
104 convert_bind_target_to_gl_target (CoglBufferBindTarget target)
105 {
106   switch (target)
107     {
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;
116       default:
117         g_return_val_if_reached (COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK);
118     }
119 }
120
121 static void *
122 _cogl_buffer_bind_no_create (CoglBuffer *buffer,
123                              CoglBufferBindTarget target)
124 {
125   CoglContext *ctx = buffer->context;
126
127   _COGL_RETURN_VAL_IF_FAIL (buffer != NULL, NULL);
128
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,
131                             NULL);
132
133   /* Don't allow nesting binds to the same target */
134   _COGL_RETURN_VAL_IF_FAIL (ctx->current_buffer[target] == NULL, NULL);
135
136   buffer->last_target = target;
137   ctx->current_buffer[target] = buffer;
138
139   if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT)
140     {
141       GLenum gl_target = convert_bind_target_to_gl_target (buffer->last_target);
142       GE( ctx, glBindBuffer (gl_target, buffer->gl_handle) );
143       return NULL;
144     }
145   else
146     return buffer->data;
147 }
148
149 static GLenum
150 _cogl_buffer_hints_to_gl_enum (CoglBufferUsageHint  usage_hint,
151                                CoglBufferUpdateHint update_hint)
152 {
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 */
159 #ifdef HAVE_COGL_GL
160   if (update_hint == COGL_BUFFER_UPDATE_HINT_STREAM)
161     return GL_STREAM_DRAW;
162 #endif
163
164   return GL_STATIC_DRAW;
165 }
166
167 static void
168 bo_recreate_store (CoglBuffer *buffer)
169 {
170   GLenum gl_target;
171   GLenum gl_enum;
172
173   /* This assumes the buffer is already bound */
174
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);
178
179   GE( buffer->context, glBufferData (gl_target,
180                                      buffer->size,
181                                      NULL,
182                                      gl_enum) );
183   buffer->store_created = TRUE;
184 }
185
186 static void *
187 bo_map (CoglBuffer       *buffer,
188         CoglBufferAccess  access,
189         CoglBufferMapHint hints)
190 {
191   guint8 *data;
192   CoglBufferBindTarget target;
193   GLenum gl_target;
194   CoglContext *ctx = buffer->context;
195
196   if ((access & COGL_BUFFER_ACCESS_READ) &&
197       !cogl_has_feature (ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_READ))
198     return NULL;
199   if ((access & COGL_BUFFER_ACCESS_WRITE) &&
200       !cogl_has_feature (ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE))
201     return NULL;
202
203   target = buffer->last_target;
204   _cogl_buffer_bind_no_create (buffer, target);
205
206   gl_target = convert_bind_target_to_gl_target (target);
207
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);
213
214   GE_RET( data, ctx, glMapBuffer (gl_target,
215                                   _cogl_buffer_access_to_gl_enum (access)) );
216   if (data)
217     buffer->flags |= COGL_BUFFER_FLAG_MAPPED;
218
219   _cogl_buffer_unbind (buffer);
220
221   return data;
222 }
223
224 static void
225 bo_unmap (CoglBuffer *buffer)
226 {
227   CoglContext *ctx = buffer->context;
228
229   _cogl_buffer_bind_no_create (buffer, buffer->last_target);
230
231   GE( ctx, glUnmapBuffer (convert_bind_target_to_gl_target
232                           (buffer->last_target)) );
233   buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED;
234
235   _cogl_buffer_unbind (buffer);
236 }
237
238 static gboolean
239 bo_set_data (CoglBuffer   *buffer,
240              unsigned int  offset,
241              const void   *data,
242              unsigned int  size)
243 {
244   CoglBufferBindTarget target;
245   GLenum gl_target;
246   CoglContext *ctx = buffer->context;
247
248   target = buffer->last_target;
249   _cogl_buffer_bind (buffer, target);
250
251   gl_target = convert_bind_target_to_gl_target (target);
252
253   GE( ctx, glBufferSubData (gl_target, offset, size, data) );
254
255   _cogl_buffer_unbind (buffer);
256
257   return TRUE;
258 }
259
260 /*
261  * Fallback path, buffer->data points to a malloc'ed buffer.
262  */
263
264 static void *
265 malloc_map (CoglBuffer       *buffer,
266             CoglBufferAccess  access,
267             CoglBufferMapHint hints)
268 {
269   buffer->flags |= COGL_BUFFER_FLAG_MAPPED;
270   return buffer->data;
271 }
272
273 static void
274 malloc_unmap (CoglBuffer *buffer)
275 {
276   buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED;
277 }
278
279 static gboolean
280 malloc_set_data (CoglBuffer   *buffer,
281                  unsigned int  offset,
282                  const void   *data,
283                  unsigned int  size)
284 {
285   memcpy (buffer->data + offset, data, size);
286   return TRUE;
287 }
288
289 void
290 _cogl_buffer_initialize (CoglBuffer           *buffer,
291                          CoglContext          *context,
292                          unsigned int          size,
293                          gboolean              use_malloc,
294                          CoglBufferBindTarget  default_target,
295                          CoglBufferUsageHint   usage_hint,
296                          CoglBufferUpdateHint  update_hint)
297 {
298   buffer->context       = cogl_object_ref (context);
299   buffer->flags         = COGL_BUFFER_FLAG_NONE;
300   buffer->store_created = FALSE;
301   buffer->size          = size;
302   buffer->last_target   = default_target;
303   buffer->usage_hint    = usage_hint;
304   buffer->update_hint   = update_hint;
305   buffer->data          = NULL;
306   buffer->immutable_ref = 0;
307
308   if (use_malloc)
309     {
310       buffer->vtable.map = malloc_map;
311       buffer->vtable.unmap = malloc_unmap;
312       buffer->vtable.set_data = malloc_set_data;
313
314       buffer->data = g_malloc (size);
315     }
316   else
317     {
318       buffer->vtable.map = bo_map;
319       buffer->vtable.unmap = bo_unmap;
320       buffer->vtable.set_data = bo_set_data;
321
322       GE( context, glGenBuffers (1, &buffer->gl_handle) );
323       buffer->flags |= COGL_BUFFER_FLAG_BUFFER_OBJECT;
324     }
325 }
326
327 void
328 _cogl_buffer_fini (CoglBuffer *buffer)
329 {
330   _COGL_RETURN_IF_FAIL (!(buffer->flags & COGL_BUFFER_FLAG_MAPPED));
331   _COGL_RETURN_IF_FAIL (buffer->immutable_ref == 0);
332
333   if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT)
334     GE( buffer->context, glDeleteBuffers (1, &buffer->gl_handle) );
335   else
336     g_free (buffer->data);
337
338   cogl_object_unref (buffer->context);
339 }
340
341 GLenum
342 _cogl_buffer_access_to_gl_enum (CoglBufferAccess access)
343 {
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;
348   else
349     return GL_READ_ONLY;
350 }
351
352 void *
353 _cogl_buffer_bind (CoglBuffer *buffer, CoglBufferBindTarget target)
354 {
355   void *ret;
356
357   ret = _cogl_buffer_bind_no_create (buffer, target);
358
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);
365
366   return ret;
367 }
368
369 void
370 _cogl_buffer_unbind (CoglBuffer *buffer)
371 {
372   CoglContext *ctx = buffer->context;
373
374   _COGL_RETURN_IF_FAIL (buffer != NULL);
375
376   /* the unbind should pair up with a previous bind */
377   _COGL_RETURN_IF_FAIL (ctx->current_buffer[buffer->last_target] == buffer);
378
379   if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT)
380     {
381       GLenum gl_target = convert_bind_target_to_gl_target (buffer->last_target);
382       GE( ctx, glBindBuffer (gl_target, 0) );
383     }
384
385   ctx->current_buffer[buffer->last_target] = NULL;
386 }
387
388 unsigned int
389 cogl_buffer_get_size (CoglBuffer *buffer)
390 {
391   if (!cogl_is_buffer (buffer))
392     return 0;
393
394   return COGL_BUFFER (buffer)->size;
395 }
396
397 void
398 cogl_buffer_set_update_hint (CoglBuffer *buffer,
399                              CoglBufferUpdateHint hint)
400 {
401   if (!cogl_is_buffer (buffer))
402     return;
403
404   if (G_UNLIKELY (hint > COGL_BUFFER_UPDATE_HINT_STREAM))
405     hint = COGL_BUFFER_UPDATE_HINT_STATIC;
406
407   buffer->update_hint = hint;
408 }
409
410 CoglBufferUpdateHint
411 cogl_buffer_get_update_hint (CoglBuffer *buffer)
412 {
413   if (!cogl_is_buffer (buffer))
414     return FALSE;
415
416   return buffer->update_hint;
417 }
418
419 static void
420 warn_about_midscene_changes (void)
421 {
422   static gboolean seen = FALSE;
423   if (!seen)
424     {
425       g_warning ("Mid-scene modification of buffers has "
426                  "undefined results\n");
427       seen = TRUE;
428     }
429 }
430
431 void *
432 cogl_buffer_map (CoglBuffer        *buffer,
433                  CoglBufferAccess   access,
434                  CoglBufferMapHint  hints)
435 {
436   _COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), NULL);
437
438   if (G_UNLIKELY (buffer->immutable_ref))
439     warn_about_midscene_changes ();
440
441   if (buffer->flags & COGL_BUFFER_FLAG_MAPPED)
442     return buffer->data;
443
444   buffer->data = buffer->vtable.map (buffer, access, hints);
445   return buffer->data;
446 }
447
448 void
449 cogl_buffer_unmap (CoglBuffer *buffer)
450 {
451   if (!cogl_is_buffer (buffer))
452     return;
453
454   if (!(buffer->flags & COGL_BUFFER_FLAG_MAPPED))
455     return;
456
457   buffer->vtable.unmap (buffer);
458 }
459
460 void *
461 _cogl_buffer_map_for_fill_or_fallback (CoglBuffer *buffer)
462 {
463   CoglContext *ctx = buffer->context;
464   void *ret;
465
466   _COGL_RETURN_VAL_IF_FAIL (!ctx->buffer_map_fallback_in_use, NULL);
467
468   ctx->buffer_map_fallback_in_use = TRUE;
469
470   ret = cogl_buffer_map (buffer,
471                          COGL_BUFFER_ACCESS_WRITE,
472                          COGL_BUFFER_MAP_HINT_DISCARD);
473
474   if (ret)
475     return ret;
476   else
477     {
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);
483
484       buffer->flags |= COGL_BUFFER_FLAG_MAPPED_FALLBACK;
485
486       return ctx->buffer_map_fallback_array->data;
487     }
488 }
489
490 void
491 _cogl_buffer_unmap_for_fill_or_fallback (CoglBuffer *buffer)
492 {
493   CoglContext *ctx = buffer->context;
494
495   _COGL_RETURN_IF_FAIL (ctx->buffer_map_fallback_in_use);
496
497   ctx->buffer_map_fallback_in_use = FALSE;
498
499   if ((buffer->flags & COGL_BUFFER_FLAG_MAPPED_FALLBACK))
500     {
501       cogl_buffer_set_data (buffer, 0,
502                             ctx->buffer_map_fallback_array->data,
503                             buffer->size);
504       buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED_FALLBACK;
505     }
506   else
507     cogl_buffer_unmap (buffer);
508 }
509
510 gboolean
511 cogl_buffer_set_data (CoglBuffer   *buffer,
512                       gsize         offset,
513                       const void   *data,
514                       gsize         size)
515 {
516   _COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), FALSE);
517   _COGL_RETURN_VAL_IF_FAIL ((offset + size) <= buffer->size, FALSE);
518
519   if (G_UNLIKELY (buffer->immutable_ref))
520     warn_about_midscene_changes ();
521
522   return buffer->vtable.set_data (buffer, offset, data, size);
523 }
524
525 CoglBuffer *
526 _cogl_buffer_immutable_ref (CoglBuffer *buffer)
527 {
528   _COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), NULL);
529
530   buffer->immutable_ref++;
531   return buffer;
532 }
533
534 void
535 _cogl_buffer_immutable_unref (CoglBuffer *buffer)
536 {
537   _COGL_RETURN_IF_FAIL (cogl_is_buffer (buffer));
538   _COGL_RETURN_IF_FAIL (buffer->immutable_ref > 0);
539
540   buffer->immutable_ref--;
541 }
542