Adaptor code that lets tnl convert compiled (display list) cassettes back
authorKeith Whitwell <keith@tungstengraphics.com>
Mon, 4 Jun 2001 16:09:28 +0000 (16:09 +0000)
committerKeith Whitwell <keith@tungstengraphics.com>
Mon, 4 Jun 2001 16:09:28 +0000 (16:09 +0000)
to glVertex() type calls.  Allows driver-supplied tnl modules to avoid
fallback on glCallList inside begin/end pairs.

Still a little buggy...

src/mesa/tnl/t_context.c
src/mesa/tnl/t_context.h
src/mesa/tnl/t_imm_api.c
src/mesa/tnl/t_imm_dlist.c
src/mesa/tnl/t_imm_fixup.c
src/mesa/tnl/t_imm_fixup.h
src/mesa/tnl/tnl.h

index 37b048366327aba28d8784a635bdcf9df268cc0b..e842bd14224d447c88618ab359fb16cb8adeaf4c 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: t_context.c,v 1.18 2001/05/11 08:11:31 keithw Exp $ */
+/* $Id: t_context.c,v 1.19 2001/06/04 16:09:28 keithw Exp $ */
 
 /*
  * Mesa 3-D graphics library
@@ -103,6 +103,7 @@ _tnl_CreateContext( GLcontext *ctx )
 
 
    tnl->NeedProjCoords = GL_TRUE;
+   tnl->LoopbackDListCassettes = GL_FALSE;
 
    /* Hook our functions into exec and compile dispatch tables.
     */
@@ -123,6 +124,8 @@ _tnl_CreateContext( GLcontext *ctx )
 
    tnl->Driver.RenderTabElts = _tnl_render_tab_elts;
    tnl->Driver.RenderTabVerts = _tnl_render_tab_verts;
+
+   
    return GL_TRUE;
 }
 
@@ -210,3 +213,12 @@ _tnl_need_projected_coords( GLcontext *ctx, GLboolean mode )
       _tnl_InvalidateState( ctx, _NEW_PROJECTION );
    }
 }
+
+void
+_tnl_need_dlist_loopback( GLcontext *ctx, GLboolean mode )
+{
+   TNLcontext *tnl = TNL_CONTEXT(ctx);
+   if (tnl->LoopbackDListCassettes != mode) {
+      tnl->LoopbackDListCassettes = mode;
+   }
+}
index 54f13e46490690b45e21a9e59ea13acf297f563e..226b877d8525d705b6edf6fd996ce05478ab2375 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: t_context.h,v 1.27 2001/06/01 16:29:18 brianp Exp $ */
+/* $Id: t_context.h,v 1.28 2001/06/04 16:09:28 keithw Exp $ */
 
 /*
  * Mesa 3-D graphics library
@@ -524,6 +524,7 @@ typedef struct {
    /* Probably need a better configuration mechanism:
     */
    GLboolean NeedProjCoords;
+   GLboolean LoopbackDListCassettes;
 
    /* Derived state and storage for _tnl_eval_vb:
     */
