[cogl-vertex-buffers] Support putting index arrays into VBOS

It's now possible to add arrays of indices to a Cogl vertex buffer and
they will be put into an OpenGL vertex buffer object. Since it's quite
common for index arrays to be static it saves the OpenGL driver from
having to validate them repeatedly.

This changes the cogl_vertex_buffer_draw_elements API: It's no longer
possible to provide a pointer to an index array at draw time. So
cogl_vertex_buffer_draw_elements now takes an indices identifier that
should correspond to an idendifier returned when calling
cogl_vertex_buffer_add_indices ()
This commit is contained in:
Robert Bragg 2009-05-24 04:04:38 +01:00
parent 02faadae8b
commit 58b89eabdf
4 changed files with 232 additions and 43 deletions

View File

@ -256,6 +256,54 @@ cogl_vertex_buffer_draw (CoglHandle handle,
GLint first,
GLsizei count);
/**
* CoglIndicesType:
* @COGL_INDICES_TYPE_UNSIGNED_BYTE: Your indices are unsigned bytes
* @COGL_INDICES_TYPE_UNSIGNED_SHORT: Your indices are unsigned shorts
* @COGL_INDICES_TYPE_UNSIGNED_INT: You indices are unsigned integers
*
* You should aim to use the smallest data type that gives you enough
* range, since it reduces the size of your index array and can help
* reduce the demand on memory bandwidth.
*/
typedef enum _CoglIndicesType
{
COGL_INDICES_TYPE_UNSIGNED_BYTE,
COGL_INDICES_TYPE_UNSIGNED_SHORT,
COGL_INDICES_TYPE_UNSIGNED_INT
} CoglIndicesType;
/**
* cogl_vertex_buffer_add_indices:
* @handle: A vertex buffer handle
* @id: Any unique number. It's used to identify the indices when you later
* call cogl_vertex_buffer_draw_elements()
* @min_index: Specifies the minimum vertex index contained in indices
* @max_index: Specifies the maximum vertex index contained in indices
* @indices_type: a #CoglIndicesType specifying the data type used for
* the indices.
* @indices_array: Specifies the address of your array of indices
* @indices_len: The number of indices in indices_array
*
* Depending on how much geometry you are submitting it can be worthwhile
* optimizing the number of redundant vertices you submit. Using an index
* array allows you to reference vertices multiple times, for example
* during triangle strips.
*
* You should aim to use the COGL_INDICES_TYPE_UNSIGNED_SHORT when possible
* and correctly reflect the range of index values in the {min,max}_index
* arguments. This allows Cogl to optimize the internal storage used for
* the indices and reduce the demand for memory bandwidth.
*/
void
cogl_vertex_buffer_add_indices (CoglHandle handle,
int id,
unsigned int min_index,
unsigned int max_index,
CoglIndicesType indices_type,
const void *indices_array,
size_t indices_len);
/**
* cogl_vertex_buffer_draw_elements:
* @handle: A vertex buffer handle
@ -270,33 +318,26 @@ cogl_vertex_buffer_draw (CoglHandle handle,
* <listitem>GL_TRIANGLE_FAN</listitem>
* <listitem>GL_TRIANGLES</listitem>
* </itemizedlist>
* (Note: only types available in GLES are listed)
* @min_index: Specifies the minimum vertex index contained in indices
* @max_index: Specifies the maximum vertex index contained in indices
* @indices_id: The identifier for a an array of indices previously added to
* the given Cogl vertex buffer using
* cogl_vertex_buffer_add_indices().
* @indices_offset: An offset into named indices. The offset marks the first
* index to use for drawing.
* @count: Specifies the number of vertices you want to draw.
* @indices_type: Specifies the data type used for the indices, and must be
* one of:
* <itemizedlist>
* <listitem>GL_UNSIGNED_BYTE</listitem>
* <listitem>GL_UNSIGNED_SHORT</listitem>
* <listitem>GL_UNSIGNED_INT</listitem>
* </itemizedlist>
* @indices: Specifies the address of your array of indices
*
* This function lets you use an array of indices to specify the vertices
* within your vertex buffer that you want to draw.
* within your vertex buffer that you want to draw. The indices themselves
* are given by calling cogl_vertex_buffer_add_indices ()
*
* Any un-submitted attribute changes are automatically submitted before
* drawing.
*/
void
cogl_vertex_buffer_draw_elements (CoglHandle handle,
GLenum mode,
GLuint min_index,
GLuint max_index,
GLsizei count,
GLenum indices_type,
const GLvoid *indices);
cogl_vertex_buffer_draw_elements (CoglHandle handle,
GLenum mode,
int indices_id,
unsigned int indices_offset,
unsigned int count);
/**
* cogl_vertex_buffer_ref:

View File

@ -99,9 +99,9 @@ typedef struct _CoglVertexBufferAttrib
union _u
{
const void *pointer;
gsize vbo_offset;
size_t vbo_offset;
} u;
gsize span_bytes;
size_t span_bytes;
guint16 stride;
guint8 n_components;
guint8 texture_unit;
@ -129,24 +129,39 @@ typedef struct _CoglVertexBufferVBO
{
CoglVertexBufferVBOFlags flags;
/* Note: this is a pointer to handle fallbacks, and normally holds
* a GLuint value */
gpointer vbo_name; /*!< The name of the corresponding buffer object */
gsize vbo_bytes; /*!< The lengh of the allocated buffer object in bytes */
/* Note: this is a pointer to handle fallbacks. It normally holds
* a GLuint VBO name, but when the driver doesn't support VBOs then
* this simply points to an malloc'd buffer. */
void *vbo_name; /*!< The name of the corresponding buffer object */
size_t vbo_bytes; /*!< The lengh of the allocated buffer object in bytes */
GList *attributes;
} CoglVertexBufferVBO;
typedef struct _CoglVertexBufferIndices
{
int id;
/* Note: this is a pointer to handle fallbacks. It normally holds
* a GLuint VBO name, but when the driver doesn't support VBOs then
* this simply points to an malloc'd buffer. */
void *vbo_name;
GLenum type;
GLuint min_index;
GLuint max_index;
} CoglVertexBufferIndices;
typedef struct _CoglVertexBuffer
{
CoglHandleObject _parent;
guint n_vertices; /*!< The number of vertices in the buffer */
GList *submitted_vbos; /* The VBOs currently submitted to the GPU */
int n_vertices; /*!< The number of vertices in the buffer */
GList *submitted_vbos; /* The VBOs currently submitted to the GPU */
/* Note: new_attributes is normally NULL and only valid while
* modifying a buffer. */
GList *new_attributes; /*!< attributes pending submission */
GList *new_attributes; /*!< attributes pending submission */
GList *indices; /*!< A list of associated index arrays */
} CoglVertexBuffer;
#endif /* __COGL_VERTEX_BUFFER_H */

