mesa: modified _mesa_align_free() to accept NULL pointer
[mesa.git] / src / mesa / vbo / vbo_exec_api.c
index 9b2d59f9e4ce9fad0ec7c068be9c0537e52607b8..6feda1657da05fa1f92e94eaaaa653ae006ce8d3 100644 (file)
@@ -40,10 +40,12 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include "main/state.h"
 #include "main/light.h"
 #include "main/api_arrayelt.h"
-#include "main/api_noop.h"
+#include "main/api_validate.h"
 #include "main/dispatch.h"
 
 #include "vbo_context.h"
+#include "vbo_noop.h"
+
 
 #ifdef ERROR
 #undef ERROR
@@ -72,7 +74,7 @@ static void vbo_exec_wrap_buffers( struct vbo_exec_context *exec )
       GLuint last_begin = exec->vtx.prim[exec->vtx.prim_count-1].begin;
       GLuint last_count;
 
-      if (exec->ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) {
+      if (_mesa_inside_begin_end(exec->ctx)) {
         GLint i = exec->vtx.prim_count - 1;
         assert(i >= 0);
         exec->vtx.prim[i].count = (exec->vtx.vert_count - 
@@ -94,7 +96,7 @@ static void vbo_exec_wrap_buffers( struct vbo_exec_context *exec )
        */
       assert(exec->vtx.prim_count == 0);
 
-      if (exec->ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) {
+      if (_mesa_inside_begin_end(exec->ctx)) {
         exec->vtx.prim[0].mode = exec->ctx->Driver.CurrentExecPrimitive;
         exec->vtx.prim[0].start = 0;
         exec->vtx.prim[0].count = 0;
@@ -121,6 +123,11 @@ void vbo_exec_vtx_wrap( struct vbo_exec_context *exec )
     */
    vbo_exec_wrap_buffers( exec );
    
+   if (!exec->vtx.buffer_ptr) {
+      /* probably ran out of memory earlier when allocating the VBO */
+      return;
+   }
+
    /* Copy stored stored vertices to start of new list. 
     */
    assert(exec->vtx.max_vert - exec->vtx.vert_count > exec->vtx.copied.nr);
@@ -154,12 +161,13 @@ static void vbo_exec_copy_to_current( struct vbo_exec_context *exec )
         GLfloat *current = (GLfloat *)vbo->currval[i].Ptr;
          GLfloat tmp[4];
 
-         COPY_CLEAN_4V(tmp, 
-                       exec->vtx.attrsz[i], 
-                       exec->vtx.attrptr[i]);
+         COPY_CLEAN_4V_TYPE_AS_FLOAT(tmp,
+                                     exec->vtx.attrsz[i],
+                                     exec->vtx.attrptr[i],
+                                     exec->vtx.attrtype[i]);
          
-         if (memcmp(current, tmp, sizeof(tmp)) != 0)
-         { 
+         if (exec->vtx.attrtype[i] != vbo->currval[i].Type ||
+             memcmp(current, tmp, sizeof(tmp)) != 0) {
             memcpy(current, tmp, sizeof(tmp));
         
             /* Given that we explicitly state size here, there is no need
@@ -168,6 +176,10 @@ static void vbo_exec_copy_to_current( struct vbo_exec_context *exec )
              * directly.
              */
             vbo->currval[i].Size = exec->vtx.attrsz[i];
+            vbo->currval[i]._ElementSize = vbo->currval[i].Size * sizeof(GLfloat);
+            vbo->currval[i].Type = exec->vtx.attrtype[i];
+            vbo->currval[i].Integer =
+                  vbo_attrtype_to_integer_flag(exec->vtx.attrtype[i]);
 
             /* This triggers rather too much recalculation of Mesa state
              * that doesn't get used (eg light positions).
@@ -191,14 +203,18 @@ static void vbo_exec_copy_to_current( struct vbo_exec_context *exec )
 }
 
 
-static void vbo_exec_copy_from_current( struct vbo_exec_context *exec )
+/**
+ * Copy current vertex attribute values into the current vertex.
+ */
+static void
+vbo_exec_copy_from_current(struct vbo_exec_context *exec)
 {
    struct gl_context *ctx = exec->ctx;
    struct vbo_context *vbo = vbo_context(ctx);
    GLint i;
 
-   for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) {
-      const GLfloat *current = (GLfloat *)vbo->currval[i].Ptr;
+   for (i = VBO_ATTRIB_POS + 1; i < VBO_ATTRIB_MAX; i++) {
+      const GLfloat *current = (GLfloat *) vbo->currval[i].Ptr;
       switch (exec->vtx.attrsz[i]) {
       case 4: exec->vtx.attrptr[i][3] = current[3];
       case 3: exec->vtx.attrptr[i][2] = current[2];
@@ -212,17 +228,21 @@ static void vbo_exec_copy_from_current( struct vbo_exec_context *exec )
 
 /**
  * Flush existing data, set new attrib size, replay copied vertices.
+ * This is called when we transition from a small vertex attribute size
+ * to a larger one.  Ex: glTexCoord2f -> glTexCoord4f.
+ * We need to go back over the previous 2-component texcoords and insert
+ * zero and one values.
  */ 
-static void vbo_exec_wrap_upgrade_vertex( struct vbo_exec_context *exec,
-                                         GLuint attr,
-                                         GLuint newsz )
+static void
+vbo_exec_wrap_upgrade_vertex(struct vbo_exec_context *exec,
+                             GLuint attr, GLuint newSize )
 {
    struct gl_context *ctx = exec->ctx;
    struct vbo_context *vbo = vbo_context(ctx);
-   GLint lastcount = exec->vtx.vert_count;
+   const GLint lastcount = exec->vtx.vert_count;
    GLfloat *old_attrptr[VBO_ATTRIB_MAX];
-   GLuint old_vtx_size = exec->vtx.vertex_size;
-   GLuint oldsz = exec->vtx.attrsz[attr];
+   const GLuint old_vtx_size = exec->vtx.vertex_size; /* floats per vertex */
+   const GLuint oldSize = exec->vtx.attrsz[attr];
    GLuint i;
 
    /* Run pipeline on current vertices, copy wrapped vertices
@@ -238,7 +258,7 @@ static void vbo_exec_wrap_upgrade_vertex( struct vbo_exec_context *exec,
       memcpy(old_attrptr, exec->vtx.attrptr, sizeof(old_attrptr));
    }
 
-   if (unlikely(oldsz)) {
+   if (unlikely(oldSize)) {
       /* Do a COPY_TO_CURRENT to ensure back-copying works for the
        * case when the attribute already exists in the vertex and is
        * having its size increased.
@@ -249,22 +269,22 @@ static void vbo_exec_wrap_upgrade_vertex( struct vbo_exec_context *exec,
    /* Heuristic: Attempt to isolate attributes received outside
     * begin/end so that they don't bloat the vertices.
     */
-   if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END &&
-       !oldsz && lastcount > 8 && exec->vtx.vertex_size) {
+   if (!_mesa_inside_begin_end(ctx) &&
+       !oldSize && lastcount > 8 && exec->vtx.vertex_size) {
       vbo_exec_copy_to_current( exec );
       reset_attrfv( exec );
    }
 
    /* Fix up sizes:
     */
-   exec->vtx.attrsz[attr] = newsz;
-   exec->vtx.vertex_size += newsz - oldsz;
+   exec->vtx.attrsz[attr] = newSize;
+   exec->vtx.vertex_size += newSize - oldSize;
    exec->vtx.max_vert = ((VBO_VERT_BUFFER_SIZE - exec->vtx.buffer_used) / 
                          (exec->vtx.vertex_size * sizeof(GLfloat)));
    exec->vtx.vert_count = 0;
    exec->vtx.buffer_ptr = exec->vtx.buffer_map;
 
-   if (unlikely(oldsz)) {
+   if (unlikely(oldSize)) {
       /* Size changed, recalculate all the attrptr[] values
        */
       GLfloat *tmp = exec->vtx.vertex;
@@ -282,11 +302,11 @@ static void vbo_exec_wrap_upgrade_vertex( struct vbo_exec_context *exec,
        * values.
        */
       vbo_exec_copy_from_current( exec );
-
-   else {
+   }
+   else {
       /* Just have to append the new attribute at the end */
       exec->vtx.attrptr[attr] = exec->vtx.vertex +
-        exec->vtx.vertex_size - newsz;
+        exec->vtx.vertex_size - newSize;
    }
 
    /* Replay stored vertices to translate them
@@ -310,10 +330,12 @@ static void vbo_exec_wrap_upgrade_vertex( struct vbo_exec_context *exec,
               GLint new_offset = exec->vtx.attrptr[j] - exec->vtx.vertex;
 
               if (j == attr) {
-                 if (oldsz) {
+                 if (oldSize) {
                     GLfloat tmp[4];
-                    COPY_CLEAN_4V(tmp, oldsz, data + old_offset);
-                    COPY_SZ_4V(dest + new_offset, newsz, tmp);
+                     COPY_CLEAN_4V_TYPE_AS_FLOAT(tmp, oldSize,
+                                                 data + old_offset,
+                                                 exec->vtx.attrtype[j]);
+                    COPY_SZ_4V(dest + new_offset, newSize, tmp);
                  } else {
                     GLfloat *current = (GLfloat *)vbo->currval[j].Ptr;
                     COPY_SZ_4V(dest + new_offset, sz, current);
@@ -336,85 +358,209 @@ static void vbo_exec_wrap_upgrade_vertex( struct vbo_exec_context *exec,
 }
 
 
-static void vbo_exec_fixup_vertex( struct gl_context *ctx,
-                                  GLuint attr, GLuint sz )
+/**
+ * This is when a vertex attribute transitions to a different size.
+ * For example, we saw a bunch of glTexCoord2f() calls and now we got a
+ * glTexCoord4f() call.  We promote the array from size=2 to size=4.
+ */
+static void
+vbo_exec_fixup_vertex(struct gl_context *ctx, GLuint attr, GLuint newSize)
 {
    struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
-   int i;
 
-   if (sz > exec->vtx.attrsz[attr]) {
+   if (newSize > exec->vtx.attrsz[attr]) {
       /* New size is larger.  Need to flush existing vertices and get
        * an enlarged vertex format.
        */
-      vbo_exec_wrap_upgrade_vertex( exec, attr, sz );
+      vbo_exec_wrap_upgrade_vertex( exec, attr, newSize );
    }
-   else if (sz < exec->vtx.active_sz[attr]) {
-      static const GLfloat id[4] = { 0, 0, 0, 1 };
+   else if (newSize < exec->vtx.active_sz[attr]) {
+      GLuint i;
+      const GLfloat *id =
+            vbo_get_default_vals_as_float(exec->vtx.attrtype[attr]);
 
       /* New size is smaller - just need to fill in some
        * zeros.  Don't need to flush or wrap.
        */
-      for (i = sz ; i <= exec->vtx.attrsz[attr] ; i++)
+      for (i = newSize; i <= exec->vtx.attrsz[attr]; i++)
         exec->vtx.attrptr[attr][i-1] = id[i-1];
    }
 
-   exec->vtx.active_sz[attr] = sz;
+   exec->vtx.active_sz[attr] = newSize;
 
    /* Does setting NeedFlush belong here?  Necessitates resetting
     * vtxfmt on each flush (otherwise flags won't get reset
     * afterwards).
     */
    if (attr == 0) 
-      exec->ctx->Driver.NeedFlush |= FLUSH_STORED_VERTICES;
+      ctx->Driver.NeedFlush |= FLUSH_STORED_VERTICES;
 }
 
 
-#if FEATURE_beginend
-
-/* 
+/**
+ * This macro is used to implement all the glVertex, glColor, glTexCoord,
+ * glVertexAttrib, etc functions.
  */
-#define ATTR( A, N, V0, V1, V2, V3 )                           \
-do {                                                           \
-   struct vbo_exec_context *exec = &vbo_context(ctx)->exec;    \
+#define ATTR( A, N, T, V0, V1, V2, V3 )                                        \
+do {                                                                   \
+   struct vbo_exec_context *exec = &vbo_context(ctx)->exec;            \
                                                                        \
-   if (unlikely(!(exec->ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT))) \
-      ctx->Driver.BeginVertices( ctx );                                 \
+   if (unlikely(!(ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT)))      \
+      ctx->Driver.BeginVertices( ctx );                                        \
+                                                                       \
    if (unlikely(exec->vtx.active_sz[A] != N))                          \
       vbo_exec_fixup_vertex(ctx, A, N);                                        \
                                                                        \
-   {                                                           \
-      GLfloat *dest = exec->vtx.attrptr[A];                    \
-      if (N>0) dest[0] = V0;                                   \
-      if (N>1) dest[1] = V1;                                   \
-      if (N>2) dest[2] = V2;                                   \
-      if (N>3) dest[3] = V3;                                   \
-   }                                                           \
-                                                               \
-   if ((A) == 0) {                                             \
-      GLuint i;                                                        \
-                                                               \
-      for (i = 0; i < exec->vtx.vertex_size; i++)              \
-        exec->vtx.buffer_ptr[i] = exec->vtx.vertex[i];         \
-                                                               \
+   {                                                                   \
+      GLfloat *dest = exec->vtx.attrptr[A];                            \
+      if (N>0) dest[0] = V0;                                           \
+      if (N>1) dest[1] = V1;                                           \
+      if (N>2) dest[2] = V2;                                           \
+      if (N>3) dest[3] = V3;                                           \
+      exec->vtx.attrtype[A] = T;                                        \
+   }                                                                   \
+                                                                       \
+   if ((A) == 0) {                                                     \
+      /* This is a glVertex call */                                    \
+      GLuint i;                                                                \
+                                                                       \
+      for (i = 0; i < exec->vtx.vertex_size; i++)                      \
+        exec->vtx.buffer_ptr[i] = exec->vtx.vertex[i];                 \
+                                                                       \
       exec->vtx.buffer_ptr += exec->vtx.vertex_size;                   \
-      exec->ctx->Driver.NeedFlush |= FLUSH_STORED_VERTICES;    \
-                                                               \
-      if (++exec->vtx.vert_count >= exec->vtx.max_vert)                \
-        vbo_exec_vtx_wrap( exec );                             \
-   }                                                           \
+                                                                       \
+      /* Set FLUSH_STORED_VERTICES to indicate that there's now */     \
+      /* something to draw (not just updating a color or texcoord).*/  \
+      ctx->Driver.NeedFlush |= FLUSH_STORED_VERTICES;                  \
+                                                                       \
+      if (++exec->vtx.vert_count >= exec->vtx.max_vert)                        \
+        vbo_exec_vtx_wrap( exec );                                     \
+   }                                                                   \
 } while (0)
 
 
-#define ERROR() _mesa_error( ctx, GL_INVALID_ENUM, __FUNCTION__ )
+#define ERROR(err) _mesa_error( ctx, err, __FUNCTION__ )
 #define TAG(x) vbo_##x
 
 #include "vbo_attrib_tmp.h"
 
 
 
+/**
+ * Execute a glMaterial call.  Note that if GL_COLOR_MATERIAL is enabled,
+ * this may be a (partial) no-op.
+ */
+static void GLAPIENTRY
+vbo_Materialfv(GLenum face, GLenum pname, const GLfloat *params)
+{
+   GLbitfield updateMats;
+   GET_CURRENT_CONTEXT(ctx);
 
+   /* This function should be a no-op when it tries to update material
+    * attributes which are currently tracking glColor via glColorMaterial.
+    * The updateMats var will be a mask of the MAT_BIT_FRONT/BACK_x bits
+    * indicating which material attributes can actually be updated below.
+    */
+   if (ctx->Light.ColorMaterialEnabled) {
+      updateMats = ~ctx->Light._ColorMaterialBitmask;
+   }
+   else {
+      /* GL_COLOR_MATERIAL is disabled so don't skip any material updates */
+      updateMats = ALL_MATERIAL_BITS;
+   }
+
+   if (ctx->API == API_OPENGL_COMPAT && face == GL_FRONT) {
+      updateMats &= FRONT_MATERIAL_BITS;
+   }
+   else if (ctx->API == API_OPENGL_COMPAT && face == GL_BACK) {
+      updateMats &= BACK_MATERIAL_BITS;
+   }
+   else if (face != GL_FRONT_AND_BACK) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glMaterial(invalid face)");
+      return;
+   }
+
+   switch (pname) {
+   case GL_EMISSION:
+      if (updateMats & MAT_BIT_FRONT_EMISSION)
+         MAT_ATTR(VBO_ATTRIB_MAT_FRONT_EMISSION, 4, params);
+      if (updateMats & MAT_BIT_BACK_EMISSION)
+         MAT_ATTR(VBO_ATTRIB_MAT_BACK_EMISSION, 4, params);
+      break;
+   case GL_AMBIENT:
+      if (updateMats & MAT_BIT_FRONT_AMBIENT)
+         MAT_ATTR(VBO_ATTRIB_MAT_FRONT_AMBIENT, 4, params);
+      if (updateMats & MAT_BIT_BACK_AMBIENT)
+         MAT_ATTR(VBO_ATTRIB_MAT_BACK_AMBIENT, 4, params);
+      break;
+   case GL_DIFFUSE:
+      if (updateMats & MAT_BIT_FRONT_DIFFUSE)
+         MAT_ATTR(VBO_ATTRIB_MAT_FRONT_DIFFUSE, 4, params);
+      if (updateMats & MAT_BIT_BACK_DIFFUSE)
+         MAT_ATTR(VBO_ATTRIB_MAT_BACK_DIFFUSE, 4, params);
+      break;
+   case GL_SPECULAR:
+      if (updateMats & MAT_BIT_FRONT_SPECULAR)
+         MAT_ATTR(VBO_ATTRIB_MAT_FRONT_SPECULAR, 4, params);
+      if (updateMats & MAT_BIT_BACK_SPECULAR)
+         MAT_ATTR(VBO_ATTRIB_MAT_BACK_SPECULAR, 4, params);
+      break;
+   case GL_SHININESS:
+      if (*params < 0 || *params > ctx->Const.MaxShininess) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "glMaterial(invalid shininess: %f out range [0, %f])",
+                    *params, ctx->Const.MaxShininess);
+         return;
+      }
+      if (updateMats & MAT_BIT_FRONT_SHININESS)
+         MAT_ATTR(VBO_ATTRIB_MAT_FRONT_SHININESS, 1, params);
+      if (updateMats & MAT_BIT_BACK_SHININESS)
+         MAT_ATTR(VBO_ATTRIB_MAT_BACK_SHININESS, 1, params);
+      break;
+   case GL_COLOR_INDEXES:
+      if (ctx->API != API_OPENGL_COMPAT) {
+         _mesa_error(ctx, GL_INVALID_ENUM, "glMaterialfv(pname)");
+         return;
+      }
+      if (updateMats & MAT_BIT_FRONT_INDEXES)
+         MAT_ATTR(VBO_ATTRIB_MAT_FRONT_INDEXES, 3, params);
+      if (updateMats & MAT_BIT_BACK_INDEXES)
+         MAT_ATTR(VBO_ATTRIB_MAT_BACK_INDEXES, 3, params);
+      break;
+   case GL_AMBIENT_AND_DIFFUSE:
+      if (updateMats & MAT_BIT_FRONT_AMBIENT)
+         MAT_ATTR(VBO_ATTRIB_MAT_FRONT_AMBIENT, 4, params);
+      if (updateMats & MAT_BIT_FRONT_DIFFUSE)
+         MAT_ATTR(VBO_ATTRIB_MAT_FRONT_DIFFUSE, 4, params);
+      if (updateMats & MAT_BIT_BACK_AMBIENT)
+         MAT_ATTR(VBO_ATTRIB_MAT_BACK_AMBIENT, 4, params);
+      if (updateMats & MAT_BIT_BACK_DIFFUSE)
+         MAT_ATTR(VBO_ATTRIB_MAT_BACK_DIFFUSE, 4, params);
+      break;
+   default:
+      _mesa_error(ctx, GL_INVALID_ENUM, "glMaterialfv(pname)");
+      return;
+   }
+}
+
+
+/**
+ * Flush (draw) vertices.
+ * \param  unmap - leave VBO unmapped after flushing?
+ */
+static void
+vbo_exec_FlushVertices_internal(struct vbo_exec_context *exec, GLboolean unmap)
+{
+   if (exec->vtx.vert_count || unmap) {
+      vbo_exec_vtx_flush( exec, unmap );
+   }
+
+   if (exec->vtx.vertex_size) {
+      vbo_exec_copy_to_current( exec );
+      reset_attrfv( exec );
+   }
+}
 
-#if FEATURE_evaluators
 
 static void GLAPIENTRY vbo_exec_EvalCoord1f( GLfloat u )
 {
@@ -507,12 +653,6 @@ static void GLAPIENTRY vbo_exec_EvalPoint2( GLint i, GLint j )
    vbo_exec_EvalCoord2f( u, v );
 }
 
-/* use noop eval mesh */
-#define vbo_exec_EvalMesh1 _mesa_noop_EvalMesh1
-#define vbo_exec_EvalMesh2 _mesa_noop_EvalMesh2
-
-#endif /* FEATURE_evaluators */
-
 
 /**
  * Called via glBegin.
@@ -520,44 +660,90 @@ static void GLAPIENTRY vbo_exec_EvalPoint2( GLint i, GLint j )
 static void GLAPIENTRY vbo_exec_Begin( GLenum mode )
 {
    GET_CURRENT_CONTEXT( ctx ); 
+   struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
+   int i;
 
-   if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END) {
-      struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
-      int i;
+   if (_mesa_inside_begin_end(ctx)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glBegin");
+      return;
+   }
 
-      if (ctx->NewState) {
-        _mesa_update_state( ctx );
+   if (!_mesa_valid_prim_mode(ctx, mode, "glBegin")) {
+      return;
+   }
 
-        CALL_Begin(ctx->Exec, (mode));
-        return;
-      }
+   vbo_draw_method(vbo_context(ctx), DRAW_BEGIN_END);
 
-      if (!_mesa_valid_to_render(ctx, "glBegin")) {
-         return;
-      }
+   if (ctx->NewState) {
+      _mesa_update_state( ctx );
 
-      /* Heuristic: attempt to isolate attributes occuring outside
-       * begin/end pairs.
-       */
-      if (exec->vtx.vertex_size && !exec->vtx.attrsz[0]) 
-        vbo_exec_FlushVertices_internal( ctx, GL_FALSE );
-
-      i = exec->vtx.prim_count++;
-      exec->vtx.prim[i].mode = mode;
-      exec->vtx.prim[i].begin = 1;
-      exec->vtx.prim[i].end = 0;
-      exec->vtx.prim[i].indexed = 0;
-      exec->vtx.prim[i].weak = 0;
-      exec->vtx.prim[i].pad = 0;
-      exec->vtx.prim[i].start = exec->vtx.vert_count;
-      exec->vtx.prim[i].count = 0;
-      exec->vtx.prim[i].num_instances = 1;
-
-      ctx->Driver.CurrentExecPrimitive = mode;
+      CALL_Begin(ctx->Exec, (mode));
+      return;
+   }
+
+   if (!_mesa_valid_to_render(ctx, "glBegin")) {
+      return;
+   }
+
+   /* Heuristic: attempt to isolate attributes occuring outside
+    * begin/end pairs.
+    */
+   if (exec->vtx.vertex_size && !exec->vtx.attrsz[0])
+      vbo_exec_FlushVertices_internal(exec, GL_FALSE);
+
+   i = exec->vtx.prim_count++;
+   exec->vtx.prim[i].mode = mode;
+   exec->vtx.prim[i].begin = 1;
+   exec->vtx.prim[i].end = 0;
+   exec->vtx.prim[i].indexed = 0;
+   exec->vtx.prim[i].weak = 0;
+   exec->vtx.prim[i].pad = 0;
+   exec->vtx.prim[i].start = exec->vtx.vert_count;
+   exec->vtx.prim[i].count = 0;
+   exec->vtx.prim[i].num_instances = 1;
+   exec->vtx.prim[i].base_instance = 0;
+   exec->vtx.prim[i].is_indirect = 0;
+
+   ctx->Driver.CurrentExecPrimitive = mode;
+
+   ctx->Exec = ctx->BeginEnd;
+   /* We may have been called from a display list, in which case we should
+    * leave dlist.c's dispatch table in place.
+    */
+   if (ctx->CurrentDispatch == ctx->OutsideBeginEnd) {
+      ctx->CurrentDispatch = ctx->BeginEnd;
+      _glapi_set_dispatch(ctx->CurrentDispatch);
+   } else {
+      assert(ctx->CurrentDispatch == ctx->Save);
+   }
+}
+
+
+/**
+ * Try to merge / concatenate the two most recent VBO primitives.
+ */
+static void
+try_vbo_merge(struct vbo_exec_context *exec)
+{
+   struct _mesa_prim *cur =  &exec->vtx.prim[exec->vtx.prim_count - 1];
+
+   assert(exec->vtx.prim_count >= 1);
+
+   vbo_try_prim_conversion(cur);
+
+   if (exec->vtx.prim_count >= 2) {
+      struct _mesa_prim *prev = &exec->vtx.prim[exec->vtx.prim_count - 2];
+      assert(prev == cur - 1);
+
+      if (vbo_can_merge_prims(prev, cur)) {
+         assert(cur->begin);
+         assert(cur->end);
+         assert(prev->begin);
+         assert(prev->end);
+         vbo_merge_prims(prev, cur);
+         exec->vtx.prim_count--;  /* drop the last primitive */
+      }
    }
-   else 
-      _mesa_error( ctx, GL_INVALID_OPERATION, "glBegin" );
-      
 }
 
 
@@ -567,22 +753,38 @@ static void GLAPIENTRY vbo_exec_Begin( GLenum mode )
 static void GLAPIENTRY vbo_exec_End( void )
 {
    GET_CURRENT_CONTEXT( ctx ); 
+   struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
+
+   if (!_mesa_inside_begin_end(ctx)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glEnd");
+      return;
+   }
+
+   ctx->Exec = ctx->OutsideBeginEnd;
+   if (ctx->CurrentDispatch == ctx->BeginEnd) {
+      ctx->CurrentDispatch = ctx->OutsideBeginEnd;
+      _glapi_set_dispatch(ctx->CurrentDispatch);
+   }
 
-   if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) {
-      struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
+   if (exec->vtx.prim_count > 0) {
+      /* close off current primitive */
       int idx = exec->vtx.vert_count;
       int i = exec->vtx.prim_count - 1;
 
-      exec->vtx.prim[i].end = 1; 
+      exec->vtx.prim[i].end = 1;
       exec->vtx.prim[i].count = idx - exec->vtx.prim[i].start;
 
-      ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END;
+      try_vbo_merge(exec);
+   }
 
-      if (exec->vtx.prim_count == VBO_MAX_PRIM)
-        vbo_exec_vtx_flush( exec, GL_FALSE );
+   ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END;
+
+   if (exec->vtx.prim_count == VBO_MAX_PRIM)
+      vbo_exec_vtx_flush( exec, GL_FALSE );
+
+   if (MESA_DEBUG_FLAGS & DEBUG_ALWAYS_FLUSH) {
+      _mesa_flush(ctx);
    }
-   else 
-      _mesa_error( ctx, GL_INVALID_OPERATION, "glEnd" );
 }
 
 
@@ -610,18 +812,24 @@ vbo_exec_PrimitiveRestartNV(void)
 
 static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec )
 {
+   struct gl_context *ctx = exec->ctx;
    GLvertexformat *vfmt = &exec->vtxfmt;
 
-   _MESA_INIT_ARRAYELT_VTXFMT(vfmt, _ae_);
+   vfmt->ArrayElement = _ae_ArrayElement;
 
    vfmt->Begin = vbo_exec_Begin;
    vfmt->End = vbo_exec_End;
    vfmt->PrimitiveRestartNV = vbo_exec_PrimitiveRestartNV;
 
-   _MESA_INIT_DLIST_VTXFMT(vfmt, _mesa_);
-   _MESA_INIT_EVAL_VTXFMT(vfmt, vbo_exec_);
+   vfmt->CallList = _mesa_CallList;
+   vfmt->CallLists = _mesa_CallLists;
 
-   vfmt->Rectf = _mesa_noop_Rectf;
+   vfmt->EvalCoord1f = vbo_exec_EvalCoord1f;
+   vfmt->EvalCoord1fv = vbo_exec_EvalCoord1fv;
+   vfmt->EvalCoord2f = vbo_exec_EvalCoord2f;
+   vfmt->EvalCoord2fv = vbo_exec_EvalCoord2fv;
+   vfmt->EvalPoint1 = vbo_exec_EvalPoint1;
+   vfmt->EvalPoint2 = vbo_exec_EvalPoint2;
 
    /* from attrib_tmp.h:
     */
@@ -658,15 +866,30 @@ static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec )
    vfmt->Vertex4f = vbo_Vertex4f;
    vfmt->Vertex4fv = vbo_Vertex4fv;
    
-   vfmt->VertexAttrib1fARB = vbo_VertexAttrib1fARB;
-   vfmt->VertexAttrib1fvARB = vbo_VertexAttrib1fvARB;
-   vfmt->VertexAttrib2fARB = vbo_VertexAttrib2fARB;
-   vfmt->VertexAttrib2fvARB = vbo_VertexAttrib2fvARB;
-   vfmt->VertexAttrib3fARB = vbo_VertexAttrib3fARB;
-   vfmt->VertexAttrib3fvARB = vbo_VertexAttrib3fvARB;
-   vfmt->VertexAttrib4fARB = vbo_VertexAttrib4fARB;
-   vfmt->VertexAttrib4fvARB = vbo_VertexAttrib4fvARB;
+   if (ctx->API == API_OPENGLES2) {
+      vfmt->VertexAttrib1fARB = _es_VertexAttrib1f;
+      vfmt->VertexAttrib1fvARB = _es_VertexAttrib1fv;
+      vfmt->VertexAttrib2fARB = _es_VertexAttrib2f;
+      vfmt->VertexAttrib2fvARB = _es_VertexAttrib2fv;
+      vfmt->VertexAttrib3fARB = _es_VertexAttrib3f;
+      vfmt->VertexAttrib3fvARB = _es_VertexAttrib3fv;
+      vfmt->VertexAttrib4fARB = _es_VertexAttrib4f;
+      vfmt->VertexAttrib4fvARB = _es_VertexAttrib4fv;
+   } else {
+      vfmt->VertexAttrib1fARB = vbo_VertexAttrib1fARB;
+      vfmt->VertexAttrib1fvARB = vbo_VertexAttrib1fvARB;
+      vfmt->VertexAttrib2fARB = vbo_VertexAttrib2fARB;
+      vfmt->VertexAttrib2fvARB = vbo_VertexAttrib2fvARB;
+      vfmt->VertexAttrib3fARB = vbo_VertexAttrib3fARB;
+      vfmt->VertexAttrib3fvARB = vbo_VertexAttrib3fvARB;
+      vfmt->VertexAttrib4fARB = vbo_VertexAttrib4fARB;
+      vfmt->VertexAttrib4fvARB = vbo_VertexAttrib4fvARB;
+   }
 
+   /* Note that VertexAttrib4fNV is used from dlist.c and api_arrayelt.c so
+    * they can have a single entrypoint for updating any of the legacy
+    * attribs.
+    */
    vfmt->VertexAttrib1fNV = vbo_VertexAttrib1fNV;
    vfmt->VertexAttrib1fvNV = vbo_VertexAttrib1fvNV;
    vfmt->VertexAttrib2fNV = vbo_VertexAttrib2fNV;
@@ -700,101 +923,54 @@ static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec )
    vfmt->Indexf = vbo_Indexf;
    vfmt->Indexfv = vbo_Indexfv;
 
+   /* ARB_vertex_type_2_10_10_10_rev */
+   vfmt->VertexP2ui = vbo_VertexP2ui;
+   vfmt->VertexP2uiv = vbo_VertexP2uiv;
+   vfmt->VertexP3ui = vbo_VertexP3ui;
+   vfmt->VertexP3uiv = vbo_VertexP3uiv;
+   vfmt->VertexP4ui = vbo_VertexP4ui;
+   vfmt->VertexP4uiv = vbo_VertexP4uiv;
+
+   vfmt->TexCoordP1ui = vbo_TexCoordP1ui;
+   vfmt->TexCoordP1uiv = vbo_TexCoordP1uiv;
+   vfmt->TexCoordP2ui = vbo_TexCoordP2ui;
+   vfmt->TexCoordP2uiv = vbo_TexCoordP2uiv;
+   vfmt->TexCoordP3ui = vbo_TexCoordP3ui;
+   vfmt->TexCoordP3uiv = vbo_TexCoordP3uiv;
+   vfmt->TexCoordP4ui = vbo_TexCoordP4ui;
+   vfmt->TexCoordP4uiv = vbo_TexCoordP4uiv;
+
+   vfmt->MultiTexCoordP1ui = vbo_MultiTexCoordP1ui;
+   vfmt->MultiTexCoordP1uiv = vbo_MultiTexCoordP1uiv;
+   vfmt->MultiTexCoordP2ui = vbo_MultiTexCoordP2ui;
+   vfmt->MultiTexCoordP2uiv = vbo_MultiTexCoordP2uiv;
+   vfmt->MultiTexCoordP3ui = vbo_MultiTexCoordP3ui;
+   vfmt->MultiTexCoordP3uiv = vbo_MultiTexCoordP3uiv;
+   vfmt->MultiTexCoordP4ui = vbo_MultiTexCoordP4ui;
+   vfmt->MultiTexCoordP4uiv = vbo_MultiTexCoordP4uiv;
+   
+   vfmt->NormalP3ui = vbo_NormalP3ui;
+   vfmt->NormalP3uiv = vbo_NormalP3uiv;
+
+   vfmt->ColorP3ui = vbo_ColorP3ui;
+   vfmt->ColorP3uiv = vbo_ColorP3uiv;
+   vfmt->ColorP4ui = vbo_ColorP4ui;
+   vfmt->ColorP4uiv = vbo_ColorP4uiv;
+
+   vfmt->SecondaryColorP3ui = vbo_SecondaryColorP3ui;
+   vfmt->SecondaryColorP3uiv = vbo_SecondaryColorP3uiv;
+
+   vfmt->VertexAttribP1ui = vbo_VertexAttribP1ui;
+   vfmt->VertexAttribP1uiv = vbo_VertexAttribP1uiv;
+   vfmt->VertexAttribP2ui = vbo_VertexAttribP2ui;
+   vfmt->VertexAttribP2uiv = vbo_VertexAttribP2uiv;
+   vfmt->VertexAttribP3ui = vbo_VertexAttribP3ui;
+   vfmt->VertexAttribP3uiv = vbo_VertexAttribP3uiv;
+   vfmt->VertexAttribP4ui = vbo_VertexAttribP4ui;
+   vfmt->VertexAttribP4uiv = vbo_VertexAttribP4uiv;
 }
 
 
-#else /* FEATURE_beginend */
-
-
-#define ATTR( A, N, V0, V1, V2, V3 )                           \
-do {                                                           \
-   struct vbo_exec_context *exec = &vbo_context(ctx)->exec;    \
-                                                               \
-   /* FLUSH_UPDATE_CURRENT needs to be set manually */         \
-   exec->ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT;                \
-                                                               \
-   if (exec->vtx.active_sz[A] != N)                            \
-      vbo_exec_fixup_vertex(ctx, A, N);                                \
-                                                               \
-   {                                                           \
-      GLfloat *dest = exec->vtx.attrptr[A];                    \
-      if (N>0) dest[0] = V0;                                   \
-      if (N>1) dest[1] = V1;                                   \
-      if (N>2) dest[2] = V2;                                   \
-      if (N>3) dest[3] = V3;                                   \
-   }                                                           \
-} while (0)
-
-#define ERROR() _mesa_error( ctx, GL_INVALID_ENUM, __FUNCTION__ )
-#define TAG(x) vbo_##x
-
-#include "vbo_attrib_tmp.h"
-
-static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec )
-{
-   /* silence warnings */
-   (void) vbo_Color3f;
-   (void) vbo_Color3fv;
-   (void) vbo_Color4f;
-   (void) vbo_Color4fv;
-   (void) vbo_FogCoordfEXT;
-   (void) vbo_FogCoordfvEXT;
-   (void) vbo_MultiTexCoord1f;
-   (void) vbo_MultiTexCoord1fv;
-   (void) vbo_MultiTexCoord2f;
-   (void) vbo_MultiTexCoord2fv;
-   (void) vbo_MultiTexCoord3f;
-   (void) vbo_MultiTexCoord3fv;
-   (void) vbo_MultiTexCoord4f;
-   (void) vbo_MultiTexCoord4fv;
-   (void) vbo_Normal3f;
-   (void) vbo_Normal3fv;
-   (void) vbo_SecondaryColor3fEXT;
-   (void) vbo_SecondaryColor3fvEXT;
-   (void) vbo_TexCoord1f;
-   (void) vbo_TexCoord1fv;
-   (void) vbo_TexCoord2f;
-   (void) vbo_TexCoord2fv;
-   (void) vbo_TexCoord3f;
-   (void) vbo_TexCoord3fv;
-   (void) vbo_TexCoord4f;
-   (void) vbo_TexCoord4fv;
-   (void) vbo_Vertex2f;
-   (void) vbo_Vertex2fv;
-   (void) vbo_Vertex3f;
-   (void) vbo_Vertex3fv;
-   (void) vbo_Vertex4f;
-   (void) vbo_Vertex4fv;
-
-   (void) vbo_VertexAttrib1fARB;
-   (void) vbo_VertexAttrib1fvARB;
-   (void) vbo_VertexAttrib2fARB;
-   (void) vbo_VertexAttrib2fvARB;
-   (void) vbo_VertexAttrib3fARB;
-   (void) vbo_VertexAttrib3fvARB;
-   (void) vbo_VertexAttrib4fARB;
-   (void) vbo_VertexAttrib4fvARB;
-
-   (void) vbo_VertexAttrib1fNV;
-   (void) vbo_VertexAttrib1fvNV;
-   (void) vbo_VertexAttrib2fNV;
-   (void) vbo_VertexAttrib2fvNV;
-   (void) vbo_VertexAttrib3fNV;
-   (void) vbo_VertexAttrib3fvNV;
-   (void) vbo_VertexAttrib4fNV;
-   (void) vbo_VertexAttrib4fvNV;
-
-   (void) vbo_Materialfv;
-
-   (void) vbo_EdgeFlag;
-   (void) vbo_Indexf;
-   (void) vbo_Indexfv;
-}
-
-
-#endif /* FEATURE_beginend */
-
-
 /**
  * Tell the VBO module to use a real OpenGL vertex buffer object to
  * store accumulated immediate-mode vertex data.
@@ -814,19 +990,33 @@ void vbo_use_buffer_objects(struct gl_context *ctx)
 
    /* Make sure this func is only used once */
    assert(exec->vtx.bufferobj == ctx->Shared->NullBufferObj);
-   if (exec->vtx.buffer_map) {
-      _mesa_align_free(exec->vtx.buffer_map);
-      exec->vtx.buffer_map = NULL;
-      exec->vtx.buffer_ptr = NULL;
-   }
+
+   _mesa_align_free(exec->vtx.buffer_map);
+   exec->vtx.buffer_map = NULL;
+   exec->vtx.buffer_ptr = NULL;
 
    /* Allocate a real buffer object now */
    _mesa_reference_buffer_object(ctx, &exec->vtx.bufferobj, NULL);
    exec->vtx.bufferobj = ctx->Driver.NewBufferObject(ctx, bufName, target);
-   ctx->Driver.BufferData(ctx, target, size, NULL, usage, exec->vtx.bufferobj);
+   if (!ctx->Driver.BufferData(ctx, target, size, NULL, usage, exec->vtx.bufferobj)) {
+      _mesa_error(ctx, GL_OUT_OF_MEMORY, "VBO allocation");
+   }
 }
 
 
+/**
+ * If this function is called, all VBO buffers will be unmapped when
+ * we flush.
+ * Otherwise, if a simple command like glColor3f() is called and we flush,
+ * the current VBO may be left mapped.
+ */
+void
+vbo_always_unmap_buffers(struct gl_context *ctx)
+{
+   struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
+   exec->begin_vertices_flags |= FLUSH_STORED_VERTICES;
+}
+
 
 void vbo_exec_vtx_init( struct vbo_exec_context *exec )
 {
@@ -843,18 +1033,17 @@ void vbo_exec_vtx_init( struct vbo_exec_context *exec )
                                  ctx->Shared->NullBufferObj);
 
    ASSERT(!exec->vtx.buffer_map);
-   exec->vtx.buffer_map = (GLfloat *)_mesa_align_malloc(VBO_VERT_BUFFER_SIZE, 64);
+   exec->vtx.buffer_map = _mesa_align_malloc(VBO_VERT_BUFFER_SIZE, 64);
    exec->vtx.buffer_ptr = exec->vtx.buffer_map;
 
    vbo_exec_vtxfmt_init( exec );
-
-   /* Hook our functions into the dispatch table.
-    */
-   _mesa_install_exec_vtxfmt( exec->ctx, &exec->vtxfmt );
+   _mesa_noop_vtxfmt_init(&exec->vtxfmt_noop);
 
    for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) {
       ASSERT(i < Elements(exec->vtx.attrsz));
       exec->vtx.attrsz[i] = 0;
+      ASSERT(i < Elements(exec->vtx.attrtype));
+      exec->vtx.attrtype[i] = GL_FLOAT;
       ASSERT(i < Elements(exec->vtx.active_sz));
       exec->vtx.active_sz[i] = 0;
    }
@@ -868,20 +1057,32 @@ void vbo_exec_vtx_init( struct vbo_exec_context *exec )
       struct gl_client_array *arrays = exec->vtx.arrays;
       unsigned i;
 
-      memcpy(arrays,      vbo->legacy_currval,  16 * sizeof(arrays[0]));
-      memcpy(arrays + 16, vbo->generic_currval, 16 * sizeof(arrays[0]));
+      memcpy(arrays, &vbo->currval[VBO_ATTRIB_POS],
+             VERT_ATTRIB_FF_MAX * sizeof(arrays[0]));
+      for (i = 0; i < VERT_ATTRIB_FF_MAX; ++i) {
+         struct gl_client_array *array;
+         array = &arrays[VERT_ATTRIB_FF(i)];
+         array->BufferObj = NULL;
+         _mesa_reference_buffer_object(ctx, &arrays->BufferObj,
+                                 vbo->currval[VBO_ATTRIB_POS+i].BufferObj);
+      }
+
+      memcpy(arrays + VERT_ATTRIB_GENERIC(0),
+             &vbo->currval[VBO_ATTRIB_GENERIC0],
+             VERT_ATTRIB_GENERIC_MAX * sizeof(arrays[0]));
 
-      for (i = 0; i < 16; ++i) {
-         arrays[i     ].BufferObj = NULL;
-         arrays[i + 16].BufferObj = NULL;
-         _mesa_reference_buffer_object(ctx, &arrays[i     ].BufferObj,
-                                       vbo->legacy_currval[i].BufferObj);
-         _mesa_reference_buffer_object(ctx, &arrays[i + 16].BufferObj,
-                                       vbo->generic_currval[i].BufferObj);
+      for (i = 0; i < VERT_ATTRIB_GENERIC_MAX; ++i) {
+         struct gl_client_array *array;
+         array = &arrays[VERT_ATTRIB_GENERIC(i)];
+         array->BufferObj = NULL;
+         _mesa_reference_buffer_object(ctx, &array->BufferObj,
+                           vbo->currval[VBO_ATTRIB_GENERIC0+i].BufferObj);
       }
    }
 
    exec->vtx.vertex_size = 0;
+
+   exec->begin_vertices_flags = FLUSH_UPDATE_CURRENT;
 }
 
 
@@ -914,37 +1115,30 @@ void vbo_exec_vtx_destroy( struct vbo_exec_context *exec )
    /* Free the vertex buffer.  Unmap first if needed.
     */
    if (_mesa_bufferobj_mapped(exec->vtx.bufferobj)) {
-      ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER, exec->vtx.bufferobj);
+      ctx->Driver.UnmapBuffer(ctx, exec->vtx.bufferobj);
    }
    _mesa_reference_buffer_object(ctx, &exec->vtx.bufferobj, NULL);
 }
 
+
+/**
+ * Called upon first glVertex, glColor, glTexCoord, etc.
+ */
 void vbo_exec_BeginVertices( struct gl_context *ctx )
 {
    struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
-   if (0) printf("%s\n", __FUNCTION__);
-   vbo_exec_vtx_map( exec );
 
-   assert((exec->ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT) == 0);
-   exec->ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT;
-}
-
-void vbo_exec_FlushVertices_internal( struct gl_context *ctx, GLboolean unmap )
-{
-   struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
+   vbo_exec_vtx_map( exec );
 
-   if (exec->vtx.vert_count || unmap) {
-      vbo_exec_vtx_flush( exec, unmap );
-   }
+   assert((ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT) == 0);
+   assert(exec->begin_vertices_flags);
 
-   if (exec->vtx.vertex_size) {
-      vbo_exec_copy_to_current( exec );
-      reset_attrfv( exec );
-   }
+   ctx->Driver.NeedFlush |= exec->begin_vertices_flags;
 }
 
 
 /**
+ * Called via ctx->Driver.FlushVertices()
  * \param flags  bitmask of FLUSH_STORED_VERTICES, FLUSH_UPDATE_CURRENT
  */
 void vbo_exec_FlushVertices( struct gl_context *ctx, GLuint flags )
@@ -957,10 +1151,8 @@ void vbo_exec_FlushVertices( struct gl_context *ctx, GLuint flags )
    assert(exec->flush_call_depth == 1);
 #endif
 
-   if (0) printf("%s\n", __FUNCTION__);
-
-   if (exec->ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) {
-      if (0) printf("%s - inside begin/end\n", __FUNCTION__);
+   if (_mesa_inside_begin_end(ctx)) {
+      /* We've had glBegin but not glEnd! */
 #ifdef DEBUG
       exec->flush_call_depth--;
       assert(exec->flush_call_depth == 0);
@@ -968,14 +1160,12 @@ void vbo_exec_FlushVertices( struct gl_context *ctx, GLuint flags )
       return;
    }
 
-   vbo_exec_FlushVertices_internal( ctx, GL_TRUE );
+   /* Flush (draw), and make sure VBO is left unmapped when done */
+   vbo_exec_FlushVertices_internal(exec, GL_TRUE);
 
    /* Need to do this to ensure BeginVertices gets called again:
     */
-   if (exec->ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT)
-      exec->ctx->Driver.NeedFlush &= ~FLUSH_UPDATE_CURRENT;
-
-   exec->ctx->Driver.NeedFlush &= ~flags;
+   ctx->Driver.NeedFlush &= ~(FLUSH_UPDATE_CURRENT | flags);
 
 #ifdef DEBUG
    exec->flush_call_depth--;
@@ -990,6 +1180,7 @@ static void reset_attrfv( struct vbo_exec_context *exec )
 
    for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) {
       exec->vtx.attrsz[i] = 0;
+      exec->vtx.attrtype[i] = GL_FLOAT;
       exec->vtx.active_sz[i] = 0;
    }
 
@@ -998,35 +1189,35 @@ static void reset_attrfv( struct vbo_exec_context *exec )
       
 
 void GLAPIENTRY
-_vbo_Color4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
+_es_Color4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
 {
    vbo_Color4f(r, g, b, a);
 }
 
 
 void GLAPIENTRY
-_vbo_Normal3f(GLfloat x, GLfloat y, GLfloat z)
+_es_Normal3f(GLfloat x, GLfloat y, GLfloat z)
 {
    vbo_Normal3f(x, y, z);
 }
 
 
 void GLAPIENTRY
-_vbo_MultiTexCoord4f(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
+_es_MultiTexCoord4f(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
 {
    vbo_MultiTexCoord4f(target, s, t, r, q);
 }
 
 
 void GLAPIENTRY
-_vbo_Materialfv(GLenum face, GLenum pname, const GLfloat *params)
+_es_Materialfv(GLenum face, GLenum pname, const GLfloat *params)
 {
    vbo_Materialfv(face, pname, params);
 }
 
 
 void GLAPIENTRY
-_vbo_Materialf(GLenum face, GLenum pname, GLfloat param)
+_es_Materialf(GLenum face, GLenum pname, GLfloat param)
 {
    GLfloat p[4];
    p[0] = param;
@@ -1035,57 +1226,71 @@ _vbo_Materialf(GLenum face, GLenum pname, GLfloat param)
 }
 
 
+/**
+ * A special version of glVertexAttrib4f that does not treat index 0 as
+ * VBO_ATTRIB_POS.
+ */
+static void
+VertexAttrib4f_nopos(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   if (index < MAX_VERTEX_GENERIC_ATTRIBS)
+      ATTR(VBO_ATTRIB_GENERIC0 + index, 4, GL_FLOAT, x, y, z, w);
+   else
+      ERROR(GL_INVALID_VALUE);
+}
+
 void GLAPIENTRY
-_vbo_VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+_es_VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
 {
-   vbo_VertexAttrib4fARB(index, x, y, z, w);
+   VertexAttrib4f_nopos(index, x, y, z, w);
 }
 
 
 void GLAPIENTRY
-_vbo_VertexAttrib1f(GLuint indx, GLfloat x)
+_es_VertexAttrib1f(GLuint indx, GLfloat x)
 {
-   vbo_VertexAttrib1fARB(indx, x);
+   VertexAttrib4f_nopos(indx, x, 0.0f, 0.0f, 1.0f);
 }
 
 
 void GLAPIENTRY
-_vbo_VertexAttrib1fv(GLuint indx, const GLfloat* values)
+_es_VertexAttrib1fv(GLuint indx, const GLfloat* values)
 {
-   vbo_VertexAttrib1fvARB(indx, values);
+   VertexAttrib4f_nopos(indx, values[0], 0.0f, 0.0f, 1.0f);
 }
 
 
 void GLAPIENTRY
-_vbo_VertexAttrib2f(GLuint indx, GLfloat x, GLfloat y)
+_es_VertexAttrib2f(GLuint indx, GLfloat x, GLfloat y)
 {
-   vbo_VertexAttrib2fARB(indx, x, y);
+   VertexAttrib4f_nopos(indx, x, y, 0.0f, 1.0f);
 }
 
 
 void GLAPIENTRY
-_vbo_VertexAttrib2fv(GLuint indx, const GLfloat* values)
+_es_VertexAttrib2fv(GLuint indx, const GLfloat* values)
 {
-   vbo_VertexAttrib2fvARB(indx, values);
+   VertexAttrib4f_nopos(indx, values[0], values[1], 0.0f, 1.0f);
 }
 
 
 void GLAPIENTRY
-_vbo_VertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z)
+_es_VertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z)
 {
-   vbo_VertexAttrib3fARB(indx, x, y, z);
+   VertexAttrib4f_nopos(indx, x, y, z, 1.0f);
 }
 
 
 void GLAPIENTRY
-_vbo_VertexAttrib3fv(GLuint indx, const GLfloat* values)
+_es_VertexAttrib3fv(GLuint indx, const GLfloat* values)
 {
-   vbo_VertexAttrib3fvARB(indx, values);
+   VertexAttrib4f_nopos(indx, values[0], values[1], values[2], 1.0f);
 }
 
 
 void GLAPIENTRY
-_vbo_VertexAttrib4fv(GLuint indx, const GLfloat* values)
+_es_VertexAttrib4fv(GLuint indx, const GLfloat* values)
 {
-   vbo_VertexAttrib4fvARB(indx, values);
+   VertexAttrib4f_nopos(indx, values[0], values[1], values[2], values[3]);
 }