index 2a55e0ef637de11b2956aeebc167ca634f6bf296..9c4894ada749f148953e920a84fa3b99b2c6f3b4 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: t_imm_api.c,v 1.14 2001/05/11 15:53:06 keithw Exp $ */
+/* $Id: t_imm_api.c,v 1.15 2001/06/04 16:09:28 keithw Exp $ */
 
 /*
  * Mesa 3-D graphics library
@@ -294,7 +294,9 @@ _tnl_end( GLcontext *ctx )
       IM->Flag[count] |= VERT_END;
       IM->Primitive[last] |= PRIM_END;
       IM->PrimitiveLength[last] = count - last;
-      IM->Primitive[count] = PRIM_OUTSIDE_BEGIN_END;
+      IM->Primitive[count] = PRIM_OUTSIDE_BEGIN_END; /* removes PRIM_BEGIN 
+                                                     * flag if length == 0
+                                                     */
       IM->LastPrimitive = count;
 
       if (IM->FlushElt == FLUSH_ELT_EAGER) {
index c9d375831e4bb27cceb07a4a5f7640a69cfe9563..722d67b143562f502bd0649b0c0a7cc00b424c73 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: t_imm_dlist.c,v 1.19 2001/05/14 09:00:51 keithw Exp $ */
+/* $Id: t_imm_dlist.c,v 1.20 2001/06/04 16:09:28 keithw Exp $ */
 
 /*
  * Mesa 3-D graphics library
@@ -63,6 +63,7 @@ typedef struct {
 } TNLvertexcassette;
 
 static void execute_compiled_cassette( GLcontext *ctx, void *data );
+static void loopback_compiled_cassette( GLcontext *ctx, struct immediate *IM );
 
 
 /* Insert the active immediate struct onto the display list currently
@@ -143,6 +144,93 @@ _tnl_compile_cassette( GLcontext *ctx, struct immediate *IM )
 }
 
 
+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_BEGIN|VERT_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->Primitive[IM->CopyStart] = GL_POLYGON+1;
+        IM->PrimitiveLength[IM->CopyStart] = i - IM->CopyStart;
+        if (IM->Flag[i] & VERT_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_END|VERT_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_END|VERT_END_VB));
+
+        for (i = IM->Start ; i <= IM->Count ; i += IM->PrimitiveLength[i])
+           if (IM->Flag[i] & (VERT_END|VERT_END_VB)) {
+              IM->PrimitiveLength[IM->CopyStart] = i - IM->CopyStart;
+              if (IM->Flag[i] & VERT_END_VB) {
+                 IM->Primitive[IM->CopyStart] |= PRIM_LAST;
+                 IM->LastPrimitive = IM->CopyStart;
+              }
+              if (IM->Flag[i] & VERT_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 )
 {
@@ -152,12 +240,6 @@ execute_compiled_cassette( GLcontext *ctx, void *data )
 
 /*     fprintf(stderr, "%s\n", __FUNCTION__); */
 
-   if (ctx->NewState)
-      _mesa_update_state(ctx);
-
-   if (tnl->pipeline.build_state_changes)
-      _tnl_validate_pipeline( ctx );
-
    IM->Start = node->Start;
    IM->CopyStart = node->Start;
    IM->Count = node->Count;
@@ -183,11 +265,10 @@ execute_compiled_cassette( GLcontext *ctx, void *data )
       _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;
@@ -202,10 +283,35 @@ 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) {
+      fixup_compiled_primitives( ctx, IM );
+      loopback_compiled_cassette( ctx, IM );
+      restore_compiled_primitives( ctx, IM );
+   }
+   else if (IM->Count == IM->Start) {
+      _tnl_copy_to_current( ctx, IM, IM->OrFlag );
+   }
+   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
+        ctx->Driver.CurrentExecPrimitive =
+           IM->Primitive[IM->LastPrimitive] & PRIM_MODE_MASK;
+
+      _tnl_get_exec_copy_verts( ctx, IM );
+      _tnl_run_cassette( ctx, IM );
+
+      restore_compiled_primitives( ctx, IM );
+   }
 
    if (ctx->Driver.CurrentExecPrimitive == GL_POLYGON+1)
       tnl->ReplayHardBeginEnd = 0;
@@ -322,3 +428,162 @@ _tnl_dlist_init( GLcontext *ctx )
                          destroy_compiled_cassette,
                          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 (*vertex)( const GLfloat * );
