4
* An object oriented GL/GLES Abstraction/Utility Layer
6
* Copyright (C) 2008,2009 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, write to the
20
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21
* Boston, MA 02111-1307, USA.
24
* Robert Bragg <robert@linux.intel.com>
27
/* XXX: For an overview of the functionality implemented here, please
28
* see cogl-vertex-buffer.h, which contains the gtk-doc section overview
29
* for the Vertex Buffers API.
33
* TODO: We need to do a better job of minimizing when we call glVertexPointer
34
* and pals in enable_state_for_drawing_buffer
36
* We should have an internal 2-tuple cache of (VBO, offset) for each of them
37
* so we can avoid some GL calls. We could have cogl wrappers for the
38
* gl*Pointer funcs that look like this:
40
* cogl_vertex_pointer (n_components, gl_type, stride, vbo, offset);
41
* cogl_color_pointer (n_components, gl_type, stride, vbo, offset);
43
* They would also accept NULL for the VBO handle to support old style vertex
47
* Actually hook this up to the cogl shaders infrastructure. The vertex
48
* buffer API has been designed to allow adding of arbitrary attributes for use
49
* with shaders, but this has yet to be actually plumbed together and tested.
50
* The bits we are missing:
51
* - cogl_program_use doesn't currently record within ctx-> which program
52
* is currently in use so a.t.m only Clutter knows the current shader.
53
* - We don't query the current shader program for the generic vertex indices
54
* (using glGetAttribLocation) so that we can call glEnableVertexAttribArray
56
* (currently we just make up consecutive indices)
57
* - some dirty flag mechanims to know when the shader program has changed
58
* so we don't need to re-query it each time we draw a buffer.
61
* There is currently no API for querying back info about a buffer, E.g.:
62
* cogl_vertex_buffer_get_n_vertices (buffer_handle);
63
* cogl_vertex_buffer_get_n_components (buffer_handle, "attrib_name");
64
* cogl_vertex_buffer_get_stride (buffer_handle, "attrib_name");
65
* cogl_vertex_buffer_get_normalized (buffer_handle, "attrib_name");
66
* cogl_vertex_buffer_map (buffer_handle, "attrib_name");
67
* cogl_vertex_buffer_unmap (buffer_handle, "attrib_name");
68
* (Realistically I wouldn't expect anyone to use such an API to examine the
69
* contents of a buffer for modification, since you'd need to handle too many
70
* possibilities, but never the less there might be other value in these.)
73
* It may be worth exposing the underlying VBOs for some advanced use
75
* handle = cogl_vbo_new (COGL_VBO_FLAG_STATIC);
76
* pointer = cogl_vbo_map (handle, COGL_VBO_FLAG_WRITEONLY);
77
* cogl_vbo_unmap (handle);
78
* cogl_vbo_set_data (handle, size, data);
79
* cogl_vbo_set_sub_data (handle, offset, size, data);
80
* cogl_vbo_set_usage_hint (COGL_VBO_FLAG_DYNAMIC);
83
* Experiment with wider use of the vertex buffers API internally to Cogl.
84
* - There is potential, I think, for this API to become a work-horse API
85
* within COGL for submitting geometry to the GPU, and could unify some of
86
* the GL/GLES code paths.
88
* - Try creating a per-context vertex buffer cache for cogl_texture_rectangle
90
* - Try saving the tesselation of paths/polygons into vertex buffers
94
* Expose API that lets developers get back a buffer handle for a particular
95
* polygon so they may add custom attributes to them.
96
* - It should be possible to query/modify attributes efficiently, in place,
97
* avoiding copies. It would not be acceptable to simply require that
98
* developers must query back the n_vertices of a buffer and then the
99
* n_components, type and stride etc of each attribute since there
100
* would be too many combinations to realistically handle.
102
* - In practice, some cases might be best solved with a higher level
103
* EditableMesh API, (see futher below) but for many cases I think an
104
* API like this might be appropriate:
106
* cogl_vertex_buffer_foreach_vertex (buffer_handle,
107
* (AttributesBufferIteratorFunc)callback,
108
* "gl_Vertex", "gl_Color", NULL);
109
* static void callback (CoglVertexBufferVertex *vert)
111
* GLfloat *pos = vert->attrib[0];
112
* GLubyte *color = vert->attrib[1];
113
* GLfloat *new_attrib = buf[vert->index];
115
* new_attrib = pos*color;
119
* Think about a higher level Mesh API for building/modifying attribute buffers
120
* - E.g. look at Blender for inspiration here. They can build a mesh from
121
* "MVert", "MFace" and "MEdge" primitives.
133
#include "cogl-internal.h"
134
#include "cogl-util.h"
135
#include "cogl-context.h"
136
#include "cogl-handle.h"
137
#include "cogl-vertex-buffer-private.h"
138
#include "cogl-texture-private.h"
139
#include "cogl-material-private.h"
140
#include "cogl-primitives.h"
142
#define PAD_FOR_ALIGNMENT(VAR, TYPE_SIZE) \
143
(VAR = TYPE_SIZE + ((VAR - 1) & ~(TYPE_SIZE - 1)))
147
* GL/GLES compatability defines for VBO thingies:
150
#if defined (HAVE_COGL_GL)
152
#define glGenBuffers ctx->pf_glGenBuffersARB
153
#define glBindBuffer ctx->pf_glBindBufferARB
154
#define glBufferData ctx->pf_glBufferDataARB
155
#define glBufferSubData ctx->pf_glBufferSubDataARB
156
#define glDeleteBuffers ctx->pf_glDeleteBuffersARB
157
#define glMapBuffer ctx->pf_glMapBufferARB
158
#define glUnmapBuffer ctx->pf_glUnmapBufferARB
159
#define glActiveTexture ctx->pf_glActiveTexture
160
#define glClientActiveTexture ctx->pf_glClientActiveTexture
161
#ifndef GL_ARRAY_BUFFER
162
#define GL_ARRAY_BUFFER GL_ARRAY_BUFFER_ARB
165
#elif defined (HAVE_COGL_GLES2)
167
#include "../gles/cogl-gles2-wrapper.h"
172
* GL/GLES compatability defines for shader things:
175
#if defined (HAVE_COGL_GL)
177
#define glVertexAttribPointer ctx->pf_glVertexAttribPointerARB
178
#define glEnableVertexAttribArray ctx->pf_glEnableVertexAttribArrayARB
179
#define glDisableVertexAttribArray ctx->pf_glEnableVertexAttribArrayARB
180
#define MAY_HAVE_PROGRAMABLE_GL
182
#elif defined (HAVE_COGL_GLES2)
184
/* NB: GLES2 had shaders in core since day one so again we don't need
185
* defines in this case: */
186
#define MAY_HAVE_PROGRAMABLE_GL
192
/* GLES doesn't have glDrawRangeElements, so we simply pretend it does
193
* but that it makes no use of the start, end constraints: */
194
#define glDrawRangeElements(mode, start, end, count, type, indices) \
195
glDrawElements (mode, count, type, indices)
197
#else /* HAVE_COGL_GL */
199
#define glDrawRangeElements(mode, start, end, count, type, indices) \
200
ctx->pf_glDrawRangeElements (mode, start, end, count, type, indices)
202
#endif /* HAVE_COGL_GL */
204
static void _cogl_vertex_buffer_free (CoglVertexBuffer *buffer);
205
static void _cogl_vertex_buffer_indices_free (CoglVertexBufferIndices *buffer_indices);
207
COGL_HANDLE_DEFINE (VertexBuffer, vertex_buffer);
208
COGL_HANDLE_DEFINE (VertexBufferIndices, vertex_buffer_indices);
211
cogl_vertex_buffer_new (guint n_vertices)
213
CoglVertexBuffer *buffer = g_slice_alloc (sizeof (CoglVertexBuffer));
215
buffer->n_vertices = n_vertices;
217
buffer->submitted_vbos = NULL;
218
buffer->new_attributes = NULL;
220
/* return COGL_INVALID_HANDLE; */
221
return _cogl_vertex_buffer_handle_new (buffer);
225
cogl_vertex_buffer_get_n_vertices (CoglHandle handle)
227
CoglVertexBuffer *buffer;
229
if (!cogl_is_vertex_buffer (handle))
232
buffer = _cogl_vertex_buffer_pointer_from_handle (handle);
234
return buffer->n_vertices;
237
/* There are a number of standard OpenGL attributes that we deal with
238
* specially. These attributes are all namespaced with a "gl_" prefix
239
* so we should catch any typos instead of silently adding a custom
242
static CoglVertexBufferAttribFlags
243
validate_gl_attribute (const char *gl_attribute,
245
guint8 *texture_unit)
247
CoglVertexBufferAttribFlags type;
248
char *detail_seperator = NULL;
251
detail_seperator = strstr (gl_attribute, "::");
252
if (detail_seperator)
253
name_len = detail_seperator - gl_attribute;
255
name_len = strlen (gl_attribute);
257
if (strncmp (gl_attribute, "Vertex", name_len) == 0)
259
if (G_UNLIKELY (n_components == 1))
260
g_critical ("glVertexPointer doesn't allow 1 component vertex "
261
"positions so we currently only support \"gl_Vertex\" "
262
"attributes where n_components == 2, 3 or 4");
263
type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_VERTEX_ARRAY;
265
else if (strncmp (gl_attribute, "Color", name_len) == 0)
267
if (G_UNLIKELY (n_components != 3 && n_components != 4))
268
g_critical ("glColorPointer expects 3 or 4 component colors so we "
269
"currently only support \"gl_Color\" attributes where "
270
"n_components == 3 or 4");
271
type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_COLOR_ARRAY;
273
else if (strncmp (gl_attribute,
275
strlen ("MultiTexCoord")) == 0)
279
if (sscanf (gl_attribute, "MultiTexCoord%u", &unit) != 1)
281
g_warning ("gl_MultiTexCoord attributes should include a\n"
282
"texture unit number, E.g. gl_MultiTexCoord0\n");
285
/* FIXME: validate any '::' delimiter for this case */
286
*texture_unit = unit;
287
type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_TEXTURE_COORD_ARRAY;
289
else if (strncmp (gl_attribute, "Normal", name_len) == 0)
291
if (G_UNLIKELY (n_components != 3))
292
g_critical ("glNormalPointer expects 3 component normals so we "
293
"currently only support \"gl_Normal\" attributes where "
294
"n_components == 3");
295
type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMAL_ARRAY;
299
g_warning ("Unknown gl_* attribute name gl_%s\n", gl_attribute);
300
type = COGL_VERTEX_BUFFER_ATTRIB_FLAG_INVALID;
306
/* This validates that a custom attribute name is a valid GLSL variable name
308
* NB: attribute names may have a detail component delimited using '::' E.g.
309
* custom_attrib::foo or custom_attrib::bar
311
* maybe I should hang a compiled regex somewhere to handle this
314
validate_custom_attribute_name (const char *attribute_name)
316
char *detail_seperator = NULL;
320
detail_seperator = strstr (attribute_name, "::");
321
if (detail_seperator)
322
name_len = detail_seperator - attribute_name;
324
name_len = strlen (attribute_name);
327
|| !g_ascii_isalpha (attribute_name[0])
328
|| attribute_name[0] != '_')
331
for (i = 1; i < name_len; i++)
332
if (!g_ascii_isalnum (attribute_name[i]) || attribute_name[i] != '_')
338
/* Iterates the CoglVertexBufferVBOs of a buffer and creates a flat list
339
* of all the submitted attributes
341
* Note: The CoglVertexBufferAttrib structs are deep copied.
344
copy_submitted_attributes_list (CoglVertexBuffer *buffer)
347
GList *submitted_attributes = NULL;
349
for (tmp = buffer->submitted_vbos; tmp != NULL; tmp = tmp->next)
351
CoglVertexBufferVBO *cogl_vbo = tmp->data;
354
for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next)
356
CoglVertexBufferAttrib *attribute = tmp2->data;
357
CoglVertexBufferAttrib *copy =
358
g_slice_alloc (sizeof (CoglVertexBufferAttrib));
360
submitted_attributes = g_list_prepend (submitted_attributes, copy);
363
return submitted_attributes;
366
static CoglVertexBufferAttribFlags
367
get_attribute_gl_type_flag_from_gl_type (GLenum gl_type)
372
return COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_BYTE;
373
case GL_UNSIGNED_BYTE:
374
return COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_UNSIGNED_BYTE;
376
return COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_SHORT;
377
case GL_UNSIGNED_SHORT:
378
return COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_UNSIGNED_SHORT;
380
return COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_FLOAT;
383
return COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_INT;
384
case GL_UNSIGNED_INT:
385
return COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_UNSIGNED_INT;
387
return COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_DOUBLE;
390
g_warning ("Attribute Buffers API: "
391
"Unrecognised OpenGL type enum 0x%08x\n", gl_type);
397
get_gl_type_size (CoglVertexBufferAttribFlags flags)
399
CoglVertexBufferAttribFlags gl_type =
400
flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_MASK;
404
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_BYTE:
405
return sizeof (GLbyte);
406
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_UNSIGNED_BYTE:
407
return sizeof (GLubyte);
408
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_SHORT:
409
return sizeof (GLshort);
410
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_UNSIGNED_SHORT:
411
return sizeof (GLushort);
412
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_FLOAT:
413
return sizeof (GLfloat);
415
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_INT:
416
return sizeof (GLint);
417
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_UNSIGNED_INT:
418
return sizeof (GLuint);
419
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_DOUBLE:
420
return sizeof (GLdouble);
423
g_warning ("Vertex Buffer API: Unrecognised OpenGL type enum 0x%08x\n", gl_type);
429
cogl_vertex_buffer_add (CoglHandle handle,
430
const char *attribute_name,
432
CoglAttributeType type,
437
CoglVertexBuffer *buffer;
438
GQuark name_quark = g_quark_from_string (attribute_name);
439
gboolean modifying_an_attrib = FALSE;
440
CoglVertexBufferAttrib *attribute;
441
CoglVertexBufferAttribFlags flags = 0;
442
guint8 texture_unit = 0;
445
if (!cogl_is_vertex_buffer (handle))
448
buffer = _cogl_vertex_buffer_pointer_from_handle (handle);
450
/* The submit function works by diffing between submitted_attributes
451
* and new_attributes to minimize the upload bandwidth + cost of
452
* allocating new VBOs, so if there isn't already a list of new_attributes
454
if (!buffer->new_attributes)
455
buffer->new_attributes = copy_submitted_attributes_list (buffer);
457
/* Note: we first look for an existing attribute that we are modifying
458
* so we may skip needing to validate the name */
459
for (tmp = buffer->new_attributes; tmp != NULL; tmp = tmp->next)
461
CoglVertexBufferAttrib *submitted_attribute = tmp->data;
462
if (submitted_attribute->name == name_quark)
464
modifying_an_attrib = TRUE;
466
attribute = submitted_attribute;
468
/* since we will skip validate_gl_attribute in this case, we need
469
* to pluck out the attribute type before overwriting the flags: */
471
attribute->flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_TYPE_MASK;
476
if (!modifying_an_attrib)
478
/* Validate the attribute name, is suitable as a variable name */
479
if (strncmp (attribute_name, "gl_", 3) == 0)
481
flags |= validate_gl_attribute (attribute_name + 3,
484
if (flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_INVALID)
489
flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_CUSTOM_ARRAY;
490
if (validate_custom_attribute_name (attribute_name))
494
attribute = g_slice_alloc (sizeof (CoglVertexBufferAttrib));
497
attribute->name = g_quark_from_string (attribute_name);
498
attribute->n_components = n_components;
499
attribute->stride = buffer->n_vertices > 1 ? stride : 0;
500
attribute->u.pointer = pointer;
501
attribute->texture_unit = texture_unit;
503
flags |= get_attribute_gl_type_flag_from_gl_type (type);
504
flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED;
506
/* Note: We currently just assume, if an attribute is *ever* updated
507
* then it should be taged as frequently changing. */
508
if (modifying_an_attrib)
509
flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_FREQUENT_RESUBMIT;
511
flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_INFREQUENT_RESUBMIT;
514
flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMALIZED;
515
attribute->flags = flags;
517
/* NB: get_gl_type_size must be called after setting the type
519
if (attribute->stride)
520
attribute->span_bytes = buffer->n_vertices * attribute->stride;
522
attribute->span_bytes = buffer->n_vertices
523
* attribute->n_components
524
* get_gl_type_size (attribute->flags);
526
if (!modifying_an_attrib)
527
buffer->new_attributes =
528
g_list_prepend (buffer->new_attributes, attribute);
532
cogl_vertex_buffer_delete (CoglHandle handle,
533
const char *attribute_name)
535
CoglVertexBuffer *buffer;
536
GQuark name = g_quark_from_string (attribute_name);
539
if (!cogl_is_vertex_buffer (handle))
542
buffer = _cogl_vertex_buffer_pointer_from_handle (handle);
544
/* The submit function works by diffing between submitted_attributes
545
* and new_attributes to minimize the upload bandwidth + cost of
546
* allocating new VBOs, so if there isn't already a list of new_attributes
548
if (!buffer->new_attributes)
549
buffer->new_attributes = copy_submitted_attributes_list (buffer);
551
for (tmp = buffer->new_attributes; tmp != NULL; tmp = tmp->next)
553
CoglVertexBufferAttrib *submitted_attribute = tmp->data;
554
if (submitted_attribute->name == name)
556
buffer->new_attributes =
557
g_list_delete_link (buffer->new_attributes, tmp);
558
g_slice_free (CoglVertexBufferAttrib, submitted_attribute);
563
g_warning ("Failed to find an attribute named %s to delete\n",
568
set_attribute_enable (CoglHandle handle,
569
const char *attribute_name,
572
CoglVertexBuffer *buffer;
573
GQuark name_quark = g_quark_from_string (attribute_name);
576
if (!cogl_is_vertex_buffer (handle))
579
buffer = _cogl_vertex_buffer_pointer_from_handle (handle);
581
/* NB: If a buffer is currently being edited, then there can be two seperate
582
* lists of attributes; those that are currently submitted and a new list yet
583
* to be submitted, we need to modify both. */
585
for (tmp = buffer->new_attributes; tmp != NULL; tmp = tmp->next)
587
CoglVertexBufferAttrib *attribute = tmp->data;
588
if (attribute->name == name_quark)
591
attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED;
593
attribute->flags &= ~COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED;
598
for (tmp = buffer->submitted_vbos; tmp != NULL; tmp = tmp->next)
600
CoglVertexBufferVBO *cogl_vbo = tmp->data;
603
for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next)
605
CoglVertexBufferAttrib *attribute = tmp2->data;
606
if (attribute->name == name_quark)
609
attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED;
611
attribute->flags &= ~COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED;
617
g_warning ("Failed to find an attribute named %s to %s\n",
619
state == TRUE ? "enable" : "disable");
623
cogl_vertex_buffer_enable (CoglHandle handle,
624
const char *attribute_name)
626
set_attribute_enable (handle, attribute_name, TRUE);
630
cogl_vertex_buffer_disable (CoglHandle handle,
631
const char *attribute_name)
633
set_attribute_enable (handle, attribute_name, FALSE);
637
cogl_vertex_buffer_attribute_free (CoglVertexBufferAttrib *attribute)
639
g_slice_free (CoglVertexBufferAttrib, attribute);
642
/* Given an attribute that we know has already been submitted before, this
643
* function looks for the existing VBO that contains it.
645
* Note: It will free redundant attribute struct once the corresponding
646
* VBO has been found.
649
filter_already_submitted_attribute (CoglVertexBufferAttrib *attribute,
651
GList **submitted_vbos)
655
/* First check the cogl_vbos we already know are being reused since we
656
* are more likley to get a match here */
657
for (tmp = *reuse_vbos; tmp != NULL; tmp = tmp->next)
659
CoglVertexBufferVBO *cogl_vbo = tmp->data;
662
for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next)
664
CoglVertexBufferAttrib *vbo_attribute = tmp2->data;
666
if (vbo_attribute->name == attribute->name)
668
vbo_attribute->flags &=
669
~COGL_VERTEX_BUFFER_ATTRIB_FLAG_UNUSED;
670
/* Note: we don't free the redundant attribute here, since it
671
* will be freed after all filtering in
672
* cogl_vertex_buffer_submit */
678
for (tmp = *submitted_vbos; tmp != NULL; tmp = tmp->next)
680
CoglVertexBufferVBO *cogl_vbo = tmp->data;
681
CoglVertexBufferAttrib *reuse_attribute = NULL;
684
for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next)
686
CoglVertexBufferAttrib *vbo_attribute = tmp2->data;
687
if (vbo_attribute->name == attribute->name)
689
reuse_attribute = vbo_attribute;
690
/* Note: we don't free the redundant attribute here, since it
691
* will be freed after all filtering in
692
* cogl_vertex_buffer_submit */
694
*submitted_vbos = g_list_remove_link (*submitted_vbos, tmp);
695
tmp->next = *reuse_vbos;
701
if (!reuse_attribute)
704
/* Mark all but the matched attribute as UNUSED, so that when we
705
* finish filtering all our attributes any attrributes still
706
* marked as UNUSED can be removed from the their cogl_vbo */
707
for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next)
709
CoglVertexBufferAttrib *vbo_attribute = tmp2->data;
710
if (vbo_attribute != reuse_attribute)
711
vbo_attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_UNUSED;
717
g_critical ("Failed to find the cogl vbo that corresponds to an\n"
718
"attribute that had apparently already been submitted!");
721
/* When we first mark a CoglVertexBufferVBO to be reused, we mark the
722
* attributes as unsed, so that when filtering of attributes into VBOs is done
723
* we can then prune the now unsed attributes. */
725
remove_unused_attributes (CoglVertexBufferVBO *cogl_vbo)
730
for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = next)
732
CoglVertexBufferAttrib *attribute = tmp->data;
735
if (attribute->flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_UNUSED)
737
cogl_vbo->attributes =
738
g_list_delete_link (cogl_vbo->attributes, tmp);
739
g_slice_free (CoglVertexBufferAttrib, attribute);
744
/* Give a newly added, strided, attribute, this function looks for a
745
* CoglVertexBufferVBO that the attribute is interleved with. If it can't
746
* find one then a new CoglVertexBufferVBO is allocated and added to the
747
* list of new_strided_vbos.
750
filter_strided_attribute (CoglVertexBufferAttrib *attribute,
754
CoglVertexBufferVBO *new_cogl_vbo;
756
for (tmp = *new_vbos; tmp != NULL; tmp = tmp->next)
758
CoglVertexBufferVBO *cogl_vbo = tmp->data;
761
if (!(cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_STRIDED))
764
for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next)
766
CoglVertexBufferAttrib *vbo_attribute = tmp2->data;
767
const char *attribute_start = attribute->u.pointer;
768
const char *vbo_attribute_start = vbo_attribute->u.pointer;
770
/* NB: All attributes have buffer->n_vertices values which
771
* simplifies determining which attributes are interleved
772
* since we assume they will start no farther than +- a
773
* stride away from each other:
775
if (attribute_start <= (vbo_attribute_start - vbo_attribute->stride)
777
>= (vbo_attribute_start + vbo_attribute->stride))
778
continue; /* Not interleved */
780
cogl_vbo->attributes =
781
g_list_prepend (cogl_vbo->attributes, attribute);
783
if (attribute->flags &
784
COGL_VERTEX_BUFFER_ATTRIB_FLAG_FREQUENT_RESUBMIT)
787
~COGL_VERTEX_BUFFER_VBO_FLAG_INFREQUENT_RESUBMIT;
789
COGL_VERTEX_BUFFER_VBO_FLAG_FREQUENT_RESUBMIT;
794
new_cogl_vbo = g_slice_alloc (sizeof (CoglVertexBufferVBO));
795
new_cogl_vbo->vbo_name = 0;
796
new_cogl_vbo->attributes = NULL;
797
new_cogl_vbo->attributes =
798
g_list_prepend (new_cogl_vbo->attributes, attribute);
799
/* Any one of the interleved attributes will have the same span_bytes */
800
new_cogl_vbo->vbo_bytes = attribute->span_bytes;
801
new_cogl_vbo->flags = COGL_VERTEX_BUFFER_VBO_FLAG_STRIDED;
803
if (attribute->flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_INFREQUENT_RESUBMIT)
804
new_cogl_vbo->flags |= COGL_VERTEX_BUFFER_VBO_FLAG_INFREQUENT_RESUBMIT;
806
new_cogl_vbo->flags |= COGL_VERTEX_BUFFER_VBO_FLAG_FREQUENT_RESUBMIT;
808
*new_vbos = g_list_prepend (*new_vbos, new_cogl_vbo);
812
/* This iterates through the list of submitted VBOs looking for one that
813
* contains attribute. If found the list *link* is removed and returned */
815
unlink_submitted_vbo_containing_attribute (GList **submitted_vbos,
816
CoglVertexBufferAttrib *attribute)
821
for (tmp = *submitted_vbos; tmp != NULL; tmp = next)
823
CoglVertexBufferVBO *submitted_vbo = tmp->data;
828
for (tmp2 = submitted_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next)
830
CoglVertexBufferAttrib *submitted_attribute = tmp2->data;
832
if (submitted_attribute->name == attribute->name)
834
*submitted_vbos = g_list_remove_link (*submitted_vbos, tmp);
843
/* Unlinks all the submitted VBOs that conflict with the new cogl_vbo and
844
* returns them as a list. */
846
get_submitted_vbo_conflicts (GList **submitted_vbos,
847
CoglVertexBufferVBO *cogl_vbo)
850
GList *conflicts = NULL;
852
for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next)
855
unlink_submitted_vbo_containing_attribute (submitted_vbos,
859
/* prepend the link to the list of conflicts: */
860
link->next = conflicts;
867
/* Any attributes in cogl_vbo gets removed from conflict_vbo */
869
disassociate_conflicting_attributes (CoglVertexBufferVBO *conflict_vbo,
870
CoglVertexBufferVBO *cogl_vbo)
874
/* NB: The attributes list in conflict_vbo will be shrinking so
875
* we iterate those in the inner loop. */
877
for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next)
879
CoglVertexBufferAttrib *attribute = tmp->data;
881
for (tmp2 = conflict_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next)
883
CoglVertexBufferAttrib *conflict_attribute = tmp2->data;
885
if (conflict_attribute->name == attribute->name)
887
cogl_vertex_buffer_attribute_free (conflict_attribute);
888
conflict_vbo->attributes =
889
g_list_delete_link (conflict_vbo->attributes, tmp2);
897
cogl_vertex_buffer_vbo_free (CoglVertexBufferVBO *cogl_vbo,
898
gboolean delete_gl_vbo)
902
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
904
for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next)
906
cogl_vertex_buffer_attribute_free (tmp->data);
908
g_list_free (cogl_vbo->attributes);
910
if (delete_gl_vbo && cogl_vbo->flags &
911
COGL_VERTEX_BUFFER_VBO_FLAG_SUBMITTED)
913
if (cogl_get_features () & COGL_FEATURE_VBOS)
914
GE (glDeleteBuffers (1, (GLuint *)&cogl_vbo->vbo_name));
916
g_free (cogl_vbo->vbo_name);
919
g_slice_free (CoglVertexBufferVBO, cogl_vbo);
922
/* This figures out the lowest attribute client pointer. (This pointer is used
923
* to upload all the interleved attributes).
925
* In the process it also replaces the client pointer with the attributes
926
* offset, and marks the attribute as submitted.
929
prep_strided_vbo_for_upload (CoglVertexBufferVBO *cogl_vbo)
932
const char *lowest_pointer = NULL;
934
for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next)
936
CoglVertexBufferAttrib *attribute = tmp->data;
937
const char *client_pointer = attribute->u.pointer;
939
if (!lowest_pointer || client_pointer < lowest_pointer)
940
lowest_pointer = client_pointer;
943
for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next)
945
CoglVertexBufferAttrib *attribute = tmp->data;
946
const char *client_pointer = attribute->u.pointer;
947
attribute->u.vbo_offset = client_pointer - lowest_pointer;
948
attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED;
951
return lowest_pointer;
955
upload_multipack_vbo_via_map_buffer (CoglVertexBufferVBO *cogl_vbo)
962
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
964
_COGL_GET_CONTEXT (ctx, FALSE);
968
buf = glMapBuffer (GL_ARRAY_BUFFER, GL_WRITE_ONLY);
972
buf = cogl_vbo->vbo_name;
977
for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next)
979
CoglVertexBufferAttrib *attribute = tmp->data;
980
gsize attribute_size = attribute->span_bytes;
981
gsize gl_type_size = get_gl_type_size (attribute->flags);
983
PAD_FOR_ALIGNMENT (offset, gl_type_size);
985
memcpy (buf + offset, attribute->u.pointer, attribute_size);
987
attribute->u.vbo_offset = offset;
988
attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED;
989
offset += attribute_size;
993
glUnmapBuffer (GL_ARRAY_BUFFER);
1002
upload_multipack_vbo_via_buffer_sub_data (CoglVertexBufferVBO *cogl_vbo)
1007
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
1009
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
1011
for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next)
1013
CoglVertexBufferAttrib *attribute = tmp->data;
1014
gsize attribute_size = attribute->span_bytes;
1015
gsize gl_type_size = get_gl_type_size (attribute->flags);
1017
PAD_FOR_ALIGNMENT (offset, gl_type_size);
1021
GE (glBufferSubData (GL_ARRAY_BUFFER,
1024
attribute->u.pointer));
1028
char *dest = (char *)cogl_vbo->vbo_name + offset;
1029
memcpy (dest, attribute->u.pointer, attribute_size);
1032
attribute->u.vbo_offset = offset;
1033
attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED;
1034
offset += attribute_size;
1039
upload_gl_vbo (CoglVertexBufferVBO *cogl_vbo)
1043
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
1045
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
1047
if (cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_FREQUENT_RESUBMIT)
1048
usage = GL_DYNAMIC_DRAW;
1050
usage = GL_STATIC_DRAW;
1054
g_return_if_fail (cogl_vbo->vbo_name != 0);
1056
GE (glBindBuffer (GL_ARRAY_BUFFER,
1057
GPOINTER_TO_UINT (cogl_vbo->vbo_name)));
1059
else if (cogl_vbo->vbo_name == NULL)
1061
/* If the driver doesn't support VBOs then we simply allocate
1062
* a client side fake vbo buffer. Unlike VBOs we can't allocate
1063
* without specifying a size which is why we defer allocation
1065
cogl_vbo->vbo_name = g_malloc (cogl_vbo->vbo_bytes);
1068
if (cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_STRIDED)
1070
const void *pointer =
1071
prep_strided_vbo_for_upload (cogl_vbo);
1074
GE (glBufferData (GL_ARRAY_BUFFER,
1075
cogl_vbo->vbo_bytes,
1080
memcpy (cogl_vbo->vbo_name, pointer, cogl_vbo->vbo_bytes);
1082
else if (cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_MULTIPACK)
1084
/* First we make it obvious to the driver that we want to update the
1085
* whole buffer (without this, the driver is more likley to block
1086
* if the GPU is busy using the buffer) */
1089
GE (glBufferData (GL_ARRAY_BUFFER,
1090
cogl_vbo->vbo_bytes,
1095
/* I think it might depend on the specific driver/HW whether its better
1096
* to use glMapBuffer here or glBufferSubData here. There is even a good
1097
* thread about this topic here:
1098
* http://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg35004.html
1099
* For now I have gone with glMapBuffer, but the jury is still out.
1102
if (!upload_multipack_vbo_via_map_buffer (cogl_vbo))
1103
upload_multipack_vbo_via_buffer_sub_data (cogl_vbo);
1107
CoglVertexBufferAttrib *attribute = cogl_vbo->attributes->data;
1110
GE (glBufferData (GL_ARRAY_BUFFER,
1111
cogl_vbo->vbo_bytes,
1112
attribute->u.pointer,
1116
memcpy (cogl_vbo->vbo_name, attribute->u.pointer, cogl_vbo->vbo_bytes);
1118
/* We forget this pointer now since the client will be free
1119
* to re-use this memory */
1120
attribute->u.pointer = NULL;
1121
attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED;
1124
cogl_vbo->flags |= COGL_VERTEX_BUFFER_VBO_FLAG_SUBMITTED;
1127
GE (glBindBuffer (GL_ARRAY_BUFFER, 0));
1130
/* Note: although there ends up being quite a few inner loops involved with
1131
* resolving buffers, the number of attributes will be low so I don't expect
1132
* them to cause a problem. */
1134
cogl_vertex_buffer_vbo_resolve (CoglVertexBuffer *buffer,
1135
CoglVertexBufferVBO *new_cogl_vbo,
1141
gboolean found_target_vbo = FALSE;
1143
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
1146
get_submitted_vbo_conflicts (&buffer->submitted_vbos, new_cogl_vbo);
1148
for (tmp = conflicts; tmp != NULL; tmp = next)
1150
CoglVertexBufferVBO *conflict_vbo = tmp->data;
1154
disassociate_conflicting_attributes (conflict_vbo, new_cogl_vbo);
1156
if (!conflict_vbo->attributes)
1158
/* See if we can re-use this now empty VBO: */
1160
if (!found_target_vbo
1161
&& conflict_vbo->vbo_bytes == new_cogl_vbo->vbo_bytes)
1163
found_target_vbo = TRUE;
1164
new_cogl_vbo->vbo_name = conflict_vbo->vbo_name;
1165
cogl_vertex_buffer_vbo_free (conflict_vbo, FALSE);
1167
upload_gl_vbo (new_cogl_vbo);
1169
*final_vbos = g_list_prepend (*final_vbos, new_cogl_vbo);
1172
cogl_vertex_buffer_vbo_free (conflict_vbo, TRUE);
1176
/* Relink the VBO back into buffer->submitted_vbos since it may
1177
* be involved in other conflicts later */
1178
tmp->next = buffer->submitted_vbos;
1180
buffer->submitted_vbos = tmp;
1184
if (!found_target_vbo)
1186
if (cogl_get_features () & COGL_FEATURE_VBOS)
1187
GE (glGenBuffers (1, (GLuint *)&new_cogl_vbo->vbo_name));
1189
new_cogl_vbo->vbo_name = NULL;
1190
/* this will be allocated at upload time */
1192
upload_gl_vbo (new_cogl_vbo);
1193
*final_vbos = g_list_prepend (*final_vbos, new_cogl_vbo);
1198
cogl_vertex_buffer_submit_real (CoglVertexBuffer *buffer)
1201
CoglVertexBufferVBO *new_multipack_vbo;
1202
GList *new_multipack_vbo_link;
1203
GList *new_vbos = NULL;
1204
GList *reuse_vbos = NULL;
1205
GList *final_vbos = NULL;
1207
if (!buffer->new_attributes)
1210
/* The objective now is to copy the attribute data supplied by the client
1211
* into buffer objects, but it's important to minimize the number of
1212
* redundant data uploads.
1214
* We obviously aim to group together the attributes that are interleved so
1215
* that they can be delivered in one go to the driver.
1216
* All BOs for interleved data are created as STATIC_DRAW_ARB.
1218
* Non interleved attributes tagged as INFREQUENT_RESUBMIT will be grouped
1219
* together back to back in a single BO created as STATIC_DRAW_ARB
1221
* Non interleved attributes tagged as FREQUENT_RESUBMIT will be copied into
1222
* individual buffer objects, and the BO itself created DYNAMIC_DRAW_ARB
1224
* If we are modifying a previously submitted CoglVertexBuffer then we are
1225
* carefull not to needlesly delete OpenGL buffer objects and replace with
1226
* new ones, instead we upload new data to the existing buffers.
1229
/* NB: We must forget attribute->pointer after submitting since the user
1230
* is free to re-use that memory for other purposes now. */
1234
* Broadly speaking we start with a list of unsorted attributes, and filter
1235
* those into 'new' and 're-use' CoglVertexBufferVBO (CBO) lists. We then
1236
* take the list of new CBO structs and compare with the CBOs that have
1237
* already been submitted to the GPU (but ignoring those we already know will
1238
* be re-used) to determine what other CBOs can be re-used, due to being
1239
* superseded, and what new GL VBOs need to be created.
1241
* We have three kinds of CBOs:
1243
* These contain a single tightly packed attribute
1244
* These are currently the only ones ever marked as FREQUENT_SUBMIT
1246
* These typically contain multiple interleved sets of attributes,
1247
* though they can contain just one attribute with a stride
1249
* These contain multiple attributes tightly packed back to back)
1251
* First create a new-CBOs entry "new-multipack-CBO"
1252
* Tag "new-multipack-CBO" as MULTIPACK + INFREQUENT_RESUBMIT
1253
* For each unsorted attrib:
1254
* if already marked as submitted:
1255
* iterate reuse-CBOs:
1256
* if we find one that contains this attribute:
1257
* free redundant unsorted attrib struct
1258
* remove the UNUSED flag from the attrib found in the reuse-CBO
1259
* continue to next unsorted attrib
1260
* iterate submitted VBOs:
1261
* if we find one that contains this attribute:
1262
* free redundant unsorted attrib struct
1263
* unlink the vbo and move it to the list of reuse-CBOs
1264
* mark all attributes except the one just matched as UNUSED
1266
* continue to next unsorted attrib
1268
* iterate the new, strided, CBOs, to see if the attribute is
1269
* interleved with one of them, if found:
1270
* add to the matched CBO
1271
* else if not found:
1272
* create a new-CBOs entry tagged STRIDED + INFREQUENT_RESUBMIT
1273
* else if unstrided && tagged with FREQUENT_RESUBMIT:
1274
* create a new-CBOs entry tagged UNSTRIDED + FREQUENT_RESUBMIT
1276
* add to the new-multipack-CBO
1277
* free list of unsorted-attribs
1279
* Next compare the new list of CBOs with the submitted set and try to
1280
* minimize the memory bandwidth required to upload the attributes and the
1281
* overhead of creating new GL-BOs.
1283
* We deal with four sets of CBOs:
1285
* (as determined above during filtering)
1286
* - The "re-use" CBOs
1287
* (as determined above during filtering)
1288
* - The "submitted" CBOs
1289
* (I.e. ones currently submitted to the GPU)
1290
* - The "final" CBOs
1291
* (The result of resolving the differences between the above sets)
1293
* The re-use CBOs are dealt with first, and we simply delete any remaining
1294
* attributes in these that are still marked as UNUSED, and move them
1295
* to the list of final CBOs.
1297
* Next we iterate through the "new" CBOs, searching for conflicts
1298
* with the "submitted" CBOs and commit our decision to the "final" CBOs
1300
* When searching for submitted entries we always unlink items from the
1301
* submitted list once we make matches (before we make descisions
1302
* based on the matches). If the CBO node is superseded it is freed,
1303
* if it is modified but may be needed for more descisions later it is
1304
* relinked back into the submitted list and if it's identical to a new
1305
* CBO it will be linked into the final list.
1307
* At the end the list of submitted CBOs represents the attributes that were
1308
* deleted from the buffer.
1310
* Iterate re-use-CBOs:
1311
* Iterate attribs for each:
1313
* remove the attrib from the CBO + free
1314
* |Note: we could potentially mark this as a re-useable gap
1315
* |if needs be later.
1316
* add re-use CBO to the final-CBOs
1318
* List submitted CBOs conflicting with the this CBO (Unlinked items)
1319
* found-target-BO=FALSE
1320
* Iterate conflicting CBOs:
1321
* Disassociate conflicting attribs from conflicting CBO struct
1322
* If no attribs remain:
1323
* If found-target-BO!=TRUE
1324
* _AND_ If the total size of the conflicting CBO is compatible:
1325
* |Note: We don't currently consider re-using oversized buffers
1326
* found-target-BO=TRUE
1327
* upload replacement data
1328
* free submitted CBO struct
1329
* add new CBO struct to final-CBOs
1331
* delete conflict GL-BO
1332
* delete conflict CBO struct
1334
* relink CBO back into submitted-CBOs
1336
* if found-target-BO == FALSE:
1337
* create a new GL-BO
1339
* add new CBO struct to final-BOs
1341
* Iterate through the remaining "submitted" CBOs:
1342
* delete the submitted GL-BO
1343
* free the submitted CBO struct
1346
new_multipack_vbo = g_slice_alloc (sizeof (CoglVertexBufferVBO));
1347
new_multipack_vbo->vbo_name = 0;
1348
new_multipack_vbo->flags =
1349
COGL_VERTEX_BUFFER_VBO_FLAG_MULTIPACK
1350
| COGL_VERTEX_BUFFER_VBO_FLAG_INFREQUENT_RESUBMIT;
1351
new_multipack_vbo->vbo_bytes = 0;
1352
new_multipack_vbo->attributes = NULL;
1353
new_vbos = g_list_prepend (new_vbos, new_multipack_vbo);
1354
/* We save the link pointer here, just so we can do a fast removal later if
1355
* no attributes get added to this vbo. */
1356
new_multipack_vbo_link = new_vbos;
1358
/* Start with a list of unsorted attributes, and filter those into
1359
* potential new Cogl BO structs
1361
for (tmp = buffer->new_attributes; tmp != NULL; tmp = tmp->next)
1363
CoglVertexBufferAttrib *attribute = tmp->data;
1365
if (attribute->flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED)
1367
/* If the attribute is already marked as submitted, then we need
1368
* to find the existing VBO that contains it so we dont delete it.
1370
* NB: this also frees the attribute struct since it's implicitly
1371
* redundant in this case.
1373
filter_already_submitted_attribute (attribute,
1375
&buffer->submitted_vbos);
1377
else if (attribute->stride)
1379
/* look for a CoglVertexBufferVBO that the attribute is
1380
* interleved with. If one can't be found then a new
1381
* CoglVertexBufferVBO is allocated and added to the list of
1383
filter_strided_attribute (attribute, &new_vbos);
1385
else if (attribute->flags &
1386
COGL_VERTEX_BUFFER_ATTRIB_FLAG_FREQUENT_RESUBMIT)
1388
CoglVertexBufferVBO *cogl_vbo =
1389
g_slice_alloc (sizeof (CoglVertexBufferVBO));
1391
/* attributes we expect will be frequently resubmitted are placed
1392
* in their own VBO so that updates don't impact other attributes
1395
cogl_vbo->vbo_name = 0;
1397
COGL_VERTEX_BUFFER_VBO_FLAG_UNSTRIDED
1398
| COGL_VERTEX_BUFFER_VBO_FLAG_FREQUENT_RESUBMIT;
1399
cogl_vbo->attributes = NULL;
1400
cogl_vbo->attributes = g_list_prepend (cogl_vbo->attributes,
1402
cogl_vbo->vbo_bytes = attribute->span_bytes;
1403
new_vbos = g_list_prepend (new_vbos, cogl_vbo);
1407
gsize gl_type_size = get_gl_type_size (attribute->flags);
1409
/* Infrequently updated attributes just get packed back to back
1410
* in a single VBO: */
1411
new_multipack_vbo->attributes =
1412
g_list_prepend (new_multipack_vbo->attributes,
1415
/* Note: we have to ensure that each run of attributes is
1416
* naturally aligned according to its data type, which may
1417
* require some padding bytes: */
1419
/* XXX: We also have to be sure that the attributes aren't
1420
* reorderd before being uploaded because the alignment padding
1421
* is based on the adjacent attribute.
1424
PAD_FOR_ALIGNMENT (new_multipack_vbo->vbo_bytes, gl_type_size);
1426
new_multipack_vbo->vbo_bytes += attribute->span_bytes;
1430
/* At this point all buffer->new_attributes have been filtered into
1431
* CoglVertexBufferVBOs... */
1432
g_list_free (buffer->new_attributes);
1433
buffer->new_attributes = NULL;
1435
/* If the multipack vbo wasn't needed: */
1436
if (new_multipack_vbo->attributes == NULL)
1438
new_vbos = g_list_delete_link (new_vbos, new_multipack_vbo_link);
1439
g_slice_free (CoglVertexBufferVBO, new_multipack_vbo);
1442
for (tmp = reuse_vbos; tmp != NULL; tmp = tmp->next)
1443
remove_unused_attributes (tmp->data);
1444
final_vbos = g_list_concat (final_vbos, reuse_vbos);
1446
for (tmp = new_vbos; tmp != NULL; tmp = tmp->next)
1447
cogl_vertex_buffer_vbo_resolve (buffer, tmp->data, &final_vbos);
1449
/* Anything left corresponds to deleted attributes: */
1450
for (tmp = buffer->submitted_vbos; tmp != NULL; tmp = tmp->next)
1451
cogl_vertex_buffer_vbo_free (tmp->data, TRUE);
1452
g_list_free (buffer->submitted_vbos);
1453
g_list_free (new_vbos);
1455
buffer->submitted_vbos = final_vbos;
1459
cogl_vertex_buffer_submit (CoglHandle handle)
1461
CoglVertexBuffer *buffer;
1463
if (!cogl_is_vertex_buffer (handle))
1466
buffer = _cogl_vertex_buffer_pointer_from_handle (handle);
1468
cogl_vertex_buffer_submit_real (buffer);
1472
get_gl_type_from_attribute_flags (CoglVertexBufferAttribFlags flags)
1474
CoglVertexBufferAttribFlags gl_type =
1475
flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_MASK;
1479
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_BYTE:
1481
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_UNSIGNED_BYTE:
1482
return GL_UNSIGNED_BYTE;
1483
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_SHORT:
1485
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_UNSIGNED_SHORT:
1486
return GL_UNSIGNED_SHORT;
1487
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_FLOAT:
1490
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_INT:
1492
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_UNSIGNED_INT:
1493
return GL_UNSIGNED_INT;
1494
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_GL_TYPE_DOUBLE:
1498
g_warning ("Couldn't convert from attribute flags (0x%08x) "
1499
"to gl type enum\n", flags);
1505
enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
1509
#ifdef MAY_HAVE_PROGRAMABLE_GL
1510
GLuint generic_index = 0;
1512
gulong enable_flags = 0;
1513
guint max_texcoord_attrib_unit = 0;
1514
const GList *layers;
1515
guint32 fallback_layers = 0;
1516
guint32 disable_layers = ~0;
1518
CoglMaterialFlushOptions options;
1520
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
1522
if (buffer->new_attributes)
1523
cogl_vertex_buffer_submit_real (buffer);
1525
for (tmp = buffer->submitted_vbos; tmp != NULL; tmp = tmp->next)
1527
CoglVertexBufferVBO *cogl_vbo = tmp->data;
1530
const GLvoid *pointer;
1532
if (cogl_get_features () & COGL_FEATURE_VBOS)
1534
GE (glBindBuffer (GL_ARRAY_BUFFER,
1535
GPOINTER_TO_UINT (cogl_vbo->vbo_name)));
1539
base = cogl_vbo->vbo_name;
1541
/* When GL VBOs are bing used then the "pointer" we pass to
1542
* glColorPointer glVertexAttribPointer etc is actually an offset into
1543
* the currently bound VBO.
1545
* If we don't have VBO support though, then we must point into
1546
* our fake client side VBO.
1549
for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next)
1551
CoglVertexBufferAttrib *attribute = tmp2->data;
1552
CoglVertexBufferAttribFlags type =
1553
attribute->flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_TYPE_MASK;
1555
if (!(attribute->flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED))
1558
gl_type = get_gl_type_from_attribute_flags (attribute->flags);
1561
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_COLOR_ARRAY:
1562
enable_flags |= COGL_ENABLE_COLOR_ARRAY | COGL_ENABLE_BLEND;
1563
/* GE (glEnableClientState (GL_COLOR_ARRAY)); */
1564
pointer = (const GLvoid *)(base + attribute->u.vbo_offset);
1565
GE (glColorPointer (attribute->n_components,
1570
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMAL_ARRAY:
1571
/* FIXME: go through cogl cache to enable normal array */
1572
GE (glEnableClientState (GL_NORMAL_ARRAY));
1573
pointer = (const GLvoid *)(base + attribute->u.vbo_offset);
1574
GE (glNormalPointer (gl_type,
1578
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_TEXTURE_COORD_ARRAY:
1579
GE (glClientActiveTexture (GL_TEXTURE0 +
1580
attribute->texture_unit));
1581
GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY));
1582
pointer = (const GLvoid *)(base + attribute->u.vbo_offset);
1583
GE (glTexCoordPointer (attribute->n_components,
1587
if (attribute->texture_unit > max_texcoord_attrib_unit)
1588
max_texcoord_attrib_unit = attribute->texture_unit;
1589
disable_layers &= ~(1 << attribute->texture_unit);
1591
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_VERTEX_ARRAY:
1592
enable_flags |= COGL_ENABLE_VERTEX_ARRAY;
1593
/* GE (glEnableClientState (GL_VERTEX_ARRAY)); */
1594
pointer = (const GLvoid *)(base + attribute->u.vbo_offset);
1595
GE (glVertexPointer (attribute->n_components,
1600
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_CUSTOM_ARRAY:
1602
#ifdef MAY_HAVE_PROGRAMABLE_GL
1603
GLboolean normalized = GL_FALSE;
1604
if (attribute->flags &
1605
COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMALIZED)
1606
normalized = GL_TRUE;
1607
/* FIXME: go through cogl cache to enable generic array */
1608
GE (glEnableVertexAttribArray (generic_index++));
1609
pointer = (const GLvoid *)(base + attribute->u.vbo_offset);
1610
GE (glVertexAttribPointer (generic_index,
1611
attribute->n_components,
1620
g_warning ("Unrecognised attribute type 0x%08x", type);
1625
layers = cogl_material_get_layers (ctx->source_material);
1626
for (tmp = (GList *)layers, i = 0;
1627
tmp != NULL && i <= max_texcoord_attrib_unit;
1628
tmp = tmp->next, i++)
1630
CoglHandle layer = (CoglHandle)tmp->data;
1631
CoglHandle tex_handle = cogl_material_layer_get_texture (layer);
1632
CoglTexture *texture;
1634
/* invalid textures will be handled correctly in
1635
* _cogl_material_flush_layers_gl_state */
1636
if (tex_handle == COGL_INVALID_HANDLE)
1639
texture = _cogl_texture_pointer_from_handle (tex_handle);
1641
if (cogl_texture_is_sliced (tex_handle)
1642
|| _cogl_texture_span_has_waste (texture, 0, 0))
1644
g_warning ("Disabling layer %d of the current source material, "
1645
"because texturing with the vertex buffer API is not "
1646
"currently supported using sliced textures, or textures "
1649
/* XXX: maybe we can add a mechanism for users to forcibly use
1650
* textures with waste where it would be their responsability to use
1651
* texture coords in the range [0,1] such that sampling outside isn't
1652
* required. We can then use a texture matrix (or a modification of
1653
* the users own matrix) to map 1 to the edge of the texture data.
1655
* Potentially, given the same guarantee as above we could also
1656
* support a single sliced layer too. We would have to redraw the
1657
* vertices once for each layer, each time with a fiddled texture
1660
fallback_layers |= (1 << i);
1664
for (i = max_texcoord_attrib_unit + 1; i < ctx->n_texcoord_arrays_enabled; i++)
1666
GE (glClientActiveTexture (GL_TEXTURE0 + i));
1667
GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY));
1669
ctx->n_texcoord_arrays_enabled = max_texcoord_attrib_unit + 1;
1672
COGL_MATERIAL_FLUSH_FALLBACK_MASK |
1673
COGL_MATERIAL_FLUSH_DISABLE_MASK;
1674
options.fallback_layers = fallback_layers;
1675
options.disable_layers = disable_layers;
1677
_cogl_material_flush_gl_state (ctx->source_material, &options);
1678
enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material);
1680
if (ctx->enable_backface_culling)
1681
enable_flags |= COGL_ENABLE_BACKFACE_CULLING;
1683
cogl_enable (enable_flags);
1687
disable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
1691
#ifdef MAY_HAVE_PROGRAMABLE_GL
1692
GLuint generic_index = 0;
1695
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
1697
/* Disable all the client state that cogl doesn't currently know
1700
if (cogl_get_features () & COGL_FEATURE_VBOS)
1701
GE (glBindBuffer (GL_ARRAY_BUFFER, 0));
1703
for (tmp = buffer->submitted_vbos; tmp != NULL; tmp = tmp->next)
1705
CoglVertexBufferVBO *cogl_vbo = tmp->data;
1708
for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next)
1710
CoglVertexBufferAttrib *attribute = tmp2->data;
1711
CoglVertexBufferAttribFlags type =
1712
attribute->flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_TYPE_MASK;
1714
if (!(attribute->flags & COGL_VERTEX_BUFFER_ATTRIB_FLAG_ENABLED))
1717
gl_type = get_gl_type_from_attribute_flags(attribute->flags);
1720
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_COLOR_ARRAY:
1721
/* GE (glDisableClientState (GL_COLOR_ARRAY)); */
1723
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMAL_ARRAY:
1724
/* FIXME: go through cogl cache to enable normal array */
1725
GE (glDisableClientState (GL_NORMAL_ARRAY));
1727
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_TEXTURE_COORD_ARRAY:
1728
GE (glClientActiveTexture (GL_TEXTURE0 +
1729
attribute->texture_unit));
1730
GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY));
1732
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_VERTEX_ARRAY:
1733
/* GE (glDisableClientState (GL_VERTEX_ARRAY)); */
1735
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_CUSTOM_ARRAY:
1736
#ifdef MAY_HAVE_PROGRAMABLE_GL
1737
/* FIXME: go through cogl cache to enable generic array */
1738
GE (glDisableVertexAttribArray (generic_index++));
1742
g_warning ("Unrecognised attribute type 0x%08x", type);
1749
cogl_vertex_buffer_draw (CoglHandle handle,
1750
CoglVerticesMode mode,
1754
CoglVertexBuffer *buffer;
1756
if (!cogl_is_vertex_buffer (handle))
1759
_cogl_journal_flush ();
1760
cogl_clip_ensure ();
1762
buffer = _cogl_vertex_buffer_pointer_from_handle (handle);
1764
cogl_clip_ensure ();
1765
_cogl_flush_matrix_stacks ();
1766
enable_state_for_drawing_buffer (buffer);
1768
/* FIXME: flush cogl cache */
1769
GE (glDrawArrays (mode, first, count));
1771
disable_state_for_drawing_buffer (buffer);
1775
get_indices_type_size (GLuint indices_type)
1777
if (indices_type == GL_UNSIGNED_BYTE)
1778
return sizeof (GLubyte);
1779
if (indices_type == GL_UNSIGNED_SHORT)
1780
return sizeof (GLushort);
1783
g_critical ("Unknown indices type %d\n", indices_type);
1789
cogl_vertex_buffer_indices_new (CoglIndicesType indices_type,
1790
const void *indices_array,
1794
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
1795
size_t indices_bytes;
1796
CoglVertexBufferIndices *indices;
1798
_COGL_GET_CONTEXT (ctx, 0);
1800
indices = g_slice_alloc (sizeof (CoglVertexBufferIndices));
1802
if (indices_type == COGL_INDICES_TYPE_UNSIGNED_BYTE)
1803
indices->type = GL_UNSIGNED_BYTE;
1804
else if (indices_type == COGL_INDICES_TYPE_UNSIGNED_SHORT)
1805
indices->type = GL_UNSIGNED_SHORT;
1808
g_critical ("unknown indices type %d", indices_type);
1809
g_slice_free (CoglVertexBufferIndices, indices);
1813
indices_bytes = get_indices_type_size (indices->type) * indices_len;
1816
indices->vbo_name = g_malloc (indices_len);
1817
memcpy (indices->vbo_name, indices_array, indices_bytes);
1821
GE (glGenBuffers (1, (GLuint *)&indices->vbo_name));
1822
GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER,
1823
GPOINTER_TO_UINT (indices->vbo_name)));
1824
GE (glBufferData (GL_ELEMENT_ARRAY_BUFFER,
1828
GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0));
1831
return _cogl_vertex_buffer_indices_handle_new (indices);
1835
cogl_vertex_buffer_indices_get_type (CoglHandle indices_handle)
1837
CoglVertexBufferIndices *indices = NULL;
1839
if (!cogl_is_vertex_buffer_indices (indices_handle))
1840
return COGL_INDICES_TYPE_UNSIGNED_SHORT;
1842
indices = _cogl_vertex_buffer_indices_pointer_from_handle (indices_handle);
1844
if (indices->type == GL_UNSIGNED_BYTE)
1845
return COGL_INDICES_TYPE_UNSIGNED_BYTE;
1846
else if (indices->type == GL_UNSIGNED_SHORT)
1847
return COGL_INDICES_TYPE_UNSIGNED_SHORT;
1850
g_critical ("unknown indices type %d", indices->type);
1851
return COGL_INDICES_TYPE_UNSIGNED_SHORT;
1856
_cogl_vertex_buffer_indices_free (CoglVertexBufferIndices *indices)
1859
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
1861
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
1864
g_free (indices->vbo_name);
1866
GE (glDeleteBuffers (1, (GLuint *)&indices->vbo_name));
1868
g_slice_free (CoglVertexBufferIndices, indices);
1872
cogl_vertex_buffer_draw_elements (CoglHandle handle,
1873
CoglVerticesMode mode,
1874
CoglHandle indices_handle,
1880
CoglVertexBuffer *buffer;
1882
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
1884
CoglVertexBufferIndices *indices = NULL;
1886
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
1888
if (!cogl_is_vertex_buffer (handle))
1891
_cogl_journal_flush ();
1892
cogl_clip_ensure ();
1894
buffer = _cogl_vertex_buffer_pointer_from_handle (handle);
1896
if (!cogl_is_vertex_buffer_indices (indices_handle))
1899
indices = _cogl_vertex_buffer_indices_pointer_from_handle (indices_handle);
1901
cogl_clip_ensure ();
1902
_cogl_flush_matrix_stacks ();
1903
enable_state_for_drawing_buffer (buffer);
1905
byte_offset = indices_offset * get_indices_type_size (indices->type);
1907
byte_offset = (size_t)(((char *)indices->vbo_name) + byte_offset);
1909
GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER,
1910
GPOINTER_TO_UINT (indices->vbo_name)));
1912
/* FIXME: flush cogl cache */
1913
GE (glDrawRangeElements (mode, min_index, max_index,
1914
count, indices->type, (void *)byte_offset));
1916
disable_state_for_drawing_buffer (buffer);
1918
GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0));
1922
_cogl_vertex_buffer_free (CoglVertexBuffer *buffer)
1926
for (tmp = buffer->submitted_vbos; tmp != NULL; tmp = tmp->next)
1927
cogl_vertex_buffer_vbo_free (tmp->data, TRUE);
1928
g_list_free (buffer->submitted_vbos);
1930
for (tmp = buffer->new_attributes; tmp != NULL; tmp = tmp->next)
1931
cogl_vertex_buffer_attribute_free (tmp->data);
1932
g_list_free (buffer->new_attributes);
1934
g_slice_free (CoglVertexBuffer, buffer);
1938
cogl_vertex_buffer_indices_get_for_quads (guint n_indices)
1940
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
1942
/* Check if the indices would fit in a byte array */
1943
if (n_indices <= 256 / 4 * 6)
1945
/* Generate the byte array if we haven't already */
1946
if (ctx->quad_indices_byte == COGL_INVALID_HANDLE)
1948
guint8 *byte_array = g_malloc (256 / 4 * 6 * sizeof (guint8));
1949
guint8 *p = byte_array;
1950
int i, vert_num = 0;
1952
for (i = 0; i < 256 / 4; i++)
1954
*(p++) = vert_num + 0;
1955
*(p++) = vert_num + 1;
1956
*(p++) = vert_num + 2;
1957
*(p++) = vert_num + 0;
1958
*(p++) = vert_num + 2;
1959
*(p++) = vert_num + 3;
1963
ctx->quad_indices_byte
1964
= cogl_vertex_buffer_indices_new (COGL_INDICES_TYPE_UNSIGNED_BYTE,
1968
g_free (byte_array);
1971
return ctx->quad_indices_byte;
1975
if (ctx->quad_indices_short_len < n_indices)
1977
guint16 *short_array;
1979
int i, vert_num = 0;
1981
if (ctx->quad_indices_short != COGL_INVALID_HANDLE)
1982
cogl_handle_unref (ctx->quad_indices_short);
1983
/* Pick a power of two >= MAX (512, n_indices) */
1984
if (ctx->quad_indices_short_len == 0)
1985
ctx->quad_indices_short_len = 512;
1986
while (ctx->quad_indices_short_len < n_indices)
1987
ctx->quad_indices_short_len *= 2;
1989
/* Over-allocate to generate a whole number of quads */
1990
p = short_array = g_malloc ((ctx->quad_indices_short_len
1992
* sizeof (guint16));
1994
/* Fill in the complete quads */
1995
for (i = 0; i < ctx->quad_indices_short_len; i += 6)
1997
*(p++) = vert_num + 0;
1998
*(p++) = vert_num + 1;
1999
*(p++) = vert_num + 2;
2000
*(p++) = vert_num + 0;
2001
*(p++) = vert_num + 2;
2002
*(p++) = vert_num + 3;
2006
ctx->quad_indices_short
2007
= cogl_vertex_buffer_indices_new (COGL_INDICES_TYPE_UNSIGNED_SHORT,
2009
ctx->quad_indices_short_len);
2011
g_free (short_array);
2014
return ctx->quad_indices_short;