New files to manage splitting drawing commands

This commit is contained in:
keithw 2007-01-15 11:52:58 +00:00
parent dd60eaa6d9
commit a38cb37913
4 changed files with 1083 additions and 0 deletions

161
src/mesa/vbo/vbo_split.c Normal file
View file

@ -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);
}
}
}

72
src/mesa/vbo/vbo_split.h Normal file
View file

@ -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

View file

@ -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);
}

View file

@ -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 );
}