added GL_X_RENDERABLE to glXChooseFBConfig (bug 4181)
[mesa.git] / src / mesa / tnl / t_vtx_api.c
index d734b67dbd80582d263b7ae5b0027053637fef69..fa9e04ad336f52c38e994abff940248f477b87ee 100644 (file)
@@ -30,513 +30,483 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
  * Authors:
  *   Keith Whitwell <keith@tungstengraphics.com>
  */
-#include "mtypes.h"
+
+#include "glheader.h"
 #include "context.h"
-#include "colormac.h"
-#include "simple_list.h"
+#include "macros.h"
+#include "vtxfmt.h"
+#include "dlist.h"
+#include "state.h"
+#include "light.h"
 #include "api_arrayelt.h"
-
-#include "t_context.h"
+#include "api_noop.h"
 #include "t_vtx_api.h"
+#include "simple_list.h"
 
+#include "dispatch.h"
 
-/* Versions of all the entrypoints for situations where codegen isn't
- * available.  This is slowed significantly by all the gumph necessary
- * to get to the tnl pointer, which can be avoided with codegen.
- *
- * Note: Only one size for each attribute may be active at once.
- * Eg. if Color3f is installed/active, then Color4f may not be, even
- * if the vertex actually contains 4 color coordinates.  This is
- * because the 3f version won't otherwise set color[3] to 1.0 -- this
- * is the job of the chooser function when switching between Color4f
- * and Color3f.
- */
-#define ATTRF( ATTR, N, A, B, C, D )                   \
-{                                                      \
-   GET_CURRENT_CONTEXT( ctx );                         \
-   TNLcontext *tnl = TNL_CONTEXT(ctx);                 \
-                                                       \
-   if ((ATTR) == 0) {                                  \
-      int i;                                           \
-                                                       \
-      if (N>0) tnl->vtx.vbptr[0].f = A;                        \
-      if (N>1) tnl->vtx.vbptr[1].f = B;                        \
-      if (N>2) tnl->vtx.vbptr[2].f = C;                        \
-      if (N>3) tnl->vtx.vbptr[3].f = D;                        \
-                                                       \
-      for (i = N; i < tnl->vtx.vertex_size; i++)       \
-        tnl->vtx.vbptr[i].ui = tnl->vtx.vertex[i].ui;  \
-                                                       \
-      tnl->vtx.vbptr += tnl->vtx.vertex_size;          \
-                                                       \
-      if (--tnl->vtx.counter == 0)                     \
-        _tnl_FlushVertices( ctx, FLUSH_STORED_VERTICES );      \
-   }                                                   \
-   else {                                              \
-      union uif *dest = tnl->vtx.attrptr[ATTR];                \
-      if (N>0) dest[0].f = A;                          \
-      if (N>1) dest[1].f = B;                          \
-      if (N>2) dest[2].f = C;                          \
-      if (N>3) dest[3].f = D;                          \
-   }                                                   \
-}
+static void reset_attrfv( TNLcontext *tnl );
 
-#define ATTR4F( ATTR, A, B, C, D )  ATTRF( ATTR, 4, A, B, C, D )
-#define ATTR3F( ATTR, A, B, C )     ATTRF( ATTR, 3, A, B, C, 1 )
-#define ATTR2F( ATTR, A, B )        ATTRF( ATTR, 2, A, B, 0, 1 )
-#define ATTR1F( ATTR, A  )          ATTRF( ATTR, 1, A, 0, 0, 1 )
-
-#define ATTRS( ATTRIB )                                                \
-static void attrib_##ATTRIB##_1_0( GLfloat s )                 \
-{                                                              \
-   ATTR1F( ATTRIB, s );                                                \
-}                                                              \
-                                                               \
-static void attrib_##ATTRIB##_1_1( const GLfloat *v )          \
-{                                                              \
-   ATTR1F( ATTRIB, v[0] );                                     \
-}                                                              \
-                                                               \
-static void attrib_##ATTRIB##_2_0( GLfloat s, GLfloat t )      \
-{                                                              \
-   ATTR2F( ATTRIB, s, t );                                     \
-}                                                              \
-                                                               \
-static void attrib_##ATTRIB##_2_1( const GLfloat *v )          \
-{                                                              \
-   ATTR2F( ATTRIB, v[0], v[1] );                               \
-}                                                              \
-                                                               \
-static void attrib_##ATTRIB##_3_0( GLfloat s, GLfloat t,       \
-                                  GLfloat r )                  \
-{                                                              \
-   ATTR3F( ATTRIB, s, t, r );                                  \
-}                                                              \
-                                                               \
-static void attrib_##ATTRIB##_3_1( const GLfloat *v )          \
-{                                                              \
-   ATTR3F( ATTRIB, v[0], v[1], v[2] );                         \
-}                                                              \
-                                                               \
-static void attrib_##ATTRIB##_4_0( GLfloat s, GLfloat t,       \
-                                  GLfloat r, GLfloat q )       \
-{                                                              \
-   ATTR4F( ATTRIB, s, t, r, q );                               \
-}                                                              \
-                                                               \
-static void attrib_##ATTRIB##_4_1( const GLfloat *v )          \
-{                                                              \
-   ATTR4F( ATTRIB, v[0], v[1], v[2], v[3] );                   \
-}
+static tnl_attrfv_func choose[_TNL_MAX_ATTR_CODEGEN+1][4]; /* +1 for ERROR_ATTRIB */
+static tnl_attrfv_func generic_attr_func[_TNL_MAX_ATTR_CODEGEN][4];
 
-/* Generate a lot of functions.  These are the actual worker
- * functions, which are equivalent to those generated via codegen
- * elsewhere.
- */
-ATTRS( 0 )
-ATTRS( 1 )
-ATTRS( 2 )
-ATTRS( 3 )
-ATTRS( 4 )
-ATTRS( 5 )
-ATTRS( 6 )
-ATTRS( 7 )
-ATTRS( 8 )
-ATTRS( 9 )
-ATTRS( 10 )
-ATTRS( 11 )
-ATTRS( 12 )
-ATTRS( 13 )
-ATTRS( 14 )
-ATTRS( 15 )
 