View File

@ -214,6 +214,8 @@ cogl_vertex_buffer_new (guint n_vertices)
buffer->submitted_vbos = NULL;
buffer->new_attributes = NULL;
buffer->indices = NULL;
/* return COGL_INVALID_HANDLE; */
return _cogl_vertex_buffer_handle_new (buffer);
}
@ -1727,13 +1729,11 @@ cogl_vertex_buffer_draw (CoglHandle handle,
if (!cogl_is_vertex_buffer (handle))
return;
cogl_clip_ensure ();
buffer = _cogl_vertex_buffer_pointer_from_handle (handle);
enable_state_for_drawing_buffer (buffer);
cogl_clip_ensure ();
_cogl_current_matrix_state_flush ();
enable_state_for_drawing_buffer (buffer);
/* FIXME: flush cogl cache */
GE (glDrawArrays (mode, first, count));
@ -1741,35 +1741,158 @@ cogl_vertex_buffer_draw (CoglHandle handle,
disable_state_for_drawing_buffer (buffer);
}
static void
free_vertex_buffer_indices (CoglVertexBufferIndices *indices)
{
gboolean fallback =
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (fallback)
g_free (indices->vbo_name);
else
GE (glDeleteBuffers (1, (GLuint *)&indices->vbo_name));
g_slice_free (CoglVertexBufferIndices, indices);
}
static int
get_indices_type_size (GLuint indices_type)
{
if (indices_type == GL_UNSIGNED_BYTE)
return sizeof (GLubyte);
if (indices_type == GL_UNSIGNED_SHORT)
return sizeof (GLushort);
else
{
g_critical ("Unknown indices type %d\n", indices_type);
return 0;
}
}
void
cogl_vertex_buffer_draw_elements (CoglHandle handle,
GLenum mode,
GLuint min_index,
GLuint max_index,
GLsizei count,
GLenum indices_type,
const GLvoid *indices)
cogl_vertex_buffer_add_indices (CoglHandle handle,
int id,
unsigned int min_index,
unsigned int max_index,
CoglIndicesType indices_type,
const void *indices_array,
size_t indices_len)
{
CoglVertexBuffer *buffer;
GList *l;
gboolean fallback =
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
size_t indices_bytes;
CoglVertexBufferIndices *indices;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (!cogl_is_vertex_buffer (handle))
return;
cogl_clip_ensure ();
buffer = _cogl_vertex_buffer_pointer_from_handle (handle);
for (l = buffer->indices; l; l = l->next)
{
CoglVertexBufferIndices *current_indices = l->data;
if (current_indices->id == id)
{
free_vertex_buffer_indices (l->data);
buffer->indices = g_list_delete_link (buffer->indices, l);
break;
}
}
indices = g_slice_alloc (sizeof (CoglVertexBufferIndices));
indices->id = id;
indices->min_index = min_index;
indices->max_index = max_index;
if (indices_type == COGL_INDICES_TYPE_UNSIGNED_BYTE)
indices->type = GL_UNSIGNED_BYTE;
else if (indices_type == COGL_INDICES_TYPE_UNSIGNED_SHORT)
indices->type = GL_UNSIGNED_SHORT;
else
{
g_critical ("unknown indices type %d", indices_type);
g_slice_free (CoglVertexBufferIndices, indices);
return;
}
indices_bytes = get_indices_type_size (indices->type) * indices_len;
if (fallback)
{
indices->vbo_name = g_malloc (indices_len);
memcpy (indices->vbo_name, indices_array, indices_bytes);
}
else
{
GE (glGenBuffers (1, (GLuint *)&indices->vbo_name));
GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER,
GPOINTER_TO_UINT (indices->vbo_name)));
GE (glBufferData (GL_ELEMENT_ARRAY_BUFFER,
indices_bytes,
indices_array,
GL_STATIC_DRAW));
GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0));
}
buffer->indices = g_list_prepend (buffer->indices, indices);
}
void
cogl_vertex_buffer_draw_elements (CoglHandle handle,
GLenum mode,
int indices_id,
unsigned int indices_offset,
unsigned int count)
{
CoglVertexBuffer *buffer;
gboolean fallback =
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
size_t byte_offset;
GList *l;
CoglVertexBufferIndices *indices = NULL;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (!cogl_is_vertex_buffer (handle))
return;
buffer = _cogl_vertex_buffer_pointer_from_handle (handle);
cogl_clip_ensure ();
_cogl_current_matrix_state_flush ();
enable_state_for_drawing_buffer (buffer);
_cogl_current_matrix_state_flush ();
for (l = buffer->indices; l; l = l->next)
{
CoglVertexBufferIndices *current_indices = l->data;
if (current_indices->id == indices_id)
{
indices = current_indices;
break;
}
}
if (!indices)
return;
byte_offset = indices_offset * get_indices_type_size (indices->type);
if (fallback)
byte_offset = (size_t)(((char *)indices->vbo_name) + byte_offset);
else
GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER,
GPOINTER_TO_UINT (indices->vbo_name)));
/* FIXME: flush cogl cache */
GE (glDrawRangeElements (mode, min_index, max_index,
count, indices_type, indices));
GE (glDrawRangeElements (mode, indices->min_index, indices->max_index,
count, indices->type, (void *)byte_offset));
disable_state_for_drawing_buffer (buffer);
GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0));
}
static void
@ -1779,8 +1902,16 @@ _cogl_vertex_buffer_free (CoglVertexBuffer *buffer)
for (tmp = buffer->submitted_vbos; tmp != NULL; tmp = tmp->next)
cogl_vertex_buffer_vbo_free (tmp->data, TRUE);
g_list_free (buffer->submitted_vbos);
for (tmp = buffer->new_attributes; tmp != NULL; tmp = tmp->next)
cogl_vertex_buffer_attribute_free (tmp->data);
g_list_free (buffer->new_attributes);
for (tmp = buffer->indices; tmp != NULL; tmp = tmp->next)
free_vertex_buffer_indices (tmp->data);
g_list_free (buffer->indices);
g_slice_free (CoglVertexBuffer, buffer);
}

View File

@ -307,6 +307,8 @@ cogl_vertex_buffer_submit
cogl_vertex_buffer_disable
cogl_vertex_buffer_enable
cogl_vertex_buffer_draw
CoglIndicesType
cogl_vertex_buffer_add_indices
cogl_vertex_buffer_draw_elements
<SUBSECTION Private>
CoglVertexBufferAttribFlags