/* * Mesa 3-D graphics library * Version: 6.0 * * Copyright (C) 1999-2004 Brian Paul All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * \file t_array_api.c * \brief Vertex array API functions (glDrawArrays, etc) * \author Keith Whitwell */ #include "glheader.h" #include "api_validate.h" #include "context.h" #include "imports.h" #include "macros.h" #include "mtypes.h" #include "state.h" #include "array_cache/acache.h" #include "t_array_api.h" #include "t_array_import.h" #include "t_save_api.h" #include "t_context.h" #include "t_pipeline.h" static void fallback_drawarrays( GLcontext *ctx, GLenum mode, GLint start, GLsizei count ) { GLint i; assert(!ctx->CompileFlag); assert(ctx->Driver.CurrentExecPrimitive == GL_POLYGON+1); glBegin(mode); for (i = start; i < count; i++) glArrayElement( i ); glEnd(); } static void fallback_drawelements( GLcontext *ctx, GLenum mode, GLsizei count, const GLuint *indices) { GLint i; assert(!ctx->CompileFlag); assert(ctx->Driver.CurrentExecPrimitive == GL_POLYGON+1); /* Here, indices will already reflect the buffer object if active */ glBegin(mode); for (i = 0 ; i < count ; i++) { glArrayElement( indices[i] ); } glEnd(); } static void _tnl_draw_range_elements( GLcontext *ctx, GLenum mode, GLuint start, GLuint end, GLsizei count, GLuint *indices ) { TNLcontext *tnl = TNL_CONTEXT(ctx); struct tnl_prim prim; int i; FLUSH_CURRENT( ctx, 0 ); if (tnl->pipeline.build_state_changes) _tnl_validate_pipeline( ctx ); /* XXX is "end" correct? Looking at the implementation of * _tnl_vb_bind_arrays(), perhaps we should pass end-start. */ _tnl_vb_bind_arrays( ctx, start, end ); tnl->vb.Primitive = &prim; tnl->vb.Primitive[0].mode = mode | PRIM_BEGIN | PRIM_END; tnl->vb.Primitive[0].start = 0; tnl->vb.Primitive[0].count = count; tnl->vb.PrimitiveCount = 1; tnl->vb.Elts = (GLuint *)indices; assert (start == 0); /* XXX - indices may be read only if (start) for (i = 0 ; i < count ; i++) indices[i] -= start; */ if (ctx->Array.LockCount) tnl->Driver.RunPipeline( ctx ); else { /* The lower 16 bits represent the conventional arrays while the * upper 16 bits represent the generic arrays. OR those bits * together to indicate which vertex attribs are in effect. */ GLuint enabledArrays = ctx->Array._Enabled | (ctx->Array._Enabled >> 16); /* Note that arrays may have changed before/after execution. */ tnl->pipeline.run_input_changes |= enabledArrays; tnl->Driver.RunPipeline( ctx ); tnl->pipeline.run_input_changes |= enabledArrays; } /* XXX - indices may be read only if (start) for (i = 0 ; i < count ; i++) indices[i] += start; */ } /** * Called via the GL API dispatcher. */ void GLAPIENTRY _tnl_DrawArrays(GLenum mode, GLint start, GLsizei count) { GET_CURRENT_CONTEXT(ctx); TNLcontext *tnl = TNL_CONTEXT(ctx); GLuint thresh = (ctx->Driver.NeedFlush & FLUSH_STORED_VERTICES) ? 30 : 10; GLuint enabledArrays; if (MESA_VERBOSE & VERBOSE_API) _mesa_debug(NULL, "_tnl_DrawArrays %d %d\n", start, count); /* Check arguments, etc. */ if (!_mesa_validate_DrawArrays( ctx, mode, start, count )) return; if (tnl->pipeline.build_state_changes) _tnl_validate_pipeline( ctx ); assert(!ctx->CompileFlag); if (!ctx->Array.LockCount && (GLuint) count < thresh) { /* Small primitives: attempt to share a vb (at the expense of * using the immediate interface). */ fallback_drawarrays( ctx, mode, start, start + count ); } else if (ctx->Array.LockCount && count <= (GLint) ctx->Const.MaxArrayLockSize) { struct tnl_prim prim; /* Locked primitives which can fit in a single vertex buffer: */ FLUSH_CURRENT( ctx, 0 ); if (start < (GLint) ctx->Array.LockFirst) start = ctx->Array.LockFirst; if (start + count > (GLint) ctx->Array.LockCount) count = ctx->Array.LockCount - start; /* Locked drawarrays. Reuse any previously transformed data. */ _tnl_vb_bind_arrays( ctx, ctx->Array.LockFirst, ctx->Array.LockCount ); tnl->vb.Primitive = &prim; tnl->vb.Primitive[0].mode = mode | PRIM_BEGIN | PRIM_END; tnl->vb.Primitive[0].start = start; tnl->vb.Primitive[0].count = count; tnl->vb.PrimitiveCount = 1; tnl->Driver.RunPipeline( ctx ); } else { int bufsz = 256; /* Use a small buffer for cache goodness */ int j, nr; int minimum, modulo, skip; /* Large primitives requiring decomposition to multiple vertex * buffers: */ switch (mode) { case GL_POINTS: minimum = 0; modulo = 1; skip = 0; break; case GL_LINES: minimum = 1; modulo = 2; skip = 1; break; case GL_LINE_STRIP: minimum = 1; modulo = 1; skip = 0; break; case GL_TRIANGLES: minimum = 2; modulo = 3; skip = 2; break; case GL_TRIANGLE_STRIP: minimum = 2; modulo = 1; skip = 0; break; case GL_QUADS: minimum = 3; modulo = 4; skip = 3; break; case GL_QUAD_STRIP: minimum = 3; modulo = 2; skip = 0; break; case GL_LINE_LOOP: case GL_TRIANGLE_FAN: case GL_POLYGON: default: /* Primitives requiring a copied vertex (fan-like primitives) * must use the slow path if they cannot fit in a single * vertex buffer. */ if (count <= (GLint) ctx->Const.MaxArrayLockSize) { bufsz = ctx->Const.MaxArrayLockSize; minimum = 0; modulo = 1; skip = 0; } else { fallback_drawarrays( ctx, mode, start, start + count ); return; } } FLUSH_CURRENT( ctx, 0 ); bufsz -= bufsz % modulo; bufsz -= minimum; count += start; for (j = start + minimum ; j < count ; j += nr + skip ) { struct tnl_prim prim; nr = MIN2( bufsz, count - j ); /* XXX is the last parameter a count or index into the array??? */ _tnl_vb_bind_arrays( ctx, j - minimum, j + nr ); tnl->vb.Primitive = &prim; tnl->vb.Primitive[0].mode = mode; if (j == start + minimum) tnl->vb.Primitive[0].mode |= PRIM_BEGIN; if (j + nr + skip >= count) tnl->vb.Primitive[0].mode |= PRIM_END; tnl->vb.Primitive[0].start = 0; tnl->vb.Primitive[0].count = nr + minimum; tnl->vb.PrimitiveCount = 1; /* The lower 16 bits represent the conventional arrays while the * upper 16 bits represent the generic arrays. OR those bits * together to indicate which vertex attribs are in effect. */ enabledArrays = ctx->Array._Enabled | (ctx->Array._Enabled >> 16); /* Note that arrays may have changed before/after execution. */ tnl->pipeline.run_input_changes |= enabledArrays; tnl->Driver.RunPipeline( ctx ); tnl->pipeline.run_input_changes |= enabledArrays; } } } /** * Called via the GL API dispatcher. */ void GLAPIENTRY _tnl_DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices) { GET_CURRENT_CONTEXT(ctx); GLuint *ui_indices; if (MESA_VERBOSE & VERBOSE_API) _mesa_debug(NULL, "_tnl_DrawRangeElements %d %d %d\n", start, end, count); if (ctx->Array.ElementArrayBufferObj->Name) { /* use indices in the buffer object */ if (!ctx->Array.ElementArrayBufferObj->Data) { _mesa_warning(ctx, "DrawRangeElements with empty vertex elements buffer!"); return; } /* actual address is the sum of pointers */ indices = (const GLvoid *) ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Data, (const GLubyte *) indices); } /* Check arguments, etc. */ if (!_mesa_validate_DrawRangeElements( ctx, mode, start, end, count, type, indices )) return; ui_indices = (GLuint *)_ac_import_elements( ctx, GL_UNSIGNED_INT, count, type, indices ); assert(!ctx->CompileFlag); if (ctx->Array.LockCount) { /* Are the arrays already locked? If so we currently have to look * at the whole locked range. */ if (start == 0 && start >= ctx->Array.LockFirst && end <= ctx->Array.LockCount) _tnl_draw_range_elements( ctx, mode, ctx->Array.LockFirst, ctx->Array.LockCount, count, ui_indices ); else { /* The spec says referencing elements outside the locked * range is undefined. I'm going to make it a noop this time * round, maybe come up with something beter before 3.6. * * May be able to get away with just setting LockCount==0, * though this raises the problems of dependent state. May * have to call glUnlockArrays() directly? * * Or scan the list and replace bad indices? */ _mesa_problem( ctx, "DrawRangeElements references " "elements outside locked range."); } } else if (start == 0 && end - start + 1 <= ctx->Const.MaxArrayLockSize) { /* The arrays aren't locked but we can still fit them inside a * single vertexbuffer. */ _tnl_draw_range_elements( ctx, mode, start, end + 1, count, ui_indices ); } else { /* Range is too big to optimize: */ fallback_drawelements( ctx, mode, count, ui_indices ); } } /** * Called via the GL API dispatcher. */ void GLAPIENTRY _tnl_DrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) { GET_CURRENT_CONTEXT(ctx); GLuint *ui_indices; if (MESA_VERBOSE & VERBOSE_API) _mesa_debug(NULL, "_tnl_DrawElements %d\n", count); /* Check arguments, etc. */ if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices )) return; if (ctx->Array.ElementArrayBufferObj->Name) { /* actual address is the sum of pointers */ indices = (const GLvoid *) ADD_POINTERS(ctx->Array.ElementArrayBufferObj->Data, (const GLubyte *) indices); } ui_indices = (GLuint *)_ac_import_elements( ctx, GL_UNSIGNED_INT, count, type, indices ); assert(!ctx->CompileFlag); if (ctx->Array.LockFirst == 0 && ctx->Array.LockCount) { _tnl_draw_range_elements( ctx, mode, ctx->Array.LockFirst, ctx->Array.LockCount, count, ui_indices ); } else { /* Scan the index list and see if we can use the locked path anyway. */ GLuint max_elt = 0; GLint i; for (i = 0 ; i < count ; i++) if (ui_indices[i] > max_elt) max_elt = ui_indices[i]; /* XXX should this < really be <= ??? */ if (max_elt < ctx->Const.MaxArrayLockSize && /* can we use it? */ max_elt < (GLuint) count) /* do we want to use it? */ _tnl_draw_range_elements( ctx, mode, 0, max_elt+1, count, ui_indices ); else fallback_drawelements( ctx, mode, count, ui_indices ); } } /** * Initialize context's vertex array fields. Called during T 'n L context * creation. */ void _tnl_array_init( GLcontext *ctx ) { TNLcontext *tnl = TNL_CONTEXT(ctx); struct tnl_vertex_arrays *tmp = &tnl->array_inputs; GLvertexformat *vfmt = &(TNL_CONTEXT(ctx)->exec_vtxfmt); GLuint i; vfmt->DrawArrays = _tnl_DrawArrays; vfmt->DrawElements = _tnl_DrawElements; vfmt->DrawRangeElements = _tnl_DrawRangeElements; /* Setup vector pointers that will be used to bind arrays to VB's. */ _mesa_vector4f_init( &tmp->Obj, 0, 0 ); _mesa_vector4f_init( &tmp->Normal, 0, 0 ); _mesa_vector4f_init( &tmp->FogCoord, 0, 0 ); _mesa_vector4f_init( &tmp->Index, 0, 0 ); for (i = 0; i < ctx->Const.MaxTextureUnits; i++) _mesa_vector4f_init( &tmp->TexCoord[i], 0, 0); } /** * Destroy the context's vertex array stuff. * Called during T 'n L context destruction. */ void _tnl_array_destroy( GLcontext *ctx ) { }