-/* Flush existing data, set new attrib size, replay copied vertices.
- */ 
-static void _tnl_upgrade_vertex( GLcontext *ctx, 
-                                GLuint attr,
-                                GLuint newsz )
+/* Close off the last primitive, execute the buffer, restart the
+ * primitive.  
+ */
+static void _tnl_wrap_buffers( GLcontext *ctx )
 {
-   GLuint oldsz = tnl->vtx.attrib[attr].sz;
+   TNLcontext *tnl = TNL_CONTEXT(ctx);
+   
 
-   _tnl_flush_immediate( 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 != GL_POLYGON+1) {
+        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);
+      }
 
-   tnl->vtx.attrib[attr].sz = newsz;
-   /* What else to do here?
-    */
+      last_count = tnl->vtx.prim[tnl->vtx.prim_count-1].count;
 
-   /* Replay stored vertices to translate them to new format: Use
-    * bitmap and ffs() to speed inner loop:
-    */
-   for (i = 0 ; i < tnl->copied_verts.nr ; i++) {
-      GLfloat *data = old_data + tnl->copied_verts.offset[i];
-
-      for (j = 1 ; j < MAX_ATTRIB ; j++) {
-        if (tnl->vtx.attrib[j].sz) {
-           if (j == attr) {
-              GLfloat tmp[4];
-              COPY_4FV( tmp, id );
-              COPY_SZ_4V( tmp, oldsz, data );
-              data += oldsz;
-              tnl->vtx.attrib[attr].fv( tmp );
-           }
-           else {
-              GLfloat *tmp = data;
-              data += tnl->vtx.attrib[j].sz;
-              tnl->vtx.attrib[j].fv( tmp );
-           }
-        }
+      /* 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;
       }
-   }
-}
 
-static void _tnl_wrap_buffers( GLcontext *ctx )
-{
-   _tnl_flush_immediate( ctx );
+      /* Emit a glBegin to start the new list.
+       */
+      assert(tnl->vtx.prim_count == 0);
 
-   /* Replay stored vertices - don't really need to do this, memcpy
-    * would be fine.
-    */
-   for (i = 0 ; i < tnl->copied_verts.nr ; i++) {
-      for (j = 1 ; j < MAX_ATTRIB ; j++) {
-        GLfloat *tmp = data;
-        data += tnl->vtx.attrib[j].sz;
-        tnl->vtx.attrib[j].fv( tmp );
+      if (ctx->Driver.CurrentExecPrimitive != GL_POLYGON+1) {
+        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;
       }
    }
 }
 
 
-
-/* The functions defined below (CHOOSERS) are the initial state for
- * dispatch entries for all entrypoints except those requiring
- * double-dispatch (multitexcoord, material, vertexattrib).
+/* Deal with buffer wrapping where provoked by the vertex buffer
+ * filling up, as opposed to upgrade_vertex().
  *
- * These may provoke a vertex-upgrade where the existing vertex buffer
- * is flushed and a new element is added to the active vertex layout.
- * This can happen between begin/end pairs.
+ * 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;
+}
 
-/* An active attribute has changed size.
+
+/*
+ * Copy the active vertex's values to the ctx->Current fields.
  */
-static void _tnl_fixup_vertex( GLcontext *ctx, GLuint attr, GLuint sz )
+static void _tnl_copy_to_current( GLcontext *ctx )
 {
    TNLcontext *tnl = TNL_CONTEXT(ctx); 
+   GLuint i;
+
+   for (i = _TNL_ATTRIB_POS+1 ; i < _TNL_ATTRIB_INDEX ; 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]);
+      }
+   }
 
-   if (tnl->vtx.attrib_sz[attr] < sz) {
-      /* New size is larger.  Need to flush existing vertices and get
-       * an enlarged vertex format.
-       */
-      _tnl_upgrade_vertex( tnl, attr, sz );
+   /* color index is special (it's not a float[4] so COPY_CLEAN_4V above
+    * will trash adjacent memory!)
+    */
+   if (tnl->vtx.attrsz[_TNL_ATTRIB_INDEX]) {
+      ctx->Current.Index = tnl->vtx.attrptr[_TNL_ATTRIB_INDEX][0];
    }
-   else {
-      static float id[4] = { 0, 0, 0, 1 };
-      int i;
 
-      /* New size is smaller - just need to fill in some zeros.
-       */
-      for (i = sz ; i < tnl->vtx.attrib_sz[attr] ; i++)
-        tnl->vtx.attrptr[attr][i].f = id[i];
+   /* Edgeflag requires additional treatment:
+    */
+   if (tnl->vtx.attrsz[_TNL_ATTRIB_EDGEFLAG]) {
+      ctx->Current.EdgeFlag = 
+        (tnl->vtx.CurrentFloatEdgeFlag == 1.0);
    }
-   
-   /* Reset the dispatch table - aliasing entrypoints are invalidated.
+
+   /* Colormaterial -- this kindof sucks.
     */
-   _tnl_reset_attr_dispatch_tab( ctx );
+   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->Driver.NeedFlush &= ~FLUSH_UPDATE_CURRENT;
 }
 
-static int dispatch_offset[TNL_ATTRIB_MAX][4][2];
 
+static void _tnl_copy_from_current( GLcontext *ctx )
+{
+   TNLcontext *tnl = TNL_CONTEXT(ctx); 
+   GLint i;
 
-static void *lookup_or_generate( GLuint attr, GLuint sz, GLuint v,
-                                void *fallback_attr_func )
-{ 
-   GET_CURRENT_CONTEXT( ctx ); 
+   /* 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); 
-   void *ptr = 0;
-   struct dynfn *dfn;
-   int key;
-
-   /* This will remove any installed handlers for attr with different
-    * sz, will flush, copy and expand the copied vertices if sz won't
-    * fit in the current vertex, or will clean the current vertex if
-    * it already has this attribute in a larger size.
+   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.
     */
-   if (tnl->vtx.attrib_sz[attr] != sz)
-      _tnl_fixup_vertex( ctx, attr, sz );
+   _tnl_wrap_buffers( ctx );
 
 
-   if (attr == 0) 
-      key = tnl->vtx.vertex_size;
-   else
-      key = (GLuint)tnl->vtx.attrptr[attr];
+   /* 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 );
 
-   for (dfn = tnl->vtx.generated[sz-1][v][isvertex]; dfn; dfn = dfn->next) {
-      if (dfn->key == key) {
-        ptr = dfn->code;
-        break;
-      }
+
+   /* 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 );
    }
 
-   if (ptr == 0) {
-      dfn = tnl->vtx.codegen[sz-1][v][isvertex]( ctx, key );
-      if (dfn) {
-        ptr = dfn->code;
-        dfn->next = tnl->vtx.generated[sz-1][v][isvertex];
-        tnl->vtx.generated[sz-1][v][isvertex] = dfn;
+   /* 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 */
    }
-      
-   if (ptr == 0)
-      ptr = fallback_attr_func;
 
-   ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT;
+   /* Copy from current to repopulate the vertex with correct values.
+    */
+   _tnl_copy_from_current( ctx );
 
-   /* Need to set all the aliases to this function, too
+   /* Replay stored vertices to translate them
+    * to new format here.
+    *
+    * -- No need to replay - just copy piecewise
     */
-   if (dispatch_offset[attr][sz-1][v]) 
-      ((void **)tnl->Exec)[dispatch_offset[attr][sz-1][v]] = ptr;
+   if (tnl->vtx.copied.nr)
+   {
+      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;
+              }
+           }
+        }
+      }
 
