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