Header file clean-up:
[mesa.git] / src / mesa / tnl / t_imm_dlist.c
index 33d3149ed0a5d77d9ffe9f00d047f19b78041725..34da0ea448a0df512ec73a197cbdfc6973dc191a 100644 (file)
@@ -1,21 +1,21 @@
-/* $Id: t_imm_dlist.c,v 1.10 2001/03/03 20:33:31 brianp Exp $ */
+/* $Id: t_imm_dlist.c,v 1.43 2002/10/24 23:57:25 brianp Exp $ */
 
 /*
  * Mesa 3-D graphics library
  * Version:  3.5
- * 
- * Copyright (C) 1999  Brian Paul   All Rights Reserved.
- * 
+ *
+ * Copyright (C) 1999-2001  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
@@ -23,8 +23,8 @@
  * 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 <keithw@valinux.com>
+ * Authors:
+ *    Keith Whitwell <keithw@valinux.com>
  */
 
 
@@ -33,7 +33,7 @@
 #include "dlist.h"
 #include "debug.h"
 #include "mmath.h"
-#include "mem.h"
+#include "imports.h"
 #include "state.h"
 
 #include "t_context.h"
@@ -59,15 +59,68 @@ typedef struct {
    GLuint LastPrimitive;
    GLuint LastMaterial;
    GLuint MaterialOrMask;
+   GLuint MaterialAndMask;
 } TNLvertexcassette;
 
 static void execute_compiled_cassette( GLcontext *ctx, void *data );
+static void loopback_compiled_cassette( GLcontext *ctx, struct immediate *IM );
+
+
+static void build_normal_lengths( struct immediate *IM )
+{
+   GLuint i;
+   GLfloat len;
+   GLfloat (*data)[4] = IM->Attrib[VERT_ATTRIB_NORMAL] + IM->Start;
+   GLfloat *dest = IM->NormalLengthPtr;
+   GLuint *flags = IM->Flag + IM->Start;
+   GLuint count = IM->Count - IM->Start;
+
+   if (!dest) {
+      dest = IM->NormalLengthPtr = (GLfloat *) ALIGN_MALLOC( IMM_SIZE*sizeof(GLfloat), 32 );
+      if (!dest) return;
+   }
+   dest += IM->Start;
+
+   len = (GLfloat) LEN_3FV( data[0] );
+   if (len > 0.0F) len = 1.0F / len;
+   
+   for (i = 0 ; i < count ; ) {
+      dest[i] = len;
+      if (flags[++i] & VERT_BIT_NORMAL) {
+        len = (GLfloat) LEN_3FV( data[i] );
+        if (len > 0.0F) len = 1.0F / len;
+      }
+   } 
+}
+
+static void fixup_normal_lengths( struct immediate *IM ) 
+{
+   GLuint i;
+   GLfloat len = 1.0F;  /* just to silence warnings */
+   GLfloat (*data)[4] = IM->Attrib[VERT_ATTRIB_NORMAL];
+   GLfloat *dest = IM->NormalLengthPtr;
+   GLuint *flags = IM->Flag;
+
+   for (i = IM->CopyStart ; i <= IM->Start ; i++) {
+      len = (GLfloat) LEN_3FV( data[i] );
+      if (len > 0.0F) len = 1.0F / len;
+      dest[i] = len;
+   } 
+
+   if (i < IM->Count) {
+      while (!(flags[i] & (VERT_BIT_NORMAL|VERT_BIT_END_VB))) {
+        dest[i] = len;
+        i++;
+      }
+   }
+}
+
 
 
 /* Insert the active immediate struct onto the display list currently
  * being built.
  */
