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:
parent
9f1ee5a864
commit
db0eb3a437
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue