r200/i915/st/mesa/compiler: use common inputs read field
[mesa.git] / src / mesa / vbo / vbo_exec_api.c
index 2aaa2895ca6cb783e1e5f4578203e396b0b8652a..cbf78b49631700c9385487e9faaf5d7ba672805d 100644 (file)
@@ -1,6 +1,6 @@
 /**************************************************************************
 
-Copyright 2002-2008 Tungsten Graphics Inc., Cedar Park, Texas.
+Copyright 2002-2008 VMware, Inc.
 
 All Rights Reserved.
 
@@ -18,7 +18,7 @@ Software.
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
-TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
+VMWARE AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -27,14 +27,13 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 /*
  * Authors:
- *   Keith Whitwell <keith@tungstengraphics.com>
+ *   Keith Whitwell <keithw@vmware.com>
  */
 
 #include "main/glheader.h"
 #include "main/bufferobj.h"
 #include "main/context.h"
 #include "main/macros.h"
-#include "main/mfeatures.h"
 #include "main/vtxfmt.h"
 #include "main/dlist.h"
 #include "main/eval.h"
@@ -43,28 +42,27 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include "main/api_arrayelt.h"
 #include "main/api_validate.h"
 #include "main/dispatch.h"
+#include "util/bitscan.h"
 
 #include "vbo_context.h"
 #include "vbo_noop.h"
 
 
-#ifdef ERROR
-#undef ERROR
-#endif
-
-
 /** ID/name for immediate-mode VBO */
 #define IMM_BUFFER_NAME 0xaabbccdd
 
 
-static void reset_attrfv( struct vbo_exec_context *exec );
+static void
+vbo_reset_all_attr(struct vbo_exec_context *exec);
 
 
 /**
  * Close off the last primitive, execute the buffer, restart the
- * primitive.  
+ * primitive.  This is called when we fill a vertex buffer before
+ * hitting glEnd.
  */