-void 
+void
 _tnl_compile_cassette( GLcontext *ctx, struct immediate *IM )
 {
    struct immediate *im = TNL_CURRENT_IM(ctx);
@@ -75,43 +128,32 @@ _tnl_compile_cassette( GLcontext *ctx, struct immediate *IM )
    TNLvertexcassette *node;
    GLuint new_beginstate;
 
+   if (MESA_VERBOSE & VERBOSE_DISPLAY_LIST)
+      _mesa_debug(ctx, "_tnl_compiled_cassette IM: %d\n", IM->id); 
 
-   _tnl_compute_orflag( IM );
-
-   IM->CopyStart = IM->Start;
-
-   if (IM->OrFlag & VERT_ELT) {
-      GLuint andflag = ~0;
-      GLuint i;
-      GLuint start = IM->FlushElt ? IM->LastPrimitive : IM->CopyStart;
-      _tnl_translate_array_elts( ctx, IM, start, IM->Count ); 
-
-      /* Need to recompute andflag.
-       */
-      if (IM->AndFlag & VERT_ELT)
-        IM->CopyAndFlag = IM->AndFlag |= ctx->Array._Enabled;
-      else {
-        for (i = IM->CopyStart ; i < IM->Count ; i++)
-           andflag &= IM->Flag[i];
-        IM->CopyAndFlag = IM->AndFlag = andflag;
-      }
+   if (IM->FlushElt) {
+      ASSERT (IM->FlushElt == FLUSH_ELT_LAZY); 
+      _tnl_translate_array_elts( ctx, IM, IM->Start, IM->Count );
    }
 
-   _tnl_fixup_input( ctx, IM );
+   _tnl_compute_orflag( IM, IM->Start );
 
-   /* Mark the last primitive:
+   /* Need to clear this flag, or fixup gets confused.  (The
+    * array-elements have been translated away by now, so it's ok to
+    * remove it.)
     */
-   IM->PrimitiveLength[IM->LastPrimitive] = IM->Count - IM->LastPrimitive;
-   ASSERT(IM->Primitive[IM->LastPrimitive] & PRIM_LAST);
+   IM->OrFlag &= ~VERT_BIT_ELT;        
+   IM->AndFlag &= ~VERT_BIT_ELT;       
 
-   
-   node = (TNLvertexcassette *) 
-      _mesa_alloc_instruction(ctx, 
+   _tnl_fixup_input( ctx, IM );
+
+   node = (TNLvertexcassette *)
+      _mesa_alloc_instruction(ctx,
                              tnl->opcode_vertex_cassette,
                              sizeof(TNLvertexcassette));
-   if (!node) 
+   if (!node)
       return;
-   
+
    node->IM = im; im->ref_count++;
    node->Start = im->Start;
    node->Count = im->Count;
@@ -124,12 +166,16 @@ _tnl_compile_cassette( GLcontext *ctx, struct immediate *IM )
    node->LastPrimitive = im->LastPrimitive;
    node->LastMaterial = im->LastMaterial;
    node->MaterialOrMask = im->MaterialOrMask;
+   node->MaterialAndMask = im->MaterialAndMask;
+   
+   if (tnl->CalcDListNormalLengths) {
+      build_normal_lengths( im );
+   }
 
    if (ctx->ExecuteFlag) {
       execute_compiled_cassette( ctx, (void *)node );
    }
-        
-   
+
    /* Discard any errors raised in the last cassette.
     */
    new_beginstate = node->BeginState & (VERT_BEGIN_0|VERT_BEGIN_1);
@@ -140,65 +186,146 @@ _tnl_compile_cassette( GLcontext *ctx, struct immediate *IM )
    if (im->Count > IMM_MAXDATA - 16) {
       /* Call it full...
        */
-      struct immediate *new_im = _tnl_alloc_immediate(ctx);      
-      if (!new_im) return;
+      struct immediate *new_im = _tnl_alloc_immediate(ctx);
       new_im->ref_count++;
-      im->ref_count--;         /* remove CURRENT_IM reference */
-      ASSERT(im->ref_count > 0);
+      im->ref_count--;          /* remove CURRENT_IM reference */
+      ASSERT(im->ref_count > 0); /* it is compiled into a display list */
       SET_IMMEDIATE( ctx, new_im );
-      _tnl_reset_input( ctx, IMM_MAX_COPIED_VERTS,
-                       new_beginstate, node->SavedBeginState );
+      _tnl_reset_compile_input( ctx, IMM_MAX_COPIED_VERTS,
+                               new_beginstate, node->SavedBeginState );
    } else {
       /* Still some room in the current immediate.
        */
-      _tnl_reset_input( ctx, im->Count+1+IMM_MAX_COPIED_VERTS,
+      _tnl_reset_compile_input( ctx, im->Count+1+IMM_MAX_COPIED_VERTS,
                        new_beginstate, node->SavedBeginState);
-   }   
+   }
 }
 
 
-static void 
+static void fixup_compiled_primitives( GLcontext *ctx, struct immediate *IM )
+{
+   TNLcontext *tnl = TNL_CONTEXT(ctx);
+
+   /* Can potentially overwrite primitive details - need to save the
+    * first slot:
+    */
+   tnl->DlistPrimitive = IM->Primitive[IM->Start];
+   tnl->DlistPrimitiveLength = IM->PrimitiveLength[IM->Start];
+   tnl->DlistLastPrimitive = IM->LastPrimitive;
+
+   /* The first primitive may be different from what was recorded in
+    * the immediate struct.  Consider an immediate that starts with a
+    * glBegin, compiled in a display list, which is called from within
+    * an existing Begin/End object.
+    */
+   if (ctx->Driver.CurrentExecPrimitive == GL_POLYGON+1) {
+      GLuint i;
+
+      if (IM->BeginState & VERT_ERROR_1)
+        _mesa_error( ctx, GL_INVALID_OPERATION, "glBegin/glEnd");
+
+      for (i = IM->Start ; i <= IM->Count ; i += IM->PrimitiveLength[i])
+        if (IM->Flag[i] & (VERT_BIT_BEGIN|VERT_BIT_END_VB))
+           break;
+
+      /* Would like to just ignore vertices upto this point.  Can't
+       * set copystart because it might skip materials?
+       */
+      ASSERT(IM->Start == IM->CopyStart);
+      if (i > IM->CopyStart || !(IM->Flag[IM->Start] & VERT_BIT_BEGIN)) {
+        IM->Primitive[IM->CopyStart] = GL_POLYGON+1;
+        IM->PrimitiveLength[IM->CopyStart] = i - IM->CopyStart;
+        if (IM->Flag[i] & VERT_BIT_END_VB) {
+           IM->Primitive[IM->CopyStart] |= PRIM_LAST;
+           IM->LastPrimitive = IM->CopyStart;
+        }
+      }
+   } else {
+      GLuint i;
+
+      if (IM->BeginState & VERT_ERROR_0)
+        _mesa_error( ctx, GL_INVALID_OPERATION, "glBegin/glEnd");
+
+      if (IM->CopyStart == IM->Start &&
+         IM->Flag[IM->Start] & (VERT_BIT_END | VERT_BIT_END_VB))
+      {
+      }
+      else
+      {
+        IM->Primitive[IM->CopyStart] = ctx->Driver.CurrentExecPrimitive;
+        if (tnl->ExecParity)
+           IM->Primitive[IM->CopyStart] |= PRIM_PARITY;
+
+         /* one of these should be true, else we'll be in an infinite loop 
+         */
+         ASSERT(IM->PrimitiveLength[IM->Start] > 0 ||
+                IM->Flag[IM->Start] & (VERT_BIT_END | VERT_BIT_END_VB));
+
+        for (i = IM->Start ; i <= IM->Count ; i += IM->PrimitiveLength[i])
+           if (IM->Flag[i] & (VERT_BIT_END | VERT_BIT_END_VB)) {
+              IM->PrimitiveLength[IM->CopyStart] = i - IM->CopyStart;
+              if (IM->Flag[i] & VERT_BIT_END_VB) {
+                 IM->Primitive[IM->CopyStart] |= PRIM_LAST;
+                 IM->LastPrimitive = IM->CopyStart;
+              }
+              if (IM->Flag[i] & VERT_BIT_END) {
+                 IM->Primitive[IM->CopyStart] |= PRIM_END;
+              }
+              break;
+           }
+      }
+   }
+}
+
+/* Undo any changes potentially made to the immediate in the range
+ * IM->Start..IM->Count above.
+ */
+static void restore_compiled_primitives( GLcontext *ctx, struct immediate *IM )
+{
+   TNLcontext *tnl = TNL_CONTEXT(ctx);
+   IM->Primitive[IM->Start] = tnl->DlistPrimitive;
+   IM->PrimitiveLength[IM->Start] = tnl->DlistPrimitiveLength;
+}
+
+
+
+static void
 execute_compiled_cassette( GLcontext *ctx, void *data )
 {
    TNLcontext *tnl = TNL_CONTEXT(ctx);
    TNLvertexcassette *node = (TNLvertexcassette *)data;
    struct immediate *IM = node->IM;
 
-   if (ctx->NewState)
-      _mesa_update_state(ctx);
+/*     _mesa_debug("%s\n", __FUNCTION__); */
 
-   if (tnl->pipeline.build_state_changes)
-      _tnl_validate_pipeline( ctx );
-   
    IM->Start = node->Start;
    IM->CopyStart = node->Start;
    IM->Count = node->Count;
    IM->BeginState = node->BeginState;
-   IM->SavedBeginState = node->SavedBeginState;                
+   IM->SavedBeginState = node->SavedBeginState;
    IM->OrFlag = node->OrFlag;
-   IM->TexSize = node->TexSize;        
+   IM->TexSize = node->TexSize;
    IM->AndFlag = node->AndFlag;
    IM->LastData = node->LastData;
    IM->LastPrimitive = node->LastPrimitive;
    IM->LastMaterial = node->LastMaterial;
    IM->MaterialOrMask = node->MaterialOrMask;
+   IM->MaterialAndMask = node->MaterialAndMask;
 
    if ((MESA_VERBOSE & VERBOSE_DISPLAY_LIST) &&
        (MESA_VERBOSE & VERBOSE_IMMEDIATE))
       _tnl_print_cassette( IM );
 
    if (MESA_VERBOSE & VERBOSE_DISPLAY_LIST) {
-      fprintf(stderr, "Run cassette %d, rows %d..%d, beginstate %x ",
-             IM->id,
-             IM->Start, IM->Count, IM->BeginState);
+      _mesa_debug(ctx, "Run cassette %d, rows %d..%d, beginstate %x ",
+                  IM->id, IM->Start, IM->Count, IM->BeginState);
       _tnl_print_vert_flags("orflag", IM->OrFlag);
    }
 
-   if (IM->Count == IM->Start) {
-      _tnl_copy_to_current( ctx, IM, IM->OrFlag );
-      return;
-   }
 
+   /* Need to respect 'HardBeginEnd' even if the commands are looped
+    * back to a driver tnl module.
+    */
    if (IM->SavedBeginState) {
       if (ctx->Driver.CurrentExecPrimitive == GL_POLYGON+1)
         tnl->ReplayHardBeginEnd = 1;
@@ -213,10 +340,45 @@ execute_compiled_cassette( GLcontext *ctx, void *data )
       }
    }
 
-   _tnl_fixup_compiled_cassette( ctx, IM );
-   _tnl_get_exec_copy_verts( ctx, IM );
-   _tnl_run_cassette( ctx, IM ); 
-   _tnl_restore_compiled_cassette( ctx, IM );
+   if (tnl->LoopbackDListCassettes) {
+/*        (tnl->IsolateMaterials && (IM->OrFlag & VERT_MATERIAL)) ) { */
+      fixup_compiled_primitives( ctx, IM );
+      loopback_compiled_cassette( ctx, IM );
+      restore_compiled_primitives( ctx, IM );
+   }
+   else {
+      if (ctx->NewState)
+        _mesa_update_state(ctx);
+
+      if (tnl->pipeline.build_state_changes)
+        _tnl_validate_pipeline( ctx );
+
+      _tnl_fixup_compiled_cassette( ctx, IM );
+      fixup_compiled_primitives( ctx, IM );
+
+      if (IM->Primitive[IM->LastPrimitive] & PRIM_END)
+        ctx->Driver.CurrentExecPrimitive = GL_POLYGON+1;
+      else if ((IM->Primitive[IM->LastPrimitive] & PRIM_BEGIN) ||
+              (IM->Primitive[IM->LastPrimitive] & PRIM_MODE_MASK) == 
+              PRIM_OUTSIDE_BEGIN_END) {
+        ctx->Driver.CurrentExecPrimitive =
+           IM->Primitive[IM->LastPrimitive] & PRIM_MODE_MASK;
+      }
+
+      _tnl_get_exec_copy_verts( ctx, IM );
+
+      if (IM->NormalLengthPtr) 
+        fixup_normal_lengths( IM );
+      
+      if (IM->Count == IM->Start) 
+        _tnl_copy_to_current( ctx, IM, IM->OrFlag, IM->LastData );
+      else {
+/*      _tnl_print_cassette( IM ); */
+        _tnl_run_cassette( ctx, IM );
+      }
+
+      restore_compiled_primitives( ctx, IM );
+   }
 
    if (ctx->Driver.CurrentExecPrimitive == GL_POLYGON+1)
       tnl->ReplayHardBeginEnd = 0;
@@ -228,20 +390,21 @@ destroy_compiled_cassette( GLcontext *ctx, void *data )
    TNLvertexcassette *node = (TNLvertexcassette *)data;
 
    if ( --node->IM->ref_count == 0 )
-      _tnl_free_immediate( node->IM );
+      _tnl_free_immediate( ctx, node->IM );
 }
 
 
-static void 
+static void
 print_compiled_cassette( GLcontext *ctx, void *data )
 {
    TNLvertexcassette *node = (TNLvertexcassette *)data;
    struct immediate *IM = node->IM;
 
-   fprintf(stderr, "TNL-VERTEX-CASSETTE, id %u, rows %u..%u\n", 
-          node->IM->id, node->Start, node->Count);
+   _mesa_debug(ctx, "TNL-VERTEX-CASSETTE, id %u, rows %u..%u\n",
+               node->IM->id, node->Start, node->Count);
 
    IM->Start = node->Start;
+   IM->CopyStart = node->Start;
    IM->Count = node->Count;
    IM->BeginState = node->BeginState;
    IM->OrFlag = node->OrFlag;
@@ -251,6 +414,7 @@ print_compiled_cassette( GLcontext *ctx, void *data )
    IM->LastPrimitive = node->LastPrimitive;
    IM->LastMaterial = node->LastMaterial;
    IM->MaterialOrMask = node->MaterialOrMask;
+   IM->MaterialAndMask = node->MaterialAndMask;
 
    _tnl_print_cassette( node->IM );
 }
@@ -264,39 +428,33 @@ _tnl_BeginCallList( GLcontext *ctx, GLuint list )
 }
 
 