+   void (*texcoordfv[MAX_TEXTURE_UNITS])( GLuint, const GLfloat * );
+   GLuint maxtex = 0;
+   GLuint p, length, prim = 0;
+   
+   if (orflag & VERT_OBJ_234)
+      vertex = glVertex4fv;
+   else
+      vertex = glVertex3fv;
+   
+   if (orflag & VERT_TEX_ANY) {
+      for (j = 0 ; j < ctx->Const.MaxTextureUnits ; j++) {
+        if (orflag & VERT_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) {
+/*      fprintf(stderr, "begin %s\n", _mesa_prim_name[prim&PRIM_MODE_MASK]); */
+        glBegin(prim & PRIM_MODE_MASK);
+      }
+
+      for ( i = p ; i <= p+length ; i++) {
+        if (flags[i] & VERT_TEX_ANY) {
+           GLuint k;
+           for (k = 0 ; k < maxtex ; k++) {
+              if (flags[i] & VERT_TEX(k)) {
+                 texcoordfv[k]( GL_TEXTURE0_ARB + k, IM->TexCoord[k][i] );
+              }
+           }
+        }
+
+        if (flags[i] & VERT_NORM) {
+/*            fprintf(stderr, "normal %d: %f %f %f\n", i, */
+/*                    IM->Normal[i][0], IM->Normal[i][1], IM->Normal[i][2]);  */
+           glNormal3fv(IM->Normal[i]);
+        }
+
+        if (flags[i] & VERT_RGBA) {
+/*            fprintf(stderr, "color %d: %f %f %f\n", i, */
+/*                    IM->Color[i][0], IM->Color[i][1], IM->Color[i][2]);  */
+           glColor4fv( IM->Color[i] );
+        }
+
+        if (flags[i] & VERT_SPEC_RGB)
+           glSecondaryColor3fvEXT( IM->SecondaryColor[i] );
+
+        if (flags[i] & VERT_FOG_COORD)
+           glFogCoordfEXT( IM->FogCoord[i] );
+
+        if (flags[i] & VERT_INDEX)
+           glIndexi( IM->Index[i] );
+
+        if (flags[i] & VERT_EDGE)
+           glEdgeFlag( IM->EdgeFlag[i] );
+
+        if (flags[i] & VERT_MATERIAL) 
+           emit_material( IM->Material[i], IM->MaterialMask[i] );
+
+        if (flags[i]&VERT_OBJ_234) {
+/*            fprintf(stderr, "vertex %d: %f %f %f\n", i, */
+/*                    IM->Obj[i][0], IM->Obj[i][1], IM->Obj[i][2]); */
+           vertex( IM->Obj[i] );
+        }
+        else if (flags[i] & VERT_EVAL_C1)
+           glEvalCoord1f(IM->Obj[i][0]);
+        else if (flags[i] & VERT_EVAL_P1)
+           glEvalPoint1(IM->Obj[i][0]);
+        else if (flags[i] & VERT_EVAL_C2)
+           glEvalCoord2f( IM->Obj[i][0], IM->Obj[i][1]);
+        else if (flags[i] & VERT_EVAL_P2)
+           glEvalPoint2( IM->Obj[i][0], IM->Obj[i][1]);
+      }
+
+      if (prim & PRIM_END) {
+/*      fprintf(stderr, "end\n"); */
+        glEnd();
+      }
+   }
+}
index 126aba10068f066bb582174900f6fc1788524913..fdfeff566a144d5e987930b218274b4c016e6964 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: t_imm_fixup.c,v 1.19 2001/05/16 09:28:32 keithw Exp $ */
+/* $Id: t_imm_fixup.c,v 1.20 2001/06/04 16:09:28 keithw Exp $ */
 
 /*
  * Mesa 3-D graphics library
@@ -592,93 +592,6 @@ void _tnl_fixup_compiled_cassette( GLcontext *ctx, struct immediate *IM )
 
       } while (vulnerable);
    }
-
-   /* 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_BEGIN|VERT_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->Primitive[IM->CopyStart] = GL_POLYGON+1;
-        IM->PrimitiveLength[IM->CopyStart] = i - IM->CopyStart;
-        if (IM->Flag[i] & VERT_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_END|VERT_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_END|VERT_END_VB));
-
-        for (i = IM->Start ; i <= IM->Count ; i += IM->PrimitiveLength[i])
-           if (IM->Flag[i] & (VERT_END|VERT_END_VB)) {
-              IM->PrimitiveLength[IM->CopyStart] = i - IM->CopyStart;
-              if (IM->Flag[i] & VERT_END_VB) {
-                 IM->Primitive[IM->CopyStart] |= PRIM_LAST;
-                 IM->LastPrimitive = IM->CopyStart;
-              }
-              if (IM->Flag[i] & VERT_END) {
-                 IM->Primitive[IM->CopyStart] |= PRIM_END;
-              }
-              break;
-           }
-      }
-   }
-
-   if (IM->Primitive[IM->LastPrimitive] & PRIM_END)
-      ctx->Driver.CurrentExecPrimitive = GL_POLYGON+1;
-   else
-      ctx->Driver.CurrentExecPrimitive =
-        IM->Primitive[IM->LastPrimitive] & PRIM_MODE_MASK;
-}
-
-
-/* Undo any changes potentially made to the immediate in the range
- * IM->Start..IM->Count above.
- */
-void _tnl_restore_compiled_cassette( GLcontext *ctx, struct immediate *IM )
-{
-   TNLcontext *tnl = TNL_CONTEXT(ctx);
-   IM->Primitive[IM->Start] = tnl->DlistPrimitive;
-   IM->PrimitiveLength[IM->Start] = tnl->DlistPrimitiveLength;
 }
 
 
index 5ca68b297a0a97c80cdc12da4055e3ef58a672fa..806933aa68ab88ab5c9b646cc70b873c89d52099 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: t_imm_fixup.h,v 1.5 2001/04/30 21:08:52 keithw Exp $ */
+/* $Id: t_imm_fixup.h,v 1.6 2001/06/04 16:09:28 keithw Exp $ */
 
 /*
  * Mesa 3-D graphics library
@@ -51,9 +51,6 @@ extern void _tnl_fixup_input( GLcontext *ctx, struct immediate *IM );
 extern void _tnl_fixup_compiled_cassette( GLcontext *ctx,
                                          struct immediate *IM );
 
-extern void _tnl_restore_compiled_cassette( GLcontext *ctx,
-                                           struct immediate *IM );
-
 extern void _tnl_copy_immediate_vertices( GLcontext *ctx, 
                                          struct immediate *IM );
 
index 9ca530797e562f2be34d400c828d6a1975540fcc..413864aba1f78a0970cde14ea631e2273a091db7 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: tnl.h,v 1.6 2001/03/12 00:48:44 gareth Exp $ */
+/* $Id: tnl.h,v 1.7 2001/06/04 16:09:28 keithw Exp $ */
 
 /*
  * Mesa 3-D graphics library
@@ -64,4 +64,7 @@ _tnl_wakeup_save_exec( GLcontext *ctx );
 extern void
 _tnl_need_projected_coords( GLcontext *ctx, GLboolean flag );
 
+extern void
+_tnl_need_dlist_loopback( GLcontext *ctx, GLboolean flag );
+
 #endif