fixed bad clear value
[mesa.git] / src / mesa / tnl / t_vtx_exec.c
1 /* $XFree86$ */
2 /**************************************************************************
3
4 Copyright 2002 Tungsten Graphics Inc., Cedar Park, Texas.
5
6 All Rights Reserved.
7
8 Permission is hereby granted, free of charge, to any person obtaining a
9 copy of this software and associated documentation files (the "Software"),
10 to deal in the Software without restriction, including without limitation
11 on the rights to use, copy, modify, merge, publish, distribute, sub
12 license, and/or sell copies of the Software, and to permit persons to whom
13 the Software is furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice (including the next
16 paragraph) shall be included in all copies or substantial portions of the
17 Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22 TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
23 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25 USE OR OTHER DEALINGS IN THE SOFTWARE.
26
27 **************************************************************************/
28
29 /*
30 * Authors:
31 * Keith Whitwell <keith@tungstengraphics.com>
32 *
33 */
34 #include "api_noop.h"
35 #include "api_arrayelt.h"
36 #include "context.h"
37 #include "imports.h"
38 #include "mmath.h"
39 #include "mtypes.h"
40 #include "enums.h"
41 #include "glapi.h"
42 #include "colormac.h"
43 #include "light.h"
44 #include "state.h"
45 #include "vtxfmt.h"
46
47 #include "tnl/tnl.h"
48 #include "tnl/t_context.h"
49 #include "tnl/t_array_api.h"
50
51 static void _tnl_FlushVertices( GLcontext *, GLuint );
52
53
54 void tnl_copy_to_current( GLcontext *ctx )
55 {
56 TNLcontext *tnl = TNL_CONTEXT(ctx);
57 GLuint flag = tnl->vertex_format;
58 GLint i;
59
60 assert(ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT);
61
62 for (i = 0 ; i < 16 ; i++)
63 if (flag & (1<<i))
64 COPY_4FV( ctx->Current.Attrib[i], tnl->attribptr[i] );
65
66 if (flag & VERT_BIT_INDEX)
67 ctx->Current.Index = tnl->indexptr[0];
68
69 if (flag & VERT_BIT_EDGEFLAG)
70 ctx->Current.EdgeFlag = tnl->edgeflagptr[0];
71
72 if (flag & VERT_BIT_MATERIAL) {
73 _mesa_update_material( ctx,
74 IM->Material[IM->LastMaterial],
75 IM->MaterialOrMask );
76
77 tnl->Driver.NotifyMaterialChange( ctx );
78 }
79
80
81 ctx->Driver.NeedFlush &= ~FLUSH_UPDATE_CURRENT;
82 }
83
84 static GLboolean discreet_gl_prim[GL_POLYGON+1] = {
85 1, /* 0 points */
86 1, /* 1 lines */
87 0, /* 2 line_strip */
88 0, /* 3 line_loop */
89 1, /* 4 tris */
90 0, /* 5 tri_fan */
91 0, /* 6 tri_strip */
92 1, /* 7 quads */
93 0, /* 8 quadstrip */
94 0, /* 9 poly */
95 };
96
97 /* Optimize the primitive list: ONLY FOR EXECUTE ATM
98 */
99 static void optimize_prims( TNLcontext *tnl )
100 {
101 int i, j;
102
103 if (tnl->nrprims <= 1)
104 return;
105
106 for (j = 0, i = 1 ; i < tnl->nrprims; i++) {
107 int pj = tnl->primlist[j].prim & 0xf;
108 int pi = tnl->primlist[i].prim & 0xf;
109
110 if (pj == pi && discreet_gl_prim[pj] &&
111 tnl->primlist[i].start == tnl->primlist[j].end) {
112 tnl->primlist[j].end = tnl->primlist[i].end;
113 }
114 else {
115 j++;
116 if (j != i) tnl->primlist[j] = tnl->primlist[i];
117 }
118 }
119
120 tnl->nrprims = j+1;
121 }
122
123
124 /* Bind vertex buffer pointers, run pipeline:
125 */
126 static void flush_prims( TNLcontext *tnl )
127 {
128 int i,j;
129
130 tnl->dma.current.ptr = tnl->dma.current.start +=
131 (tnl->initial_counter - tnl->counter) * tnl->vertex_size * 4;
132
133 tnl->tcl.vertex_format = tnl->vertex_format;
134 tnl->tcl.aos_components[0] = &tmp;
135 tnl->tcl.nr_aos_components = 1;
136 tnl->dma.flush = 0;
137
138 tnl->Driver.RunPipeline( ... );
139
140 tnl->nrprims = 0;
141 }
142
143
144 static void start_prim( TNLcontext *tnl, GLuint mode )
145 {
146 if (MESA_VERBOSE & DEBUG_VFMT)
147 _mesa_debug(NULL, "%s %d\n", __FUNCTION__,
148 tnl->initial_counter - tnl->counter);
149
150 tnl->primlist[tnl->nrprims].start = tnl->initial_counter - tnl->counter;
151 tnl->primlist[tnl->nrprims].prim = mode;
152 }
153
154 static void note_last_prim( TNLcontext *tnl, GLuint flags )
155 {
156 if (MESA_VERBOSE & DEBUG_VFMT)
157 _mesa_debug(NULL, "%s %d\n", __FUNCTION__,
158 tnl->initial_counter - tnl->counter);
159
160 if (tnl->prim[0] != GL_POLYGON+1) {
161 tnl->primlist[tnl->nrprims].prim |= flags;
162 tnl->primlist[tnl->nrprims].end = tnl->initial_counter - tnl->counter;
163
164 if (++tnl->nrprims == TNL_MAX_PRIMS)
165 flush_prims( tnl );
166 }
167 }
168
169
170 static void copy_vertex( TNLcontext *tnl, GLuint n, GLfloat *dst )
171 {
172 GLuint i;
173 GLfloat *src = (GLfloat *)(tnl->dma.current.address +
174 tnl->dma.current.ptr +
175 (tnl->primlist[tnl->nrprims].start + n) *
176 tnl->vertex_size * 4);
177
178 if (MESA_VERBOSE & DEBUG_VFMT)
179 _mesa_debug(NULL, "copy_vertex %d\n",
180 tnl->primlist[tnl->nrprims].start + n);
181
182 for (i = 0 ; i < tnl->vertex_size; i++) {
183 dst[i] = src[i];
184 }
185 }
186
187 static GLuint copy_wrapped_verts( TNLcontext *tnl, GLfloat (*tmp)[15] )
188 {
189 GLuint ovf, i;
190 GLuint nr = (tnl->initial_counter - tnl->counter) - tnl->primlist[tnl->nrprims].start;
191
192 if (MESA_VERBOSE & DEBUG_VFMT)
193 _mesa_debug(NULL, "%s %d verts\n", __FUNCTION__, nr);
194
195 switch( tnl->prim[0] )
196 {
197 case GL_POINTS:
198 return 0;
199 case GL_LINES:
200 ovf = nr&1;
201 for (i = 0 ; i < ovf ; i++)
202 copy_vertex( tnl, nr-ovf+i, tmp[i] );
203 return i;
204 case GL_TRIANGLES:
205 ovf = nr%3;
206 for (i = 0 ; i < ovf ; i++)
207 copy_vertex( tnl, nr-ovf+i, tmp[i] );
208 return i;
209 case GL_QUADS:
210 ovf = nr&3;
211 for (i = 0 ; i < ovf ; i++)
212 copy_vertex( tnl, nr-ovf+i, tmp[i] );
213 return i;
214 case GL_LINE_STRIP:
215 if (nr == 0)
216 return 0;
217 copy_vertex( tnl, nr-1, tmp[0] );
218 return 1;
219 case GL_LINE_LOOP:
220 case GL_TRIANGLE_FAN:
221 case GL_POLYGON:
222 if (nr == 0)
223 return 0;
224 else if (nr == 1) {
225 copy_vertex( tnl, 0, tmp[0] );
226 return 1;
227 } else {
228 copy_vertex( tnl, 0, tmp[0] );
229 copy_vertex( tnl, nr-1, tmp[1] );
230 return 2;
231 }
232 case GL_TRIANGLE_STRIP:
233 ovf = MIN2( nr-1, 2 );
234 for (i = 0 ; i < ovf ; i++)
235 copy_vertex( tnl, nr-ovf+i, tmp[i] );
236 return i;
237 case GL_QUAD_STRIP:
238 ovf = MIN2( nr-1, 2 );
239 if (nr > 2) ovf += nr&1;
240 for (i = 0 ; i < ovf ; i++)
241 copy_vertex( tnl, nr-ovf+i, tmp[i] );
242 return i;
243 default:
244 assert(0);
245 return 0;
246 }
247 }
248
249
250
251 /* Extend for vertex-format changes on wrap:
252 */
253 static void wrap_buffer( void )
254 {
255 TNLcontext *tnl = tnl->tnl;
256 GLfloat tmp[3][15];
257 GLuint i, nrverts;
258
259 if (MESA_VERBOSE & (DEBUG_VFMT|DEBUG_PRIMS))
260 _mesa_debug(NULL, "%s %d\n", __FUNCTION__,
261 tnl->initial_counter - tnl->counter);
262
263 /* Don't deal with parity. *** WONT WORK FOR COMPILE
264 */
265 if ((((tnl->initial_counter - tnl->counter) -
266 tnl->primlist[tnl->nrprims].start) & 1)) {
267 tnl->counter++;
268 tnl->initial_counter++;
269 return;
270 }
271
272 /* Copy vertices out of dma:
273 */
274 nrverts = copy_dma_verts( tnl, tmp );
275
276 if (MESA_VERBOSE & DEBUG_VFMT)
277 _mesa_debug(NULL, "%d vertices to copy\n", nrverts);
278
279
280 /* Finish the prim at this point:
281 */
282 note_last_prim( tnl, 0 );
283 flush_prims( tnl );
284
285 /* Reset counter, dmaptr
286 */
287 tnl->dmaptr = (int *)(tnl->dma.current.ptr + tnl->dma.current.address);
288 tnl->counter = (tnl->dma.current.end - tnl->dma.current.ptr) /
289 (tnl->vertex_size * 4);
290 tnl->counter--;
291 tnl->initial_counter = tnl->counter;
292 tnl->notify = wrap_buffer;
293
294 tnl->dma.flush = flush_prims;
295 start_prim( tnl, tnl->prim[0] );
296
297
298 /* Reemit saved vertices
299 * *** POSSIBLY IN NEW FORMAT
300 * --> Can't always extend at end of vertex?
301 */
302 for (i = 0 ; i < nrverts; i++) {
303 if (MESA_VERBOSE & DEBUG_VERTS) {
304 int j;
305 _mesa_debug(NULL, "re-emit vertex %d to %p\n", i, tnl->dmaptr);
306 if (MESA_VERBOSE & DEBUG_VERBOSE)
307 for (j = 0 ; j < tnl->vertex_size; j++)
308 _mesa_debug(NULL, "\t%08x/%f\n", *(int*)&tmp[i][j], tmp[i][j]);
309 }
310
311 memcpy( tnl->dmaptr, tmp[i], tnl->vertex_size * 4 );
312 tnl->dmaptr += tnl->vertex_size;
313 tnl->counter--;
314 }
315 }
316
317
318
319 /* Always follow data, don't try to predict what's necessary.
320 */
321 static GLboolean check_vtx_fmt( GLcontext *ctx )
322 {
323 TNLcontext *tnl = TNL_CONTEXT(ctx);
324
325 if (ctx->Driver.NeedFlush & FLUSH_UPDATE_CURRENT)
326 ctx->Driver.FlushVertices( ctx, FLUSH_UPDATE_CURRENT );
327
328
329 TNL_NEWPRIM(tnl);
330 tnl->vertex_format = VERT_BIT_POS;
331 tnl->prim = &ctx->Driver.CurrentExecPrimitive;
332
333
334 /* Currently allow the full 4 components per attrib. Can use the
335 * mechanism from radeon driver color handling to reduce this (and
336 * also to store ubyte colors where these are incoming). This
337 * won't work for compile mode.
338 *
339 * Only adding components when they are first received eliminates
340 * the need for displaylist fixup, as there are no 'empty' slots
341 * at the start of buffers.
342 */
343 for (i = 0 ; i < 16 ; i++) {
344 if (ind & (1<<i)) {
345 tnl->attribptr[i] = &tnl->vertex[tnl->vertex_size].f;
346 tnl->vertex_size += 4;
347 tnl->attribptr[i][0] = ctx->Current.Attrib[i][0];
348 tnl->attribptr[i][1] = ctx->Current.Attrib[i][1];
349 tnl->attribptr[i][2] = ctx->Current.Attrib[i][2];
350 tnl->attribptr[i][3] = ctx->Current.Attrib[i][3];
351 }
352 else
353 tnl->attribptr[i] = ctx->Current.Attrib[i];
354 }
355
356 /* Edgeflag, Index:
357 */
358 for (i = 16 ; i < 18 ; i++)
359 ;
360
361 /* Materials:
362 */
363 for (i = 18 ; i < 28 ; i++)
364 ;
365
366 /* Eval:
367 */
368 for (i = 28 ; i < 29 ; i++)
369 ;
370
371
372 if (tnl->installed_vertex_format != tnl->vertex_format) {
373 if (MESA_VERBOSE & DEBUG_VFMT)
374 _mesa_debug(NULL, "reinstall on vertex_format change\n");
375 _mesa_install_exec_vtxfmt( ctx, &tnl->vtxfmt );
376 tnl->installed_vertex_format = tnl->vertex_format;
377 }
378
379 return GL_TRUE;
380 }
381
382
383 void _tnl_InvalidateVtxfmt( GLcontext *ctx )
384 {
385 tnl->recheck = GL_TRUE;
386 tnl->fell_back = GL_FALSE;
387 }
388
389
390
391
392 static void _tnl_ValidateVtxfmt( GLcontext *ctx )
393 {
394 if (MESA_VERBOSE & DEBUG_VFMT)
395 _mesa_debug(NULL, "%s\n", __FUNCTION__);
396
397 if (ctx->Driver.NeedFlush)
398 ctx->Driver.FlushVertices( ctx, ctx->Driver.NeedFlush );
399
400 tnl->recheck = GL_FALSE;
401
402 if (check_vtx_fmt( ctx )) {
403 if (!tnl->installed) {
404 if (MESA_VERBOSE & DEBUG_VFMT)
405 _mesa_debug(NULL, "reinstall (new install)\n");
406
407 _mesa_install_exec_vtxfmt( ctx, &tnl->vtxfmt );
408 ctx->Driver.FlushVertices = _tnl_FlushVertices;
409 tnl->installed = GL_TRUE;
410 }
411 else
412 _mesa_debug(NULL, "%s: already installed", __FUNCTION__);
413 }
414 else {
415 if (MESA_VERBOSE & DEBUG_VFMT)
416 _mesa_debug(NULL, "%s: failed\n", __FUNCTION__);
417
418 if (tnl->installed) {
419 if (tnl->tnl->dma.flush)
420 tnl->tnl->dma.flush( tnl->tnl );
421 _tnl_wakeup_exec( ctx );
422 tnl->installed = GL_FALSE;
423 }
424 }
425 }
426
427
428
429
430
431 /* Begin/End
432 */
433 static void _tnl_Begin( GLenum mode )
434 {
435 GLcontext *ctx = tnl->context;
436 TNLcontext *tnl = tnl->tnl;
437
438 if (MESA_VERBOSE & DEBUG_VFMT)
439 _mesa_debug(NULL, "%s\n", __FUNCTION__);
440
441 if (mode > GL_POLYGON) {
442 _mesa_error( ctx, GL_INVALID_ENUM, "glBegin" );
443 return;
444 }
445
446 if (tnl->prim[0] != GL_POLYGON+1) {
447 _mesa_error( ctx, GL_INVALID_OPERATION, "glBegin" );
448 return;
449 }
450
451 if (ctx->NewState)
452 _mesa_update_state( ctx );
453
454 if (tnl->recheck)
455 _tnl_ValidateVtxfmt( ctx );
456
457 if (tnl->dma.flush && tnl->counter < 12) {
458 if (MESA_VERBOSE & DEBUG_VFMT)
459 _mesa_debug(NULL, "%s: flush almost-empty buffers\n", __FUNCTION__);
460 flush_prims( tnl );
461 }
462
463 if (!tnl->dma.flush) {
464 if (tnl->dma.current.ptr + 12*tnl->vertex_size*4 >
465 tnl->dma.current.end) {
466 TNL_NEWPRIM( tnl );
467 _tnl_RefillCurrentDmaRegion( tnl );
468 }
469
470 tnl->dmaptr = (int *)(tnl->dma.current.address + tnl->dma.current.ptr);
471 tnl->counter = (tnl->dma.current.end - tnl->dma.current.ptr) /
472 (tnl->vertex_size * 4);
473 tnl->counter--;
474 tnl->initial_counter = tnl->counter;
475 tnl->notify = wrap_buffer;
476 tnl->dma.flush = flush_prims;
477 tnl->context->Driver.NeedFlush |= FLUSH_STORED_VERTICES;
478 }
479
480
481 tnl->prim[0] = mode;
482 start_prim( tnl, mode | PRIM_BEGIN );
483 }
484
485
486
487
488
489 static void _tnl_End( void )
490 {
491 TNLcontext *tnl = tnl->tnl;
492 GLcontext *ctx = tnl->context;
493
494 if (MESA_VERBOSE & DEBUG_VFMT)
495 _mesa_debug(NULL, "%s\n", __FUNCTION__);
496
497 if (tnl->prim[0] == GL_POLYGON+1) {
498 _mesa_error( ctx, GL_INVALID_OPERATION, "glEnd" );
499 return;
500 }
501
502 note_last_prim( tnl, PRIM_END );
503 tnl->prim[0] = GL_POLYGON+1;
504 }
505
506
507 static void _tnl_FlushVertices( GLcontext *ctx, GLuint flags )
508 {
509 if (MESA_VERBOSE & DEBUG_VFMT)
510 _mesa_debug(NULL, "%s\n", __FUNCTION__);
511
512 assert(tnl->installed);
513
514 if (flags & FLUSH_UPDATE_CURRENT) {
515 _tnl_copy_to_current( ctx );
516 if (MESA_VERBOSE & DEBUG_VFMT)
517 _mesa_debug(NULL, "reinstall on update_current\n");
518 _mesa_install_exec_vtxfmt( ctx, &tnl->vtxfmt );
519 ctx->Driver.NeedFlush &= ~FLUSH_UPDATE_CURRENT;
520 }
521
522 if (flags & FLUSH_STORED_VERTICES) {
523 TNLcontext *tnl = TNL_CONTEXT( ctx );
524 assert (tnl->dma.flush == 0 ||
525 tnl->dma.flush == flush_prims);
526 if (tnl->dma.flush == flush_prims)
527 flush_prims( TNL_CONTEXT( ctx ) );
528 ctx->Driver.NeedFlush &= ~FLUSH_STORED_VERTICES;
529 }
530 }
531
532
533
534 /* At this point, don't expect very many versions of each function to
535 * be generated, so not concerned about freeing them?
536 */
537
538
539 static void _tnl_InitVtxfmt( GLcontext *ctx )
540 {
541 GLvertexformat *vfmt = &(tnl->vtxfmt);
542
543 MEMSET( vfmt, 0, sizeof(GLvertexformat) );
544
545 /* Hook in chooser functions for codegen, etc:
546 */
547 _tnl_InitVtxfmtChoosers( vfmt );
548
549 /* Handled fully in supported states, but no codegen:
550 */
551 vfmt->ArrayElement = _ae_loopback_array_elt; /* generic helper */
552 vfmt->Rectf = _mesa_noop_Rectf; /* generic helper */
553 vfmt->Begin = _tnl_Begin;
554 vfmt->End = _tnl_End;
555
556 tnl->context = ctx;
557 tnl->tnl = TNL_CONTEXT(ctx);
558 tnl->prim = &ctx->Driver.CurrentExecPrimitive;
559 tnl->primflags = 0;
560
561 make_empty_list( &tnl->dfn_cache.Vertex2f );
562 make_empty_list( &tnl->dfn_cache.Vertex2fv );
563 make_empty_list( &tnl->dfn_cache.Vertex3f );
564 make_empty_list( &tnl->dfn_cache.Vertex3fv );
565 make_empty_list( &tnl->dfn_cache.Color4ub );
566 make_empty_list( &tnl->dfn_cache.Color4ubv );
567 make_empty_list( &tnl->dfn_cache.Color3ub );
568 make_empty_list( &tnl->dfn_cache.Color3ubv );
569 make_empty_list( &tnl->dfn_cache.Color4f );
570 make_empty_list( &tnl->dfn_cache.Color4fv );
571 make_empty_list( &tnl->dfn_cache.Color3f );
572 make_empty_list( &tnl->dfn_cache.Color3fv );
573 make_empty_list( &tnl->dfn_cache.SecondaryColor3fEXT );
574 make_empty_list( &tnl->dfn_cache.SecondaryColor3fvEXT );
575 make_empty_list( &tnl->dfn_cache.SecondaryColor3ubEXT );
576 make_empty_list( &tnl->dfn_cache.SecondaryColor3ubvEXT );
577 make_empty_list( &tnl->dfn_cache.Normal3f );
578 make_empty_list( &tnl->dfn_cache.Normal3fv );
579 make_empty_list( &tnl->dfn_cache.TexCoord2f );
580 make_empty_list( &tnl->dfn_cache.TexCoord2fv );
581 make_empty_list( &tnl->dfn_cache.TexCoord1f );
582 make_empty_list( &tnl->dfn_cache.TexCoord1fv );
583 make_empty_list( &tnl->dfn_cache.MultiTexCoord2fARB );
584 make_empty_list( &tnl->dfn_cache.MultiTexCoord2fvARB );
585 make_empty_list( &tnl->dfn_cache.MultiTexCoord1fARB );
586 make_empty_list( &tnl->dfn_cache.MultiTexCoord1fvARB );
587
588 _tnl_InitCodegen( &tnl->codegen );
589 }
590
591 static void free_funcs( struct dynfn *l )
592 {
593 struct dynfn *f, *tmp;
594 foreach_s (f, tmp, l) {
595 remove_from_list( f );
596 ALIGN_FREE( f->code );
597 FREE( f );
598 }
599 }
600
601
602 static void _tnl_DestroyVtxfmt( GLcontext *ctx )
603 {
604 count_funcs();
605 free_funcs( &tnl->dfn_cache.Vertex2f );
606 free_funcs( &tnl->dfn_cache.Vertex2fv );
607 free_funcs( &tnl->dfn_cache.Vertex3f );
608 free_funcs( &tnl->dfn_cache.Vertex3fv );
609 free_funcs( &tnl->dfn_cache.Color4ub );
610 free_funcs( &tnl->dfn_cache.Color4ubv );
611 free_funcs( &tnl->dfn_cache.Color3ub );
612 free_funcs( &tnl->dfn_cache.Color3ubv );
613 free_funcs( &tnl->dfn_cache.Color4f );
614 free_funcs( &tnl->dfn_cache.Color4fv );
615 free_funcs( &tnl->dfn_cache.Color3f );
616 free_funcs( &tnl->dfn_cache.Color3fv );
617 free_funcs( &tnl->dfn_cache.SecondaryColor3ubEXT );
618 free_funcs( &tnl->dfn_cache.SecondaryColor3ubvEXT );
619 free_funcs( &tnl->dfn_cache.SecondaryColor3fEXT );
620 free_funcs( &tnl->dfn_cache.SecondaryColor3fvEXT );
621 free_funcs( &tnl->dfn_cache.Normal3f );
622 free_funcs( &tnl->dfn_cache.Normal3fv );
623 free_funcs( &tnl->dfn_cache.TexCoord2f );
624 free_funcs( &tnl->dfn_cache.TexCoord2fv );
625 free_funcs( &tnl->dfn_cache.TexCoord1f );
626 free_funcs( &tnl->dfn_cache.TexCoord1fv );
627 free_funcs( &tnl->dfn_cache.MultiTexCoord2fARB );
628 free_funcs( &tnl->dfn_cache.MultiTexCoord2fvARB );
629 free_funcs( &tnl->dfn_cache.MultiTexCoord1fARB );
630 free_funcs( &tnl->dfn_cache.MultiTexCoord1fvARB );
631 }
632