-/* Called at the tail of a CallList.  Copy vertices out of the display
- * list if necessary.
+/* Called at the tail of a CallList.  Make current immediate aware of
+ * any new to-be-copied vertices.
  */
 void
 _tnl_EndCallList( GLcontext *ctx )
 {
-   /* May have to copy vertices from a dangling begin/end inside the
-    * list to the current immediate.  
-    */
-   if (ctx->CallDepth == 0) {
-      TNLcontext *tnl = TNL_CONTEXT(ctx);
-      struct immediate *IM = TNL_CURRENT_IM(ctx);
+   GLuint beginstate = 0;
 
-      if (tnl->ExecCopySource != IM) 
-        _tnl_copy_immediate_vertices( ctx, IM );
-   }
+   if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END)
+      beginstate = VERT_BEGIN_0|VERT_BEGIN_1;
+
+   _tnl_reset_exec_input( ctx, TNL_CURRENT_IM(ctx)->Start, beginstate, 0 );
 }
 
 
-void 
+void
 _tnl_EndList( GLcontext *ctx )
 {
-   TNLcontext *tnl = TNL_CONTEXT(ctx);
    struct immediate *IM = TNL_CURRENT_IM(ctx);
 
    ctx->swtnl_im = 0;
    IM->ref_count--;
-   if (IM == tnl->ExecCopySource) {
-      IM->ref_count--;
-   } else {
-      if ( --tnl->ExecCopySource->ref_count == 0 )
-        _tnl_free_immediate( tnl->ExecCopySource );
-   }
+
+   /* outside begin/end, even in COMPILE_AND_EXEC,
+    * so no vertices to copy, right?
+    */
+   ASSERT(TNL_CONTEXT(ctx)->ExecCopyCount == 0);
 
    /* If this one isn't free, get a clean one.  (Otherwise we'll be
     * using one that's already half full).
@@ -306,22 +464,14 @@ _tnl_EndList( GLcontext *ctx )
 
    ASSERT(IM->ref_count == 0);
 
-   tnl->ExecCopySource = IM;
-   IM->ref_count++;
-
    SET_IMMEDIATE( ctx, IM );
    IM->ref_count++;
 
-   _tnl_reset_input( ctx, IMM_MAX_COPIED_VERTS, 0, 0 );        
-
-   /* outside begin/end, even in COMPILE_AND_EXEC, 
-    * so no vertices to copy, right? 
-    */
-   ASSERT(TNL_CONTEXT(ctx)->ExecCopyCount == 0);
+   _tnl_reset_exec_input( ctx, IMM_MAX_COPIED_VERTS, 0, 0 );
 }
 
 
