summaryrefslogtreecommitdiffstats
path: root/src/mesa/tnl
diff options
context:
space:
mode:
authorMathias Fröhlich <[email protected]>2018-03-25 19:16:54 +0200
committerMathias Fröhlich <[email protected]>2018-03-31 06:32:14 +0200
commit6e9f00e3fc6f3b1331031f0995254c768d38ea81 (patch)
tree41db2a496029ecc3b4a57d798d8d63c7c8b26614 /src/mesa/tnl
parent245f9a3977dcc097ded07c535b589b82191d5e94 (diff)
vbo: Move vbo_split into the tnl module.
Move the files, adapt to the naming scheme in tnl, update callers and build system. Reviewed-by: Brian Paul <[email protected]> Signed-off-by: Mathias Fröhlich <[email protected]>
Diffstat (limited to 'src/mesa/tnl')
-rw-r--r--src/mesa/tnl/t_draw.c8
-rw-r--r--src/mesa/tnl/t_rebase.c3
-rw-r--r--src/mesa/tnl/t_rebase.h4
-rw-r--r--src/mesa/tnl/t_split.c160
-rw-r--r--src/mesa/tnl/t_split.h74
-rw-r--r--src/mesa/tnl/t_split_copy.c638
-rw-r--r--src/mesa/tnl/t_split_inplace.c298
-rw-r--r--src/mesa/tnl/tnl.h80
8 files changed, 1258 insertions, 7 deletions
diff --git a/src/mesa/tnl/t_draw.c b/src/mesa/tnl/t_draw.c
index a0fd58432a1..a83b98eede1 100644
--- a/src/mesa/tnl/t_draw.c
+++ b/src/mesa/tnl/t_draw.c
@@ -486,10 +486,10 @@ void _tnl_draw_prims(struct gl_context *ctx,
/* This will split the buffers one way or another and
* recursively call back into this function.
*/
- vbo_split_prims( ctx, arrays, prim, nr_prims, ib,
- 0, max_index + prim->basevertex,
- _tnl_draw_prims,
- &limits );
+ _tnl_split_prims( ctx, arrays, prim, nr_prims, ib,
+ 0, max_index + prim->basevertex,
+ _tnl_draw_prims,
+ &limits );
}
else {
/* May need to map a vertex buffer object for every attribute plus
diff --git a/src/mesa/tnl/t_rebase.c b/src/mesa/tnl/t_rebase.c
index 19e759f44be..d28512423c3 100644
--- a/src/mesa/tnl/t_rebase.c
+++ b/src/mesa/tnl/t_rebase.c
@@ -51,6 +51,7 @@
#include "main/glheader.h"
#include "main/imports.h"
#include "main/mtypes.h"
+#include "vbo/vbo.h"
#include "t_rebase.h"
@@ -108,7 +109,7 @@ void t_rebase_prims( struct gl_context *ctx,
const struct _mesa_index_buffer *ib,
GLuint min_index,
GLuint max_index,
- vbo_draw_func draw )
+ tnl_draw_func draw )
{
struct gl_array_attributes tmp_attribs[VERT_ATTRIB_MAX];
struct gl_vertex_array tmp_arrays[VERT_ATTRIB_MAX];
diff --git a/src/mesa/tnl/t_rebase.h b/src/mesa/tnl/t_rebase.h
index 16a3a2b5a33..ce2e8b0590e 100644
--- a/src/mesa/tnl/t_rebase.h
+++ b/src/mesa/tnl/t_rebase.h
@@ -25,7 +25,7 @@
#ifndef _T_REBASE_H_
#define _T_REBASE_H_
-#include "vbo/vbo.h"
+#include "tnl.h"
void t_rebase_prims( struct gl_context *ctx,
const struct gl_vertex_array *arrays,
@@ -34,6 +34,6 @@ void t_rebase_prims( struct gl_context *ctx,
const struct _mesa_index_buffer *ib,
GLuint min_index,
GLuint max_index,
- vbo_draw_func draw );
+ tnl_draw_func draw );
#endif
diff --git a/src/mesa/tnl/t_split.c b/src/mesa/tnl/t_split.c
new file mode 100644
index 00000000000..b98bd404d52
--- /dev/null
+++ b/src/mesa/tnl/t_split.c
@@ -0,0 +1,160 @@
+
+/*
+ * Mesa 3-D graphics library
+ *
+ * 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
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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]>
+ */
+
+/* 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 "main/glheader.h"
+#include "main/mtypes.h"
+#include "vbo/vbo.h"
+
+#include "t_split.h"
+
+
+/* True if a primitive can be split without copying of vertices, false
+ * otherwise.
+ */
+GLboolean
+_tnl_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
+_tnl_split_prims(struct gl_context *ctx,
+ const struct gl_vertex_array arrays[],
+ const struct _mesa_prim *prim,
+ GLuint nr_prims,
+ const struct _mesa_index_buffer *ib,
+ GLuint min_index,
+ GLuint max_index,
+ tnl_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. 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.
+ */
+ _tnl_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.
+ */
+ _tnl_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.
+ */
+ _tnl_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/tnl/t_split.h b/src/mesa/tnl/t_split.h
new file mode 100644
index 00000000000..ced7d30bdf1
--- /dev/null
+++ b/src/mesa/tnl/t_split.h
@@ -0,0 +1,74 @@
+/*
+ * mesa 3-D graphics library
+ *
+ * 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
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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.
+ */
+
+/**
+ * \brief VBO builder module datatypes and definitions.
+ * \author Keith Whitwell
+ */
+
+
+/**
+ * \mainpage The TNL splitter
+ *
+ * This is the private data used internally to the _tnl_split_prims()
+ * helper function. Nobody outside the _tnl_split* files needs to
+ * include or know about this structure.
+ */
+
+
+#ifndef _TNL_SPLIT_H
+#define _TNL_SPLIT_H
+
+#include "tnl.h"
+
+
+/* True if a primitive can be split without copying of vertices, false
+ * otherwise.
+ */
+GLboolean
+_tnl_split_prim_inplace(GLenum mode, GLuint *first, GLuint *incr);
+
+void
+_tnl_split_inplace(struct gl_context *ctx,
+ const struct gl_vertex_array arrays[],
+ const struct _mesa_prim *prim,
+ GLuint nr_prims,
+ const struct _mesa_index_buffer *ib,
+ GLuint min_index,
+ GLuint max_index,
+ tnl_draw_func draw,
+ const struct split_limits *limits);
+
+/* Requires ib != NULL:
+ */
+void
+_tnl_split_copy(struct gl_context *ctx,
+ const struct gl_vertex_array arrays[],
+ const struct _mesa_prim *prim,
+ GLuint nr_prims,
+ const struct _mesa_index_buffer *ib,
+ tnl_draw_func draw,
+ const struct split_limits *limits);
+
+#endif
diff --git a/src/mesa/tnl/t_split_copy.c b/src/mesa/tnl/t_split_copy.c
new file mode 100644
index 00000000000..f76a470b5ff
--- /dev/null
+++ b/src/mesa/tnl/t_split_copy.c
@@ -0,0 +1,638 @@
+
+/*
+ * Mesa 3-D graphics library
+ *
+ * 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
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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 <stdio.h>
+
+#include "main/glheader.h"
+#include "main/bufferobj.h"
+#include "main/imports.h"
+#include "main/glformats.h"
+#include "main/macros.h"
+#include "main/mtypes.h"
+#include "main/varray.h"
+#include "vbo/vbo.h"
+
+#include "t_split.h"
+#include "tnl.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 {
+ struct gl_context *ctx;
+ const struct gl_vertex_array *array;
+ const struct _mesa_prim *prim;
+ GLuint nr_prims;
+ const struct _mesa_index_buffer *ib;
+ tnl_draw_func draw;
+
+ const struct split_limits *limits;
+
+ struct {
+ GLuint attr;
+ GLuint size;
+ const struct gl_vertex_array *array;
+ const GLubyte *src_ptr;
+
+ struct gl_vertex_buffer_binding dstbinding;
+ struct gl_array_attributes dstattribs;
+
+ } varying[VERT_ATTRIB_MAX];
+ GLuint nr_varying;
+
+ struct gl_vertex_array dstarray[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
+attr_size(const struct gl_array_attributes *attrib)
+{
+ return attrib->Size * _mesa_sizeof_type(attrib->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)
+{
+ GLenum mode = copy->dstprim[copy->dstprim_nr].mode;
+
+ if (GL_TRIANGLE_STRIP == mode &&
+ copy->dstelt_nr & 1) { /* see bug9962 */
+ return GL_FALSE;
+ }
+
+ 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;
+}
+
+
+/**
+ * Dump the parameters/info for a vbo->draw() call.
+ */
+static void
+dump_draw_info(struct gl_context *ctx,
+ const struct gl_vertex_array *arrays,
+ const struct _mesa_prim *prims,
+ GLuint nr_prims,
+ const struct _mesa_index_buffer *ib,
+ GLuint min_index,
+ GLuint max_index)
+{
+ GLuint i, j;
+
+ printf("VBO Draw:\n");
+ for (i = 0; i < nr_prims; i++) {
+ printf("Prim %u of %u\n", i, nr_prims);
+ printf(" Prim mode 0x%x\n", prims[i].mode);
+ printf(" IB: %p\n", (void*) ib);
+ for (j = 0; j < VERT_ATTRIB_MAX; j++) {
+ const struct gl_vertex_array *array = &arrays[j];
+ const struct gl_vertex_buffer_binding *binding
+ = array->BufferBinding;
+ const struct gl_array_attributes *attrib = array->VertexAttrib;
+ const GLubyte *ptr = _mesa_vertex_attrib_address(attrib, binding);
+ printf(" array %d at %p:\n", j, (void*) &arrays[j]);
+ printf(" ptr %p, size %d, type 0x%x, stride %d\n",
+ ptr, attrib->Size, attrib->Type, binding->Stride);
+ if (0) {
+ GLint k = prims[i].start + prims[i].count - 1;
+ GLfloat *last = (GLfloat *) (ptr + binding->Stride * k);
+ printf(" last: %f %f %f\n",
+ last[0], last[1], last[2]);
+ }
+ }
+ }
+}
+
+
+static void
+flush(struct copy_context *copy)
+{
+ struct gl_context *ctx = copy->ctx;
+ GLuint i;
+
+ /* Set some counters:
+ */
+ copy->dstib.count = copy->dstelt_nr;
+
+#if 0
+ dump_draw_info(copy->ctx,
+ copy->dstarray,
+ copy->dstprim,
+ copy->dstprim_nr,
+ &copy->dstib,
+ 0,
+ copy->dstbuf_nr);
+#else
+ (void) dump_draw_info;
+#endif
+
+ copy->draw(ctx,
+ copy->dstarray,
+ copy->dstprim,
+ copy->dstprim_nr,
+ &copy->dstib,
+ GL_TRUE,
+ 0,
+ copy->dstbuf_nr - 1,
+ NULL, 0, NULL);
+
+ /* 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;
+}
+
+
+/**
+ * Called at begin of each primitive during replay.
+ */
+static void
+begin(struct copy_context *copy, GLenum mode, GLboolean begin_flag)
+{
+ struct _mesa_prim *prim = &copy->dstprim[copy->dstprim_nr];
+
+ prim->mode = mode;
+ prim->begin = begin_flag;
+ prim->num_instances = 1;
+}
+
+
+/**
+ * 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] + copy->prim->basevertex;
+ GLuint slot = elt & (ELT_TABLE_SIZE-1);
+
+ /* 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;
+
+ for (i = 0; i < copy->nr_varying; i++) {
+ const struct gl_vertex_array *srcarray = copy->varying[i].array;
+ const struct gl_vertex_buffer_binding* srcbinding
+ = srcarray->BufferBinding;
+ const GLubyte *srcptr
+ = copy->varying[i].src_ptr + elt * srcbinding->Stride;
+
+ memcpy(csr, srcptr, copy->varying[i].size);
+ csr += copy->varying[i].size;
+
+#ifdef NAN_CHECK
+ if (srcarray->Type == GL_FLOAT) {
+ GLuint k;
+ GLfloat *f = (GLfloat *) srcptr;
+ for (k = 0; k < srcarray->Size; k++) {
+ assert(!IS_INF_OR_NAN(f[k]));
+ assert(f[k] <= 1.0e20 && f[k] >= -1.0e20);
+ }
+ }
+#endif
+
+ if (0) {
+ const GLuint *f = (const GLuint *)srcptr;
+ GLuint j;
+ printf(" varying %d: ", i);
+ for (j = 0; j < copy->varying[i].size / 4; j++)
+ printf("%x ", f[j]);
+ 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));
+ }
+
+ copy->dstelt[copy->dstelt_nr++] = copy->vert_cache[slot].out;
+ return check_flush(copy);
+}
+
+
+/**
+ * Called at end of each primitive during replay.
+ */
+static void
+end(struct copy_context *copy, GLboolean end_flag)
+{
+ struct _mesa_prim *prim = &copy->dstprim[copy->dstprim_nr];
+
+ 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)_tnl_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)
+{
+ struct gl_context *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++) {
+ const struct gl_vertex_array *array = &copy->array[i];
+ const struct gl_vertex_buffer_binding *binding = array->BufferBinding;
+
+ if (binding->Stride == 0) {
+ _mesa_copy_vertex_array(&copy->dstarray[i], array);
+ }
+ else {
+ const struct gl_array_attributes *attrib = array->VertexAttrib;
+ struct gl_buffer_object *vbo = binding->BufferObj;
+ const GLubyte *ptr = _mesa_vertex_attrib_address(attrib, binding);
+ GLuint j = copy->nr_varying++;
+
+ copy->varying[j].attr = i;
+ copy->varying[j].array = &copy->array[i];
+ copy->varying[j].size = attr_size(attrib);
+ copy->vertex_size += attr_size(attrib);
+
+ if (_mesa_is_bufferobj(vbo) &&
+ !_mesa_bufferobj_mapped(vbo, MAP_INTERNAL))
+ ctx->Driver.MapBufferRange(ctx, 0, vbo->Size, GL_MAP_READ_BIT, vbo,
+ MAP_INTERNAL);
+
+ copy->varying[j].src_ptr =
+ ADD_POINTERS(vbo->Mappings[MAP_INTERNAL].Pointer, ptr);
+
+ copy->dstarray[i].VertexAttrib = &copy->varying[j].dstattribs;
+ copy->dstarray[i].BufferBinding = &copy->varying[j].dstbinding;
+ }
+ }
+
+ /* There must always be an index buffer. Currently require the
+ * caller convert non-indexed prims to indexed. Could alternately
+ * do it internally.
+ */
+ if (_mesa_is_bufferobj(copy->ib->obj) &&
+ !_mesa_bufferobj_mapped(copy->ib->obj, MAP_INTERNAL))
+ ctx->Driver.MapBufferRange(ctx, 0, copy->ib->obj->Size, GL_MAP_READ_BIT,
+ copy->ib->obj, MAP_INTERNAL);
+
+ srcptr = (const GLubyte *)
+ ADD_POINTERS(copy->ib->obj->Mappings[MAP_INTERNAL].Pointer,
+ copy->ib->ptr);
+
+ switch (copy->ib->index_size) {
+ case 1:
+ copy->translated_elt_buf = 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 2:
+ copy->translated_elt_buf = 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 4:
+ 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 = 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_vertex_array *src = copy->varying[i].array;
+ const struct gl_array_attributes *srcattr = src->VertexAttrib;
+ struct gl_vertex_array *dst = &copy->dstarray[i];
+ struct gl_vertex_buffer_binding *dstbind = &copy->varying[i].dstbinding;
+ struct gl_array_attributes *dstattr = &copy->varying[i].dstattribs;
+
+ dstattr->Size = srcattr->Size;
+ dstattr->Type = srcattr->Type;
+ dstattr->Format = GL_RGBA;
+ dstbind->Stride = copy->vertex_size;
+ dstattr->Ptr = copy->dstbuf + offset;
+ dstattr->Normalized = srcattr->Normalized;
+ dstattr->Integer = srcattr->Integer;
+ dstattr->Doubles = srcattr->Doubles;
+ dstbind->BufferObj = ctx->Shared->NullBufferObj;
+ dstattr->_ElementSize = srcattr->_ElementSize;
+ dst->BufferBinding = dstbind;
+ dst->VertexAttrib = dstattr;
+
+ 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 = 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.index_size = 4;
+ copy->dstib.obj = ctx->Shared->NullBufferObj;
+ copy->dstib.ptr = copy->dstelt;
+}
+
+
+/**
+ * Free up everything allocated during split/replay.
+ */
+static void
+replay_finish(struct copy_context *copy)
+{
+ struct gl_context *ctx = copy->ctx;
+ GLuint i;
+
+ /* Free our vertex and index buffers */
+ free(copy->translated_elt_buf);
+ free(copy->dstbuf);
+ free(copy->dstelt);
+
+ /* Unmap VBO's */
+ for (i = 0; i < copy->nr_varying; i++) {
+ struct gl_buffer_object *vbo =
+ copy->varying[i].array->BufferBinding->BufferObj;
+ if (_mesa_is_bufferobj(vbo) && _mesa_bufferobj_mapped(vbo, MAP_INTERNAL))
+ ctx->Driver.UnmapBuffer(ctx, vbo, MAP_INTERNAL);
+ }
+
+ /* Unmap index buffer */
+ if (_mesa_is_bufferobj(copy->ib->obj) &&
+ _mesa_bufferobj_mapped(copy->ib->obj, MAP_INTERNAL)) {
+ ctx->Driver.UnmapBuffer(ctx, copy->ib->obj, MAP_INTERNAL);
+ }
+}
+
+
+/**
+ * Split VBO into smaller pieces, draw the pieces.
+ */
+void
+_tnl_split_copy(struct gl_context *ctx,
+ const struct gl_vertex_array *arrays,
+ const struct _mesa_prim *prim,
+ GLuint nr_prims,
+ const struct _mesa_index_buffer *ib,
+ tnl_draw_func draw,
+ const struct split_limits *limits)
+{
+ struct copy_context copy;
+ GLuint i, this_nr_prims;
+
+ for (i = 0; i < nr_prims;) {
+ /* Our SW TNL pipeline doesn't handle basevertex yet, so bind_indices
+ * will rebase the elements to the basevertex, and we'll only
+ * emit strings of prims with the same basevertex in one draw call.
+ */
+ for (this_nr_prims = 1; i + this_nr_prims < nr_prims;
+ this_nr_prims++) {
+ if (prim[i].basevertex != prim[i + this_nr_prims].basevertex)
+ break;
+ }
+
+ memset(&copy, 0, sizeof(copy));
+
+ /* Require indexed primitives:
+ */
+ assert(ib);
+
+ copy.ctx = ctx;
+ copy.array = arrays;
+ copy.prim = &prim[i];
+ copy.nr_prims = this_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/tnl/t_split_inplace.c b/src/mesa/tnl/t_split_inplace.c
new file mode 100644
index 00000000000..15a09861c73
--- /dev/null
+++ b/src/mesa/tnl/t_split_inplace.c
@@ -0,0 +1,298 @@
+
+/*
+ * Mesa 3-D graphics library
+ *
+ * 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
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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]>
+ */
+
+
+#include "main/mtypes.h"
+#include "main/macros.h"
+#include "main/enums.h"
+#include "vbo/vbo.h"
+
+#include "t_split.h"
+
+
+#define MAX_PRIM 32
+
+/* Used for splitting without copying. No attempt is made to handle
+ * too large indexed vertex buffers: In general you need to copy to do
+ * that.
+ */
+struct split_context {
+ struct gl_context *ctx;
+ const struct gl_vertex_array *array;
+ const struct _mesa_prim *prim;
+ GLuint nr_prims;
+ const struct _mesa_index_buffer *ib;
+ GLuint min_index;
+ GLuint max_index;
+ tnl_draw_func draw;
+
+ const struct split_limits *limits;
+ GLuint limit;
+
+ struct _mesa_prim dstprim[MAX_PRIM];
+ GLuint dstprim_nr;
+};
+
+
+
+
+static void
+flush_vertex( struct split_context *split)
+{
+ struct gl_context *ctx = split->ctx;
+ struct _mesa_index_buffer ib;
+ GLuint i;
+
+ if (!split->dstprim_nr)
+ return;
+
+ if (split->ib) {
+ ib = *split->ib;
+
+ ib.count = split->max_index - split->min_index + 1;
+ ib.ptr = (const void *)((const char *)ib.ptr +
+ split->min_index * ib.index_size);
+
+ /* Rebase the primitives to save index buffer entries. */
+ for (i = 0; i < split->dstprim_nr; i++)
+ split->dstprim[i].start -= split->min_index;
+ }
+
+ assert(split->max_index >= split->min_index);
+
+ split->draw(ctx,
+ split->array,
+ split->dstprim,
+ split->dstprim_nr,
+ split->ib ? &ib : NULL,
+ !split->ib,
+ split->min_index,
+ split->max_index,
+ NULL, 0, NULL);
+
+ split->dstprim_nr = 0;
+ split->min_index = ~0;
+ split->max_index = 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 void
+update_index_bounds(struct split_context *split,
+ const struct _mesa_prim *prim)
+{
+ split->min_index = MIN2(split->min_index, prim->start);
+ split->max_index = MAX2(split->max_index, prim->start + prim->count - 1);
+}
+
+
+/* Return the maximum amount of vertices that can be emitted for a
+ * primitive starting at 'prim->start', depending on the previous
+ * index bounds.
+ */
+static GLuint
+get_max_vertices(struct split_context *split,
+ const struct _mesa_prim *prim)
+{
+ if ((prim->start > split->min_index &&
+ prim->start - split->min_index >= split->limit) ||
+ (prim->start < split->max_index &&
+ split->max_index - prim->start >= split->limit))
+ /* "prim" starts too far away from the old range. */
+ return 0;
+
+ return MIN2(split->min_index, prim->start) + split->limit - prim->start;
+}
+
+
+/* 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 i;
+
+ for (i = 0; i < split->nr_prims; i++) {
+ const struct _mesa_prim *prim = &split->prim[i];
+ GLuint first, incr;
+ GLboolean split_inplace =
+ _tnl_split_prim_inplace(prim->mode, &first, &incr);
+ GLuint available = get_max_vertices(split, prim);
+ GLuint count = prim->count - (prim->count - first) % incr;
+
+ if (prim->count < first)
+ continue;
+
+ if ((available < count && !split_inplace) ||
+ (available < first && split_inplace)) {
+ flush_vertex(split);
+ available = get_max_vertices(split, prim);
+ }
+
+ if (available >= count) {
+ struct _mesa_prim *outprim = next_outprim(split);
+
+ *outprim = *prim;
+ update_index_bounds(split, outprim);
+ }
+ else if (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;
+ outprim->num_instances = prim->num_instances;
+ outprim->base_instance = prim->base_instance;
+
+ update_index_bounds(split, outprim);
+
+ if (nr == remaining) {
+ /* Finished */
+ j += nr;
+ }
+ else {
+ /* Wrapped the primitive */
+ j += nr - (first - incr);
+ flush_vertex(split);
+ available = get_max_vertices(split, prim);
+ }
+ }
+ }
+ 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.index_size = 4;
+ ib.obj = split->ctx->Shared->NullBufferObj;
+ ib.ptr = elts;
+
+ tmpprim = *prim;
+ tmpprim.indexed = 1;
+ tmpprim.start = 0;
+ tmpprim.count = count;
+ tmpprim.num_instances = 1;
+ tmpprim.base_instance = 0;
+
+ flush_vertex(split);
+
+ _tnl_split_copy(split->ctx,
+ split->array,
+ &tmpprim, 1,
+ &ib,
+ split->draw,
+ split->limits);
+
+ free(elts);
+ }
+ else {
+ flush_vertex(split);
+
+ _tnl_split_copy(split->ctx,
+ split->array,
+ prim, 1,
+ split->ib,
+ split->draw,
+ split->limits);
+ }
+ }
+
+ flush_vertex(split);
+}
+
+
+void
+_tnl_split_inplace(struct gl_context *ctx,
+ const struct gl_vertex_array *arrays,
+ const struct _mesa_prim *prim,
+ GLuint nr_prims,
+ const struct _mesa_index_buffer *ib,
+ GLuint min_index,
+ GLuint max_index,
+ tnl_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;
+
+ /* Empty interval, makes calculations simpler. */
+ split.min_index = ~0;
+ split.max_index = 0;
+
+ split.draw = draw;
+ split.limits = limits;
+ split.limit = ib ? limits->max_indices : limits->max_verts;
+
+ split_prims(&split);
+}
+
+
diff --git a/src/mesa/tnl/tnl.h b/src/mesa/tnl/tnl.h
index e79c4f62048..45052a3a89c 100644
--- a/src/mesa/tnl/tnl.h
+++ b/src/mesa/tnl/tnl.h
@@ -108,4 +108,84 @@ _tnl_RasterPos(struct gl_context *ctx, const GLfloat vObj[4]);
extern void
_tnl_validate_shine_tables( struct gl_context *ctx );
+
+
+/**
+ * For indirect array drawing:
+ *
+ * typedef struct {
+ * GLuint count;
+ * GLuint primCount;
+ * GLuint first;
+ * GLuint baseInstance; // in GL 4.2 and later, must be zero otherwise
+ * } DrawArraysIndirectCommand;
+ *
+ * For indirect indexed drawing:
+ *
+ * typedef struct {
+ * GLuint count;
+ * GLuint primCount;
+ * GLuint firstIndex;
+ * GLint baseVertex;
+ * GLuint baseInstance; // in GL 4.2 and later, must be zero otherwise
+ * } DrawElementsIndirectCommand;
+ */
+
+
+/**
+ * Draw a number of primitives.
+ * \param prims array [nr_prims] describing what to draw (prim type,
+ * vertex count, first index, instance count, etc).
+ * \param arrays array of vertex arrays for draw
+ * \param ib index buffer for indexed drawing, NULL for array drawing
+ * \param index_bounds_valid are min_index and max_index valid?
+ * \param min_index lowest vertex index used
+ * \param max_index highest vertex index used
+ * \param tfb_vertcount if non-null, indicates which transform feedback
+ * object has the vertex count.
+ * \param tfb_stream If called via DrawTransformFeedbackStream, specifies the
+ * vertex stream buffer from which to get the vertex count.
+ * \param indirect If any prims are indirect, this specifies the buffer
+ * to find the "DrawArrays/ElementsIndirectCommand" data.
+ * This may be deprecated in the future
+ */
+typedef void (*tnl_draw_func)(struct gl_context *ctx,
+ const struct gl_vertex_array* arrays,
+ const struct _mesa_prim *prims,
+ GLuint nr_prims,
+ const struct _mesa_index_buffer *ib,
+ GLboolean index_bounds_valid,
+ GLuint min_index,
+ GLuint max_index,
+ struct gl_transform_feedback_object *tfb_vertcount,
+ unsigned tfb_stream,
+ struct gl_buffer_object *indirect);
+
+
+/* Utility function to cope with various constraints on tnl modules or
+ * hardware. This can be used to split an incoming set of arrays and
+ * primitives against the following constraints:
+ * - Maximum number of indices in index buffer.
+ * - Maximum number of vertices referenced by index buffer.
+ * - Maximum hardware vertex buffer size.
+ */
+struct split_limits
+{
+ GLuint max_verts;
+ GLuint max_indices;
+ GLuint max_vb_size; /* bytes */
+};
+
+void
+_tnl_split_prims(struct gl_context *ctx,
+ const struct gl_vertex_array *arrays,
+ const struct _mesa_prim *prim,
+ GLuint nr_prims,
+ const struct _mesa_index_buffer *ib,
+ GLuint min_index,
+ GLuint max_index,
+ tnl_draw_func draw,
+ const struct split_limits *limits);
+
+
#endif