From 01df5d59e56a1b060568f8cad2e89f7eea22fc70 Mon Sep 17 00:00:00 2001 From: marha Date: Mon, 29 Aug 2011 08:51:20 +0200 Subject: xwininfo libX11 libXmu libxcb mesa xserver xkeyboard-config git update 29 aug 2011 --- mesalib/src/mesa/vbo/vbo_exec_api.c | 38 +- mesalib/src/mesa/vbo/vbo_exec_array.c | 60 +- mesalib/src/mesa/vbo/vbo_exec_draw.c | 843 +++++++++++----------- mesalib/src/mesa/vbo/vbo_rebase.c | 10 +- mesalib/src/mesa/vbo/vbo_save_api.c | 11 +- mesalib/src/mesa/vbo/vbo_save_draw.c | 14 +- mesalib/src/mesa/vbo/vbo_split.c | 337 +++++---- mesalib/src/mesa/vbo/vbo_split_copy.c | 1254 ++++++++++++++++----------------- 8 files changed, 1282 insertions(+), 1285 deletions(-) (limited to 'mesalib/src/mesa/vbo') diff --git a/mesalib/src/mesa/vbo/vbo_exec_api.c b/mesalib/src/mesa/vbo/vbo_exec_api.c index 2b8d38ef2..8474c787a 100644 --- a/mesalib/src/mesa/vbo/vbo_exec_api.c +++ b/mesalib/src/mesa/vbo/vbo_exec_api.c @@ -431,6 +431,24 @@ do { \ #include "vbo_attrib_tmp.h" +/** + * Flush (draw) vertices. + * \param unmap - leave VBO unmapped after flushing? + */ +static void +vbo_exec_FlushVertices_internal(struct vbo_exec_context *exec, GLboolean unmap) +{ + if (exec->vtx.vert_count || unmap) { + vbo_exec_vtx_flush( exec, unmap ); + } + + if (exec->vtx.vertex_size) { + vbo_exec_copy_to_current( exec ); + reset_attrfv( exec ); + } +} + + #if FEATURE_beginend @@ -534,24 +552,6 @@ static void GLAPIENTRY vbo_exec_EvalPoint2( GLint i, GLint j ) #endif /* FEATURE_evaluators */ -/** - * Flush (draw) vertices. - * \param unmap - leave VBO unmapped after flushing? - */ -static void -vbo_exec_FlushVertices_internal(struct vbo_exec_context *exec, GLboolean unmap) -{ - if (exec->vtx.vert_count || unmap) { - vbo_exec_vtx_flush( exec, unmap ); - } - - if (exec->vtx.vertex_size) { - vbo_exec_copy_to_current( exec ); - reset_attrfv( exec ); - } -} - - /** * Called via glBegin. */ @@ -947,7 +947,7 @@ void vbo_exec_vtx_destroy( struct vbo_exec_context *exec ) /* Free the vertex buffer. Unmap first if needed. */ if (_mesa_bufferobj_mapped(exec->vtx.bufferobj)) { - ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER, exec->vtx.bufferobj); + ctx->Driver.UnmapBuffer(ctx, exec->vtx.bufferobj); } _mesa_reference_buffer_object(ctx, &exec->vtx.bufferobj, NULL); } diff --git a/mesalib/src/mesa/vbo/vbo_exec_array.c b/mesalib/src/mesa/vbo/vbo_exec_array.c index 32ce0e4a8..18719d5f5 100644 --- a/mesalib/src/mesa/vbo/vbo_exec_array.c +++ b/mesalib/src/mesa/vbo/vbo_exec_array.c @@ -95,10 +95,25 @@ vbo_get_minmax_index(struct gl_context *ctx, GLuint i; if (_mesa_is_bufferobj(ib->obj)) { - const GLvoid *map = - ctx->Driver.MapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER_ARB, - GL_READ_ONLY, ib->obj); - indices = ADD_POINTERS(map, ib->ptr); + unsigned map_size; + + switch (ib->type) { + case GL_UNSIGNED_INT: + map_size = count * sizeof(GLuint); + break; + case GL_UNSIGNED_SHORT: + map_size = count * sizeof(GLushort); + break; + case GL_UNSIGNED_BYTE: + map_size = count * sizeof(GLubyte); + break; + default: + assert(0); + map_size = 0; + } + + indices = ctx->Driver.MapBufferRange(ctx, (GLsizeiptr) ib->ptr, map_size, + GL_MAP_READ_BIT, ib->obj); } else { indices = ib->ptr; } @@ -176,7 +191,7 @@ vbo_get_minmax_index(struct gl_context *ctx, } if (_mesa_is_bufferobj(ib->obj)) { - ctx->Driver.UnmapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER_ARB, ib->obj); + ctx->Driver.UnmapBuffer(ctx, ib->obj); } } @@ -196,8 +211,8 @@ check_array_data(struct gl_context *ctx, struct gl_client_array *array, if (!array->BufferObj->Pointer) { /* need to map now */ array->BufferObj->Pointer = - ctx->Driver.MapBuffer(ctx, GL_ARRAY_BUFFER_ARB, - GL_READ_ONLY, array->BufferObj); + ctx->Driver.MapBufferRange(ctx, 0, array->BufferObj->Size, + GL_MAP_READ_BIT, array->BufferObj); } data = ADD_POINTERS(data, array->BufferObj->Pointer); } @@ -238,7 +253,7 @@ unmap_array_buffer(struct gl_context *ctx, struct gl_client_array *array) if (array->Enabled && _mesa_is_bufferobj(array->BufferObj) && _mesa_bufferobj_mapped(array->BufferObj)) { - ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER_ARB, array->BufferObj); + ctx->Driver.UnmapBuffer(ctx, array->BufferObj); } } @@ -256,10 +271,10 @@ check_draw_elements_data(struct gl_context *ctx, GLsizei count, GLenum elemType, GLint i, k; if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) { - elemMap = ctx->Driver.MapBuffer(ctx, - GL_ELEMENT_ARRAY_BUFFER_ARB, - GL_READ_ONLY, - ctx->Array.ElementArrayBufferObj); + elemMap = ctx->Driver.MapBufferRange(ctx, 0, + ctx->Array.ElementArrayBufferObj->Size, + GL_MAP_READ_BIT, + ctx->Array.ElementArrayBufferObj); elements = ADD_POINTERS(elements, elemMap); } @@ -296,8 +311,7 @@ check_draw_elements_data(struct gl_context *ctx, GLsizei count, GLenum elemType, } if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) { - ctx->Driver.UnmapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER_ARB, - ctx->Array.ElementArrayBufferObj); + ctx->Driver.UnmapBuffer(ctx, ctx->Array.ElementArrayBufferObj); } unmap_array_buffer(ctx, &arrayObj->Vertex); @@ -351,8 +365,8 @@ print_draw_arrays(struct gl_context *ctx, bufName); if (bufName) { - GLubyte *p = ctx->Driver.MapBuffer(ctx, GL_ARRAY_BUFFER_ARB, - GL_READ_ONLY_ARB, bufObj); + GLubyte *p = ctx->Driver.MapBufferRange(ctx, 0, bufObj->Size, + GL_MAP_READ_BIT, bufObj); int offset = (int) (GLintptr) exec->array.inputs[i]->Ptr; float *f = (float *) (p + offset); int *k = (int *) f; @@ -364,7 +378,7 @@ print_draw_arrays(struct gl_context *ctx, for (i = 0; i < n; i++) { printf(" float[%d] = 0x%08x %f\n", i, k[i], f[i]); } - ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER_ARB, bufObj); + ctx->Driver.UnmapBuffer(ctx, bufObj); } } } @@ -715,10 +729,11 @@ vbo_exec_DrawArraysInstanced(GLenum mode, GLint start, GLsizei count, static void dump_element_buffer(struct gl_context *ctx, GLenum type) { - const GLvoid *map = ctx->Driver.MapBuffer(ctx, - GL_ELEMENT_ARRAY_BUFFER_ARB, - GL_READ_ONLY, - ctx->Array.ElementArrayBufferObj); + const GLvoid *map = + ctx->Driver.MapBufferRange(ctx, 0, + ctx->Array.ElementArrayBufferObj->Size, + GL_MAP_READ_BIT, + ctx->Array.ElementArrayBufferObj); switch (type) { case GL_UNSIGNED_BYTE: { @@ -760,8 +775,7 @@ dump_element_buffer(struct gl_context *ctx, GLenum type) ; } - ctx->Driver.UnmapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER_ARB, - ctx->Array.ElementArrayBufferObj); + ctx->Driver.UnmapBuffer(ctx, ctx->Array.ElementArrayBufferObj); } diff --git a/mesalib/src/mesa/vbo/vbo_exec_draw.c b/mesalib/src/mesa/vbo/vbo_exec_draw.c index 98f585792..8ffaaaa48 100644 --- a/mesalib/src/mesa/vbo/vbo_exec_draw.c +++ b/mesalib/src/mesa/vbo/vbo_exec_draw.c @@ -1,427 +1,416 @@ -/* - * Mesa 3-D graphics library - * Version: 7.2 - * - * Copyright (C) 1999-2008 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 - */ - -#include "main/glheader.h" -#include "main/bufferobj.h" -#include "main/compiler.h" -#include "main/enums.h" -#include "main/mfeatures.h" -#include "main/state.h" - -#include "vbo_context.h" - - -#if FEATURE_beginend - - -static void -vbo_exec_debug_verts( struct vbo_exec_context *exec ) -{ - GLuint count = exec->vtx.vert_count; - GLuint i; - - printf("%s: %u vertices %d primitives, %d vertsize\n", - __FUNCTION__, - count, - exec->vtx.prim_count, - exec->vtx.vertex_size); - - for (i = 0 ; i < exec->vtx.prim_count ; i++) { - struct _mesa_prim *prim = &exec->vtx.prim[i]; - printf(" prim %d: %s%s %d..%d %s %s\n", - i, - _mesa_lookup_prim_by_nr(prim->mode), - prim->weak ? " (weak)" : "", - prim->start, - prim->start + prim->count, - prim->begin ? "BEGIN" : "(wrap)", - prim->end ? "END" : "(wrap)"); - } -} - - -/* - * NOTE: Need to have calculated primitives by this point -- do it on the fly. - * NOTE: Old 'parity' issue is gone. - */ -static GLuint -vbo_copy_vertices( struct vbo_exec_context *exec ) -{ - GLuint nr = exec->vtx.prim[exec->vtx.prim_count-1].count; - GLuint ovf, i; - GLuint sz = exec->vtx.vertex_size; - GLfloat *dst = exec->vtx.copied.buffer; - const GLfloat *src = (exec->vtx.buffer_map + - exec->vtx.prim[exec->vtx.prim_count-1].start * - exec->vtx.vertex_size); - - - switch (exec->ctx->Driver.CurrentExecPrimitive) { - case GL_POINTS: - return 0; - case GL_LINES: - ovf = nr&1; - for (i = 0 ; i < ovf ; i++) - memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz * sizeof(GLfloat) ); - return i; - case GL_TRIANGLES: - ovf = nr%3; - for (i = 0 ; i < ovf ; i++) - memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz * sizeof(GLfloat) ); - return i; - case GL_QUADS: - ovf = nr&3; - for (i = 0 ; i < ovf ; i++) - memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz * sizeof(GLfloat) ); - return i; - case GL_LINE_STRIP: - if (nr == 0) { - return 0; - } - else { - memcpy( dst, src+(nr-1)*sz, sz * sizeof(GLfloat) ); - return 1; - } - case GL_LINE_LOOP: - case GL_TRIANGLE_FAN: - case GL_POLYGON: - if (nr == 0) { - return 0; - } - else if (nr == 1) { - memcpy( dst, src+0, sz * sizeof(GLfloat) ); - return 1; - } - else { - memcpy( dst, src+0, sz * sizeof(GLfloat) ); - memcpy( dst+sz, src+(nr-1)*sz, sz * sizeof(GLfloat) ); - return 2; - } - case GL_TRIANGLE_STRIP: - /* no parity issue, but need to make sure the tri is not drawn twice */ - if (nr & 1) { - exec->vtx.prim[exec->vtx.prim_count-1].count--; - } - /* fallthrough */ - case GL_QUAD_STRIP: - switch (nr) { - case 0: - ovf = 0; - break; - case 1: - ovf = 1; - break; - default: - ovf = 2 + (nr & 1); - break; - } - for (i = 0 ; i < ovf ; i++) - memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz * sizeof(GLfloat) ); - return i; - case PRIM_OUTSIDE_BEGIN_END: - return 0; - default: - assert(0); - return 0; - } -} - - - -/* TODO: populate these as the vertex is defined: - */ -static void -vbo_exec_bind_arrays( struct gl_context *ctx ) -{ - struct vbo_context *vbo = vbo_context(ctx); - struct vbo_exec_context *exec = &vbo->exec; - struct gl_client_array *arrays = exec->vtx.arrays; - const GLuint count = exec->vtx.vert_count; - const GLuint *map; - GLuint attr; - GLbitfield varying_inputs = 0x0; - - /* Install the default (ie Current) attributes first, then overlay - * all active ones. - */ - switch (get_program_mode(exec->ctx)) { - case VP_NONE: - for (attr = 0; attr < 16; attr++) { - exec->vtx.inputs[attr] = &vbo->legacy_currval[attr]; - } - for (attr = 0; attr < MAT_ATTRIB_MAX; attr++) { - ASSERT(attr + 16 < Elements(exec->vtx.inputs)); - exec->vtx.inputs[attr + 16] = &vbo->mat_currval[attr]; - } - map = vbo->map_vp_none; - break; - case VP_NV: - case VP_ARB: - /* The aliasing of attributes for NV vertex programs has already - * occurred. NV vertex programs cannot access material values, - * nor attributes greater than VERT_ATTRIB_TEX7. - */ - for (attr = 0; attr < 16; attr++) { - exec->vtx.inputs[attr] = &vbo->legacy_currval[attr]; - ASSERT(attr + 16 < Elements(exec->vtx.inputs)); - exec->vtx.inputs[attr + 16] = &vbo->generic_currval[attr]; - } - map = vbo->map_vp_arb; - - /* check if VERT_ATTRIB_POS is not read but VERT_BIT_GENERIC0 is read. - * In that case we effectively need to route the data from - * glVertexAttrib(0, val) calls to feed into the GENERIC0 input. - */ - if ((ctx->VertexProgram._Current->Base.InputsRead & VERT_BIT_POS) == 0 && - (ctx->VertexProgram._Current->Base.InputsRead & VERT_BIT_GENERIC0)) { - exec->vtx.inputs[16] = exec->vtx.inputs[0]; - exec->vtx.attrsz[16] = exec->vtx.attrsz[0]; - exec->vtx.attrptr[16] = exec->vtx.attrptr[0]; - exec->vtx.attrsz[0] = 0; - } - break; - default: - assert(0); - } - - /* Make all active attributes (including edgeflag) available as - * arrays of floats. - */ - for (attr = 0; attr < VERT_ATTRIB_MAX ; attr++) { - const GLuint src = map[attr]; - - if (exec->vtx.attrsz[src]) { - GLsizeiptr offset = (GLbyte *)exec->vtx.attrptr[src] - - (GLbyte *)exec->vtx.vertex; - - /* override the default array set above */ - ASSERT(attr < Elements(exec->vtx.inputs)); - ASSERT(attr < Elements(exec->vtx.arrays)); /* arrays[] */ - exec->vtx.inputs[attr] = &arrays[attr]; - - if (_mesa_is_bufferobj(exec->vtx.bufferobj)) { - /* a real buffer obj: Ptr is an offset, not a pointer*/ - assert(exec->vtx.bufferobj->Pointer); /* buf should be mapped */ - assert(offset >= 0); - arrays[attr].Ptr = (GLubyte *)exec->vtx.bufferobj->Offset + offset; - } - else { - /* Ptr into ordinary app memory */ - arrays[attr].Ptr = (GLubyte *)exec->vtx.buffer_map + offset; - } - arrays[attr].Size = exec->vtx.attrsz[src]; - arrays[attr].StrideB = exec->vtx.vertex_size * sizeof(GLfloat); - arrays[attr].Stride = exec->vtx.vertex_size * sizeof(GLfloat); - arrays[attr].Type = GL_FLOAT; - arrays[attr].Format = GL_RGBA; - arrays[attr].Enabled = 1; - arrays[attr]._ElementSize = arrays[attr].Size * sizeof(GLfloat); - _mesa_reference_buffer_object(ctx, - &arrays[attr].BufferObj, - exec->vtx.bufferobj); - arrays[attr]._MaxElement = count; /* ??? */ - - varying_inputs |= 1 << attr; - ctx->NewState |= _NEW_ARRAY; - } - } - - _mesa_set_varying_vp_inputs( ctx, varying_inputs ); -} - - -/** - * Unmap the VBO. This is called before drawing. - */ -static void -vbo_exec_vtx_unmap( struct vbo_exec_context *exec ) -{ - GLenum target = GL_ARRAY_BUFFER_ARB; - - if (_mesa_is_bufferobj(exec->vtx.bufferobj)) { - struct gl_context *ctx = exec->ctx; - - if (ctx->Driver.FlushMappedBufferRange) { - GLintptr offset = exec->vtx.buffer_used - exec->vtx.bufferobj->Offset; - GLsizeiptr length = (exec->vtx.buffer_ptr - exec->vtx.buffer_map) * sizeof(float); - - if (length) - ctx->Driver.FlushMappedBufferRange(ctx, target, - offset, length, - exec->vtx.bufferobj); - } - - exec->vtx.buffer_used += (exec->vtx.buffer_ptr - - exec->vtx.buffer_map) * sizeof(float); - - assert(exec->vtx.buffer_used <= VBO_VERT_BUFFER_SIZE); - assert(exec->vtx.buffer_ptr != NULL); - - ctx->Driver.UnmapBuffer(ctx, target, exec->vtx.bufferobj); - exec->vtx.buffer_map = NULL; - exec->vtx.buffer_ptr = NULL; - exec->vtx.max_vert = 0; - } -} - - -/** - * Map the vertex buffer to begin storing glVertex, glColor, etc data. - */ -void -vbo_exec_vtx_map( struct vbo_exec_context *exec ) -{ - struct gl_context *ctx = exec->ctx; - const GLenum target = GL_ARRAY_BUFFER_ARB; - const GLenum access = GL_READ_WRITE_ARB; /* for MapBuffer */ - const GLenum accessRange = GL_MAP_WRITE_BIT | /* for MapBufferRange */ - GL_MAP_INVALIDATE_RANGE_BIT | - GL_MAP_UNSYNCHRONIZED_BIT | - GL_MAP_FLUSH_EXPLICIT_BIT | - MESA_MAP_NOWAIT_BIT; - const GLenum usage = GL_STREAM_DRAW_ARB; - - if (!_mesa_is_bufferobj(exec->vtx.bufferobj)) - return; - - assert(!exec->vtx.buffer_map); - assert(!exec->vtx.buffer_ptr); - - if (VBO_VERT_BUFFER_SIZE > exec->vtx.buffer_used + 1024 && - ctx->Driver.MapBufferRange) { - /* The VBO exists and there's room for more */ - exec->vtx.buffer_map = - (GLfloat *)ctx->Driver.MapBufferRange(ctx, - target, - exec->vtx.buffer_used, - (VBO_VERT_BUFFER_SIZE - - exec->vtx.buffer_used), - accessRange, - exec->vtx.bufferobj); - exec->vtx.buffer_ptr = exec->vtx.buffer_map; - } - - if (!exec->vtx.buffer_map) { - /* Need to allocate a new VBO */ - exec->vtx.buffer_used = 0; - - ctx->Driver.BufferData(ctx, target, - VBO_VERT_BUFFER_SIZE, - NULL, usage, exec->vtx.bufferobj); - - - if (ctx->Driver.MapBufferRange) - exec->vtx.buffer_map = - (GLfloat *)ctx->Driver.MapBufferRange(ctx, target, - 0, VBO_VERT_BUFFER_SIZE, - accessRange, - exec->vtx.bufferobj); - if (!exec->vtx.buffer_map) - exec->vtx.buffer_map = - (GLfloat *)ctx->Driver.MapBuffer(ctx, target, access, exec->vtx.bufferobj); - assert(exec->vtx.buffer_map); - exec->vtx.buffer_ptr = exec->vtx.buffer_map; - } - - if (0) - printf("map %d..\n", exec->vtx.buffer_used); -} - - - -/** - * Execute the buffer and save copied verts. - * \param keep_unmapped if true, leave the VBO unmapped when we're done. - */ -void -vbo_exec_vtx_flush(struct vbo_exec_context *exec, GLboolean keepUnmapped) -{ - if (0) - vbo_exec_debug_verts( exec ); - - if (exec->vtx.prim_count && - exec->vtx.vert_count) { - - exec->vtx.copied.nr = vbo_copy_vertices( exec ); - - if (exec->vtx.copied.nr != exec->vtx.vert_count) { - struct gl_context *ctx = exec->ctx; - - /* Before the update_state() as this may raise _NEW_ARRAY - * from _mesa_set_varying_vp_inputs(). - */ - vbo_exec_bind_arrays( ctx ); - - if (ctx->NewState) - _mesa_update_state( ctx ); - - if (_mesa_is_bufferobj(exec->vtx.bufferobj)) { - vbo_exec_vtx_unmap( exec ); - } - - if (0) - printf("%s %d %d\n", __FUNCTION__, exec->vtx.prim_count, - exec->vtx.vert_count); - - vbo_context(ctx)->draw_prims( ctx, - exec->vtx.inputs, - exec->vtx.prim, - exec->vtx.prim_count, - NULL, - GL_TRUE, - 0, - exec->vtx.vert_count - 1); - - /* If using a real VBO, get new storage -- unless asked not to. - */ - if (_mesa_is_bufferobj(exec->vtx.bufferobj) && !keepUnmapped) { - vbo_exec_vtx_map( exec ); - } - } - } - - /* May have to unmap explicitly if we didn't draw: - */ - if (keepUnmapped && - _mesa_is_bufferobj(exec->vtx.bufferobj) && - exec->vtx.buffer_map) { - vbo_exec_vtx_unmap( exec ); - } - - if (keepUnmapped || exec->vtx.vertex_size == 0) - exec->vtx.max_vert = 0; - else - exec->vtx.max_vert = ((VBO_VERT_BUFFER_SIZE - exec->vtx.buffer_used) / - (exec->vtx.vertex_size * sizeof(GLfloat))); - - exec->vtx.buffer_ptr = exec->vtx.buffer_map; - exec->vtx.prim_count = 0; - exec->vtx.vert_count = 0; -} - - -#endif /* FEATURE_beginend */ +/* + * Mesa 3-D graphics library + * Version: 7.2 + * + * Copyright (C) 1999-2008 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 + */ + +#include "main/glheader.h" +#include "main/bufferobj.h" +#include "main/compiler.h" +#include "main/enums.h" +#include "main/mfeatures.h" +#include "main/state.h" + +#include "vbo_context.h" + + +#if FEATURE_beginend + + +static void +vbo_exec_debug_verts( struct vbo_exec_context *exec ) +{ + GLuint count = exec->vtx.vert_count; + GLuint i; + + printf("%s: %u vertices %d primitives, %d vertsize\n", + __FUNCTION__, + count, + exec->vtx.prim_count, + exec->vtx.vertex_size); + + for (i = 0 ; i < exec->vtx.prim_count ; i++) { + struct _mesa_prim *prim = &exec->vtx.prim[i]; + printf(" prim %d: %s%s %d..%d %s %s\n", + i, + _mesa_lookup_prim_by_nr(prim->mode), + prim->weak ? " (weak)" : "", + prim->start, + prim->start + prim->count, + prim->begin ? "BEGIN" : "(wrap)", + prim->end ? "END" : "(wrap)"); + } +} + + +/* + * NOTE: Need to have calculated primitives by this point -- do it on the fly. + * NOTE: Old 'parity' issue is gone. + */ +static GLuint +vbo_copy_vertices( struct vbo_exec_context *exec ) +{ + GLuint nr = exec->vtx.prim[exec->vtx.prim_count-1].count; + GLuint ovf, i; + GLuint sz = exec->vtx.vertex_size; + GLfloat *dst = exec->vtx.copied.buffer; + const GLfloat *src = (exec->vtx.buffer_map + + exec->vtx.prim[exec->vtx.prim_count-1].start * + exec->vtx.vertex_size); + + + switch (exec->ctx->Driver.CurrentExecPrimitive) { + case GL_POINTS: + return 0; + case GL_LINES: + ovf = nr&1; + for (i = 0 ; i < ovf ; i++) + memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz * sizeof(GLfloat) ); + return i; + case GL_TRIANGLES: + ovf = nr%3; + for (i = 0 ; i < ovf ; i++) + memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz * sizeof(GLfloat) ); + return i; + case GL_QUADS: + ovf = nr&3; + for (i = 0 ; i < ovf ; i++) + memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz * sizeof(GLfloat) ); + return i; + case GL_LINE_STRIP: + if (nr == 0) { + return 0; + } + else { + memcpy( dst, src+(nr-1)*sz, sz * sizeof(GLfloat) ); + return 1; + } + case GL_LINE_LOOP: + case GL_TRIANGLE_FAN: + case GL_POLYGON: + if (nr == 0) { + return 0; + } + else if (nr == 1) { + memcpy( dst, src+0, sz * sizeof(GLfloat) ); + return 1; + } + else { + memcpy( dst, src+0, sz * sizeof(GLfloat) ); + memcpy( dst+sz, src+(nr-1)*sz, sz * sizeof(GLfloat) ); + return 2; + } + case GL_TRIANGLE_STRIP: + /* no parity issue, but need to make sure the tri is not drawn twice */ + if (nr & 1) { + exec->vtx.prim[exec->vtx.prim_count-1].count--; + } + /* fallthrough */ + case GL_QUAD_STRIP: + switch (nr) { + case 0: + ovf = 0; + break; + case 1: + ovf = 1; + break; + default: + ovf = 2 + (nr & 1); + break; + } + for (i = 0 ; i < ovf ; i++) + memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz * sizeof(GLfloat) ); + return i; + case PRIM_OUTSIDE_BEGIN_END: + return 0; + default: + assert(0); + return 0; + } +} + + + +/* TODO: populate these as the vertex is defined: + */ +static void +vbo_exec_bind_arrays( struct gl_context *ctx ) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct gl_client_array *arrays = exec->vtx.arrays; + const GLuint count = exec->vtx.vert_count; + const GLuint *map; + GLuint attr; + GLbitfield varying_inputs = 0x0; + + /* Install the default (ie Current) attributes first, then overlay + * all active ones. + */ + switch (get_program_mode(exec->ctx)) { + case VP_NONE: + for (attr = 0; attr < 16; attr++) { + exec->vtx.inputs[attr] = &vbo->legacy_currval[attr]; + } + for (attr = 0; attr < MAT_ATTRIB_MAX; attr++) { + ASSERT(attr + 16 < Elements(exec->vtx.inputs)); + exec->vtx.inputs[attr + 16] = &vbo->mat_currval[attr]; + } + map = vbo->map_vp_none; + break; + case VP_NV: + case VP_ARB: + /* The aliasing of attributes for NV vertex programs has already + * occurred. NV vertex programs cannot access material values, + * nor attributes greater than VERT_ATTRIB_TEX7. + */ + for (attr = 0; attr < 16; attr++) { + exec->vtx.inputs[attr] = &vbo->legacy_currval[attr]; + ASSERT(attr + 16 < Elements(exec->vtx.inputs)); + exec->vtx.inputs[attr + 16] = &vbo->generic_currval[attr]; + } + map = vbo->map_vp_arb; + + /* check if VERT_ATTRIB_POS is not read but VERT_BIT_GENERIC0 is read. + * In that case we effectively need to route the data from + * glVertexAttrib(0, val) calls to feed into the GENERIC0 input. + */ + if ((ctx->VertexProgram._Current->Base.InputsRead & VERT_BIT_POS) == 0 && + (ctx->VertexProgram._Current->Base.InputsRead & VERT_BIT_GENERIC0)) { + exec->vtx.inputs[16] = exec->vtx.inputs[0]; + exec->vtx.attrsz[16] = exec->vtx.attrsz[0]; + exec->vtx.attrptr[16] = exec->vtx.attrptr[0]; + exec->vtx.attrsz[0] = 0; + } + break; + default: + assert(0); + } + + /* Make all active attributes (including edgeflag) available as + * arrays of floats. + */ + for (attr = 0; attr < VERT_ATTRIB_MAX ; attr++) { + const GLuint src = map[attr]; + + if (exec->vtx.attrsz[src]) { + GLsizeiptr offset = (GLbyte *)exec->vtx.attrptr[src] - + (GLbyte *)exec->vtx.vertex; + + /* override the default array set above */ + ASSERT(attr < Elements(exec->vtx.inputs)); + ASSERT(attr < Elements(exec->vtx.arrays)); /* arrays[] */ + exec->vtx.inputs[attr] = &arrays[attr]; + + if (_mesa_is_bufferobj(exec->vtx.bufferobj)) { + /* a real buffer obj: Ptr is an offset, not a pointer*/ + assert(exec->vtx.bufferobj->Pointer); /* buf should be mapped */ + assert(offset >= 0); + arrays[attr].Ptr = (GLubyte *)exec->vtx.bufferobj->Offset + offset; + } + else { + /* Ptr into ordinary app memory */ + arrays[attr].Ptr = (GLubyte *)exec->vtx.buffer_map + offset; + } + arrays[attr].Size = exec->vtx.attrsz[src]; + arrays[attr].StrideB = exec->vtx.vertex_size * sizeof(GLfloat); + arrays[attr].Stride = exec->vtx.vertex_size * sizeof(GLfloat); + arrays[attr].Type = GL_FLOAT; + arrays[attr].Format = GL_RGBA; + arrays[attr].Enabled = 1; + arrays[attr]._ElementSize = arrays[attr].Size * sizeof(GLfloat); + _mesa_reference_buffer_object(ctx, + &arrays[attr].BufferObj, + exec->vtx.bufferobj); + arrays[attr]._MaxElement = count; /* ??? */ + + varying_inputs |= 1 << attr; + ctx->NewState |= _NEW_ARRAY; + } + } + + _mesa_set_varying_vp_inputs( ctx, varying_inputs ); +} + + +/** + * Unmap the VBO. This is called before drawing. + */ +static void +vbo_exec_vtx_unmap( struct vbo_exec_context *exec ) +{ + if (_mesa_is_bufferobj(exec->vtx.bufferobj)) { + struct gl_context *ctx = exec->ctx; + + if (ctx->Driver.FlushMappedBufferRange) { + GLintptr offset = exec->vtx.buffer_used - exec->vtx.bufferobj->Offset; + GLsizeiptr length = (exec->vtx.buffer_ptr - exec->vtx.buffer_map) * sizeof(float); + + if (length) + ctx->Driver.FlushMappedBufferRange(ctx, offset, length, + exec->vtx.bufferobj); + } + + exec->vtx.buffer_used += (exec->vtx.buffer_ptr - + exec->vtx.buffer_map) * sizeof(float); + + assert(exec->vtx.buffer_used <= VBO_VERT_BUFFER_SIZE); + assert(exec->vtx.buffer_ptr != NULL); + + ctx->Driver.UnmapBuffer(ctx, exec->vtx.bufferobj); + exec->vtx.buffer_map = NULL; + exec->vtx.buffer_ptr = NULL; + exec->vtx.max_vert = 0; + } +} + + +/** + * Map the vertex buffer to begin storing glVertex, glColor, etc data. + */ +void +vbo_exec_vtx_map( struct vbo_exec_context *exec ) +{ + struct gl_context *ctx = exec->ctx; + const GLenum accessRange = GL_MAP_WRITE_BIT | /* for MapBufferRange */ + GL_MAP_INVALIDATE_RANGE_BIT | + GL_MAP_UNSYNCHRONIZED_BIT | + GL_MAP_FLUSH_EXPLICIT_BIT | + MESA_MAP_NOWAIT_BIT; + const GLenum usage = GL_STREAM_DRAW_ARB; + + if (!_mesa_is_bufferobj(exec->vtx.bufferobj)) + return; + + assert(!exec->vtx.buffer_map); + assert(!exec->vtx.buffer_ptr); + + if (VBO_VERT_BUFFER_SIZE > exec->vtx.buffer_used + 1024) { + /* The VBO exists and there's room for more */ + exec->vtx.buffer_map = + (GLfloat *)ctx->Driver.MapBufferRange(ctx, + exec->vtx.buffer_used, + (VBO_VERT_BUFFER_SIZE - + exec->vtx.buffer_used), + accessRange, + exec->vtx.bufferobj); + exec->vtx.buffer_ptr = exec->vtx.buffer_map; + } + + if (!exec->vtx.buffer_map) { + /* Need to allocate a new VBO */ + exec->vtx.buffer_used = 0; + + ctx->Driver.BufferData(ctx, GL_ARRAY_BUFFER_ARB, + VBO_VERT_BUFFER_SIZE, + NULL, usage, exec->vtx.bufferobj); + + + exec->vtx.buffer_map = + (GLfloat *)ctx->Driver.MapBufferRange(ctx, + 0, VBO_VERT_BUFFER_SIZE, + accessRange, + exec->vtx.bufferobj); + assert(exec->vtx.buffer_map); + exec->vtx.buffer_ptr = exec->vtx.buffer_map; + } + + if (0) + printf("map %d..\n", exec->vtx.buffer_used); +} + + + +/** + * Execute the buffer and save copied verts. + * \param keep_unmapped if true, leave the VBO unmapped when we're done. + */ +void +vbo_exec_vtx_flush(struct vbo_exec_context *exec, GLboolean keepUnmapped) +{ + if (0) + vbo_exec_debug_verts( exec ); + + if (exec->vtx.prim_count && + exec->vtx.vert_count) { + + exec->vtx.copied.nr = vbo_copy_vertices( exec ); + + if (exec->vtx.copied.nr != exec->vtx.vert_count) { + struct gl_context *ctx = exec->ctx; + + /* Before the update_state() as this may raise _NEW_ARRAY + * from _mesa_set_varying_vp_inputs(). + */ + vbo_exec_bind_arrays( ctx ); + + if (ctx->NewState) + _mesa_update_state( ctx ); + + if (_mesa_is_bufferobj(exec->vtx.bufferobj)) { + vbo_exec_vtx_unmap( exec ); + } + + if (0) + printf("%s %d %d\n", __FUNCTION__, exec->vtx.prim_count, + exec->vtx.vert_count); + + vbo_context(ctx)->draw_prims( ctx, + exec->vtx.inputs, + exec->vtx.prim, + exec->vtx.prim_count, + NULL, + GL_TRUE, + 0, + exec->vtx.vert_count - 1); + + /* If using a real VBO, get new storage -- unless asked not to. + */ + if (_mesa_is_bufferobj(exec->vtx.bufferobj) && !keepUnmapped) { + vbo_exec_vtx_map( exec ); + } + } + } + + /* May have to unmap explicitly if we didn't draw: + */ + if (keepUnmapped && + _mesa_is_bufferobj(exec->vtx.bufferobj) && + exec->vtx.buffer_map) { + vbo_exec_vtx_unmap( exec ); + } + + if (keepUnmapped || exec->vtx.vertex_size == 0) + exec->vtx.max_vert = 0; + else + exec->vtx.max_vert = ((VBO_VERT_BUFFER_SIZE - exec->vtx.buffer_used) / + (exec->vtx.vertex_size * sizeof(GLfloat))); + + exec->vtx.buffer_ptr = exec->vtx.buffer_map; + exec->vtx.prim_count = 0; + exec->vtx.vert_count = 0; +} + + +#endif /* FEATURE_beginend */ diff --git a/mesalib/src/mesa/vbo/vbo_rebase.c b/mesalib/src/mesa/vbo/vbo_rebase.c index 1de290ff6..a1eab752a 100644 --- a/mesalib/src/mesa/vbo/vbo_rebase.c +++ b/mesalib/src/mesa/vbo/vbo_rebase.c @@ -159,10 +159,8 @@ void vbo_rebase_prims( struct gl_context *ctx, void *ptr; if (map_ib) - ctx->Driver.MapBuffer(ctx, - GL_ELEMENT_ARRAY_BUFFER, - GL_READ_ONLY_ARB, - ib->obj); + ctx->Driver.MapBufferRange(ctx, 0, ib->obj->Size, GL_MAP_READ_BIT, + ib->obj); ptr = ADD_POINTERS(ib->obj->Pointer, ib->ptr); @@ -183,9 +181,7 @@ void vbo_rebase_prims( struct gl_context *ctx, } if (map_ib) - ctx->Driver.UnmapBuffer(ctx, - GL_ELEMENT_ARRAY_BUFFER, - ib->obj); + ctx->Driver.UnmapBuffer(ctx, ib->obj); tmp_ib.obj = ctx->Shared->NullBufferObj; tmp_ib.ptr = tmp_indices; diff --git a/mesalib/src/mesa/vbo/vbo_save_api.c b/mesalib/src/mesa/vbo/vbo_save_api.c index 9041f791e..ad36e9332 100644 --- a/mesalib/src/mesa/vbo/vbo_save_api.c +++ b/mesalib/src/mesa/vbo/vbo_save_api.c @@ -232,11 +232,10 @@ map_vertex_store(struct gl_context *ctx, assert(vertex_store->bufferobj); assert(!vertex_store->buffer); vertex_store->buffer = - (GLfloat *) ctx->Driver.MapBuffer(ctx, - GL_ARRAY_BUFFER_ARB, /* not used */ - GL_WRITE_ONLY, /* not used */ - vertex_store-> - bufferobj); + (GLfloat *) ctx->Driver.MapBufferRange(ctx, 0, + vertex_store->bufferobj->Size, + GL_MAP_WRITE_BIT, /* not used */ + vertex_store->bufferobj); assert(vertex_store->buffer); return vertex_store->buffer + vertex_store->used; @@ -247,7 +246,7 @@ static void unmap_vertex_store(struct gl_context *ctx, struct vbo_save_vertex_store *vertex_store) { - ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER_ARB, vertex_store->bufferobj); + ctx->Driver.UnmapBuffer(ctx, vertex_store->bufferobj); vertex_store->buffer = NULL; } diff --git a/mesalib/src/mesa/vbo/vbo_save_draw.c b/mesalib/src/mesa/vbo/vbo_save_draw.c index a37af73e0..6cda831aa 100644 --- a/mesalib/src/mesa/vbo/vbo_save_draw.c +++ b/mesalib/src/mesa/vbo/vbo_save_draw.c @@ -70,7 +70,7 @@ _playback_copy_to_current(struct gl_context *ctx, else offset = node->buffer_offset; - ctx->Driver.GetBufferSubData( ctx, 0, offset, + ctx->Driver.GetBufferSubData( ctx, offset, node->vertex_size * sizeof(GLfloat), data, node->vertex_store->bufferobj ); @@ -217,10 +217,11 @@ static void vbo_save_loopback_vertex_list(struct gl_context *ctx, const struct vbo_save_vertex_list *list) { - const char *buffer = ctx->Driver.MapBuffer(ctx, - GL_ARRAY_BUFFER_ARB, - GL_READ_ONLY, /* ? */ - list->vertex_store->bufferobj); + const char *buffer = + ctx->Driver.MapBufferRange(ctx, 0, + list->vertex_store->bufferobj->Size, + GL_MAP_READ_BIT, /* ? */ + list->vertex_store->bufferobj); vbo_loopback_vertex_list(ctx, (const GLfloat *)(buffer + list->buffer_offset), @@ -230,8 +231,7 @@ vbo_save_loopback_vertex_list(struct gl_context *ctx, list->wrap_count, list->vertex_size); - ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER_ARB, - list->vertex_store->bufferobj); + ctx->Driver.UnmapBuffer(ctx, list->vertex_store->bufferobj); } diff --git a/mesalib/src/mesa/vbo/vbo_split.c b/mesalib/src/mesa/vbo/vbo_split.c index e28f9afcf..c933a3f9a 100644 --- a/mesalib/src/mesa/vbo/vbo_split.c +++ b/mesalib/src/mesa/vbo/vbo_split.c @@ -1,169 +1,168 @@ - -/* - * 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 - */ - -/* 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/imports.h" -#include "main/mtypes.h" -#include "main/macros.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( struct gl_context *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 ) -{ - GLint max_basevertex = prim->basevertex; - GLuint i; - - for (i = 1; i < nr_prims; i++) - max_basevertex = MAX2(max_basevertex, prim[i].basevertex); - - /* XXX max_basevertex is computed but not used, why? */ - - 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); - } - } -} - + +/* + * 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 + */ + +/* 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/imports.h" +#include "main/mtypes.h" +#include "main/macros.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( struct gl_context *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 ) +{ + GLint max_basevertex = prim->basevertex; + GLuint i; + + for (i = 1; i < nr_prims; i++) + max_basevertex = MAX2(max_basevertex, prim[i].basevertex); + + /* XXX max_basevertex is computed but not used, why? */ + + 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. + */ + 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); + } + } +} + diff --git a/mesalib/src/mesa/vbo/vbo_split_copy.c b/mesalib/src/mesa/vbo/vbo_split_copy.c index 3a869d712..40906e389 100644 --- a/mesalib/src/mesa/vbo/vbo_split_copy.c +++ b/mesalib/src/mesa/vbo/vbo_split_copy.c @@ -1,627 +1,627 @@ - -/* - * 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 - */ - -/* Split indexed primitives with per-vertex copying. - */ - -#include "main/glheader.h" -#include "main/bufferobj.h" -#include "main/imports.h" -#include "main/image.h" -#include "main/macros.h" -#include "main/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 { - - struct gl_context *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 attr_size( const struct gl_client_array *array ) -{ - return array->Size * _mesa_sizeof_type(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 ) -{ - 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_client_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++) { - printf(" array %d at %p:\n", j, (void*) arrays[j]); - printf(" enabled %d, ptr %p, size %d, type 0x%x, stride %d\n", - arrays[j]->Enabled, arrays[j]->Ptr, - arrays[j]->Size, arrays[j]->Type, arrays[j]->StrideB); - if (0) { - GLint k = prims[i].start + prims[i].count - 1; - GLfloat *last = (GLfloat *) (arrays[j]->Ptr + arrays[j]->Stride * k); - printf(" last: %f %f %f\n", - last[0], last[1], last[2]); - } - } - } -} - - -static void -flush( struct copy_context *copy ) -{ - GLuint i; - - /* Set some counters: - */ - copy->dstib.count = copy->dstelt_nr; - -#if 0 - dump_draw_info(copy->ctx, - copy->dstarray_ptr, - copy->dstprim, - copy->dstprim_nr, - ©->dstib, - 0, - copy->dstbuf_nr); -#else - (void) dump_draw_info; -#endif - - copy->draw( copy->ctx, - copy->dstarray_ptr, - copy->dstprim, - copy->dstprim_nr, - ©->dstib, - GL_TRUE, - 0, - copy->dstbuf_nr - 1 ); - - /* 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 = ©->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]; - GLuint slot = elt & (ELT_TABLE_SIZE-1); - -/* 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; - -/* 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; - -#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)); - } -/* else */ -/* printf(" --> reuse vertex\n"); */ - -/* printf(" --> emit %d\n", copy->vert_cache[slot].out); */ - 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 = ©->dstprim[copy->dstprim_nr]; - -/* 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 = ©->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 ) -{ - 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++) { - 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 (_mesa_is_bufferobj(vbo) && !_mesa_bufferobj_mapped(vbo)) - ctx->Driver.MapBuffer(ctx, GL_ARRAY_BUFFER, GL_READ_ONLY, vbo); - - copy->varying[j].src_ptr = ADD_POINTERS(vbo->Pointer, - copy->array[i]->Ptr); - - copy->dstarray_ptr[i] = ©->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 (_mesa_is_bufferobj(copy->ib->obj) && - !_mesa_bufferobj_mapped(copy->ib->obj)) - ctx->Driver.MapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER, GL_READ_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 = 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 = 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; - } - - /* 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_client_array *src = copy->varying[i].array; - struct gl_client_array *dst = ©->varying[i].dstarray; - - dst->Size = src->Size; - dst->Type = src->Type; - dst->Format = GL_RGBA; - 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->Shared->NullBufferObj; - dst->_ElementSize = src->_ElementSize; - 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 + 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.type = GL_UNSIGNED_INT; - 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->BufferObj; - if (_mesa_is_bufferobj(vbo) && _mesa_bufferobj_mapped(vbo)) - ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER, vbo); - } - - /* Unmap index buffer: - */ - if (_mesa_is_bufferobj(copy->ib->obj) && - _mesa_bufferobj_mapped(copy->ib->obj)) { - ctx->Driver.UnmapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER, copy->ib->obj); - } -} - - -/** - * Split VBO into smaller pieces, draw the pieces. - */ -void vbo_split_copy( struct gl_context *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, 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(©, 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(©); - replay_elts(©); - replay_finish(©); - } -} + +/* + * 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 + */ + +/* Split indexed primitives with per-vertex copying. + */ + +#include "main/glheader.h" +#include "main/bufferobj.h" +#include "main/imports.h" +#include "main/image.h" +#include "main/macros.h" +#include "main/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 { + + struct gl_context *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 attr_size( const struct gl_client_array *array ) +{ + return array->Size * _mesa_sizeof_type(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 ) +{ + 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_client_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++) { + printf(" array %d at %p:\n", j, (void*) arrays[j]); + printf(" enabled %d, ptr %p, size %d, type 0x%x, stride %d\n", + arrays[j]->Enabled, arrays[j]->Ptr, + arrays[j]->Size, arrays[j]->Type, arrays[j]->StrideB); + if (0) { + GLint k = prims[i].start + prims[i].count - 1; + GLfloat *last = (GLfloat *) (arrays[j]->Ptr + arrays[j]->Stride * k); + printf(" last: %f %f %f\n", + last[0], last[1], last[2]); + } + } + } +} + + +static void +flush( struct copy_context *copy ) +{ + GLuint i; + + /* Set some counters: + */ + copy->dstib.count = copy->dstelt_nr; + +#if 0 + dump_draw_info(copy->ctx, + copy->dstarray_ptr, + copy->dstprim, + copy->dstprim_nr, + ©->dstib, + 0, + copy->dstbuf_nr); +#else + (void) dump_draw_info; +#endif + + copy->draw( copy->ctx, + copy->dstarray_ptr, + copy->dstprim, + copy->dstprim_nr, + ©->dstib, + GL_TRUE, + 0, + copy->dstbuf_nr - 1 ); + + /* 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 = ©->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]; + GLuint slot = elt & (ELT_TABLE_SIZE-1); + +/* 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; + +/* 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; + +#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)); + } +/* else */ +/* printf(" --> reuse vertex\n"); */ + +/* printf(" --> emit %d\n", copy->vert_cache[slot].out); */ + 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 = ©->dstprim[copy->dstprim_nr]; + +/* 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 = ©->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 ) +{ + 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++) { + 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 (_mesa_is_bufferobj(vbo) && !_mesa_bufferobj_mapped(vbo)) + ctx->Driver.MapBufferRange(ctx, 0, vbo->Size, GL_MAP_READ_BIT, vbo); + + copy->varying[j].src_ptr = ADD_POINTERS(vbo->Pointer, + copy->array[i]->Ptr); + + copy->dstarray_ptr[i] = ©->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 (_mesa_is_bufferobj(copy->ib->obj) && + !_mesa_bufferobj_mapped(copy->ib->obj)) + ctx->Driver.MapBufferRange(ctx, 0, copy->ib->obj->Size, GL_MAP_READ_BIT, + 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 = 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 = 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; + } + + /* 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_client_array *src = copy->varying[i].array; + struct gl_client_array *dst = ©->varying[i].dstarray; + + dst->Size = src->Size; + dst->Type = src->Type; + dst->Format = GL_RGBA; + 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->Shared->NullBufferObj; + dst->_ElementSize = src->_ElementSize; + 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 + 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.type = GL_UNSIGNED_INT; + 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->BufferObj; + if (_mesa_is_bufferobj(vbo) && _mesa_bufferobj_mapped(vbo)) + ctx->Driver.UnmapBuffer(ctx, vbo); + } + + /* Unmap index buffer: + */ + if (_mesa_is_bufferobj(copy->ib->obj) && + _mesa_bufferobj_mapped(copy->ib->obj)) { + ctx->Driver.UnmapBuffer(ctx, copy->ib->obj); + } +} + + +/** + * Split VBO into smaller pieces, draw the pieces. + */ +void vbo_split_copy( struct gl_context *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, 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(©, 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(©); + replay_elts(©); + replay_finish(©); + } +} -- cgit v1.2.3