vbo: rename prim to prims
[mesa.git] / src / mesa / vbo / vbo_save_api.c
index 26951bd9413d9b63232147140f96572515d162d5..84d8d1e05231bbd3cf9910a746fe16a4c142eb3b 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,7 +27,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 /*
  * Authors:
- *   Keith Whitwell <keith@tungstengraphics.com>
+ *   Keith Whitwell <keithw@vmware.com>
  */
 
 
@@ -41,7 +41,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
  * example by building a list that consists of one very long primitive
  * (eg Begin(Triangles), 1000 vertices, End), and calling that list
  * from inside a different begin/end object (Begin(Lines), CallList,
- * End).  
+ * End).
  *
  * In that case the code will have to replay the list as individual
  * commands through the Exec dispatch table, or fix up the copied
@@ -60,7 +60,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
  *
  * The list compiler currently doesn't attempt to compile lists
  * containing EvalCoord or EvalPoint commands.  On encountering one of
- * these, compilation falls back to opcodes.  
+ * these, compilation falls back to opcodes.
  *
  * This could be improved to fallback only when a mix of EvalCoord and
  * Vertex commands are issued within a single primitive.
@@ -78,6 +78,8 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include "main/api_arrayelt.h"
 #include "main/vtxfmt.h"
 #include "main/dispatch.h"
+#include "main/state.h"
+#include "util/bitscan.h"
 
 #include "vbo_context.h"
 #include "vbo_noop.h"
