vbo: Fix up in-place splitting for non-contiguous/indexed primitives.

The in-place splitting code wasn't dealing with index buffers at all
(and it was being called from vbo_split_prims for too big index
buffers, causing some occasional corruption).

Additionally, it wasn't taking into account primitives arrays with
non-contiguous indices (e.g. given prim[0].start = 0 and prim[1].start
= max_verts, it would happily call back the driver with (max_index -
min_index) still greater than max_verts, causing infinite recursion).

It still doesn't handle too large indexed vertex buffers: use
vbo_split_copy for that.
This commit is contained in:
Francisco Jerez 2010-02-03 03:18:28 -08:00 committed by Keith Whitwell
parent 9f1ee5a864
commit db0eb3a437
1 changed files with 61 additions and 50 deletions

View File

@ -30,12 +30,15 @@
#include "main/mtypes.h" #include "main/mtypes.h"
#include "main/macros.h" #include "main/macros.h"
#include "main/enums.h" #include "main/enums.h"
#include "main/image.h"
#include "vbo_split.h" #include "vbo_split.h"
#define MAX_PRIM 32 #define MAX_PRIM 32
/* Used for splitting without copying. /* 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 split_context {
GLcontext *ctx; GLcontext *ctx;
@ -48,6 +51,7 @@ struct split_context {
vbo_draw_func draw; vbo_draw_func draw;
const struct split_limits *limits; const struct split_limits *limits;
GLuint limit;
struct _mesa_prim dstprim[MAX_PRIM]; struct _mesa_prim dstprim[MAX_PRIM];
GLuint dstprim_nr; GLuint dstprim_nr;
@ -58,38 +62,37 @@ struct split_context {
static void flush_vertex( struct split_context *split ) static void flush_vertex( struct split_context *split )
{ {
GLuint min_index, max_index; struct _mesa_index_buffer ib;
GLuint i; GLuint i;
if (!split->dstprim_nr) if (!split->dstprim_nr)
return; return;
min_index = split->dstprim[0].start; if (split->ib) {
max_index = min_index + split->dstprim[0].count - 1; ib = *split->ib;
for (i = 1; i < split->dstprim_nr; i++) { ib.count = split->max_index - split->min_index + 1;
GLuint tmp_min = split->dstprim[i].start; ib.ptr += split->min_index * _mesa_sizeof_type(ib.type);
GLuint tmp_max = tmp_min + split->dstprim[i].count - 1;
if (tmp_min < min_index) /* Rebase the primitives to save index buffer entries. */
min_index = tmp_min; for (i = 0; i < split->dstprim_nr; i++)
split->dstprim[i].start -= split->min_index;
if (tmp_max > max_index)
max_index = tmp_max;
} }
assert(max_index >= min_index); assert(split->max_index >= split->min_index);
split->draw( split->ctx, split->draw(split->ctx,
split->array, split->array,
split->dstprim, split->dstprim,
split->dstprim_nr, split->dstprim_nr,
NULL, split->ib ? &ib : NULL,
GL_TRUE, !split->ib,
min_index, split->min_index,
max_index); split->max_index);
split->dstprim_nr = 0; split->dstprim_nr = 0;
split->min_index = ~0;
split->max_index = 0;
} }
@ -106,62 +109,67 @@ static struct _mesa_prim *next_outprim( struct split_context *split )
} }
} }
static int align(int value, int alignment) static void update_index_bounds(struct split_context *split,
const struct _mesa_prim *prim)
{ {
return (value + alignment - 1) & ~(alignment - 1); 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 /* Break large primitives into smaller ones. If not possible, convert
* the primitive to indexed and pass to split_elts(). * the primitive to indexed and pass to split_elts().
*/ */
static void split_prims( struct split_context *split) static void split_prims( struct split_context *split)
{ {
GLuint csr = 0;
GLuint i; GLuint i;
for (i = 0; i < split->nr_prims; i++) { for (i = 0; i < split->nr_prims; i++) {
const struct _mesa_prim *prim = &split->prim[i]; const struct _mesa_prim *prim = &split->prim[i];
GLuint first, incr; GLuint first, incr;
GLboolean split_inplace = split_prim_inplace(prim->mode, &first, &incr); GLboolean split_inplace = split_prim_inplace(prim->mode, &first, &incr);
GLuint count; GLuint available = get_max_vertices(split, prim);
GLuint count = prim->count - (prim->count - first) % incr;
/* 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);
if (prim->count < first) if (prim->count < first)
continue; continue;
count = prim->count - (prim->count - first) % incr;
if ((available < count && !split_inplace) ||
if ((available < count && !split_inplace) ||
(available < first && split_inplace)) { (available < first && split_inplace)) {
flush_vertex(split); flush_vertex(split);
csr = 0; available = get_max_vertices(split, prim);
available = align(split->limits->max_verts - csr - 1, 2);
} }
if (available >= count) { if (available >= count) {
struct _mesa_prim *outprim = next_outprim(split); struct _mesa_prim *outprim = next_outprim(split);
*outprim = *prim; *outprim = *prim;
csr += prim->count; update_index_bounds(split, outprim);
available = align(split->limits->max_verts - csr - 1, 2); }
}
else if (split_inplace) { else if (split_inplace) {
GLuint j, nr; GLuint j, nr;
for (j = 0 ; j < count ; ) { for (j = 0 ; j < count ; ) {
GLuint remaining = count - j; GLuint remaining = count - j;
struct _mesa_prim *outprim = next_outprim(split); struct _mesa_prim *outprim = next_outprim(split);
nr = MIN2( available, remaining ); nr = MIN2( available, remaining );
nr -= (nr - first) % incr; nr -= (nr - first) % incr;
outprim->mode = prim->mode; outprim->mode = prim->mode;
@ -169,21 +177,20 @@ static void split_prims( struct split_context *split)
outprim->end = (nr == remaining && prim->end); outprim->end = (nr == remaining && prim->end);
outprim->start = prim->start + j; outprim->start = prim->start + j;
outprim->count = nr; outprim->count = nr;
update_index_bounds(split, outprim);
if (nr == remaining) { if (nr == remaining) {
/* Finished. /* Finished.
*/ */
j += nr; j += nr;
csr += nr;
available = align(split->limits->max_verts - csr - 1, 2);
} }
else { else {
/* Wrapped the primitive: /* Wrapped the primitive:
*/ */
j += nr - (first - incr); j += nr - (first - incr);
flush_vertex(split); flush_vertex(split);
csr = 0; available = get_max_vertices(split, prim);
available = align(split->limits->max_verts - csr - 1, 2);
} }
} }
} }
@ -260,10 +267,14 @@ void vbo_split_inplace( GLcontext *ctx,
split.prim = prim; split.prim = prim;
split.nr_prims = nr_prims; split.nr_prims = nr_prims;
split.ib = ib; split.ib = ib;
split.min_index = min_index;
split.max_index = max_index; /* Empty interval, makes calculations simpler. */
split.min_index = ~0;
split.max_index = 0;
split.draw = draw; split.draw = draw;
split.limits = limits; split.limits = limits;
split.limit = ib ? limits->max_indices : limits->max_verts;
split_prims( &split ); split_prims( &split );
} }