-   return ptr;
-}
+      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];
+      }
 
-/* These functions choose one of the ATTR's generated above (or from
- * codegen).  Like the ATTR functions, they live in the GL dispatch
- * table and in the second-level dispatch table for MultiTexCoord,
- * AttribNV, etc.
- *
- * Need ATTR1 for use in constructing name of 'attrib_x_y_z' function.
- */
-#define CHOOSE( ATTR1, ATTR2, SZ, V, FNTYPE, ARGS1, ARGS2 )    \
-static void choose_##ATTR2##_##SZ##_##V ARGS1                  \
-{                                                              \
-   void *ptr = lookup_or_generate(ATTR1, SZ, V,                        \
-                     (void *)attrib_##ATTR1##_##SZ##_##V );    \
-                                                               \
-   assert(ATTR1 == ATTR2);                                     \
-   ((FNTYPE) ptr) ARGS2;                                       \
 }
 
-#define afv (const GLfloat *v)
-#define a1f (GLfloat a)
-#define a2f (GLfloat a, GLfloat b)
-#define a3f (GLfloat a, GLfloat b, GLfloat c)
-#define a4f (GLfloat a, GLfloat b, GLfloat c, GLfloat d)
 
-/* Not that many entrypoints when it all boils down:
- */
-CHOOSE( 0, VERT_ATTRIB_POS, 2, 1, pfv, afv, (v) )
-CHOOSE( 0, VERT_ATTRIB_POS, 2, 0, p2f, a2f, (a,b) )
-CHOOSE( 0, VERT_ATTRIB_POS, 3, 1, pfv, afv, (v) )
-CHOOSE( 0, VERT_ATTRIB_POS, 3, 0, p3f, a3f, (a,b,c) )
-CHOOSE( 0, VERT_ATTRIB_POS, 4, 1, pfv, afv, (v) )
-CHOOSE( 0, VERT_ATTRIB_POS, 4, 0, p4f, a4f, (a,b,c,d) )
-CHOOSE( 2, VERT_ATTRIB_NORMAL, 3, 1, pfv, afv, (v) )
-CHOOSE( 2, VERT_ATTRIB_NORMAL, 3, 0, p3f, a3f, (a,b,c) )
-CHOOSE( 3, VERT_ATTRIB_COLOR0, 3, 1, pfv, afv, (v) )
-CHOOSE( 3, VERT_ATTRIB_COLOR0, 3, 0, p3f, a3f, (a,b,c) )
-CHOOSE( 3, VERT_ATTRIB_COLOR0, 4, 1, pfv, afv, (v) )
-CHOOSE( 3, VERT_ATTRIB_COLOR0, 4, 0, p4f, a4f, (a,b,c,d) )
-CHOOSE( 4, VERT_ATTRIB_COLOR1, 3, 1, pfv, afv, (v) )
-CHOOSE( 4, VERT_ATTRIB_COLOR1, 3, 0, p3f, a3f, (a,b,c) )
-CHOOSE( 5, VERT_ATTRIB_FOG, 1, 1, pfv, afv, (v) )
-CHOOSE( 5, VERT_ATTRIB_FOG, 1, 0, p1f, a1f, (a) )
-CHOOSE( 8, VERT_ATTRIB_TEX0, 1, 1, pfv, afv, (v) )
-CHOOSE( 8, VERT_ATTRIB_TEX0, 1, 0, p1f, a1f, (a) )
-CHOOSE( 8, VERT_ATTRIB_TEX0, 2, 1, pfv, afv, (v) )
-CHOOSE( 8, VERT_ATTRIB_TEX0, 2, 0, p2f, a2f, (a,b) )
-CHOOSE( 8, VERT_ATTRIB_TEX0, 3, 1, pfv, afv, (v) )
-CHOOSE( 8, VERT_ATTRIB_TEX0, 3, 0, p3f, a3f, (a,b,c) )
-CHOOSE( 8, VERT_ATTRIB_TEX0, 4, 1, pfv, afv, (v) )
-CHOOSE( 8, VERT_ATTRIB_TEX0, 4, 0, p4f, a4f, (a,b,c,d) )
-
-
-/* Gack.  Need to do this without going through the
- * GET_CURRENT_CONTEXT hoohah.  Could codegen this, I suppose...
- */
-#define DISPATCH_ATTRFV( ATTR, COUNT, P )      \
-do {                                           \
-   GET_CURRENT_CONTEXT( ctx );                         \
-   TNLcontext *tnl = TNL_CONTEXT(ctx);                 \
-   tnl->vtx.tabfv[COUNT-1][ATTR]( P );         \
-} while (0)
+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;
 
-#define DISPATCH_ATTR1FV( ATTR, V ) DISPATCH_ATTRFV( ATTR, 1, V )
-#define DISPATCH_ATTR2FV( ATTR, V ) DISPATCH_ATTRFV( ATTR, 2, V )
-#define DISPATCH_ATTR3FV( ATTR, V ) DISPATCH_ATTRFV( ATTR, 3, V )
-#define DISPATCH_ATTR4FV( ATTR, V ) DISPATCH_ATTRFV( ATTR, 4, V )
+   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 (tnl->vtx.attrsz[attr] > sz) {
+      /* 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];
+   }
+
+   /* 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;
+}
 
-#define DISPATCH_ATTR1F( ATTR, S ) DISPATCH_ATTRFV( ATTR, 1, &(S) )
 #ifdef USE_X86_ASM
-/* Naughty cheat:
- */
-#define DISPATCH_ATTR2F( ATTR, S,T ) DISPATCH_ATTRFV( ATTR, 2, &(S) )
-#define DISPATCH_ATTR3F( ATTR, S,T,R ) DISPATCH_ATTRFV( ATTR, 3, &(S) )
-#define DISPATCH_ATTR4F( ATTR, S,T,R,Q ) DISPATCH_ATTRFV( ATTR, 4, &(S) )
-#else
-/* Safe:
- */
-#define DISPATCH_ATTR2F( ATTR, S,T )           \
-do {                                           \
-   GLfloat v[2];                               \
-   v[0] = S; v[1] = T;                         \
-   DISPATCH_ATTR2FV( ATTR, v );                        \
-} while (0)
-#define DISPATCH_ATTR3F( ATTR, S,T,R )                 \
-do {                                           \
-   GLfloat v[3];                               \
-   v[0] = S; v[1] = T; v[2] = R;               \
-   DISPATCH_ATTR3FV( ATTR, v );                        \
-} while (0)
-#define DISPATCH_ATTR4F( ATTR, S,T,R,Q )       \
-do {                                           \
-   GLfloat v[4];                               \
-   v[0] = S; v[1] = T; v[2] = R; v[3] = Q;     \
-   DISPATCH_ATTR4FV( ATTR, v );                        \
-} while (0)
-#endif
 
+static struct _tnl_dynfn *lookup( struct _tnl_dynfn *l, GLuint key )
+{
+   struct _tnl_dynfn *f;
 
+   foreach( f, l ) {
+      if (f->key == key) 
+        return f;
+   }
 
-static void enum_error( void )
-{
-   GET_CURRENT_CONTEXT( ctx );
-   _mesa_error( ctx, GL_INVALID_ENUM, __FUNCTION__ );
+   return NULL;
 }
 
-static void op_error( void )
+
+static tnl_attrfv_func do_codegen( GLcontext *ctx, GLuint attr, GLuint sz )
 {
-   GET_CURRENT_CONTEXT( ctx );
-   _mesa_error( ctx, GL_INVALID_OPERATION, __FUNCTION__ );
-}
+   TNLcontext *tnl = TNL_CONTEXT(ctx); 
+   struct _tnl_dynfn *dfn = NULL;
 
+   if (attr == 0) {
+      GLuint key = tnl->vtx.vertex_size;
 
-/* First level for MultiTexcoord:  Send through second level dispatch.
- * These are permanently installed in the toplevel dispatch.
- *
- * Assembly can optimize the generation of arrays by using &s instead
- * of building 'v'.
- */
-static void _tnl_MultiTexCoord1f( GLenum target, GLfloat s  )
-{
-   GLuint attr = (target & 0x7) + VERT_ATTRIB_TEX0;
-   DISPATCH_ATTR1FV( attr, &s );
-}
+      dfn = lookup( &tnl->vtx.cache.Vertex[sz-1], key );
 
-static void _tnl_MultiTexCoord1fv( GLenum target, const GLfloat *v )
-{
-   GLuint attr = (target & 0x7) + VERT_ATTRIB_TEX0;
-   DISPATCH_ATTR1FV( attr, v );
-}
+      if (!dfn)
+        dfn = tnl->vtx.gen.Vertex[sz-1]( ctx, key );
+   }
+   else {
+      GLuint key = (GLuint) tnl->vtx.attrptr[attr];
 
-static void _tnl_MultiTexCoord2f( GLenum target, GLfloat s, GLfloat t )
-{
-   GLuint attr = (target & 0x7) + VERT_ATTRIB_TEX0;
-   DISPATCH_ATTR2F( attr, s, t );
-}
+      dfn = lookup( &tnl->vtx.cache.Attribute[sz-1], key );
 
-static void _tnl_MultiTexCoord2fv( GLenum target, const GLfloat *v )
-{
-   GLuint attr = (target & 0x7) + VERT_ATTRIB_TEX0;
-   DISPATCH_ATTR2FV( attr, v );
-}
+      if (!dfn)
+        dfn = tnl->vtx.gen.Attribute[sz-1]( ctx, key );
+   }
 
-static void _tnl_MultiTexCoord3f( GLenum target, GLfloat s, GLfloat t,
-                                   GLfloat r)
-{
-   GLuint attr = (target & 0x7) + VERT_ATTRIB_TEX0;
-   DISPATCH_ATTR3F( attr, s, t, r );
+   if (dfn) 
+      return *(tnl_attrfv_func *) &dfn->code;
+   else
+      return NULL;
 }
 
-static void _tnl_MultiTexCoord3fv( GLenum target, const GLfloat *v )
-{
-   GLuint attr = (target & 0x7) + VERT_ATTRIB_TEX0;
-   DISPATCH_ATTR3FV( attr, v );
-}
+#endif /* USE_X86_ASM */
 
-static void _tnl_MultiTexCoord4f( GLenum target, GLfloat s, GLfloat t,
-                                   GLfloat r, GLfloat q )
-{
-   GLuint attr = (target & 0x7) + VERT_ATTRIB_TEX0;
-   DISPATCH_ATTR4F( attr, s, t, r, q );
-}
+/* Helper function for 'CHOOSE' macro.  Do what's necessary when an
+ * entrypoint is called for the first time.
+ */
 
-static void _tnl_MultiTexCoord4fv( GLenum target, const GLfloat *v )
-{
-   GLuint attr = (target & 0x7) + VERT_ATTRIB_TEX0;
-   DISPATCH_ATTR4FV( attr, v );
-}
+static tnl_attrfv_func do_choose( GLuint attr, GLuint sz )
+{ 
+   GET_CURRENT_CONTEXT( ctx ); 
+   TNLcontext *tnl = TNL_CONTEXT(ctx); 
+   GLuint oldsz = tnl->vtx.attrsz[attr];
 
+   assert(attr < _TNL_MAX_ATTR_CODEGEN);
 
-/* First level for NV_vertex_program:
- *
- * Check for errors & reroute through second dispatch layer to get
- * size tracking per-attribute.
- */
-static void _tnl_VertexAttrib1fNV( GLuint index, GLfloat s )
-{
-   if (index < TNL_ATTRIB_MAX)
-      DISPATCH_ATTR1F( index, s );
-   else
-      enum_error(); 
-}
+   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 );
+   }
 
-static void _tnl_VertexAttrib1fvNV( GLuint index, const GLfloat *v )
-{
-   if (index < TNL_ATTRIB_MAX)
-      DISPATCH_ATTR1FV( index, v );
-   else
-      enum_error();
-}
 
-static void _tnl_VertexAttrib2fNV( GLuint index, GLfloat s, GLfloat t )
-{
-   if (index < TNL_ATTRIB_MAX)
-      DISPATCH_ATTR2F( index, s, t );
+   /* Try to use codegen:
+    */
+#ifdef USE_X86_ASM
+   if (tnl->AllowCodegen)
+      tnl->vtx.tabfv[attr][sz-1] = do_codegen( ctx, attr, sz );
    else
-      enum_error();
-}
+#endif
+      tnl->vtx.tabfv[attr][sz-1] = NULL;
 
-static void _tnl_VertexAttrib2fvNV( GLuint index, const GLfloat *v )
-{
-   if (index < TNL_ATTRIB_MAX)
-      DISPATCH_ATTR2FV( index, v );
-   else
-      enum_error();
-}
+   /* Else use generic version:
+    */
+   if (!tnl->vtx.tabfv[attr][sz-1])
+      tnl->vtx.tabfv[attr][sz-1] = generic_attr_func[attr][sz-1];
 
-static void _tnl_VertexAttrib3fNV( GLuint index, GLfloat s, GLfloat t, 
-                                 GLfloat r )
-{
-   if (index < TNL_ATTRIB_MAX)
-      DISPATCH_ATTR3F( index, s, t, r );
-   else
-      enum_error();
+   return tnl->vtx.tabfv[attr][sz-1];
 }
 
-static void _tnl_VertexAttrib3fvNV( GLuint index, const GLfloat *v )
-{
-   if (index < TNL_ATTRIB_MAX)
-      DISPATCH_ATTR3FV( index, v );
-   else
-      enum_error();
-}
 
-static void _tnl_VertexAttrib4fNV( GLuint index, GLfloat s, GLfloat t,
-                                 GLfloat r, GLfloat q )
-{
-   if (index < TNL_ATTRIB_MAX)
-      DISPATCH_ATTR4F( index, s, t, r, q );
-   else
-      enum_error();
-}
 
-static void _tnl_VertexAttrib4fvNV( GLuint index, const GLfloat *v )
+#define CHOOSE( ATTR, N )                              \
+static void choose_##ATTR##_##N( const GLfloat *v )    \
+{                                                      \
+   tnl_attrfv_func f = do_choose(ATTR, N);                     \
+   f( v );                                             \
+}
+
+#define CHOOSERS( ATTRIB ) \
+   CHOOSE( ATTRIB, 1 )                         \
+   CHOOSE( ATTRIB, 2 )                         \
+   CHOOSE( ATTRIB, 3 )                         \
+   CHOOSE( ATTRIB, 4 )                         \
+
+
+#define INIT_CHOOSERS(ATTR)                            \
+   choose[ATTR][0] = choose_##ATTR##_1;                                \
+   choose[ATTR][1] = choose_##ATTR##_2;                                \
+   choose[ATTR][2] = choose_##ATTR##_3;                                \
+   choose[ATTR][3] = choose_##ATTR##_4;
+
+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 )
+
+static void error_attrib( const GLfloat *unused )
 {
-   if (index < TNL_ATTRIB_MAX)
-      DISPATCH_ATTR4FV( index, v );
-   else
-      enum_error();
+   GET_CURRENT_CONTEXT( ctx );
+   (void) unused;
+   _mesa_error( ctx, GL_INVALID_ENUM, "glVertexAttrib" );
+}   
+
+
+
+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;
+
+        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:  
@@ -549,350 +519,458 @@ static void _tnl_VertexAttrib4fvNV( GLuint index, const GLfloat *v )
  * 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 MAT_ATTR( A, N, params )                       \
+#define OTHER_ATTR( A, N, params )             \
 do {                                                   \
-   if (tnl->vtx.attrib_sz[A] != N) {                   \
-      tnl_fixup_vertex( ctx, A, N );                   \
+   if (tnl->vtx.attrsz[A] != N) {                      \
+      _tnl_fixup_vertex( ctx, A, N );                  \
    }                                                   \
                                                        \
    {                                                   \
-      union uif *dest = tnl->vtx.attrptr[A];           \
-      if (N>0) dest[0].f = params[0];                  \
-      if (N>1) dest[1].f = params[1];                  \
-      if (N>2) dest[2].f = params[2];                  \
-      if (N>3) dest[3].f = params[3];                  \
-      ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT;   \
+      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)                                        \
-      MAT_ATTR( ATTR, N, params ); /* front */         \
-   if (face != GL_FRONT)                               \
-      MAT_ATTR( ATTR + 1, N, params ); /* back */      \
+#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)
 
 
-/* NOTE: Have to remove/deal-with colormaterial crossovers, probably
- * later on - in the meantime just store everything.  
+/* Colormaterial is dealt with later on.
  */
-static void _tnl_Materialfv( GLenum face, GLenum pname, 
+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( VERT_ATTRIB_MAT_FRONT_EMISSION, 4, face, params );
+      MAT( _TNL_ATTRIB_MAT_FRONT_EMISSION, 4, face, params );
       break;
    case GL_AMBIENT:
-      MAT( VERT_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params );
+      MAT( _TNL_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params );
       break;
    case GL_DIFFUSE:
-      MAT( VERT_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params );
+      MAT( _TNL_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params );
       break;
    case GL_SPECULAR:
-      MAT( VERT_ATTRIB_MAT_FRONT_SPECULAR, 4, face, params );
+      MAT( _TNL_ATTRIB_MAT_FRONT_SPECULAR, 4, face, params );
       break;
    case GL_SHININESS:
-      MAT( VERT_ATTRIB_MAT_FRONT_SHININESS, 1, face, params );
+      MAT( _TNL_ATTRIB_MAT_FRONT_SHININESS, 1, face, params );
       break;
    case GL_COLOR_INDEXES:
-      MAT( VERT_ATTRIB_MAT_FRONT_INDEXES, 3, face, params );
+      MAT( _TNL_ATTRIB_MAT_FRONT_INDEXES, 3, face, params );
       break;
    case GL_AMBIENT_AND_DIFFUSE:
-      MAT( VERT_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params );
-      MAT( VERT_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params );
+      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, __FUNCTION__ );
+      _mesa_error( ctx, GL_INVALID_ENUM, "glMaterialfv" );
       return;
    }
-}
-
 
-#define IDX_ATTR( A, IDX )                             \
-do {                                                   \
-   GET_CURRENT_CONTEXT( ctx );                         \
-   TNLcontext *tnl = TNL_CONTEXT(ctx);                 \
-                                                       \
-   if (tnl->vtx.attrib_sz[A] != 1) {                   \
-      tnl_fixup_vertex( ctx, A, 1 );                   \
-   }                                                   \
-                                                       \
-   {                                                   \
-      union uif *dest = tnl->vtx.attrptr[A];           \
-      dest[0].ui = IDX;                                \
-      ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT;   \
-   }                                                   \
-} while (0)
+   tnl->vtx.have_materials = GL_TRUE;
+}
 
 
-static void _tnl_EdgeFlag( GLboolean f )
+static void GLAPIENTRY _tnl_EdgeFlag( GLboolean b )
 {
-   IDX_ATTR( VERT_ATTRIB_EDGEFLAG, f );
-}
+   GET_CURRENT_CONTEXT( ctx ); 
+   TNLcontext *tnl = TNL_CONTEXT(ctx);
+   GLfloat f = (GLfloat)b;
 