@@ -99,14 +101,14 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
 static GLuint
 _save_copy_vertices(struct gl_context *ctx,
                     const struct vbo_save_vertex_list *node,
-                    const GLfloat * src_buffer)
+                    const fi_type * src_buffer)
 {
    struct vbo_save_context *save = &vbo_context(ctx)->save;
-   const struct _mesa_prim *prim = &node->prim[node->prim_count - 1];
+   const struct _mesa_prim *prim = &node->prims[node->prim_count - 1];
    GLuint nr = prim->count;
    GLuint sz = save->vertex_size;
-   const GLfloat *src = src_buffer + prim->start * sz;
-   GLfloat *dst = save->copied.buffer;
+   const fi_type *src = src_buffer + prim->start * sz;
+   fi_type *dst = save->copied.buffer;
    GLuint ovf, i;
 
    if (prim->end)
@@ -191,15 +193,15 @@ alloc_vertex_store(struct gl_context *ctx)
     * user.  Perhaps there could be a special number for internal
     * buffers:
     */
-   vertex_store->bufferobj = ctx->Driver.NewBufferObject(ctx,
-                                                         VBO_BUF_ID,
-                                                         GL_ARRAY_BUFFER_ARB);
+   vertex_store->bufferobj = ctx->Driver.NewBufferObject(ctx, VBO_BUF_ID);
    if (vertex_store->bufferobj) {
       save->out_of_memory =
          !ctx->Driver.BufferData(ctx,
                                  GL_ARRAY_BUFFER_ARB,
                                  VBO_SAVE_BUFFER_SIZE * sizeof(GLfloat),
                                  NULL, GL_STATIC_DRAW_ARB,
+                                 GL_MAP_WRITE_BIT |
+                                 GL_DYNAMIC_STORAGE_BIT,
                                  vertex_store->bufferobj);
    }
    else {
@@ -211,7 +213,7 @@ alloc_vertex_store(struct gl_context *ctx)
       _mesa_install_save_vtxfmt(ctx, &save->vtxfmt_noop);
    }
 
-   vertex_store->buffer = NULL;
+   vertex_store->buffer_map = NULL;
    vertex_store->used = 0;
    vertex_store->refcount = 1;
 
@@ -223,7 +225,7 @@ static void
 free_vertex_store(struct gl_context *ctx,
                   struct vbo_save_vertex_store *vertex_store)
 {
-   assert(!vertex_store->buffer);
+   assert(!vertex_store->buffer_map);
 
    if (vertex_store->bufferobj) {
       _mesa_reference_buffer_object(ctx, &vertex_store->bufferobj, NULL);
@@ -233,20 +235,36 @@ free_vertex_store(struct gl_context *ctx,
 }
 
 
-GLfloat *
+fi_type *
 vbo_save_map_vertex_store(struct gl_context *ctx,
                           struct vbo_save_vertex_store *vertex_store)
 {
+   const GLbitfield access = (GL_MAP_WRITE_BIT |
+                              GL_MAP_INVALIDATE_RANGE_BIT |
+                              GL_MAP_UNSYNCHRONIZED_BIT |
+                              GL_MAP_FLUSH_EXPLICIT_BIT);
+
    assert(vertex_store->bufferobj);
-   assert(!vertex_store->buffer);
+   assert(!vertex_store->buffer_map);  /* the buffer should not be mapped */
+
    if (vertex_store->bufferobj->Size > 0) {
-      vertex_store->buffer =
-         (GLfloat *) ctx->Driver.MapBufferRange(ctx, 0,
-                                                vertex_store->bufferobj->Size,
-                                                GL_MAP_WRITE_BIT,  /* not used */
-                                                vertex_store->bufferobj);
-      assert(vertex_store->buffer);
-      return vertex_store->buffer + vertex_store->used;
+      /* Map the remaining free space in the VBO */
+      GLintptr offset = vertex_store->used * sizeof(GLfloat);
+      GLsizeiptr size = vertex_store->bufferobj->Size - offset;
+      fi_type *range = (fi_type *)
+         ctx->Driver.MapBufferRange(ctx, offset, size, access,
+                                    vertex_store->bufferobj,
+                                    MAP_INTERNAL);
+      if (range) {
+         /* compute address of start of whole buffer (needed elsewhere) */
+         vertex_store->buffer_map = range - vertex_store->used;
+         assert(vertex_store->buffer_map);
+         return range;
+      }
+      else {
+         vertex_store->buffer_map = NULL;
+         return NULL;
+      }
    }
    else {
       /* probably ran out of memory for buffers */
@@ -260,18 +278,26 @@ vbo_save_unmap_vertex_store(struct gl_context *ctx,
                             struct vbo_save_vertex_store *vertex_store)
 {
    if (vertex_store->bufferobj->Size > 0) {
-      ctx->Driver.UnmapBuffer(ctx, vertex_store->bufferobj);
+      GLintptr offset = 0;
+      GLsizeiptr length = vertex_store->used * sizeof(GLfloat)
+         - vertex_store->bufferobj->Mappings[MAP_INTERNAL].Offset;
+
+      /* Explicitly flush the region we wrote to */
+      ctx->Driver.FlushMappedBufferRange(ctx, offset, length,
+                                         vertex_store->bufferobj,
+                                         MAP_INTERNAL);
+
+      ctx->Driver.UnmapBuffer(ctx, vertex_store->bufferobj, MAP_INTERNAL);
    }
-   vertex_store->buffer = NULL;
+   vertex_store->buffer_map = NULL;
 }
 
 
 static struct vbo_save_primitive_store *
-alloc_prim_store(struct gl_context *ctx)
+alloc_prim_store(void)
 {
    struct vbo_save_primitive_store *store =
       CALLOC_STRUCT(vbo_save_primitive_store);
-   (void) ctx;
    store->used = 0;
    store->refcount = 1;
    return store;
@@ -283,21 +309,21 @@ _save_reset_counters(struct gl_context *ctx)
 {
    struct vbo_save_context *save = &vbo_context(ctx)->save;
 
-   save->prim = save->prim_store->buffer + save->prim_store->used;
-   save->buffer = save->vertex_store->buffer + save->vertex_store->used;
+   save->prims = save->prim_store->prims + save->prim_store->used;
+   save->buffer_map = save->vertex_store->buffer_map + save->vertex_store->used;
 
-   assert(save->buffer == save->buffer_ptr);
+   assert(save->buffer_map == save->buffer_ptr);
 
    if (save->vertex_size)
-      save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) /
-                        save->vertex_size);
+      save->max_vert = (VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) /
+                        save->vertex_size;
    else
       save->max_vert = 0;
 
    save->vert_count = 0;
    save->prim_count = 0;
    save->prim_max = VBO_SAVE_PRIM_SIZE - save->prim_store->used;
-   save->dangling_attr_ref = 0;
+   save->dangling_attr_ref = GL_FALSE;
 }
 
 /**
@@ -305,8 +331,7 @@ _save_reset_counters(struct gl_context *ctx)
  * previous prim.
  */
 static void
-merge_prims(struct gl_context *ctx,
-            struct _mesa_prim *prim_list,
+merge_prims(struct _mesa_prim *prim_list,
             GLuint *prim_count)
 {
    GLuint i;
@@ -336,6 +361,51 @@ merge_prims(struct gl_context *ctx,
    *prim_count = prev_prim - prim_list + 1;
 }
 
+
+/**
+ * Convert GL_LINE_LOOP primitive into GL_LINE_STRIP so that drivers
+ * don't have to worry about handling the _mesa_prim::begin/end flags.
+ * See https://bugs.freedesktop.org/show_bug.cgi?id=81174
+ */
+static void
+convert_line_loop_to_strip(struct vbo_save_context *save,
+                           struct vbo_save_vertex_list *node)
+{
+   struct _mesa_prim *prim = &node->prims[node->prim_count - 1];
+
+   assert(prim->mode == GL_LINE_LOOP);
+
+   if (prim->end) {
+      /* Copy the 0th vertex to end of the buffer and extend the
+       * vertex count by one to finish the line loop.
+       */
+      const GLuint sz = save->vertex_size;
+      /* 0th vertex: */
+      const fi_type *src = save->buffer_map + prim->start * sz;
+      /* end of buffer: */
+      fi_type *dst = save->buffer_map + (prim->start + prim->count) * sz;
+
+      memcpy(dst, src, sz * sizeof(float));
+
+      prim->count++;
+      node->vertex_count++;
+      save->vert_count++;
+      save->buffer_ptr += sz;
+      save->vertex_store->used += sz;
+   }
+
+   if (!prim->begin) {
+      /* Drawing the second or later section of a long line loop.
+       * Skip the 0th vertex.
+       */
+      prim->start++;
+      prim->count--;
+   }
+
+   prim->mode = GL_LINE_STRIP;
+}
+
+
 /**
  * Insert the active immediate struct onto the display list currently
  * being built.
@@ -350,22 +420,26 @@ _save_compile_vertex_list(struct gl_context *ctx)
     * being compiled.
     */
    node = (struct vbo_save_vertex_list *)
-      _mesa_dlist_alloc(ctx, save->opcode_vertex_list, sizeof(*node));
+      _mesa_dlist_alloc_aligned(ctx, save->opcode_vertex_list, sizeof(*node));
 
    if (!node)
       return;
 
+   /* Make sure the pointer is aligned to the size of a pointer */
+   assert((GLintptr) node % sizeof(void *) == 0);
+
    /* Duplicate our template, increment refcounts to the storage structs:
     */
+   node->enabled = save->enabled;
    memcpy(node->attrsz, save->attrsz, sizeof(node->attrsz));
    memcpy(node->attrtype, save->attrtype, sizeof(node->attrtype));
    node->vertex_size = save->vertex_size;
    node->buffer_offset =
-      (save->buffer - save->vertex_store->buffer) * sizeof(GLfloat);
-   node->count = save->vert_count;
+      (save->buffer_map - save->vertex_store->buffer_map) * sizeof(GLfloat);
+   node->vertex_count = save->vert_count;
    node->wrap_count = save->copied.nr;
    node->dangling_attr_ref = save->dangling_attr_ref;
-   node->prim = save->prim;
+   node->prims = save->prims;
    node->prim_count = save->prim_count;
    node->vertex_store = save->vertex_store;
    node->prim_store = save->prim_store;
@@ -373,7 +447,7 @@ _save_compile_vertex_list(struct gl_context *ctx)
    node->vertex_store->refcount++;
    node->prim_store->refcount++;
 
-   if (node->prim[0].no_current_update) {
+   if (node->prims[0].no_current_update) {
       node->current_size = 0;
       node->current_data = NULL;
    }
@@ -387,13 +461,13 @@ _save_compile_vertex_list(struct gl_context *ctx)
           */
          node->current_data = malloc(node->current_size * sizeof(GLfloat));
          if (node->current_data) {
-            const char *buffer = (const char *) save->vertex_store->buffer;
+            const char *buffer = (const char *) save->vertex_store->buffer_map;
             unsigned attr_offset = node->attrsz[0] * sizeof(GLfloat);
             unsigned vertex_offset = 0;
 
-            if (node->count)
+            if (node->vertex_count)
                vertex_offset =
-                  (node->count - 1) * node->vertex_size * sizeof(GLfloat);
+                  (node->vertex_count - 1) * node->vertex_size * sizeof(GLfloat);
 
             memcpy(node->current_data,
                    buffer + node->buffer_offset + vertex_offset + attr_offset,
@@ -402,19 +476,23 @@ _save_compile_vertex_list(struct gl_context *ctx)
       }
    }
 
-   assert(node->attrsz[VBO_ATTRIB_POS] != 0 || node->count == 0);
+   assert(node->attrsz[VBO_ATTRIB_POS] != 0 || node->vertex_count == 0);
 
    if (save->dangling_attr_ref)
       ctx->ListState.CurrentList->Flags |= DLIST_DANGLING_REFS;
 
-   save->vertex_store->used += save->vertex_size * node->count;
+   save->vertex_store->used += save->vertex_size * node->vertex_count;
    save->prim_store->used += node->prim_count;
 
-   /* Copy duplicated vertices 
+   /* Copy duplicated vertices
     */
-   save->copied.nr = _save_copy_vertices(ctx, node, save->buffer);
+   save->copied.nr = _save_copy_vertices(ctx, node, save->buffer_map);
+
+   if (node->prims[node->prim_count - 1].mode == GL_LINE_LOOP) {
+      convert_line_loop_to_strip(save, node);
+   }
 
-   merge_prims(ctx, node->prim, &node->prim_count);
+   merge_prims(node->prims, &node->prim_count);
 
    /* Deal with GL_COMPILE_AND_EXECUTE:
     */
@@ -425,9 +503,9 @@ _save_compile_vertex_list(struct gl_context *ctx)
 
       vbo_loopback_vertex_list(ctx,
                                (const GLfloat *) ((const char *) save->
-                                                  vertex_store->buffer +
+                                                  vertex_store->buffer_map +
                                                   node->buffer_offset),
-                               node->attrsz, node->prim, node->prim_count,
+                               node->attrsz, node->prims, node->prim_count,
                                node->wrap_count, node->vertex_size);
 
       _glapi_set_dispatch(dispatch);
@@ -455,11 +533,16 @@ _save_compile_vertex_list(struct gl_context *ctx)
       save->buffer_ptr = vbo_save_map_vertex_store(ctx, save->vertex_store);
       save->out_of_memory = save->buffer_ptr == NULL;
    }
+   else {
+      /* update buffer_ptr for next vertex */
+      save->buffer_ptr = save->vertex_store->buffer_map
+         + save->vertex_store->used;
+   }
 
    if (save->prim_store->used > VBO_SAVE_PRIM_SIZE - 6) {
       save->prim_store->refcount--;
       assert(save->prim_store->refcount != 0);
-      save->prim_store = alloc_prim_store(ctx);
+      save->prim_store = alloc_prim_store();
    }
 
    /* Reset our structures for the next run of vertices:
@@ -487,10 +570,10 @@ _save_wrap_buffers(struct gl_context *ctx)
 
    /* Close off in-progress primitive.
     */
-   save->prim[i].count = (save->vert_count - save->prim[i].start);
-   mode = save->prim[i].mode;
-   weak = save->prim[i].weak;
-   no_current_update = save->prim[i].no_current_update;
+   save->prims[i].count = (save->vert_count - save->prims[i].start);
+   mode = save->prims[i].mode;
+   weak = save->prims[i].weak;
+   no_current_update = save->prims[i].no_current_update;
 
    /* store the copied vertices, and allocate a new list.
     */
@@ -498,30 +581,30 @@ _save_wrap_buffers(struct gl_context *ctx)
 
    /* Restart interrupted primitive
     */
-   save->prim[0].mode = mode;
-   save->prim[0].weak = weak;
-   save->prim[0].no_current_update = no_current_update;
-   save->prim[0].begin = 0;
-   save->prim[0].end = 0;
-   save->prim[0].pad = 0;
-   save->prim[0].start = 0;
-   save->prim[0].count = 0;
-   save->prim[0].num_instances = 1;
-   save->prim[0].base_instance = 0;
+   save->prims[0].mode = mode;
+   save->prims[0].weak = weak;
+   save->prims[0].no_current_update = no_current_update;
+   save->prims[0].begin = 0;
+   save->prims[0].end = 0;
+   save->prims[0].pad = 0;
+   save->prims[0].start = 0;
+   save->prims[0].count = 0;
+   save->prims[0].num_instances = 1;
+   save->prims[0].base_instance = 0;
+   save->prims[0].is_indirect = 0;
    save->prim_count = 1;
 }
 
 
 /**
  * Called only when buffers are wrapped as the result of filling the
- * vertex_store struct.  
+ * vertex_store struct.
  */
 static void
 _save_wrap_filled_vertex(struct gl_context *ctx)
 {
    struct vbo_save_context *save = &vbo_context(ctx)->save;
-   GLfloat *data = save->copied.buffer;
-   GLuint i;
+   unsigned numComponents;
 
    /* Emit a glEnd to close off the last vertex list.
     */
@@ -531,12 +614,12 @@ _save_wrap_filled_vertex(struct gl_context *ctx)
     */
    assert(save->max_vert - save->vert_count > save->copied.nr);
 
-   for (i = 0; i < save->copied.nr; i++) {
-      memcpy(save->buffer_ptr, data, save->vertex_size * sizeof(GLfloat));
-      data += save->vertex_size;
-      save->buffer_ptr += save->vertex_size;
-      save->vert_count++;
-   }
+   numComponents = save->copied.nr * save->vertex_size;
+   memcpy(save->buffer_ptr,
+          save->copied.buffer,
+          numComponents * sizeof(fi_type));
+   save->buffer_ptr += numComponents;
+   save->vert_count += save->copied.nr;
 }
 
 
@@ -544,14 +627,15 @@ static void
 _save_copy_to_current(struct gl_context *ctx)
 {
    struct vbo_save_context *save = &vbo_context(ctx)->save;
-   GLuint i;
+   GLbitfield64 enabled = save->enabled & (~BITFIELD64_BIT(VBO_ATTRIB_POS));
 
-   for (i = VBO_ATTRIB_POS + 1; i < VBO_ATTRIB_MAX; i++) {
-      if (save->attrsz[i]) {
-         save->currentsz[i][0] = save->attrsz[i];
-         COPY_CLEAN_4V_TYPE_AS_FLOAT(save->current[i], save->attrsz[i],
-                                     save->attrptr[i], save->attrtype[i]);
-      }
+   while (enabled) {
+      const int i = u_bit_scan64(&enabled);
+      assert(save->attrsz[i]);
+
+      save->currentsz[i][0] = save->attrsz[i];
+      COPY_CLEAN_4V_TYPE_AS_UNION(save->current[i], save->attrsz[i],
+                                  save->attrptr[i], save->attrtype[i]);
    }
 }
 
@@ -560,9 +644,11 @@ static void
 _save_copy_from_current(struct gl_context *ctx)
 {
    struct vbo_save_context *save = &vbo_context(ctx)->save;
-   GLint i;
+   GLbitfield64 enabled = save->enabled & (~BITFIELD64_BIT(VBO_ATTRIB_POS));
+
+   while (enabled) {
+      const int i = u_bit_scan64(&enabled);
 
-   for (i = VBO_ATTRIB_POS + 1; i < VBO_ATTRIB_MAX; i++) {
       switch (save->attrsz[i]) {
       case 4:
          save->attrptr[i][3] = save->current[i][3];
@@ -572,7 +658,9 @@ _save_copy_from_current(struct gl_context *ctx)
          save->attrptr[i][1] = save->current[i][1];
       case 1:
          save->attrptr[i][0] = save->current[i][0];
+         break;
       case 0:
+         assert(0);
          break;
       }
    }
@@ -591,7 +679,7 @@ _save_upgrade_vertex(struct gl_context *ctx, GLuint attr, GLuint newsz)
    struct vbo_save_context *save = &vbo_context(ctx)->save;
    GLuint oldsz;
    GLuint i;
-   GLfloat *tmp;
+   fi_type *tmp;
 
    /* Store the current run of vertices, and emit a GL_END.  Emit a
     * BEGIN in the new buffer.
@@ -603,7 +691,7 @@ _save_upgrade_vertex(struct gl_context *ctx, GLuint attr, GLuint newsz)
 
    /* 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.  
+    * its size increased.
     */
    _save_copy_to_current(ctx);
 
@@ -611,6 +699,7 @@ _save_upgrade_vertex(struct gl_context *ctx, GLuint attr, GLuint newsz)
     */
    oldsz = save->attrsz[attr];
    save->attrsz[attr] = newsz;
+   save->enabled |= BITFIELD64_BIT(attr);
 
    save->vertex_size += newsz - oldsz;
    save->max_vert = ((VBO_SAVE_BUFFER_SIZE - save->vertex_store->used) /
@@ -619,7 +708,8 @@ _save_upgrade_vertex(struct gl_context *ctx, GLuint attr, GLuint newsz)
 
    /* Recalculate all the attrptr[] values:
     */
-   for (i = 0, tmp = save->vertex; i < VBO_ATTRIB_MAX; i++) {
+   tmp = save->vertex;
+   for (i = 0; i < VBO_ATTRIB_MAX; i++) {
       if (save->attrsz[i]) {
          save->attrptr[i] = tmp;
          tmp += save->attrsz[i];
@@ -640,9 +730,8 @@ _save_upgrade_vertex(struct gl_context *ctx, GLuint attr, GLuint newsz)
     * and will need fixup at runtime.
     */
    if (save->copied.nr) {
-      const GLfloat *data = save->copied.buffer;
-      GLfloat *dest = save->buffer;
-      GLuint j;
+      const fi_type *data = save->copied.buffer;
+      fi_type *dest = save->buffer_map;
 
       /* Need to note this and fix up at runtime (or loopback):
        */
@@ -652,27 +741,28 @@ _save_upgrade_vertex(struct gl_context *ctx, GLuint attr, GLuint newsz)
       }
 
       for (i = 0; i < save->copied.nr; i++) {
-         for (j = 0; j < VBO_ATTRIB_MAX; j++) {
-            if (save->attrsz[j]) {
-               if (j == attr) {
-                  if (oldsz) {
-                     COPY_CLEAN_4V_TYPE_AS_FLOAT(dest, oldsz, data,
-                                                 save->attrtype[j]);
-                     data += oldsz;
-                     dest += newsz;
-                  }
-                  else {
-                     COPY_SZ_4V(dest, newsz, save->current[attr]);
-                     dest += newsz;
-                  }
+         GLbitfield64 enabled = save->enabled;
+         while (enabled) {
+            const int j = u_bit_scan64(&enabled);
+            assert(save->attrsz[j]);
+            if (j == attr) {
+               if (oldsz) {
+                  COPY_CLEAN_4V_TYPE_AS_UNION(dest, oldsz, data,
+                                              save->attrtype[j]);
+                  data += oldsz;
+                  dest += newsz;
                }
                else {
-                  GLint sz = save->attrsz[j];
-                  COPY_SZ_4V(dest, sz, data);
-                  data += sz;
-                  dest += sz;
+                  COPY_SZ_4V(dest, newsz, save->current[attr]);
+                  dest += newsz;
                }
             }
+            else {
+               GLint sz = save->attrsz[j];
+               COPY_SZ_4V(dest, sz, data);
+               data += sz;
+               dest += sz;
+            }
          }
       }
 
@@ -700,7 +790,7 @@ save_fixup_vertex(struct gl_context *ctx, GLuint attr, GLuint sz)
    }
    else if (sz < save->active_sz[attr]) {
       GLuint i;
-      const GLfloat *id = vbo_get_default_vals_as_float(save->attrtype[attr]);
+      const fi_type *id = vbo_get_default_vals_as_union(save->attrtype[attr]);
 
       /* New size is equal or smaller - just need to fill in some
        * zeros.
@@ -722,9 +812,10 @@ static void
 _save_reset_vertex(struct gl_context *ctx)
 {
    struct vbo_save_context *save = &vbo_context(ctx)->save;
-   GLuint i;
 
-   for (i = 0; i < VBO_ATTRIB_MAX; i++) {
+   while (save->enabled) {
+      const int i = u_bit_scan64(&save->enabled);
+      assert(save->attrsz[i]);
       save->attrsz[i] = 0;
       save->active_sz[i] = 0;
    }
@@ -734,7 +825,7 @@ _save_reset_vertex(struct gl_context *ctx)
 
 
 
-#define ERROR(err)   _mesa_compile_error(ctx, err, __FUNCTION__);
+#define ERROR(err)   _mesa_compile_error(ctx, err, __func__);
 
 
 /* Only one size for each attribute may be active at once.  Eg. if
@@ -743,7 +834,7 @@ _save_reset_vertex(struct gl_context *ctx)
  * 3f version won't otherwise set color[3] to 1.0 -- this is the job
  * of the chooser function when switching between Color4f and Color3f.
  */
-#define ATTR(A, N, T, V0, V1, V2, V3)                          \
+#define ATTR_UNION(A, N, T, C, V0, V1, V2, V3)                 \
 do {                                                           \
    struct vbo_save_context *save = &vbo_context(ctx)->save;    \
                                                                \
@@ -751,12 +842,12 @@ do {                                                              \
       save_fixup_vertex(ctx, A, N);                            \
                                                                \
    {                                                           \
-      GLfloat *dest = save->attrptr[A];                                \
+      C *dest = (C *)save->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;                                   \
-      save->attrtype[A] = T;                                    \
+      save->attrtype[A] = T;                                   \
    }                                                           \
                                                                \
    if ((A) == 0) {                                             \
@@ -849,14 +940,14 @@ dlist_fallback(struct gl_context *ctx)
       if (save->prim_count > 0) {
          /* Close off in-progress primitive. */
          GLint i = save->prim_count - 1;
-         save->prim[i].count = save->vert_count - save->prim[i].start;
+         save->prims[i].count = save->vert_count - save->prims[i].start;
       }
 
       /* Need to replay this display list with loopback,
        * unfortunately, otherwise this primitive won't be handled
        * properly:
        */
-      save->dangling_attr_ref = 1;
+      save->dangling_attr_ref = GL_TRUE;
 
       _save_compile_vertex_list(ctx);
    }
@@ -941,28 +1032,28 @@ _save_CallLists(GLsizei n, GLenum type, const GLvoid * v)
 
 
 /**
- * Called via ctx->Driver.NotifySaveBegin() when a glBegin is getting
- * compiled into a display list.
+ * Called when a glBegin is getting compiled into a display list.
  * Updating of ctx->Driver.CurrentSavePrimitive is already taken care of.
  */
-GLboolean
+void
 vbo_save_NotifyBegin(struct gl_context *ctx, GLenum mode)
 {
    struct vbo_save_context *save = &vbo_context(ctx)->save;
    const GLuint i = save->prim_count++;
 
    assert(i < save->prim_max);
-   save->prim[i].mode = mode & VBO_SAVE_PRIM_MODE_MASK;
-   save->prim[i].begin = 1;
-   save->prim[i].end = 0;
-   save->prim[i].weak = (mode & VBO_SAVE_PRIM_WEAK) ? 1 : 0;
-   save->prim[i].no_current_update =
+   save->prims[i].mode = mode & VBO_SAVE_PRIM_MODE_MASK;
+   save->prims[i].begin = 1;
+   save->prims[i].end = 0;
+   save->prims[i].weak = (mode & VBO_SAVE_PRIM_WEAK) ? 1 : 0;
+   save->prims[i].no_current_update =
       (mode & VBO_SAVE_PRIM_NO_CURRENT_UPDATE) ? 1 : 0;
-   save->prim[i].pad = 0;
-   save->prim[i].start = save->vert_count;
-   save->prim[i].count = 0;
-   save->prim[i].num_instances = 1;
-   save->prim[i].base_instance = 0;
+   save->prims[i].pad = 0;
+   save->prims[i].start = save->vert_count;
+   save->prims[i].count = 0;
+   save->prims[i].num_instances = 1;
+   save->prims[i].base_instance = 0;
+   save->prims[i].is_indirect = 0;
 
    if (save->out_of_memory) {
       _mesa_install_save_vtxfmt(ctx, &save->vtxfmt_noop);
@@ -971,13 +1062,8 @@ vbo_save_NotifyBegin(struct gl_context *ctx, GLenum mode)
       _mesa_install_save_vtxfmt(ctx, &save->vtxfmt);
    }
 
-   /* We need to call SaveFlushVertices() if there's state change */
+   /* We need to call vbo_save_SaveFlushVertices() if there's state change */
    ctx->Driver.SaveNeedFlush = GL_TRUE;
-
-   /* GL_TRUE means we've handled this glBegin here; don't compile a BEGIN
-    * opcode into the display list.
-    */
-   return GL_TRUE;
 }
 
 
@@ -989,8 +1075,8 @@ _save_End(void)
    const GLint i = save->prim_count - 1;
 
    ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
-   save->prim[i].end = 1;
-   save->prim[i].count = (save->vert_count - save->prim[i].start);
+   save->prims[i].end = 1;
+   save->prims[i].count = (save->vert_count - save->prims[i].start);
 
    if (i == (GLint) save->prim_max - 1) {
       _save_compile_vertex_list(ctx);
@@ -1022,13 +1108,23 @@ _save_Begin(GLenum mode)
 static void GLAPIENTRY
 _save_PrimitiveRestartNV(void)
 {
-   GLenum curPrim;
    GET_CURRENT_CONTEXT(ctx);
+   struct vbo_save_context *save = &vbo_context(ctx)->save;
 
-   curPrim = ctx->Driver.CurrentSavePrimitive;
-
-   _save_End();
-   _save_Begin(curPrim);
+   if (save->prim_count == 0) {
+      /* We're not inside a glBegin/End pair, so calling glPrimitiverRestartNV
+       * is an error.
+       */
+      _mesa_compile_error(ctx, GL_INVALID_OPERATION,
+                          "glPrimitiveRestartNV called outside glBegin/End");
+   } else {
+      /* get current primitive mode */
+      GLenum curPrim = save->prims[save->prim_count - 1].mode;
+
+      /* restart primitive */
+      CALL_End(GET_DISPATCH(), ());
+      vbo_save_NotifyBegin(ctx, curPrim);
+   }
 }
 
 
@@ -1069,6 +1165,9 @@ _save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count)
    if (save->out_of_memory)
       return;
 
+   /* Make sure to process any VBO binding changes */
+   _mesa_update_state(ctx);
+
    _ae_map_vbos(ctx);
 
    vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK
@@ -1082,15 +1181,50 @@ _save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count)
 }
 
 
+static void GLAPIENTRY
+_save_OBE_MultiDrawArrays(GLenum mode, const GLint *first,
+                          const GLsizei *count, GLsizei primcount)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   GLint i;
+
+   if (!_mesa_is_valid_prim_mode(ctx, mode)) {
+      _mesa_compile_error(ctx, GL_INVALID_ENUM, "glMultiDrawArrays(mode)");
+      return;
+   }
+
+   if (primcount < 0) {
+      _mesa_compile_error(ctx, GL_INVALID_VALUE,
+                          "glMultiDrawArrays(primcount<0)");
+      return;
+   }
+
+   for (i = 0; i < primcount; i++) {
+      if (count[i] < 0) {
+         _mesa_compile_error(ctx, GL_INVALID_VALUE,
+                             "glMultiDrawArrays(count[i]<0)");
+         return;
+      }
+   }
+
+   for (i = 0; i < primcount; i++) {
+      if (count[i] > 0) {
+         _save_OBE_DrawArrays(mode, first[i], count[i]);
+      }
+   }
+}
+
+
 /* Could do better by copying the arrays and element list intact and
  * then emitting an indexed prim at runtime.
  */
 static void GLAPIENTRY
-_save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
-                       const GLvoid * indices)
+_save_OBE_DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type,
+                                 const GLvoid * indices, GLint basevertex)
 {
    GET_CURRENT_CONTEXT(ctx);
    struct vbo_save_context *save = &vbo_context(ctx)->save;
+   struct gl_buffer_object *indexbuf = ctx->Array.VAO->IndexBufferObj;
    GLint i;
 
    if (!_mesa_is_valid_prim_mode(ctx, mode)) {
@@ -1111,11 +1245,14 @@ _save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
    if (save->out_of_memory)
       return;
 
+   /* Make sure to process any VBO binding changes */
+   _mesa_update_state(ctx);
+
    _ae_map_vbos(ctx);
 
-   if (_mesa_is_bufferobj(ctx->Array.ArrayObj->ElementArrayBufferObj))
+   if (_mesa_is_bufferobj(indexbuf))
       indices =
-         ADD_POINTERS(ctx->Array.ArrayObj->ElementArrayBufferObj->Pointer, indices);
+         ADD_POINTERS(indexbuf->Mappings[MAP_INTERNAL].Pointer, indices);
 
    vbo_save_NotifyBegin(ctx, (mode | VBO_SAVE_PRIM_WEAK |
                               VBO_SAVE_PRIM_NO_CURRENT_UPDATE));
@@ -1123,15 +1260,15 @@ _save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
    switch (type) {
    case GL_UNSIGNED_BYTE:
       for (i = 0; i < count; i++)
-         CALL_ArrayElement(GET_DISPATCH(), (((GLubyte *) indices)[i]));
+         CALL_ArrayElement(GET_DISPATCH(), (basevertex + ((GLubyte *) indices)[i]));
       break;
    case GL_UNSIGNED_SHORT:
       for (i = 0; i < count; i++)
-         CALL_ArrayElement(GET_DISPATCH(), (((GLushort *) indices)[i]));
+         CALL_ArrayElement(GET_DISPATCH(), (basevertex + ((GLushort *) indices)[i]));
       break;
    case GL_UNSIGNED_INT:
       for (i = 0; i < count; i++)
-         CALL_ArrayElement(GET_DISPATCH(), (((GLuint *) indices)[i]));
+         CALL_ArrayElement(GET_DISPATCH(), (basevertex + ((GLuint *) indices)[i]));
       break;
    default:
       _mesa_error(ctx, GL_INVALID_ENUM, "glDrawElements(type)");
@@ -1143,6 +1280,13 @@ _save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
    _ae_unmap_vbos(ctx);
 }
 
+static void GLAPIENTRY
+_save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
+                       const GLvoid * indices)
+{
+   _save_OBE_DrawElementsBaseVertex(mode, count, type, indices, 0);
+}
+
 
 static void GLAPIENTRY
 _save_OBE_DrawRangeElements(GLenum mode, GLuint start, GLuint end,
@@ -1182,7 +1326,7 @@ _save_OBE_DrawRangeElements(GLenum mode, GLuint start, GLuint end,
 
 static void GLAPIENTRY
 _save_OBE_MultiDrawElements(GLenum mode, const GLsizei *count, GLenum type,
-                            const GLvoid **indices, GLsizei primcount)
+                            const GLvoid * const *indices, GLsizei primcount)
 {
    GLsizei i;
 
@@ -1341,6 +1485,19 @@ _save_vtxfmt_init(struct gl_context *ctx)
    vfmt->VertexAttribP3uiv = _save_VertexAttribP3uiv;
    vfmt->VertexAttribP4uiv = _save_VertexAttribP4uiv;
 
+   vfmt->VertexAttribL1d = _save_VertexAttribL1d;
+   vfmt->VertexAttribL2d = _save_VertexAttribL2d;
+   vfmt->VertexAttribL3d = _save_VertexAttribL3d;
+   vfmt->VertexAttribL4d = _save_VertexAttribL4d;
+
+   vfmt->VertexAttribL1dv = _save_VertexAttribL1dv;
+   vfmt->VertexAttribL2dv = _save_VertexAttribL2dv;
+   vfmt->VertexAttribL3dv = _save_VertexAttribL3dv;
+   vfmt->VertexAttribL4dv = _save_VertexAttribL4dv;
+
+   vfmt->VertexAttribL1ui64ARB = _save_VertexAttribL1ui64ARB;
+   vfmt->VertexAttribL1ui64vARB = _save_VertexAttribL1ui64vARB;
+
    /* This will all require us to fallback to saving the list as opcodes:
     */
    vfmt->CallList = _save_CallList;
@@ -1369,7 +1526,9 @@ vbo_initialize_save_dispatch(const struct gl_context *ctx,
                              struct _glapi_table *exec)
 {
    SET_DrawArrays(exec, _save_OBE_DrawArrays);
+   SET_MultiDrawArrays(exec, _save_OBE_MultiDrawArrays);
    SET_DrawElements(exec, _save_OBE_DrawElements);
+   SET_DrawElementsBaseVertex(exec, _save_OBE_DrawElementsBaseVertex);
    SET_DrawRangeElements(exec, _save_OBE_DrawRangeElements);
    SET_MultiDrawElementsEXT(exec, _save_OBE_MultiDrawElements);
    SET_MultiDrawElementsBaseVertex(exec, _save_OBE_MultiDrawElementsBaseVertex);
@@ -1408,7 +1567,7 @@ vbo_save_NewList(struct gl_context *ctx, GLuint list, GLenum mode)
    (void) mode;
 
    if (!save->prim_store)
-      save->prim_store = alloc_prim_store(ctx);
+      save->prim_store = alloc_prim_store();
 
    if (!save->vertex_store)
       save->vertex_store = alloc_vertex_store(ctx);
@@ -1432,14 +1591,14 @@ vbo_save_EndList(struct gl_context *ctx)
       if (save->prim_count > 0) {
          GLint i = save->prim_count - 1;
          ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
-         save->prim[i].end = 0;
-         save->prim[i].count = (save->vert_count - save->prim[i].start);
+         save->prims[i].end = 0;
+         save->prims[i].count = save->vert_count - save->prims[i].start;
       }
 
       /* Make sure this vertex list gets replayed by the "loopback"
        * mechanism:
        */
-      save->dangling_attr_ref = 1;
+      save->dangling_attr_ref = GL_TRUE;
       vbo_save_SaveFlushVertices(ctx);
 
       /* Swap out this vertex format while outside begin/end.  Any color,
@@ -1495,18 +1654,22 @@ vbo_destroy_vertex_list(struct gl_context *ctx, void *data)
 
 
 static void
-vbo_print_vertex_list(struct gl_context *ctx, void *data)
+vbo_print_vertex_list(struct gl_context *ctx, void *data, FILE *f)
 {
    struct vbo_save_vertex_list *node = (struct vbo_save_vertex_list *) data;
    GLuint i;
+   struct gl_buffer_object *buffer = node->vertex_store ?
+      node->vertex_store->bufferobj : NULL;
    (void) ctx;
 
-   printf("VBO-VERTEX-LIST, %u vertices %d primitives, %d vertsize\n",
-          node->count, node->prim_count, node->vertex_size);
+   fprintf(f, "VBO-VERTEX-LIST, %u vertices, %d primitives, %d vertsize, "
+           "buffer %p\n",
+           node->vertex_count, node->prim_count, node->vertex_size,
+           buffer);
 
    for (i = 0; i < node->prim_count; i++) {
-      struct _mesa_prim *prim = &node->prim[i];
-      printf("   prim %d: %s%s %d..%d %s %s\n",
+      struct _mesa_prim *prim = &node->prims[i];
+      fprintf(f, "   prim %d: %s%s %d..%d %s %s\n",
              i,
              _mesa_lookup_prim_by_nr(prim->mode),
              prim->weak ? " (weak)" : "",
@@ -1529,16 +1692,16 @@ _save_current_init(struct gl_context *ctx)
 
    for (i = VBO_ATTRIB_POS; i <= VBO_ATTRIB_GENERIC15; i++) {
       const GLuint j = i - VBO_ATTRIB_POS;
-      ASSERT(j < VERT_ATTRIB_MAX);
+      assert(j < VERT_ATTRIB_MAX);
       save->currentsz[i] = &ctx->ListState.ActiveAttribSize[j];
-      save->current[i] = ctx->ListState.CurrentAttrib[j];
+      save->current[i] = (fi_type *) ctx->ListState.CurrentAttrib[j];
    }
 
    for (i = VBO_ATTRIB_FIRST_MATERIAL; i <= VBO_ATTRIB_LAST_MATERIAL; i++) {
       const GLuint j = i - VBO_ATTRIB_FIRST_MATERIAL;
-      ASSERT(j < MAT_ATTRIB_MAX);
+      assert(j < MAT_ATTRIB_MAX);
       save->currentsz[i] = &ctx->ListState.ActiveMaterialSize[j];
-      save->current[i] = ctx->ListState.CurrentMaterial[j];
+      save->current[i] = (fi_type *) ctx->ListState.CurrentMaterial[j];
    }
 }
 
@@ -1559,8 +1722,6 @@ vbo_save_api_init(struct vbo_save_context *save)
                                vbo_destroy_vertex_list,
                                vbo_print_vertex_list);
 
-   ctx->Driver.NotifySaveBegin = vbo_save_NotifyBegin;
-
    _save_vtxfmt_init(ctx);
    _save_current_init(ctx);
    _mesa_noop_vtxfmt_init(&save->vtxfmt_noop);