diff --git a/cogl-mesh.h b/cogl-mesh.h new file mode 100644 index 000000000..0ce3f83e5 --- /dev/null +++ b/cogl-mesh.h @@ -0,0 +1,286 @@ +/* cogl-mesh.h: Handle extensible arrays of vertex attributes + * This file is part of Clutter + * + * Copyright (C) 2008 Intel Corporation. + * + * Authored by: Robert Bragg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_MESH_H__ +#define __COGL_MESH_H__ + +#include +#include + +G_BEGIN_DECLS + +/** + * SECTION:cogl-mesh + * @short_description: An API for submitting extensible arrays of vertex + * attributes to OpenGL in a way that aims to minimise + * copying or reformatting of the original data. + * + * The Mesh API is designed to be a fairly raw mechanism for developers + * to be able to submit geometry to Cogl in a format that can be directly + * consumed by an OpenGL driver and with awareness of the specific hardware + * being used then costly format conversion can also be avoided. + * + * They are designed to work on top of buffer objects and developers should + * understand that mesh objects are not cheap to create but once they + * have been submitted they are stored in GPU addressable memory and can + * be quickly reused. + * + * Although this API does allow you to modify mesh objects after they have + * been submitted to the GPU you must note that modification is still + * not cheap, so if at all possible think of tricks that let you reuse + * a static buffer. To help with this, it is possible to enable and disable + * individual attributes cheaply. + * + * Take for example a mesh representing an elipse. If you were to submit + * a mesh with color attributes, texture coordinates and normals, then + * you would be able to draw an elipses in the following different ways + * without creating a new mesh: + * + * Flat colored elipse + * Textured elipse + * Smoothly lit textured elipse blended with the color. + * + * + * Another trick that can be used is submitting a highly detailed mesh + * and then using cogl_mesh_draw_range_elements to sample lower resolution + * geometry out from a fixed mesh. + * + * The API doesn't currently give you any control over the actual buffer + * objects that are created, but you can expect that when you first submit + * your attributes they start off in one or more GL_STATIC_DRAW buffers. + * If you then update some of your attributes; then these attributes will + * normally be moved into new GL_DYNAMIC_DRAW draw buffers. + */ + +/** + * cogl_mesh_new: + * @n_vertices: The number of vertices that will make up your mesh. + * + * This creates a Cogl handle for a new mesh that you can then start to add + * attributes too. + */ +CoglHandle +cogl_mesh_new (guint n_vertices); + +/** + * cogl_mesh_add_attribute: + * @handle: A Cogl mesh handle + * @attribute_name: The name of your attribute. It should be a valid GLSL + * variable name and standard attribute types must use one + * of following built-in names: (Note: they correspond to the + * built-in names in GLSL) + * + * "gl_Color" + * "gl_Normal" + * "gl_MultiTexCoord0, gl_MultiTexCoord1, ..." + * "gl_Vertex" + * + * To support adding multiple variations of the same attribute + * the name can have a detail component, E.g. + * "gl_Color::active" or "gl_Color::inactive" + * @n_components: The number of components per attribute and must be 1,2,3 or 4 + * @gl_type: Specifies the data type of each component (GL_BYTE, GL_UNSIGNED_BYTE, + * GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT or + * GL_DOUBLE) + * @normalized: If GL_TRUE, this specifies that values stored in an integer + * format should be mapped into the range [-1.0, 1.0] or [0.1, 1.0] + * for unsigned values. If GL_FALSE they are converted to floats + * directly. + * @stride: This specifies the number of bytes from the start of one attribute + * value to the start of the next value (for the same attribute). So + * for example with a position interleved with color like this: + * XYRGBAXYRGBAXYRGBA, then if each letter represents a byte, the + * stride for both attributes is 6. The special value 0 means the + * values are stored sequentially in memory. + * @pointer: This addresses the first attribute in the vertex array. (This + * must remain valid until you call cogl_mesh_submit) + * + * This function lets you add an attribute to a mesh. You either use one + * of the built-in names to add standard attributes, like positions, colors + * and normals or you can add custom attributes for use in shaders. + * + * Note: The number of vertices declared when creating the mesh is used to + * determine how many attribute values will be read from the supplied pointer. + * + * Note: the data supplied here isn't copied anywhere until you call + * cogl_mesh_submit, so the supplied pointer must remain valid until then. + * (This is an important optimisation since we can't create a buffer + * object until we know about all the attributes, and repeatedly copying + * large buffers of vertex data may be very costly) If you add attributes + * after submitting then you will need to re-call cogl_mesh_submit to + * commit the changes to the GPU. (Be carefull to minimize the number of + * calls to cogl_mesh_submit though) + * + * Note: If you are interleving attributes it is assumed that that each + * interleaved attribute starts no farther than +- stride bytes from + * the other attributes it is interleved with. I.e. this is ok: + * |-0-0-0-0-0-0-0-0-0-0| + * This is not ok: + * |- - - - -0-0-0-0-0-0 0 0 0 0| + * (Though you can have multiple groups of interleved attributes) + */ +void +cogl_mesh_add_attribute (CoglHandle handle, + const char *attribute_name, + guint8 n_components, + GLenum gl_type, + gboolean normalized, + guint16 stride, + const void *pointer); + +/** + * cogl_mesh_delete_attribute: + * @handle: A Cogl mesh handle + * @attribute_name: The name of a previously added attribute + * + * This function deletes an attribute from a mesh. You will need to + * call cogl_mesh_submit to commit this change to the GPU. + */ +void +cogl_mesh_delete_attribute (CoglHandle handle, + const char *attribute_name); + +/** + * cogl_mesh_enable_attribute: + * @handle: A Cogl mesh handle + * @attribute_name: The name of the attribute you want to enable + * + * This function enables a previosuly added attribute + * + * Since it is costly to create new mesh objects, then to make individual mesh + * objects more reuseable it is possible to enable and disable attributes + * before using a mesh for drawing. + * + * Note: You don't need to call cogl_mesh_submit after using this function + */ +void +cogl_mesh_enable_attribute (CoglHandle handle, + const char *attribute_name); + +/** + * cogl_mesh_disable_attribute: + * @handle: A Cogl mesh handle + * @attribute_name: The name of the attribute you want to disable + * + * This function disables a previosuly added attribute + * + * Since it is costly to create new mesh objects, then to make individual mesh + * objects more reuseable it is possible to enable and disable attributes + * before using a mesh for drawing. + * + * Note: You don't need to call cogl_mesh_submit after using this function + */ +void +cogl_mesh_disable_attribute (CoglHandle handle, + const char *attribute_name); + +/** + * cogl_mesh_draw_arrays: + * @handle: A Cogl mesh handle + * @mode: Specifies how the vertices should be interpreted, and should be + * a valid GL primitive type: + * + * GL_POINTS + * GL_LINE_STRIP + * GL_LINE_LOOP + * GL_LINES + * GL_TRIANGLE_STRIP + * GL_TRIANGLE_FAN + * GL_TRIANGLES + * + * (Note: only types available in GLES are listed) + * @first: Specifies the index of the first vertex you want to draw with + * @count: Specifies the number of vertices you want to draw. + * + * This function lets you draw geometry using all or a subset of the + * vertices in a mesh object. + */ +void +cogl_mesh_draw_arrays (CoglHandle handle, + GLenum mode, + GLint first, + GLsizei count); + +/** + * cogl_mesh_draw_range_elements: + * @handle: A Cogl mesh handle + * @mode: Specifies how the vertices should be interpreted, and should be + * a valid GL primitive type: + * + * GL_POINTS + * GL_LINE_STRIP + * GL_LINE_LOOP + * GL_LINES + * GL_TRIANGLE_STRIP + * GL_TRIANGLE_FAN + * GL_TRIANGLES + * + * (Note: only types available in GLES are listed) + * @start: Specifies the minimum vertex index contained in indices + * @end: Specifies the maximum vertex index contained in indices + * @count: Specifies the number of vertices you want to draw. + * @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 mesh pbject that you want to draw. + */ +void +cogl_mesh_draw_range_elements (CoglHandle handle, + GLenum mode, + GLuint start, + GLuint end, + GLsizei count, + GLenum type, + const GLvoid *indices); + +/** + * cogl_mesh_ref: + * @handle: a @CoglHandle. + * + * Increment the reference count for a cogl mesh. + * + * Returns: the @handle. + */ +CoglHandle cogl_mesh_ref (CoglHandle handle); + +/** + * cogl_mesh_unref: + * @handle: a @CoglHandle. + * + * Deccrement the reference count for a cogl mesh. + */ +void cogl_mesh_unref (CoglHandle handle); + + +G_END_DECLS + +#endif /* __COGL_MESH_H__ */ diff --git a/cogl-types.h b/cogl-types.h index 09fbf08ea..bb5ed3309 100644 --- a/cogl-types.h +++ b/cogl-types.h @@ -183,6 +183,7 @@ typedef enum * @COGL_FEATURE_OFFSCREEN_BLIT: * @COGL_FEATURE_FOUR_CLIP_PLANES: * @COGL_FEATURE_STENCIL_BUFFER: + * @COGL_FEATURE_VBOS: * * Flags for the supported features. */ @@ -197,7 +198,8 @@ typedef enum COGL_FEATURE_OFFSCREEN_MULTISAMPLE = (1 << 7), COGL_FEATURE_OFFSCREEN_BLIT = (1 << 8), COGL_FEATURE_FOUR_CLIP_PLANES = (1 << 9), - COGL_FEATURE_STENCIL_BUFFER = (1 << 10) + COGL_FEATURE_STENCIL_BUFFER = (1 << 10), + COGL_FEATURE_VBOS = (1 << 11) } CoglFeatureFlags; /** diff --git a/cogl.h.in b/cogl.h.in index b65069783..6b921e73e 100644 --- a/cogl.h.in +++ b/cogl.h.in @@ -32,6 +32,7 @@ #include +#include #include #include #include diff --git a/common/Makefile.am b/common/Makefile.am index 7e3d7dfc0..446d994bf 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -28,4 +28,6 @@ libclutter_cogl_common_la_SOURCES = \ cogl-clip-stack.h \ cogl-clip-stack.c \ cogl-fixed.c \ - cogl-color.c + cogl-color.c \ + cogl-mesh.c \ + cogl-mesh.h diff --git a/common/cogl-mesh-private.h b/common/cogl-mesh-private.h new file mode 100644 index 000000000..db014c3bd --- /dev/null +++ b/common/cogl-mesh-private.h @@ -0,0 +1,143 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Robert Bragg + * + * Copyright (C) 2008 Intel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_MESH_H +#define __COGL_MESH_H + +/* Note we put quite a bit into the flags here to help keep + * the down size of the CoglMeshAttribute struct below. */ +typedef enum _CoglMeshAttributeFlags +{ + /* Types */ + /* NB: update the _TYPE_MASK below if these are changed */ + COGL_MESH_ATTRIBUTE_FLAG_COLOR_ARRAY = 1<<0, + COGL_MESH_ATTRIBUTE_FLAG_NORMAL_ARRAY = 1<<1, + COGL_MESH_ATTRIBUTE_FLAG_TEXTURE_COORD_ARRAY = 1<<2, + COGL_MESH_ATTRIBUTE_FLAG_VERTEX_ARRAY = 1<<3, + COGL_MESH_ATTRIBUTE_FLAG_CUSTOM_ARRAY = 1<<4, + COGL_MESH_ATTRIBUTE_FLAG_INVALID = 1<<5, + + COGL_MESH_ATTRIBUTE_FLAG_NORMALIZED = 1<<6, + COGL_MESH_ATTRIBUTE_FLAG_ENABLED = 1<<7, + + /* Usage hints */ + /* FIXME - flatten into one flag, since its used as a boolean */ + COGL_MESH_ATTRIBUTE_FLAG_INFREQUENT_RESUBMIT = 1<<8, + COGL_MESH_ATTRIBUTE_FLAG_FREQUENT_RESUBMIT = 1<<9, + + /* GL Data types */ + /* NB: Update the _GL_TYPE_MASK below if these are changed */ + COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_BYTE = 1<<10, + COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_BYTE = 1<<11, + COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_SHORT = 1<<12, + COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_SHORT = 1<<13, + COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_INT = 1<<14, + COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_INT = 1<<15, + COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_FLOAT = 1<<16, + COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_DOUBLE = 1<<17, + + COGL_MESH_ATTRIBUTE_FLAG_SUBMITTED = 1<<18, + COGL_MESH_ATTRIBUTE_FLAG_UNUSED = 1<<19 + + /* XXX NB: If we need > 24 bits then look at changing the layout + * of struct _CoglMeshAttribute below */ +} CoglMeshAttributeFlags; + +#define COGL_MESH_ATTRIBUTE_FLAG_TYPE_MASK \ + (COGL_MESH_ATTRIBUTE_FLAG_COLOR_ARRAY \ + | COGL_MESH_ATTRIBUTE_FLAG_NORMAL_ARRAY \ + | COGL_MESH_ATTRIBUTE_FLAG_TEXTURE_COORD_ARRAY \ + | COGL_MESH_ATTRIBUTE_FLAG_VERTEX_ARRAY \ + | COGL_MESH_ATTRIBUTE_FLAG_CUSTOM_ARRAY \ + | COGL_MESH_ATTRIBUTE_FLAG_INVALID) + +#define COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_MASK \ + (COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_BYTE \ + | COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_BYTE \ + | COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_SHORT \ + | COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_SHORT \ + | COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_INT \ + | COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_INT \ + | COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_FLOAT \ + | COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_DOUBLE) + + +typedef struct _CoglMeshAttribute +{ + /* TODO: look at breaking up the flags into seperate + * bitfields and seperate enums */ + CoglMeshAttributeFlags flags:24; + guint8 id; + GQuark name; + union _u + { + const void *pointer; + gsize vbo_offset; + } u; + gsize span_bytes; + guint16 stride; + guint8 n_components; + guint8 texture_unit; + +} CoglMeshAttribute; + +typedef enum _CoglMeshVBOFlags +{ + COGL_MESH_VBO_FLAG_UNSTRIDED = 1<<0, + COGL_MESH_VBO_FLAG_STRIDED = 1<<1, + COGL_MESH_VBO_FLAG_MULTIPACK = 1<<2, + + /* FIXME - flatten into one flag, since its used as a boolean */ + COGL_MESH_VBO_FLAG_INFREQUENT_RESUBMIT = 1<<3, + COGL_MESH_VBO_FLAG_FREQUENT_RESUBMIT = 1<<4, + + COGL_MESH_VBO_FLAG_SUBMITTED = 1<<5 +} CoglMeshVBOFlags; + +/* + * A CoglMeshVBO represents one or more attributes in a single buffer object + */ +typedef struct _CoglMeshVBO +{ + CoglMeshVBOFlags flags; + GLuint vbo_name; /*!< The name of the corresponding buffer object */ + gsize vbo_bytes; /*!< The lengh of the allocated buffer object in bytes */ + GList *attributes; +} CoglMeshVBO; + + +typedef struct _CoglMesh +{ + guint ref_count; + guint n_vertices; /*!< The number of vertices in the mesh */ + GList *submitted_vbos; /* The VBOs currently submitted to the GPU */ + + /* Note: new_attributes is normally NULL and only valid while + * modifying a mesh object. */ + GList *new_attributes; /*!< attributes pending submission */ +} CoglMesh; + +#endif /* __COGL_MESH_H */ + diff --git a/common/cogl-mesh.c b/common/cogl-mesh.c new file mode 100644 index 000000000..5727dee9d --- /dev/null +++ b/common/cogl-mesh.c @@ -0,0 +1,1611 @@ +/* Mesh API: Handle extensible arrays of vertex attributes + * + * Copyright (C) 2008 Intel Corporation. + * + * Authored by: Robert Bragg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +/* XXX: For an overview of the functionality implemented here, please + * see cogl.h.in, which contains the gtk-doc section overview for the + * Mesh API. + */ + +/* + * TODO: We need to do a better job of minimizing when we call glVertexPointer + * and pals in enable_state_for_drawing_mesh + * + * We should have an internal 2-tuple cache of (VBO, offset) for each of them + * so we can avoid some GL calls. We could have cogl wrappers for the + * gl*Pointer funcs that look like this: + * + * cogl_vertex_pointer (n_components, gl_type, stride, vbo, offset); + * cogl_color_pointer (n_components, gl_type, stride, vbo, offset); + * + * They would also accept NULL for the VBO handle to support old style vertex + * arrays. + * + * TODO: + * Actually hook this up to the cogl shaders infrastructure. The mesh API has + * been designed to allow adding of arbitrary attributes for use with shaders, + * but this has yet to be actually plumbed together and tested. + * The bits we are missing: + * - cogl_program_use doesn't currently record within ctx-> which program + * is currently in use so a.t.m only Clutter knows the current shader. + * - We don't query the current shader program for the generic vertex indices + * (using glGetAttribLocation) so that we can call glEnableVertexAttribArray + * with those indices. + * (currently we just make up consecutive indices) + * - some dirty flag meshanims to know when the shader program has changed + * so we don't need to re-query it each time we draw a mesh. + * + * TODO: + * There is currently no API for querying back info about a mesh, E.g.: + * cogl_mesh_get_n_vertices (mesh_handle); + * cogl_mesh_attribute_get_n_components (mesh_handle, "attrib_name"); + * cogl_mesh_attribute_get_stride (mesh_handle, "attrib_name"); + * cogl_mesh_attribute_get_normalized (mesh_handle, "attrib_name"); + * cogl_mesh_attribute_map (mesh_handle, "attrib_name"); + * cogl_mesh_attribute_unmap (mesh_handle, "attrib_name"); + * (Realistically I wouldn't expect anyone to use such an API examine the + * contents of a mesh for modification, since you'd need to handle too many + * possibilities, but never the less there might be other value in these.) + + * TODO: + * It may be worth exposing the underlying VBOs for some advanced use + * cases, e.g.: + * handle = cogl_vbo_new (COGL_VBO_FLAG_STATIC); + * pointer = cogl_vbo_map (handle, COGL_VBO_FLAG_WRITEONLY); + * cogl_vbo_unmap (handle); + * cogl_vbo_set_data (handle, size, data); + * cogl_vbo_set_sub_data (handle, offset, size, data); + * cogl_vbo_set_usage_hint (COGL_VBO_FLAG_DYNAMIC); + * + * TODO: + * Experiment with wider use of the mesh API internally to Cogl. + * - There is potential, I think, for this API to become a work-horse API + * within COGL for submitting geometry to the GPU, and could unify some of + * the GL/GLES code paths. + * E.g.: + * - Try creating a per-context mesh cache for cogl_texture_rectangle to sit + * on top of. + * - Try saving the tesselation of paths/polygons into mesh objects internally. + * + * TODO + * Expose API that lets developers get back a mesh handle for a particular + * polygon so they may add custom attributes to them. + * - It should be possible to query/modify a mesh efficiently, in place, + * avoiding copies. It would not be acceptable to simply require that + * developers must query back the n_vertices of a mesh and then the + * n_components, type and stride etc of each component since there + * would be too many combinations to realistically handle. + * + * - In practice, some cases might be best solved with a higher level + * EditableMesh API, (see futher below) but for many cases I think an + * API like this might be appropriate: + * + * cogl_mesh_foreach_vertex (mesh_handle, (MeshIteratorFunc)callback, + * "gl_Vertex", "gl_Color", NULL); + * void callback (CoglMeshVertex *vert) + * { + * GLfloat *pos = vert->attrib[0]; + * GLubyte *color = vert->attrib[1]; + * GLfloat *new_attrib = buf[vert->index]; + * + * new_attrib = pos*color; + * } + * + * TODO + * Think about a higher level EditableMesh API for building/modifying mesh + * objects. + * - E.g. look at Blender for inspiration here. They can build a mesh + * from "MVert", "MFace" and "MEdge" primitives. + * - It would be possible to bake an EditableMesh into a regular Mesh, and + * vica versa + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-context.h" +#include "cogl-handle.h" +#include "cogl-mesh-private.h" + +#define PAD_FOR_ALIGNMENT(VAR, TYPE_SIZE) \ + (VAR = TYPE_SIZE + ((VAR - 1) & ~(TYPE_SIZE - 1))) + + +/* + * GL/GLES compatability defines for VBO thingies: + */ + +#if HAVE_COGL_GL + +#define glGenBuffers ctx->pf_glGenBuffersARB +#define glBindBuffer ctx->pf_glBindBufferARB +#define glBufferData ctx->pf_glBufferDataARB +#define glBufferDataSub ctx->pf_glBufferDataSubARB +#define glDeleteBuffers ctx->pf_glDeleteBuffersARB +#define glMapBuffer ctx->pf_glMapBufferARB +#define glUnmapBuffer ctx->pf_glUnmapBufferARB +#ifndef GL_ARRAY_BUFFER +#define GL_ARRAY_BUFFER GL_ARRAY_BUFFER_ARB +#endif + +#else + +/* NB: GLES has had VBOs/GLSL since 1.1, so we don't need any defines in + * this case except for glBufferSubData which, just for the fun of it, has a + * different name: + */ +#define glBufferDataSub glBufferSubData + +#endif + +/* + * GL/GLES compatability defines for shader things: + */ + +#ifdef HAVE_COGL_GL +#define glVertexAttribPointer ctx->pf_glVertexAttribPointerARB +#define glEnableVertexAttribArray ctx->pf_glEnableVertexAttribArrayARB +#define glDisableVertexAttribArray ctx->pf_glEnableVertexAttribArrayARB +#define MAY_HAVE_PROGRAMABLE_GL +#endif + +#ifdef HAVE_COGL_GLES2 +/* NB: GLES2 had shaders in core since day one so again we don't need + * defines in this case: */ +#define MAY_HAVE_PROGRAMABLE_GL +#endif + +#ifndef HAVE_COGL_GL +/* GLES doesn't glDrawRangeElements, so we simply pretend it does + * but that it makes no use of the start, end constraints: */ +#define glDrawRangeElements(mode, start, end, count, type, indices) \ + glDrawElements (mode, count, type, indices) +#endif + +static void _cogl_mesh_free (CoglMesh *mesh); + +COGL_HANDLE_DEFINE (Mesh, mesh, mesh_handles); + +/** + * cogl_mesh_new: + * @n_vertices: The number of vertices that will make up your mesh. + * + * This creates a Cogl handle for a new mesh that you can then start to add + * attributes too. + */ +CoglHandle +cogl_mesh_new (guint n_vertices) +{ + CoglMesh *mesh = g_slice_alloc (sizeof (CoglMesh)); + + mesh->ref_count = 1; + COGL_HANDLE_DEBUG_NEW (mesh, mesh); + + mesh->n_vertices = n_vertices; + + mesh->submitted_vbos = NULL; + mesh->new_attributes = NULL; + + /* return COGL_INVALID_HANDLE; */ + return _cogl_mesh_handle_new (mesh); +} + +/* There are a number of standard OpenGL attributes that we deal with + * specially. These attributes are all namespaced with a "gl_" prefix + * so we should catch any typos instead of silently adding a custom + * attribute. + */ +static CoglMeshAttributeFlags +validate_gl_attribute (const char *gl_attribute, + guint8 *n_components, + guint8 *texture_unit) +{ + CoglMeshAttributeFlags type; + char *detail_seperator = NULL; + int name_len; + + detail_seperator = strstr (gl_attribute, "::"); + if (detail_seperator) + name_len = detail_seperator - gl_attribute; + else + name_len = strlen (gl_attribute); + + if (strncmp (gl_attribute, "Vertex", name_len) == 0) + { + type = COGL_MESH_ATTRIBUTE_FLAG_VERTEX_ARRAY; + } + else if (strncmp (gl_attribute, "Color", name_len) == 0) + { + type = COGL_MESH_ATTRIBUTE_FLAG_COLOR_ARRAY; + } + else if (strncmp (gl_attribute, + "MultiTexCoord", + strlen ("MultiTexCoord")) == 0) + { + unsigned int unit; + + if (sscanf (gl_attribute, "MultiTexCoord%u", &unit) != 1) + { + g_warning ("gl_MultiTexCoord attributes should include a\n" + "texture unit number, E.g. gl_MultiTexCoord0\n"); + unit = 0; + } + /* FIXME: validate any '::' delimiter for this case */ + *texture_unit = unit; + type = COGL_MESH_ATTRIBUTE_FLAG_TEXTURE_COORD_ARRAY; + } + else if (strncmp (gl_attribute, "Normal", name_len) == 0) + { + *n_components = 1; + type = COGL_MESH_ATTRIBUTE_FLAG_NORMAL_ARRAY; + } + else + { + g_warning ("Unknown gl_* attribute name gl_%s\n", gl_attribute); + type = COGL_MESH_ATTRIBUTE_FLAG_INVALID; + } + + return type; +} + +/* This validates that a custom attribute name is a valid GLSL variable name + * + * NB: attribute names may have a detail component delimited using '::' E.g. + * custom_attrib::foo or custom_atrib::bar + * + * maybe I should hang a compiled regex somewhere to handle this + */ +static gboolean +validate_custom_attribute_name (const char *attribute_name) +{ + char *detail_seperator = NULL; + int name_len; + const char *p; + int i; + + detail_seperator = strstr (attribute_name, "::"); + if (detail_seperator) + name_len = detail_seperator - attribute_name; + else + name_len = strlen (attribute_name); + + if (name_len == 0 || !g_ascii_isalpha (*p) || *p != '_') + return FALSE; + + for (i = 1; i < name_len; i++) + if (!g_ascii_isalnum (*p) || *p != '_') + return FALSE; + + return TRUE; +} + +/* Iterates the the CoglMeshVBOs of a mesh and create a flat list of all the + * submitted attributes + * + * Note: The CoglMeshAttribute structs are deep copied. + */ +static GList * +copy_submitted_attributes_list (CoglMesh *mesh) +{ + GList *tmp; + GList *submitted_attributes = NULL; + + for (tmp = mesh->submitted_vbos; tmp != NULL; tmp = tmp->next) + { + CoglMeshVBO *cogl_vbo = tmp->data; + GList *tmp2; + + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglMeshAttribute *attribute = tmp2->data; + CoglMeshAttribute *copy = g_slice_alloc (sizeof (CoglMeshAttribute)); + *copy = *attribute; + submitted_attributes = g_list_prepend (submitted_attributes, copy); + } + } + return submitted_attributes; +} + +static CoglMeshAttributeFlags +get_attribute_gl_type_flag_from_gl_type (GLenum gl_type) +{ + switch (gl_type) + { + case GL_BYTE: + return COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_BYTE; + case GL_UNSIGNED_BYTE: + return COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_BYTE; + case GL_SHORT: + return COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_SHORT; + case GL_UNSIGNED_SHORT: + return COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_SHORT; + case GL_FLOAT: + return COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_FLOAT; +#if HAVE_COGL_GL + case GL_INT: + return COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_INT; + case GL_UNSIGNED_INT: + return COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_INT; + case GL_DOUBLE: + return COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_DOUBLE; +#endif + default: + g_warning ("Mesh API: Unrecognised OpenGL type enum 0x%08x\n", gl_type); + return 0; + } +} + +static gsize +get_gl_type_size (CoglMeshAttributeFlags flags) +{ + CoglMeshAttributeFlags gl_type = + flags & COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_MASK; + + switch (gl_type) + { + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_BYTE: + return sizeof (GLbyte); + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_BYTE: + return sizeof (GLubyte); + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_SHORT: + return sizeof (GLshort); + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_SHORT: + return sizeof (GLushort); + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_FLOAT: + return sizeof (GLfloat); +#if HAVE_COGL_GL + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_INT: + return sizeof (GLint); + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_INT: + return sizeof (GLuint); + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_DOUBLE: + return sizeof (GLdouble); +#endif + default: + g_warning ("Mesh API: Unrecognised OpenGL type enum 0x%08x\n", gl_type); + return 0; + } +} + +void +cogl_mesh_add_attribute (CoglHandle handle, + const char *attribute_name, + guint8 n_components, + GLenum gl_type, + gboolean normalized, + guint16 stride, + const void *pointer) +{ + CoglMesh *mesh; + GQuark name_quark = g_quark_from_string (attribute_name); + gboolean modifying_an_attrib = FALSE; + CoglMeshAttribute *attribute; + CoglMeshAttributeFlags flags = 0; + guint8 texture_unit = 0; + GList *tmp; + + if (!cogl_is_mesh (handle)) + return; + + mesh = _cogl_mesh_pointer_from_handle (handle); + + /* The submit function works by diffing between submitted_attributes + * and new_attributes to minimize the upload bandwidth + cost of + * allocating new VBOs, so if there isn't already a list of new_attributes + * we create one: */ + if (!mesh->new_attributes) + mesh->new_attributes = copy_submitted_attributes_list (mesh); + + /* Note: we first look for an existing attribute that we are modifying + * so we may skip needing to validate the name */ + for (tmp = mesh->new_attributes; tmp != NULL; tmp = tmp->next) + { + CoglMeshAttribute *submitted_attribute = tmp->data; + if (submitted_attribute->name == name_quark) + { + modifying_an_attrib = TRUE; + + attribute = submitted_attribute; + + /* since we will skip validate_gl_attribute in this case, we need + * to pluck out the attribute type before overwriting the flags: */ + flags |= attribute->flags & COGL_MESH_ATTRIBUTE_FLAG_TYPE_MASK; + break; + } + } + + if (!modifying_an_attrib) + { + /* Validate the attribute name, is suitable as a variable name */ + if (strncmp (attribute_name, "gl_", 3) == 0) + { + flags |= validate_gl_attribute (attribute_name + 3, + &n_components, + &texture_unit); + if (flags & COGL_MESH_ATTRIBUTE_FLAG_INVALID) + return; + } + else + { + flags |= COGL_MESH_ATTRIBUTE_FLAG_CUSTOM_ARRAY; + if (validate_custom_attribute_name (attribute_name)) + return; + } + + attribute = g_slice_alloc (sizeof (CoglMeshAttribute)); + } + + attribute->name = g_quark_from_string (attribute_name); + attribute->n_components = n_components; + attribute->stride = mesh->n_vertices > 1 ? stride : 0; + attribute->u.pointer = pointer; + attribute->texture_unit = texture_unit; + + flags |= get_attribute_gl_type_flag_from_gl_type (gl_type); + flags |= COGL_MESH_ATTRIBUTE_FLAG_ENABLED; + + /* Note: We currently just assume, if an attribute is *ever* updated + * then it should be taged as frequently changing. */ + if (modifying_an_attrib) + flags |= COGL_MESH_ATTRIBUTE_FLAG_FREQUENT_RESUBMIT; + else + flags |= COGL_MESH_ATTRIBUTE_FLAG_INFREQUENT_RESUBMIT; + + if (normalized) + flags |= COGL_MESH_ATTRIBUTE_FLAG_NORMALIZED; + attribute->flags = flags; + + /* NB: get_gl_type_size must be called after setting the type + * flags, above. */ + if (attribute->stride) + attribute->span_bytes = mesh->n_vertices * attribute->stride; + else + attribute->span_bytes = mesh->n_vertices + * attribute->n_components + * get_gl_type_size (attribute->flags); + + if (!modifying_an_attrib) + mesh->new_attributes = + g_list_prepend (mesh->new_attributes, attribute); +} + +void +cogl_mesh_delete_attribute (CoglHandle handle, + const char *attribute_name) +{ + CoglMesh *mesh; + GQuark name = g_quark_from_string (attribute_name); + GList *tmp; + + if (!cogl_is_mesh (handle)) + return; + + mesh = _cogl_mesh_pointer_from_handle (handle); + + /* The submit function works by diffing between submitted_attributes + * and new_attributes to minimize the upload bandwidth + cost of + * allocating new VBOs, so if there isn't already a list of new_attributes + * we create one: */ + if (!mesh->new_attributes) + mesh->new_attributes = copy_submitted_attributes_list (mesh); + + for (tmp = mesh->new_attributes; tmp != NULL; tmp = tmp->next) + { + CoglMeshAttribute *submitted_attribute = tmp->data; + if (submitted_attribute->name == name) + { + mesh->new_attributes = + g_list_delete_link (mesh->new_attributes, tmp); + g_slice_free (CoglMeshAttribute, submitted_attribute); + return; + } + } + + g_warning ("Failed to find an attribute named %s to delete\n", + attribute_name); +} + +static void +set_attribute_enable (CoglHandle handle, + const char *attribute_name, + gboolean state) +{ + CoglMesh *mesh; + GQuark name_quark = g_quark_from_string (attribute_name); + GList *tmp; + + if (!cogl_is_mesh (handle)) + return; + + mesh = _cogl_mesh_pointer_from_handle (handle); + + /* NB: If a mesh is currently being edited, then there can be two seperate + * lists of attributes; those that are currently submitted and a new + * list yet to be submitted, we need to modify both. */ + + for (tmp = mesh->new_attributes; tmp != NULL; tmp = tmp->next) + { + CoglMeshAttribute *attribute = tmp->data; + if (attribute->name == name_quark) + { + if (state) + attribute->flags |= COGL_MESH_ATTRIBUTE_FLAG_ENABLED; + else + attribute->flags &= ~COGL_MESH_ATTRIBUTE_FLAG_ENABLED; + break; + } + } + + for (tmp = mesh->submitted_vbos; tmp != NULL; tmp = tmp->next) + { + CoglMeshVBO *cogl_vbo = tmp->data; + GList *tmp2; + + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglMeshAttribute *attribute = tmp2->data; + if (attribute->name == name_quark) + { + if (state) + attribute->flags |= COGL_MESH_ATTRIBUTE_FLAG_ENABLED; + else + attribute->flags &= ~COGL_MESH_ATTRIBUTE_FLAG_ENABLED; + return; + } + } + } + + g_warning ("Failed to find an attribute named %s to %s\n", + attribute_name, + state == TRUE ? "enable" : "disable"); +} + +void +cogl_mesh_enable_attribute (CoglHandle handle, + const char *attribute_name) +{ + set_attribute_enable (handle, attribute_name, TRUE); +} + +void +cogl_mesh_disable_attribute (CoglHandle handle, + const char *attribute_name) +{ + set_attribute_enable (handle, attribute_name, FALSE); +} + +static void +free_mesh_attribute (CoglMeshAttribute *attribute) +{ + g_slice_free (CoglMeshAttribute, attribute); +} + +/* Given an attribute that we know has already been submitted before, this + * function looks for the existing VBO that contains it. + * + * Note: It will free redundant attribute struct once the corresponding + * VBO has been found. + */ +static void +filter_already_submitted_attribute (CoglMeshAttribute *attribute, + GList **reuse_vbos, + GList **submitted_vbos) +{ + GList *tmp; + + /* First check the cogl_vbos we already know are being reused since we + * are more likley to get a match here */ + for (tmp = *reuse_vbos; tmp != NULL; tmp = tmp->next) + { + CoglMeshVBO *cogl_vbo = tmp->data; + GList *tmp2; + + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglMeshAttribute *vbo_attribute = tmp2->data; + + if (vbo_attribute->name == attribute->name) + { + vbo_attribute->flags &= ~COGL_MESH_ATTRIBUTE_FLAG_UNUSED; + /* Note: we don't free the redundant attribute here, since it + * will be freed after all filtering in cogl_mesh_submit */ + return; + } + } + } + + for (tmp = *submitted_vbos; tmp != NULL; tmp = tmp->next) + { + CoglMeshVBO *cogl_vbo = tmp->data; + CoglMeshAttribute *reuse_attribute = NULL; + GList *tmp2; + + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglMeshAttribute *vbo_attribute = tmp2->data; + if (vbo_attribute->name == attribute->name) + { + reuse_attribute = vbo_attribute; + /* Note: we don't free the redundant attribute here, since it + * will be freed after all filtering in cogl_mesh_submit */ + + *submitted_vbos = g_list_remove_link (*submitted_vbos, tmp); + tmp->next = *reuse_vbos; + *reuse_vbos = tmp; + break; + } + } + + if (!reuse_attribute) + continue; + + /* Mark all but the matched attribute as UNUSED, so that when we + * finish filtering all our attributes any attrributes still + * marked as UNUSED can be removed from the their cogl_vbo */ + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglMeshAttribute *vbo_attribute = tmp2->data; + if (vbo_attribute != reuse_attribute) + vbo_attribute->flags |= COGL_MESH_ATTRIBUTE_FLAG_UNUSED; + } + + return; + } + + g_critical ("Failed to find the cogl vbo that corresponds to an\n" + "attribute that had apparently already been submitted!"); +} + +/* When we first mark a CoglMeshVBO to be reused, we mark the attributes + * as unsed, so that when filtering of attributes into VBOs is done + * we can then prune the now unsed attributes. */ +static void +remove_unused_attributes (CoglMeshVBO *cogl_vbo) +{ + GList *tmp; + GList *next; + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = next) + { + CoglMeshAttribute *attribute = tmp->data; + next = tmp->next; + + if (attribute->flags & COGL_MESH_ATTRIBUTE_FLAG_UNUSED) + { + cogl_vbo->attributes = + g_list_delete_link (cogl_vbo->attributes, tmp); + g_slice_free (CoglMeshAttribute, attribute); + } + } +} + +/* Give a newly added, strided, attribute, this function looks for a + * CoglMeshVBO that the attribute is interleved with. If it can't find + * one then a new CoglMeshVBO is allocated and added to the list of + * new_strided_vbos + */ +static void +filter_strided_attribute (CoglMeshAttribute *attribute, + GList **new_vbos) +{ + GList *tmp; + CoglMeshVBO *new_cogl_vbo; + + for (tmp = *new_vbos; tmp != NULL; tmp = tmp->next) + { + CoglMeshVBO *cogl_vbo = tmp->data; + GList *tmp2; + + if (!cogl_vbo->flags & COGL_MESH_VBO_FLAG_STRIDED) + continue; + + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglMeshAttribute *vbo_attribute = tmp2->data; + const char *attribute_start = attribute->u.pointer; + const char *vbo_attribute_start = vbo_attribute->u.pointer; + + /* NB: All attributes have mesh->n_vertices values which + * simplifies determining which attributes are interleved + * since we assume they will start no farther than +- a + * stride away from each other: + */ + if (attribute_start <= (vbo_attribute_start - vbo_attribute->stride) + || attribute_start + >= (vbo_attribute_start + vbo_attribute->stride)) + continue; /* Not interleved */ + + cogl_vbo->attributes = + g_list_prepend (cogl_vbo->attributes, attribute); + + if (attribute->flags & COGL_MESH_ATTRIBUTE_FLAG_FREQUENT_RESUBMIT) + { + cogl_vbo->flags &= ~COGL_MESH_VBO_FLAG_INFREQUENT_RESUBMIT; + cogl_vbo->flags |= COGL_MESH_VBO_FLAG_FREQUENT_RESUBMIT; + } + return; + } + } + new_cogl_vbo = g_slice_alloc (sizeof (CoglMeshVBO)); + new_cogl_vbo->vbo_name = 0; + new_cogl_vbo->attributes = NULL; + new_cogl_vbo->attributes = + g_list_prepend (new_cogl_vbo->attributes, attribute); + /* Any one of the interleved attributes will have the same span_bytes */ + new_cogl_vbo->vbo_bytes = attribute->span_bytes; + new_cogl_vbo->flags = COGL_MESH_VBO_FLAG_STRIDED; + + if (attribute->flags & COGL_MESH_ATTRIBUTE_FLAG_INFREQUENT_RESUBMIT) + new_cogl_vbo->flags |= COGL_MESH_VBO_FLAG_INFREQUENT_RESUBMIT; + else + new_cogl_vbo->flags |= COGL_MESH_VBO_FLAG_FREQUENT_RESUBMIT; + + *new_vbos = g_list_prepend (*new_vbos, new_cogl_vbo); + return; +} + +/* This iterates through the list of submitted VBOs looking for one that + * contains attribute. If found the list *link* is removed and returned */ +static GList * +unlink_submitted_vbo_containing_attribute (GList **submitted_vbos, + CoglMeshAttribute *attribute) +{ + GList *tmp; + GList *next = NULL; + + for (tmp = *submitted_vbos; tmp != NULL; tmp = next) + { + CoglMeshVBO *submitted_vbo = tmp->data; + GList *tmp2; + + next = tmp->next; + + for (tmp2 = submitted_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglMeshAttribute *submitted_attribute = tmp2->data; + + if (submitted_attribute->name == attribute->name) + { + *submitted_vbos = g_list_remove_link (*submitted_vbos, tmp); + return tmp; + } + } + } + + return NULL; +} + +/* Unlinks all the submitted VBOs that conflict with the new cogl_vbo and + * returns them as a list. */ +static GList * +get_submitted_vbo_conflicts (GList **submitted_vbos, CoglMeshVBO *cogl_vbo) +{ + GList *tmp; + GList *conflicts = NULL; + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next) + { + GList *link = + unlink_submitted_vbo_containing_attribute (submitted_vbos, + tmp->data); + if (link) + { + /* prepend the link to the list of conflicts: */ + link->next = conflicts; + conflicts = link; + } + } + return conflicts; +} + +/* Any attributes in cogl_vbo gets removed from conflict_vbo */ +static void +disassociate_conflicting_attributes (CoglMeshVBO *conflict_vbo, + CoglMeshVBO *cogl_vbo) +{ + GList *tmp; + + /* NB: The attributes list in conflict_vbo will be shrinking so + * we iterate those in the inner loop. */ + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next) + { + CoglMeshAttribute *attribute = tmp->data; + GList *tmp2; + for (tmp2 = conflict_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglMeshAttribute *conflict_attribute = tmp2->data; + + if (conflict_attribute->name == attribute->name) + { + free_mesh_attribute (conflict_attribute); + conflict_vbo->attributes = + g_list_delete_link (conflict_vbo->attributes, tmp2); + break; + } + } + } +} + +static void +free_cogl_mesh_vbo (CoglMeshVBO *cogl_vbo, gboolean delete_gl_vbo) +{ + GList *tmp; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next) + { + free_mesh_attribute (tmp->data); + } + g_list_free (cogl_vbo->attributes); + + if (delete_gl_vbo && cogl_vbo->flags & COGL_MESH_VBO_FLAG_SUBMITTED) + GE (glDeleteBuffers (1, &cogl_vbo->vbo_name)); + + g_slice_free (CoglMeshVBO, cogl_vbo); +} + +/* This figures out the lowest attribute client pointer. (This pointer is used + * to upload all the interleved attributes). + * + * In the process it also replaces the client pointer with the attributes + * offset, and marks the attribute as submitted. + */ +static const void * +prep_strided_vbo_for_upload (CoglMeshVBO *cogl_vbo) +{ + GList *tmp; + const char *lowest_pointer = NULL; + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next) + { + CoglMeshAttribute *attribute = tmp->data; + const char *client_pointer = attribute->u.pointer; + + if (!lowest_pointer || client_pointer < lowest_pointer) + lowest_pointer = client_pointer; + } + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next) + { + CoglMeshAttribute *attribute = tmp->data; + const char *client_pointer = attribute->u.pointer; + attribute->u.vbo_offset = client_pointer - lowest_pointer; + attribute->flags |= COGL_MESH_ATTRIBUTE_FLAG_SUBMITTED; + } + + return lowest_pointer; +} + +static gboolean +upload_multipack_vbo_via_map_buffer (CoglMeshVBO *cogl_vbo) +{ +#if HAVE_COGL_GL + GList *tmp; + guint offset = 0; + char *buf; + + _COGL_GET_CONTEXT (ctx, FALSE); + + buf = glMapBuffer (GL_ARRAY_BUFFER, GL_WRITE_ONLY); + glGetError(); + if (!buf) + return FALSE; + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next) + { + CoglMeshAttribute *attribute = tmp->data; + gsize attribute_size = attribute->span_bytes; + gsize gl_type_size = get_gl_type_size (attribute->flags); + + PAD_FOR_ALIGNMENT (offset, gl_type_size); + + memcpy (buf + offset, attribute->u.pointer, attribute_size); + + attribute->u.vbo_offset = offset; + attribute->flags |= COGL_MESH_ATTRIBUTE_FLAG_SUBMITTED; + offset += attribute_size; + } + glUnmapBuffer (GL_ARRAY_BUFFER); + + return TRUE; +#else + return FALSE; +#endif +} + +static void +upload_multipack_vbo_via_buffer_sub_data (CoglMeshVBO *cogl_vbo) +{ + GList *tmp; + guint offset = 0; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + for (tmp = cogl_vbo->attributes; tmp != NULL; tmp = tmp->next) + { + CoglMeshAttribute *attribute = tmp->data; + gsize attribute_size = attribute->span_bytes; + gsize gl_type_size = get_gl_type_size (attribute->flags); + + PAD_FOR_ALIGNMENT (offset, gl_type_size); + + GE (glBufferDataSub (GL_ARRAY_BUFFER, + offset, + attribute_size, + attribute->u.pointer)); + attribute->u.vbo_offset = offset; + attribute->flags |= COGL_MESH_ATTRIBUTE_FLAG_SUBMITTED; + offset += attribute_size; + } +} + +static void +upload_gl_vbo (CoglMeshVBO *cogl_vbo) +{ + GLenum usage; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + g_return_if_fail (cogl_vbo->vbo_name != 0); + + if (cogl_vbo->flags & COGL_MESH_VBO_FLAG_FREQUENT_RESUBMIT) + usage = GL_DYNAMIC_DRAW; + else + usage = GL_STATIC_DRAW; + + GE (glBindBuffer (GL_ARRAY_BUFFER, cogl_vbo->vbo_name)); + + if (cogl_vbo->flags & COGL_MESH_VBO_FLAG_STRIDED) + { + const void *pointer = + prep_strided_vbo_for_upload (cogl_vbo); + GE (glBufferData (GL_ARRAY_BUFFER, + cogl_vbo->vbo_bytes, + pointer, + usage)); + } + else if (cogl_vbo->flags & COGL_MESH_VBO_FLAG_MULTIPACK) + { + /* First we make it obvious to the driver that we want to update the + * whole buffer (without this, the driver is more likley to block + * if the GPU is busy using the buffer) */ + GE (glBufferData (GL_ARRAY_BUFFER, + cogl_vbo->vbo_bytes, + NULL, + usage)); + + /* I think it might depend on the specific driver/HW whether its better to + * use glMapBuffer here or glBufferSubData here. There is even a good + * thread about this topic here: + * http://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg35004.html + * For now I have gone with glMapBuffer, but the jury is still out. + */ + + if (!upload_multipack_vbo_via_map_buffer (cogl_vbo)) + upload_multipack_vbo_via_buffer_sub_data (cogl_vbo); + } + else + { + CoglMeshAttribute *attribute = cogl_vbo->attributes->data; + GE (glBufferData (GL_ARRAY_BUFFER, + cogl_vbo->vbo_bytes, + attribute->u.pointer, + usage)); + /* We forget this pointer now since the client will be free + * to re-use this memory */ + attribute->u.pointer = NULL; + attribute->flags |= COGL_MESH_ATTRIBUTE_FLAG_SUBMITTED; + } + + cogl_vbo->flags |= COGL_MESH_VBO_FLAG_SUBMITTED; + + GE (glBindBuffer (GL_ARRAY_BUFFER, 0)); +} + +/* Note: although there ends up being quite a few inner loops involved + * with resolving buffers, the number of attributes will be low so I + * don't expect them to cause a problem. */ +static void +resolve_new_cogl_mesh_vbo (CoglMesh *mesh, + CoglMeshVBO *new_cogl_vbo, + GList **final_vbos) +{ + GList *conflicts; + GList *tmp; + GList *next; + gboolean found_target_vbo = FALSE; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + conflicts = + get_submitted_vbo_conflicts (&mesh->submitted_vbos, new_cogl_vbo); + + for (tmp = conflicts; tmp != NULL; tmp = next) + { + CoglMeshVBO *conflict_vbo = tmp->data; + + next = tmp->next; + + disassociate_conflicting_attributes (conflict_vbo, new_cogl_vbo); + + if (!conflict_vbo->attributes) + { + /* See if we can re-use this now empty VBO: */ + + if (!found_target_vbo + && conflict_vbo->vbo_bytes == new_cogl_vbo->vbo_bytes) + { + found_target_vbo = TRUE; + new_cogl_vbo->vbo_name = conflict_vbo->vbo_name; + free_cogl_mesh_vbo (conflict_vbo, FALSE); + + upload_gl_vbo (new_cogl_vbo); + + *final_vbos = g_list_prepend (*final_vbos, new_cogl_vbo); + } + else + free_cogl_mesh_vbo (conflict_vbo, TRUE); + } + else + { + /* Relink the VBO back into mesh->submitted_vbos since it may + * be involved in other conflicts later */ + tmp->next = mesh->submitted_vbos; + tmp->prev = NULL; + mesh->submitted_vbos = tmp; + } + } + + if (!found_target_vbo) + { + GE (glGenBuffers (1, &new_cogl_vbo->vbo_name)); + /* FIXME: debug */ + g_assert (glGetError() == GL_NO_ERROR); + + upload_gl_vbo (new_cogl_vbo); + *final_vbos = g_list_prepend (*final_vbos, new_cogl_vbo); + } +} + +/** + * cogl_mesh_submit: + * @handle: A Cogl mesh handle + * + * This function copies all the user added attributes into a buffer object + * managed by the OpenGL driver. + * + * After the attributes have been submitted, then you may no longer add or + * remove attributes from a mesh, though you can enable or disable them. + */ +void +cogl_mesh_submit (CoglHandle handle) +{ + CoglMesh *mesh; + GList *tmp; + CoglMeshVBO *new_multipack_vbo; + GList *new_multipack_vbo_link; + GList *new_vbos = NULL; + GList *reuse_vbos = NULL; + GList *final_vbos = NULL; + + if (!cogl_is_mesh (handle)) + return; + + mesh = _cogl_mesh_pointer_from_handle (handle); + + /* The objective now is to copy the attribute data supplied by the client + * into buffer objects, but it's important to minimize the amount of memory + * bandwidth we waste here. + * + * We need to group together the attributes that are interleved so that the + * driver can use a single continguous memcpy for these. All BOs for + * interleved data are created as STATIC_DRAW_ARB. + * + * Non interleved attributes tagged as INFREQUENT_RESUBMIT will be grouped + * together back to back in a single BO created as STATIC_DRAW_ARB + * + * Non interleved attributes tagged as FREQUENT_RESUBMIT will be copied into + * individual buffer objects, and the BO itself created DYNAMIC_DRAW_ARB + * + * If we are modifying an submitted mesh object then we are carefull not + * to needlesly delete submitted buffer objects and replace with new ones, + * instead we upload new data to the submitted buffers. + */ + + /* NB: We must forget attribute->pointer after submitting since the user + * is free to re-use that memory for other purposes now. */ + + /* Pseudo code: + * + * Broadly speaking we start with a list of unsorted attributes, and filter + * those into 'new' and 're-use' CoglMeshVBO (CBO) lists. We then take the + * list of new CBO structs and compare with the CBOs that have already been + * submitted to the GPU (but ignoring those we already know will be re-used) + * to determine what other CBOs can be re-used, due to being superseded, + * and what new GL VBOs need to be created. + * + * We have three kinds of CBOs: + * - Unstrided CBOs + * These contain a single tightly packed attribute + * These are currently the only ones ever marked as FREQUENT_SUBMIT + * - Strided CBOs + * These typically contain multiple interleved sets of attributes, + * though they can contain just one attribute with a stride + * - Multi Pack CBOs + * These contain multiple attributes tightly packed back to back) + * + * First create a new-CBOs entry "new-multipack-CBO" + * Tag "new-multipack-CBO" as MULTIPACK + INFREQUENT_RESUBMIT + * For each unsorted attrib: + * if already marked as submitted: + * iterate reuse-CBOs: + * if we find one that contains this attribute: + * free redundant unsorted attrib struct + * remove the UNUSED flag from the attrib found in the reuse-CBO + * continue to next unsorted attrib + * iterate submitted VBOs: + * if we find one that contains this attribute: + * free redundant unsorted attrib struct + * unlink the vbo and move it to the list of reuse-CBOs + * mark all attributes except the one just matched as UNUSED + * assert (found) + * continue to next unsorted attrib + * if strided: + * iterate the new, strided, CBOs, to see if the attribute is + * interleved with one of them, if found: + * add to the matched CBO + * else if not found: + * create a new-CBOs entry tagged STRIDED + INFREQUENT_RESUBMIT + * else if unstrided && tagged with FREQUENT_RESUBMIT: + * create a new-CBOs entry tagged UNSTRIDED + FREQUENT_RESUBMIT + * else + * add to the new-multipack-CBO + * free list of unsorted-attribs + * + * Next compare the new list of CBOs with the submitted set and try to + * minimize the memory bandwidth required to upload the attributes and the + * overhead of creating new GL-BOs. + * + * We deal with four sets of CBOs: + * - The "new" CBOs + * (as determined above during filtering) + * - The "re-use" CBOs + * (as determined above during filtering) + * - The "submitted" CBOs + * (I.e. ones currently submitted to the GPU) + * - The "final" CBOs + * (The result of resolving the differences between the above sets) + * + * The re-use CBOs are dealt with first, and we simply delete any remaining + * attributes in these that are still marked as UNUSED, and move them + * to the list of final CBOs. + * + * Next we iterate through the "new" CBOs, searching for conflicts + * with the "submitted" CBOs and commit our decision to the "final" CBOs + * + * When searching for submitted entries we always unlink items from the + * submitted list once we make matches (before we make descisions + * based on the matches). If the CBO node is superseded it is freed, + * if it is modified but may be needed for more descisions later it is + * relinked back into the submitted list and if it's identical to a new + * CBO it will be linked into the final list. + * + * At the end the list of submitted CBOs represents the attributes that were + * deleted from the mesh. + * + * Iterate re-use-CBOs: + * Iterate attribs for each: + * if attrib UNUSED: + * remove the attrib from the CBO + free + * |Note: we could potentially mark this as a re-useable gap + * |if needs be later. + * add re-use CBO to the final-CBOs + * Iterate new-CBOs: + * List submitted CBOs conflicting with the this CBO (Unlinked items) + * found-target-BO=FALSE + * Iterate conflicting CBOs: + * Disassociate conflicting attribs from conflicting CBO struct + * If no attribs remain: + * If found-target-BO!=TRUE + * _AND_ If the total size of the conflicting CBO is compatible: + * |Note: We don't currently consider re-using oversized buffers + * found-target-BO=TRUE + * upload replacement data + * free submitted CBO struct + * add new CBO struct to final-CBOs + * else: + * delete conflict GL-BO + * delete conflict CBO struct + * else: + * relink CBO back into submitted-CBOs + * + * if found-target-BO == FALSE: + * create a new GL-BO + * upload data + * add new CBO struct to final-BOs + * + * Iterate through the remaining "submitted" CBOs: + * delete the submitted GL-BO + * free the submitted CBO struct + */ + + new_multipack_vbo = g_slice_alloc (sizeof (CoglMeshVBO)); + new_multipack_vbo->vbo_name = 0; + new_multipack_vbo->flags = COGL_MESH_VBO_FLAG_MULTIPACK + | COGL_MESH_VBO_FLAG_INFREQUENT_RESUBMIT; + new_multipack_vbo->vbo_bytes = 0; + new_multipack_vbo->attributes = NULL; + new_vbos = g_list_prepend (new_vbos, new_multipack_vbo); + /* We save the link pointer here, just so we can do a fast removal later if + * no attributes get added to this vbo. */ + new_multipack_vbo_link = new_vbos; + + /* Start with a list of unsorted attributes, and filter those into + * potential new Cogl BO structs + */ + for (tmp = mesh->new_attributes; tmp != NULL; tmp = tmp->next) + { + CoglMeshAttribute *attribute = tmp->data; + + if (attribute->flags & COGL_MESH_ATTRIBUTE_FLAG_SUBMITTED) + { + /* If the attribute is already marked as submitted, then we need + * to find the existing VBO that contains it so we dont delete it. + * + * NB: this also frees the attribute struct since it's implicitly + * redundant in this case. + */ + filter_already_submitted_attribute (attribute, + &reuse_vbos, + &mesh->submitted_vbos); + } + else if (attribute->stride) + { + /* look for a CoglMeshVBO that the attribute is interleved with. If + * one can't be found then a new CoglMeshVBO is allocated and added + * to the list of new_vbos: */ + filter_strided_attribute (attribute, &new_vbos); + } + else if (attribute->flags & COGL_MESH_ATTRIBUTE_FLAG_FREQUENT_RESUBMIT) + { + CoglMeshVBO *cogl_vbo = g_slice_alloc (sizeof (CoglMeshVBO)); + + /* attributes we expect will be frequently resubmitted are placed + * in their own VBO so that updates don't impact other attributes + */ + + cogl_vbo->vbo_name = 0; + cogl_vbo->flags = COGL_MESH_VBO_FLAG_UNSTRIDED + | COGL_MESH_VBO_FLAG_FREQUENT_RESUBMIT; + cogl_vbo->attributes = NULL; + cogl_vbo->attributes = g_list_prepend (cogl_vbo->attributes, + attribute); + cogl_vbo->vbo_bytes = attribute->span_bytes; + new_vbos = g_list_prepend (new_vbos, cogl_vbo); + } + else + { + gsize gl_type_size = get_gl_type_size (attribute->flags); + + /* Infrequently updated attributes just get packed back to back + * in a single VBO: */ + new_multipack_vbo->attributes = + g_list_prepend (new_multipack_vbo->attributes, + attribute); + + /* Note: we have to ensure that each run of attributes is + * naturally aligned according to its data type, which may + * require some padding bytes: */ + + /* XXX: We also have to be sure that the attributes aren't + * reorderd before being uploaded because the alignment padding + * is based on the adjacent attribute. + */ + + PAD_FOR_ALIGNMENT (new_multipack_vbo->vbo_bytes, gl_type_size); + + new_multipack_vbo->vbo_bytes += attribute->span_bytes; + } + } + + /* At this point all mesh->new_attributes have been filtered into + * CoglMeshVBOs... */ + g_list_free (mesh->new_attributes); + mesh->new_attributes = NULL; + + /* If the multipack vbo wasn't needed: */ + if (new_multipack_vbo->attributes == NULL) + { + new_vbos = g_list_delete_link (new_vbos, new_multipack_vbo_link); + g_slice_free (CoglMeshVBO, new_multipack_vbo); + } + + for (tmp = reuse_vbos; tmp != NULL; tmp = tmp->next) + remove_unused_attributes (tmp->data); + final_vbos = g_list_concat (final_vbos, reuse_vbos); + + for (tmp = new_vbos; tmp != NULL; tmp = tmp->next) + resolve_new_cogl_mesh_vbo (mesh, tmp->data, &final_vbos); + + /* Anything left corresponds to deleted attributes: */ + for (tmp = mesh->submitted_vbos; tmp != NULL; tmp = tmp->next) + free_cogl_mesh_vbo (tmp->data, TRUE); + g_list_free (mesh->submitted_vbos); + + mesh->submitted_vbos = final_vbos; +} + +static GLenum +get_gl_type_from_attribute_flags (CoglMeshAttributeFlags flags) +{ + CoglMeshAttributeFlags gl_type = + flags & COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_MASK; + + switch (gl_type) + { + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_BYTE: + return GL_BYTE; + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_BYTE: + return GL_UNSIGNED_BYTE; + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_SHORT: + return GL_SHORT; + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_SHORT: + return GL_UNSIGNED_SHORT; + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_FLOAT: + return GL_FLOAT; +#if HAVE_COGL_GL + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_INT: + return GL_INT; + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_UNSIGNED_INT: + return GL_UNSIGNED_INT; + case COGL_MESH_ATTRIBUTE_FLAG_GL_TYPE_DOUBLE: + return GL_DOUBLE; +#endif + default: + g_warning ("Couldn't convert from attribute flags (0x%08x) " + "to gl type enum\n", flags); + return 0; + } +} + +static void +enable_state_for_drawing_mesh (CoglMesh *mesh) +{ + GList *tmp; + GLenum gl_type; + GLuint generic_index = 0; + gulong enable_flags = COGL_ENABLE_BLEND; + /* FIXME: I don't think it's appropriate to force enable + * GL_BLEND here. */ + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + for (tmp = mesh->submitted_vbos; tmp != NULL; tmp = tmp->next) + { + CoglMeshVBO *cogl_vbo = tmp->data; + GList *tmp2; + + GE (glBindBuffer (GL_ARRAY_BUFFER, cogl_vbo->vbo_name)); + + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglMeshAttribute *attribute = tmp2->data; + CoglMeshAttributeFlags type = + attribute->flags & COGL_MESH_ATTRIBUTE_FLAG_TYPE_MASK; + + if (!(attribute->flags & COGL_MESH_ATTRIBUTE_FLAG_ENABLED)) + continue; + + gl_type = get_gl_type_from_attribute_flags (attribute->flags); + switch (type) + { + case COGL_MESH_ATTRIBUTE_FLAG_COLOR_ARRAY: + /* FIXME: go through cogl cache to enable color array */ + GE (glEnableClientState (GL_COLOR_ARRAY)); + GE (glColorPointer (attribute->n_components, + gl_type, + attribute->stride, + (const GLvoid *)attribute->u.vbo_offset)); + break; + case COGL_MESH_ATTRIBUTE_FLAG_NORMAL_ARRAY: + /* FIXME: go through cogl cache to enable normal array */ + GE (glEnableClientState (GL_NORMAL_ARRAY)); + GE (glNormalPointer (gl_type, + attribute->stride, + (const GLvoid *)attribute->u.vbo_offset)); + break; + case COGL_MESH_ATTRIBUTE_FLAG_TEXTURE_COORD_ARRAY: + /* FIXME: set the active texture unit */ + /* NB: Cogl currently manages unit 0 */ + enable_flags |= (COGL_ENABLE_TEXCOORD_ARRAY + | COGL_ENABLE_TEXTURE_2D); + /* FIXME: I don't think it's appropriate to force enable + * GL_TEXTURE_2D here. */ + /* GE (glEnableClientState (GL_VERTEX_ARRAY)); */ + GE (glTexCoordPointer (attribute->n_components, + gl_type, + attribute->stride, + (const GLvoid *)attribute->u.vbo_offset)); + break; + case COGL_MESH_ATTRIBUTE_FLAG_VERTEX_ARRAY: + enable_flags |= COGL_ENABLE_VERTEX_ARRAY; + /* GE (glEnableClientState (GL_VERTEX_ARRAY)); */ + GE (glVertexPointer (attribute->n_components, + gl_type, + attribute->stride, + (const GLvoid *)attribute->u.vbo_offset)); + break; + case COGL_MESH_ATTRIBUTE_FLAG_CUSTOM_ARRAY: + { +#ifdef MAY_HAVE_PROGRAMABLE_GL + GLboolean normalized = GL_FALSE; + if (attribute->flags & COGL_MESH_ATTRIBUTE_FLAG_NORMALIZED) + normalized = GL_TRUE; + /* FIXME: go through cogl cache to enable generic array */ + GE (glEnableVertexAttribArray (generic_index++)); + GE (glVertexAttribPointer (generic_index, + attribute->n_components, + gl_type, + normalized, + attribute->stride, + (const GLvoid *) + attribute->u.vbo_offset)); +#endif + } + break; + default: + g_warning ("Unrecognised attribute type 0x%08x", type); + } + } + } + + cogl_enable (enable_flags); + +} + +static void +disable_state_for_drawing_mesh (CoglMesh *mesh) +{ + GList *tmp; + GLenum gl_type; + GLuint generic_index = 0; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Disable all the client state that cogl doesn't currently know + * about: + */ + GE (glBindBuffer (GL_ARRAY_BUFFER, 0)); + + generic_index = 0; + for (tmp = mesh->submitted_vbos; tmp != NULL; tmp = tmp->next) + { + CoglMeshVBO *cogl_vbo = tmp->data; + GList *tmp2; + + for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) + { + CoglMeshAttribute *attribute = tmp2->data; + CoglMeshAttributeFlags type = + attribute->flags & COGL_MESH_ATTRIBUTE_FLAG_TYPE_MASK; + + if (!(attribute->flags & COGL_MESH_ATTRIBUTE_FLAG_ENABLED)) + continue; + + gl_type = get_gl_type_from_attribute_flags(attribute->flags); + switch (type) + { + case COGL_MESH_ATTRIBUTE_FLAG_COLOR_ARRAY: + /* FIXME: go through cogl cache to enable color array */ + GE (glDisableClientState (GL_COLOR_ARRAY)); + break; + case COGL_MESH_ATTRIBUTE_FLAG_NORMAL_ARRAY: + /* FIXME: go through cogl cache to enable normal array */ + GE (glDisableClientState (GL_NORMAL_ARRAY)); + break; + case COGL_MESH_ATTRIBUTE_FLAG_TEXTURE_COORD_ARRAY: + /* FIXME: set the active texture unit */ + /* NB: Cogl currently manages unit 0 */ + /* GE (glDisableClientState (GL_VERTEX_ARRAY)); */ + break; + case COGL_MESH_ATTRIBUTE_FLAG_VERTEX_ARRAY: + /* GE (glDisableClientState (GL_VERTEX_ARRAY)); */ + break; + case COGL_MESH_ATTRIBUTE_FLAG_CUSTOM_ARRAY: +#ifdef MAY_HAVE_PROGRAMABLE_GL + /* FIXME: go through cogl cache to enable generic array */ + GE (glDisableVertexAttribArray (generic_index++)); +#endif + break; + default: + g_warning ("Unrecognised attribute type 0x%08x", type); + } + } + } +} + +void +cogl_mesh_draw_arrays (CoglHandle handle, + GLenum mode, + GLint first, + GLsizei count) +{ + CoglMesh *mesh; + + if (!cogl_is_mesh (handle)) + return; + + mesh = _cogl_mesh_pointer_from_handle (handle); + + enable_state_for_drawing_mesh (mesh); + + /* FIXME: flush cogl cache */ + GE (glDrawArrays (mode, first, count)); + + disable_state_for_drawing_mesh (mesh); +} + +void +cogl_mesh_draw_range_elements (CoglHandle handle, + GLenum mode, + GLuint start, + GLuint end, + GLsizei count, + GLenum type, + const GLvoid *indices) +{ + CoglMesh *mesh; + + if (!cogl_is_mesh (handle)) + return; + + mesh = _cogl_mesh_pointer_from_handle (handle); + + enable_state_for_drawing_mesh (mesh); + + /* FIXME: flush cogl cache */ + GE (glDrawRangeElements (mode, start, end, count, type, indices)); + + disable_state_for_drawing_mesh (mesh); +} + +static void +_cogl_mesh_free (CoglMesh *mesh) +{ + GList *tmp; + + for (tmp = mesh->submitted_vbos; tmp != NULL; tmp = tmp->next) + free_cogl_mesh_vbo (tmp->data, TRUE); + for (tmp = mesh->new_attributes; tmp != NULL; tmp = tmp->next) + free_mesh_attribute (tmp->data); + + g_slice_free (CoglMesh, mesh); +} + diff --git a/doc/reference/cogl/cogl-docs.sgml b/doc/reference/cogl/cogl-docs.sgml index 2a906f1de..d1718437e 100644 --- a/doc/reference/cogl/cogl-docs.sgml +++ b/doc/reference/cogl/cogl-docs.sgml @@ -60,6 +60,7 @@ + diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index d045538d1..c2c9f40e7 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -257,3 +257,18 @@ cogl_color_get_green_float cogl_color_get_blue_float cogl_color_get_alpha_float + +
+cogl-mesh +Mesh API +cogl_mesh_new +cogl_mesh_ref +cogl_mesh_unref +cogl_mesh_add_attribute +cogl_mesh_delete_attribute +cogl_mesh_enable_attribute +cogl_mesh_disable_attribute +cogl_mesh_draw_arrays +cogl_mesh_draw_range_elements +
+ diff --git a/gl/cogl-context.c b/gl/cogl-context.c index d3832178f..e4b7b0493 100644 --- a/gl/cogl-context.c +++ b/gl/cogl-context.c @@ -67,6 +67,8 @@ cogl_create_context () _context->shader_handles = NULL; _context->program_handles = NULL; + + _context->mesh_handles = NULL; _context->pf_glGenRenderbuffersEXT = NULL; _context->pf_glBindRenderbufferEXT = NULL; diff --git a/gl/cogl-context.h b/gl/cogl-context.h index 4b442ecba..62216dcbc 100644 --- a/gl/cogl-context.h +++ b/gl/cogl-context.h @@ -68,6 +68,9 @@ typedef struct /* Programs */ GArray *program_handles; + /* Mesh */ + GArray *mesh_handles; + /* Relying on glext.h to define these */ COGL_PFNGLGENRENDERBUFFERSEXTPROC pf_glGenRenderbuffersEXT; COGL_PFNGLDELETERENDERBUFFERSEXTPROC pf_glDeleteRenderbuffersEXT; @@ -94,7 +97,18 @@ typedef struct COGL_PFNGLGETINFOLOGARBPROC pf_glGetInfoLogARB; COGL_PFNGLGETOBJECTPARAMETERIVARBPROC pf_glGetObjectParameterivARB; COGL_PFNGLUNIFORM1FARBPROC pf_glUniform1fARB; - + COGL_PFNGLVERTEXATTRIBPOINTERARBPROC pf_glVertexAttribPointerARB; + COGL_PFNGLENABLEVERTEXATTRIBARRAYARBPROC pf_glEnableVertexAttribArrayARB; + COGL_PFNGLDISABLEVERTEXATTRIBARRAYARBPROC pf_glDisableVertexAttribArrayARB; + + COGL_PFNGLGENBUFFERSARBPROC pf_glGenBuffersARB; + COGL_PFNGLBINDBUFFERARBPROC pf_glBindBufferARB; + COGL_PFNGLBUFFERDATAARBPROC pf_glBufferDataARB; + COGL_PFNGLBUFFERDATASUBARBPROC pf_glBufferDataSubARB; + COGL_PFNGLMAPBUFFERARBPROC pf_glMapBufferARB; + COGL_PFNGLUNMAPBUFFERARBPROC pf_glUnmapBufferARB; + COGL_PFNGLDELETEBUFFERSARBPROC pf_glDeleteBuffersARB; + } CoglContext; CoglContext * diff --git a/gl/cogl-defines.h.in b/gl/cogl-defines.h.in index a93f9f030..d33a8323b 100644 --- a/gl/cogl-defines.h.in +++ b/gl/cogl-defines.h.in @@ -842,6 +842,61 @@ typedef void (GLint location, GLfloat v0); +typedef void + (APIENTRYP COGL_PFNGLVERTEXATTRIBPOINTERARBPROC) + (GLuint index, + GLint size, + GLenum type, + GLboolean normalized, + GLsizei stride, + const GLvoid *pointer); + +typedef void + (APIENTRYP COGL_PFNGLENABLEVERTEXATTRIBARRAYARBPROC) + (GLuint index); + +typedef void + (APIENTRYP COGL_PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) + (GLuint index); + +typedef void + (APIENTRYP COGL_PFNGLGENBUFFERSARBPROC) + (GLuint n, + GLuint *buffers); + +typedef void + (APIENTRYP COGL_PFNGLBINDBUFFERARBPROC) + (GLenum target, + GLuint buffer); + +typedef void + (APIENTRYP COGL_PFNGLBUFFERDATAARBPROC) + (GLenum target, + GLsizeiptr size, + const GLvoid *data, + GLenum usage); + +typedef void + (APIENTRYP COGL_PFNGLBUFFERDATASUBARBPROC) + (GLenum target, + GLintptr offset, + GLsizeiptr size, + const GLvoid *data); + +typedef void * + (APIENTRYP COGL_PFNGLMAPBUFFERARBPROC) + (GLenum target, + GLenum access); + +typedef GLboolean + (APIENTRYP COGL_PFNGLUNMAPBUFFERARBPROC) + (GLenum target); + +typedef void + (APIENTRYP COGL_PFNGLDELETEBUFFERSARBPROC) + (GLsizei n, + const GLuint *buffers); + G_END_DECLS #endif diff --git a/gl/cogl.c b/gl/cogl.c index a77256e78..e3da3506f 100644 --- a/gl/cogl.c +++ b/gl/cogl.c @@ -1015,6 +1015,18 @@ _cogl_features_init () ctx->pf_glUniform1fARB = (COGL_PFNGLUNIFORM1FARBPROC) cogl_get_proc_address ("glUniform1fARB"); + + ctx->pf_glVertexAttribPointerARB = + (COGL_PFNGLVERTEXATTRIBPOINTERARBPROC) + cogl_get_proc_address ("glVertexAttribPointerARB"); + + ctx->pf_glEnableVertexAttribArrayARB = + (COGL_PFNGLENABLEVERTEXATTRIBARRAYARBPROC) + cogl_get_proc_address ("glEnableVertexAttribArrayARB"); + + ctx->pf_glDisableVertexAttribArrayARB = + (COGL_PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) + cogl_get_proc_address ("glDisableVertexAttribArrayARB"); if (ctx->pf_glCreateProgramObjectARB && ctx->pf_glCreateShaderObjectARB && @@ -1027,7 +1039,10 @@ _cogl_features_init () ctx->pf_glDeleteObjectARB && ctx->pf_glGetInfoLogARB && ctx->pf_glGetObjectParameterivARB && - ctx->pf_glUniform1fARB) + ctx->pf_glUniform1fARB && + ctx->pf_glVertexAttribPointerARB && + ctx->pf_glEnableVertexAttribArrayARB && + ctx->pf_glDisableVertexAttribArrayARB) flags |= COGL_FEATURE_SHADERS_GLSL; } @@ -1116,6 +1131,39 @@ _cogl_features_init () if (max_clip_planes >= 4) flags |= COGL_FEATURE_FOUR_CLIP_PLANES; + if (cogl_check_extension ("GL_ARB_vertex_buffer_object", gl_extensions)) + { + ctx->pf_glGenBuffersARB = + (COGL_PFNGLGENBUFFERSARBPROC) + cogl_get_proc_address ("glGenBuffersARB"); + ctx->pf_glBindBufferARB = + (COGL_PFNGLBINDBUFFERARBPROC) + cogl_get_proc_address ("glBindBufferARB"); + ctx->pf_glBufferDataARB = + (COGL_PFNGLBUFFERDATAARBPROC) + cogl_get_proc_address ("glBufferDataARB"); + ctx->pf_glBufferDataSubARB = + (COGL_PFNGLBUFFERDATASUBARBPROC) + cogl_get_proc_address ("glBufferDataSubARB"); + ctx->pf_glDeleteBuffersARB = + (COGL_PFNGLDELETEBUFFERSARBPROC) + cogl_get_proc_address ("glDeleteBuffersARB"); + ctx->pf_glMapBufferARB = + (COGL_PFNGLMAPBUFFERARBPROC) + cogl_get_proc_address ("glMapBufferARB"); + ctx->pf_glUnmapBufferARB = + (COGL_PFNGLUNMAPBUFFERARBPROC) + cogl_get_proc_address ("glUnmapBufferARB"); + if (ctx->pf_glGenBuffersARB + && ctx->pf_glBindBufferARB + && ctx->pf_glBufferDataARB + && ctx->pf_glBufferDataSubARB + && ctx->pf_glDeleteBuffersARB + && ctx->pf_glMapBufferARB + && ctx->pf_glUnmapBufferARB) + flags |= COGL_FEATURE_VBOS; + } + /* Cache features */ ctx->feature_flags = flags; ctx->features_cached = TRUE; @@ -1265,3 +1313,4 @@ cogl_fog_set (const CoglColor *fog_color, glFogf (GL_FOG_START, COGL_FIXED_TO_FLOAT (start)); glFogf (GL_FOG_END, COGL_FIXED_TO_FLOAT (stop)); } + diff --git a/gles/cogl-context.c b/gles/cogl-context.c index 5e061ecb0..f70160659 100644 --- a/gles/cogl-context.c +++ b/gles/cogl-context.c @@ -67,6 +67,8 @@ cogl_create_context () _context->program_handles = NULL; _context->shader_handles = NULL; _context->draw_buffer = COGL_WINDOW_BUFFER; + + _context->mesh_handles = NULL; _context->blend_src_factor = CGL_SRC_ALPHA; _context->blend_dst_factor = CGL_ONE_MINUS_SRC_ALPHA; diff --git a/gles/cogl-context.h b/gles/cogl-context.h index f13ffc390..e21c52e15 100644 --- a/gles/cogl-context.h +++ b/gles/cogl-context.h @@ -77,6 +77,9 @@ typedef struct GArray *program_handles; GArray *shader_handles; + /* Mesh */ + GArray *mesh_handles; + #ifdef HAVE_COGL_GLES2 CoglGles2Wrapper gles2;