-static void _tnl_EdgeFlagv( const GLboolean *f )
-{
-   IDX_ATTR( VERT_ATTRIB_EDGEFLAG, f[0] );
+   OTHER_ATTR( _TNL_ATTRIB_EDGEFLAG, 1, &f );
 }
 
-static void _tnl_Indexi( GLint i )
+static void GLAPIENTRY _tnl_EdgeFlagv( const GLboolean *v )
 {
-   IDX_ATTR( VERT_ATTRIB_INDEX, i );
+   GET_CURRENT_CONTEXT( ctx ); 
+   TNLcontext *tnl = TNL_CONTEXT(ctx);
+   GLfloat f = (GLfloat)v[0];
+
+   OTHER_ATTR( _TNL_ATTRIB_EDGEFLAG, 1, &f );
 }
 
-static void _tnl_Indexiv( const GLint *i )
+static void GLAPIENTRY _tnl_Indexf( GLfloat f )
 {
-   IDX_ATTR( VERT_ATTRIB_INDEX, i[0] );
+   GET_CURRENT_CONTEXT( ctx ); 
+   TNLcontext *tnl = TNL_CONTEXT(ctx);
+
+   OTHER_ATTR( _TNL_ATTRIB_INDEX, 1, &f );
 }
 
+static void GLAPIENTRY _tnl_Indexfv( const GLfloat *v )
+{
+   GET_CURRENT_CONTEXT( ctx ); 
+   TNLcontext *tnl = TNL_CONTEXT(ctx);
 
+   OTHER_ATTR( _TNL_ATTRIB_INDEX, 1, v );
+}
 
