#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
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;
struct {
GLuint in;
GLuint out;
} vert_cache[ELT_TABLE_SIZE];
GLuint vertex_size;
GLubyte *dstbuf;
GLubyte *dstptr;
GLuint dstbuf_size;
GLuint dstbuf_nr;
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);
}
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) {
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;
}
static void flush( struct copy_context *copy )
{
GLuint i;
copy->dstib.count = copy->dstelt_nr;
copy->draw( copy->ctx,
copy->dstarray_ptr,
copy->dstprim,
copy->dstprim_nr,
©->dstib,
0,
copy->dstbuf_nr );
copy->dstprim_nr = 0;
copy->dstelt_nr = 0;
copy->dstbuf_nr = 0;
copy->dstptr = copy->dstbuf;
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 = ©->dstprim[copy->dstprim_nr];
prim->mode = mode;
prim->begin = begin_flag;
}
static GLuint elt(struct copy_context *copy, GLuint elt_idx)
{
GLuint elt = copy->srcelt[elt_idx];
GLuint slot = elt & (ELT_TABLE_SIZE-1);
if (copy->vert_cache[slot].in != elt) {
GLubyte *csr = copy->dstptr;
GLuint i;
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));
}
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 = ©->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 = ©->prim[i];
const GLuint start = prim->start;
GLuint first, incr;
switch (prim->mode) {
case GL_LINE_LOOP:
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) {
if (prim->end)
(void)elt(copy, start + 0);
end(copy, prim->end);
}
else {
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) {
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) {
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;
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,
vbo);
copy->varying[j].src_ptr = ADD_POINTERS(vbo->Pointer,
copy->array[i]->Ptr);
copy->dstarray_ptr[i] = ©->varying[j].dstarray;
}
}
if (copy->ib->obj->Name && !copy->ib->obj->Pointer)
ctx->Driver.MapBuffer(ctx,
GL_ARRAY_BUFFER_ARB,
GL_WRITE_ONLY,
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;
}
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;
}
copy->dstbuf = _mesa_malloc(copy->dstbuf_size *
copy->vertex_size);
copy->dstptr = copy->dstbuf;
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 = ©->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 = src->Normalized;
dst->BufferObj = ctx->Array.NullBufferObj;
dst->_MaxElement = copy->dstbuf_size;
offset += copy->varying[i].size;
}
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;
copy->dstib.count = 0;
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;
_mesa_free(copy->translated_elt_buf);
_mesa_free(copy->dstbuf);
_mesa_free(copy->dstelt);
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);
}
if (copy->ib->obj->Name && copy->ib->obj->Pointer) {
ctx->Driver.UnmapBuffer(ctx,
GL_ARRAY_BUFFER_ARB,
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(©, 0, sizeof(copy));
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;
for (i = 0; i < ELT_TABLE_SIZE; i++)
copy.vert_cache[i].in = ~0;
replay_init(©);
replay_elts(©);
replay_finish(©);
}