-static void vbo_exec_wrap_buffers( struct vbo_exec_context *exec )
+static void
+vbo_exec_wrap_buffers(struct vbo_exec_context *exec)
 {
    if (exec->vtx.prim_count == 0) {
       exec->vtx.copied.nr = 0;
@@ -72,17 +70,31 @@ static void vbo_exec_wrap_buffers( struct vbo_exec_context *exec )
       exec->vtx.buffer_ptr = exec->vtx.buffer_map;
    }
    else {
-      GLuint last_begin = exec->vtx.prim[exec->vtx.prim_count-1].begin;
+      struct _mesa_prim *last_prim = &exec->vtx.prim[exec->vtx.prim_count - 1];
+      const GLuint last_begin = last_prim->begin;
       GLuint last_count;
 
-      if (exec->ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) {
-        GLint i = exec->vtx.prim_count - 1;
-        assert(i >= 0);
-        exec->vtx.prim[i].count = (exec->vtx.vert_count - 
-                                   exec->vtx.prim[i].start);
+      if (_mesa_inside_begin_end(exec->ctx)) {
+        last_prim->count = exec->vtx.vert_count - last_prim->start;
       }
 
-      last_count = exec->vtx.prim[exec->vtx.prim_count-1].count;
+      last_count = last_prim->count;
+
+      /* Special handling for wrapping GL_LINE_LOOP */
+      if (last_prim->mode == GL_LINE_LOOP &&
+          last_count > 0 &&
+          !last_prim->end) {
+         /* draw this section of the incomplete line loop as a line strip */
+         last_prim->mode = GL_LINE_STRIP;
+         if (!last_prim->begin) {
+            /* This is not the first section of the line loop, so don't
+             * draw the 0th vertex.  We're saving it until we draw the
+             * very last section of the loop.
+             */
+            last_prim->start++;
+            last_prim->count--;
+         }
+      }
 
       /* Execute the buffer and save copied vertices.
        */
@@ -97,12 +109,14 @@ 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].begin = 0;
+         exec->vtx.prim[0].end = 0;
         exec->vtx.prim[0].start = 0;
         exec->vtx.prim[0].count = 0;
         exec->vtx.prim_count++;
-      
+
         if (exec->vtx.copied.nr == last_count)
            exec->vtx.prim[0].begin = last_begin;
       }
@@ -114,27 +128,31 @@ static void vbo_exec_wrap_buffers( struct vbo_exec_context *exec )
  * Deal with buffer wrapping where provoked by the vertex buffer
  * filling up, as opposed to upgrade_vertex().
  */
-void vbo_exec_vtx_wrap( struct vbo_exec_context *exec )
+static void
+vbo_exec_vtx_wrap(struct vbo_exec_context *exec)
 {
-   GLfloat *data = exec->vtx.copied.buffer;
-   GLuint i;
+   unsigned numComponents;
 
    /* Run pipeline on current vertices, copy wrapped vertices
     * to exec->vtx.copied.
     */
    vbo_exec_wrap_buffers( exec );
-   
-   /* Copy stored stored vertices to start of new list. 
+
+   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);
 
-   for (i = 0 ; i < exec->vtx.copied.nr ; i++) {
-      memcpy( exec->vtx.buffer_ptr, data, 
-             exec->vtx.vertex_size * sizeof(GLfloat));
-      exec->vtx.buffer_ptr += exec->vtx.vertex_size;
-      data += exec->vtx.vertex_size;
-      exec->vtx.vert_count++;
-   }
+   numComponents = exec->vtx.copied.nr * exec->vtx.vertex_size;
+   memcpy(exec->vtx.buffer_ptr,
+          exec->vtx.copied.buffer,
+          numComponents * sizeof(fi_type));
+   exec->vtx.buffer_ptr += numComponents;
+   exec->vtx.vert_count += exec->vtx.copied.nr;
 
    exec->vtx.copied.nr = 0;
 }
@@ -143,49 +161,62 @@ void vbo_exec_vtx_wrap( struct vbo_exec_context *exec )
 /**
  * Copy the active vertex's values to the ctx->Current fields.
  */
-static void vbo_exec_copy_to_current( struct vbo_exec_context *exec )
+static void
+vbo_exec_copy_to_current(struct vbo_exec_context *exec)
 {
    struct gl_context *ctx = exec->ctx;
    struct vbo_context *vbo = vbo_context(ctx);
-   GLuint i;
+   GLbitfield64 enabled = exec->vtx.enabled & (~BITFIELD64_BIT(VBO_ATTRIB_POS));
 
-   for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) {
-      if (exec->vtx.attrsz[i]) {
-         /* Note: the exec->vtx.current[i] pointers point into the
-          * ctx->Current.Attrib and ctx->Light.Material.Attrib arrays.
-          */
-        GLfloat *current = (GLfloat *)vbo->currval[i].Ptr;
-         GLfloat tmp[4];
+   while (enabled) {
+      const int i = u_bit_scan64(&enabled);
+
+      /* Note: the exec->vtx.current[i] pointers point into the
+       * ctx->Current.Attrib and ctx->Light.Material.Attrib arrays.
+       */
+      GLfloat *current = (GLfloat *)vbo->currval[i].Ptr;
+      fi_type tmp[8]; /* space for doubles */
+      int dmul = exec->vtx.attrtype[i] == GL_DOUBLE ? 2 : 1;
+
+      assert(exec->vtx.attrsz[i]);
 
-         COPY_CLEAN_4V_TYPE_AS_FLOAT(tmp,
+      if (exec->vtx.attrtype[i] == GL_DOUBLE) {
+         memset(tmp, 0, sizeof(tmp));
+         memcpy(tmp, exec->vtx.attrptr[i], exec->vtx.attrsz[i] * sizeof(GLfloat));
+      } else {
+         COPY_CLEAN_4V_TYPE_AS_UNION(tmp,
                                      exec->vtx.attrsz[i],
                                      exec->vtx.attrptr[i],
                                      exec->vtx.attrtype[i]);
-         
-         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
-             * for the COPY_CLEAN above, could just copy 16 bytes and be
-             * done.  The only problem is when Mesa accesses ctx->Current
-             * 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).
-             */
-            if (i >= VBO_ATTRIB_MAT_FRONT_AMBIENT &&
-                i <= VBO_ATTRIB_MAT_BACK_INDEXES)
-               ctx->NewState |= _NEW_LIGHT;
-            
-            ctx->NewState |= _NEW_CURRENT_ATTRIB;
-         }
+      }
+
+      if (exec->vtx.attrtype[i] != vbo->currval[i].Type ||
+          memcmp(current, tmp, 4 * sizeof(GLfloat) * dmul) != 0) {
+         memcpy(current, tmp, 4 * sizeof(GLfloat) * dmul);
+
+         /* Given that we explicitly state size here, there is no need
+          * for the COPY_CLEAN above, could just copy 16 bytes and be
+          * done.  The only problem is when Mesa accesses ctx->Current
+          * directly.
+          */
+         /* Size here is in components - not bytes */
+         vbo->currval[i].Size = exec->vtx.attrsz[i] / dmul;
+         vbo->currval[i]._ElementSize =
+            vbo->currval[i].Size * sizeof(GLfloat) * dmul;
+         vbo->currval[i].Type = exec->vtx.attrtype[i];
+         vbo->currval[i].Integer =
+            vbo_attrtype_to_integer_flag(exec->vtx.attrtype[i]);
+         vbo->currval[i].Doubles =
+            vbo_attrtype_to_double_flag(exec->vtx.attrtype[i]);
+
+         /* This triggers rather too much recalculation of Mesa state
+          * that doesn't get used (eg light positions).
+          */
+         if (i >= VBO_ATTRIB_MAT_FRONT_AMBIENT &&
+             i <= VBO_ATTRIB_MAT_BACK_INDEXES)
+            ctx->NewState |= _NEW_LIGHT;
+
+         ctx->NewState |= _NEW_CURRENT_ATTRIB;
       }
    }
 
@@ -193,7 +224,7 @@ static void vbo_exec_copy_to_current( struct vbo_exec_context *exec )
     */
    if (ctx->Light.ColorMaterialEnabled &&
        exec->vtx.attrsz[VBO_ATTRIB_COLOR0]) {
-      _mesa_update_color_material(ctx, 
+      _mesa_update_color_material(ctx,
                                  ctx->Current.Attrib[VBO_ATTRIB_COLOR0]);
    }
 }
@@ -210,13 +241,18 @@ vbo_exec_copy_from_current(struct vbo_exec_context *exec)
    GLint i;
 
    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];
-      case 2: exec->vtx.attrptr[i][1] = current[1];
-      case 1: exec->vtx.attrptr[i][0] = current[0];
-        break;
+      if (exec->vtx.attrtype[i] == GL_DOUBLE) {
+         memcpy(exec->vtx.attrptr[i], vbo->currval[i].Ptr,
+                exec->vtx.attrsz[i] * sizeof(GLfloat));
+      } else {
+         const fi_type *current = (fi_type *) 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];
+         case 2: exec->vtx.attrptr[i][1] = current[1];
+         case 1: exec->vtx.attrptr[i][0] = current[0];
+            break;
+         }
       }
    }
 }
@@ -228,7 +264,7 @@ vbo_exec_copy_from_current(struct vbo_exec_context *exec)
  * 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 newSize )
