From 2fb58f16eeec8ef3ec2a25e246477aab64e38a7d Mon Sep 17 00:00:00 2001 From: marha Date: Sun, 10 Apr 2011 18:43:19 +0000 Subject: mesa git update --- mesalib/src/mesa/vbo/vbo_context.c | 3 + mesalib/src/mesa/vbo/vbo_exec_api.c | 2244 +++++++++++++-------------- mesalib/src/mesa/vbo/vbo_exec_array.c | 2677 +++++++++++++++++---------------- mesalib/src/mesa/vbo/vbo_exec_draw.c | 853 +++++------ mesalib/src/mesa/vbo/vbo_save_draw.c | 613 ++++---- mesalib/src/mesa/vbo/vbo_split_copy.c | 1253 +++++++-------- 6 files changed, 3829 insertions(+), 3814 deletions(-) (limited to 'mesalib/src/mesa/vbo') diff --git a/mesalib/src/mesa/vbo/vbo_context.c b/mesalib/src/mesa/vbo/vbo_context.c index bbf422f74..5f03ce547 100644 --- a/mesalib/src/mesa/vbo/vbo_context.c +++ b/mesalib/src/mesa/vbo/vbo_context.c @@ -72,6 +72,7 @@ static void init_legacy_currval(struct gl_context *ctx) cl->Type = GL_FLOAT; cl->Format = GL_RGBA; cl->Ptr = (const void *)ctx->Current.Attrib[i]; + cl->_ElementSize = cl->Size * sizeof(GLfloat); _mesa_reference_buffer_object(ctx, &cl->BufferObj, ctx->Shared->NullBufferObj); } @@ -98,6 +99,7 @@ static void init_generic_currval(struct gl_context *ctx) cl->Stride = 0; cl->StrideB = 0; cl->Enabled = 1; + cl->_ElementSize = cl->Size * sizeof(GLfloat); _mesa_reference_buffer_object(ctx, &cl->BufferObj, ctx->Shared->NullBufferObj); } @@ -143,6 +145,7 @@ static void init_mat_currval(struct gl_context *ctx) cl->Stride = 0; cl->StrideB = 0; cl->Enabled = 1; + cl->_ElementSize = cl->Size * sizeof(GLfloat); _mesa_reference_buffer_object(ctx, &cl->BufferObj, ctx->Shared->NullBufferObj); } diff --git a/mesalib/src/mesa/vbo/vbo_exec_api.c b/mesalib/src/mesa/vbo/vbo_exec_api.c index fcd544de7..6b919e8b4 100644 --- a/mesalib/src/mesa/vbo/vbo_exec_api.c +++ b/mesalib/src/mesa/vbo/vbo_exec_api.c @@ -1,1121 +1,1123 @@ -/************************************************************************** - -Copyright 2002-2008 Tungsten Graphics Inc., Cedar Park, Texas. - -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 -on the rights to use, copy, modify, merge, publish, distribute, sub -license, 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 (including the next -paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL -TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS 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/context.h" -#include "main/macros.h" -#include "main/mfeatures.h" -#include "main/vtxfmt.h" -#include "main/dlist.h" -#include "main/eval.h" -#include "main/state.h" -#include "main/light.h" -#include "main/api_arrayelt.h" -#include "main/api_noop.h" -#include "main/dispatch.h" - -#include "vbo_context.h" - -#ifdef ERROR -#undef ERROR -#endif - - -/** ID/name for immediate-mode VBO */ -#define IMM_BUFFER_NAME 0xaabbccdd - - -static void reset_attrfv( struct vbo_exec_context *exec ); - - -/** - * Close off the last primitive, execute the buffer, restart the - * primitive. - */ -static void vbo_exec_wrap_buffers( struct vbo_exec_context *exec ) -{ - if (exec->vtx.prim_count == 0) { - exec->vtx.copied.nr = 0; - exec->vtx.vert_count = 0; - exec->vtx.buffer_ptr = exec->vtx.buffer_map; - } - else { - GLuint last_begin = exec->vtx.prim[exec->vtx.prim_count-1].begin; - GLuint last_count; - - if (exec->ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { - GLint i = exec->vtx.prim_count - 1; - assert(i >= 0); - exec->vtx.prim[i].count = (exec->vtx.vert_count - - exec->vtx.prim[i].start); - } - - last_count = exec->vtx.prim[exec->vtx.prim_count-1].count; - - /* Execute the buffer and save copied vertices. - */ - if (exec->vtx.vert_count) - vbo_exec_vtx_flush( exec, GL_FALSE ); - else { - exec->vtx.prim_count = 0; - exec->vtx.copied.nr = 0; - } - - /* Emit a glBegin to start the new list. - */ - assert(exec->vtx.prim_count == 0); - - if (exec->ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { - exec->vtx.prim[0].mode = exec->ctx->Driver.CurrentExecPrimitive; - exec->vtx.prim[0].start = 0; - exec->vtx.prim[0].count = 0; - exec->vtx.prim_count++; - - if (exec->vtx.copied.nr == last_count) - exec->vtx.prim[0].begin = last_begin; - } - } -} - - -/** - * Deal with buffer wrapping where provoked by the vertex buffer - * filling up, as opposed to upgrade_vertex(). - */ -void vbo_exec_vtx_wrap( struct vbo_exec_context *exec ) -{ - GLfloat *data = exec->vtx.copied.buffer; - GLuint i; - - /* Run pipeline on current vertices, copy wrapped vertices - * to exec->vtx.copied. - */ - vbo_exec_wrap_buffers( exec ); - - /* Copy stored stored vertices to start of new list. - */ - assert(exec->vtx.max_vert - exec->vtx.vert_count > exec->vtx.copied.nr); - - for (i = 0 ; i < exec->vtx.copied.nr ; i++) { - memcpy( exec->vtx.buffer_ptr, data, - exec->vtx.vertex_size * sizeof(GLfloat)); - exec->vtx.buffer_ptr += exec->vtx.vertex_size; - data += exec->vtx.vertex_size; - exec->vtx.vert_count++; - } - - exec->vtx.copied.nr = 0; -} - - -/** - * Copy the active vertex's values to the ctx->Current fields. - */ -static void vbo_exec_copy_to_current( struct vbo_exec_context *exec ) -{ - struct gl_context *ctx = exec->ctx; - struct vbo_context *vbo = vbo_context(ctx); - GLuint i; - - for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) { - if (exec->vtx.attrsz[i]) { - /* Note: the exec->vtx.current[i] pointers point into the - * ctx->Current.Attrib and ctx->Light.Material.Attrib arrays. - */ - GLfloat *current = (GLfloat *)vbo->currval[i].Ptr; - GLfloat tmp[4]; - - COPY_CLEAN_4V(tmp, - exec->vtx.attrsz[i], - exec->vtx.attrptr[i]); - - if (memcmp(current, tmp, sizeof(tmp)) != 0) { - memcpy(current, tmp, sizeof(tmp)); - - /* Given that we explicitly state size here, there is no need - * for the COPY_CLEAN above, could just copy 16 bytes and be - * done. The only problem is when Mesa accesses ctx->Current - * directly. - */ - vbo->currval[i].Size = exec->vtx.attrsz[i]; - - /* This triggers rather too much recalculation of Mesa state - * that doesn't get used (eg light positions). - */ - if (i >= VBO_ATTRIB_MAT_FRONT_AMBIENT && - i <= VBO_ATTRIB_MAT_BACK_INDEXES) - ctx->NewState |= _NEW_LIGHT; - - ctx->NewState |= _NEW_CURRENT_ATTRIB; - } - } - } - - /* Colormaterial -- this kindof sucks. - */ - if (ctx->Light.ColorMaterialEnabled && - exec->vtx.attrsz[VBO_ATTRIB_COLOR0]) { - _mesa_update_color_material(ctx, - ctx->Current.Attrib[VBO_ATTRIB_COLOR0]); - } -} - - -/** - * Copy current vertex attribute values into the current vertex. - */ -static void -vbo_exec_copy_from_current(struct vbo_exec_context *exec) -{ - struct gl_context *ctx = exec->ctx; - struct vbo_context *vbo = vbo_context(ctx); - GLint i; - - for (i = VBO_ATTRIB_POS + 1; i < VBO_ATTRIB_MAX; i++) { - const GLfloat *current = (GLfloat *) vbo->currval[i].Ptr; - switch (exec->vtx.attrsz[i]) { - case 4: exec->vtx.attrptr[i][3] = current[3]; - case 3: exec->vtx.attrptr[i][2] = current[2]; - case 2: exec->vtx.attrptr[i][1] = current[1]; - case 1: exec->vtx.attrptr[i][0] = current[0]; - break; - } - } -} - - -/** - * Flush existing data, set new attrib size, replay copied vertices. - * This is called when we transition from a small vertex attribute size - * to a larger one. Ex: glTexCoord2f -> glTexCoord4f. - * We need to go back over the previous 2-component texcoords and insert - * zero and one values. - */ -static void -vbo_exec_wrap_upgrade_vertex(struct vbo_exec_context *exec, - GLuint attr, GLuint newSize ) -{ - struct gl_context *ctx = exec->ctx; - struct vbo_context *vbo = vbo_context(ctx); - const GLint lastcount = exec->vtx.vert_count; - GLfloat *old_attrptr[VBO_ATTRIB_MAX]; - const GLuint old_vtx_size = exec->vtx.vertex_size; /* floats per vertex */ - const GLuint oldSize = exec->vtx.attrsz[attr]; - GLuint i; - - /* Run pipeline on current vertices, copy wrapped vertices - * to exec->vtx.copied. - */ - vbo_exec_wrap_buffers( exec ); - - if (unlikely(exec->vtx.copied.nr)) { - /* We're in the middle of a primitive, keep the old vertex - * format around to be able to translate the copied vertices to - * the new format. - */ - memcpy(old_attrptr, exec->vtx.attrptr, sizeof(old_attrptr)); - } - - if (unlikely(oldSize)) { - /* Do a COPY_TO_CURRENT to ensure back-copying works for the - * case when the attribute already exists in the vertex and is - * having its size increased. - */ - vbo_exec_copy_to_current( exec ); - } - - /* Heuristic: Attempt to isolate attributes received outside - * begin/end so that they don't bloat the vertices. - */ - if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END && - !oldSize && lastcount > 8 && exec->vtx.vertex_size) { - vbo_exec_copy_to_current( exec ); - reset_attrfv( exec ); - } - - /* Fix up sizes: - */ - exec->vtx.attrsz[attr] = newSize; - exec->vtx.vertex_size += newSize - oldSize; - exec->vtx.max_vert = ((VBO_VERT_BUFFER_SIZE - exec->vtx.buffer_used) / - (exec->vtx.vertex_size * sizeof(GLfloat))); - exec->vtx.vert_count = 0; - exec->vtx.buffer_ptr = exec->vtx.buffer_map; - - if (unlikely(oldSize)) { - /* Size changed, recalculate all the attrptr[] values - */ - GLfloat *tmp = exec->vtx.vertex; - - for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) { - if (exec->vtx.attrsz[i]) { - exec->vtx.attrptr[i] = tmp; - tmp += exec->vtx.attrsz[i]; - } - else - exec->vtx.attrptr[i] = NULL; /* will not be dereferenced */ - } - - /* Copy from current to repopulate the vertex with correct - * values. - */ - vbo_exec_copy_from_current( exec ); - } - else { - /* Just have to append the new attribute at the end */ - exec->vtx.attrptr[attr] = exec->vtx.vertex + - exec->vtx.vertex_size - newSize; - } - - /* Replay stored vertices to translate them - * to new format here. - * - * -- No need to replay - just copy piecewise - */ - if (unlikely(exec->vtx.copied.nr)) { - GLfloat *data = exec->vtx.copied.buffer; - GLfloat *dest = exec->vtx.buffer_ptr; - GLuint j; - - assert(exec->vtx.buffer_ptr == exec->vtx.buffer_map); - - for (i = 0 ; i < exec->vtx.copied.nr ; i++) { - for (j = 0 ; j < VBO_ATTRIB_MAX ; j++) { - GLuint sz = exec->vtx.attrsz[j]; - - if (sz) { - GLint old_offset = old_attrptr[j] - exec->vtx.vertex; - GLint new_offset = exec->vtx.attrptr[j] - exec->vtx.vertex; - - if (j == attr) { - if (oldSize) { - GLfloat tmp[4]; - COPY_CLEAN_4V(tmp, oldSize, data + old_offset); - COPY_SZ_4V(dest + new_offset, newSize, tmp); - } else { - GLfloat *current = (GLfloat *)vbo->currval[j].Ptr; - COPY_SZ_4V(dest + new_offset, sz, current); - } - } - else { - COPY_SZ_4V(dest + new_offset, sz, data + old_offset); - } - } - } - - data += old_vtx_size; - dest += exec->vtx.vertex_size; - } - - exec->vtx.buffer_ptr = dest; - exec->vtx.vert_count += exec->vtx.copied.nr; - exec->vtx.copied.nr = 0; - } -} - - -/** - * This is when a vertex attribute transitions to a different size. - * For example, we saw a bunch of glTexCoord2f() calls and now we got a - * glTexCoord4f() call. We promote the array from size=2 to size=4. - */ -static void -vbo_exec_fixup_vertex(struct gl_context *ctx, GLuint attr, GLuint newSize) -{ - struct vbo_exec_context *exec = &vbo_context(ctx)->exec; - - if (newSize > exec->vtx.attrsz[attr]) { - /* New size is larger. Need to flush existing vertices and get - * an enlarged vertex format. - */ - vbo_exec_wrap_upgrade_vertex( exec, attr, newSize ); - } - else if (newSize < exec->vtx.active_sz[attr]) { - static const GLfloat id[4] = { 0, 0, 0, 1 }; - GLuint i; - - /* New size is smaller - just need to fill in some - * zeros. Don't need to flush or wrap. - */ - for (i = newSize; i <= exec->vtx.attrsz[attr]; i++) - exec->vtx.attrptr[attr][i-1] = id[i-1]; - } - - exec->vtx.active_sz[attr] = newSize; - - /* Does setting NeedFlush belong here? Necessitates resetting - * vtxfmt on each flush (otherwise flags won't get reset - * afterwards). - */ - if (attr == 0) - ctx->Driver.NeedFlush |= FLUSH_STORED_VERTICES; -} - - -/** - * This macro is used to implement all the glVertex, glColor, glTexCoord, - * glVertexAttrib, etc functions. - */ -#define ATTR( A, N, V0, V1, V2, V3 ) \ -do { \ - struct vbo_exec_context *exec = &vbo_context(ctx)->exec; \ - \ - if (unlikely(!(ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT))) \ - ctx->Driver.BeginVertices( ctx ); \ - \ - if (unlikely(exec->vtx.active_sz[A] != N)) \ - vbo_exec_fixup_vertex(ctx, A, N); \ - \ - { \ - GLfloat *dest = exec->vtx.attrptr[A]; \ - if (N>0) dest[0] = V0; \ - if (N>1) dest[1] = V1; \ - if (N>2) dest[2] = V2; \ - if (N>3) dest[3] = V3; \ - } \ - \ - if ((A) == 0) { \ - /* This is a glVertex call */ \ - GLuint i; \ - \ - for (i = 0; i < exec->vtx.vertex_size; i++) \ - exec->vtx.buffer_ptr[i] = exec->vtx.vertex[i]; \ - \ - exec->vtx.buffer_ptr += exec->vtx.vertex_size; \ - \ - /* Set FLUSH_STORED_VERTICES to indicate that there's now */ \ - /* something to draw (not just updating a color or texcoord).*/ \ - ctx->Driver.NeedFlush |= FLUSH_STORED_VERTICES; \ - \ - if (++exec->vtx.vert_count >= exec->vtx.max_vert) \ - vbo_exec_vtx_wrap( exec ); \ - } \ -} while (0) - - -#define ERROR(err) _mesa_error( ctx, err, __FUNCTION__ ) -#define TAG(x) vbo_##x - -#include "vbo_attrib_tmp.h" - - -#if FEATURE_beginend - - -#if FEATURE_evaluators - -static void GLAPIENTRY vbo_exec_EvalCoord1f( GLfloat u ) -{ - GET_CURRENT_CONTEXT( ctx ); - struct vbo_exec_context *exec = &vbo_context(ctx)->exec; - - { - GLint i; - if (exec->eval.recalculate_maps) - vbo_exec_eval_update( exec ); - - for (i = 0; i <= VBO_ATTRIB_TEX7; i++) { - if (exec->eval.map1[i].map) - if (exec->vtx.active_sz[i] != exec->eval.map1[i].sz) - vbo_exec_fixup_vertex( ctx, i, exec->eval.map1[i].sz ); - } - } - - - memcpy( exec->vtx.copied.buffer, exec->vtx.vertex, - exec->vtx.vertex_size * sizeof(GLfloat)); - - vbo_exec_do_EvalCoord1f( exec, u ); - - memcpy( exec->vtx.vertex, exec->vtx.copied.buffer, - exec->vtx.vertex_size * sizeof(GLfloat)); -} - -static void GLAPIENTRY vbo_exec_EvalCoord2f( GLfloat u, GLfloat v ) -{ - GET_CURRENT_CONTEXT( ctx ); - struct vbo_exec_context *exec = &vbo_context(ctx)->exec; - - { - GLint i; - if (exec->eval.recalculate_maps) - vbo_exec_eval_update( exec ); - - for (i = 0; i <= VBO_ATTRIB_TEX7; i++) { - if (exec->eval.map2[i].map) - if (exec->vtx.active_sz[i] != exec->eval.map2[i].sz) - vbo_exec_fixup_vertex( ctx, i, exec->eval.map2[i].sz ); - } - - if (ctx->Eval.AutoNormal) - if (exec->vtx.active_sz[VBO_ATTRIB_NORMAL] != 3) - vbo_exec_fixup_vertex( ctx, VBO_ATTRIB_NORMAL, 3 ); - } - - memcpy( exec->vtx.copied.buffer, exec->vtx.vertex, - exec->vtx.vertex_size * sizeof(GLfloat)); - - vbo_exec_do_EvalCoord2f( exec, u, v ); - - memcpy( exec->vtx.vertex, exec->vtx.copied.buffer, - exec->vtx.vertex_size * sizeof(GLfloat)); -} - -static void GLAPIENTRY vbo_exec_EvalCoord1fv( const GLfloat *u ) -{ - vbo_exec_EvalCoord1f( u[0] ); -} - -static void GLAPIENTRY vbo_exec_EvalCoord2fv( const GLfloat *u ) -{ - vbo_exec_EvalCoord2f( u[0], u[1] ); -} - -static void GLAPIENTRY vbo_exec_EvalPoint1( GLint i ) -{ - GET_CURRENT_CONTEXT( ctx ); - GLfloat du = ((ctx->Eval.MapGrid1u2 - ctx->Eval.MapGrid1u1) / - (GLfloat) ctx->Eval.MapGrid1un); - GLfloat u = i * du + ctx->Eval.MapGrid1u1; - - vbo_exec_EvalCoord1f( u ); -} - - -static void GLAPIENTRY vbo_exec_EvalPoint2( GLint i, GLint j ) -{ - GET_CURRENT_CONTEXT( ctx ); - GLfloat du = ((ctx->Eval.MapGrid2u2 - ctx->Eval.MapGrid2u1) / - (GLfloat) ctx->Eval.MapGrid2un); - GLfloat dv = ((ctx->Eval.MapGrid2v2 - ctx->Eval.MapGrid2v1) / - (GLfloat) ctx->Eval.MapGrid2vn); - GLfloat u = i * du + ctx->Eval.MapGrid2u1; - GLfloat v = j * dv + ctx->Eval.MapGrid2v1; - - vbo_exec_EvalCoord2f( u, v ); -} - -/* use noop eval mesh */ -#define vbo_exec_EvalMesh1 _mesa_noop_EvalMesh1 -#define vbo_exec_EvalMesh2 _mesa_noop_EvalMesh2 - -#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. - */ -static void GLAPIENTRY vbo_exec_Begin( GLenum mode ) -{ - GET_CURRENT_CONTEXT( ctx ); - - if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END) { - struct vbo_exec_context *exec = &vbo_context(ctx)->exec; - int i; - - if (ctx->NewState) { - _mesa_update_state( ctx ); - - CALL_Begin(ctx->Exec, (mode)); - return; - } - - if (!_mesa_valid_to_render(ctx, "glBegin")) { - return; - } - - /* Heuristic: attempt to isolate attributes occuring outside - * begin/end pairs. - */ - if (exec->vtx.vertex_size && !exec->vtx.attrsz[0]) - vbo_exec_FlushVertices_internal(exec, GL_FALSE); - - i = exec->vtx.prim_count++; - exec->vtx.prim[i].mode = mode; - exec->vtx.prim[i].begin = 1; - exec->vtx.prim[i].end = 0; - exec->vtx.prim[i].indexed = 0; - exec->vtx.prim[i].weak = 0; - exec->vtx.prim[i].pad = 0; - exec->vtx.prim[i].start = exec->vtx.vert_count; - exec->vtx.prim[i].count = 0; - exec->vtx.prim[i].num_instances = 1; - - ctx->Driver.CurrentExecPrimitive = mode; - } - else - _mesa_error( ctx, GL_INVALID_OPERATION, "glBegin" ); - -} - - -/** - * Called via glEnd. - */ -static void GLAPIENTRY vbo_exec_End( void ) -{ - GET_CURRENT_CONTEXT( ctx ); - - if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { - struct vbo_exec_context *exec = &vbo_context(ctx)->exec; - int idx = exec->vtx.vert_count; - int i = exec->vtx.prim_count - 1; - - exec->vtx.prim[i].end = 1; - exec->vtx.prim[i].count = idx - exec->vtx.prim[i].start; - - ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END; - - if (exec->vtx.prim_count == VBO_MAX_PRIM) - vbo_exec_vtx_flush( exec, GL_FALSE ); - } - else - _mesa_error( ctx, GL_INVALID_OPERATION, "glEnd" ); -} - - -/** - * Called via glPrimitiveRestartNV() - */ -static void GLAPIENTRY -vbo_exec_PrimitiveRestartNV(void) -{ - GLenum curPrim; - GET_CURRENT_CONTEXT( ctx ); - - curPrim = ctx->Driver.CurrentExecPrimitive; - - if (curPrim == PRIM_OUTSIDE_BEGIN_END) { - _mesa_error( ctx, GL_INVALID_OPERATION, "glPrimitiveRestartNV" ); - } - else { - vbo_exec_End(); - vbo_exec_Begin(curPrim); - } -} - - - -static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec ) -{ - GLvertexformat *vfmt = &exec->vtxfmt; - - _MESA_INIT_ARRAYELT_VTXFMT(vfmt, _ae_); - - vfmt->Begin = vbo_exec_Begin; - vfmt->End = vbo_exec_End; - vfmt->PrimitiveRestartNV = vbo_exec_PrimitiveRestartNV; - - _MESA_INIT_DLIST_VTXFMT(vfmt, _mesa_); - _MESA_INIT_EVAL_VTXFMT(vfmt, vbo_exec_); - - vfmt->Rectf = _mesa_noop_Rectf; - - /* from attrib_tmp.h: - */ - vfmt->Color3f = vbo_Color3f; - vfmt->Color3fv = vbo_Color3fv; - vfmt->Color4f = vbo_Color4f; - vfmt->Color4fv = vbo_Color4fv; - vfmt->FogCoordfEXT = vbo_FogCoordfEXT; - vfmt->FogCoordfvEXT = vbo_FogCoordfvEXT; - vfmt->MultiTexCoord1fARB = vbo_MultiTexCoord1f; - vfmt->MultiTexCoord1fvARB = vbo_MultiTexCoord1fv; - vfmt->MultiTexCoord2fARB = vbo_MultiTexCoord2f; - vfmt->MultiTexCoord2fvARB = vbo_MultiTexCoord2fv; - vfmt->MultiTexCoord3fARB = vbo_MultiTexCoord3f; - vfmt->MultiTexCoord3fvARB = vbo_MultiTexCoord3fv; - vfmt->MultiTexCoord4fARB = vbo_MultiTexCoord4f; - vfmt->MultiTexCoord4fvARB = vbo_MultiTexCoord4fv; - vfmt->Normal3f = vbo_Normal3f; - vfmt->Normal3fv = vbo_Normal3fv; - vfmt->SecondaryColor3fEXT = vbo_SecondaryColor3fEXT; - vfmt->SecondaryColor3fvEXT = vbo_SecondaryColor3fvEXT; - vfmt->TexCoord1f = vbo_TexCoord1f; - vfmt->TexCoord1fv = vbo_TexCoord1fv; - vfmt->TexCoord2f = vbo_TexCoord2f; - vfmt->TexCoord2fv = vbo_TexCoord2fv; - vfmt->TexCoord3f = vbo_TexCoord3f; - vfmt->TexCoord3fv = vbo_TexCoord3fv; - vfmt->TexCoord4f = vbo_TexCoord4f; - vfmt->TexCoord4fv = vbo_TexCoord4fv; - vfmt->Vertex2f = vbo_Vertex2f; - vfmt->Vertex2fv = vbo_Vertex2fv; - vfmt->Vertex3f = vbo_Vertex3f; - vfmt->Vertex3fv = vbo_Vertex3fv; - vfmt->Vertex4f = vbo_Vertex4f; - vfmt->Vertex4fv = vbo_Vertex4fv; - - vfmt->VertexAttrib1fARB = vbo_VertexAttrib1fARB; - vfmt->VertexAttrib1fvARB = vbo_VertexAttrib1fvARB; - vfmt->VertexAttrib2fARB = vbo_VertexAttrib2fARB; - vfmt->VertexAttrib2fvARB = vbo_VertexAttrib2fvARB; - vfmt->VertexAttrib3fARB = vbo_VertexAttrib3fARB; - vfmt->VertexAttrib3fvARB = vbo_VertexAttrib3fvARB; - vfmt->VertexAttrib4fARB = vbo_VertexAttrib4fARB; - vfmt->VertexAttrib4fvARB = vbo_VertexAttrib4fvARB; - - vfmt->VertexAttrib1fNV = vbo_VertexAttrib1fNV; - vfmt->VertexAttrib1fvNV = vbo_VertexAttrib1fvNV; - vfmt->VertexAttrib2fNV = vbo_VertexAttrib2fNV; - vfmt->VertexAttrib2fvNV = vbo_VertexAttrib2fvNV; - vfmt->VertexAttrib3fNV = vbo_VertexAttrib3fNV; - vfmt->VertexAttrib3fvNV = vbo_VertexAttrib3fvNV; - vfmt->VertexAttrib4fNV = vbo_VertexAttrib4fNV; - vfmt->VertexAttrib4fvNV = vbo_VertexAttrib4fvNV; - - /* integer-valued */ - vfmt->VertexAttribI1i = vbo_VertexAttribI1i; - vfmt->VertexAttribI2i = vbo_VertexAttribI2i; - vfmt->VertexAttribI3i = vbo_VertexAttribI3i; - vfmt->VertexAttribI4i = vbo_VertexAttribI4i; - vfmt->VertexAttribI2iv = vbo_VertexAttribI2iv; - vfmt->VertexAttribI3iv = vbo_VertexAttribI3iv; - vfmt->VertexAttribI4iv = vbo_VertexAttribI4iv; - - /* unsigned integer-valued */ - vfmt->VertexAttribI1ui = vbo_VertexAttribI1ui; - vfmt->VertexAttribI2ui = vbo_VertexAttribI2ui; - vfmt->VertexAttribI3ui = vbo_VertexAttribI3ui; - vfmt->VertexAttribI4ui = vbo_VertexAttribI4ui; - vfmt->VertexAttribI2uiv = vbo_VertexAttribI2uiv; - vfmt->VertexAttribI3uiv = vbo_VertexAttribI3uiv; - vfmt->VertexAttribI4uiv = vbo_VertexAttribI4uiv; - - vfmt->Materialfv = vbo_Materialfv; - - vfmt->EdgeFlag = vbo_EdgeFlag; - vfmt->Indexf = vbo_Indexf; - vfmt->Indexfv = vbo_Indexfv; - -} - - -#else /* FEATURE_beginend */ - - -static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec ) -{ - /* silence warnings */ - (void) vbo_Color3f; - (void) vbo_Color3fv; - (void) vbo_Color4f; - (void) vbo_Color4fv; - (void) vbo_FogCoordfEXT; - (void) vbo_FogCoordfvEXT; - (void) vbo_MultiTexCoord1f; - (void) vbo_MultiTexCoord1fv; - (void) vbo_MultiTexCoord2f; - (void) vbo_MultiTexCoord2fv; - (void) vbo_MultiTexCoord3f; - (void) vbo_MultiTexCoord3fv; - (void) vbo_MultiTexCoord4f; - (void) vbo_MultiTexCoord4fv; - (void) vbo_Normal3f; - (void) vbo_Normal3fv; - (void) vbo_SecondaryColor3fEXT; - (void) vbo_SecondaryColor3fvEXT; - (void) vbo_TexCoord1f; - (void) vbo_TexCoord1fv; - (void) vbo_TexCoord2f; - (void) vbo_TexCoord2fv; - (void) vbo_TexCoord3f; - (void) vbo_TexCoord3fv; - (void) vbo_TexCoord4f; - (void) vbo_TexCoord4fv; - (void) vbo_Vertex2f; - (void) vbo_Vertex2fv; - (void) vbo_Vertex3f; - (void) vbo_Vertex3fv; - (void) vbo_Vertex4f; - (void) vbo_Vertex4fv; - - (void) vbo_VertexAttrib1fARB; - (void) vbo_VertexAttrib1fvARB; - (void) vbo_VertexAttrib2fARB; - (void) vbo_VertexAttrib2fvARB; - (void) vbo_VertexAttrib3fARB; - (void) vbo_VertexAttrib3fvARB; - (void) vbo_VertexAttrib4fARB; - (void) vbo_VertexAttrib4fvARB; - - (void) vbo_VertexAttrib1fNV; - (void) vbo_VertexAttrib1fvNV; - (void) vbo_VertexAttrib2fNV; - (void) vbo_VertexAttrib2fvNV; - (void) vbo_VertexAttrib3fNV; - (void) vbo_VertexAttrib3fvNV; - (void) vbo_VertexAttrib4fNV; - (void) vbo_VertexAttrib4fvNV; - - (void) vbo_Materialfv; - - (void) vbo_EdgeFlag; - (void) vbo_Indexf; - (void) vbo_Indexfv; -} - - -#endif /* FEATURE_beginend */ - - -/** - * Tell the VBO module to use a real OpenGL vertex buffer object to - * store accumulated immediate-mode vertex data. - * This replaces the malloced buffer which was created in - * vb_exec_vtx_init() below. - */ -void vbo_use_buffer_objects(struct gl_context *ctx) -{ - struct vbo_exec_context *exec = &vbo_context(ctx)->exec; - /* Any buffer name but 0 can be used here since this bufferobj won't - * go into the bufferobj hashtable. - */ - GLuint bufName = IMM_BUFFER_NAME; - GLenum target = GL_ARRAY_BUFFER_ARB; - GLenum usage = GL_STREAM_DRAW_ARB; - GLsizei size = VBO_VERT_BUFFER_SIZE; - - /* Make sure this func is only used once */ - assert(exec->vtx.bufferobj == ctx->Shared->NullBufferObj); - if (exec->vtx.buffer_map) { - _mesa_align_free(exec->vtx.buffer_map); - exec->vtx.buffer_map = NULL; - exec->vtx.buffer_ptr = NULL; - } - - /* Allocate a real buffer object now */ - _mesa_reference_buffer_object(ctx, &exec->vtx.bufferobj, NULL); - exec->vtx.bufferobj = ctx->Driver.NewBufferObject(ctx, bufName, target); - ctx->Driver.BufferData(ctx, target, size, NULL, usage, exec->vtx.bufferobj); -} - - -/** - * If this function is called, all VBO buffers will be unmapped when - * we flush. - * Otherwise, if a simple command like glColor3f() is called and we flush, - * the current VBO may be left mapped. - */ -void -vbo_always_unmap_buffers(struct gl_context *ctx) -{ - struct vbo_exec_context *exec = &vbo_context(ctx)->exec; - exec->begin_vertices_flags |= FLUSH_STORED_VERTICES; -} - - -void vbo_exec_vtx_init( struct vbo_exec_context *exec ) -{ - struct gl_context *ctx = exec->ctx; - struct vbo_context *vbo = vbo_context(ctx); - GLuint i; - - /* Allocate a buffer object. Will just reuse this object - * continuously, unless vbo_use_buffer_objects() is called to enable - * use of real VBOs. - */ - _mesa_reference_buffer_object(ctx, - &exec->vtx.bufferobj, - ctx->Shared->NullBufferObj); - - ASSERT(!exec->vtx.buffer_map); - exec->vtx.buffer_map = (GLfloat *)_mesa_align_malloc(VBO_VERT_BUFFER_SIZE, 64); - exec->vtx.buffer_ptr = exec->vtx.buffer_map; - - vbo_exec_vtxfmt_init( exec ); - - /* Hook our functions into the dispatch table. - */ - _mesa_install_exec_vtxfmt( ctx, &exec->vtxfmt ); - - for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) { - ASSERT(i < Elements(exec->vtx.attrsz)); - exec->vtx.attrsz[i] = 0; - ASSERT(i < Elements(exec->vtx.active_sz)); - exec->vtx.active_sz[i] = 0; - } - for (i = 0 ; i < VERT_ATTRIB_MAX; i++) { - ASSERT(i < Elements(exec->vtx.inputs)); - ASSERT(i < Elements(exec->vtx.arrays)); - exec->vtx.inputs[i] = &exec->vtx.arrays[i]; - } - - { - struct gl_client_array *arrays = exec->vtx.arrays; - unsigned i; - - memcpy(arrays, vbo->legacy_currval, 16 * sizeof(arrays[0])); - memcpy(arrays + 16, vbo->generic_currval, 16 * sizeof(arrays[0])); - - for (i = 0; i < 16; ++i) { - arrays[i ].BufferObj = NULL; - arrays[i + 16].BufferObj = NULL; - _mesa_reference_buffer_object(ctx, &arrays[i ].BufferObj, - vbo->legacy_currval[i].BufferObj); - _mesa_reference_buffer_object(ctx, &arrays[i + 16].BufferObj, - vbo->generic_currval[i].BufferObj); - } - } - - exec->vtx.vertex_size = 0; - - exec->begin_vertices_flags = FLUSH_UPDATE_CURRENT; -} - - -void vbo_exec_vtx_destroy( struct vbo_exec_context *exec ) -{ - /* using a real VBO for vertex data */ - struct gl_context *ctx = exec->ctx; - unsigned i; - - /* True VBOs should already be unmapped - */ - if (exec->vtx.buffer_map) { - ASSERT(exec->vtx.bufferobj->Name == 0 || - exec->vtx.bufferobj->Name == IMM_BUFFER_NAME); - if (exec->vtx.bufferobj->Name == 0) { - _mesa_align_free(exec->vtx.buffer_map); - exec->vtx.buffer_map = NULL; - exec->vtx.buffer_ptr = NULL; - } - } - - /* Drop any outstanding reference to the vertex buffer - */ - for (i = 0; i < Elements(exec->vtx.arrays); i++) { - _mesa_reference_buffer_object(ctx, - &exec->vtx.arrays[i].BufferObj, - NULL); - } - - /* 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); - } - _mesa_reference_buffer_object(ctx, &exec->vtx.bufferobj, NULL); -} - - -/** - * Called upon first glVertex, glColor, glTexCoord, etc. - */ -void vbo_exec_BeginVertices( struct gl_context *ctx ) -{ - struct vbo_exec_context *exec = &vbo_context(ctx)->exec; - - vbo_exec_vtx_map( exec ); - - assert((ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT) == 0); - assert(exec->begin_vertices_flags); - - ctx->Driver.NeedFlush |= exec->begin_vertices_flags; -} - - -/** - * Called via ctx->Driver.FlushVertices() - * \param flags bitmask of FLUSH_STORED_VERTICES, FLUSH_UPDATE_CURRENT - */ -void vbo_exec_FlushVertices( struct gl_context *ctx, GLuint flags ) -{ - struct vbo_exec_context *exec = &vbo_context(ctx)->exec; - -#ifdef DEBUG - /* debug check: make sure we don't get called recursively */ - exec->flush_call_depth++; - assert(exec->flush_call_depth == 1); -#endif - - if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { - /* We've had glBegin but not glEnd! */ -#ifdef DEBUG - exec->flush_call_depth--; - assert(exec->flush_call_depth == 0); -#endif - return; - } - - /* Flush (draw), and make sure VBO is left unmapped when done */ - vbo_exec_FlushVertices_internal(exec, GL_TRUE); - - /* Need to do this to ensure BeginVertices gets called again: - */ - ctx->Driver.NeedFlush &= ~(FLUSH_UPDATE_CURRENT | flags); - -#ifdef DEBUG - exec->flush_call_depth--; - assert(exec->flush_call_depth == 0); -#endif -} - - -static void reset_attrfv( struct vbo_exec_context *exec ) -{ - GLuint i; - - for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) { - exec->vtx.attrsz[i] = 0; - exec->vtx.active_sz[i] = 0; - } - - exec->vtx.vertex_size = 0; -} - - -void GLAPIENTRY -_es_Color4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a) -{ - vbo_Color4f(r, g, b, a); -} - - -void GLAPIENTRY -_es_Normal3f(GLfloat x, GLfloat y, GLfloat z) -{ - vbo_Normal3f(x, y, z); -} - - -void GLAPIENTRY -_es_MultiTexCoord4f(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) -{ - vbo_MultiTexCoord4f(target, s, t, r, q); -} - - -void GLAPIENTRY -_es_Materialfv(GLenum face, GLenum pname, const GLfloat *params) -{ - vbo_Materialfv(face, pname, params); -} - - -void GLAPIENTRY -_es_Materialf(GLenum face, GLenum pname, GLfloat param) -{ - GLfloat p[4]; - p[0] = param; - p[1] = p[2] = p[3] = 0.0F; - vbo_Materialfv(face, pname, p); -} - - -/** - * A special version of glVertexAttrib4f that does not treat index 0 as - * VBO_ATTRIB_POS. - */ -static void -VertexAttrib4f_nopos(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) -{ - GET_CURRENT_CONTEXT(ctx); - if (index < MAX_VERTEX_GENERIC_ATTRIBS) - ATTR(VBO_ATTRIB_GENERIC0 + index, 4, x, y, z, w); - else - ERROR(GL_INVALID_VALUE); -} - -void GLAPIENTRY -_es_VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) -{ - VertexAttrib4f_nopos(index, x, y, z, w); -} - - -void GLAPIENTRY -_es_VertexAttrib1f(GLuint indx, GLfloat x) -{ - VertexAttrib4f_nopos(indx, x, 0.0f, 0.0f, 1.0f); -} - - -void GLAPIENTRY -_es_VertexAttrib1fv(GLuint indx, const GLfloat* values) -{ - VertexAttrib4f_nopos(indx, values[0], 0.0f, 0.0f, 1.0f); -} - - -void GLAPIENTRY -_es_VertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) -{ - VertexAttrib4f_nopos(indx, x, y, 0.0f, 1.0f); -} - - -void GLAPIENTRY -_es_VertexAttrib2fv(GLuint indx, const GLfloat* values) -{ - VertexAttrib4f_nopos(indx, values[0], values[1], 0.0f, 1.0f); -} - - -void GLAPIENTRY -_es_VertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) -{ - VertexAttrib4f_nopos(indx, x, y, z, 1.0f); -} - - -void GLAPIENTRY -_es_VertexAttrib3fv(GLuint indx, const GLfloat* values) -{ - VertexAttrib4f_nopos(indx, values[0], values[1], values[2], 1.0f); -} - - -void GLAPIENTRY -_es_VertexAttrib4fv(GLuint indx, const GLfloat* values) -{ - VertexAttrib4f_nopos(indx, values[0], values[1], values[2], values[3]); -} +/************************************************************************** + +Copyright 2002-2008 Tungsten Graphics Inc., Cedar Park, Texas. + +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 +on the rights to use, copy, modify, merge, publish, distribute, sub +license, 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 (including the next +paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL +TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS 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/context.h" +#include "main/macros.h" +#include "main/mfeatures.h" +#include "main/vtxfmt.h" +#include "main/dlist.h" +#include "main/eval.h" +#include "main/state.h" +#include "main/light.h" +#include "main/api_arrayelt.h" +#include "main/api_noop.h" +#include "main/dispatch.h" + +#include "vbo_context.h" + +#ifdef ERROR +#undef ERROR +#endif + + +/** ID/name for immediate-mode VBO */ +#define IMM_BUFFER_NAME 0xaabbccdd + + +static void reset_attrfv( struct vbo_exec_context *exec ); + + +/** + * Close off the last primitive, execute the buffer, restart the + * primitive. + */ +static void vbo_exec_wrap_buffers( struct vbo_exec_context *exec ) +{ + if (exec->vtx.prim_count == 0) { + exec->vtx.copied.nr = 0; + exec->vtx.vert_count = 0; + exec->vtx.buffer_ptr = exec->vtx.buffer_map; + } + else { + GLuint last_begin = exec->vtx.prim[exec->vtx.prim_count-1].begin; + GLuint last_count; + + if (exec->ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { + GLint i = exec->vtx.prim_count - 1; + assert(i >= 0); + exec->vtx.prim[i].count = (exec->vtx.vert_count - + exec->vtx.prim[i].start); + } + + last_count = exec->vtx.prim[exec->vtx.prim_count-1].count; + + /* Execute the buffer and save copied vertices. + */ + if (exec->vtx.vert_count) + vbo_exec_vtx_flush( exec, GL_FALSE ); + else { + exec->vtx.prim_count = 0; + exec->vtx.copied.nr = 0; + } + + /* Emit a glBegin to start the new list. + */ + assert(exec->vtx.prim_count == 0); + + if (exec->ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { + exec->vtx.prim[0].mode = exec->ctx->Driver.CurrentExecPrimitive; + exec->vtx.prim[0].start = 0; + exec->vtx.prim[0].count = 0; + exec->vtx.prim_count++; + + if (exec->vtx.copied.nr == last_count) + exec->vtx.prim[0].begin = last_begin; + } + } +} + + +/** + * Deal with buffer wrapping where provoked by the vertex buffer + * filling up, as opposed to upgrade_vertex(). + */ +void vbo_exec_vtx_wrap( struct vbo_exec_context *exec ) +{ + GLfloat *data = exec->vtx.copied.buffer; + GLuint i; + + /* Run pipeline on current vertices, copy wrapped vertices + * to exec->vtx.copied. + */ + vbo_exec_wrap_buffers( exec ); + + /* Copy stored stored vertices to start of new list. + */ + assert(exec->vtx.max_vert - exec->vtx.vert_count > exec->vtx.copied.nr); + + for (i = 0 ; i < exec->vtx.copied.nr ; i++) { + memcpy( exec->vtx.buffer_ptr, data, + exec->vtx.vertex_size * sizeof(GLfloat)); + exec->vtx.buffer_ptr += exec->vtx.vertex_size; + data += exec->vtx.vertex_size; + exec->vtx.vert_count++; + } + + exec->vtx.copied.nr = 0; +} + + +/** + * Copy the active vertex's values to the ctx->Current fields. + */ +static void vbo_exec_copy_to_current( struct vbo_exec_context *exec ) +{ + struct gl_context *ctx = exec->ctx; + struct vbo_context *vbo = vbo_context(ctx); + GLuint i; + + for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) { + if (exec->vtx.attrsz[i]) { + /* Note: the exec->vtx.current[i] pointers point into the + * ctx->Current.Attrib and ctx->Light.Material.Attrib arrays. + */ + GLfloat *current = (GLfloat *)vbo->currval[i].Ptr; + GLfloat tmp[4]; + + COPY_CLEAN_4V(tmp, + exec->vtx.attrsz[i], + exec->vtx.attrptr[i]); + + if (memcmp(current, tmp, sizeof(tmp)) != 0) { + memcpy(current, tmp, sizeof(tmp)); + + /* Given that we explicitly state size here, there is no need + * for the COPY_CLEAN above, could just copy 16 bytes and be + * done. The only problem is when Mesa accesses ctx->Current + * directly. + */ + vbo->currval[i].Size = exec->vtx.attrsz[i]; + assert(vbo->currval[i].Type == GL_FLOAT); + vbo->currval[i]._ElementSize = vbo->currval[i].Size * sizeof(GLfloat); + + /* This triggers rather too much recalculation of Mesa state + * that doesn't get used (eg light positions). + */ + if (i >= VBO_ATTRIB_MAT_FRONT_AMBIENT && + i <= VBO_ATTRIB_MAT_BACK_INDEXES) + ctx->NewState |= _NEW_LIGHT; + + ctx->NewState |= _NEW_CURRENT_ATTRIB; + } + } + } + + /* Colormaterial -- this kindof sucks. + */ + if (ctx->Light.ColorMaterialEnabled && + exec->vtx.attrsz[VBO_ATTRIB_COLOR0]) { + _mesa_update_color_material(ctx, + ctx->Current.Attrib[VBO_ATTRIB_COLOR0]); + } +} + + +/** + * Copy current vertex attribute values into the current vertex. + */ +static void +vbo_exec_copy_from_current(struct vbo_exec_context *exec) +{ + struct gl_context *ctx = exec->ctx; + struct vbo_context *vbo = vbo_context(ctx); + GLint i; + + for (i = VBO_ATTRIB_POS + 1; i < VBO_ATTRIB_MAX; i++) { + const GLfloat *current = (GLfloat *) vbo->currval[i].Ptr; + switch (exec->vtx.attrsz[i]) { + case 4: exec->vtx.attrptr[i][3] = current[3]; + case 3: exec->vtx.attrptr[i][2] = current[2]; + case 2: exec->vtx.attrptr[i][1] = current[1]; + case 1: exec->vtx.attrptr[i][0] = current[0]; + break; + } + } +} + + +/** + * Flush existing data, set new attrib size, replay copied vertices. + * This is called when we transition from a small vertex attribute size + * to a larger one. Ex: glTexCoord2f -> glTexCoord4f. + * We need to go back over the previous 2-component texcoords and insert + * zero and one values. + */ +static void +vbo_exec_wrap_upgrade_vertex(struct vbo_exec_context *exec, + GLuint attr, GLuint newSize ) +{ + struct gl_context *ctx = exec->ctx; + struct vbo_context *vbo = vbo_context(ctx); + const GLint lastcount = exec->vtx.vert_count; + GLfloat *old_attrptr[VBO_ATTRIB_MAX]; + const GLuint old_vtx_size = exec->vtx.vertex_size; /* floats per vertex */ + const GLuint oldSize = exec->vtx.attrsz[attr]; + GLuint i; + + /* Run pipeline on current vertices, copy wrapped vertices + * to exec->vtx.copied. + */ + vbo_exec_wrap_buffers( exec ); + + if (unlikely(exec->vtx.copied.nr)) { + /* We're in the middle of a primitive, keep the old vertex + * format around to be able to translate the copied vertices to + * the new format. + */ + memcpy(old_attrptr, exec->vtx.attrptr, sizeof(old_attrptr)); + } + + if (unlikely(oldSize)) { + /* Do a COPY_TO_CURRENT to ensure back-copying works for the + * case when the attribute already exists in the vertex and is + * having its size increased. + */ + vbo_exec_copy_to_current( exec ); + } + + /* Heuristic: Attempt to isolate attributes received outside + * begin/end so that they don't bloat the vertices. + */ + if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END && + !oldSize && lastcount > 8 && exec->vtx.vertex_size) { + vbo_exec_copy_to_current( exec ); + reset_attrfv( exec ); + } + + /* Fix up sizes: + */ + exec->vtx.attrsz[attr] = newSize; + exec->vtx.vertex_size += newSize - oldSize; + exec->vtx.max_vert = ((VBO_VERT_BUFFER_SIZE - exec->vtx.buffer_used) / + (exec->vtx.vertex_size * sizeof(GLfloat))); + exec->vtx.vert_count = 0; + exec->vtx.buffer_ptr = exec->vtx.buffer_map; + + if (unlikely(oldSize)) { + /* Size changed, recalculate all the attrptr[] values + */ + GLfloat *tmp = exec->vtx.vertex; + + for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) { + if (exec->vtx.attrsz[i]) { + exec->vtx.attrptr[i] = tmp; + tmp += exec->vtx.attrsz[i]; + } + else + exec->vtx.attrptr[i] = NULL; /* will not be dereferenced */ + } + + /* Copy from current to repopulate the vertex with correct + * values. + */ + vbo_exec_copy_from_current( exec ); + } + else { + /* Just have to append the new attribute at the end */ + exec->vtx.attrptr[attr] = exec->vtx.vertex + + exec->vtx.vertex_size - newSize; + } + + /* Replay stored vertices to translate them + * to new format here. + * + * -- No need to replay - just copy piecewise + */ + if (unlikely(exec->vtx.copied.nr)) { + GLfloat *data = exec->vtx.copied.buffer; + GLfloat *dest = exec->vtx.buffer_ptr; + GLuint j; + + assert(exec->vtx.buffer_ptr == exec->vtx.buffer_map); + + for (i = 0 ; i < exec->vtx.copied.nr ; i++) { + for (j = 0 ; j < VBO_ATTRIB_MAX ; j++) { + GLuint sz = exec->vtx.attrsz[j]; + + if (sz) { + GLint old_offset = old_attrptr[j] - exec->vtx.vertex; + GLint new_offset = exec->vtx.attrptr[j] - exec->vtx.vertex; + + if (j == attr) { + if (oldSize) { + GLfloat tmp[4]; + COPY_CLEAN_4V(tmp, oldSize, data + old_offset); + COPY_SZ_4V(dest + new_offset, newSize, tmp); + } else { + GLfloat *current = (GLfloat *)vbo->currval[j].Ptr; + COPY_SZ_4V(dest + new_offset, sz, current); + } + } + else { + COPY_SZ_4V(dest + new_offset, sz, data + old_offset); + } + } + } + + data += old_vtx_size; + dest += exec->vtx.vertex_size; + } + + exec->vtx.buffer_ptr = dest; + exec->vtx.vert_count += exec->vtx.copied.nr; + exec->vtx.copied.nr = 0; + } +} + + +/** + * This is when a vertex attribute transitions to a different size. + * For example, we saw a bunch of glTexCoord2f() calls and now we got a + * glTexCoord4f() call. We promote the array from size=2 to size=4. + */ +static void +vbo_exec_fixup_vertex(struct gl_context *ctx, GLuint attr, GLuint newSize) +{ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + + if (newSize > exec->vtx.attrsz[attr]) { + /* New size is larger. Need to flush existing vertices and get + * an enlarged vertex format. + */ + vbo_exec_wrap_upgrade_vertex( exec, attr, newSize ); + } + else if (newSize < exec->vtx.active_sz[attr]) { + static const GLfloat id[4] = { 0, 0, 0, 1 }; + GLuint i; + + /* New size is smaller - just need to fill in some + * zeros. Don't need to flush or wrap. + */ + for (i = newSize; i <= exec->vtx.attrsz[attr]; i++) + exec->vtx.attrptr[attr][i-1] = id[i-1]; + } + + exec->vtx.active_sz[attr] = newSize; + + /* Does setting NeedFlush belong here? Necessitates resetting + * vtxfmt on each flush (otherwise flags won't get reset + * afterwards). + */ + if (attr == 0) + ctx->Driver.NeedFlush |= FLUSH_STORED_VERTICES; +} + + +/** + * This macro is used to implement all the glVertex, glColor, glTexCoord, + * glVertexAttrib, etc functions. + */ +#define ATTR( A, N, V0, V1, V2, V3 ) \ +do { \ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; \ + \ + if (unlikely(!(ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT))) \ + ctx->Driver.BeginVertices( ctx ); \ + \ + if (unlikely(exec->vtx.active_sz[A] != N)) \ + vbo_exec_fixup_vertex(ctx, A, N); \ + \ + { \ + GLfloat *dest = exec->vtx.attrptr[A]; \ + if (N>0) dest[0] = V0; \ + if (N>1) dest[1] = V1; \ + if (N>2) dest[2] = V2; \ + if (N>3) dest[3] = V3; \ + } \ + \ + if ((A) == 0) { \ + /* This is a glVertex call */ \ + GLuint i; \ + \ + for (i = 0; i < exec->vtx.vertex_size; i++) \ + exec->vtx.buffer_ptr[i] = exec->vtx.vertex[i]; \ + \ + exec->vtx.buffer_ptr += exec->vtx.vertex_size; \ + \ + /* Set FLUSH_STORED_VERTICES to indicate that there's now */ \ + /* something to draw (not just updating a color or texcoord).*/ \ + ctx->Driver.NeedFlush |= FLUSH_STORED_VERTICES; \ + \ + if (++exec->vtx.vert_count >= exec->vtx.max_vert) \ + vbo_exec_vtx_wrap( exec ); \ + } \ +} while (0) + + +#define ERROR(err) _mesa_error( ctx, err, __FUNCTION__ ) +#define TAG(x) vbo_##x + +#include "vbo_attrib_tmp.h" + + +#if FEATURE_beginend + + +#if FEATURE_evaluators + +static void GLAPIENTRY vbo_exec_EvalCoord1f( GLfloat u ) +{ + GET_CURRENT_CONTEXT( ctx ); + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + + { + GLint i; + if (exec->eval.recalculate_maps) + vbo_exec_eval_update( exec ); + + for (i = 0; i <= VBO_ATTRIB_TEX7; i++) { + if (exec->eval.map1[i].map) + if (exec->vtx.active_sz[i] != exec->eval.map1[i].sz) + vbo_exec_fixup_vertex( ctx, i, exec->eval.map1[i].sz ); + } + } + + + memcpy( exec->vtx.copied.buffer, exec->vtx.vertex, + exec->vtx.vertex_size * sizeof(GLfloat)); + + vbo_exec_do_EvalCoord1f( exec, u ); + + memcpy( exec->vtx.vertex, exec->vtx.copied.buffer, + exec->vtx.vertex_size * sizeof(GLfloat)); +} + +static void GLAPIENTRY vbo_exec_EvalCoord2f( GLfloat u, GLfloat v ) +{ + GET_CURRENT_CONTEXT( ctx ); + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + + { + GLint i; + if (exec->eval.recalculate_maps) + vbo_exec_eval_update( exec ); + + for (i = 0; i <= VBO_ATTRIB_TEX7; i++) { + if (exec->eval.map2[i].map) + if (exec->vtx.active_sz[i] != exec->eval.map2[i].sz) + vbo_exec_fixup_vertex( ctx, i, exec->eval.map2[i].sz ); + } + + if (ctx->Eval.AutoNormal) + if (exec->vtx.active_sz[VBO_ATTRIB_NORMAL] != 3) + vbo_exec_fixup_vertex( ctx, VBO_ATTRIB_NORMAL, 3 ); + } + + memcpy( exec->vtx.copied.buffer, exec->vtx.vertex, + exec->vtx.vertex_size * sizeof(GLfloat)); + + vbo_exec_do_EvalCoord2f( exec, u, v ); + + memcpy( exec->vtx.vertex, exec->vtx.copied.buffer, + exec->vtx.vertex_size * sizeof(GLfloat)); +} + +static void GLAPIENTRY vbo_exec_EvalCoord1fv( const GLfloat *u ) +{ + vbo_exec_EvalCoord1f( u[0] ); +} + +static void GLAPIENTRY vbo_exec_EvalCoord2fv( const GLfloat *u ) +{ + vbo_exec_EvalCoord2f( u[0], u[1] ); +} + +static void GLAPIENTRY vbo_exec_EvalPoint1( GLint i ) +{ + GET_CURRENT_CONTEXT( ctx ); + GLfloat du = ((ctx->Eval.MapGrid1u2 - ctx->Eval.MapGrid1u1) / + (GLfloat) ctx->Eval.MapGrid1un); + GLfloat u = i * du + ctx->Eval.MapGrid1u1; + + vbo_exec_EvalCoord1f( u ); +} + + +static void GLAPIENTRY vbo_exec_EvalPoint2( GLint i, GLint j ) +{ + GET_CURRENT_CONTEXT( ctx ); + GLfloat du = ((ctx->Eval.MapGrid2u2 - ctx->Eval.MapGrid2u1) / + (GLfloat) ctx->Eval.MapGrid2un); + GLfloat dv = ((ctx->Eval.MapGrid2v2 - ctx->Eval.MapGrid2v1) / + (GLfloat) ctx->Eval.MapGrid2vn); + GLfloat u = i * du + ctx->Eval.MapGrid2u1; + GLfloat v = j * dv + ctx->Eval.MapGrid2v1; + + vbo_exec_EvalCoord2f( u, v ); +} + +/* use noop eval mesh */ +#define vbo_exec_EvalMesh1 _mesa_noop_EvalMesh1 +#define vbo_exec_EvalMesh2 _mesa_noop_EvalMesh2 + +#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. + */ +static void GLAPIENTRY vbo_exec_Begin( GLenum mode ) +{ + GET_CURRENT_CONTEXT( ctx ); + + if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END) { + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + int i; + + if (ctx->NewState) { + _mesa_update_state( ctx ); + + CALL_Begin(ctx->Exec, (mode)); + return; + } + + if (!_mesa_valid_to_render(ctx, "glBegin")) { + return; + } + + /* Heuristic: attempt to isolate attributes occuring outside + * begin/end pairs. + */ + if (exec->vtx.vertex_size && !exec->vtx.attrsz[0]) + vbo_exec_FlushVertices_internal(exec, GL_FALSE); + + i = exec->vtx.prim_count++; + exec->vtx.prim[i].mode = mode; + exec->vtx.prim[i].begin = 1; + exec->vtx.prim[i].end = 0; + exec->vtx.prim[i].indexed = 0; + exec->vtx.prim[i].weak = 0; + exec->vtx.prim[i].pad = 0; + exec->vtx.prim[i].start = exec->vtx.vert_count; + exec->vtx.prim[i].count = 0; + exec->vtx.prim[i].num_instances = 1; + + ctx->Driver.CurrentExecPrimitive = mode; + } + else + _mesa_error( ctx, GL_INVALID_OPERATION, "glBegin" ); + +} + + +/** + * Called via glEnd. + */ +static void GLAPIENTRY vbo_exec_End( void ) +{ + GET_CURRENT_CONTEXT( ctx ); + + if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + int idx = exec->vtx.vert_count; + int i = exec->vtx.prim_count - 1; + + exec->vtx.prim[i].end = 1; + exec->vtx.prim[i].count = idx - exec->vtx.prim[i].start; + + ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END; + + if (exec->vtx.prim_count == VBO_MAX_PRIM) + vbo_exec_vtx_flush( exec, GL_FALSE ); + } + else + _mesa_error( ctx, GL_INVALID_OPERATION, "glEnd" ); +} + + +/** + * Called via glPrimitiveRestartNV() + */ +static void GLAPIENTRY +vbo_exec_PrimitiveRestartNV(void) +{ + GLenum curPrim; + GET_CURRENT_CONTEXT( ctx ); + + curPrim = ctx->Driver.CurrentExecPrimitive; + + if (curPrim == PRIM_OUTSIDE_BEGIN_END) { + _mesa_error( ctx, GL_INVALID_OPERATION, "glPrimitiveRestartNV" ); + } + else { + vbo_exec_End(); + vbo_exec_Begin(curPrim); + } +} + + + +static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec ) +{ + GLvertexformat *vfmt = &exec->vtxfmt; + + _MESA_INIT_ARRAYELT_VTXFMT(vfmt, _ae_); + + vfmt->Begin = vbo_exec_Begin; + vfmt->End = vbo_exec_End; + vfmt->PrimitiveRestartNV = vbo_exec_PrimitiveRestartNV; + + _MESA_INIT_DLIST_VTXFMT(vfmt, _mesa_); + _MESA_INIT_EVAL_VTXFMT(vfmt, vbo_exec_); + + vfmt->Rectf = _mesa_noop_Rectf; + + /* from attrib_tmp.h: + */ + vfmt->Color3f = vbo_Color3f; + vfmt->Color3fv = vbo_Color3fv; + vfmt->Color4f = vbo_Color4f; + vfmt->Color4fv = vbo_Color4fv; + vfmt->FogCoordfEXT = vbo_FogCoordfEXT; + vfmt->FogCoordfvEXT = vbo_FogCoordfvEXT; + vfmt->MultiTexCoord1fARB = vbo_MultiTexCoord1f; + vfmt->MultiTexCoord1fvARB = vbo_MultiTexCoord1fv; + vfmt->MultiTexCoord2fARB = vbo_MultiTexCoord2f; + vfmt->MultiTexCoord2fvARB = vbo_MultiTexCoord2fv; + vfmt->MultiTexCoord3fARB = vbo_MultiTexCoord3f; + vfmt->MultiTexCoord3fvARB = vbo_MultiTexCoord3fv; + vfmt->MultiTexCoord4fARB = vbo_MultiTexCoord4f; + vfmt->MultiTexCoord4fvARB = vbo_MultiTexCoord4fv; + vfmt->Normal3f = vbo_Normal3f; + vfmt->Normal3fv = vbo_Normal3fv; + vfmt->SecondaryColor3fEXT = vbo_SecondaryColor3fEXT; + vfmt->SecondaryColor3fvEXT = vbo_SecondaryColor3fvEXT; + vfmt->TexCoord1f = vbo_TexCoord1f; + vfmt->TexCoord1fv = vbo_TexCoord1fv; + vfmt->TexCoord2f = vbo_TexCoord2f; + vfmt->TexCoord2fv = vbo_TexCoord2fv; + vfmt->TexCoord3f = vbo_TexCoord3f; + vfmt->TexCoord3fv = vbo_TexCoord3fv; + vfmt->TexCoord4f = vbo_TexCoord4f; + vfmt->TexCoord4fv = vbo_TexCoord4fv; + vfmt->Vertex2f = vbo_Vertex2f; + vfmt->Vertex2fv = vbo_Vertex2fv; + vfmt->Vertex3f = vbo_Vertex3f; + vfmt->Vertex3fv = vbo_Vertex3fv; + vfmt->Vertex4f = vbo_Vertex4f; + vfmt->Vertex4fv = vbo_Vertex4fv; + + vfmt->VertexAttrib1fARB = vbo_VertexAttrib1fARB; + vfmt->VertexAttrib1fvARB = vbo_VertexAttrib1fvARB; + vfmt->VertexAttrib2fARB = vbo_VertexAttrib2fARB; + vfmt->VertexAttrib2fvARB = vbo_VertexAttrib2fvARB; + vfmt->VertexAttrib3fARB = vbo_VertexAttrib3fARB; + vfmt->VertexAttrib3fvARB = vbo_VertexAttrib3fvARB; + vfmt->VertexAttrib4fARB = vbo_VertexAttrib4fARB; + vfmt->VertexAttrib4fvARB = vbo_VertexAttrib4fvARB; + + vfmt->VertexAttrib1fNV = vbo_VertexAttrib1fNV; + vfmt->VertexAttrib1fvNV = vbo_VertexAttrib1fvNV; + vfmt->VertexAttrib2fNV = vbo_VertexAttrib2fNV; + vfmt->VertexAttrib2fvNV = vbo_VertexAttrib2fvNV; + vfmt->VertexAttrib3fNV = vbo_VertexAttrib3fNV; + vfmt->VertexAttrib3fvNV = vbo_VertexAttrib3fvNV; + vfmt->VertexAttrib4fNV = vbo_VertexAttrib4fNV; + vfmt->VertexAttrib4fvNV = vbo_VertexAttrib4fvNV; + + /* integer-valued */ + vfmt->VertexAttribI1i = vbo_VertexAttribI1i; + vfmt->VertexAttribI2i = vbo_VertexAttribI2i; + vfmt->VertexAttribI3i = vbo_VertexAttribI3i; + vfmt->VertexAttribI4i = vbo_VertexAttribI4i; + vfmt->VertexAttribI2iv = vbo_VertexAttribI2iv; + vfmt->VertexAttribI3iv = vbo_VertexAttribI3iv; + vfmt->VertexAttribI4iv = vbo_VertexAttribI4iv; + + /* unsigned integer-valued */ + vfmt->VertexAttribI1ui = vbo_VertexAttribI1ui; + vfmt->VertexAttribI2ui = vbo_VertexAttribI2ui; + vfmt->VertexAttribI3ui = vbo_VertexAttribI3ui; + vfmt->VertexAttribI4ui = vbo_VertexAttribI4ui; + vfmt->VertexAttribI2uiv = vbo_VertexAttribI2uiv; + vfmt->VertexAttribI3uiv = vbo_VertexAttribI3uiv; + vfmt->VertexAttribI4uiv = vbo_VertexAttribI4uiv; + + vfmt->Materialfv = vbo_Materialfv; + + vfmt->EdgeFlag = vbo_EdgeFlag; + vfmt->Indexf = vbo_Indexf; + vfmt->Indexfv = vbo_Indexfv; + +} + + +#else /* FEATURE_beginend */ + + +static void vbo_exec_vtxfmt_init( struct vbo_exec_context *exec ) +{ + /* silence warnings */ + (void) vbo_Color3f; + (void) vbo_Color3fv; + (void) vbo_Color4f; + (void) vbo_Color4fv; + (void) vbo_FogCoordfEXT; + (void) vbo_FogCoordfvEXT; + (void) vbo_MultiTexCoord1f; + (void) vbo_MultiTexCoord1fv; + (void) vbo_MultiTexCoord2f; + (void) vbo_MultiTexCoord2fv; + (void) vbo_MultiTexCoord3f; + (void) vbo_MultiTexCoord3fv; + (void) vbo_MultiTexCoord4f; + (void) vbo_MultiTexCoord4fv; + (void) vbo_Normal3f; + (void) vbo_Normal3fv; + (void) vbo_SecondaryColor3fEXT; + (void) vbo_SecondaryColor3fvEXT; + (void) vbo_TexCoord1f; + (void) vbo_TexCoord1fv; + (void) vbo_TexCoord2f; + (void) vbo_TexCoord2fv; + (void) vbo_TexCoord3f; + (void) vbo_TexCoord3fv; + (void) vbo_TexCoord4f; + (void) vbo_TexCoord4fv; + (void) vbo_Vertex2f; + (void) vbo_Vertex2fv; + (void) vbo_Vertex3f; + (void) vbo_Vertex3fv; + (void) vbo_Vertex4f; + (void) vbo_Vertex4fv; + + (void) vbo_VertexAttrib1fARB; + (void) vbo_VertexAttrib1fvARB; + (void) vbo_VertexAttrib2fARB; + (void) vbo_VertexAttrib2fvARB; + (void) vbo_VertexAttrib3fARB; + (void) vbo_VertexAttrib3fvARB; + (void) vbo_VertexAttrib4fARB; + (void) vbo_VertexAttrib4fvARB; + + (void) vbo_VertexAttrib1fNV; + (void) vbo_VertexAttrib1fvNV; + (void) vbo_VertexAttrib2fNV; + (void) vbo_VertexAttrib2fvNV; + (void) vbo_VertexAttrib3fNV; + (void) vbo_VertexAttrib3fvNV; + (void) vbo_VertexAttrib4fNV; + (void) vbo_VertexAttrib4fvNV; + + (void) vbo_Materialfv; + + (void) vbo_EdgeFlag; + (void) vbo_Indexf; + (void) vbo_Indexfv; +} + + +#endif /* FEATURE_beginend */ + + +/** + * Tell the VBO module to use a real OpenGL vertex buffer object to + * store accumulated immediate-mode vertex data. + * This replaces the malloced buffer which was created in + * vb_exec_vtx_init() below. + */ +void vbo_use_buffer_objects(struct gl_context *ctx) +{ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + /* Any buffer name but 0 can be used here since this bufferobj won't + * go into the bufferobj hashtable. + */ + GLuint bufName = IMM_BUFFER_NAME; + GLenum target = GL_ARRAY_BUFFER_ARB; + GLenum usage = GL_STREAM_DRAW_ARB; + GLsizei size = VBO_VERT_BUFFER_SIZE; + + /* Make sure this func is only used once */ + assert(exec->vtx.bufferobj == ctx->Shared->NullBufferObj); + if (exec->vtx.buffer_map) { + _mesa_align_free(exec->vtx.buffer_map); + exec->vtx.buffer_map = NULL; + exec->vtx.buffer_ptr = NULL; + } + + /* Allocate a real buffer object now */ + _mesa_reference_buffer_object(ctx, &exec->vtx.bufferobj, NULL); + exec->vtx.bufferobj = ctx->Driver.NewBufferObject(ctx, bufName, target); + ctx->Driver.BufferData(ctx, target, size, NULL, usage, exec->vtx.bufferobj); +} + + +/** + * If this function is called, all VBO buffers will be unmapped when + * we flush. + * Otherwise, if a simple command like glColor3f() is called and we flush, + * the current VBO may be left mapped. + */ +void +vbo_always_unmap_buffers(struct gl_context *ctx) +{ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + exec->begin_vertices_flags |= FLUSH_STORED_VERTICES; +} + + +void vbo_exec_vtx_init( struct vbo_exec_context *exec ) +{ + struct gl_context *ctx = exec->ctx; + struct vbo_context *vbo = vbo_context(ctx); + GLuint i; + + /* Allocate a buffer object. Will just reuse this object + * continuously, unless vbo_use_buffer_objects() is called to enable + * use of real VBOs. + */ + _mesa_reference_buffer_object(ctx, + &exec->vtx.bufferobj, + ctx->Shared->NullBufferObj); + + ASSERT(!exec->vtx.buffer_map); + exec->vtx.buffer_map = (GLfloat *)_mesa_align_malloc(VBO_VERT_BUFFER_SIZE, 64); + exec->vtx.buffer_ptr = exec->vtx.buffer_map; + + vbo_exec_vtxfmt_init( exec ); + + /* Hook our functions into the dispatch table. + */ + _mesa_install_exec_vtxfmt( ctx, &exec->vtxfmt ); + + for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) { + ASSERT(i < Elements(exec->vtx.attrsz)); + exec->vtx.attrsz[i] = 0; + ASSERT(i < Elements(exec->vtx.active_sz)); + exec->vtx.active_sz[i] = 0; + } + for (i = 0 ; i < VERT_ATTRIB_MAX; i++) { + ASSERT(i < Elements(exec->vtx.inputs)); + ASSERT(i < Elements(exec->vtx.arrays)); + exec->vtx.inputs[i] = &exec->vtx.arrays[i]; + } + + { + struct gl_client_array *arrays = exec->vtx.arrays; + unsigned i; + + memcpy(arrays, vbo->legacy_currval, 16 * sizeof(arrays[0])); + memcpy(arrays + 16, vbo->generic_currval, 16 * sizeof(arrays[0])); + + for (i = 0; i < 16; ++i) { + arrays[i ].BufferObj = NULL; + arrays[i + 16].BufferObj = NULL; + _mesa_reference_buffer_object(ctx, &arrays[i ].BufferObj, + vbo->legacy_currval[i].BufferObj); + _mesa_reference_buffer_object(ctx, &arrays[i + 16].BufferObj, + vbo->generic_currval[i].BufferObj); + } + } + + exec->vtx.vertex_size = 0; + + exec->begin_vertices_flags = FLUSH_UPDATE_CURRENT; +} + + +void vbo_exec_vtx_destroy( struct vbo_exec_context *exec ) +{ + /* using a real VBO for vertex data */ + struct gl_context *ctx = exec->ctx; + unsigned i; + + /* True VBOs should already be unmapped + */ + if (exec->vtx.buffer_map) { + ASSERT(exec->vtx.bufferobj->Name == 0 || + exec->vtx.bufferobj->Name == IMM_BUFFER_NAME); + if (exec->vtx.bufferobj->Name == 0) { + _mesa_align_free(exec->vtx.buffer_map); + exec->vtx.buffer_map = NULL; + exec->vtx.buffer_ptr = NULL; + } + } + + /* Drop any outstanding reference to the vertex buffer + */ + for (i = 0; i < Elements(exec->vtx.arrays); i++) { + _mesa_reference_buffer_object(ctx, + &exec->vtx.arrays[i].BufferObj, + NULL); + } + + /* 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); + } + _mesa_reference_buffer_object(ctx, &exec->vtx.bufferobj, NULL); +} + + +/** + * Called upon first glVertex, glColor, glTexCoord, etc. + */ +void vbo_exec_BeginVertices( struct gl_context *ctx ) +{ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + + vbo_exec_vtx_map( exec ); + + assert((ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT) == 0); + assert(exec->begin_vertices_flags); + + ctx->Driver.NeedFlush |= exec->begin_vertices_flags; +} + + +/** + * Called via ctx->Driver.FlushVertices() + * \param flags bitmask of FLUSH_STORED_VERTICES, FLUSH_UPDATE_CURRENT + */ +void vbo_exec_FlushVertices( struct gl_context *ctx, GLuint flags ) +{ + struct vbo_exec_context *exec = &vbo_context(ctx)->exec; + +#ifdef DEBUG + /* debug check: make sure we don't get called recursively */ + exec->flush_call_depth++; + assert(exec->flush_call_depth == 1); +#endif + + if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { + /* We've had glBegin but not glEnd! */ +#ifdef DEBUG + exec->flush_call_depth--; + assert(exec->flush_call_depth == 0); +#endif + return; + } + + /* Flush (draw), and make sure VBO is left unmapped when done */ + vbo_exec_FlushVertices_internal(exec, GL_TRUE); + + /* Need to do this to ensure BeginVertices gets called again: + */ + ctx->Driver.NeedFlush &= ~(FLUSH_UPDATE_CURRENT | flags); + +#ifdef DEBUG + exec->flush_call_depth--; + assert(exec->flush_call_depth == 0); +#endif +} + + +static void reset_attrfv( struct vbo_exec_context *exec ) +{ + GLuint i; + + for (i = 0 ; i < VBO_ATTRIB_MAX ; i++) { + exec->vtx.attrsz[i] = 0; + exec->vtx.active_sz[i] = 0; + } + + exec->vtx.vertex_size = 0; +} + + +void GLAPIENTRY +_es_Color4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a) +{ + vbo_Color4f(r, g, b, a); +} + + +void GLAPIENTRY +_es_Normal3f(GLfloat x, GLfloat y, GLfloat z) +{ + vbo_Normal3f(x, y, z); +} + + +void GLAPIENTRY +_es_MultiTexCoord4f(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) +{ + vbo_MultiTexCoord4f(target, s, t, r, q); +} + + +void GLAPIENTRY +_es_Materialfv(GLenum face, GLenum pname, const GLfloat *params) +{ + vbo_Materialfv(face, pname, params); +} + + +void GLAPIENTRY +_es_Materialf(GLenum face, GLenum pname, GLfloat param) +{ + GLfloat p[4]; + p[0] = param; + p[1] = p[2] = p[3] = 0.0F; + vbo_Materialfv(face, pname, p); +} + + +/** + * A special version of glVertexAttrib4f that does not treat index 0 as + * VBO_ATTRIB_POS. + */ +static void +VertexAttrib4f_nopos(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + GET_CURRENT_CONTEXT(ctx); + if (index < MAX_VERTEX_GENERIC_ATTRIBS) + ATTR(VBO_ATTRIB_GENERIC0 + index, 4, x, y, z, w); + else + ERROR(GL_INVALID_VALUE); +} + +void GLAPIENTRY +_es_VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + VertexAttrib4f_nopos(index, x, y, z, w); +} + + +void GLAPIENTRY +_es_VertexAttrib1f(GLuint indx, GLfloat x) +{ + VertexAttrib4f_nopos(indx, x, 0.0f, 0.0f, 1.0f); +} + + +void GLAPIENTRY +_es_VertexAttrib1fv(GLuint indx, const GLfloat* values) +{ + VertexAttrib4f_nopos(indx, values[0], 0.0f, 0.0f, 1.0f); +} + + +void GLAPIENTRY +_es_VertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) +{ + VertexAttrib4f_nopos(indx, x, y, 0.0f, 1.0f); +} + + +void GLAPIENTRY +_es_VertexAttrib2fv(GLuint indx, const GLfloat* values) +{ + VertexAttrib4f_nopos(indx, values[0], values[1], 0.0f, 1.0f); +} + + +void GLAPIENTRY +_es_VertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) +{ + VertexAttrib4f_nopos(indx, x, y, z, 1.0f); +} + + +void GLAPIENTRY +_es_VertexAttrib3fv(GLuint indx, const GLfloat* values) +{ + VertexAttrib4f_nopos(indx, values[0], values[1], values[2], 1.0f); +} + + +void GLAPIENTRY +_es_VertexAttrib4fv(GLuint indx, const GLfloat* values) +{ + VertexAttrib4f_nopos(indx, values[0], values[1], values[2], values[3]); +} diff --git a/mesalib/src/mesa/vbo/vbo_exec_array.c b/mesalib/src/mesa/vbo/vbo_exec_array.c index 98d6badc4..a189ab76b 100644 --- a/mesalib/src/mesa/vbo/vbo_exec_array.c +++ b/mesalib/src/mesa/vbo/vbo_exec_array.c @@ -1,1336 +1,1341 @@ -/************************************************************************** - * - * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. - * Copyright 2009 VMware, Inc. - * 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, sub license, 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 (including the - * next paragraph) 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 NON-INFRINGEMENT. - * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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. - * - **************************************************************************/ - -#include "main/glheader.h" -#include "main/context.h" -#include "main/state.h" -#include "main/api_validate.h" -#include "main/varray.h" -#include "main/bufferobj.h" -#include "main/enums.h" -#include "main/macros.h" - -#include "vbo_context.h" - - -/** - * All vertex buffers should be in an unmapped state when we're about - * to draw. This debug function checks that. - */ -static void -check_buffers_are_unmapped(const struct gl_client_array **inputs) -{ -#ifdef DEBUG - GLuint i; - - for (i = 0; i < VERT_ATTRIB_MAX; i++) { - if (inputs[i]) { - struct gl_buffer_object *obj = inputs[i]->BufferObj; - assert(!_mesa_bufferobj_mapped(obj)); - (void) obj; - } - } -#endif -} - - -/** - * A debug function that may be called from other parts of Mesa as - * needed during debugging. - */ -void -vbo_check_buffers_are_unmapped(struct gl_context *ctx) -{ - struct vbo_context *vbo = vbo_context(ctx); - struct vbo_exec_context *exec = &vbo->exec; - /* check the current vertex arrays */ - check_buffers_are_unmapped(exec->array.inputs); - /* check the current glBegin/glVertex/glEnd-style VBO */ - assert(!_mesa_bufferobj_mapped(exec->vtx.bufferobj)); -} - - - -/** - * Compute min and max elements by scanning the index buffer for - * glDraw[Range]Elements() calls. - * If primitive restart is enabled, we need to ignore restart - * indexes when computing min/max. - */ -void -vbo_get_minmax_index(struct gl_context *ctx, - const struct _mesa_prim *prim, - const struct _mesa_index_buffer *ib, - GLuint *min_index, GLuint *max_index) -{ - const GLboolean restart = ctx->Array.PrimitiveRestart; - const GLuint restartIndex = ctx->Array.RestartIndex; - const GLuint count = prim->count; - const void *indices; - 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); - } else { - indices = ib->ptr; - } - - switch (ib->type) { - case GL_UNSIGNED_INT: { - const GLuint *ui_indices = (const GLuint *)indices; - GLuint max_ui = 0; - GLuint min_ui = ~0U; - if (restart) { - for (i = 0; i < count; i++) { - if (ui_indices[i] != restartIndex) { - if (ui_indices[i] > max_ui) max_ui = ui_indices[i]; - if (ui_indices[i] < min_ui) min_ui = ui_indices[i]; - } - } - } - else { - for (i = 0; i < count; i++) { - if (ui_indices[i] > max_ui) max_ui = ui_indices[i]; - if (ui_indices[i] < min_ui) min_ui = ui_indices[i]; - } - } - *min_index = min_ui; - *max_index = max_ui; - break; - } - case GL_UNSIGNED_SHORT: { - const GLushort *us_indices = (const GLushort *)indices; - GLuint max_us = 0; - GLuint min_us = ~0U; - if (restart) { - for (i = 0; i < count; i++) { - if (us_indices[i] != restartIndex) { - if (us_indices[i] > max_us) max_us = us_indices[i]; - if (us_indices[i] < min_us) min_us = us_indices[i]; - } - } - } - else { - for (i = 0; i < count; i++) { - if (us_indices[i] > max_us) max_us = us_indices[i]; - if (us_indices[i] < min_us) min_us = us_indices[i]; - } - } - *min_index = min_us; - *max_index = max_us; - break; - } - case GL_UNSIGNED_BYTE: { - const GLubyte *ub_indices = (const GLubyte *)indices; - GLuint max_ub = 0; - GLuint min_ub = ~0U; - if (restart) { - for (i = 0; i < count; i++) { - if (ub_indices[i] != restartIndex) { - if (ub_indices[i] > max_ub) max_ub = ub_indices[i]; - if (ub_indices[i] < min_ub) min_ub = ub_indices[i]; - } - } - } - else { - for (i = 0; i < count; i++) { - if (ub_indices[i] > max_ub) max_ub = ub_indices[i]; - if (ub_indices[i] < min_ub) min_ub = ub_indices[i]; - } - } - *min_index = min_ub; - *max_index = max_ub; - break; - } - default: - assert(0); - break; - } - - if (_mesa_is_bufferobj(ib->obj)) { - ctx->Driver.UnmapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER_ARB, ib->obj); - } -} - - -/** - * Check that element 'j' of the array has reasonable data. - * Map VBO if needed. - * For debugging purposes; not normally used. - */ -static void -check_array_data(struct gl_context *ctx, struct gl_client_array *array, - GLuint attrib, GLuint j) -{ - if (array->Enabled) { - const void *data = array->Ptr; - if (_mesa_is_bufferobj(array->BufferObj)) { - if (!array->BufferObj->Pointer) { - /* need to map now */ - array->BufferObj->Pointer = - ctx->Driver.MapBuffer(ctx, GL_ARRAY_BUFFER_ARB, - GL_READ_ONLY, array->BufferObj); - } - data = ADD_POINTERS(data, array->BufferObj->Pointer); - } - switch (array->Type) { - case GL_FLOAT: - { - GLfloat *f = (GLfloat *) ((GLubyte *) data + array->StrideB * j); - GLint k; - for (k = 0; k < array->Size; k++) { - if (IS_INF_OR_NAN(f[k]) || - f[k] >= 1.0e20 || f[k] <= -1.0e10) { - printf("Bad array data:\n"); - printf(" Element[%u].%u = %f\n", j, k, f[k]); - printf(" Array %u at %p\n", attrib, (void* ) array); - printf(" Type 0x%x, Size %d, Stride %d\n", - array->Type, array->Size, array->Stride); - printf(" Address/offset %p in Buffer Object %u\n", - array->Ptr, array->BufferObj->Name); - f[k] = 1.0; /* XXX replace the bad value! */ - } - /*assert(!IS_INF_OR_NAN(f[k]));*/ - } - } - break; - default: - ; - } - } -} - - -/** - * Unmap the buffer object referenced by given array, if mapped. - */ -static void -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); - } -} - - -/** - * Examine the array's data for NaNs, etc. - * For debug purposes; not normally used. - */ -static void -check_draw_elements_data(struct gl_context *ctx, GLsizei count, GLenum elemType, - const void *elements, GLint basevertex) -{ - struct gl_array_object *arrayObj = ctx->Array.ArrayObj; - const void *elemMap; - 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); - elements = ADD_POINTERS(elements, elemMap); - } - - for (i = 0; i < count; i++) { - GLuint j; - - /* j = element[i] */ - switch (elemType) { - case GL_UNSIGNED_BYTE: - j = ((const GLubyte *) elements)[i]; - break; - case GL_UNSIGNED_SHORT: - j = ((const GLushort *) elements)[i]; - break; - case GL_UNSIGNED_INT: - j = ((const GLuint *) elements)[i]; - break; - default: - assert(0); - } - - /* check element j of each enabled array */ - check_array_data(ctx, &arrayObj->Vertex, VERT_ATTRIB_POS, j); - check_array_data(ctx, &arrayObj->Normal, VERT_ATTRIB_NORMAL, j); - check_array_data(ctx, &arrayObj->Color, VERT_ATTRIB_COLOR0, j); - check_array_data(ctx, &arrayObj->SecondaryColor, VERT_ATTRIB_COLOR1, j); - for (k = 0; k < Elements(arrayObj->TexCoord); k++) { - check_array_data(ctx, &arrayObj->TexCoord[k], VERT_ATTRIB_TEX0 + k, j); - } - for (k = 0; k < Elements(arrayObj->VertexAttrib); k++) { - check_array_data(ctx, &arrayObj->VertexAttrib[k], - VERT_ATTRIB_GENERIC0 + k, j); - } - } - - if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) { - ctx->Driver.UnmapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER_ARB, - ctx->Array.ElementArrayBufferObj); - } - - unmap_array_buffer(ctx, &arrayObj->Vertex); - unmap_array_buffer(ctx, &arrayObj->Normal); - unmap_array_buffer(ctx, &arrayObj->Color); - for (k = 0; k < Elements(arrayObj->TexCoord); k++) { - unmap_array_buffer(ctx, &arrayObj->TexCoord[k]); - } - for (k = 0; k < Elements(arrayObj->VertexAttrib); k++) { - unmap_array_buffer(ctx, &arrayObj->VertexAttrib[k]); - } -} - - -/** - * Check array data, looking for NaNs, etc. - */ -static void -check_draw_arrays_data(struct gl_context *ctx, GLint start, GLsizei count) -{ - /* TO DO */ -} - - -/** - * Print info/data for glDrawArrays(), for debugging. - */ -static void -print_draw_arrays(struct gl_context *ctx, - GLenum mode, GLint start, GLsizei count) -{ - struct vbo_context *vbo = vbo_context(ctx); - struct vbo_exec_context *exec = &vbo->exec; - int i; - - printf("vbo_exec_DrawArrays(mode 0x%x, start %d, count %d):\n", - mode, start, count); - - for (i = 0; i < 32; i++) { - GLuint bufName = exec->array.inputs[i]->BufferObj->Name; - GLint stride = exec->array.inputs[i]->Stride; - printf("attr %2d: size %d stride %d enabled %d " - "ptr %p Bufobj %u\n", - i, - exec->array.inputs[i]->Size, - stride, - /*exec->array.inputs[i]->Enabled,*/ - exec->array.legacy_array[i]->Enabled, - exec->array.inputs[i]->Ptr, - bufName); - - if (bufName) { - struct gl_buffer_object *buf = _mesa_lookup_bufferobj(ctx, bufName); - GLubyte *p = ctx->Driver.MapBuffer(ctx, GL_ARRAY_BUFFER_ARB, - GL_READ_ONLY_ARB, buf); - int offset = (int) (GLintptr) exec->array.inputs[i]->Ptr; - float *f = (float *) (p + offset); - int *k = (int *) f; - int i; - int n = (count * stride) / 4; - if (n > 32) - n = 32; - printf(" Data at offset %d:\n", offset); - 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, buf); - } - } -} - - -/** - * Bind the VBO executor to the current vertex array object prior - * to drawing. - * - * Just translate the arrayobj into a sane layout. - */ -static void -bind_array_obj(struct gl_context *ctx) -{ - struct vbo_context *vbo = vbo_context(ctx); - struct vbo_exec_context *exec = &vbo->exec; - struct gl_array_object *arrayObj = ctx->Array.ArrayObj; - GLuint i; - - /* TODO: Fix the ArrayObj struct to keep legacy arrays in an array - * rather than as individual named arrays. Then this function can - * go away. - */ - exec->array.legacy_array[VERT_ATTRIB_POS] = &arrayObj->Vertex; - exec->array.legacy_array[VERT_ATTRIB_WEIGHT] = &arrayObj->Weight; - exec->array.legacy_array[VERT_ATTRIB_NORMAL] = &arrayObj->Normal; - exec->array.legacy_array[VERT_ATTRIB_COLOR0] = &arrayObj->Color; - exec->array.legacy_array[VERT_ATTRIB_COLOR1] = &arrayObj->SecondaryColor; - exec->array.legacy_array[VERT_ATTRIB_FOG] = &arrayObj->FogCoord; - exec->array.legacy_array[VERT_ATTRIB_COLOR_INDEX] = &arrayObj->Index; - if (arrayObj->PointSize.Enabled) { - /* this aliases COLOR_INDEX */ - exec->array.legacy_array[VERT_ATTRIB_POINT_SIZE] = &arrayObj->PointSize; - } - exec->array.legacy_array[VERT_ATTRIB_EDGEFLAG] = &arrayObj->EdgeFlag; - - for (i = 0; i < Elements(arrayObj->TexCoord); i++) - exec->array.legacy_array[VERT_ATTRIB_TEX0 + i] = &arrayObj->TexCoord[i]; - - for (i = 0; i < Elements(arrayObj->VertexAttrib); i++) { - assert(i < Elements(exec->array.generic_array)); - exec->array.generic_array[i] = &arrayObj->VertexAttrib[i]; - } - - exec->array.array_obj = arrayObj->Name; -} - - -/** - * Set the vbo->exec->inputs[] pointers to point to the enabled - * vertex arrays. This depends on the current vertex program/shader - * being executed because of whether or not generic vertex arrays - * alias the conventional vertex arrays. - * For arrays that aren't enabled, we set the input[attrib] pointer - * to point at a zero-stride current value "array". - */ -static void -recalculate_input_bindings(struct gl_context *ctx) -{ - struct vbo_context *vbo = vbo_context(ctx); - struct vbo_exec_context *exec = &vbo->exec; - const struct gl_client_array **inputs = &exec->array.inputs[0]; - GLbitfield const_inputs = 0x0; - GLuint i; - - exec->array.program_mode = get_program_mode(ctx); - exec->array.enabled_flags = ctx->Array.ArrayObj->_Enabled; - - switch (exec->array.program_mode) { - case VP_NONE: - /* When no vertex program is active (or the vertex program is generated - * from fixed-function state). We put the material values into the - * generic slots. This is the only situation where material values - * are available as per-vertex attributes. - */ - for (i = 0; i <= VERT_ATTRIB_TEX7; i++) { - if (exec->array.legacy_array[i]->Enabled) - inputs[i] = exec->array.legacy_array[i]; - else { - inputs[i] = &vbo->legacy_currval[i]; - const_inputs |= 1 << i; - } - } - - for (i = 0; i < MAT_ATTRIB_MAX; i++) { - inputs[VERT_ATTRIB_GENERIC0 + i] = &vbo->mat_currval[i]; - const_inputs |= 1 << (VERT_ATTRIB_GENERIC0 + i); - } - - /* Could use just about anything, just to fill in the empty - * slots: - */ - for (i = MAT_ATTRIB_MAX; i < VERT_ATTRIB_MAX - VERT_ATTRIB_GENERIC0; i++) { - inputs[VERT_ATTRIB_GENERIC0 + i] = &vbo->generic_currval[i]; - const_inputs |= 1 << (VERT_ATTRIB_GENERIC0 + i); - } - - /* There is no need to make _NEW_ARRAY dirty here for the TnL program, - * because it already takes care of invalidating the state necessary - * to revalidate vertex arrays. Not marking the state as dirty also - * improves performance (quite significantly in some apps). - */ - if (!ctx->VertexProgram._MaintainTnlProgram) - ctx->NewState |= _NEW_ARRAY; - break; - - case VP_NV: - /* NV_vertex_program - attribute arrays alias and override - * conventional, legacy arrays. No materials, and the generic - * slots are vacant. - */ - for (i = 0; i <= VERT_ATTRIB_TEX7; i++) { - if (exec->array.generic_array[i]->Enabled) - inputs[i] = exec->array.generic_array[i]; - else if (exec->array.legacy_array[i]->Enabled) - inputs[i] = exec->array.legacy_array[i]; - else { - inputs[i] = &vbo->legacy_currval[i]; - const_inputs |= 1 << i; - } - } - - /* Could use just about anything, just to fill in the empty - * slots: - */ - for (i = VERT_ATTRIB_GENERIC0; i < VERT_ATTRIB_MAX; i++) { - inputs[i] = &vbo->generic_currval[i - VERT_ATTRIB_GENERIC0]; - const_inputs |= 1 << i; - } - - ctx->NewState |= _NEW_ARRAY; - break; - - case VP_ARB: - /* GL_ARB_vertex_program or GLSL vertex shader - Only the generic[0] - * attribute array aliases and overrides the legacy position array. - * - * Otherwise, legacy attributes available in the legacy slots, - * generic attributes in the generic slots and materials are not - * available as per-vertex attributes. - */ - if (exec->array.generic_array[0]->Enabled) - inputs[0] = exec->array.generic_array[0]; - else if (exec->array.legacy_array[0]->Enabled) - inputs[0] = exec->array.legacy_array[0]; - else { - inputs[0] = &vbo->legacy_currval[0]; - const_inputs |= 1 << 0; - } - - for (i = 1; i <= VERT_ATTRIB_TEX7; i++) { - if (exec->array.legacy_array[i]->Enabled) - inputs[i] = exec->array.legacy_array[i]; - else { - inputs[i] = &vbo->legacy_currval[i]; - const_inputs |= 1 << i; - } - } - - for (i = 0; i < MAX_VERTEX_GENERIC_ATTRIBS; i++) { - if (exec->array.generic_array[i]->Enabled) - inputs[VERT_ATTRIB_GENERIC0 + i] = exec->array.generic_array[i]; - else { - inputs[VERT_ATTRIB_GENERIC0 + i] = &vbo->generic_currval[i]; - const_inputs |= 1 << (VERT_ATTRIB_GENERIC0 + i); - } - } - - ctx->NewState |= _NEW_ARRAY; - break; - } - - _mesa_set_varying_vp_inputs( ctx, ~const_inputs ); -} - - -/** - * Examine the enabled vertex arrays to set the exec->array.inputs[] values. - * These will point to the arrays to actually use for drawing. Some will - * be user-provided arrays, other will be zero-stride const-valued arrays. - * Note that this might set the _NEW_ARRAY dirty flag so state validation - * must be done after this call. - */ -static void -bind_arrays(struct gl_context *ctx) -{ - if (!ctx->Array.RebindArrays) { - return; - } - - bind_array_obj(ctx); - recalculate_input_bindings(ctx); - ctx->Array.RebindArrays = GL_FALSE; -} - - -/** - * Helper function called by the other DrawArrays() functions below. - * This is where we handle primitive restart for drawing non-indexed - * arrays. If primitive restart is enabled, it typically means - * splitting one DrawArrays() into two. - */ -static void -vbo_draw_arrays(struct gl_context *ctx, GLenum mode, GLint start, - GLsizei count, GLuint numInstances) -{ - struct vbo_context *vbo = vbo_context(ctx); - struct vbo_exec_context *exec = &vbo->exec; - struct _mesa_prim prim[2]; - - bind_arrays(ctx); - - /* Again... because we may have changed the bitmask of per-vertex varying - * attributes. If we regenerate the fixed-function vertex program now - * we may be able to prune down the number of vertex attributes which we - * need in the shader. - */ - if (ctx->NewState) - _mesa_update_state(ctx); - - prim[0].begin = 1; - prim[0].end = 1; - prim[0].weak = 0; - prim[0].pad = 0; - prim[0].mode = mode; - prim[0].start = 0; /* filled in below */ - prim[0].count = 0; /* filled in below */ - prim[0].indexed = 0; - prim[0].basevertex = 0; - prim[0].num_instances = numInstances; - - /* Implement the primitive restart index */ - if (ctx->Array.PrimitiveRestart && ctx->Array.RestartIndex < count) { - GLuint primCount = 0; - - if (ctx->Array.RestartIndex == start) { - /* special case: RestartIndex at beginning */ - if (count > 1) { - prim[0].start = start + 1; - prim[0].count = count - 1; - primCount = 1; - } - } - else if (ctx->Array.RestartIndex == start + count - 1) { - /* special case: RestartIndex at end */ - if (count > 1) { - prim[0].start = start; - prim[0].count = count - 1; - primCount = 1; - } - } - else { - /* general case: RestartIndex in middle, split into two prims */ - prim[0].start = start; - prim[0].count = ctx->Array.RestartIndex - start; - - prim[1] = prim[0]; - prim[1].start = ctx->Array.RestartIndex + 1; - prim[1].count = count - prim[1].start; - - primCount = 2; - } - - if (primCount > 0) { - /* draw one or two prims */ - check_buffers_are_unmapped(exec->array.inputs); - vbo->draw_prims(ctx, exec->array.inputs, prim, primCount, NULL, - GL_TRUE, start, start + count - 1); - } - } - else { - /* no prim restart */ - prim[0].start = start; - prim[0].count = count; - - check_buffers_are_unmapped(exec->array.inputs); - vbo->draw_prims(ctx, exec->array.inputs, prim, 1, NULL, - GL_TRUE, start, start + count - 1); - } -} - - - -/** - * Called from glDrawArrays when in immediate mode (not display list mode). - */ -static void GLAPIENTRY -vbo_exec_DrawArrays(GLenum mode, GLint start, GLsizei count) -{ - GET_CURRENT_CONTEXT(ctx); - - if (MESA_VERBOSE & VERBOSE_DRAW) - _mesa_debug(ctx, "glDrawArrays(%s, %d, %d)\n", - _mesa_lookup_enum_by_nr(mode), start, count); - - if (!_mesa_validate_DrawArrays( ctx, mode, start, count )) - return; - - FLUSH_CURRENT( ctx, 0 ); - - if (!_mesa_valid_to_render(ctx, "glDrawArrays")) { - return; - } - - if (0) - check_draw_arrays_data(ctx, start, count); - - vbo_draw_arrays(ctx, mode, start, count, 1); - - if (0) - print_draw_arrays(ctx, mode, start, count); -} - - -/** - * Called from glDrawArraysInstanced when in immediate mode (not - * display list mode). - */ -static void GLAPIENTRY -vbo_exec_DrawArraysInstanced(GLenum mode, GLint start, GLsizei count, - GLsizei numInstances) -{ - GET_CURRENT_CONTEXT(ctx); - - if (MESA_VERBOSE & VERBOSE_DRAW) - _mesa_debug(ctx, "glDrawArraysInstanced(%s, %d, %d, %d)\n", - _mesa_lookup_enum_by_nr(mode), start, count, numInstances); - - if (!_mesa_validate_DrawArraysInstanced(ctx, mode, start, count, numInstances)) - return; - - FLUSH_CURRENT( ctx, 0 ); - - if (!_mesa_valid_to_render(ctx, "glDrawArraysInstanced")) { - return; - } - - if (0) - check_draw_arrays_data(ctx, start, count); - - vbo_draw_arrays(ctx, mode, start, count, numInstances); - - if (0) - print_draw_arrays(ctx, mode, start, count); -} - - -/** - * Map GL_ELEMENT_ARRAY_BUFFER and print contents. - * For debugging. - */ -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); - switch (type) { - case GL_UNSIGNED_BYTE: - { - const GLubyte *us = (const GLubyte *) map; - GLint i; - for (i = 0; i < ctx->Array.ElementArrayBufferObj->Size; i++) { - printf("%02x ", us[i]); - if (i % 32 == 31) - printf("\n"); - } - printf("\n"); - } - break; - case GL_UNSIGNED_SHORT: - { - const GLushort *us = (const GLushort *) map; - GLint i; - for (i = 0; i < ctx->Array.ElementArrayBufferObj->Size / 2; i++) { - printf("%04x ", us[i]); - if (i % 16 == 15) - printf("\n"); - } - printf("\n"); - } - break; - case GL_UNSIGNED_INT: - { - const GLuint *us = (const GLuint *) map; - GLint i; - for (i = 0; i < ctx->Array.ElementArrayBufferObj->Size / 4; i++) { - printf("%08x ", us[i]); - if (i % 8 == 7) - printf("\n"); - } - printf("\n"); - } - break; - default: - ; - } - - ctx->Driver.UnmapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER_ARB, - ctx->Array.ElementArrayBufferObj); -} - - -/** - * Inner support for both _mesa_DrawElements and _mesa_DrawRangeElements. - * Do the rendering for a glDrawElements or glDrawRangeElements call after - * we've validated buffer bounds, etc. - */ -static void -vbo_validated_drawrangeelements(struct gl_context *ctx, GLenum mode, - GLboolean index_bounds_valid, - GLuint start, GLuint end, - GLsizei count, GLenum type, - const GLvoid *indices, - GLint basevertex, GLint numInstances) -{ - struct vbo_context *vbo = vbo_context(ctx); - struct vbo_exec_context *exec = &vbo->exec; - struct _mesa_index_buffer ib; - struct _mesa_prim prim[1]; - - FLUSH_CURRENT( ctx, 0 ); - - if (!_mesa_valid_to_render(ctx, "glDraw[Range]Elements")) { - return; - } - - bind_arrays( ctx ); - - /* check for dirty state again */ - if (ctx->NewState) - _mesa_update_state( ctx ); - - ib.count = count; - ib.type = type; - ib.obj = ctx->Array.ElementArrayBufferObj; - ib.ptr = indices; - - prim[0].begin = 1; - prim[0].end = 1; - prim[0].weak = 0; - prim[0].pad = 0; - prim[0].mode = mode; - prim[0].start = 0; - prim[0].count = count; - prim[0].indexed = 1; - prim[0].basevertex = basevertex; - prim[0].num_instances = numInstances; - - /* Need to give special consideration to rendering a range of - * indices starting somewhere above zero. Typically the - * application is issuing multiple DrawRangeElements() to draw - * successive primitives layed out linearly in the vertex arrays. - * Unless the vertex arrays are all in a VBO (or locked as with - * CVA), the OpenGL semantics imply that we need to re-read or - * re-upload the vertex data on each draw call. - * - * In the case of hardware tnl, we want to avoid starting the - * upload at zero, as it will mean every draw call uploads an - * increasing amount of not-used vertex data. Worse - in the - * software tnl module, all those vertices might be transformed and - * lit but never rendered. - * - * If we just upload or transform the vertices in start..end, - * however, the indices will be incorrect. - * - * At this level, we don't know exactly what the requirements of - * the backend are going to be, though it will likely boil down to - * either: - * - * 1) Do nothing, everything is in a VBO and is processed once - * only. - * - * 2) Adjust the indices and vertex arrays so that start becomes - * zero. - * - * Rather than doing anything here, I'll provide a helper function - * for the latter case elsewhere. - */ - - check_buffers_are_unmapped(exec->array.inputs); - vbo->draw_prims( ctx, exec->array.inputs, prim, 1, &ib, - index_bounds_valid, start, end ); -} - - -/** - * Called by glDrawRangeElementsBaseVertex() in immediate mode. - */ -static void GLAPIENTRY -vbo_exec_DrawRangeElementsBaseVertex(GLenum mode, - GLuint start, GLuint end, - GLsizei count, GLenum type, - const GLvoid *indices, - GLint basevertex) -{ - static GLuint warnCount = 0; - GET_CURRENT_CONTEXT(ctx); - - if (MESA_VERBOSE & VERBOSE_DRAW) - _mesa_debug(ctx, - "glDrawRangeElementsBaseVertex(%s, %u, %u, %d, %s, %p, %d)\n", - _mesa_lookup_enum_by_nr(mode), start, end, count, - _mesa_lookup_enum_by_nr(type), indices, basevertex); - - if (!_mesa_validate_DrawRangeElements( ctx, mode, start, end, count, - type, indices, basevertex )) - return; - - /* NOTE: It's important that 'end' is a reasonable value. - * in _tnl_draw_prims(), we use end to determine how many vertices - * to transform. If it's too large, we can unnecessarily split prims - * or we can read/write out of memory in several different places! - */ - - /* Catch/fix some potential user errors */ - if (type == GL_UNSIGNED_BYTE) { - start = MIN2(start, 0xff); - end = MIN2(end, 0xff); - } - else if (type == GL_UNSIGNED_SHORT) { - start = MIN2(start, 0xffff); - end = MIN2(end, 0xffff); - } - - if (end >= ctx->Array.ArrayObj->_MaxElement) { - /* the max element is out of bounds of one or more enabled arrays */ - warnCount++; - - if (warnCount < 10) { - _mesa_warning(ctx, "glDraw[Range]Elements(start %u, end %u, count %d, " - "type 0x%x, indices=%p)\n" - "\tend is out of bounds (max=%u) " - "Element Buffer %u (size %d)\n" - "\tThis should probably be fixed in the application.", - start, end, count, type, indices, - ctx->Array.ArrayObj->_MaxElement - 1, - ctx->Array.ElementArrayBufferObj->Name, - (int) ctx->Array.ElementArrayBufferObj->Size); - } - - if (0) - dump_element_buffer(ctx, type); - - if (0) - _mesa_print_arrays(ctx); - -#ifdef DEBUG - /* 'end' was out of bounds, but now let's check the actual array - * indexes to see if any of them are out of bounds. - */ - { - GLuint max = _mesa_max_buffer_index(ctx, count, type, indices, - ctx->Array.ElementArrayBufferObj); - if (max >= ctx->Array.ArrayObj->_MaxElement) { - if (warnCount < 10) { - _mesa_warning(ctx, "glDraw[Range]Elements(start %u, end %u, " - "count %d, type 0x%x, indices=%p)\n" - "\tindex=%u is out of bounds (max=%u) " - "Element Buffer %u (size %d)\n" - "\tSkipping the glDrawRangeElements() call", - start, end, count, type, indices, max, - ctx->Array.ArrayObj->_MaxElement - 1, - ctx->Array.ElementArrayBufferObj->Name, - (int) ctx->Array.ElementArrayBufferObj->Size); - } - } - /* XXX we could also find the min index and compare to 'start' - * to see if start is correct. But it's more likely to get the - * upper bound wrong. - */ - } -#endif - - /* Set 'end' to the max possible legal value */ - assert(ctx->Array.ArrayObj->_MaxElement >= 1); - end = ctx->Array.ArrayObj->_MaxElement - 1; - } - else if (0) { - printf("glDraw[Range]Elements{,BaseVertex}" - "(start %u, end %u, type 0x%x, count %d) ElemBuf %u, " - "base %d\n", - start, end, type, count, - ctx->Array.ElementArrayBufferObj->Name, - basevertex); - } - -#if 0 - check_draw_elements_data(ctx, count, type, indices); -#else - (void) check_draw_elements_data; -#endif - - vbo_validated_drawrangeelements(ctx, mode, GL_TRUE, start, end, - count, type, indices, basevertex, 1); -} - - -/** - * Called by glDrawRangeElements() in immediate mode. - */ -static void GLAPIENTRY -vbo_exec_DrawRangeElements(GLenum mode, GLuint start, GLuint end, - GLsizei count, GLenum type, const GLvoid *indices) -{ - GET_CURRENT_CONTEXT(ctx); - - if (MESA_VERBOSE & VERBOSE_DRAW) - _mesa_debug(ctx, - "glDrawRangeElements(%s, %u, %u, %d, %s, %p)\n", - _mesa_lookup_enum_by_nr(mode), start, end, count, - _mesa_lookup_enum_by_nr(type), indices); - - vbo_exec_DrawRangeElementsBaseVertex(mode, start, end, count, type, - indices, 0); -} - - -/** - * Called by glDrawElements() in immediate mode. - */ -static void GLAPIENTRY -vbo_exec_DrawElements(GLenum mode, GLsizei count, GLenum type, - const GLvoid *indices) -{ - GET_CURRENT_CONTEXT(ctx); - - if (MESA_VERBOSE & VERBOSE_DRAW) - _mesa_debug(ctx, "glDrawElements(%s, %u, %s, %p)\n", - _mesa_lookup_enum_by_nr(mode), count, - _mesa_lookup_enum_by_nr(type), indices); - - if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices, 0 )) - return; - - vbo_validated_drawrangeelements(ctx, mode, GL_FALSE, ~0, ~0, - count, type, indices, 0, 1); -} - - -/** - * Called by glDrawElementsBaseVertex() in immediate mode. - */ -static void GLAPIENTRY -vbo_exec_DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, - const GLvoid *indices, GLint basevertex) -{ - GET_CURRENT_CONTEXT(ctx); - - if (MESA_VERBOSE & VERBOSE_DRAW) - _mesa_debug(ctx, "glDrawElementsBaseVertex(%s, %d, %s, %p, %d)\n", - _mesa_lookup_enum_by_nr(mode), count, - _mesa_lookup_enum_by_nr(type), indices, basevertex); - - if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices, - basevertex )) - return; - - vbo_validated_drawrangeelements(ctx, mode, GL_FALSE, ~0, ~0, - count, type, indices, basevertex, 1); -} - - -/** - * Called by glDrawElementsInstanced() in immediate mode. - */ -static void GLAPIENTRY -vbo_exec_DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, - const GLvoid *indices, GLsizei numInstances) -{ - GET_CURRENT_CONTEXT(ctx); - - if (MESA_VERBOSE & VERBOSE_DRAW) - _mesa_debug(ctx, "glDrawElementsInstanced(%s, %d, %s, %p, %d)\n", - _mesa_lookup_enum_by_nr(mode), count, - _mesa_lookup_enum_by_nr(type), indices, numInstances); - - if (!_mesa_validate_DrawElementsInstanced(ctx, mode, count, type, indices, - numInstances)) - return; - - vbo_validated_drawrangeelements(ctx, mode, GL_FALSE, ~0, ~0, - count, type, indices, 0, numInstances); -} - - -/** - * Inner support for both _mesa_MultiDrawElements() and - * _mesa_MultiDrawRangeElements(). - * This does the actual rendering after we've checked array indexes, etc. - */ -static void -vbo_validated_multidrawelements(struct gl_context *ctx, GLenum mode, - const GLsizei *count, GLenum type, - const GLvoid **indices, GLsizei primcount, - const GLint *basevertex) -{ - struct vbo_context *vbo = vbo_context(ctx); - struct vbo_exec_context *exec = &vbo->exec; - struct _mesa_index_buffer ib; - struct _mesa_prim *prim; - unsigned int index_type_size = 0; - uintptr_t min_index_ptr, max_index_ptr; - GLboolean fallback = GL_FALSE; - int i; - - if (primcount == 0) - return; - - FLUSH_CURRENT( ctx, 0 ); - - if (!_mesa_valid_to_render(ctx, "glMultiDrawElements")) { - return; - } - - prim = calloc(1, primcount * sizeof(*prim)); - if (prim == NULL) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMultiDrawElements"); - return; - } - - /* Decide if we can do this all as one set of primitives sharing the - * same index buffer, or if we have to reset the index pointer per - * primitive. - */ - bind_arrays( ctx ); - - /* check for dirty state again */ - if (ctx->NewState) - _mesa_update_state( ctx ); - - switch (type) { - case GL_UNSIGNED_INT: - index_type_size = 4; - break; - case GL_UNSIGNED_SHORT: - index_type_size = 2; - break; - case GL_UNSIGNED_BYTE: - index_type_size = 1; - break; - default: - assert(0); - } - - min_index_ptr = (uintptr_t)indices[0]; - max_index_ptr = 0; - for (i = 0; i < primcount; i++) { - min_index_ptr = MIN2(min_index_ptr, (uintptr_t)indices[i]); - max_index_ptr = MAX2(max_index_ptr, (uintptr_t)indices[i] + - index_type_size * count[i]); - } - - /* Check if we can handle this thing as a bunch of index offsets from the - * same index pointer. If we can't, then we have to fall back to doing - * a draw_prims per primitive. - * Check that the difference between each prim's indexes is a multiple of - * the index/element size. - */ - if (index_type_size != 1) { - for (i = 0; i < primcount; i++) { - if ((((uintptr_t)indices[i] - min_index_ptr) % index_type_size) != 0) { - fallback = GL_TRUE; - break; - } - } - } - - /* If the index buffer isn't in a VBO, then treating the application's - * subranges of the index buffer as one large index buffer may lead to - * us reading unmapped memory. - */ - if (!_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) - fallback = GL_TRUE; - - if (!fallback) { - ib.count = (max_index_ptr - min_index_ptr) / index_type_size; - ib.type = type; - ib.obj = ctx->Array.ElementArrayBufferObj; - ib.ptr = (void *)min_index_ptr; - - for (i = 0; i < primcount; i++) { - prim[i].begin = (i == 0); - prim[i].end = (i == primcount - 1); - prim[i].weak = 0; - prim[i].pad = 0; - prim[i].mode = mode; - prim[i].start = ((uintptr_t)indices[i] - min_index_ptr) / index_type_size; - prim[i].count = count[i]; - prim[i].indexed = 1; - prim[i].num_instances = 1; - if (basevertex != NULL) - prim[i].basevertex = basevertex[i]; - else - prim[i].basevertex = 0; - } - - check_buffers_are_unmapped(exec->array.inputs); - vbo->draw_prims(ctx, exec->array.inputs, prim, primcount, &ib, - GL_FALSE, ~0, ~0); - } else { - /* render one prim at a time */ - for (i = 0; i < primcount; i++) { - ib.count = count[i]; - ib.type = type; - ib.obj = ctx->Array.ElementArrayBufferObj; - ib.ptr = indices[i]; - - prim[0].begin = 1; - prim[0].end = 1; - prim[0].weak = 0; - prim[0].pad = 0; - prim[0].mode = mode; - prim[0].start = 0; - prim[0].count = count[i]; - prim[0].indexed = 1; - prim[0].num_instances = 1; - if (basevertex != NULL) - prim[0].basevertex = basevertex[i]; - else - prim[0].basevertex = 0; - - check_buffers_are_unmapped(exec->array.inputs); - vbo->draw_prims(ctx, exec->array.inputs, prim, 1, &ib, - GL_FALSE, ~0, ~0); - } - } - - free(prim); -} - - -static void GLAPIENTRY -vbo_exec_MultiDrawElements(GLenum mode, - const GLsizei *count, GLenum type, - const GLvoid **indices, - GLsizei primcount) -{ - GET_CURRENT_CONTEXT(ctx); - GLint i; - - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - for (i = 0; i < primcount; i++) { - if (!_mesa_validate_DrawElements(ctx, mode, count[i], type, indices[i], - 0)) - return; - } - - vbo_validated_multidrawelements(ctx, mode, count, type, indices, primcount, - NULL); -} - - -static void GLAPIENTRY -vbo_exec_MultiDrawElementsBaseVertex(GLenum mode, - const GLsizei *count, GLenum type, - const GLvoid **indices, - GLsizei primcount, - const GLsizei *basevertex) -{ - GET_CURRENT_CONTEXT(ctx); - GLint i; - - ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); - - for (i = 0; i < primcount; i++) { - if (!_mesa_validate_DrawElements(ctx, mode, count[i], type, indices[i], - basevertex[i])) - return; - } - - vbo_validated_multidrawelements(ctx, mode, count, type, indices, primcount, - basevertex); -} - - -/** - * Plug in the immediate-mode vertex array drawing commands into the - * givven vbo_exec_context object. - */ -void -vbo_exec_array_init( struct vbo_exec_context *exec ) -{ - exec->vtxfmt.DrawArrays = vbo_exec_DrawArrays; - exec->vtxfmt.DrawElements = vbo_exec_DrawElements; - exec->vtxfmt.DrawRangeElements = vbo_exec_DrawRangeElements; - exec->vtxfmt.MultiDrawElementsEXT = vbo_exec_MultiDrawElements; - exec->vtxfmt.DrawElementsBaseVertex = vbo_exec_DrawElementsBaseVertex; - exec->vtxfmt.DrawRangeElementsBaseVertex = vbo_exec_DrawRangeElementsBaseVertex; - exec->vtxfmt.MultiDrawElementsBaseVertex = vbo_exec_MultiDrawElementsBaseVertex; - exec->vtxfmt.DrawArraysInstanced = vbo_exec_DrawArraysInstanced; - exec->vtxfmt.DrawElementsInstanced = vbo_exec_DrawElementsInstanced; -} - - -void -vbo_exec_array_destroy( struct vbo_exec_context *exec ) -{ - /* nothing to do */ -} - - - -/** - * The following functions are only used for OpenGL ES 1/2 support. - * And some aren't even supported (yet) in ES 1/2. - */ - - -void GLAPIENTRY -_mesa_DrawArrays(GLenum mode, GLint first, GLsizei count) -{ - vbo_exec_DrawArrays(mode, first, count); -} - - -void GLAPIENTRY -_mesa_DrawElements(GLenum mode, GLsizei count, GLenum type, - const GLvoid *indices) -{ - vbo_exec_DrawElements(mode, count, type, indices); -} - - -void GLAPIENTRY -_mesa_DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, - const GLvoid *indices, GLint basevertex) -{ - vbo_exec_DrawElementsBaseVertex(mode, count, type, indices, basevertex); -} - - -void GLAPIENTRY -_mesa_DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, - GLenum type, const GLvoid *indices) -{ - vbo_exec_DrawRangeElements(mode, start, end, count, type, indices); -} - - -void GLAPIENTRY -_mesa_DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, - GLsizei count, GLenum type, - const GLvoid *indices, GLint basevertex) -{ - vbo_exec_DrawRangeElementsBaseVertex(mode, start, end, count, type, - indices, basevertex); -} - - -void GLAPIENTRY -_mesa_MultiDrawElementsEXT(GLenum mode, const GLsizei *count, GLenum type, - const GLvoid **indices, GLsizei primcount) -{ - vbo_exec_MultiDrawElements(mode, count, type, indices, primcount); -} - - -void GLAPIENTRY -_mesa_MultiDrawElementsBaseVertex(GLenum mode, - const GLsizei *count, GLenum type, - const GLvoid **indices, GLsizei primcount, - const GLint *basevertex) -{ - vbo_exec_MultiDrawElementsBaseVertex(mode, count, type, indices, - primcount, basevertex); -} +/************************************************************************** + * + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright 2009 VMware, Inc. + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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. + * + **************************************************************************/ + +#include "main/glheader.h" +#include "main/context.h" +#include "main/state.h" +#include "main/api_validate.h" +#include "main/varray.h" +#include "main/bufferobj.h" +#include "main/enums.h" +#include "main/macros.h" + +#include "vbo_context.h" + + +/** + * All vertex buffers should be in an unmapped state when we're about + * to draw. This debug function checks that. + */ +static void +check_buffers_are_unmapped(const struct gl_client_array **inputs) +{ +#ifdef DEBUG + GLuint i; + + for (i = 0; i < VERT_ATTRIB_MAX; i++) { + if (inputs[i]) { + struct gl_buffer_object *obj = inputs[i]->BufferObj; + assert(!_mesa_bufferobj_mapped(obj)); + (void) obj; + } + } +#endif +} + + +/** + * A debug function that may be called from other parts of Mesa as + * needed during debugging. + */ +void +vbo_check_buffers_are_unmapped(struct gl_context *ctx) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + /* check the current vertex arrays */ + check_buffers_are_unmapped(exec->array.inputs); + /* check the current glBegin/glVertex/glEnd-style VBO */ + assert(!_mesa_bufferobj_mapped(exec->vtx.bufferobj)); +} + + + +/** + * Compute min and max elements by scanning the index buffer for + * glDraw[Range]Elements() calls. + * If primitive restart is enabled, we need to ignore restart + * indexes when computing min/max. + */ +void +vbo_get_minmax_index(struct gl_context *ctx, + const struct _mesa_prim *prim, + const struct _mesa_index_buffer *ib, + GLuint *min_index, GLuint *max_index) +{ + const GLboolean restart = ctx->Array.PrimitiveRestart; + const GLuint restartIndex = ctx->Array.RestartIndex; + const GLuint count = prim->count; + const void *indices; + 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); + } else { + indices = ib->ptr; + } + + switch (ib->type) { + case GL_UNSIGNED_INT: { + const GLuint *ui_indices = (const GLuint *)indices; + GLuint max_ui = 0; + GLuint min_ui = ~0U; + if (restart) { + for (i = 0; i < count; i++) { + if (ui_indices[i] != restartIndex) { + if (ui_indices[i] > max_ui) max_ui = ui_indices[i]; + if (ui_indices[i] < min_ui) min_ui = ui_indices[i]; + } + } + } + else { + for (i = 0; i < count; i++) { + if (ui_indices[i] > max_ui) max_ui = ui_indices[i]; + if (ui_indices[i] < min_ui) min_ui = ui_indices[i]; + } + } + *min_index = min_ui; + *max_index = max_ui; + break; + } + case GL_UNSIGNED_SHORT: { + const GLushort *us_indices = (const GLushort *)indices; + GLuint max_us = 0; + GLuint min_us = ~0U; + if (restart) { + for (i = 0; i < count; i++) { + if (us_indices[i] != restartIndex) { + if (us_indices[i] > max_us) max_us = us_indices[i]; + if (us_indices[i] < min_us) min_us = us_indices[i]; + } + } + } + else { + for (i = 0; i < count; i++) { + if (us_indices[i] > max_us) max_us = us_indices[i]; + if (us_indices[i] < min_us) min_us = us_indices[i]; + } + } + *min_index = min_us; + *max_index = max_us; + break; + } + case GL_UNSIGNED_BYTE: { + const GLubyte *ub_indices = (const GLubyte *)indices; + GLuint max_ub = 0; + GLuint min_ub = ~0U; + if (restart) { + for (i = 0; i < count; i++) { + if (ub_indices[i] != restartIndex) { + if (ub_indices[i] > max_ub) max_ub = ub_indices[i]; + if (ub_indices[i] < min_ub) min_ub = ub_indices[i]; + } + } + } + else { + for (i = 0; i < count; i++) { + if (ub_indices[i] > max_ub) max_ub = ub_indices[i]; + if (ub_indices[i] < min_ub) min_ub = ub_indices[i]; + } + } + *min_index = min_ub; + *max_index = max_ub; + break; + } + default: + assert(0); + break; + } + + if (_mesa_is_bufferobj(ib->obj)) { + ctx->Driver.UnmapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER_ARB, ib->obj); + } +} + + +/** + * Check that element 'j' of the array has reasonable data. + * Map VBO if needed. + * For debugging purposes; not normally used. + */ +static void +check_array_data(struct gl_context *ctx, struct gl_client_array *array, + GLuint attrib, GLuint j) +{ + if (array->Enabled) { + const void *data = array->Ptr; + if (_mesa_is_bufferobj(array->BufferObj)) { + if (!array->BufferObj->Pointer) { + /* need to map now */ + array->BufferObj->Pointer = + ctx->Driver.MapBuffer(ctx, GL_ARRAY_BUFFER_ARB, + GL_READ_ONLY, array->BufferObj); + } + data = ADD_POINTERS(data, array->BufferObj->Pointer); + } + switch (array->Type) { + case GL_FLOAT: + { + GLfloat *f = (GLfloat *) ((GLubyte *) data + array->StrideB * j); + GLint k; + for (k = 0; k < array->Size; k++) { + if (IS_INF_OR_NAN(f[k]) || + f[k] >= 1.0e20 || f[k] <= -1.0e10) { + printf("Bad array data:\n"); + printf(" Element[%u].%u = %f\n", j, k, f[k]); + printf(" Array %u at %p\n", attrib, (void* ) array); + printf(" Type 0x%x, Size %d, Stride %d\n", + array->Type, array->Size, array->Stride); + printf(" Address/offset %p in Buffer Object %u\n", + array->Ptr, array->BufferObj->Name); + f[k] = 1.0; /* XXX replace the bad value! */ + } + /*assert(!IS_INF_OR_NAN(f[k]));*/ + } + } + break; + default: + ; + } + } +} + + +/** + * Unmap the buffer object referenced by given array, if mapped. + */ +static void +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); + } +} + + +/** + * Examine the array's data for NaNs, etc. + * For debug purposes; not normally used. + */ +static void +check_draw_elements_data(struct gl_context *ctx, GLsizei count, GLenum elemType, + const void *elements, GLint basevertex) +{ + struct gl_array_object *arrayObj = ctx->Array.ArrayObj; + const void *elemMap; + 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); + elements = ADD_POINTERS(elements, elemMap); + } + + for (i = 0; i < count; i++) { + GLuint j; + + /* j = element[i] */ + switch (elemType) { + case GL_UNSIGNED_BYTE: + j = ((const GLubyte *) elements)[i]; + break; + case GL_UNSIGNED_SHORT: + j = ((const GLushort *) elements)[i]; + break; + case GL_UNSIGNED_INT: + j = ((const GLuint *) elements)[i]; + break; + default: + assert(0); + } + + /* check element j of each enabled array */ + check_array_data(ctx, &arrayObj->Vertex, VERT_ATTRIB_POS, j); + check_array_data(ctx, &arrayObj->Normal, VERT_ATTRIB_NORMAL, j); + check_array_data(ctx, &arrayObj->Color, VERT_ATTRIB_COLOR0, j); + check_array_data(ctx, &arrayObj->SecondaryColor, VERT_ATTRIB_COLOR1, j); + for (k = 0; k < Elements(arrayObj->TexCoord); k++) { + check_array_data(ctx, &arrayObj->TexCoord[k], VERT_ATTRIB_TEX0 + k, j); + } + for (k = 0; k < Elements(arrayObj->VertexAttrib); k++) { + check_array_data(ctx, &arrayObj->VertexAttrib[k], + VERT_ATTRIB_GENERIC0 + k, j); + } + } + + if (_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) { + ctx->Driver.UnmapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER_ARB, + ctx->Array.ElementArrayBufferObj); + } + + unmap_array_buffer(ctx, &arrayObj->Vertex); + unmap_array_buffer(ctx, &arrayObj->Normal); + unmap_array_buffer(ctx, &arrayObj->Color); + for (k = 0; k < Elements(arrayObj->TexCoord); k++) { + unmap_array_buffer(ctx, &arrayObj->TexCoord[k]); + } + for (k = 0; k < Elements(arrayObj->VertexAttrib); k++) { + unmap_array_buffer(ctx, &arrayObj->VertexAttrib[k]); + } +} + + +/** + * Check array data, looking for NaNs, etc. + */ +static void +check_draw_arrays_data(struct gl_context *ctx, GLint start, GLsizei count) +{ + /* TO DO */ +} + + +/** + * Print info/data for glDrawArrays(), for debugging. + */ +static void +print_draw_arrays(struct gl_context *ctx, + GLenum mode, GLint start, GLsizei count) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + int i; + + printf("vbo_exec_DrawArrays(mode 0x%x, start %d, count %d):\n", + mode, start, count); + + for (i = 0; i < 32; i++) { + GLuint bufName = exec->array.inputs[i]->BufferObj->Name; + GLint stride = exec->array.inputs[i]->Stride; + printf("attr %2d: size %d stride %d enabled %d " + "ptr %p Bufobj %u\n", + i, + exec->array.inputs[i]->Size, + stride, + /*exec->array.inputs[i]->Enabled,*/ + exec->array.legacy_array[i]->Enabled, + exec->array.inputs[i]->Ptr, + bufName); + + if (bufName) { + struct gl_buffer_object *buf = _mesa_lookup_bufferobj(ctx, bufName); + GLubyte *p = ctx->Driver.MapBuffer(ctx, GL_ARRAY_BUFFER_ARB, + GL_READ_ONLY_ARB, buf); + int offset = (int) (GLintptr) exec->array.inputs[i]->Ptr; + float *f = (float *) (p + offset); + int *k = (int *) f; + int i; + int n = (count * stride) / 4; + if (n > 32) + n = 32; + printf(" Data at offset %d:\n", offset); + 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, buf); + } + } +} + + +/** + * Bind the VBO executor to the current vertex array object prior + * to drawing. + * + * Just translate the arrayobj into a sane layout. + */ +static void +bind_array_obj(struct gl_context *ctx) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct gl_array_object *arrayObj = ctx->Array.ArrayObj; + GLuint i; + + /* TODO: Fix the ArrayObj struct to keep legacy arrays in an array + * rather than as individual named arrays. Then this function can + * go away. + */ + exec->array.legacy_array[VERT_ATTRIB_POS] = &arrayObj->Vertex; + exec->array.legacy_array[VERT_ATTRIB_WEIGHT] = &arrayObj->Weight; + exec->array.legacy_array[VERT_ATTRIB_NORMAL] = &arrayObj->Normal; + exec->array.legacy_array[VERT_ATTRIB_COLOR0] = &arrayObj->Color; + exec->array.legacy_array[VERT_ATTRIB_COLOR1] = &arrayObj->SecondaryColor; + exec->array.legacy_array[VERT_ATTRIB_FOG] = &arrayObj->FogCoord; + exec->array.legacy_array[VERT_ATTRIB_COLOR_INDEX] = &arrayObj->Index; + if (arrayObj->PointSize.Enabled) { + /* this aliases COLOR_INDEX */ + exec->array.legacy_array[VERT_ATTRIB_POINT_SIZE] = &arrayObj->PointSize; + } + exec->array.legacy_array[VERT_ATTRIB_EDGEFLAG] = &arrayObj->EdgeFlag; + + for (i = 0; i < Elements(arrayObj->TexCoord); i++) + exec->array.legacy_array[VERT_ATTRIB_TEX0 + i] = &arrayObj->TexCoord[i]; + + for (i = 0; i < Elements(arrayObj->VertexAttrib); i++) { + assert(i < Elements(exec->array.generic_array)); + exec->array.generic_array[i] = &arrayObj->VertexAttrib[i]; + } + + exec->array.array_obj = arrayObj->Name; +} + + +/** + * Set the vbo->exec->inputs[] pointers to point to the enabled + * vertex arrays. This depends on the current vertex program/shader + * being executed because of whether or not generic vertex arrays + * alias the conventional vertex arrays. + * For arrays that aren't enabled, we set the input[attrib] pointer + * to point at a zero-stride current value "array". + */ +static void +recalculate_input_bindings(struct gl_context *ctx) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + const struct gl_client_array **inputs = &exec->array.inputs[0]; + GLbitfield const_inputs = 0x0; + GLuint i; + + exec->array.program_mode = get_program_mode(ctx); + exec->array.enabled_flags = ctx->Array.ArrayObj->_Enabled; + + switch (exec->array.program_mode) { + case VP_NONE: + /* When no vertex program is active (or the vertex program is generated + * from fixed-function state). We put the material values into the + * generic slots. This is the only situation where material values + * are available as per-vertex attributes. + */ + for (i = 0; i <= VERT_ATTRIB_TEX7; i++) { + if (exec->array.legacy_array[i]->Enabled) + inputs[i] = exec->array.legacy_array[i]; + else { + inputs[i] = &vbo->legacy_currval[i]; + const_inputs |= 1 << i; + } + } + + for (i = 0; i < MAT_ATTRIB_MAX; i++) { + inputs[VERT_ATTRIB_GENERIC0 + i] = &vbo->mat_currval[i]; + const_inputs |= 1 << (VERT_ATTRIB_GENERIC0 + i); + } + + /* Could use just about anything, just to fill in the empty + * slots: + */ + for (i = MAT_ATTRIB_MAX; i < VERT_ATTRIB_MAX - VERT_ATTRIB_GENERIC0; i++) { + inputs[VERT_ATTRIB_GENERIC0 + i] = &vbo->generic_currval[i]; + const_inputs |= 1 << (VERT_ATTRIB_GENERIC0 + i); + } + + /* There is no need to make _NEW_ARRAY dirty here for the TnL program, + * because it already takes care of invalidating the state necessary + * to revalidate vertex arrays. Not marking the state as dirty also + * improves performance (quite significantly in some apps). + */ + if (!ctx->VertexProgram._MaintainTnlProgram) + ctx->NewState |= _NEW_ARRAY; + break; + + case VP_NV: + /* NV_vertex_program - attribute arrays alias and override + * conventional, legacy arrays. No materials, and the generic + * slots are vacant. + */ + for (i = 0; i <= VERT_ATTRIB_TEX7; i++) { + if (exec->array.generic_array[i]->Enabled) + inputs[i] = exec->array.generic_array[i]; + else if (exec->array.legacy_array[i]->Enabled) + inputs[i] = exec->array.legacy_array[i]; + else { + inputs[i] = &vbo->legacy_currval[i]; + const_inputs |= 1 << i; + } + } + + /* Could use just about anything, just to fill in the empty + * slots: + */ + for (i = VERT_ATTRIB_GENERIC0; i < VERT_ATTRIB_MAX; i++) { + inputs[i] = &vbo->generic_currval[i - VERT_ATTRIB_GENERIC0]; + const_inputs |= 1 << i; + } + + ctx->NewState |= _NEW_ARRAY; + break; + + case VP_ARB: + /* GL_ARB_vertex_program or GLSL vertex shader - Only the generic[0] + * attribute array aliases and overrides the legacy position array. + * + * Otherwise, legacy attributes available in the legacy slots, + * generic attributes in the generic slots and materials are not + * available as per-vertex attributes. + */ + if (exec->array.generic_array[0]->Enabled) + inputs[0] = exec->array.generic_array[0]; + else if (exec->array.legacy_array[0]->Enabled) + inputs[0] = exec->array.legacy_array[0]; + else { + inputs[0] = &vbo->legacy_currval[0]; + const_inputs |= 1 << 0; + } + + for (i = 1; i <= VERT_ATTRIB_TEX7; i++) { + if (exec->array.legacy_array[i]->Enabled) + inputs[i] = exec->array.legacy_array[i]; + else { + inputs[i] = &vbo->legacy_currval[i]; + const_inputs |= 1 << i; + } + } + + for (i = 0; i < MAX_VERTEX_GENERIC_ATTRIBS; i++) { + if (exec->array.generic_array[i]->Enabled) + inputs[VERT_ATTRIB_GENERIC0 + i] = exec->array.generic_array[i]; + else { + inputs[VERT_ATTRIB_GENERIC0 + i] = &vbo->generic_currval[i]; + const_inputs |= 1 << (VERT_ATTRIB_GENERIC0 + i); + } + } + + ctx->NewState |= _NEW_ARRAY; + break; + } + + _mesa_set_varying_vp_inputs( ctx, ~const_inputs ); +} + + +/** + * Examine the enabled vertex arrays to set the exec->array.inputs[] values. + * These will point to the arrays to actually use for drawing. Some will + * be user-provided arrays, other will be zero-stride const-valued arrays. + * Note that this might set the _NEW_ARRAY dirty flag so state validation + * must be done after this call. + */ +static void +bind_arrays(struct gl_context *ctx) +{ + if (!ctx->Array.RebindArrays) { + return; + } + + bind_array_obj(ctx); + recalculate_input_bindings(ctx); + ctx->Array.RebindArrays = GL_FALSE; +} + + +/** + * Helper function called by the other DrawArrays() functions below. + * This is where we handle primitive restart for drawing non-indexed + * arrays. If primitive restart is enabled, it typically means + * splitting one DrawArrays() into two. + */ +static void +vbo_draw_arrays(struct gl_context *ctx, GLenum mode, GLint start, + GLsizei count, GLuint numInstances) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct _mesa_prim prim[2]; + + bind_arrays(ctx); + + /* Again... because we may have changed the bitmask of per-vertex varying + * attributes. If we regenerate the fixed-function vertex program now + * we may be able to prune down the number of vertex attributes which we + * need in the shader. + */ + if (ctx->NewState) + _mesa_update_state(ctx); + + prim[0].begin = 1; + prim[0].end = 1; + prim[0].weak = 0; + prim[0].pad = 0; + prim[0].mode = mode; + prim[0].start = 0; /* filled in below */ + prim[0].count = 0; /* filled in below */ + prim[0].indexed = 0; + prim[0].basevertex = 0; + prim[0].num_instances = numInstances; + + /* Implement the primitive restart index */ + if (ctx->Array.PrimitiveRestart && ctx->Array.RestartIndex < count) { + GLuint primCount = 0; + + if (ctx->Array.RestartIndex == start) { + /* special case: RestartIndex at beginning */ + if (count > 1) { + prim[0].start = start + 1; + prim[0].count = count - 1; + primCount = 1; + } + } + else if (ctx->Array.RestartIndex == start + count - 1) { + /* special case: RestartIndex at end */ + if (count > 1) { + prim[0].start = start; + prim[0].count = count - 1; + primCount = 1; + } + } + else { + /* general case: RestartIndex in middle, split into two prims */ + prim[0].start = start; + prim[0].count = ctx->Array.RestartIndex - start; + + prim[1] = prim[0]; + prim[1].start = ctx->Array.RestartIndex + 1; + prim[1].count = count - prim[1].start; + + primCount = 2; + } + + if (primCount > 0) { + /* draw one or two prims */ + check_buffers_are_unmapped(exec->array.inputs); + vbo->draw_prims(ctx, exec->array.inputs, prim, primCount, NULL, + GL_TRUE, start, start + count - 1); + } + } + else { + /* no prim restart */ + prim[0].start = start; + prim[0].count = count; + + check_buffers_are_unmapped(exec->array.inputs); + vbo->draw_prims(ctx, exec->array.inputs, prim, 1, NULL, + GL_TRUE, start, start + count - 1); + } +} + + + +/** + * Called from glDrawArrays when in immediate mode (not display list mode). + */ +static void GLAPIENTRY +vbo_exec_DrawArrays(GLenum mode, GLint start, GLsizei count) +{ + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_DRAW) + _mesa_debug(ctx, "glDrawArrays(%s, %d, %d)\n", + _mesa_lookup_enum_by_nr(mode), start, count); + + if (!_mesa_validate_DrawArrays( ctx, mode, start, count )) + return; + + FLUSH_CURRENT( ctx, 0 ); + + if (!_mesa_valid_to_render(ctx, "glDrawArrays")) { + return; + } + + if (0) + check_draw_arrays_data(ctx, start, count); + + vbo_draw_arrays(ctx, mode, start, count, 1); + + if (0) + print_draw_arrays(ctx, mode, start, count); +} + + +/** + * Called from glDrawArraysInstanced when in immediate mode (not + * display list mode). + */ +static void GLAPIENTRY +vbo_exec_DrawArraysInstanced(GLenum mode, GLint start, GLsizei count, + GLsizei numInstances) +{ + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_DRAW) + _mesa_debug(ctx, "glDrawArraysInstanced(%s, %d, %d, %d)\n", + _mesa_lookup_enum_by_nr(mode), start, count, numInstances); + + if (!_mesa_validate_DrawArraysInstanced(ctx, mode, start, count, numInstances)) + return; + + FLUSH_CURRENT( ctx, 0 ); + + if (!_mesa_valid_to_render(ctx, "glDrawArraysInstanced")) { + return; + } + + if (0) + check_draw_arrays_data(ctx, start, count); + + vbo_draw_arrays(ctx, mode, start, count, numInstances); + + if (0) + print_draw_arrays(ctx, mode, start, count); +} + + +/** + * Map GL_ELEMENT_ARRAY_BUFFER and print contents. + * For debugging. + */ +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); + switch (type) { + case GL_UNSIGNED_BYTE: + { + const GLubyte *us = (const GLubyte *) map; + GLint i; + for (i = 0; i < ctx->Array.ElementArrayBufferObj->Size; i++) { + printf("%02x ", us[i]); + if (i % 32 == 31) + printf("\n"); + } + printf("\n"); + } + break; + case GL_UNSIGNED_SHORT: + { + const GLushort *us = (const GLushort *) map; + GLint i; + for (i = 0; i < ctx->Array.ElementArrayBufferObj->Size / 2; i++) { + printf("%04x ", us[i]); + if (i % 16 == 15) + printf("\n"); + } + printf("\n"); + } + break; + case GL_UNSIGNED_INT: + { + const GLuint *us = (const GLuint *) map; + GLint i; + for (i = 0; i < ctx->Array.ElementArrayBufferObj->Size / 4; i++) { + printf("%08x ", us[i]); + if (i % 8 == 7) + printf("\n"); + } + printf("\n"); + } + break; + default: + ; + } + + ctx->Driver.UnmapBuffer(ctx, GL_ELEMENT_ARRAY_BUFFER_ARB, + ctx->Array.ElementArrayBufferObj); +} + + +/** + * Inner support for both _mesa_DrawElements and _mesa_DrawRangeElements. + * Do the rendering for a glDrawElements or glDrawRangeElements call after + * we've validated buffer bounds, etc. + */ +static void +vbo_validated_drawrangeelements(struct gl_context *ctx, GLenum mode, + GLboolean index_bounds_valid, + GLuint start, GLuint end, + GLsizei count, GLenum type, + const GLvoid *indices, + GLint basevertex, GLint numInstances) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct _mesa_index_buffer ib; + struct _mesa_prim prim[1]; + + FLUSH_CURRENT( ctx, 0 ); + + if (!_mesa_valid_to_render(ctx, "glDraw[Range]Elements")) { + return; + } + + bind_arrays( ctx ); + + /* check for dirty state again */ + if (ctx->NewState) + _mesa_update_state( ctx ); + + ib.count = count; + ib.type = type; + ib.obj = ctx->Array.ElementArrayBufferObj; + ib.ptr = indices; + + prim[0].begin = 1; + prim[0].end = 1; + prim[0].weak = 0; + prim[0].pad = 0; + prim[0].mode = mode; + prim[0].start = 0; + prim[0].count = count; + prim[0].indexed = 1; + prim[0].basevertex = basevertex; + prim[0].num_instances = numInstances; + + /* Need to give special consideration to rendering a range of + * indices starting somewhere above zero. Typically the + * application is issuing multiple DrawRangeElements() to draw + * successive primitives layed out linearly in the vertex arrays. + * Unless the vertex arrays are all in a VBO (or locked as with + * CVA), the OpenGL semantics imply that we need to re-read or + * re-upload the vertex data on each draw call. + * + * In the case of hardware tnl, we want to avoid starting the + * upload at zero, as it will mean every draw call uploads an + * increasing amount of not-used vertex data. Worse - in the + * software tnl module, all those vertices might be transformed and + * lit but never rendered. + * + * If we just upload or transform the vertices in start..end, + * however, the indices will be incorrect. + * + * At this level, we don't know exactly what the requirements of + * the backend are going to be, though it will likely boil down to + * either: + * + * 1) Do nothing, everything is in a VBO and is processed once + * only. + * + * 2) Adjust the indices and vertex arrays so that start becomes + * zero. + * + * Rather than doing anything here, I'll provide a helper function + * for the latter case elsewhere. + */ + + check_buffers_are_unmapped(exec->array.inputs); + vbo->draw_prims( ctx, exec->array.inputs, prim, 1, &ib, + index_bounds_valid, start, end ); +} + + +/** + * Called by glDrawRangeElementsBaseVertex() in immediate mode. + */ +static void GLAPIENTRY +vbo_exec_DrawRangeElementsBaseVertex(GLenum mode, + GLuint start, GLuint end, + GLsizei count, GLenum type, + const GLvoid *indices, + GLint basevertex) +{ + static GLuint warnCount = 0; + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_DRAW) + _mesa_debug(ctx, + "glDrawRangeElementsBaseVertex(%s, %u, %u, %d, %s, %p, %d)\n", + _mesa_lookup_enum_by_nr(mode), start, end, count, + _mesa_lookup_enum_by_nr(type), indices, basevertex); + + if (!_mesa_validate_DrawRangeElements( ctx, mode, start, end, count, + type, indices, basevertex )) + return; + + /* NOTE: It's important that 'end' is a reasonable value. + * in _tnl_draw_prims(), we use end to determine how many vertices + * to transform. If it's too large, we can unnecessarily split prims + * or we can read/write out of memory in several different places! + */ + + /* Catch/fix some potential user errors */ + if (type == GL_UNSIGNED_BYTE) { + start = MIN2(start, 0xff); + end = MIN2(end, 0xff); + } + else if (type == GL_UNSIGNED_SHORT) { + start = MIN2(start, 0xffff); + end = MIN2(end, 0xffff); + } + + if (end >= ctx->Array.ArrayObj->_MaxElement) { + /* the max element is out of bounds of one or more enabled arrays */ + warnCount++; + + if (warnCount < 10) { + _mesa_warning(ctx, "glDraw[Range]Elements(start %u, end %u, count %d, " + "type 0x%x, indices=%p)\n" + "\tend is out of bounds (max=%u) " + "Element Buffer %u (size %d)\n" + "\tThis should probably be fixed in the application.", + start, end, count, type, indices, + ctx->Array.ArrayObj->_MaxElement - 1, + ctx->Array.ElementArrayBufferObj->Name, + (int) ctx->Array.ElementArrayBufferObj->Size); + } + + if (0) + dump_element_buffer(ctx, type); + + if (0) + _mesa_print_arrays(ctx); + +#ifdef DEBUG + /* 'end' was out of bounds, but now let's check the actual array + * indexes to see if any of them are out of bounds. + */ + { + GLuint max = _mesa_max_buffer_index(ctx, count, type, indices, + ctx->Array.ElementArrayBufferObj); + if (max >= ctx->Array.ArrayObj->_MaxElement) { + if (warnCount < 10) { + _mesa_warning(ctx, "glDraw[Range]Elements(start %u, end %u, " + "count %d, type 0x%x, indices=%p)\n" + "\tindex=%u is out of bounds (max=%u) " + "Element Buffer %u (size %d)\n" + "\tSkipping the glDrawRangeElements() call", + start, end, count, type, indices, max, + ctx->Array.ArrayObj->_MaxElement - 1, + ctx->Array.ElementArrayBufferObj->Name, + (int) ctx->Array.ElementArrayBufferObj->Size); + } + } + /* XXX we could also find the min index and compare to 'start' + * to see if start is correct. But it's more likely to get the + * upper bound wrong. + */ + } +#endif + + /* Set 'end' to the max possible legal value */ + assert(ctx->Array.ArrayObj->_MaxElement >= 1); + end = ctx->Array.ArrayObj->_MaxElement - 1; + + if (end < start) { + return; + } + } + + if (0) { + printf("glDraw[Range]Elements{,BaseVertex}" + "(start %u, end %u, type 0x%x, count %d) ElemBuf %u, " + "base %d\n", + start, end, type, count, + ctx->Array.ElementArrayBufferObj->Name, + basevertex); + } + +#if 0 + check_draw_elements_data(ctx, count, type, indices); +#else + (void) check_draw_elements_data; +#endif + + vbo_validated_drawrangeelements(ctx, mode, GL_TRUE, start, end, + count, type, indices, basevertex, 1); +} + + +/** + * Called by glDrawRangeElements() in immediate mode. + */ +static void GLAPIENTRY +vbo_exec_DrawRangeElements(GLenum mode, GLuint start, GLuint end, + GLsizei count, GLenum type, const GLvoid *indices) +{ + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_DRAW) + _mesa_debug(ctx, + "glDrawRangeElements(%s, %u, %u, %d, %s, %p)\n", + _mesa_lookup_enum_by_nr(mode), start, end, count, + _mesa_lookup_enum_by_nr(type), indices); + + vbo_exec_DrawRangeElementsBaseVertex(mode, start, end, count, type, + indices, 0); +} + + +/** + * Called by glDrawElements() in immediate mode. + */ +static void GLAPIENTRY +vbo_exec_DrawElements(GLenum mode, GLsizei count, GLenum type, + const GLvoid *indices) +{ + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_DRAW) + _mesa_debug(ctx, "glDrawElements(%s, %u, %s, %p)\n", + _mesa_lookup_enum_by_nr(mode), count, + _mesa_lookup_enum_by_nr(type), indices); + + if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices, 0 )) + return; + + vbo_validated_drawrangeelements(ctx, mode, GL_FALSE, ~0, ~0, + count, type, indices, 0, 1); +} + + +/** + * Called by glDrawElementsBaseVertex() in immediate mode. + */ +static void GLAPIENTRY +vbo_exec_DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, + const GLvoid *indices, GLint basevertex) +{ + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_DRAW) + _mesa_debug(ctx, "glDrawElementsBaseVertex(%s, %d, %s, %p, %d)\n", + _mesa_lookup_enum_by_nr(mode), count, + _mesa_lookup_enum_by_nr(type), indices, basevertex); + + if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices, + basevertex )) + return; + + vbo_validated_drawrangeelements(ctx, mode, GL_FALSE, ~0, ~0, + count, type, indices, basevertex, 1); +} + + +/** + * Called by glDrawElementsInstanced() in immediate mode. + */ +static void GLAPIENTRY +vbo_exec_DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, + const GLvoid *indices, GLsizei numInstances) +{ + GET_CURRENT_CONTEXT(ctx); + + if (MESA_VERBOSE & VERBOSE_DRAW) + _mesa_debug(ctx, "glDrawElementsInstanced(%s, %d, %s, %p, %d)\n", + _mesa_lookup_enum_by_nr(mode), count, + _mesa_lookup_enum_by_nr(type), indices, numInstances); + + if (!_mesa_validate_DrawElementsInstanced(ctx, mode, count, type, indices, + numInstances)) + return; + + vbo_validated_drawrangeelements(ctx, mode, GL_FALSE, ~0, ~0, + count, type, indices, 0, numInstances); +} + + +/** + * Inner support for both _mesa_MultiDrawElements() and + * _mesa_MultiDrawRangeElements(). + * This does the actual rendering after we've checked array indexes, etc. + */ +static void +vbo_validated_multidrawelements(struct gl_context *ctx, GLenum mode, + const GLsizei *count, GLenum type, + const GLvoid **indices, GLsizei primcount, + const GLint *basevertex) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_exec_context *exec = &vbo->exec; + struct _mesa_index_buffer ib; + struct _mesa_prim *prim; + unsigned int index_type_size = 0; + uintptr_t min_index_ptr, max_index_ptr; + GLboolean fallback = GL_FALSE; + int i; + + if (primcount == 0) + return; + + FLUSH_CURRENT( ctx, 0 ); + + if (!_mesa_valid_to_render(ctx, "glMultiDrawElements")) { + return; + } + + prim = calloc(1, primcount * sizeof(*prim)); + if (prim == NULL) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glMultiDrawElements"); + return; + } + + /* Decide if we can do this all as one set of primitives sharing the + * same index buffer, or if we have to reset the index pointer per + * primitive. + */ + bind_arrays( ctx ); + + /* check for dirty state again */ + if (ctx->NewState) + _mesa_update_state( ctx ); + + switch (type) { + case GL_UNSIGNED_INT: + index_type_size = 4; + break; + case GL_UNSIGNED_SHORT: + index_type_size = 2; + break; + case GL_UNSIGNED_BYTE: + index_type_size = 1; + break; + default: + assert(0); + } + + min_index_ptr = (uintptr_t)indices[0]; + max_index_ptr = 0; + for (i = 0; i < primcount; i++) { + min_index_ptr = MIN2(min_index_ptr, (uintptr_t)indices[i]); + max_index_ptr = MAX2(max_index_ptr, (uintptr_t)indices[i] + + index_type_size * count[i]); + } + + /* Check if we can handle this thing as a bunch of index offsets from the + * same index pointer. If we can't, then we have to fall back to doing + * a draw_prims per primitive. + * Check that the difference between each prim's indexes is a multiple of + * the index/element size. + */ + if (index_type_size != 1) { + for (i = 0; i < primcount; i++) { + if ((((uintptr_t)indices[i] - min_index_ptr) % index_type_size) != 0) { + fallback = GL_TRUE; + break; + } + } + } + + /* If the index buffer isn't in a VBO, then treating the application's + * subranges of the index buffer as one large index buffer may lead to + * us reading unmapped memory. + */ + if (!_mesa_is_bufferobj(ctx->Array.ElementArrayBufferObj)) + fallback = GL_TRUE; + + if (!fallback) { + ib.count = (max_index_ptr - min_index_ptr) / index_type_size; + ib.type = type; + ib.obj = ctx->Array.ElementArrayBufferObj; + ib.ptr = (void *)min_index_ptr; + + for (i = 0; i < primcount; i++) { + prim[i].begin = (i == 0); + prim[i].end = (i == primcount - 1); + prim[i].weak = 0; + prim[i].pad = 0; + prim[i].mode = mode; + prim[i].start = ((uintptr_t)indices[i] - min_index_ptr) / index_type_size; + prim[i].count = count[i]; + prim[i].indexed = 1; + prim[i].num_instances = 1; + if (basevertex != NULL) + prim[i].basevertex = basevertex[i]; + else + prim[i].basevertex = 0; + } + + check_buffers_are_unmapped(exec->array.inputs); + vbo->draw_prims(ctx, exec->array.inputs, prim, primcount, &ib, + GL_FALSE, ~0, ~0); + } else { + /* render one prim at a time */ + for (i = 0; i < primcount; i++) { + ib.count = count[i]; + ib.type = type; + ib.obj = ctx->Array.ElementArrayBufferObj; + ib.ptr = indices[i]; + + prim[0].begin = 1; + prim[0].end = 1; + prim[0].weak = 0; + prim[0].pad = 0; + prim[0].mode = mode; + prim[0].start = 0; + prim[0].count = count[i]; + prim[0].indexed = 1; + prim[0].num_instances = 1; + if (basevertex != NULL) + prim[0].basevertex = basevertex[i]; + else + prim[0].basevertex = 0; + + check_buffers_are_unmapped(exec->array.inputs); + vbo->draw_prims(ctx, exec->array.inputs, prim, 1, &ib, + GL_FALSE, ~0, ~0); + } + } + + free(prim); +} + + +static void GLAPIENTRY +vbo_exec_MultiDrawElements(GLenum mode, + const GLsizei *count, GLenum type, + const GLvoid **indices, + GLsizei primcount) +{ + GET_CURRENT_CONTEXT(ctx); + GLint i; + + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + for (i = 0; i < primcount; i++) { + if (!_mesa_validate_DrawElements(ctx, mode, count[i], type, indices[i], + 0)) + return; + } + + vbo_validated_multidrawelements(ctx, mode, count, type, indices, primcount, + NULL); +} + + +static void GLAPIENTRY +vbo_exec_MultiDrawElementsBaseVertex(GLenum mode, + const GLsizei *count, GLenum type, + const GLvoid **indices, + GLsizei primcount, + const GLsizei *basevertex) +{ + GET_CURRENT_CONTEXT(ctx); + GLint i; + + ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); + + for (i = 0; i < primcount; i++) { + if (!_mesa_validate_DrawElements(ctx, mode, count[i], type, indices[i], + basevertex[i])) + return; + } + + vbo_validated_multidrawelements(ctx, mode, count, type, indices, primcount, + basevertex); +} + + +/** + * Plug in the immediate-mode vertex array drawing commands into the + * givven vbo_exec_context object. + */ +void +vbo_exec_array_init( struct vbo_exec_context *exec ) +{ + exec->vtxfmt.DrawArrays = vbo_exec_DrawArrays; + exec->vtxfmt.DrawElements = vbo_exec_DrawElements; + exec->vtxfmt.DrawRangeElements = vbo_exec_DrawRangeElements; + exec->vtxfmt.MultiDrawElementsEXT = vbo_exec_MultiDrawElements; + exec->vtxfmt.DrawElementsBaseVertex = vbo_exec_DrawElementsBaseVertex; + exec->vtxfmt.DrawRangeElementsBaseVertex = vbo_exec_DrawRangeElementsBaseVertex; + exec->vtxfmt.MultiDrawElementsBaseVertex = vbo_exec_MultiDrawElementsBaseVertex; + exec->vtxfmt.DrawArraysInstanced = vbo_exec_DrawArraysInstanced; + exec->vtxfmt.DrawElementsInstanced = vbo_exec_DrawElementsInstanced; +} + + +void +vbo_exec_array_destroy( struct vbo_exec_context *exec ) +{ + /* nothing to do */ +} + + + +/** + * The following functions are only used for OpenGL ES 1/2 support. + * And some aren't even supported (yet) in ES 1/2. + */ + + +void GLAPIENTRY +_mesa_DrawArrays(GLenum mode, GLint first, GLsizei count) +{ + vbo_exec_DrawArrays(mode, first, count); +} + + +void GLAPIENTRY +_mesa_DrawElements(GLenum mode, GLsizei count, GLenum type, + const GLvoid *indices) +{ + vbo_exec_DrawElements(mode, count, type, indices); +} + + +void GLAPIENTRY +_mesa_DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, + const GLvoid *indices, GLint basevertex) +{ + vbo_exec_DrawElementsBaseVertex(mode, count, type, indices, basevertex); +} + + +void GLAPIENTRY +_mesa_DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, + GLenum type, const GLvoid *indices) +{ + vbo_exec_DrawRangeElements(mode, start, end, count, type, indices); +} + + +void GLAPIENTRY +_mesa_DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, + GLsizei count, GLenum type, + const GLvoid *indices, GLint basevertex) +{ + vbo_exec_DrawRangeElementsBaseVertex(mode, start, end, count, type, + indices, basevertex); +} + + +void GLAPIENTRY +_mesa_MultiDrawElementsEXT(GLenum mode, const GLsizei *count, GLenum type, + const GLvoid **indices, GLsizei primcount) +{ + vbo_exec_MultiDrawElements(mode, count, type, indices, primcount); +} + + +void GLAPIENTRY +_mesa_MultiDrawElementsBaseVertex(GLenum mode, + const GLsizei *count, GLenum type, + const GLvoid **indices, GLsizei primcount, + const GLint *basevertex) +{ + vbo_exec_MultiDrawElementsBaseVertex(mode, count, type, indices, + primcount, basevertex); +} diff --git a/mesalib/src/mesa/vbo/vbo_exec_draw.c b/mesalib/src/mesa/vbo/vbo_exec_draw.c index 539658021..98f585792 100644 --- a/mesalib/src/mesa/vbo/vbo_exec_draw.c +++ b/mesalib/src/mesa/vbo/vbo_exec_draw.c @@ -1,426 +1,427 @@ -/* - * 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; - _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 ) +{ + 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 */ diff --git a/mesalib/src/mesa/vbo/vbo_save_draw.c b/mesalib/src/mesa/vbo/vbo_save_draw.c index 634a6d3f8..d0f627a35 100644 --- a/mesalib/src/mesa/vbo/vbo_save_draw.c +++ b/mesalib/src/mesa/vbo/vbo_save_draw.c @@ -1,305 +1,308 @@ -/* - * 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. - */ - -/* Author: - * Keith Whitwell - */ - -#include "main/glheader.h" -#include "main/bufferobj.h" -#include "main/context.h" -#include "main/imports.h" -#include "main/mfeatures.h" -#include "main/mtypes.h" -#include "main/macros.h" -#include "main/light.h" -#include "main/state.h" - -#include "vbo_context.h" - - -#if FEATURE_dlist - - -/** - * After playback, copy everything but the position from the - * last vertex to the saved state - */ -static void -_playback_copy_to_current(struct gl_context *ctx, - const struct vbo_save_vertex_list *node) -{ - struct vbo_context *vbo = vbo_context(ctx); - GLfloat vertex[VBO_ATTRIB_MAX * 4]; - GLfloat *data; - GLuint i, offset; - - if (node->current_size == 0) - return; - - if (node->current_data) { - data = node->current_data; - } - else { - data = vertex; - - if (node->count) - offset = (node->buffer_offset + - (node->count-1) * node->vertex_size * sizeof(GLfloat)); - else - offset = node->buffer_offset; - - ctx->Driver.GetBufferSubData( ctx, 0, offset, - node->vertex_size * sizeof(GLfloat), - data, node->vertex_store->bufferobj ); - - data += node->attrsz[0]; /* skip vertex position */ - } - - for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) { - if (node->attrsz[i]) { - GLfloat *current = (GLfloat *)vbo->currval[i].Ptr; - GLfloat tmp[4]; - - COPY_CLEAN_4V(tmp, - node->attrsz[i], - data); - - if (memcmp(current, tmp, 4 * sizeof(GLfloat)) != 0) { - memcpy(current, tmp, 4 * sizeof(GLfloat)); - - vbo->currval[i].Size = node->attrsz[i]; - - if (i >= VBO_ATTRIB_FIRST_MATERIAL && - i <= VBO_ATTRIB_LAST_MATERIAL) - ctx->NewState |= _NEW_LIGHT; - - ctx->NewState |= _NEW_CURRENT_ATTRIB; - } - - data += node->attrsz[i]; - } - } - - /* Colormaterial -- this kindof sucks. - */ - if (ctx->Light.ColorMaterialEnabled) { - _mesa_update_color_material(ctx, ctx->Current.Attrib[VBO_ATTRIB_COLOR0]); - } - - /* CurrentExecPrimitive - */ - if (node->prim_count) { - const struct _mesa_prim *prim = &node->prim[node->prim_count - 1]; - if (prim->end) - ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END; - else - ctx->Driver.CurrentExecPrimitive = prim->mode; - } -} - - - -/** - * Treat the vertex storage as a VBO, define vertex arrays pointing - * into it: - */ -static void vbo_bind_vertex_list(struct gl_context *ctx, - const struct vbo_save_vertex_list *node) -{ - struct vbo_context *vbo = vbo_context(ctx); - struct vbo_save_context *save = &vbo->save; - struct gl_client_array *arrays = save->arrays; - GLuint buffer_offset = node->buffer_offset; - const GLuint *map; - GLuint attr; - GLubyte node_attrsz[VBO_ATTRIB_MAX]; /* copy of node->attrsz[] */ - GLbitfield varying_inputs = 0x0; - - memcpy(node_attrsz, node->attrsz, sizeof(node->attrsz)); - - /* Install the default (ie Current) attributes first, then overlay - * all active ones. - */ - switch (get_program_mode(ctx)) { - case VP_NONE: - for (attr = 0; attr < 16; attr++) { - save->inputs[attr] = &vbo->legacy_currval[attr]; - } - for (attr = 0; attr < MAT_ATTRIB_MAX; attr++) { - save->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++) { - save->inputs[attr] = &vbo->legacy_currval[attr]; - save->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)) { - save->inputs[16] = save->inputs[0]; - node_attrsz[16] = node_attrsz[0]; - node_attrsz[0] = 0; - } - break; - default: - assert(0); - } - - for (attr = 0; attr < VERT_ATTRIB_MAX; attr++) { - const GLuint src = map[attr]; - - if (node_attrsz[src]) { - /* override the default array set above */ - save->inputs[attr] = &arrays[attr]; - - arrays[attr].Ptr = (const GLubyte *) NULL + buffer_offset; - arrays[attr].Size = node->attrsz[src]; - arrays[attr].StrideB = node->vertex_size * sizeof(GLfloat); - arrays[attr].Stride = node->vertex_size * sizeof(GLfloat); - arrays[attr].Type = GL_FLOAT; - arrays[attr].Format = GL_RGBA; - arrays[attr].Enabled = 1; - _mesa_reference_buffer_object(ctx, - &arrays[attr].BufferObj, - node->vertex_store->bufferobj); - arrays[attr]._MaxElement = node->count; /* ??? */ - - assert(arrays[attr].BufferObj->Name); - - buffer_offset += node->attrsz[src] * sizeof(GLfloat); - varying_inputs |= 1<NewState |= _NEW_ARRAY; - } - } - - _mesa_set_varying_vp_inputs( ctx, varying_inputs ); -} - - -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); - - vbo_loopback_vertex_list(ctx, - (const GLfloat *)(buffer + list->buffer_offset), - list->attrsz, - list->prim, - list->prim_count, - list->wrap_count, - list->vertex_size); - - ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER_ARB, - list->vertex_store->bufferobj); -} - - -/** - * Execute the buffer and save copied verts. - * This is called from the display list code when executing - * a drawing command. - */ -void -vbo_save_playback_vertex_list(struct gl_context *ctx, void *data) -{ - const struct vbo_save_vertex_list *node = - (const struct vbo_save_vertex_list *) data; - struct vbo_save_context *save = &vbo_context(ctx)->save; - - FLUSH_CURRENT(ctx, 0); - - if (node->prim_count > 0 && node->count > 0) { - - if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END && - node->prim[0].begin) { - - /* Degenerate case: list is called inside begin/end pair and - * includes operations such as glBegin or glDrawArrays. - */ - if (0) - printf("displaylist recursive begin"); - - vbo_save_loopback_vertex_list( ctx, node ); - return; - } - else if (save->replay_flags) { - /* Various degnerate cases: translate into immediate mode - * calls rather than trying to execute in place. - */ - vbo_save_loopback_vertex_list( ctx, node ); - return; - } - - if (ctx->NewState) - _mesa_update_state( ctx ); - - /* XXX also need to check if shader enabled, but invalid */ - if ((ctx->VertexProgram.Enabled && !ctx->VertexProgram._Enabled) || - (ctx->FragmentProgram.Enabled && !ctx->FragmentProgram._Enabled)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glBegin (invalid vertex/fragment program)"); - return; - } - - vbo_bind_vertex_list( ctx, node ); - - /* Again... - */ - if (ctx->NewState) - _mesa_update_state( ctx ); - - vbo_context(ctx)->draw_prims(ctx, - save->inputs, - node->prim, - node->prim_count, - NULL, - GL_TRUE, - 0, /* Node is a VBO, so this is ok */ - node->count - 1); - } - - /* Copy to current? - */ - _playback_copy_to_current( ctx, node ); -} - - -#endif /* FEATURE_dlist */ +/* + * 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. + */ + +/* Author: + * Keith Whitwell + */ + +#include "main/glheader.h" +#include "main/bufferobj.h" +#include "main/context.h" +#include "main/imports.h" +#include "main/mfeatures.h" +#include "main/mtypes.h" +#include "main/macros.h" +#include "main/light.h" +#include "main/state.h" + +#include "vbo_context.h" + + +#if FEATURE_dlist + + +/** + * After playback, copy everything but the position from the + * last vertex to the saved state + */ +static void +_playback_copy_to_current(struct gl_context *ctx, + const struct vbo_save_vertex_list *node) +{ + struct vbo_context *vbo = vbo_context(ctx); + GLfloat vertex[VBO_ATTRIB_MAX * 4]; + GLfloat *data; + GLuint i, offset; + + if (node->current_size == 0) + return; + + if (node->current_data) { + data = node->current_data; + } + else { + data = vertex; + + if (node->count) + offset = (node->buffer_offset + + (node->count-1) * node->vertex_size * sizeof(GLfloat)); + else + offset = node->buffer_offset; + + ctx->Driver.GetBufferSubData( ctx, 0, offset, + node->vertex_size * sizeof(GLfloat), + data, node->vertex_store->bufferobj ); + + data += node->attrsz[0]; /* skip vertex position */ + } + + for (i = VBO_ATTRIB_POS+1 ; i < VBO_ATTRIB_MAX ; i++) { + if (node->attrsz[i]) { + GLfloat *current = (GLfloat *)vbo->currval[i].Ptr; + GLfloat tmp[4]; + + COPY_CLEAN_4V(tmp, + node->attrsz[i], + data); + + if (memcmp(current, tmp, 4 * sizeof(GLfloat)) != 0) { + memcpy(current, tmp, 4 * sizeof(GLfloat)); + + vbo->currval[i].Size = node->attrsz[i]; + assert(vbo->currval[i].Type == GL_FLOAT); + vbo->currval[i]._ElementSize = vbo->currval[i].Size * sizeof(GLfloat); + + if (i >= VBO_ATTRIB_FIRST_MATERIAL && + i <= VBO_ATTRIB_LAST_MATERIAL) + ctx->NewState |= _NEW_LIGHT; + + ctx->NewState |= _NEW_CURRENT_ATTRIB; + } + + data += node->attrsz[i]; + } + } + + /* Colormaterial -- this kindof sucks. + */ + if (ctx->Light.ColorMaterialEnabled) { + _mesa_update_color_material(ctx, ctx->Current.Attrib[VBO_ATTRIB_COLOR0]); + } + + /* CurrentExecPrimitive + */ + if (node->prim_count) { + const struct _mesa_prim *prim = &node->prim[node->prim_count - 1]; + if (prim->end) + ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END; + else + ctx->Driver.CurrentExecPrimitive = prim->mode; + } +} + + + +/** + * Treat the vertex storage as a VBO, define vertex arrays pointing + * into it: + */ +static void vbo_bind_vertex_list(struct gl_context *ctx, + const struct vbo_save_vertex_list *node) +{ + struct vbo_context *vbo = vbo_context(ctx); + struct vbo_save_context *save = &vbo->save; + struct gl_client_array *arrays = save->arrays; + GLuint buffer_offset = node->buffer_offset; + const GLuint *map; + GLuint attr; + GLubyte node_attrsz[VBO_ATTRIB_MAX]; /* copy of node->attrsz[] */ + GLbitfield varying_inputs = 0x0; + + memcpy(node_attrsz, node->attrsz, sizeof(node->attrsz)); + + /* Install the default (ie Current) attributes first, then overlay + * all active ones. + */ + switch (get_program_mode(ctx)) { + case VP_NONE: + for (attr = 0; attr < 16; attr++) { + save->inputs[attr] = &vbo->legacy_currval[attr]; + } + for (attr = 0; attr < MAT_ATTRIB_MAX; attr++) { + save->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++) { + save->inputs[attr] = &vbo->legacy_currval[attr]; + save->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)) { + save->inputs[16] = save->inputs[0]; + node_attrsz[16] = node_attrsz[0]; + node_attrsz[0] = 0; + } + break; + default: + assert(0); + } + + for (attr = 0; attr < VERT_ATTRIB_MAX; attr++) { + const GLuint src = map[attr]; + + if (node_attrsz[src]) { + /* override the default array set above */ + save->inputs[attr] = &arrays[attr]; + + arrays[attr].Ptr = (const GLubyte *) NULL + buffer_offset; + arrays[attr].Size = node->attrsz[src]; + arrays[attr].StrideB = node->vertex_size * sizeof(GLfloat); + arrays[attr].Stride = node->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, + node->vertex_store->bufferobj); + arrays[attr]._MaxElement = node->count; /* ??? */ + + assert(arrays[attr].BufferObj->Name); + + buffer_offset += node->attrsz[src] * sizeof(GLfloat); + varying_inputs |= 1<NewState |= _NEW_ARRAY; + } + } + + _mesa_set_varying_vp_inputs( ctx, varying_inputs ); +} + + +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); + + vbo_loopback_vertex_list(ctx, + (const GLfloat *)(buffer + list->buffer_offset), + list->attrsz, + list->prim, + list->prim_count, + list->wrap_count, + list->vertex_size); + + ctx->Driver.UnmapBuffer(ctx, GL_ARRAY_BUFFER_ARB, + list->vertex_store->bufferobj); +} + + +/** + * Execute the buffer and save copied verts. + * This is called from the display list code when executing + * a drawing command. + */ +void +vbo_save_playback_vertex_list(struct gl_context *ctx, void *data) +{ + const struct vbo_save_vertex_list *node = + (const struct vbo_save_vertex_list *) data; + struct vbo_save_context *save = &vbo_context(ctx)->save; + + FLUSH_CURRENT(ctx, 0); + + if (node->prim_count > 0 && node->count > 0) { + + if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END && + node->prim[0].begin) { + + /* Degenerate case: list is called inside begin/end pair and + * includes operations such as glBegin or glDrawArrays. + */ + if (0) + printf("displaylist recursive begin"); + + vbo_save_loopback_vertex_list( ctx, node ); + return; + } + else if (save->replay_flags) { + /* Various degnerate cases: translate into immediate mode + * calls rather than trying to execute in place. + */ + vbo_save_loopback_vertex_list( ctx, node ); + return; + } + + if (ctx->NewState) + _mesa_update_state( ctx ); + + /* XXX also need to check if shader enabled, but invalid */ + if ((ctx->VertexProgram.Enabled && !ctx->VertexProgram._Enabled) || + (ctx->FragmentProgram.Enabled && !ctx->FragmentProgram._Enabled)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBegin (invalid vertex/fragment program)"); + return; + } + + vbo_bind_vertex_list( ctx, node ); + + /* Again... + */ + if (ctx->NewState) + _mesa_update_state( ctx ); + + vbo_context(ctx)->draw_prims(ctx, + save->inputs, + node->prim, + node->prim_count, + NULL, + GL_TRUE, + 0, /* Node is a VBO, so this is ok */ + node->count - 1); + } + + /* Copy to current? + */ + _playback_copy_to_current( ctx, node ); +} + + +#endif /* FEATURE_dlist */ diff --git a/mesalib/src/mesa/vbo/vbo_split_copy.c b/mesalib/src/mesa/vbo/vbo_split_copy.c index 8c981f93e..3a869d712 100644 --- a/mesalib/src/mesa/vbo/vbo_split_copy.c +++ b/mesalib/src/mesa/vbo/vbo_split_copy.c @@ -1,626 +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->_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.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(©); + } +} -- cgit v1.2.3