diff options
Diffstat (limited to 'src/mesa/tnl/t_vtx_api.c')
-rw-r--r-- | src/mesa/tnl/t_vtx_api.c | 1046 |
1 files changed, 1046 insertions, 0 deletions
diff --git a/src/mesa/tnl/t_vtx_api.c b/src/mesa/tnl/t_vtx_api.c new file mode 100644 index 00000000000..77eec8b3b44 --- /dev/null +++ b/src/mesa/tnl/t_vtx_api.c @@ -0,0 +1,1046 @@ +/************************************************************************** + +Copyright 2002 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 <[email protected]> + */ + +#include "glheader.h" +#include "context.h" +#include "macros.h" +#include "vtxfmt.h" +#include "dlist.h" +#include "state.h" +#include "light.h" +#include "api_arrayelt.h" +#include "api_noop.h" +#include "t_vtx_api.h" +#include "simple_list.h" + +#include "dispatch.h" + +static void reset_attrfv( TNLcontext *tnl ); + +/** Note extra space for error index: */ +static tnl_attrfv_func choose[_TNL_ATTRIB_ERROR+1][4]; +static tnl_attrfv_func generic_attr_func[_TNL_MAX_ATTR_CODEGEN][4]; + + +/* Close off the last primitive, execute the buffer, restart the + * primitive. + */ +static void _tnl_wrap_buffers( GLcontext *ctx ) +{ + TNLcontext *tnl = TNL_CONTEXT(ctx); + + + if (tnl->vtx.prim_count == 0) { + tnl->vtx.copied.nr = 0; + tnl->vtx.counter = tnl->vtx.initial_counter; + tnl->vtx.vbptr = tnl->vtx.buffer; + } + else { + GLuint last_prim = tnl->vtx.prim[tnl->vtx.prim_count-1].mode; + GLuint last_count; + + if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { + GLint i = tnl->vtx.prim_count - 1; + assert(i >= 0); + tnl->vtx.prim[i].count = ((tnl->vtx.initial_counter - + tnl->vtx.counter) - + tnl->vtx.prim[i].start); + } + + last_count = tnl->vtx.prim[tnl->vtx.prim_count-1].count; + + /* Execute the buffer and save copied vertices. + */ + if (tnl->vtx.counter != tnl->vtx.initial_counter) + _tnl_flush_vtx( ctx ); + else { + tnl->vtx.prim_count = 0; + tnl->vtx.copied.nr = 0; + } + + /* Emit a glBegin to start the new list. + */ + assert(tnl->vtx.prim_count == 0); + + if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { + tnl->vtx.prim[0].mode = ctx->Driver.CurrentExecPrimitive; + tnl->vtx.prim[0].start = 0; + tnl->vtx.prim[0].count = 0; + tnl->vtx.prim_count++; + + if (tnl->vtx.copied.nr == last_count) + tnl->vtx.prim[0].mode |= last_prim & PRIM_BEGIN; + } + } +} + + +/* Deal with buffer wrapping where provoked by the vertex buffer + * filling up, as opposed to upgrade_vertex(). + * + * Make it GLAPIENTRY, so we can tail from the codegen'ed Vertex*fv + */ +void GLAPIENTRY _tnl_wrap_filled_vertex( GLcontext *ctx ) +{ + TNLcontext *tnl = TNL_CONTEXT(ctx); + GLfloat *data = tnl->vtx.copied.buffer; + GLuint i; + + /* Run pipeline on current vertices, copy wrapped vertices + * to tnl->copied. + */ + _tnl_wrap_buffers( ctx ); + + /* Copy stored stored vertices to start of new list. + */ + assert(tnl->vtx.counter > tnl->vtx.copied.nr); + + for (i = 0 ; i < tnl->vtx.copied.nr ; i++) { + _mesa_memcpy( tnl->vtx.vbptr, data, + tnl->vtx.vertex_size * sizeof(GLfloat)); + tnl->vtx.vbptr += tnl->vtx.vertex_size; + data += tnl->vtx.vertex_size; + tnl->vtx.counter--; + } + + tnl->vtx.copied.nr = 0; +} + + +/* + * Copy the active vertex's values to the ctx->Current fields. + */ +static void _tnl_copy_to_current( GLcontext *ctx ) +{ + TNLcontext *tnl = TNL_CONTEXT(ctx); + GLuint i; + + for (i = _TNL_ATTRIB_POS+1 ; i < _TNL_ATTRIB_EDGEFLAG ; i++) { + if (tnl->vtx.attrsz[i]) { + /* Note: the tnl->vtx.current[i] pointers points to + * the ctx->Current fields. The first 16 or so, anyway. + */ + COPY_CLEAN_4V(tnl->vtx.current[i], + tnl->vtx.attrsz[i], + tnl->vtx.attrptr[i]); + } + } + + /* Edgeflag requires additional treatment: + */ + if (tnl->vtx.attrsz[_TNL_ATTRIB_EDGEFLAG]) { + ctx->Current.EdgeFlag = + (tnl->vtx.CurrentFloatEdgeFlag == 1.0); + } + + /* Colormaterial -- this kindof sucks. + */ + if (ctx->Light.ColorMaterialEnabled) { + _mesa_update_color_material(ctx, + ctx->Current.Attrib[VERT_ATTRIB_COLOR0]); + } + + if (tnl->vtx.have_materials) { + tnl->Driver.NotifyMaterialChange( ctx ); + ctx->NewState |= _NEW_LIGHT; + } + + ctx->Driver.NeedFlush &= ~FLUSH_UPDATE_CURRENT; +} + + +static void _tnl_copy_from_current( GLcontext *ctx ) +{ + TNLcontext *tnl = TNL_CONTEXT(ctx); + GLint i; + + /* Edgeflag requires additional treatment: + */ + tnl->vtx.CurrentFloatEdgeFlag = (GLfloat) ctx->Current.EdgeFlag; + + for (i = _TNL_ATTRIB_POS+1 ; i < _TNL_ATTRIB_MAX ; i++) + switch (tnl->vtx.attrsz[i]) { + case 4: tnl->vtx.attrptr[i][3] = tnl->vtx.current[i][3]; + case 3: tnl->vtx.attrptr[i][2] = tnl->vtx.current[i][2]; + case 2: tnl->vtx.attrptr[i][1] = tnl->vtx.current[i][1]; + case 1: tnl->vtx.attrptr[i][0] = tnl->vtx.current[i][0]; + break; + } + + ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT; +} + + +/* Flush existing data, set new attrib size, replay copied vertices. + */ +static void _tnl_wrap_upgrade_vertex( GLcontext *ctx, + GLuint attr, + GLuint newsz ) +{ + TNLcontext *tnl = TNL_CONTEXT(ctx); + GLuint oldsz; + GLuint i; + GLfloat *tmp; + GLint lastcount = tnl->vtx.initial_counter - tnl->vtx.counter; + + /* Run pipeline on current vertices, copy wrapped vertices + * to tnl->vtx.copied. + */ + _tnl_wrap_buffers( ctx ); + + + /* 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. + */ + _tnl_copy_to_current( ctx ); + + + /* 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 && + tnl->vtx.attrsz[attr] == 0 && + lastcount > 8 && + tnl->vtx.vertex_size) { + reset_attrfv( tnl ); + } + + /* Fix up sizes: + */ + oldsz = tnl->vtx.attrsz[attr]; + tnl->vtx.attrsz[attr] = newsz; + + tnl->vtx.vertex_size += newsz - oldsz; + tnl->vtx.counter = MIN2( VERT_BUFFER_SIZE / tnl->vtx.vertex_size, + ctx->Const.MaxArrayLockSize ); + tnl->vtx.initial_counter = tnl->vtx.counter; + tnl->vtx.vbptr = tnl->vtx.buffer; + + + /* Recalculate all the attrptr[] values + */ + for (i = 0, tmp = tnl->vtx.vertex ; i < _TNL_ATTRIB_MAX ; i++) { + if (tnl->vtx.attrsz[i]) { + tnl->vtx.attrptr[i] = tmp; + tmp += tnl->vtx.attrsz[i]; + } + else + tnl->vtx.attrptr[i] = NULL; /* will not be dereferenced */ + } + + /* Copy from current to repopulate the vertex with correct values. + */ + _tnl_copy_from_current( ctx ); + + /* Replay stored vertices to translate them + * to new format here. + * + * -- No need to replay - just copy piecewise + */ + if (tnl->vtx.copied.nr) + { + const GLfloat *data = tnl->vtx.copied.buffer; + GLfloat *dest = tnl->vtx.buffer; + GLuint j; + + for (i = 0 ; i < tnl->vtx.copied.nr ; i++) { + for (j = 0 ; j < _TNL_ATTRIB_MAX ; j++) { + if (tnl->vtx.attrsz[j]) { + if (j == attr) { + if (oldsz) { + COPY_CLEAN_4V( dest, oldsz, data ); + data += oldsz; + dest += newsz; + } else { + COPY_SZ_4V( dest, newsz, tnl->vtx.current[j] ); + dest += newsz; + } + } + else { + GLuint sz = tnl->vtx.attrsz[j]; + COPY_SZ_4V( dest, sz, data ); + dest += sz; + data += sz; + } + } + } + } + + tnl->vtx.vbptr = dest; + tnl->vtx.counter -= tnl->vtx.copied.nr; + tnl->vtx.copied.nr = 0; + } + + /* For codegen - attrptr's may have changed, so need to redo + * codegen. Might be a reasonable place to try & detect attributes + * in the vertex which aren't being submitted any more. + */ + for (i = 0 ; i < _TNL_ATTRIB_MAX ; i++) + if (tnl->vtx.attrsz[i]) { + GLuint j = tnl->vtx.attrsz[i] - 1; + + if (i < _TNL_MAX_ATTR_CODEGEN) + tnl->vtx.tabfv[i][j] = choose[i][j]; + } + +} + + +static void _tnl_fixup_vertex( GLcontext *ctx, GLuint attr, GLuint sz ) +{ + TNLcontext *tnl = TNL_CONTEXT(ctx); + static const GLfloat id[4] = { 0, 0, 0, 1 }; + int i; + + if (0) + _mesa_printf("%s attr %d sz %d -> %d\n", + __FUNCTION__, attr, tnl->vtx.attrsz[attr], sz); + + if (tnl->vtx.attrsz[attr] < sz) { + /* New size is larger. Need to flush existing vertices and get + * an enlarged vertex format. + */ + _tnl_wrap_upgrade_vertex( ctx, attr, sz ); + } + else if (sz < tnl->vtx.active_sz[attr]) { + /* New size is smaller - just need to fill in some + * zeros. Don't need to flush or wrap. + */ + for (i = sz ; i <= tnl->vtx.attrsz[attr] ; i++) + tnl->vtx.attrptr[attr][i-1] = id[i-1]; + } + + tnl->vtx.active_sz[attr] = sz; + + /* 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; + else + ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT; +} + +#ifdef USE_X86_ASM + +static struct _tnl_dynfn *lookup( struct _tnl_dynfn *l, GLuint key ) +{ + struct _tnl_dynfn *f; + + foreach( f, l ) { + if (f->key == key) + return f; + } + + return NULL; +} + + +static tnl_attrfv_func do_codegen( GLcontext *ctx, GLuint attr, GLuint sz ) +{ + TNLcontext *tnl = TNL_CONTEXT(ctx); + struct _tnl_dynfn *dfn = NULL; + + if (attr == 0) { + GLuint key = tnl->vtx.vertex_size; + + dfn = lookup( &tnl->vtx.cache.Vertex[sz-1], key ); + + if (!dfn) + dfn = tnl->vtx.gen.Vertex[sz-1]( ctx, key ); + } + else { + GLuint key = (GLuint) tnl->vtx.attrptr[attr]; + + dfn = lookup( &tnl->vtx.cache.Attribute[sz-1], key ); + + if (!dfn) + dfn = tnl->vtx.gen.Attribute[sz-1]( ctx, key ); + } + + if (dfn) + return *(tnl_attrfv_func *) &dfn->code; + else + return NULL; +} + +#endif /* USE_X86_ASM */ + +/* Helper function for 'CHOOSE' macro. Do what's necessary when an + * entrypoint is called for the first time. + */ + +static tnl_attrfv_func do_choose( GLuint attr, GLuint sz ) +{ + GET_CURRENT_CONTEXT( ctx ); + TNLcontext *tnl = TNL_CONTEXT(ctx); + GLuint oldsz = tnl->vtx.active_sz[attr]; + + assert(attr < _TNL_MAX_ATTR_CODEGEN); + + if (oldsz != sz) { + /* Reset any active pointers for this attribute + */ + if (oldsz) + tnl->vtx.tabfv[attr][oldsz-1] = choose[attr][oldsz-1]; + + _tnl_fixup_vertex( ctx, attr, sz ); + + } + + + /* Try to use codegen: + */ +#ifdef USE_X86_ASM + if (tnl->AllowCodegen) + tnl->vtx.tabfv[attr][sz-1] = do_codegen( ctx, attr, sz ); + else +#endif + tnl->vtx.tabfv[attr][sz-1] = NULL; + + /* Else use generic version: + */ + if (!tnl->vtx.tabfv[attr][sz-1]) + tnl->vtx.tabfv[attr][sz-1] = generic_attr_func[attr][sz-1]; + + ASSERT(tnl->vtx.tabfv[attr][sz-1]); + return tnl->vtx.tabfv[attr][sz-1]; +} + + + +#define CHOOSE( ATTR, N ) \ +static void choose_##ATTR##_##N( const GLfloat *v ) \ +{ \ + tnl_attrfv_func f = do_choose(ATTR, N); \ + ASSERT(f); \ + f( v ); \ +} + +#define CHOOSERS( ATTRIB ) \ + CHOOSE( ATTRIB, 1 ) \ + CHOOSE( ATTRIB, 2 ) \ + CHOOSE( ATTRIB, 3 ) \ + CHOOSE( ATTRIB, 4 ) \ + + +#define INIT_CHOOSERS(ATTR) \ + ASSERT(ATTR <= _TNL_ATTRIB_ERROR);\ + choose[ATTR][0] = choose_##ATTR##_1; \ + choose[ATTR][1] = choose_##ATTR##_2; \ + choose[ATTR][2] = choose_##ATTR##_3; \ + choose[ATTR][3] = choose_##ATTR##_4; + +/* conventional attributes */ +CHOOSERS( 0 ) +CHOOSERS( 1 ) +CHOOSERS( 2 ) +CHOOSERS( 3 ) +CHOOSERS( 4 ) +CHOOSERS( 5 ) +CHOOSERS( 6 ) +CHOOSERS( 7 ) +CHOOSERS( 8 ) +CHOOSERS( 9 ) +CHOOSERS( 10 ) +CHOOSERS( 11 ) +CHOOSERS( 12 ) +CHOOSERS( 13 ) +CHOOSERS( 14 ) +CHOOSERS( 15 ) + +/* generic attributes */ +CHOOSERS( 16 ) +CHOOSERS( 17 ) +CHOOSERS( 18 ) +CHOOSERS( 19 ) +CHOOSERS( 20 ) +CHOOSERS( 21 ) +CHOOSERS( 22 ) +CHOOSERS( 23 ) +CHOOSERS( 24 ) +CHOOSERS( 25 ) +CHOOSERS( 26 ) +CHOOSERS( 27 ) +CHOOSERS( 28 ) +CHOOSERS( 29 ) +CHOOSERS( 30 ) +CHOOSERS( 31 ) + + +/** + * This function will get called when glVertexAttribNV/ARB() is called + * with an invalid index parameter. + */ +static void +error_attrib(const GLfloat *unused) +{ + GET_CURRENT_CONTEXT( ctx ); + (void) unused; + _mesa_error( ctx, GL_INVALID_VALUE, "glVertexAttrib(index)" ); +} + + + +/** + * Reset all the per-vertex functions pointers to point to the default + * "chooser" functions. + */ +static void +reset_attrfv(TNLcontext *tnl) +{ + GLuint i; + + for (i = 0 ; i < _TNL_ATTRIB_MAX ; i++) + if (tnl->vtx.attrsz[i]) { + GLint j = tnl->vtx.attrsz[i] - 1; + tnl->vtx.attrsz[i] = 0; + tnl->vtx.active_sz[i] = 0; + + if (i < _TNL_MAX_ATTR_CODEGEN) { + while (j >= 0) { + tnl->vtx.tabfv[i][j] = choose[i][j]; + j--; + } + } + } + + tnl->vtx.vertex_size = 0; + tnl->vtx.have_materials = 0; +} + + + +/** + * Materials: + * + * These are treated as per-vertex attributes, at indices above where + * the NV_vertex_program leaves off. There are a lot of good things + * about treating materials this way. + * + * However: I don't want to double the number of generated functions + * just to cope with this, so I unroll the 'C' varients of CHOOSE and + * ATTRF into this function, and dispense with codegen and + * second-level dispatch. + * + * There is no aliasing of material attributes with other entrypoints. + */ +#define OTHER_ATTR( A, N, params ) \ +do { \ + if (tnl->vtx.active_sz[A] != N) { \ + _tnl_fixup_vertex( ctx, A, N ); \ + } \ + \ + { \ + GLfloat *dest = tnl->vtx.attrptr[A]; \ + if (N>0) dest[0] = (params)[0]; \ + if (N>1) dest[1] = (params)[1]; \ + if (N>2) dest[2] = (params)[2]; \ + if (N>3) dest[3] = (params)[3]; \ + } \ +} while (0) + + +#define MAT( ATTR, N, face, params ) \ +do { \ + if (face != GL_BACK) \ + OTHER_ATTR( ATTR, N, params ); /* front */ \ + if (face != GL_FRONT) \ + OTHER_ATTR( ATTR + 1, N, params ); /* back */ \ +} while (0) + + +/** + * Called by glMaterialfv(). + * Colormaterial is dealt with later on. + */ +static void GLAPIENTRY +_tnl_Materialfv( GLenum face, GLenum pname, const GLfloat *params ) +{ + GET_CURRENT_CONTEXT( ctx ); + TNLcontext *tnl = TNL_CONTEXT(ctx); + + switch (face) { + case GL_FRONT: + case GL_BACK: + case GL_FRONT_AND_BACK: + break; + default: + _mesa_error( ctx, GL_INVALID_ENUM, "glMaterialfv" ); + return; + } + + switch (pname) { + case GL_EMISSION: + MAT( _TNL_ATTRIB_MAT_FRONT_EMISSION, 4, face, params ); + break; + case GL_AMBIENT: + MAT( _TNL_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params ); + break; + case GL_DIFFUSE: + MAT( _TNL_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params ); + break; + case GL_SPECULAR: + MAT( _TNL_ATTRIB_MAT_FRONT_SPECULAR, 4, face, params ); + break; + case GL_SHININESS: + MAT( _TNL_ATTRIB_MAT_FRONT_SHININESS, 1, face, params ); + break; + case GL_COLOR_INDEXES: + MAT( _TNL_ATTRIB_MAT_FRONT_INDEXES, 3, face, params ); + break; + case GL_AMBIENT_AND_DIFFUSE: + MAT( _TNL_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params ); + MAT( _TNL_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params ); + break; + default: + _mesa_error( ctx, GL_INVALID_ENUM, "glMaterialfv" ); + return; + } + + tnl->vtx.have_materials = GL_TRUE; +} + + +static void GLAPIENTRY _tnl_EdgeFlag( GLboolean b ) +{ + GET_CURRENT_CONTEXT( ctx ); + TNLcontext *tnl = TNL_CONTEXT(ctx); + GLfloat f = (GLfloat)b; + + OTHER_ATTR( _TNL_ATTRIB_EDGEFLAG, 1, &f ); +} + + +/* Eval + */ +static void GLAPIENTRY _tnl_EvalCoord1f( GLfloat u ) +{ + GET_CURRENT_CONTEXT( ctx ); + TNLcontext *tnl = TNL_CONTEXT(ctx); + + /* TODO: use a CHOOSE() function for this: */ + { + GLint i; + if (tnl->vtx.eval.new_state) + _tnl_update_eval( ctx ); + + for (i = 0; i < _TNL_NUM_EVAL; i++) { + if (tnl->vtx.eval.map1[i].map) + if (tnl->vtx.active_sz[i] != tnl->vtx.eval.map1[i].sz) + _tnl_fixup_vertex( ctx, i, tnl->vtx.eval.map1[i].sz ); + } + } + + + _mesa_memcpy( tnl->vtx.copied.buffer, tnl->vtx.vertex, + tnl->vtx.vertex_size * sizeof(GLfloat)); + + _tnl_do_EvalCoord1f( ctx, u ); + + _mesa_memcpy( tnl->vtx.vertex, tnl->vtx.copied.buffer, + tnl->vtx.vertex_size * sizeof(GLfloat)); +} + +static void GLAPIENTRY _tnl_EvalCoord2f( GLfloat u, GLfloat v ) +{ + GET_CURRENT_CONTEXT( ctx ); + TNLcontext *tnl = TNL_CONTEXT(ctx); + + /* TODO: use a CHOOSE() function for this: */ + { + GLint i; + if (tnl->vtx.eval.new_state) + _tnl_update_eval( ctx ); + + for (i = 0; i < _TNL_NUM_EVAL; i++) { + if (tnl->vtx.eval.map2[i].map) + if (tnl->vtx.active_sz[i] != tnl->vtx.eval.map2[i].sz) + _tnl_fixup_vertex( ctx, i, tnl->vtx.eval.map2[i].sz ); + } + + if (ctx->Eval.AutoNormal) + if (tnl->vtx.active_sz[_TNL_ATTRIB_NORMAL] != 3) + _tnl_fixup_vertex( ctx, _TNL_ATTRIB_NORMAL, 3 ); + } + + _mesa_memcpy( tnl->vtx.copied.buffer, tnl->vtx.vertex, + tnl->vtx.vertex_size * sizeof(GLfloat)); + + _tnl_do_EvalCoord2f( ctx, u, v ); + + _mesa_memcpy( tnl->vtx.vertex, tnl->vtx.copied.buffer, + tnl->vtx.vertex_size * sizeof(GLfloat)); +} + +static void GLAPIENTRY _tnl_EvalCoord1fv( const GLfloat *u ) +{ + _tnl_EvalCoord1f( u[0] ); +} + +static void GLAPIENTRY _tnl_EvalCoord2fv( const GLfloat *u ) +{ + _tnl_EvalCoord2f( u[0], u[1] ); +} + +static void GLAPIENTRY _tnl_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; + + _tnl_EvalCoord1f( u ); +} + + +static void GLAPIENTRY _tnl_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; + + _tnl_EvalCoord2f( u, v ); +} + + +/** + * Called from glBegin. + * ctx->Driver.CurrentExecPrimitive will be set to <mode>. + */ +static void GLAPIENTRY _tnl_Begin( GLenum mode ) +{ + GET_CURRENT_CONTEXT( ctx ); + + if (mode > GL_POLYGON) { + _mesa_error(ctx, GL_INVALID_ENUM, "glBegin(mode)"); + return; + } + + if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END) { + /* we're not inside a glBegin/End pair */ + TNLcontext *tnl = TNL_CONTEXT(ctx); + GLuint i; + + if (ctx->NewState) { + _mesa_update_state( ctx ); + + if ((ctx->VertexProgram.Enabled && !ctx->VertexProgram._Enabled) || + (ctx->FragmentProgram.Enabled && !ctx->FragmentProgram._Enabled)) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glBegin (invalid vertex/fragment program)"); + tnl->DiscardPrimitive = GL_TRUE; + return; + } + + if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { + _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT, + "glBegin(incomplete framebuffer)"); + tnl->DiscardPrimitive = GL_TRUE; + return; + } + + tnl->DiscardPrimitive = GL_FALSE; + + if (!(tnl->Driver.NotifyBegin && + tnl->Driver.NotifyBegin( ctx, mode ))) + CALL_Begin(ctx->Exec, (mode)); + return; + } + + /* Heuristic: attempt to isolate attributes occuring outside + * begin/end pairs. + */ + if (tnl->vtx.vertex_size && !tnl->vtx.attrsz[0]) + _tnl_FlushVertices( ctx, ~0 ); + + i = tnl->vtx.prim_count++; + tnl->vtx.prim[i].mode = mode | PRIM_BEGIN; + tnl->vtx.prim[i].start = tnl->vtx.initial_counter - tnl->vtx.counter; + tnl->vtx.prim[i].count = 0; + + ctx->Driver.CurrentExecPrimitive = mode; + } + else { + /* already inside glBegin/End */ + _mesa_error( ctx, GL_INVALID_OPERATION, "glBegin" ); + } +} + + +/** + * Called from glEnd. + */ +static void GLAPIENTRY _tnl_End( void ) +{ + GET_CURRENT_CONTEXT( ctx ); + + if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { + /* closing an open glBegin primitive */ + TNLcontext *tnl = TNL_CONTEXT(ctx); + int idx = tnl->vtx.initial_counter - tnl->vtx.counter; + int i = tnl->vtx.prim_count - 1; + + tnl->vtx.prim[i].mode |= PRIM_END; + tnl->vtx.prim[i].count = idx - tnl->vtx.prim[i].start; + + ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END; + + /* Two choices which effect the way vertex attributes are + * carried over (or not) between adjacent primitives. + */ +#if 0 + if (tnl->vtx.prim_count == TNL_MAX_PRIM) + _tnl_FlushVertices( ctx, ~0 ); +#else + if (tnl->vtx.prim_count == TNL_MAX_PRIM) + _tnl_flush_vtx( ctx ); +#endif + + } + else { + /* glBegin hasn't been called! */ + _mesa_error( ctx, GL_INVALID_OPERATION, "glEnd" ); + } +} + + +/** + * XXX why aren't all members initialized here?? + */ +static void _tnl_exec_vtxfmt_init( GLcontext *ctx ) +{ + GLvertexformat *vfmt = &(TNL_CONTEXT(ctx)->exec_vtxfmt); + + vfmt->ArrayElement = _ae_loopback_array_elt; /* generic helper */ + vfmt->Begin = _tnl_Begin; + vfmt->CallList = _mesa_CallList; + vfmt->CallLists = _mesa_CallLists; + vfmt->EdgeFlag = _tnl_EdgeFlag; + vfmt->End = _tnl_End; + vfmt->EvalCoord1f = _tnl_EvalCoord1f; + vfmt->EvalCoord1fv = _tnl_EvalCoord1fv; + vfmt->EvalCoord2f = _tnl_EvalCoord2f; + vfmt->EvalCoord2fv = _tnl_EvalCoord2fv; + vfmt->EvalPoint1 = _tnl_EvalPoint1; + vfmt->EvalPoint2 = _tnl_EvalPoint2; + vfmt->Materialfv = _tnl_Materialfv; + + vfmt->Rectf = _mesa_noop_Rectf; + vfmt->EvalMesh1 = _mesa_noop_EvalMesh1; + vfmt->EvalMesh2 = _mesa_noop_EvalMesh2; +} + + + +void _tnl_FlushVertices( GLcontext *ctx, GLuint flags ) +{ + TNLcontext *tnl = TNL_CONTEXT(ctx); + (void) flags; + + if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END) { + /* still inside a glBegin/End pair. How'd we get here??? */ + return; + } + + if (tnl->DiscardPrimitive) { + /* discard any primitives */ + tnl->vtx.prim_count = 0; + tnl->vtx.counter = tnl->vtx.initial_counter; + tnl->vtx.vbptr = tnl->vtx.buffer; + } + + if (tnl->vtx.counter != tnl->vtx.initial_counter) { + _tnl_flush_vtx( ctx ); + } + + if (tnl->vtx.vertex_size) { + _tnl_copy_to_current( ctx ); + reset_attrfv( tnl ); + } + + ctx->Driver.NeedFlush = 0; +} + + +/** + * Init the tnl->vtx->current[] pointers to point to the corresponding + * fields in ctx->Current attribute group. + */ +static void _tnl_current_init( GLcontext *ctx ) +{ + TNLcontext *tnl = TNL_CONTEXT(ctx); + GLint i; + + /* setup the pointers for the typical (32) vertex attributes */ + for (i = 0; i < VERT_ATTRIB_MAX; i++) + tnl->vtx.current[i] = ctx->Current.Attrib[i]; + + /* setup pointers for the 12 material attributes */ + for (i = 0; i < MAT_ATTRIB_MAX; i++) + tnl->vtx.current[_TNL_ATTRIB_MAT_FRONT_AMBIENT + i] = + ctx->Light.Material.Attrib[i]; + + /* special case */ + tnl->vtx.current[_TNL_ATTRIB_EDGEFLAG] = &tnl->vtx.CurrentFloatEdgeFlag; +} + +static struct _tnl_dynfn *no_codegen( GLcontext *ctx, int key ) +{ + (void) ctx; (void) key; + return NULL; +} + +void _tnl_vtx_init( GLcontext *ctx ) +{ + TNLcontext *tnl = TNL_CONTEXT(ctx); + struct tnl_vertex_arrays *tmp = &tnl->vtx_inputs; + GLuint i; + static int firsttime = 1; + + if (firsttime) { + firsttime = 0; + + /* conventional attributes */ + INIT_CHOOSERS( 0 ); + INIT_CHOOSERS( 1 ); + INIT_CHOOSERS( 2 ); + INIT_CHOOSERS( 3 ); + INIT_CHOOSERS( 4 ); + INIT_CHOOSERS( 5 ); + INIT_CHOOSERS( 6 ); + INIT_CHOOSERS( 7 ); + INIT_CHOOSERS( 8 ); + INIT_CHOOSERS( 9 ); + INIT_CHOOSERS( 10 ); + INIT_CHOOSERS( 11 ); + INIT_CHOOSERS( 12 ); + INIT_CHOOSERS( 13 ); + INIT_CHOOSERS( 14 ); + INIT_CHOOSERS( 15 ); + + /* generic attributes */ + INIT_CHOOSERS( 16 ); + INIT_CHOOSERS( 17 ); + INIT_CHOOSERS( 18 ); + INIT_CHOOSERS( 19 ); + INIT_CHOOSERS( 20 ); + INIT_CHOOSERS( 21 ); + INIT_CHOOSERS( 22 ); + INIT_CHOOSERS( 23 ); + INIT_CHOOSERS( 24 ); + INIT_CHOOSERS( 25 ); + INIT_CHOOSERS( 26 ); + INIT_CHOOSERS( 27 ); + INIT_CHOOSERS( 28 ); + INIT_CHOOSERS( 29 ); + INIT_CHOOSERS( 30 ); + INIT_CHOOSERS( 31 ); + + choose[_TNL_ATTRIB_ERROR][0] = error_attrib; + choose[_TNL_ATTRIB_ERROR][1] = error_attrib; + choose[_TNL_ATTRIB_ERROR][2] = error_attrib; + choose[_TNL_ATTRIB_ERROR][3] = error_attrib; + +#ifdef USE_X86_ASM + if (tnl->AllowCodegen) { + _tnl_x86choosers(choose, do_choose); /* x86 INIT_CHOOSERS */ + } +#endif + + _tnl_generic_attr_table_init( generic_attr_func ); + } + + for (i = 0; i < _TNL_ATTRIB_EDGEFLAG; i++) + _mesa_vector4f_init( &tmp->Attribs[i], 0, NULL); + + for (i = 0; i < 4; i++) { + make_empty_list( &tnl->vtx.cache.Vertex[i] ); + make_empty_list( &tnl->vtx.cache.Attribute[i] ); + tnl->vtx.gen.Vertex[i] = no_codegen; + tnl->vtx.gen.Attribute[i] = no_codegen; + } + +#ifdef USE_X86_ASM + _tnl_InitX86Codegen( &tnl->vtx.gen ); +#endif + + _tnl_current_init( ctx ); + _tnl_exec_vtxfmt_init( ctx ); + _tnl_generic_exec_vtxfmt_init( ctx ); +#ifdef USE_X86_ASM + if (tnl->AllowCodegen) { + _tnl_x86_exec_vtxfmt_init( ctx ); /* x86 DISPATCH_ATTRFV */ + } +#endif + + _mesa_install_exec_vtxfmt( ctx, &tnl->exec_vtxfmt ); + + _mesa_memcpy( tnl->vtx.tabfv, choose, sizeof(choose) ); + + for (i = 0 ; i < _TNL_ATTRIB_MAX ; i++) { + tnl->vtx.attrsz[i] = 0; + tnl->vtx.active_sz[i] = 0; + } + + tnl->vtx.vertex_size = 0; + tnl->vtx.have_materials = 0; +} + +static void free_funcs( struct _tnl_dynfn *l ) +{ + struct _tnl_dynfn *f, *tmp; + foreach_s (f, tmp, l) { + remove_from_list( f ); + ALIGN_FREE( f->code ); + FREE( f ); + } +} + + +void _tnl_vtx_destroy( GLcontext *ctx ) +{ + TNLcontext *tnl = TNL_CONTEXT(ctx); + GLuint i; + + for (i = 0; i < 4; i++) { + free_funcs( &tnl->vtx.cache.Vertex[i] ); + free_funcs( &tnl->vtx.cache.Attribute[i] ); + } +} + |