New files to manage splitting drawing commands
authorkeithw <keithw@keithw-laptop.(none)>
Mon, 15 Jan 2007 11:52:58 +0000 (11:52 +0000)
committerkeithw <keithw@keithw-laptop.(none)>
Mon, 15 Jan 2007 11:52:58 +0000 (11:52 +0000)
src/mesa/vbo/vbo_split.c [new file with mode: 0644]
src/mesa/vbo/vbo_split.h [new file with mode: 0644]
src/mesa/vbo/vbo_split_copy.c [new file with mode: 0644]
src/mesa/vbo/vbo_split_inplace.c [new file with mode: 0644]

diff --git a/src/mesa/vbo/vbo_split.c b/src/mesa/vbo/vbo_split.c
new file mode 100644 (file)
index 0000000..171859a
--- /dev/null
@@ -0,0 +1,161 @@
+
+/*
+ * Mesa 3-D graphics library
+ * Version:  6.5
+ *
+ * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the 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 NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL 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.
+ *
+ * Authors:
+ *    Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+/* Deal with hardware and/or swtnl maximums:
+ * - maximum number of vertices in buffer
+ * - maximum number of elements (maybe zero)
+ *
+ * The maximums may vary with opengl state (eg if a larger hardware
+ * vertex is required in this state, the maximum number of vertices
+ * may be smaller than in another state).
+ *
+ * We want buffer splitting to be a convenience function for the code
+ * actually drawing the primitives rather than a system-wide maximum,
+ * otherwise it is hard to avoid pessimism.  
+ *
+ * For instance, if a driver has no hardware limits on vertex buffer
+ * dimensions, it would not ordinarily want to split vbos.  But if
+ * there is an unexpected fallback, eg memory manager fails to upload
+ * textures, it will want to pass the drawing commands onto swtnl,
+ * which does have limitations.  A convenience function allows swtnl
+ * to split the drawing and vbos internally without imposing its
+ * limitations on drivers which want to use it as a fallback path.
+ */
+
+#include "glheader.h"
+#include "imports.h"
+#include "mtypes.h"
+
+#include "vbo_split.h"
+#include "vbo.h"
+
+/* True if a primitive can be split without copying of vertices, false
+ * otherwise.
+ */
+GLboolean split_prim_inplace(GLenum mode, GLuint *first, GLuint *incr)
+{
+   switch (mode) {
+   case GL_POINTS:
+      *first = 1;
+      *incr = 1;
+      return GL_TRUE;
+   case GL_LINES:
+      *first = 2;
+      *incr = 2;
+      return GL_TRUE;
+   case GL_LINE_STRIP:
+      *first = 2;
+      *incr = 1;
+      return GL_TRUE;
+   case GL_TRIANGLES:
+      *first = 3;
+      *incr = 3;
+      return GL_TRUE;
+   case GL_TRIANGLE_STRIP:
+      *first = 3;
+      *incr = 1;
+      return GL_TRUE;
+   case GL_QUADS:
+      *first = 4;
+      *incr = 4;
+      return GL_TRUE;
+   case GL_QUAD_STRIP:
+      *first = 4;
+      *incr = 2;
+      return GL_TRUE;
+   default:
+      *first = 0;
+      *incr = 1;               /* so that count % incr works */
+      return GL_FALSE;
+   }
+}
+
+
+
+void vbo_split_prims( GLcontext *ctx,
+                     const struct gl_client_array *arrays[],
+                     const struct _mesa_prim *prim,
+                     GLuint nr_prims,
+                     const struct _mesa_index_buffer *ib,
+                     GLuint min_index,
+                     GLuint max_index,
+                     vbo_draw_func draw,
+                     const struct split_limits *limits )
+{
+  
+   if (ib) {
+      if (limits->max_indices == 0) {
+        /* Could traverse the indices, re-emitting vertices in turn.
+         * But it's hard to see why this case would be needed - for
+         * software tnl, it is better to convert to non-indexed
+         * rendering after transformation is complete, as is done in
+         * the t_dd_rendertmp.h templates.  Are there any devices
+         * with hardware tnl that cannot do indexed rendering?
+         *
+         * For now, this path is disabled.
+         */
+        assert(0);
+      }
+      else if (max_index - min_index > limits->max_verts) {
+        /* The vertex buffers are too large for hardware (or the
+         * swtnl module).  Traverse the indices, re-emitting vertices
+         * in turn.  Use a vertex cache to preserve some of the
+         * sharing from the original index list.
+         */
+        vbo_split_copy(ctx, arrays, prim, nr_prims, ib,
+                       draw, limits );
+      }
+      else if (ib->count > limits->max_indices) {
+        /* The index buffer is too large for hardware.  Try to split
+         * on whole-primitive boundaries, otherwise try to split the
+         * individual primitives.
+         */
+        vbo_split_inplace(ctx, arrays, prim, nr_prims, ib,
+                          min_index, max_index, draw, limits );
+      }
+      else {
+        /* Why were we called? */
+        assert(0);
+      }
+   }
+   else {
+      if (max_index - min_index >= limits->max_verts) {
+        /* The vertex buffer is too large for hardware (or the swtnl
+         * module).  Try to split on whole-primitive boundaries,
+         * otherwise try to split the individual primitives.
+         */
+        vbo_split_inplace(ctx, arrays, prim, nr_prims, ib,
+                          min_index, max_index, draw, limits );
+      }
+      else {
+        /* Why were we called? */
+        assert(0);
+      }
+   }
+}
+
diff --git a/src/mesa/vbo/vbo_split.h b/src/mesa/vbo/vbo_split.h
new file mode 100644 (file)
index 0000000..05888d0
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * mesa 3-D graphics library
+ * Version:  6.5
+ *
+ * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the 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 NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL 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.
+ */
+
+/**
+ * \file vbo_context.h
+ * \brief VBO builder module datatypes and definitions.
+ * \author Keith Whitwell
+ */
+
+
+/**
+ * \mainpage The VBO splitter
+ *
+ * This is the private data used internally to the vbo_split_prims()
+ * helper function.  Nobody outside the vbo_split* files needs to
+ * include or know about this structure.
+ */
+
+
+#ifndef _VBO_SPLIT_H
+#define _VBO_SPLIT_H
+
+#include "vbo.h"
+
+
+/* True if a primitive can be split without copying of vertices, false
+ * otherwise.
+ */
+GLboolean split_prim_inplace(GLenum mode, GLuint *first, GLuint *incr);
+
+void vbo_split_inplace( GLcontext *ctx,
+                       const struct gl_client_array *arrays[],
+                       const struct _mesa_prim *prim,
+                       GLuint nr_prims,
+                       const struct _mesa_index_buffer *ib,
+                       GLuint min_index,
+                       GLuint max_index,
+                       vbo_draw_func draw,
+                       const struct split_limits *limits );
+
+/* Requires ib != NULL:
+ */
+void vbo_split_copy( GLcontext *ctx,
+                    const struct gl_client_array *arrays[],
+                    const struct _mesa_prim *prim,
+                    GLuint nr_prims,
+                    const struct _mesa_index_buffer *ib,
+                    vbo_draw_func draw,
+                    const struct split_limits *limits );
+
+#endif
diff --git a/src/mesa/vbo/vbo_split_copy.c b/src/mesa/vbo/vbo_split_copy.c
new file mode 100644 (file)
index 0000000..0adad71
--- /dev/null
@@ -0,0 +1,549 @@
+
+/*
+ * Mesa 3-D graphics library
+ * Version:  6.5
+ *
+ * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the 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 NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL 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.
+ *
+ * Authors:
+ *    Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+/* Split indexed primitives with per-vertex copying.
+ */
+
+#include "glheader.h"
+#include "imports.h"
+#include "macros.h"
+#include "enums.h"
+#include "mtypes.h"
+
+#include "vbo_split.h"
+#include "vbo.h"
+
+
+#define ELT_TABLE_SIZE 16
+
+/* Used for vertex-level splitting of indexed buffers.  Note that
+ * non-indexed primitives may be converted to indexed in some cases
+ * (eg loops, fans) in order to use this splitting path.
+ */
+struct copy_context {
+
+   GLcontext *ctx;
+   const struct gl_client_array **array;
+   const struct _mesa_prim *prim;
+   GLuint nr_prims;
+   const struct _mesa_index_buffer *ib;
+   vbo_draw_func draw;
+
+   const struct split_limits *limits;
+
+   struct {
+      GLuint attr;
+      GLuint size;
+      const struct gl_client_array *array;
+      const GLubyte *src_ptr;
+
+      struct gl_client_array dstarray;
+
+   } varying[VERT_ATTRIB_MAX];
+   GLuint nr_varying;
+
+   const struct gl_client_array *dstarray_ptr[VERT_ATTRIB_MAX];
+   struct _mesa_index_buffer dstib;
+
+   GLuint *translated_elt_buf;
+   const GLuint *srcelt;
+
+   /* A baby hash table to avoid re-emitting (some) duplicate
+    * vertices when splitting indexed primitives.
+    */
+   struct { 
+      GLuint in;
+      GLuint out;
+   } vert_cache[ELT_TABLE_SIZE];
+      
+
+   GLuint vertex_size;
+   GLubyte *dstbuf;
+   GLubyte *dstptr;            /* dstptr == dstbuf + dstelt_max * vertsize */
+   GLuint dstbuf_size; /* in vertices */
+   GLuint dstbuf_nr;           /* count of emitted vertices, also the
+                                * largest value in dstelt.  Our
+                                * MaxIndex.
+                                */
+
+   GLuint *dstelt;
+   GLuint dstelt_nr;
+   GLuint dstelt_size;
+
+#define MAX_PRIM 32
+   struct _mesa_prim dstprim[MAX_PRIM];
+   GLuint dstprim_nr;
+
+};
+
+
+static GLuint type_size( GLenum type )
+{
+   switch(type) {
+   case GL_BYTE: return sizeof(GLbyte);
+   case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
+   case GL_SHORT: return sizeof(GLshort);
+   case GL_UNSIGNED_SHORT: return sizeof(GLushort);
+   case GL_INT: return sizeof(GLint);
+   case GL_UNSIGNED_INT: return sizeof(GLuint);
+   case GL_FLOAT: return sizeof(GLfloat);
+   case GL_DOUBLE: return sizeof(GLdouble);
+   default: return 0;
+   }
+}
+
+static GLuint attr_size( const struct gl_client_array *array )
+{
+   return array->Size * type_size(array->Type);
+}
+
+
+/* Starts returning true slightly before the buffer fills, to ensure
+ * that there is sufficient room for any remaining vertices to finish
+ * off the prim:
+ */
+static GLboolean check_flush( struct copy_context *copy )
+{
+   if (copy->dstbuf_nr + 4 > copy->dstbuf_size)
+      return GL_TRUE;
+
+   if (copy->dstelt_nr + 4 > copy->dstelt_size)
+      return GL_TRUE;
+
+   return GL_FALSE;
+}
+
+static void flush( struct copy_context *copy )
+{
+   GLuint i;
+
+   /* Set some counters: 
+    */
+   copy->dstib.count = copy->dstelt_nr;
+
+   copy->draw( copy->ctx,
+              copy->dstarray_ptr,
+              copy->dstprim,
+              copy->dstprim_nr,
+              &copy->dstib,
+              0,
+              copy->dstbuf_nr );
+
+   /* Reset all pointers: 
+    */
+   copy->dstprim_nr = 0;
+   copy->dstelt_nr = 0;
+   copy->dstbuf_nr = 0;
+   copy->dstptr = copy->dstbuf;
+
+   /* Clear the vertex cache:
+    */
+   for (i = 0; i < ELT_TABLE_SIZE; i++)
+      copy->vert_cache[i].in = ~0;
+}
+
+
+
+static void begin( struct copy_context *copy, GLenum mode, GLboolean begin_flag )
+{
+   struct _mesa_prim *prim = &copy->dstprim[copy->dstprim_nr];
+
+   _mesa_printf("begin %s (%d)\n", _mesa_lookup_enum_by_nr(mode), begin_flag);
+               
+   prim->mode = mode;
+   prim->begin = begin_flag;
+}
+
+
+/* Use a hashtable to attempt to identify recently-emitted vertices
+ * and avoid re-emitting them.
+ */
+static GLuint elt(struct copy_context *copy, GLuint elt_idx)
+{
+   GLuint elt = copy->srcelt[elt_idx];
+   GLuint slot = elt & (ELT_TABLE_SIZE-1);
+
+   _mesa_printf("elt %d\n", elt);
+
+   /* Look up the incoming element in the vertex cache.  Re-emit if
+    * necessary.   
+    */
+   if (copy->vert_cache[slot].in != elt) {
+      GLubyte *csr = copy->dstptr;
+      GLuint i;
+
+      _mesa_printf("  --> emit to dstelt %d\n", copy->dstbuf_nr);
+
+      for (i = 0; i < copy->nr_varying; i++) {
+        const struct gl_client_array *srcarray = copy->varying[i].array;
+        const GLubyte *srcptr = copy->varying[i].src_ptr + elt * srcarray->StrideB;
+
+        memcpy(csr, srcptr, copy->varying[i].size);
+        csr += copy->varying[i].size;
+
+        {
+           const GLuint *f = (const GLuint *)srcptr;
+           GLuint j;
+           _mesa_printf("  varying %d: ", i);
+           for(j = 0; j < copy->varying[i].size / 4; j++)
+              _mesa_printf("%x ", f[j]);
+           _mesa_printf("\n");
+        }
+              
+      }
+
+      copy->vert_cache[slot].in = elt;
+      copy->vert_cache[slot].out = copy->dstbuf_nr++;
+      copy->dstptr += copy->vertex_size;
+
+      assert(csr == copy->dstptr);
+      assert(copy->dstptr == (copy->dstbuf + 
+                                   copy->dstbuf_nr * 
+                                   copy->vertex_size));
+   }
+   else
+      _mesa_printf("  --> reuse vertex\n");
+   
+   _mesa_printf("  --> emit %d\n", copy->vert_cache[slot].out);
+   copy->dstelt[copy->dstelt_nr++] = copy->vert_cache[slot].out;
+   return check_flush(copy);
+}
+
+static void end( struct copy_context *copy, GLboolean end_flag )
+{
+   struct _mesa_prim *prim = &copy->dstprim[copy->dstprim_nr];
+
+   _mesa_printf("end (%d)\n", end_flag);
+
+   prim->end = end_flag;
+   prim->count = copy->dstelt_nr - prim->start;
+
+   if (++copy->dstprim_nr == MAX_PRIM ||
+       check_flush(copy)) 
+      flush(copy);
+}
+
+
+
+static void replay_elts( struct copy_context *copy )
+{
+   GLuint i, j, k;
+   GLboolean split;
+
+   for (i = 0; i < copy->nr_prims; i++) {
+      const struct _mesa_prim *prim = &copy->prim[i];
+      const GLuint start = prim->start;
+      GLuint first, incr;
+
+      switch (prim->mode) {
+        
+      case GL_LINE_LOOP:
+        /* Convert to linestrip and emit the final vertex explicitly,
+         * but only in the resultant strip that requires it.
+         */
+        j = 0;
+        while (j != prim->count) {
+           begin(copy, GL_LINE_STRIP, prim->begin && j == 0);
+
+           for (split = GL_FALSE; j != prim->count && !split; j++)
+              split = elt(copy, start + j);
+
+           if (j == prim->count) {
+              /* Done, emit final line.  Split doesn't matter as
+               * it is always raised a bit early so we can emit
+               * the last verts if necessary!
+               */
+              if (prim->end) 
+                 (void)elt(copy, start + 0);
+
+              end(copy, prim->end);
+           }
+           else {
+              /* Wrap
+               */
+              assert(split);
+              end(copy, 0);
+              j--;
+           }
+        }
+        break;
+
+      case GL_TRIANGLE_FAN:
+      case GL_POLYGON:
+        j = 2;
+        while (j != prim->count) {
+           begin(copy, prim->mode, prim->begin && j == 0);
+
+           split = elt(copy, start+0); 
+           assert(!split);
+
+           split = elt(copy, start+j-1); 
+           assert(!split);
+
+           for (; j != prim->count && !split; j++)
+              split = elt(copy, start+j);
+
+           end(copy, prim->end && j == prim->count);
+
+           if (j != prim->count) {
+              /* Wrapped the primitive, need to repeat some vertices:
+               */
+              j -= 1;
+           }
+        }
+        break;
+
+      default:
+        (void)split_prim_inplace(prim->mode, &first, &incr);
+        
+        j = 0;
+        while (j != prim->count) {
+
+           begin(copy, prim->mode, prim->begin && j == 0);
+
+           split = 0;
+           for (k = 0; k < first; k++, j++)
+              split |= elt(copy, start+j);
+
+           assert(!split);
+
+           for (; j != prim->count && !split; )
+              for (k = 0; k < incr; k++, j++)
+                 split |= elt(copy, start+j);
+
+           end(copy, prim->end && j == prim->count);
+
+           if (j != prim->count) {
+              /* Wrapped the primitive, need to repeat some vertices:
+               */
+              assert(j > first - incr);
+              j -= (first - incr);
+           }
+        }
+        break;
+      }
+   }
+
+   if (copy->dstprim_nr)
+      flush(copy);
+}
+
+
+static void replay_init( struct copy_context *copy )
+{
+   GLcontext *ctx = copy->ctx;
+   GLuint i;
+   GLuint offset;
+
+   /* Make a list of varying attributes and their vbo's.  Also
+    * calculate vertex size.
+    */
+   copy->vertex_size = 0;
+   for (i = 0; i < VERT_ATTRIB_MAX; i++) {
+      struct gl_buffer_object *vbo = copy->array[i]->BufferObj;
+
+      if (copy->array[i]->StrideB == 0) {
+        copy->dstarray_ptr[i] = copy->array[i];
+      }
+      else {
+        GLuint j = copy->nr_varying++;
+        
+        copy->varying[j].attr = i;
+        copy->varying[j].array = copy->array[i];
+        copy->varying[j].size = attr_size(copy->array[i]);
+        copy->vertex_size += attr_size(copy->array[i]);
+      
+        if (vbo->Name && !vbo->Pointer) 
+           ctx->Driver.MapBuffer(ctx,
+                                 GL_ARRAY_BUFFER_ARB, 
+                                 GL_DYNAMIC_DRAW_ARB, /* XXX */
+                                 vbo);
+
+        copy->varying[j].src_ptr = ADD_POINTERS(vbo->Pointer,
+                                                copy->array[i]->Ptr);
+
+        copy->dstarray_ptr[i] = &copy->varying[j].dstarray;
+      }
+   }
+
+   /* There must always be an index buffer.  Currently require the
+    * caller convert non-indexed prims to indexed.  Could alternately
+    * do it internally.
+    */
+   if (copy->ib->obj->Name && !copy->ib->obj->Pointer) 
+      ctx->Driver.MapBuffer(ctx, 
+                           GL_ARRAY_BUFFER_ARB, /* XXX */
+                           GL_DYNAMIC_DRAW_ARB, /* XXX */
+                           copy->ib->obj);
+
+   switch (copy->ib->type) {
+   case GL_UNSIGNED_BYTE:
+      copy->translated_elt_buf = _mesa_malloc(sizeof(GLuint) * copy->ib->count);
+      copy->srcelt = copy->translated_elt_buf;
+      
+      for (i = 0; i < copy->ib->count; i++)
+        copy->translated_elt_buf[i] = ((const GLubyte *)copy->ib->ptr)[i];
+      break;
+
+   case GL_UNSIGNED_SHORT:
+      copy->translated_elt_buf = _mesa_malloc(sizeof(GLuint) * copy->ib->count);
+      copy->srcelt = copy->translated_elt_buf;
+
+      for (i = 0; i < copy->ib->count; i++)
+        copy->translated_elt_buf[i] = ((const GLushort *)copy->ib->ptr)[i];
+      break;
+
+   case GL_UNSIGNED_INT:
+      copy->translated_elt_buf = NULL;
+      copy->srcelt = (const GLuint *)ADD_POINTERS(copy->ib->obj->Pointer, 
+                                                 copy->ib->ptr);
+      break;
+   }
+   
+
+   /* Figure out the maximum allowed vertex buffer size:
+    */
+   if (copy->vertex_size * copy->limits->max_verts <= copy->limits->max_vb_size) {
+      copy->dstbuf_size = copy->limits->max_verts;
+   }
+   else {
+      copy->dstbuf_size = copy->limits->max_vb_size / copy->vertex_size;
+   }
+
+   /* Allocate an output vertex buffer:
+    *
+    * XXX:  This should be a VBO!
+    */
+   copy->dstbuf = _mesa_malloc(copy->dstbuf_size * 
+                              copy->vertex_size);   
+   copy->dstptr = copy->dstbuf;
+
+   /* Setup new vertex arrays to point into the output buffer: 
+    */
+   for (offset = 0, i = 0; i < copy->nr_varying; i++) {
+      const struct gl_client_array *src = copy->varying[i].array;
+      struct gl_client_array *dst = &copy->varying[i].dstarray;
+
+      dst->Size = src->Size;
+      dst->Type = src->Type;
+      dst->Stride = copy->vertex_size;
+      dst->StrideB = copy->vertex_size;
+      dst->Ptr = copy->dstbuf + offset;
+      dst->Enabled = GL_TRUE;
+      dst->Normalized = GL_TRUE;
+      dst->BufferObj = ctx->Array.NullBufferObj;
+      dst->_MaxElement = copy->dstbuf_size; /* may be less! */
+
+      offset += copy->varying[i].size;
+   }
+
+   /* Allocate an output element list:
+    */
+   copy->dstelt_size = MIN2(65536,
+                           copy->ib->count * 2);
+   copy->dstelt_size = MIN2(copy->dstelt_size,
+                           copy->limits->max_indices);
+   copy->dstelt = _mesa_malloc(copy->dstelt_size);
+   copy->dstelt_nr = 0;
+
+   /* Setup the new index buffer to point to the allocated element
+    * list:
+    */
+   copy->dstib.count = 0;      /* duplicates dstelt_nr */
+   copy->dstib.type = GL_UNSIGNED_INT;
+   copy->dstib.obj = ctx->Array.NullBufferObj;
+   copy->dstib.ptr = copy->dstelt;
+   copy->dstib.rebase = 0;     
+}
+
+
+static void replay_finish( struct copy_context *copy )
+{
+   GLcontext *ctx = copy->ctx;
+   GLuint i;
+
+   /* Free our vertex and index buffers: 
+    */
+   _mesa_free(copy->translated_elt_buf);
+   _mesa_free(copy->dstbuf);
+   _mesa_free(copy->dstelt);
+   
+   /* Unmap VBO's 
+    */
+   for (i = 0; i < copy->nr_varying; i++) {
+      struct gl_buffer_object *vbo = copy->varying[i].array->BufferObj;
+
+      if (vbo->Name && vbo->Pointer) 
+        ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER_ARB, vbo);
+   }
+
+   /* Unmap index buffer:
+    */
+   if (copy->ib->obj->Name && copy->ib->obj->Pointer) {
+      ctx->Driver.UnmapBuffer(ctx, 
+                             GL_ARRAY_BUFFER_ARB, /* XXX */
+                             copy->ib->obj);
+   }
+}
+
+void vbo_split_copy( GLcontext *ctx,
+                    const struct gl_client_array *arrays[],
+                    const struct _mesa_prim *prim,
+                    GLuint nr_prims,
+                    const struct _mesa_index_buffer *ib,
+                    vbo_draw_func draw,
+                    const struct split_limits *limits )
+{
+   struct copy_context copy;
+   GLuint i;
+
+   memset(&copy, 0, sizeof(copy));
+
+   /* Require indexed primitives:
+    */
+   assert(ib);
+   
+   copy.ctx = ctx;
+   copy.array = arrays;
+   copy.prim = prim;
+   copy.nr_prims = nr_prims;
+   copy.ib = ib;
+   copy.draw = draw;
+   copy.limits = limits;
+
+
+   /* Clear the vertex cache:
+    */
+   for (i = 0; i < ELT_TABLE_SIZE; i++)
+      copy.vert_cache[i].in = ~0;
+
+
+   replay_init(&copy);
+   replay_elts(&copy);
+   replay_finish(&copy);
+}
diff --git a/src/mesa/vbo/vbo_split_inplace.c b/src/mesa/vbo/vbo_split_inplace.c
new file mode 100644 (file)
index 0000000..66c94ac
--- /dev/null
@@ -0,0 +1,301 @@
+
+/*
+ * Mesa 3-D graphics library
+ * Version:  6.5
+ *
+ * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the 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 NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL 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.
+ *
+ * Authors:
+ *    Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+
+#include "mtypes.h"
+#include "macros.h"
+#include "enums.h"
+#include "vbo_split.h"
+
+
+#define MAX_PRIM 32
+
+/* Used for splitting without copying.
+ */
+struct split_context {
+   GLcontext *ctx;
+   const struct gl_client_array **array;
+   const struct _mesa_prim *prim;
+   GLuint nr_prims;
+   const struct _mesa_index_buffer *ib;
+   GLuint min_index;
+   GLuint max_index;
+   vbo_draw_func draw;
+
+   const struct split_limits *limits;
+
+/*    GLuint out_maxindex; */
+/*    GLuint out_minindex; */
+
+   struct _mesa_prim dstprim[MAX_PRIM];
+   GLuint dstprim_nr;
+};
+
+
+
+
+static void flush_vertex( struct split_context *split )
+{
+   GLint min_index, max_index;
+
+   if (!split->dstprim_nr) 
+      return;
+
+   if (split->ib) {
+      /* This should basically be multipass rendering over the same
+       * unchanging set of VBO's.  Would like the driver not to
+       * re-upload the data, or swtnl not to re-transform the
+       * vertices.
+       */
+      assert(split->max_index - split->min_index < split->limits->max_verts);
+      min_index = split->min_index;
+      max_index = split->max_index;
+   }
+   else {
+      /* Non-indexed rendering.  Cannot assume that the primitives are
+       * ordered by increasing vertex, because of entrypoints like
+       * MultiDrawArrays.
+       */
+      GLuint i;
+      min_index = split->dstprim[0].start;
+      max_index = min_index + split->dstprim[0].count - 1;
+
+      for (i = 1; i < split->dstprim_nr; i++) {
+        GLuint tmp_min = split->dstprim[i].start;
+        GLuint tmp_max = tmp_min + split->dstprim[i].count - 1;
+
+        if (tmp_min < min_index) 
+           min_index = tmp_min;
+
+        if (tmp_max > max_index) 
+           max_index = tmp_max;
+      }
+   }
+
+   assert(max_index >= min_index);
+
+   split->draw( split->ctx, 
+               split->array, 
+               split->dstprim,
+               split->dstprim_nr,
+               NULL,
+               min_index,
+               max_index);
+
+   split->dstprim_nr = 0;
+}
+
+
+static struct _mesa_prim *next_outprim( struct split_context *split )
+{
+   if (split->dstprim_nr == MAX_PRIM-1) {
+      flush_vertex(split);
+   }
+
+   {
+      struct _mesa_prim *prim = &split->dstprim[split->dstprim_nr++];
+      memset(prim, 0, sizeof(*prim));
+      return prim;
+   }
+}
+
+static int align(int value, int alignment)
+{
+   return (value + alignment - 1) & ~(alignment - 1);
+}
+
+
+
+/* Break large primitives into smaller ones.  If not possible, convert
+ * the primitive to indexed and pass to split_elts().
+ */
+static void split_prims( struct split_context *split) 
+{
+   GLuint csr = 0;
+   GLuint i;
+
+   for (i = 0; i < split->nr_prims; i++) {
+      const struct _mesa_prim *prim = &split->prim[i];
+      GLuint first, incr;
+      GLboolean split_inplace = split_prim_inplace(prim->mode, &first, &incr);
+      GLuint count;
+
+      /* Always wrap on an even numbered vertex to avoid problems with
+       * triangle strips.  
+       */
+      GLuint available = align(split->limits->max_verts - csr - 1, 2); 
+      assert(split->limits->max_verts >= csr);
+
+      _mesa_printf("%s: prim %d: %s %d..%d\n", __FUNCTION__,
+                  i,
+                  _mesa_lookup_enum_by_nr(prim->mode),
+                  prim->start, prim->count);
+
+      _mesa_printf("a: available %d\n", available);
+
+      if (prim->count < first)
+        continue;
+      
+      count = prim->count - (prim->count - first) % incr; 
+
+
+      if ((available < count && !split_inplace) || 
+         (available < first && split_inplace)) {
+        flush_vertex(split);
+        csr = 0;
+        available = align(split->limits->max_verts - csr - 1, 2);
+      }
+      
+      _mesa_printf("b: available %d\n", available);
+
+      if (available >= count) {
+        struct _mesa_prim *outprim = next_outprim(split);
+        *outprim = *prim;
+        csr += prim->count;
+        available = align(split->limits->max_verts - csr - 1, 2); 
+      } 
+      else if (0 && split_inplace) {
+        GLuint j, nr;
+
+
+        for (j = 0 ; j < count ; ) {
+           GLuint remaining = count - j;
+           struct _mesa_prim *outprim = next_outprim(split);
+
+           nr = MIN2( available, remaining );
+           
+           nr -= (nr - first) % incr;
+           
+           outprim->mode = prim->mode;
+           outprim->begin = (j == 0 && prim->begin);
+           outprim->end = (nr == remaining && prim->end);
+           outprim->start = prim->start + j;
+           outprim->count = nr;
+           
+           if (nr == remaining) {
+              /* Finished. 
+               */
+              j += nr;         
+              csr += nr;
+              available = align(split->limits->max_verts - csr - 1, 2); 
+           }
+           else {
+              /* Wrapped the primitive: 
+               */
+              _mesa_printf("wrap %d %d\n", nr, first-incr);
+
+              j += nr - (first - incr);
+              flush_vertex(split);
+              csr = 0;
+              available = align(split->limits->max_verts - csr - 1, 2); 
+           }
+        }
+      }
+      else if (split->ib == NULL) {
+        /* XXX: could at least send the first max_verts off from the
+         * inplace buffers.
+         */
+
+        /* else convert to indexed primitive and pass to split_elts,
+         * which will do the necessary copying and turn it back into a
+         * vertex primitive for rendering...
+         */
+        struct _mesa_index_buffer ib;
+        struct _mesa_prim tmpprim;
+        GLuint *elts = malloc(count * sizeof(GLuint));
+        GLuint j;
+        
+        for (j = 0; j < count; j++)
+           elts[j] = prim->start + j;
+
+        ib.count = count;
+        ib.type = GL_UNSIGNED_INT;
+        ib.obj = split->ctx->Array.NullBufferObj;
+        ib.ptr = elts;
+        ib.rebase = 0;         /* ? */
+           
+        tmpprim = *prim;
+        tmpprim.indexed = 1;
+        tmpprim.start = 0;
+        tmpprim.count = count;
+
+        flush_vertex(split);
+
+        vbo_split_copy(split->ctx,
+                       split->array,
+                       &tmpprim, 1, 
+                       &ib,
+                       split->draw,
+                       split->limits);
+           
+        free(elts);
+      }
+      else {
+        flush_vertex(split);
+
+        vbo_split_copy(split->ctx,
+                       split->array,
+                       prim, 1, 
+                       split->ib,
+                       split->draw,
+                       split->limits);
+      }
+   }
+
+   flush_vertex(split);
+}
+
+
+void vbo_split_inplace( GLcontext *ctx,
+                       const struct gl_client_array *arrays[],
+                       const struct _mesa_prim *prim,
+                       GLuint nr_prims,
+                       const struct _mesa_index_buffer *ib,
+                       GLuint min_index,
+                       GLuint max_index,
+                       vbo_draw_func draw,
+                       const struct split_limits *limits )
+{
+   struct split_context split;
+
+   memset(&split, 0, sizeof(split));
+
+   split.ctx = ctx;
+   split.array = arrays;
+   split.prim = prim;
+   split.nr_prims = nr_prims;
+   split.ib = ib;
+   split.min_index = min_index;
+   split.max_index = max_index;
+   split.draw = draw;
+   split.limits = limits;
+
+   split_prims( &split );
+}
+
+