@@ -236,7 +272,7 @@ vbo_exec_wrap_upgrade_vertex(struct vbo_exec_context *exec,
    struct gl_context *ctx = exec->ctx;
    struct vbo_context *vbo = vbo_context(ctx);
    const GLint lastcount = exec->vtx.vert_count;
-   GLfloat *old_attrptr[VBO_ATTRIB_MAX];
+   fi_type *old_attrptr[VBO_ATTRIB_MAX];
    const GLuint old_vtx_size = exec->vtx.vertex_size; /* floats per vertex */
    const GLuint oldSize = exec->vtx.attrsz[attr];
    GLuint i;
@@ -265,25 +301,25 @@ 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 &&
+   if (!_mesa_inside_begin_end(ctx) &&
        !oldSize && lastcount > 8 && exec->vtx.vertex_size) {
       vbo_exec_copy_to_current( exec );
-      reset_attrfv( exec );
+      vbo_reset_all_attr(exec);
    }
 
    /* Fix up sizes:
     */
    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.max_vert = vbo_compute_max_verts(exec);
    exec->vtx.vert_count = 0;
    exec->vtx.buffer_ptr = exec->vtx.buffer_map;
+   exec->vtx.enabled |= BITFIELD64_BIT(attr);
 
    if (unlikely(oldSize)) {
       /* Size changed, recalculate all the attrptr[] values
        */
-      GLfloat *tmp = exec->vtx.vertex;
+      fi_type *tmp = exec->vtx.vertex;
 
       for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) {
         if (exec->vtx.attrsz[i]) {
@@ -302,7 +338,7 @@ vbo_exec_wrap_upgrade_vertex(struct vbo_exec_context *exec,
    else {
       /* Just have to append the new attribute at the end */
       exec->vtx.attrptr[attr] = exec->vtx.vertex +
-        exec->vtx.vertex_size - newSize;
+        exec->vtx.vertex_size - newSize;
    }
 
    /* Replay stored vertices to translate them
@@ -311,36 +347,36 @@ vbo_exec_wrap_upgrade_vertex(struct vbo_exec_context *exec,
     * -- No need to replay - just copy piecewise
     */
    if (unlikely(exec->vtx.copied.nr)) {
-      GLfloat *data = exec->vtx.copied.buffer;
-      GLfloat *dest = exec->vtx.buffer_ptr;
-      GLuint j;
+      fi_type *data = exec->vtx.copied.buffer;
+      fi_type *dest = exec->vtx.buffer_ptr;
 
       assert(exec->vtx.buffer_ptr == exec->vtx.buffer_map);
 
       for (i = 0 ; i < exec->vtx.copied.nr ; i++) {
-        for (j = 0 ; j < VBO_ATTRIB_MAX ; j++) {
+         GLbitfield64 enabled = exec->vtx.enabled;
+         while (enabled) {
+            const int j = u_bit_scan64(&enabled);
            GLuint sz = exec->vtx.attrsz[j];
-
-           if (sz) {
-              GLint old_offset = old_attrptr[j] - exec->vtx.vertex;
-              GLint new_offset = exec->vtx.attrptr[j] - exec->vtx.vertex;
-
-              if (j == attr) {
-                 if (oldSize) {
-                    GLfloat tmp[4];
-                     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);
-                 }
-              }
-              else {
-                 COPY_SZ_4V(dest + new_offset, sz, data + old_offset);
-              }
-           }
+            GLint old_offset = old_attrptr[j] - exec->vtx.vertex;
+            GLint new_offset = exec->vtx.attrptr[j] - exec->vtx.vertex;
+
+            assert(sz);
+
+            if (j == attr) {
+               if (oldSize) {
+                  fi_type tmp[4];
+                  COPY_CLEAN_4V_TYPE_AS_UNION(tmp, oldSize,
+                                              data + old_offset,
+                                              exec->vtx.attrtype[j]);
+                  COPY_SZ_4V(dest + new_offset, newSize, tmp);
+               } else {
+                  fi_type *current = (fi_type *)vbo->currval[j].Ptr;
+                  COPY_SZ_4V(dest + new_offset, sz, current);
+               }
+            }
+            else {
+               COPY_SZ_4V(dest + new_offset, sz, data + old_offset);
+            }
         }
 
         data += old_vtx_size;
@@ -358,13 +394,16 @@ vbo_exec_wrap_upgrade_vertex(struct vbo_exec_context *exec,
  * 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.
+ * \param newSize  size of new vertex (number of 32-bit words).
  */
 static void
-vbo_exec_fixup_vertex(struct gl_context *ctx, GLuint attr, GLuint newSize)
+vbo_exec_fixup_vertex(struct gl_context *ctx, GLuint attr,
+                      GLuint newSize, GLenum newType)
 {
    struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
 
-   if (newSize > exec->vtx.attrsz[attr]) {
+   if (newSize > exec->vtx.attrsz[attr] ||
+       newType != exec->vtx.attrtype[attr]) {
       /* New size is larger.  Need to flush existing vertices and get
        * an enlarged vertex format.
        */
@@ -372,54 +411,91 @@ vbo_exec_fixup_vertex(struct gl_context *ctx, GLuint attr, GLuint newSize)
    }
    else if (newSize < exec->vtx.active_sz[attr]) {
       GLuint i;
-      const GLfloat *id =
-            vbo_get_default_vals_as_float(exec->vtx.attrtype[attr]);
+      const fi_type *id =
+            vbo_get_default_vals_as_union(exec->vtx.attrtype[attr]);
 
       /* New size is smaller - just need to fill in some
        * zeros.  Don't need to flush or wrap.
        */
       for (i = newSize; i <= exec->vtx.attrsz[attr]; i++)
-        exec->vtx.attrptr[attr][i-1] = id[i-1];
+         exec->vtx.attrptr[attr][i-1] = id[i-1];
    }
 
    exec->vtx.active_sz[attr] = newSize;
+   exec->vtx.attrtype[attr] = newType;
 
    /* Does setting NeedFlush belong here?  Necessitates resetting
     * vtxfmt on each flush (otherwise flags won't get reset
     * afterwards).
     */
-   if (attr == 0) 
+   if (attr == 0)
       ctx->Driver.NeedFlush |= FLUSH_STORED_VERTICES;
 }
 
 
+/**
+ * Called upon first glVertex, glColor, glTexCoord, etc.
+ */
+static void
+vbo_exec_begin_vertices(struct gl_context *ctx)
+{
+   struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
+
+   vbo_exec_vtx_map( exec );
+
+   assert((ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT) == 0);
+   assert(exec->begin_vertices_flags);
+
+   ctx->Driver.NeedFlush |= exec->begin_vertices_flags;
+}
+
+
 /**
  * This macro is used to implement all the glVertex, glColor, glTexCoord,
  * glVertexAttrib, etc functions.
+ * \param A  attribute index
+ * \param N  attribute size (1..4)
+ * \param T  type (GL_FLOAT, GL_DOUBLE, GL_INT, GL_UNSIGNED_INT)
+ * \param C  cast type (fi_type or double)
+ * \param V0, V1, v2, V3  attribute value
  */
-#define ATTR( A, N, T, V0, V1, V2, V3 )                                        \
+#define ATTR_UNION( A, N, T, C, V0, V1, V2, V3 )                        \
 do {                                                                   \
    struct vbo_exec_context *exec = &vbo_context(ctx)->exec;            \
-                                                                       \
-   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);                                        \
-                                                                       \
+   int sz = (sizeof(C) / sizeof(GLfloat));                              \
+                                                                        \
+   assert(sz == 1 || sz == 2);                                          \
+                                                                        \
+   /* check if attribute size or type is changing */                    \
+   if (unlikely(exec->vtx.active_sz[A] != N * sz) ||                    \
+       unlikely(exec->vtx.attrtype[A] != T)) {                          \
+      vbo_exec_fixup_vertex(ctx, A, N * sz, T);                         \
+   }                                                                   \
+                                                                        \
+   /* store vertex attribute in vertex buffer */                        \
    {                                                                   \
-      GLfloat *dest = exec->vtx.attrptr[A];                            \
+      C *dest = (C *)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;                                        \
+      assert(exec->vtx.attrtype[A] == T);                               \
    }                                                                   \
                                                                        \
    if ((A) == 0) {                                                     \
       /* This is a glVertex call */                                    \
       GLuint i;                                                                \
                                                                        \
+      if (unlikely((ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT) == 0)) { \
+         vbo_exec_begin_vertices(ctx);                                  \
+      }                                                                 \
+                                                                        \
+      if (unlikely(!exec->vtx.buffer_ptr)) {                            \
+         vbo_exec_vtx_map(exec);                                        \
+      }                                                                 \
+      assert(exec->vtx.buffer_ptr);                                     \
+                                                                        \
+      /* copy 32-bit words */                                           \
       for (i = 0; i < exec->vtx.vertex_size; i++)                      \
         exec->vtx.buffer_ptr[i] = exec->vtx.vertex[i];                 \
                                                                        \
@@ -431,11 +507,15 @@ do {                                                                      \
                                                                        \
       if (++exec->vtx.vert_count >= exec->vtx.max_vert)                        \
         vbo_exec_vtx_wrap( exec );                                     \
-   }                                                                   \
+   } else {                                                             \
+      /* we now have accumulated per-vertex attributes */               \
+      ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT;                    \
+   }                                                                    \
 } while (0)
 
 
-#define ERROR(err) _mesa_error( ctx, err, __FUNCTION__ )
+#undef ERROR
+#define ERROR(err) _mesa_error( ctx, err, __func__ )
 #define TAG(x) vbo_##x
 
 #include "vbo_attrib_tmp.h"
@@ -553,30 +633,31 @@ vbo_exec_FlushVertices_internal(struct vbo_exec_context *exec, GLboolean unmap)
 
    if (exec->vtx.vertex_size) {
       vbo_exec_copy_to_current( exec );
-      reset_attrfv( exec );
+      vbo_reset_all_attr(exec);
    }
 }
 
 
-static void GLAPIENTRY vbo_exec_EvalCoord1f( GLfloat u )
+static void GLAPIENTRY
+vbo_exec_EvalCoord1f(GLfloat u)
 {
    GET_CURRENT_CONTEXT( ctx );
    struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
 
    {
       GLint i;
-      if (exec->eval.recalculate_maps) 
+      if (exec->eval.recalculate_maps)
         vbo_exec_eval_update( exec );
 
       for (i = 0; i <= VBO_ATTRIB_TEX7; i++) {
-        if (exec->eval.map1[i].map) 
+        if (exec->eval.map1[i].map)
            if (exec->vtx.active_sz[i] != exec->eval.map1[i].sz)
-              vbo_exec_fixup_vertex( ctx, i, exec->eval.map1[i].sz );
+              vbo_exec_fixup_vertex( ctx, i, exec->eval.map1[i].sz, GL_FLOAT );
       }
    }
 
 
-   memcpy( exec->vtx.copied.buffer, exec->vtx.vertex, 
+   memcpy( exec->vtx.copied.buffer, exec->vtx.vertex,
            exec->vtx.vertex_size * sizeof(GLfloat));
 
    vbo_exec_do_EvalCoord1f( exec, u );
@@ -585,47 +666,55 @@ static void GLAPIENTRY vbo_exec_EvalCoord1f( GLfloat u )
            exec->vtx.vertex_size * sizeof(GLfloat));
 }
 
-static void GLAPIENTRY vbo_exec_EvalCoord2f( GLfloat u, GLfloat v )
+
+static void GLAPIENTRY
+vbo_exec_EvalCoord2f(GLfloat u, GLfloat v)
 {
    GET_CURRENT_CONTEXT( ctx );
    struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
 
    {
       GLint i;
-      if (exec->eval.recalculate_maps) 
+      if (exec->eval.recalculate_maps)
         vbo_exec_eval_update( exec );
 
       for (i = 0; i <= VBO_ATTRIB_TEX7; i++) {
-        if (exec->eval.map2[i].map) 
+        if (exec->eval.map2[i].map)
            if (exec->vtx.active_sz[i] != exec->eval.map2[i].sz)
-              vbo_exec_fixup_vertex( ctx, i, exec->eval.map2[i].sz );
+              vbo_exec_fixup_vertex( ctx, i, exec->eval.map2[i].sz, GL_FLOAT );
       }
 
-      if (ctx->Eval.AutoNormal) 
+      if (ctx->Eval.AutoNormal)
         if (exec->vtx.active_sz[VBO_ATTRIB_NORMAL] != 3)
-           vbo_exec_fixup_vertex( ctx, VBO_ATTRIB_NORMAL, 3 );
+           vbo_exec_fixup_vertex( ctx, VBO_ATTRIB_NORMAL, 3, GL_FLOAT );
    }
 
-   memcpy( exec->vtx.copied.buffer, exec->vtx.vertex, 
+   memcpy( exec->vtx.copied.buffer, exec->vtx.vertex,
            exec->vtx.vertex_size * sizeof(GLfloat));
 
    vbo_exec_do_EvalCoord2f( exec, u, v );
 
-   memcpy( exec->vtx.vertex, exec->vtx.copied.buffer, 
+   memcpy( exec->vtx.vertex, exec->vtx.copied.buffer,
            exec->vtx.vertex_size * sizeof(GLfloat));
 }
 
-static void GLAPIENTRY vbo_exec_EvalCoord1fv( const GLfloat *u )
+
+static void GLAPIENTRY
+vbo_exec_EvalCoord1fv(const GLfloat *u)
 {
    vbo_exec_EvalCoord1f( u[0] );
 }
 
-static void GLAPIENTRY vbo_exec_EvalCoord2fv( const GLfloat *u )
+
+static void GLAPIENTRY
+vbo_exec_EvalCoord2fv(const GLfloat *u)
 {
    vbo_exec_EvalCoord2f( u[0], u[1] );
 }
 
-static void GLAPIENTRY vbo_exec_EvalPoint1( GLint i )
+
+static void GLAPIENTRY
+vbo_exec_EvalPoint1(GLint i)
 {
    GET_CURRENT_CONTEXT( ctx );
    GLfloat du = ((ctx->Eval.MapGrid1u2 - ctx->Eval.MapGrid1u1) /
@@ -636,12 +725,13 @@ static void GLAPIENTRY vbo_exec_EvalPoint1( GLint i )
 }
 
 
-static void GLAPIENTRY vbo_exec_EvalPoint2( GLint i, GLint j )
+static void GLAPIENTRY
+vbo_exec_EvalPoint2(GLint i, GLint j)
 {
    GET_CURRENT_CONTEXT( ctx );
-   GLfloat du = ((ctx->Eval.MapGrid2u2 - ctx->Eval.MapGrid2u1) / 
+   GLfloat du = ((ctx->Eval.MapGrid2u2 - ctx->Eval.MapGrid2u1) /
                 (GLfloat) ctx->Eval.MapGrid2un);
-   GLfloat dv = ((ctx->Eval.MapGrid2v2 - ctx->Eval.MapGrid2v1) / 
+   GLfloat dv = ((ctx->Eval.MapGrid2v2 - ctx->Eval.MapGrid2v1) /
                 (GLfloat) ctx->Eval.MapGrid2vn);
    GLfloat u = i * du + ctx->Eval.MapGrid2u1;
    GLfloat v = j * dv + ctx->Eval.MapGrid2v1;
@@ -650,222 +740,158 @@ static void GLAPIENTRY vbo_exec_EvalPoint2( GLint i, GLint j )
 }
 
 
+/**
+ * Called via glBegin.
+ */
 static void GLAPIENTRY
-vbo_exec_EvalMesh1(GLenum mode, GLint i1, GLint i2)
+vbo_exec_Begin(GLenum mode)
 {
-   GET_CURRENT_CONTEXT(ctx);
-   GLint i;
-   GLfloat u, du;
-   GLenum prim;
-
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
+   GET_CURRENT_CONTEXT( ctx );
+   struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
+   int i;
 
-   switch (mode) {
-   case GL_POINT:
-      prim = GL_POINTS;
-      break;
-   case GL_LINE:
-      prim = GL_LINE_STRIP;
-      break;
-   default:
-      _mesa_error( ctx, GL_INVALID_ENUM, "glEvalMesh1(mode)" );
+   if (_mesa_inside_begin_end(ctx)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glBegin");
       return;
    }
 
-   /* No effect if vertex maps disabled.
-    */
-   if (!ctx->Eval.Map1Vertex4 && 
-       !ctx->Eval.Map1Vertex3)
+   if (!_mesa_valid_prim_mode(ctx, mode, "glBegin")) {
       return;
-
-   du = ctx->Eval.MapGrid1du;
-   u = ctx->Eval.MapGrid1u1 + i1 * du;
-
-   CALL_Begin(GET_DISPATCH(), (prim));
-   for (i=i1;i<=i2;i++,u+=du) {
-      CALL_EvalCoord1f(GET_DISPATCH(), (u));
    }
-   CALL_End(GET_DISPATCH(), ());
-}
 
+   vbo_draw_method(vbo_context(ctx), DRAW_BEGIN_END);
 
-static void GLAPIENTRY
-vbo_exec_EvalMesh2(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2)
-{
-   GET_CURRENT_CONTEXT(ctx);
-   GLfloat u, du, v, dv, v1, u1;
-   GLint i, j;
+   if (ctx->NewState) {
+      _mesa_update_state( ctx );
 
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
-
-   switch (mode) {
-   case GL_POINT:
-   case GL_LINE:
-   case GL_FILL:
-      break;
-   default:
-      _mesa_error( ctx, GL_INVALID_ENUM, "glEvalMesh2(mode)" );
+      CALL_Begin(ctx->Exec, (mode));
       return;
    }
 
-   /* No effect if vertex maps disabled.
-    */
-   if (!ctx->Eval.Map2Vertex4 && 
-       !ctx->Eval.Map2Vertex3)
+   if (!_mesa_valid_to_render(ctx, "glBegin")) {
       return;
-
-   du = ctx->Eval.MapGrid2du;
-   dv = ctx->Eval.MapGrid2dv;
-   v1 = ctx->Eval.MapGrid2v1 + j1 * dv;
-   u1 = ctx->Eval.MapGrid2u1 + i1 * du;
-
-   switch (mode) {
-   case GL_POINT:
-      CALL_Begin(GET_DISPATCH(), (GL_POINTS));
-      for (v=v1,j=j1;j<=j2;j++,v+=dv) {
-        for (u=u1,i=i1;i<=i2;i++,u+=du) {
-           CALL_EvalCoord2f(GET_DISPATCH(), (u, v));
-        }
-      }
-      CALL_End(GET_DISPATCH(), ());
-      break;
-   case GL_LINE:
-      for (v=v1,j=j1;j<=j2;j++,v+=dv) {
-        CALL_Begin(GET_DISPATCH(), (GL_LINE_STRIP));
-        for (u=u1,i=i1;i<=i2;i++,u+=du) {
-           CALL_EvalCoord2f(GET_DISPATCH(), (u, v));
-        }
-        CALL_End(GET_DISPATCH(), ());
-      }
-      for (u=u1,i=i1;i<=i2;i++,u+=du) {
-        CALL_Begin(GET_DISPATCH(), (GL_LINE_STRIP));
-        for (v=v1,j=j1;j<=j2;j++,v+=dv) {
-           CALL_EvalCoord2f(GET_DISPATCH(), (u, v));
-        }
-        CALL_End(GET_DISPATCH(), ());
-      }
-      break;
-   case GL_FILL:
-      for (v=v1,j=j1;j<j2;j++,v+=dv) {
-        CALL_Begin(GET_DISPATCH(), (GL_TRIANGLE_STRIP));
-        for (u=u1,i=i1;i<=i2;i++,u+=du) {
-           CALL_EvalCoord2f(GET_DISPATCH(), (u, v));
-           CALL_EvalCoord2f(GET_DISPATCH(), (u, v+dv));
-        }
-        CALL_End(GET_DISPATCH(), ());
-      }
-      break;
    }
-}
 
-
-/**
- * Execute a glRectf() function.  This is not suitable for GL_COMPILE
- * modes (as the test for outside begin/end is not compiled),
- * but may be useful for drivers in circumstances which exclude
- * display list interactions.
- *
- * (None of the functions in this file are suitable for GL_COMPILE
- * modes).
- */
-static void GLAPIENTRY
-vbo_exec_Rectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
-{
-   GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END(ctx);
-
-   CALL_Begin(GET_DISPATCH(), (GL_QUADS));
-   CALL_Vertex2f(GET_DISPATCH(), (x1, y1));
-   CALL_Vertex2f(GET_DISPATCH(), (x2, y1));
-   CALL_Vertex2f(GET_DISPATCH(), (x2, y2));
-   CALL_Vertex2f(GET_DISPATCH(), (x1, y2));
-   CALL_End(GET_DISPATCH(), ());
+   /* Heuristic: attempt to isolate attributes occurring 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);
+   }
 }
 
 
 /**
- * Called via glBegin.
+ * Try to merge / concatenate the two most recent VBO primitives.
  */
-static void GLAPIENTRY vbo_exec_Begin( GLenum mode )
+static void
+try_vbo_merge(struct vbo_exec_context *exec)
 {
-   GET_CURRENT_CONTEXT( ctx ); 
-
-   if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END) {
-      struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
-      int i;
-
-      if (!_mesa_valid_prim_mode(ctx, mode, "glBegin")) {
-         return;
-      }
+   struct _mesa_prim *cur =  &exec->vtx.prim[exec->vtx.prim_count - 1];
 
-      vbo_draw_method(vbo_context(ctx), DRAW_BEGIN_END);
+   assert(exec->vtx.prim_count >= 1);
 
-      if (ctx->Driver.PrepareExecBegin)
-        ctx->Driver.PrepareExecBegin(ctx);
+   vbo_try_prim_conversion(cur);
 
-      if (ctx->NewState) {
-        _mesa_update_state( ctx );
+   if (exec->vtx.prim_count >= 2) {
+      struct _mesa_prim *prev = &exec->vtx.prim[exec->vtx.prim_count - 2];
+      assert(prev == cur - 1);
 
-        CALL_Begin(ctx->Exec, (mode));
-        return;
+      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 */
       }
-
-      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;
-
-      ctx->Driver.CurrentExecPrimitive = mode;
    }
-   else 
-      _mesa_error( ctx, GL_INVALID_OPERATION, "glBegin" );
-      
 }
 
 
 /**
  * Called via glEnd.
  */
-static void GLAPIENTRY vbo_exec_End( void )
+static void GLAPIENTRY
+vbo_exec_End(void)
 {
-   GET_CURRENT_CONTEXT( ctx ); 
+   GET_CURRENT_CONTEXT( ctx );
+   struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
 
-   if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) {
-      struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
+   if (!_mesa_inside_begin_end(ctx)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glEnd");
+      return;
+   }
 
-      if (exec->vtx.prim_count > 0) {
-         /* close off current primitive */
-         int idx = exec->vtx.vert_count;
-         int i = exec->vtx.prim_count - 1;
+   ctx->Exec = ctx->OutsideBeginEnd;
+   if (ctx->CurrentDispatch == ctx->BeginEnd) {
+      ctx->CurrentDispatch = ctx->OutsideBeginEnd;
+      _glapi_set_dispatch(ctx->CurrentDispatch);
+   }
 
-         exec->vtx.prim[i].end = 1; 
-         exec->vtx.prim[i].count = idx - exec->vtx.prim[i].start;
-      }
+   if (exec->vtx.prim_count > 0) {
+      /* close off current primitive */
+      struct _mesa_prim *last_prim = &exec->vtx.prim[exec->vtx.prim_count - 1];
 
-      ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END;
+      last_prim->end = 1;
+      last_prim->count = exec->vtx.vert_count - last_prim->start;
 
-      if (exec->vtx.prim_count == VBO_MAX_PRIM)
-        vbo_exec_vtx_flush( exec, GL_FALSE );
+      /* Special handling for GL_LINE_LOOP */
+      if (last_prim->mode == GL_LINE_LOOP && last_prim->begin == 0) {
+         /* We're finishing drawing a line loop.  Append 0th vertex onto
+          * end of vertex buffer so we can draw it as a line strip.
+          */
+         const fi_type *src = exec->vtx.buffer_map +
+            last_prim->start * exec->vtx.vertex_size;
+         fi_type *dst = exec->vtx.buffer_map +
+            exec->vtx.vert_count * exec->vtx.vertex_size;
+
+         /* copy 0th vertex to end of buffer */
+         memcpy(dst, src, exec->vtx.vertex_size * sizeof(fi_type));
+
+         last_prim->start++;  /* skip vertex0 */
+         /* note that last_prim->count stays unchanged */
+         last_prim->mode = GL_LINE_STRIP;
+
+         /* Increment the vertex count so the next primitive doesn't
+          * overwrite the last vertex which we just added.
+          */
+         exec->vtx.vert_count++;
+         exec->vtx.buffer_ptr += exec->vtx.vertex_size;
+      }
+
+      try_vbo_merge(exec);
    }
-   else 
-      _mesa_error( ctx, GL_INVALID_OPERATION, "glEnd" );
+
+   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);
@@ -880,7 +906,7 @@ static void GLAPIENTRY
 vbo_exec_PrimitiveRestartNV(void)
 {
    GLenum curPrim;
-   GET_CURRENT_CONTEXT( ctx ); 
+   GET_CURRENT_CONTEXT(ctx);
 
    curPrim = ctx->Driver.CurrentExecPrimitive;
 
@@ -894,22 +920,27 @@ vbo_exec_PrimitiveRestartNV(void)
 }
 
 
-
-static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec )
+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 = vbo_exec_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:
     */
@@ -945,7 +976,7 @@ static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec )
    vfmt->Vertex3fv = vbo_Vertex3fv;
    vfmt->Vertex4f = vbo_Vertex4f;
    vfmt->Vertex4fv = vbo_Vertex4fv;
-   
+
    if (ctx->API == API_OPENGLES2) {
       vfmt->VertexAttrib1fARB = _es_VertexAttrib1f;
       vfmt->VertexAttrib1fvARB = _es_VertexAttrib1fv;
@@ -1028,7 +1059,7 @@ static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec )
    vfmt->MultiTexCoordP3uiv = vbo_MultiTexCoordP3uiv;
    vfmt->MultiTexCoordP4ui = vbo_MultiTexCoordP4ui;
    vfmt->MultiTexCoordP4uiv = vbo_MultiTexCoordP4uiv;
-   
+
    vfmt->NormalP3ui = vbo_NormalP3ui;
    vfmt->NormalP3uiv = vbo_NormalP3uiv;
 
@@ -1048,6 +1079,16 @@ static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec )
    vfmt->VertexAttribP3uiv = vbo_VertexAttribP3uiv;
    vfmt->VertexAttribP4ui = vbo_VertexAttribP4ui;
    vfmt->VertexAttribP4uiv = vbo_VertexAttribP4uiv;
+
+   vfmt->VertexAttribL1d = vbo_VertexAttribL1d;
+   vfmt->VertexAttribL2d = vbo_VertexAttribL2d;
+   vfmt->VertexAttribL3d = vbo_VertexAttribL3d;
+   vfmt->VertexAttribL4d = vbo_VertexAttribL4d;
+
+   vfmt->VertexAttribL1dv = vbo_VertexAttribL1dv;
+   vfmt->VertexAttribL2dv = vbo_VertexAttribL2dv;
+   vfmt->VertexAttribL3dv = vbo_VertexAttribL3dv;
+   vfmt->VertexAttribL4dv = vbo_VertexAttribL4dv;
 }
 
 
@@ -1057,7 +1098,8 @@ static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec )
  * This replaces the malloced buffer which was created in
  * vb_exec_vtx_init() below.
  */
-void vbo_use_buffer_objects(struct gl_context *ctx)
+void
+vbo_use_buffer_objects(struct gl_context *ctx)
 {
    struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
    /* Any buffer name but 0 can be used here since this bufferobj won't
@@ -1070,16 +1112,19 @@ 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);
-   if (!ctx->Driver.BufferData(ctx, target, size, NULL, usage, exec->vtx.bufferobj)) {
+   exec->vtx.bufferobj = ctx->Driver.NewBufferObject(ctx, bufName);
+   if (!ctx->Driver.BufferData(ctx, target, size, NULL, usage,
+                               GL_MAP_WRITE_BIT |
+                               GL_DYNAMIC_STORAGE_BIT |
+                               GL_CLIENT_STORAGE_BIT,
+                               exec->vtx.bufferobj)) {
       _mesa_error(ctx, GL_OUT_OF_MEMORY, "VBO allocation");
    }
 }
@@ -1099,7 +1144,8 @@ vbo_always_unmap_buffers(struct gl_context *ctx)
 }
 
 
-void vbo_exec_vtx_init( struct vbo_exec_context *exec )
+void
+vbo_exec_vtx_init(struct vbo_exec_context *exec)
 {
    struct gl_context *ctx = exec->ctx;
    struct vbo_context *vbo = vbo_context(ctx);
@@ -1113,31 +1159,28 @@ void vbo_exec_vtx_init( struct vbo_exec_context *exec )
                                  &exec->vtx.bufferobj,
                                  ctx->Shared->NullBufferObj);
 
-   ASSERT(!exec->vtx.buffer_map);
+   assert(!exec->vtx.buffer_map);
    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 );
    _mesa_noop_vtxfmt_init(&exec->vtxfmt_noop);
 
-   /* Hook our functions into the dispatch table.
-    */
-   _mesa_install_exec_vtxfmt( ctx, &exec->vtxfmt );
-
+   exec->vtx.enabled = 0;
    for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) {
-      ASSERT(i < Elements(exec->vtx.attrsz));
+      assert(i < ARRAY_SIZE(exec->vtx.attrsz));
       exec->vtx.attrsz[i] = 0;
-      ASSERT(i < Elements(exec->vtx.attrtype));
+      assert(i < ARRAY_SIZE(exec->vtx.attrtype));
       exec->vtx.attrtype[i] = GL_FLOAT;
-      ASSERT(i < Elements(exec->vtx.active_sz));
+      assert(i < ARRAY_SIZE(exec->vtx.active_sz));
       exec->vtx.active_sz[i] = 0;
    }
    for (i = 0 ; i < VERT_ATTRIB_MAX; i++) {
-      ASSERT(i < Elements(exec->vtx.inputs));
-      ASSERT(i < Elements(exec->vtx.arrays));
+      assert(i < ARRAY_SIZE(exec->vtx.inputs));
+      assert(i < ARRAY_SIZE(exec->vtx.arrays));
       exec->vtx.inputs[i] = &exec->vtx.arrays[i];
    }
-   
+
    {
       struct gl_client_array *arrays = exec->vtx.arrays;
       unsigned i;
@@ -1148,7 +1191,7 @@ void vbo_exec_vtx_init( struct vbo_exec_context *exec )
          struct gl_client_array *array;
          array = &arrays[VERT_ATTRIB_FF(i)];
          array->BufferObj = NULL;
-         _mesa_reference_buffer_object(ctx, &arrays->BufferObj,
+         _mesa_reference_buffer_object(ctx, &array->BufferObj,
                                  vbo->currval[VBO_ATTRIB_POS+i].BufferObj);
       }
 
@@ -1171,7 +1214,8 @@ void vbo_exec_vtx_init( struct vbo_exec_context *exec )
 }
 
 
-void vbo_exec_vtx_destroy( struct vbo_exec_context *exec )
+void
+vbo_exec_vtx_destroy(struct vbo_exec_context *exec)
 {
    /* using a real VBO for vertex data */
    struct gl_context *ctx = exec->ctx;
@@ -1180,7 +1224,7 @@ void vbo_exec_vtx_destroy( struct vbo_exec_context *exec )
    /* True VBOs should already be unmapped
     */
    if (exec->vtx.buffer_map) {
-      ASSERT(exec->vtx.bufferobj->Name == 0 ||
+      assert(exec->vtx.bufferobj->Name == 0 ||
              exec->vtx.bufferobj->Name == IMM_BUFFER_NAME);
       if (exec->vtx.bufferobj->Name == 0) {
          _mesa_align_free(exec->vtx.buffer_map);
@@ -1191,7 +1235,7 @@ void vbo_exec_vtx_destroy( struct vbo_exec_context *exec )
 
    /* Drop any outstanding reference to the vertex buffer
     */
-   for (i = 0; i < Elements(exec->vtx.arrays); i++) {
+   for (i = 0; i < ARRAY_SIZE(exec->vtx.arrays); i++) {
       _mesa_reference_buffer_object(ctx,
                                     &exec->vtx.arrays[i].BufferObj,
                                     NULL);
@@ -1199,34 +1243,26 @@ 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, exec->vtx.bufferobj);
+   if (_mesa_bufferobj_mapped(exec->vtx.bufferobj, MAP_INTERNAL)) {
+      ctx->Driver.UnmapBuffer(ctx, exec->vtx.bufferobj, MAP_INTERNAL);
    }
    _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;
-
-   vbo_exec_vtx_map( exec );
-
-   assert((ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT) == 0);
-   assert(exec->begin_vertices_flags);
-
-   ctx->Driver.NeedFlush |= exec->begin_vertices_flags;
-}
-
-
-/**
- * Called via ctx->Driver.FlushVertices()
+ * If inside glBegin()/glEnd(), it should assert(0).  Otherwise, if
+ * FLUSH_STORED_VERTICES bit in \p flags is set flushes any buffered
+ * vertices, if FLUSH_UPDATE_CURRENT bit is set updates
+ * __struct gl_contextRec::Current and gl_light_attrib::Material
+ *
+ * Note that the default T&L engine never clears the
+ * FLUSH_UPDATE_CURRENT bit, even after performing the update.
+ *
  * \param flags  bitmask of FLUSH_STORED_VERTICES, FLUSH_UPDATE_CURRENT
  */
-void vbo_exec_FlushVertices( struct gl_context *ctx, GLuint flags )
+void
+vbo_exec_FlushVertices(struct gl_context *ctx, GLuint flags)
 {
    struct vbo_exec_context *exec = &vbo_context(ctx)->exec;
 
@@ -1236,7 +1272,7 @@ void vbo_exec_FlushVertices( struct gl_context *ctx, GLuint flags )
    assert(exec->flush_call_depth == 1);
 #endif
 
-   if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) {
+   if (_mesa_inside_begin_end(ctx)) {
       /* We've had glBegin but not glEnd! */
 #ifdef DEBUG
       exec->flush_call_depth--;
@@ -1248,7 +1284,7 @@ void vbo_exec_FlushVertices( struct gl_context *ctx, GLuint flags )
    /* 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:
+   /* Need to do this to ensure vbo_exec_begin_vertices gets called again:
     */
    ctx->Driver.NeedFlush &= ~(FLUSH_UPDATE_CURRENT | flags);
 
@@ -1259,19 +1295,29 @@ void vbo_exec_FlushVertices( struct gl_context *ctx, GLuint flags )
 }
 
 
-static void reset_attrfv( struct vbo_exec_context *exec )
-{   
-   GLuint i;
+/**
+ * Reset the vertex attribute by setting its size to zero.
+ */
+static void
+vbo_reset_attr(struct vbo_exec_context *exec, GLuint attr)
+{
+   exec->vtx.attrsz[attr] = 0;
+   exec->vtx.attrtype[attr] = GL_FLOAT;
+   exec->vtx.active_sz[attr] = 0;
+}
 
-   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;
+
+static void
+vbo_reset_all_attr(struct vbo_exec_context *exec)
+{
+   while (exec->vtx.enabled) {
+      const int i = u_bit_scan64(&exec->vtx.enabled);
+      vbo_reset_attr(exec, i);
    }
 
    exec->vtx.vertex_size = 0;
 }
-      
+
 
 void GLAPIENTRY
 _es_Color4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
@@ -1320,7 +1366,7 @@ 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);
+      ATTRF(VBO_ATTRIB_GENERIC0 + index, 4, x, y, z, w);
    else
       ERROR(GL_INVALID_VALUE);
 }