-/* EvalCoord needs special treatment as ususal:
+/* Eval
  */
-static void evalcoord( GLfloat a, GLfloat b, GLuint type ) 
+static void GLAPIENTRY _tnl_EvalCoord1f( GLfloat u )
 {
-#if 0
-   GET_CURRENT_CONTEXT( ctx );                                 
-   TNLcontext *tnl = TNL_CONTEXT(ctx);                         
-   
-   /* Initialize the list of eval fixups:
-    */
-   if (!tnl->evalptr) {
-      init_eval_ptr( ctx );
+   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_ATTRIB_INDEX ; i++) {
+        if (tnl->vtx.eval.map1[i].map) 
+           if (tnl->vtx.attrsz[i] != tnl->vtx.eval.map1[i].sz)
+              _tnl_fixup_vertex( ctx, i, tnl->vtx.eval.map1[i].sz );
+      }
    }
 
-   /* Note that this vertex will need to be fixed up:
-    */
-   tnl->evalptr[0].vert = tnl->initial_counter - tnl->counter;
-   tnl->evalptr[0].type = type;
 
-   /* Now emit the vertex with eval data in obj coordinates:
-    */
-   ATTRF( 0, 2, a, b, 0, 1 );
-#endif
-}
+   _mesa_memcpy( tnl->vtx.copied.buffer, tnl->vtx.vertex, 
+                 tnl->vtx.vertex_size * sizeof(GLfloat));
 
