[cogl vertex buffers] Adds fallbacks for drivers without VBO support

Buffer objects aren't currently available for glx indirect contexts, so we
now have a fallback that simply allocates fake client side vbos to store the
attributes.
This commit is contained in:
Robert Bragg 2009-04-15 19:25:55 +01:00
parent bf4a0fa03f
commit c1afd0de69
3 changed files with 121 additions and 35 deletions

View File

@ -125,7 +125,10 @@ typedef enum _CoglVertexBufferVBOFlags
typedef struct _CoglVertexBufferVBO typedef struct _CoglVertexBufferVBO
{ {
CoglVertexBufferVBOFlags flags; CoglVertexBufferVBOFlags flags;
GLuint vbo_name; /*!< The name of the corresponding buffer object */
/* 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 */ gsize vbo_bytes; /*!< The lengh of the allocated buffer object in bytes */
GList *attributes; GList *attributes;
} CoglVertexBufferVBO; } CoglVertexBufferVBO;

View File

@ -5,7 +5,7 @@
* *
* Vertex Buffer API: Handle extensible arrays of vertex attributes * Vertex Buffer API: Handle extensible arrays of vertex attributes
* *
* Copyright (C) 2008 Intel Corporation. * Copyright (C) 2008, 2009 Intel Corporation.
* *
* Authored by: Robert Bragg <robert@linux.intel.com> * Authored by: Robert Bragg <robert@linux.intel.com>
* *
@ -893,7 +893,12 @@ cogl_vertex_buffer_vbo_free (CoglVertexBufferVBO *cogl_vbo,
if (delete_gl_vbo && cogl_vbo->flags & if (delete_gl_vbo && cogl_vbo->flags &
COGL_VERTEX_BUFFER_VBO_FLAG_SUBMITTED) COGL_VERTEX_BUFFER_VBO_FLAG_SUBMITTED)
GE (glDeleteBuffers (1, &cogl_vbo->vbo_name)); {
if (cogl_get_features () & COGL_FEATURE_VBOS)
GE (glDeleteBuffers (1, (GLuint *)&cogl_vbo->vbo_name));
else
g_free (cogl_vbo->vbo_name);
}
g_slice_free (CoglVertexBufferVBO, cogl_vbo); g_slice_free (CoglVertexBufferVBO, cogl_vbo);
} }
@ -937,11 +942,19 @@ upload_multipack_vbo_via_map_buffer (CoglVertexBufferVBO *cogl_vbo)
GList *tmp; GList *tmp;
guint offset = 0; guint offset = 0;
char *buf; char *buf;
gboolean fallback =
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
_COGL_GET_CONTEXT (ctx, FALSE); _COGL_GET_CONTEXT (ctx, FALSE);
buf = glMapBuffer (GL_ARRAY_BUFFER, GL_WRITE_ONLY); if (!fallback)
glGetError(); {
buf = glMapBuffer (GL_ARRAY_BUFFER, GL_WRITE_ONLY);
glGetError();
}
else
buf = cogl_vbo->vbo_name;
if (!buf) if (!buf)
return FALSE; return FALSE;
@ -959,7 +972,9 @@ upload_multipack_vbo_via_map_buffer (CoglVertexBufferVBO *cogl_vbo)
attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED; attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED;
offset += attribute_size; offset += attribute_size;
} }
glUnmapBuffer (GL_ARRAY_BUFFER);
if (!fallback)
glUnmapBuffer (GL_ARRAY_BUFFER);
return TRUE; return TRUE;
#else #else
@ -972,6 +987,8 @@ upload_multipack_vbo_via_buffer_sub_data (CoglVertexBufferVBO *cogl_vbo)
{ {
GList *tmp; GList *tmp;
guint offset = 0; guint offset = 0;
gboolean fallback =
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
@ -983,10 +1000,19 @@ upload_multipack_vbo_via_buffer_sub_data (CoglVertexBufferVBO *cogl_vbo)
PAD_FOR_ALIGNMENT (offset, gl_type_size); PAD_FOR_ALIGNMENT (offset, gl_type_size);
GE (glBufferSubData (GL_ARRAY_BUFFER, if (!fallback)
offset, {
attribute_size, GE (glBufferSubData (GL_ARRAY_BUFFER,
attribute->u.pointer)); offset,
attribute_size,
attribute->u.pointer));
}
else
{
char *dest = (char *)cogl_vbo->vbo_name + offset;
memcpy (dest, attribute->u.pointer, attribute_size);
}
attribute->u.vbo_offset = offset; attribute->u.vbo_offset = offset;
attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED; attribute->flags |= COGL_VERTEX_BUFFER_ATTRIB_FLAG_SUBMITTED;
offset += attribute_size; offset += attribute_size;
@ -997,36 +1023,58 @@ static void
upload_gl_vbo (CoglVertexBufferVBO *cogl_vbo) upload_gl_vbo (CoglVertexBufferVBO *cogl_vbo)
{ {
GLenum usage; GLenum usage;
gboolean fallback =
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
g_return_if_fail (cogl_vbo->vbo_name != 0);
if (cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_FREQUENT_RESUBMIT) if (cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_FREQUENT_RESUBMIT)
usage = GL_DYNAMIC_DRAW; usage = GL_DYNAMIC_DRAW;
else else
usage = GL_STATIC_DRAW; usage = GL_STATIC_DRAW;
GE (glBindBuffer (GL_ARRAY_BUFFER, cogl_vbo->vbo_name)); if (!fallback)
{
g_return_if_fail (cogl_vbo->vbo_name != 0);
GE (glBindBuffer (GL_ARRAY_BUFFER,
GPOINTER_TO_UINT (cogl_vbo->vbo_name)));
}
else if (cogl_vbo->vbo_name == NULL)
{
/* If the driver doesn't support VBOs then we simply allocate
* a client side fake vbo buffer. Unlike VBOs we can't allocate
* without specifying a size which is why we defer allocation
* until here. */
cogl_vbo->vbo_name = g_malloc (cogl_vbo->vbo_bytes);
}
if (cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_STRIDED) if (cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_STRIDED)
{ {
const void *pointer = const void *pointer =
prep_strided_vbo_for_upload (cogl_vbo); prep_strided_vbo_for_upload (cogl_vbo);
GE (glBufferData (GL_ARRAY_BUFFER, if (!fallback)
cogl_vbo->vbo_bytes, {
pointer, GE (glBufferData (GL_ARRAY_BUFFER,
usage)); cogl_vbo->vbo_bytes,
pointer,
usage));
}
else
memcpy (cogl_vbo->vbo_name, pointer, cogl_vbo->vbo_bytes);
} }
else if (cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_MULTIPACK) else if (cogl_vbo->flags & COGL_VERTEX_BUFFER_VBO_FLAG_MULTIPACK)
{ {
/* First we make it obvious to the driver that we want to update the /* 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 * whole buffer (without this, the driver is more likley to block
* if the GPU is busy using the buffer) */ * if the GPU is busy using the buffer) */
GE (glBufferData (GL_ARRAY_BUFFER, if (!fallback)
cogl_vbo->vbo_bytes, {
NULL, GE (glBufferData (GL_ARRAY_BUFFER,
usage)); cogl_vbo->vbo_bytes,
NULL,
usage));
}
/* I think it might depend on the specific driver/HW whether its better /* 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 * to use glMapBuffer here or glBufferSubData here. There is even a good
@ -1041,10 +1089,16 @@ upload_gl_vbo (CoglVertexBufferVBO *cogl_vbo)
else else
{ {
CoglVertexBufferAttrib *attribute = cogl_vbo->attributes->data; CoglVertexBufferAttrib *attribute = cogl_vbo->attributes->data;
GE (glBufferData (GL_ARRAY_BUFFER, if (!fallback)
cogl_vbo->vbo_bytes, {
attribute->u.pointer, GE (glBufferData (GL_ARRAY_BUFFER,
usage)); cogl_vbo->vbo_bytes,
attribute->u.pointer,
usage));
}
else
memcpy (cogl_vbo->vbo_name, attribute->u.pointer, cogl_vbo->vbo_bytes);
/* We forget this pointer now since the client will be free /* We forget this pointer now since the client will be free
* to re-use this memory */ * to re-use this memory */
attribute->u.pointer = NULL; attribute->u.pointer = NULL;
@ -1053,7 +1107,8 @@ upload_gl_vbo (CoglVertexBufferVBO *cogl_vbo)
cogl_vbo->flags |= COGL_VERTEX_BUFFER_VBO_FLAG_SUBMITTED; cogl_vbo->flags |= COGL_VERTEX_BUFFER_VBO_FLAG_SUBMITTED;
GE (glBindBuffer (GL_ARRAY_BUFFER, 0)); if (!fallback)
GE (glBindBuffer (GL_ARRAY_BUFFER, 0));
} }
/* Note: although there ends up being quite a few inner loops involved with /* Note: although there ends up being quite a few inner loops involved with
@ -1112,7 +1167,11 @@ cogl_vertex_buffer_vbo_resolve (CoglVertexBuffer *buffer,
if (!found_target_vbo) if (!found_target_vbo)
{ {
GE (glGenBuffers (1, &new_cogl_vbo->vbo_name)); if (cogl_get_features () & COGL_FEATURE_VBOS)
GE (glGenBuffers (1, (GLuint *)&new_cogl_vbo->vbo_name));
else
new_cogl_vbo->vbo_name = NULL;
/* this will be allocated at upload time */
upload_gl_vbo (new_cogl_vbo); upload_gl_vbo (new_cogl_vbo);
*final_vbos = g_list_prepend (*final_vbos, new_cogl_vbo); *final_vbos = g_list_prepend (*final_vbos, new_cogl_vbo);
@ -1449,8 +1508,25 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
{ {
CoglVertexBufferVBO *cogl_vbo = tmp->data; CoglVertexBufferVBO *cogl_vbo = tmp->data;
GList *tmp2; GList *tmp2;
char *base;
const GLvoid *pointer;
GE (glBindBuffer (GL_ARRAY_BUFFER, cogl_vbo->vbo_name)); if (cogl_get_features () & COGL_FEATURE_VBOS)
{
GE (glBindBuffer (GL_ARRAY_BUFFER,
GPOINTER_TO_UINT (cogl_vbo->vbo_name)));
base = 0;
}
else
base = cogl_vbo->vbo_name;
/* When GL VBOs are bing used then the "pointer" we pass to
* glColorPointer glVertexAttribPointer etc is actually an offset into
* the currently bound VBO.
*
* If we don't have VBO support though, then we must point into
* our fake client side VBO.
*/
for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next) for (tmp2 = cogl_vbo->attributes; tmp2 != NULL; tmp2 = tmp2->next)
{ {
@ -1467,26 +1543,29 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_COLOR_ARRAY: case COGL_VERTEX_BUFFER_ATTRIB_FLAG_COLOR_ARRAY:
enable_flags |= COGL_ENABLE_COLOR_ARRAY | COGL_ENABLE_BLEND; enable_flags |= COGL_ENABLE_COLOR_ARRAY | COGL_ENABLE_BLEND;
/* GE (glEnableClientState (GL_COLOR_ARRAY)); */ /* GE (glEnableClientState (GL_COLOR_ARRAY)); */
pointer = (const GLvoid *)(base + attribute->u.vbo_offset);
GE (glColorPointer (attribute->n_components, GE (glColorPointer (attribute->n_components,
gl_type, gl_type,
attribute->stride, attribute->stride,
(const GLvoid *)attribute->u.vbo_offset)); pointer));
break; break;
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMAL_ARRAY: case COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMAL_ARRAY:
/* FIXME: go through cogl cache to enable normal array */ /* FIXME: go through cogl cache to enable normal array */
GE (glEnableClientState (GL_NORMAL_ARRAY)); GE (glEnableClientState (GL_NORMAL_ARRAY));
pointer = (const GLvoid *)(base + attribute->u.vbo_offset);
GE (glNormalPointer (gl_type, GE (glNormalPointer (gl_type,
attribute->stride, attribute->stride,
(const GLvoid *)attribute->u.vbo_offset)); pointer));
break; break;
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_TEXTURE_COORD_ARRAY: case COGL_VERTEX_BUFFER_ATTRIB_FLAG_TEXTURE_COORD_ARRAY:
GE (glClientActiveTexture (GL_TEXTURE0 + GE (glClientActiveTexture (GL_TEXTURE0 +
attribute->texture_unit)); attribute->texture_unit));
GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY)); GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY));
pointer = (const GLvoid *)(base + attribute->u.vbo_offset);
GE (glTexCoordPointer (attribute->n_components, GE (glTexCoordPointer (attribute->n_components,
gl_type, gl_type,
attribute->stride, attribute->stride,
(const GLvoid *)attribute->u.vbo_offset)); pointer));
if (attribute->texture_unit > max_texcoord_attrib_unit) if (attribute->texture_unit > max_texcoord_attrib_unit)
max_texcoord_attrib_unit = attribute->texture_unit; max_texcoord_attrib_unit = attribute->texture_unit;
disable_mask &= ~(1 << attribute->texture_unit); disable_mask &= ~(1 << attribute->texture_unit);
@ -1494,10 +1573,11 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_VERTEX_ARRAY: case COGL_VERTEX_BUFFER_ATTRIB_FLAG_VERTEX_ARRAY:
enable_flags |= COGL_ENABLE_VERTEX_ARRAY; enable_flags |= COGL_ENABLE_VERTEX_ARRAY;
/* GE (glEnableClientState (GL_VERTEX_ARRAY)); */ /* GE (glEnableClientState (GL_VERTEX_ARRAY)); */
pointer = (const GLvoid *)(base + attribute->u.vbo_offset);
GE (glVertexPointer (attribute->n_components, GE (glVertexPointer (attribute->n_components,
gl_type, gl_type,
attribute->stride, attribute->stride,
(const GLvoid *)attribute->u.vbo_offset)); pointer));
break; break;
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_CUSTOM_ARRAY: case COGL_VERTEX_BUFFER_ATTRIB_FLAG_CUSTOM_ARRAY:
{ {
@ -1508,13 +1588,13 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
normalized = GL_TRUE; normalized = GL_TRUE;
/* FIXME: go through cogl cache to enable generic array */ /* FIXME: go through cogl cache to enable generic array */
GE (glEnableVertexAttribArray (generic_index++)); GE (glEnableVertexAttribArray (generic_index++));
pointer = (const GLvoid *)(base + attribute->u.vbo_offset);
GE (glVertexAttribPointer (generic_index, GE (glVertexAttribPointer (generic_index,
attribute->n_components, attribute->n_components,
gl_type, gl_type,
normalized, normalized,
attribute->stride, attribute->stride,
(const GLvoid *) pointer));
attribute->u.vbo_offset));
#endif #endif
} }
break; break;
@ -1583,7 +1663,8 @@ disable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
/* Disable all the client state that cogl doesn't currently know /* Disable all the client state that cogl doesn't currently know
* about: * about:
*/ */
GE (glBindBuffer (GL_ARRAY_BUFFER, 0)); if (cogl_get_features () & COGL_FEATURE_VBOS)
GE (glBindBuffer (GL_ARRAY_BUFFER, 0));
for (tmp = buffer->submitted_vbos; tmp != NULL; tmp = tmp->next) for (tmp = buffer->submitted_vbos; tmp != NULL; tmp = tmp->next)
{ {

View File

@ -65,6 +65,8 @@ _cogl_features_init (void)
flags |= COGL_FEATURE_SHADERS_GLSL | COGL_FEATURE_OFFSCREEN; flags |= COGL_FEATURE_SHADERS_GLSL | COGL_FEATURE_OFFSCREEN;
#endif #endif
flags |= COGL_FEATURE_VBOS;
/* Cache features */ /* Cache features */
ctx->feature_flags = flags; ctx->feature_flags = flags;
ctx->features_cached = TRUE; ctx->features_cached = TRUE;