-void 
+void
 _tnl_NewList( GLcontext *ctx, GLuint list, GLenum mode )
 {
    struct immediate *IM = TNL_CURRENT_IM(ctx);
@@ -341,12 +491,12 @@ _tnl_NewList( GLcontext *ctx, GLuint list, GLenum mode )
 }
 
 
-void 
+void
 _tnl_dlist_init( GLcontext *ctx )
 {
    TNLcontext *tnl = TNL_CONTEXT(ctx);
 
-   tnl->opcode_vertex_cassette = 
+   tnl->opcode_vertex_cassette =
       _mesa_alloc_opcode( ctx,
                          sizeof(TNLvertexcassette),
                          execute_compiled_cassette,
@@ -354,3 +504,153 @@ _tnl_dlist_init( GLcontext *ctx )
                          print_compiled_cassette );
 }
 
+
+static void emit_material( struct gl_material *src, GLuint bitmask )
+{
+   if (bitmask & FRONT_EMISSION_BIT) 
+      glMaterialfv( GL_FRONT, GL_EMISSION, src[0].Emission );
+
+   if (bitmask & BACK_EMISSION_BIT) 
+      glMaterialfv( GL_BACK, GL_EMISSION, src[1].Emission );
+
+   if (bitmask & FRONT_AMBIENT_BIT) 
+      glMaterialfv( GL_FRONT, GL_AMBIENT, src[0].Ambient );
+
+   if (bitmask & BACK_AMBIENT_BIT) 
+      glMaterialfv( GL_BACK, GL_AMBIENT, src[1].Ambient );
+
+   if (bitmask & FRONT_DIFFUSE_BIT) 
+      glMaterialfv( GL_FRONT, GL_DIFFUSE, src[0].Diffuse );
+
+   if (bitmask & BACK_DIFFUSE_BIT) 
+      glMaterialfv( GL_BACK, GL_DIFFUSE, src[1].Diffuse );
+
+   if (bitmask & FRONT_SPECULAR_BIT) 
+      glMaterialfv( GL_FRONT, GL_SPECULAR, src[0].Specular );
+
+   if (bitmask & BACK_SPECULAR_BIT) 
+      glMaterialfv( GL_BACK, GL_SPECULAR, src[1].Specular );
+
+   if (bitmask & FRONT_SHININESS_BIT) 
+      glMaterialfv( GL_FRONT, GL_SHININESS, &src[0].Shininess );
+
+   if (bitmask & BACK_SHININESS_BIT) 
+      glMaterialfv( GL_BACK, GL_SHININESS, &src[1].Shininess );
+
+   if (bitmask & FRONT_INDEXES_BIT) {
+      GLfloat ind[3];
+      ind[0] = src[0].AmbientIndex;
+      ind[1] = src[0].DiffuseIndex;
+      ind[2] = src[0].SpecularIndex;
+      glMaterialfv( GL_FRONT, GL_COLOR_INDEXES, ind );
+   }
+
+   if (bitmask & BACK_INDEXES_BIT) {
+      GLfloat ind[3];
+      ind[0] = src[1].AmbientIndex;
+      ind[1] = src[1].DiffuseIndex;
+      ind[2] = src[1].SpecularIndex;
+      glMaterialfv( GL_BACK, GL_COLOR_INDEXES, ind );
+   }
+}
+
+
+/* Low-performance helper function to allow driver-supplied tnl
+ * modules to process tnl display lists.  This is primarily supplied
+ * to avoid fallbacks if CallList is invoked inside a Begin/End pair.
+ * For higher performance, drivers should fallback to tnl (if outside
+ * begin/end), or (for tnl hardware) implement their own display list
+ * mechanism. 
+ */
+static void loopback_compiled_cassette( GLcontext *ctx, struct immediate *IM )
+{
+   GLuint i;
+   GLuint *flags = IM->Flag;
+   GLuint orflag = IM->OrFlag;
+   GLuint j;
+   void (GLAPIENTRY *vertex)( const GLfloat * );
+   void (GLAPIENTRY *texcoordfv[MAX_TEXTURE_UNITS])( GLenum, const GLfloat * );
+   GLuint maxtex = 0;
+   GLuint p, length, prim = 0;
+   
+   if (orflag & VERT_BITS_OBJ_234)
+      vertex = (void (GLAPIENTRY *)(const GLfloat *)) glVertex4fv;
+   else
+      vertex = (void (GLAPIENTRY *)(const GLfloat *)) glVertex3fv;
+   
+   if (orflag & VERT_BITS_TEX_ANY) {
+      for (j = 0 ; j < ctx->Const.MaxTextureUnits ; j++) {
+        if (orflag & VERT_BIT_TEX(j)) {
+           maxtex = j+1;
+           if ((IM->TexSize & TEX_SIZE_4(j)) == TEX_SIZE_4(j))
+              texcoordfv[j] = glMultiTexCoord4fvARB;
+           else if (IM->TexSize & TEX_SIZE_3(j))
+              texcoordfv[j] = glMultiTexCoord3fvARB;
+           else
+              texcoordfv[j] = glMultiTexCoord2fvARB;
+        }
+      }      
+   }
+
+   for (p = IM->Start ; !(prim & PRIM_LAST) ; p += length)
+   {
+      prim = IM->Primitive[p];
+      length= IM->PrimitiveLength[p];
+      ASSERT(length || (prim & PRIM_LAST));
+      ASSERT((prim & PRIM_MODE_MASK) <= GL_POLYGON+1);
+
+      if (prim & PRIM_BEGIN) {
+        glBegin(prim & PRIM_MODE_MASK);
+      }
+
+      for ( i = p ; i <= p+length ; i++) {
+        if (flags[i] & VERT_BITS_TEX_ANY) {
+           GLuint k;
+           for (k = 0 ; k < maxtex ; k++) {
+              if (flags[i] & VERT_BIT_TEX(k)) {
+                 texcoordfv[k]( GL_TEXTURE0_ARB + k,
+                                 IM->Attrib[VERT_ATTRIB_TEX0 + k][i] );
+              }
+           }
+        }
+
+        if (flags[i] & VERT_BIT_NORMAL) 
+           glNormal3fv(IM->Attrib[VERT_ATTRIB_NORMAL][i]);
+
+        if (flags[i] & VERT_BIT_COLOR0) 
+           glColor4fv( IM->Attrib[VERT_ATTRIB_COLOR0][i] );
+
+        if (flags[i] & VERT_BIT_COLOR1)
+           _glapi_Dispatch->SecondaryColor3fvEXT( IM->Attrib[VERT_ATTRIB_COLOR1][i] );
+
+        if (flags[i] & VERT_BIT_FOG)
+           _glapi_Dispatch->FogCoordfEXT( IM->Attrib[VERT_ATTRIB_FOG][i][0] );
+
+        if (flags[i] & VERT_BIT_INDEX)
+           glIndexi( IM->Index[i] );
+
+        if (flags[i] & VERT_BIT_EDGEFLAG)
+           glEdgeFlag( IM->EdgeFlag[i] );
+
+        if (flags[i] & VERT_BIT_MATERIAL) 
+           emit_material( IM->Material[i], IM->MaterialMask[i] );
+
+        if (flags[i]&VERT_BITS_OBJ_234) 
+           vertex( IM->Attrib[VERT_ATTRIB_POS][i] );
+        else if (flags[i] & VERT_BIT_EVAL_C1)
+           glEvalCoord1f( IM->Attrib[VERT_ATTRIB_POS][i][0] );
+        else if (flags[i] & VERT_BIT_EVAL_P1)
+           glEvalPoint1( (GLint) IM->Attrib[VERT_ATTRIB_POS][i][0] );
+        else if (flags[i] & VERT_BIT_EVAL_C2)
+           glEvalCoord2f( IM->Attrib[VERT_ATTRIB_POS][i][0],
+                           IM->Attrib[VERT_ATTRIB_POS][i][1] );
+        else if (flags[i] & VERT_BIT_EVAL_P2)
+           glEvalPoint2( (GLint) IM->Attrib[VERT_ATTRIB_POS][i][0],
+                          (GLint) IM->Attrib[VERT_ATTRIB_POS][i][1] );
+      }
+
+      if (prim & PRIM_END) {
+        glEnd();
+      }
+   }
+}