+   _tnl_do_EvalCoord1f( ctx, u );
 
-static void _tnl_EvalCoord1f( GLfloat u )
-{
-   evalcoord( u, 0, TNL_EVAL_COORD1 );
+   _mesa_memcpy( tnl->vtx.vertex, tnl->vtx.copied.buffer,
+                 tnl->vtx.vertex_size * sizeof(GLfloat));
 }
 
-static void _tnl_EvalCoord1fv( const GLfloat *v )
+static void GLAPIENTRY _tnl_EvalCoord2f( GLfloat u, GLfloat v )
 {
-   evalcoord( v[0], 0, TNL_EVAL_COORD1 );
+   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_ATTRIB_INDEX ; i++) {
+        if (tnl->vtx.eval.map2[i].map) 
+           if (tnl->vtx.attrsz[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.attrsz[_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 _tnl_EvalCoord2f( GLfloat u, GLfloat v )
+static void GLAPIENTRY _tnl_EvalCoord1fv( const GLfloat *u )
 {
-   evalcoord( u, v, TNL_EVAL_COORD2 );
+   _tnl_EvalCoord1f( u[0] );
 }
 
-static void _tnl_EvalCoord2fv( const GLfloat *v )
+static void GLAPIENTRY _tnl_EvalCoord2fv( const GLfloat *u )
 {
-   evalcoord( v[0], v[1], TNL_EVAL_COORD2 );
+   _tnl_EvalCoord2f( u[0], u[1] );
 }
 
-static void _tnl_EvalPoint1( GLint i )
+static void GLAPIENTRY _tnl_EvalPoint1( GLint i )
 {
-   evalcoord( (GLfloat)i, 0, TNL_EVAL_POINT1 );
+   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 _tnl_EvalPoint2( GLint i, GLint j )
+
+static void GLAPIENTRY _tnl_EvalPoint2( GLint i, GLint j )
 {
-   evalcoord( (GLfloat)i, (GLfloat)j, TNL_EVAL_POINT2 );
+   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 );
 }
 
-/* Don't do a lot of processing here - errors are raised when this
- * list is scanned later on (perhaps in display list playback) to
- * build tnl_prim structs.
+
+/* Build a list of primitives on the fly.  Keep
+ * ctx->Driver.CurrentExecPrimitive uptodate as well.
  */
-static void _tnl_Begin( GLenum mode )
+static void GLAPIENTRY _tnl_Begin( GLenum mode )
 {
    GET_CURRENT_CONTEXT( ctx ); 
-   TNLcontext *tnl = TNL_CONTEXT(ctx); 
-   int i;
 
-   i = tnl->vtx.be_count++;
-   tnl->vtx.be[i].type = TNL_BEGIN;
-   tnl->vtx.be[i].idx = tnl->vtx.initial_counter - tnl->vtx.counter;
-   tnl->vtx.be[i].mode = mode;
+   if (ctx->Driver.CurrentExecPrimitive == GL_POLYGON+1) {
+      TNLcontext *tnl = TNL_CONTEXT(ctx); 
+      int i;
 
-   if (tnl->vtx.be_count == TNL_BE_MAX)
-      _tnl_FlushVertices( ctx, FLUSH_STORED_VERTICES );        
+      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)");
+            return;
+         }
+
+        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 
+      _mesa_error( ctx, GL_INVALID_OPERATION, "glBegin" );
+      
 }
 
-static void _tnl_End( void )
+static void GLAPIENTRY _tnl_End( void )
 {
    GET_CURRENT_CONTEXT( ctx ); 
-   TNLcontext *tnl = TNL_CONTEXT(ctx); 
-   int i;
 
-   i = tnl->vtx.be_count++;
-   tnl->vtx.be[i].type = TNL_END;
-   tnl->vtx.be[i].idx = tnl->vtx.initial_counter - tnl->vtx.counter;
-   tnl->vtx.be[i].mode = 0;
+   if (ctx->Driver.CurrentExecPrimitive != GL_POLYGON+1) {
+      TNLcontext *tnl = TNL_CONTEXT(ctx); 
+      int idx = tnl->vtx.initial_counter - tnl->vtx.counter;
+      int i = tnl->vtx.prim_count - 1;
 
-   if (tnl->vtx.be_count == TNL_BE_MAX)
-      _tnl_FlushVertices( ctx, FLUSH_STORED_VERTICES );        
+      tnl->vtx.prim[i].mode |= PRIM_END; 
+      tnl->vtx.prim[i].count = idx - tnl->vtx.prim[i].start;
+
+      ctx->Driver.CurrentExecPrimitive = GL_POLYGON+1;
+
+      /* 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 
+      _mesa_error( ctx, GL_INVALID_OPERATION, "glEnd" );
 }
 
 
+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->EdgeFlagv = _tnl_EdgeFlagv;
+   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->Indexf = _tnl_Indexf;
+   vfmt->Indexfv = _tnl_Indexfv;
+   vfmt->Materialfv = _tnl_Materialfv;
 
+   vfmt->Rectf = _mesa_noop_Rectf;
+   vfmt->EvalMesh1 = _mesa_noop_EvalMesh1;
+   vfmt->EvalMesh2 = _mesa_noop_EvalMesh2;
+}
 
 
 
-static void _tnl_InitDispatch( struct _glapi_table *tab )
+void _tnl_FlushVertices( GLcontext *ctx, GLuint flags )
 {
-   GLint i;
-   
-   /* Most operations boil down to error/transition behaviour.
-    * However if we transition eagerly, all that's needed is a single
-    * 'error' operation.  This will do for now, but requires that the
-    * old 'flush' stuff lives on in the state functions, and is
-    * wasteful if swapping is expensive (threads?).
-    */
-   for (i = 0 ; i < sizeof(tab)/sizeof(void*) ; i++) 
-      ((void **)tab)[i] = (void *)op_error;
-
-   tab->Begin = _tnl_Begin;
-   tab->End = _tnl_End;
-   tab->Color3f = choose_VERT_ATTRIB_COLOR0_3_0;
-   tab->Color3fv = choose_VERT_ATTRIB_COLOR0_3_1;
-   tab->Color4f = choose_VERT_ATTRIB_COLOR0_4_0;
-   tab->Color4fv = choose_VERT_ATTRIB_COLOR0_4_1;
-   tab->SecondaryColor3fEXT = choose_VERT_ATTRIB_COLOR1_3_0;
-   tab->SecondaryColor3fvEXT = choose_VERT_ATTRIB_COLOR1_3_1;
-   tab->MultiTexCoord1fARB = _tnl_MultiTexCoord1f;
-   tab->MultiTexCoord1fvARB = _tnl_MultiTexCoord1fv;
-   tab->MultiTexCoord2fARB = _tnl_MultiTexCoord2f;
-   tab->MultiTexCoord2fvARB = _tnl_MultiTexCoord2fv;
-   tab->MultiTexCoord3fARB = _tnl_MultiTexCoord3f;
-   tab->MultiTexCoord3fvARB = _tnl_MultiTexCoord3fv;
-   tab->MultiTexCoord4fARB = _tnl_MultiTexCoord4f;
-   tab->MultiTexCoord4fvARB = _tnl_MultiTexCoord4fv;
-   tab->Normal3f = choose_VERT_ATTRIB_NORMAL_3_0;
-   tab->Normal3fv = choose_VERT_ATTRIB_NORMAL_3_1;
-   tab->TexCoord1f = choose_VERT_ATTRIB_TEX0_1_0;
-   tab->TexCoord1fv = choose_VERT_ATTRIB_TEX0_1_1;
-   tab->TexCoord2f = choose_VERT_ATTRIB_TEX0_2_0;
-   tab->TexCoord2fv = choose_VERT_ATTRIB_TEX0_2_1;
-   tab->TexCoord3f = choose_VERT_ATTRIB_TEX0_3_0;
-   tab->TexCoord3fv = choose_VERT_ATTRIB_TEX0_3_1;
-   tab->TexCoord4f = choose_VERT_ATTRIB_TEX0_4_0;
-   tab->TexCoord4fv = choose_VERT_ATTRIB_TEX0_4_1;
-   tab->Vertex2f = choose_VERT_ATTRIB_POS_2_0;
-   tab->Vertex2fv = choose_VERT_ATTRIB_POS_2_1;
-   tab->Vertex3f = choose_VERT_ATTRIB_POS_3_0;
-   tab->Vertex3fv = choose_VERT_ATTRIB_POS_3_1;
-   tab->Vertex4f = choose_VERT_ATTRIB_POS_4_0;
-   tab->Vertex4fv = choose_VERT_ATTRIB_POS_4_1;
-   tab->FogCoordfEXT = choose_VERT_ATTRIB_FOG_1_0;
-   tab->FogCoordfvEXT = choose_VERT_ATTRIB_FOG_1_1;
-   tab->EdgeFlag = _tnl_EdgeFlag;
-   tab->EdgeFlagv = _tnl_EdgeFlagv;
-   tab->Indexi = _tnl_Indexi;
-   tab->Indexiv = _tnl_Indexiv;
-   tab->EvalCoord1f = _tnl_EvalCoord1f;
-   tab->EvalCoord1fv = _tnl_EvalCoord1fv;
-   tab->EvalCoord2f = _tnl_EvalCoord2f;
-   tab->EvalCoord2fv = _tnl_EvalCoord2fv;
-   tab->Materialfv = _tnl_Materialfv;
-   tab->ArrayElement = _ae_loopback_array_elt;         /* generic helper */
+   TNLcontext *tnl = TNL_CONTEXT(ctx);
+   (void) flags;
+
+   if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END)
+      return;
+
+   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;
 }
 
 
+static void _tnl_current_init( GLcontext *ctx ) 
+{
+   TNLcontext *tnl = TNL_CONTEXT(ctx);
+   GLint i;
+
+   /* setup the pointers for the typical 16 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];
 
-static struct dynfn *codegen_noop( GLcontext *ctx, int key )
+   tnl->vtx.current[_TNL_ATTRIB_INDEX] = &ctx->Current.Index;
+   tnl->vtx.current[_TNL_ATTRIB_EDGEFLAG] = &tnl->vtx.CurrentFloatEdgeFlag;
+}
+
+static struct _tnl_dynfn *no_codegen( GLcontext *ctx, int key )
 {
    (void) ctx; (void) key;
-   return 0;
+   return NULL;
 }
 
-static void _tnl_InitCodegen( GLcontext *ctx )
+void _tnl_vtx_init( GLcontext *ctx )
 {
-   TNLcontext *tnl = TNL_CONTEXT(ctx);                         
-   int sz, v, z;
+   TNLcontext *tnl = TNL_CONTEXT(ctx); 
+   struct tnl_vertex_arrays *tmp = &tnl->vtx_inputs;
+   GLuint i;
+   static int firsttime = 1;
+   
+   if (firsttime) {
+      firsttime = 0;
+
+      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 );
+
+      choose[ERROR_ATTRIB][0] = error_attrib;
+      choose[ERROR_ATTRIB][1] = error_attrib;
+      choose[ERROR_ATTRIB][2] = error_attrib;
+      choose[ERROR_ATTRIB][3] = error_attrib;
 
-   /* attr[n][v] 
-    * vertex[n][v]
-    *
-    * Generated functions parameterized by:
-    *    nr     1..4
-    *    v      y/n
-    *    vertex y/n
-    *
-    * Vertex functions also parameterized by:
-    *    vertex_size
-    *  
-    * Attr functions also parameterized by:
-    *    pointer   (destination to receive data)
-    */
-   for (sz = 1 ; sz < 5 ; sz++) {
-      for (v = 0 ; v < 2 ; v++) {
-        for (z = 0 ; z < 2 ; z++) {
-           tnl->vtx.codegen[sz-1][v][z] = codegen_noop;
-           tnl->vtx.generated[sz-1][v][z] = 0;
-        }
+#ifdef USE_X86_ASM
+      if (tnl->AllowCodegen) {
+         _tnl_x86choosers(choose, do_choose); /* x86 INIT_CHOOSERS */
       }
+#endif
+
+      _tnl_generic_attr_table_init( generic_attr_func );
    }
 
-#if 0
-   if (!getenv("MESA_NO_CODEGEN")) {
-#if defined(USE_X86_ASM)
-      _tnl_InitX86Codegen( gen );
-#endif
+   for (i = 0; i < _TNL_ATTRIB_INDEX; i++)
+      _mesa_vector4f_init( &tmp->Attribs[i], 0, NULL);
 
-#if defined(USE_SSE_ASM)
-      _tnl_InitSSECodegen( gen );
-#endif
+   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;
+   }
 
-#if defined(USE_3DNOW_ASM)
+#ifdef USE_X86_ASM
+   _tnl_InitX86Codegen( &tnl->vtx.gen );
 #endif
 
-#if defined(USE_SPARC_ASM)
-#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 );
 
+   memcpy( tnl->vtx.tabfv, choose, sizeof(choose) );
 
+   for (i = 0 ; i < _TNL_ATTRIB_MAX ; i++) 
+      tnl->vtx.attrsz[i] = 0;
 
-void _tnl_vtx_init( GLcontext *ctx )
-{
-   TNLcontext *tnl = TNL_CONTEXT(ctx); 
-   _tnl_InitDispatch( tnl->Exec );
-   _tnl_InitCodegen( ctx );
+   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);                         
-   int sz, v, z;
-   struct dynfn *dfn, *next;
-
-   for (sz = 1 ; sz <= 4 ; sz++) {
-      for (v = 0 ; v <= 1 ; v++) {
-        for (z = 0 ; z <= 1 ; z++) {
-           dfn = tnl->vtx.generated[sz-1][v][z];
-           while (dfn) {
-              next = dfn->next;
-              FREE(dfn);
-              dfn = next;
-           }
-        }
-      }
+   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] ); 
    }
 }