summaryrefslogtreecommitdiffstats
path: root/src/mesa/vbo/vbo_split_copy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mesa/vbo/vbo_split_copy.c')
-rw-r--r--src/mesa/vbo/vbo_split_copy.c551
1 files changed, 551 insertions, 0 deletions
diff --git a/src/mesa/vbo/vbo_split_copy.c b/src/mesa/vbo/vbo_split_copy.c
new file mode 100644
index 00000000000..e142dde6803
--- /dev/null
+++ b/src/mesa/vbo/vbo_split_copy.c
@@ -0,0 +1,551 @@
+
+/*
+ * 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 <[email protected]>
+ */
+
+/* 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;
+
+ if (0)
+ {
+ 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;
+ const GLvoid *srcptr;
+
+ /* 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_WRITE_ONLY, /* 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_WRITE_ONLY, /* XXX */
+ copy->ib->obj);
+
+ srcptr = (const GLubyte *)ADD_POINTERS(copy->ib->obj->Pointer, copy->ib->ptr);
+
+ 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 *)srcptr)[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 *)srcptr)[i];
+ break;
+
+ case GL_UNSIGNED_INT:
+ copy->translated_elt_buf = NULL;
+ copy->srcelt = (const GLuint *)srcptr;
+ 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 + 3);
+ copy->dstelt_size = MIN2(copy->dstelt_size,
+ copy->limits->max_indices);
+ copy->dstelt = _mesa_malloc(sizeof(GLuint) * 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;
+}
+
+
+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);
+}