From 58b89eabdf342b2bc1b684b28fd19dc8774f4795 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sun, 24 May 2009 04:04:38 +0100 Subject: [PATCH] [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 () --- cogl-vertex-buffer.h | 79 +++++++++---- common/cogl-vertex-buffer-private.h | 33 ++++-- common/cogl-vertex-buffer.c | 161 ++++++++++++++++++++++++--- doc/reference/cogl/cogl-sections.txt | 2 + 4 files changed, 232 insertions(+), 43 deletions(-) diff --git a/cogl-vertex-buffer.h b/cogl-vertex-buffer.h index 717281db0..580ecfa10 100644 --- a/cogl-vertex-buffer.h +++ b/cogl-vertex-buffer.h @@ -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, * GL_TRIANGLE_FAN * GL_TRIANGLES * - * (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: - * - * GL_UNSIGNED_BYTE - * GL_UNSIGNED_SHORT - * GL_UNSIGNED_INT - * - * @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: diff --git a/common/cogl-vertex-buffer-private.h b/common/cogl-vertex-buffer-private.h index 68a709b54..cc87b1828 100644 --- a/common/cogl-vertex-buffer-private.h +++ b/common/cogl-vertex-buffer-private.h @@ -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 */ diff --git a/common/cogl-vertex-buffer.c b/common/cogl-vertex-buffer.c index 842551b04..cd450ea93 100644 --- a/common/cogl-vertex-buffer.c +++ b/common/cogl-vertex-buffer.c @@ -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); } + diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index 1041ded78..8b1d5161c 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -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 CoglVertexBufferAttribFlags