merge of glsl-compiler-1 branch
[mesa.git] / src / mesa / tnl / t_save_api.c
1 /**************************************************************************
2
3 Copyright 2002 Tungsten Graphics Inc., Cedar Park, Texas.
4
5 All Rights Reserved.
6
7 Permission is hereby granted, free of charge, to any person obtaining a
8 copy of this software and associated documentation files (the "Software"),
9 to deal in the Software without restriction, including without limitation
10 on the rights to use, copy, modify, merge, publish, distribute, sub
11 license, and/or sell copies of the Software, and to permit persons to whom
12 the Software is furnished to do so, subject to the following conditions:
13
14 The above copyright notice and this permission notice (including the next
15 paragraph) shall be included in all copies or substantial portions of the
16 Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26 **************************************************************************/
27
28 /*
29 * Authors:
30 * Keith Whitwell <keith@tungstengraphics.com>
31 */
32
33
34
35 /**
36 * The display list compiler attempts to store lists of vertices with the
37 * same vertex layout. Additionally it attempts to minimize the need
38 * for execute-time fixup of these vertex lists, allowing them to be
39 * cached on hardware.
40 *
41 * There are still some circumstances where this can be thwarted, for
42 * example by building a list that consists of one very long primitive
43 * (eg Begin(Triangles), 1000 vertices, End), and calling that list
44 * from inside a different begin/end object (Begin(Lines), CallList,
45 * End).
46 *
47 * In that case the code will have to replay the list as individual
48 * commands through the Exec dispatch table, or fix up the copied
49 * vertices at execute-time.
50 *
51 * The other case where fixup is required is when a vertex attribute
52 * is introduced in the middle of a primitive. Eg:
53 * Begin(Lines)
54 * TexCoord1f() Vertex2f()
55 * TexCoord1f() Color3f() Vertex2f()
56 * End()
57 *
58 * If the current value of Color isn't known at compile-time, this
59 * primitive will require fixup.
60 *
61 *
62 * The list compiler currently doesn't attempt to compile lists
63 * containing EvalCoord or EvalPoint commands. On encountering one of
64 * these, compilation falls back to opcodes.
65 *
66 * This could be improved to fallback only when a mix of EvalCoord and
67 * Vertex commands are issued within a single primitive.
68 */
69
70
71 #include "glheader.h"
72 #include "context.h"
73 #include "dlist.h"
74 #include "enums.h"
75 #include "macros.h"
76 #include "api_validate.h"
77 #include "api_arrayelt.h"
78 #include "vtxfmt.h"
79 #include "t_save_api.h"
80 #include "dispatch.h"
81
82 /*
83 * NOTE: Old 'parity' issue is gone, but copying can still be
84 * wrong-footed on replay.
85 */
86 static GLuint _save_copy_vertices( GLcontext *ctx,
87 const struct tnl_vertex_list *node )
88 {
89 TNLcontext *tnl = TNL_CONTEXT( ctx );
90 const struct tnl_prim *prim = &node->prim[node->prim_count-1];
91 GLuint nr = prim->count;
92 GLuint sz = tnl->save.vertex_size;
93 const GLfloat *src = node->buffer + prim->start * sz;
94 GLfloat *dst = tnl->save.copied.buffer;
95 GLuint ovf, i;
96
97 if (prim->mode & PRIM_END)
98 return 0;
99
100 switch( prim->mode & PRIM_MODE_MASK )
101 {
102 case GL_POINTS:
103 return 0;
104 case GL_LINES:
105 ovf = nr&1;
106 for (i = 0 ; i < ovf ; i++)
107 _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) );
108 return i;
109 case GL_TRIANGLES:
110 ovf = nr%3;
111 for (i = 0 ; i < ovf ; i++)
112 _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) );
113 return i;
114 case GL_QUADS:
115 ovf = nr&3;
116 for (i = 0 ; i < ovf ; i++)
117 _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) );
118 return i;
119 case GL_LINE_STRIP:
120 if (nr == 0)
121 return 0;
122 else {
123 _mesa_memcpy( dst, src+(nr-1)*sz, sz*sizeof(GLfloat) );
124 return 1;
125 }
126 case GL_LINE_LOOP:
127 case GL_TRIANGLE_FAN:
128 case GL_POLYGON:
129 if (nr == 0)
130 return 0;
131 else if (nr == 1) {
132 _mesa_memcpy( dst, src+0, sz*sizeof(GLfloat) );
133 return 1;
134 } else {
135 _mesa_memcpy( dst, src+0, sz*sizeof(GLfloat) );
136 _mesa_memcpy( dst+sz, src+(nr-1)*sz, sz*sizeof(GLfloat) );
137 return 2;
138 }
139 case GL_TRIANGLE_STRIP:
140 case GL_QUAD_STRIP:
141 switch (nr) {
142 case 0: ovf = 0; break;
143 case 1: ovf = 1; break;
144 default: ovf = 2 + (nr&1); break;
145 }
146 for (i = 0 ; i < ovf ; i++)
147 _mesa_memcpy( dst+i*sz, src+(nr-ovf+i)*sz, sz*sizeof(GLfloat) );
148 return i;
149 default:
150 assert(0);
151 return 0;
152 }
153 }
154
155
156 static void
157 build_normal_lengths( struct tnl_vertex_list *node )
158 {
159 GLuint i;
160 GLfloat *len;
161 GLfloat *n = node->buffer;
162 GLuint stride = node->vertex_size;
163 GLuint count = node->count;
164
165 len = node->normal_lengths = (GLfloat *) MALLOC( count * sizeof(GLfloat) );
166 if (!len)
167 return;
168
169 /* Find the normal of the first vertex:
170 */
171 for (i = 0 ; i < _TNL_ATTRIB_NORMAL ; i++)
172 n += node->attrsz[i];
173
174 for (i = 0 ; i < count ; i++, n += stride) {
175 len[i] = LEN_3FV( n );
176 if (len[i] > 0.0F) len[i] = 1.0F / len[i];
177 }
178 }
179
180 static struct tnl_vertex_store *alloc_vertex_store( GLcontext *ctx )
181 {
182 struct tnl_vertex_store *store = MALLOC_STRUCT(tnl_vertex_store);
183 (void) ctx;
184 store->used = 0;
185 store->refcount = 1;
186 return store;
187 }
188
189 static struct tnl_primitive_store *alloc_prim_store( GLcontext *ctx )
190 {
191 struct tnl_primitive_store *store = MALLOC_STRUCT(tnl_primitive_store);
192 (void) ctx;
193 store->used = 0;
194 store->refcount = 1;
195 return store;
196 }
197
198 static void _save_reset_counters( GLcontext *ctx )
199 {
200 TNLcontext *tnl = TNL_CONTEXT(ctx);
201
202 tnl->save.prim = tnl->save.prim_store->buffer + tnl->save.prim_store->used;
203 tnl->save.buffer = (tnl->save.vertex_store->buffer +
204 tnl->save.vertex_store->used);
205
206 if (tnl->save.vertex_size)
207 tnl->save.initial_counter = ((SAVE_BUFFER_SIZE -
208 tnl->save.vertex_store->used) /
209 tnl->save.vertex_size);
210 else
211 tnl->save.initial_counter = 0;
212
213 if (tnl->save.initial_counter > ctx->Const.MaxArrayLockSize )
214 tnl->save.initial_counter = ctx->Const.MaxArrayLockSize;
215
216 tnl->save.counter = tnl->save.initial_counter;
217 tnl->save.prim_count = 0;
218 tnl->save.prim_max = SAVE_PRIM_SIZE - tnl->save.prim_store->used;
219 tnl->save.copied.nr = 0;
220 tnl->save.dangling_attr_ref = 0;
221 }
222
223
224 /* Insert the active immediate struct onto the display list currently
225 * being built.
226 */
227 static void _save_compile_vertex_list( GLcontext *ctx )
228 {
229 TNLcontext *tnl = TNL_CONTEXT(ctx);
230 struct tnl_vertex_list *node;
231
232 /* Allocate space for this structure in the display list currently
233 * being compiled.
234 */
235 node = (struct tnl_vertex_list *)
236 _mesa_alloc_instruction(ctx, tnl->save.opcode_vertex_list, sizeof(*node));
237
238 if (!node)
239 return;
240
241 /* Duplicate our template, increment refcounts to the storage structs:
242 */
243 _mesa_memcpy(node->attrsz, tnl->save.attrsz, sizeof(node->attrsz));
244 node->vertex_size = tnl->save.vertex_size;
245 node->buffer = tnl->save.buffer;
246 node->count = tnl->save.initial_counter - tnl->save.counter;
247 node->wrap_count = tnl->save.copied.nr;
248 node->have_materials = tnl->save.have_materials;
249 node->dangling_attr_ref = tnl->save.dangling_attr_ref;
250 node->normal_lengths = NULL;
251 node->prim = tnl->save.prim;
252 node->prim_count = tnl->save.prim_count;
253 node->vertex_store = tnl->save.vertex_store;
254 node->prim_store = tnl->save.prim_store;
255
256 node->vertex_store->refcount++;
257 node->prim_store->refcount++;
258
259 assert(node->attrsz[_TNL_ATTRIB_POS] != 0 ||
260 node->count == 0);
261
262 if (tnl->save.dangling_attr_ref)
263 ctx->ListState.CurrentList->flags |= MESA_DLIST_DANGLING_REFS;
264
265 /* Maybe calculate normal lengths:
266 */
267 if (tnl->CalcDListNormalLengths &&
268 node->attrsz[_TNL_ATTRIB_NORMAL] == 3 &&
269 !(ctx->ListState.CurrentList->flags & MESA_DLIST_DANGLING_REFS))
270 build_normal_lengths( node );
271
272
273 tnl->save.vertex_store->used += tnl->save.vertex_size * node->count;
274 tnl->save.prim_store->used += node->prim_count;
275
276 /* Decide whether the storage structs are full, or can be used for
277 * the next vertex lists as well.
278 */
279 if (tnl->save.vertex_store->used >
280 SAVE_BUFFER_SIZE - 16 * (tnl->save.vertex_size + 4)) {
281
282 tnl->save.vertex_store->refcount--;
283 assert(tnl->save.vertex_store->refcount != 0);
284 tnl->save.vertex_store = alloc_vertex_store( ctx );
285 tnl->save.vbptr = tnl->save.vertex_store->buffer;
286 }
287
288 if (tnl->save.prim_store->used > SAVE_PRIM_SIZE - 6) {
289 tnl->save.prim_store->refcount--;
290 assert(tnl->save.prim_store->refcount != 0);
291 tnl->save.prim_store = alloc_prim_store( ctx );
292 }
293
294 /* Reset our structures for the next run of vertices:
295 */
296 _save_reset_counters( ctx );
297
298 /* Copy duplicated vertices
299 */
300 tnl->save.copied.nr = _save_copy_vertices( ctx, node );
301
302
303 /* Deal with GL_COMPILE_AND_EXECUTE:
304 */
305 if (ctx->ExecuteFlag) {
306 _tnl_playback_vertex_list( ctx, (void *) node );
307 }
308 }
309
310
311 /* TODO -- If no new vertices have been stored, don't bother saving
312 * it.
313 */
314 static void _save_wrap_buffers( GLcontext *ctx )
315 {
316 TNLcontext *tnl = TNL_CONTEXT(ctx);
317 GLint i = tnl->save.prim_count - 1;
318 GLenum mode;
319
320 assert(i < (GLint) tnl->save.prim_max);
321 assert(i >= 0);
322
323 /* Close off in-progress primitive.
324 */
325 tnl->save.prim[i].count = ((tnl->save.initial_counter - tnl->save.counter) -
326 tnl->save.prim[i].start);
327 mode = tnl->save.prim[i].mode & ~(PRIM_BEGIN|PRIM_END);
328
329 /* store the copied vertices, and allocate a new list.
330 */
331 _save_compile_vertex_list( ctx );
332
333 /* Restart interrupted primitive
334 */
335 tnl->save.prim[0].mode = mode;
336 tnl->save.prim[0].start = 0;
337 tnl->save.prim[0].count = 0;
338 tnl->save.prim_count = 1;
339 }
340
341
342
343 /* Called only when buffers are wrapped as the result of filling the
344 * vertex_store struct.
345 */
346 static void _save_wrap_filled_vertex( GLcontext *ctx )
347 {
348 TNLcontext *tnl = TNL_CONTEXT(ctx);
349 GLfloat *data = tnl->save.copied.buffer;
350 GLuint i;
351
352 /* Emit a glEnd to close off the last vertex list.
353 */
354 _save_wrap_buffers( ctx );
355
356 /* Copy stored stored vertices to start of new list.
357 */
358 assert(tnl->save.counter > tnl->save.copied.nr);
359
360 for (i = 0 ; i < tnl->save.copied.nr ; i++) {
361 _mesa_memcpy( tnl->save.vbptr, data, tnl->save.vertex_size * sizeof(GLfloat));
362 data += tnl->save.vertex_size;
363 tnl->save.vbptr += tnl->save.vertex_size;
364 tnl->save.counter--;
365 }
366 }
367
368
369 static void _save_copy_to_current( GLcontext *ctx )
370 {
371 TNLcontext *tnl = TNL_CONTEXT(ctx);
372 GLuint i;
373
374 /* XXX Use _TNL_FIRST_* and _TNL_LAST_* values instead? */
375 for (i = _TNL_ATTRIB_POS+1 ; i <= _TNL_ATTRIB_EDGEFLAG ; i++) {
376 if (tnl->save.attrsz[i]) {
377 tnl->save.currentsz[i][0] = tnl->save.attrsz[i];
378 COPY_CLEAN_4V(tnl->save.current[i],
379 tnl->save.attrsz[i],
380 tnl->save.attrptr[i]);
381 }
382 }
383
384 /* Edgeflag requires special treatment:
385 *
386 * TODO: change edgeflag to GLfloat in Mesa.
387 */
388 if (tnl->save.attrsz[_TNL_ATTRIB_EDGEFLAG]) {
389 ctx->ListState.ActiveEdgeFlag = 1;
390 tnl->save.CurrentFloatEdgeFlag =
391 tnl->save.attrptr[_TNL_ATTRIB_EDGEFLAG][0];
392 ctx->ListState.CurrentEdgeFlag =
393 (tnl->save.CurrentFloatEdgeFlag == 1.0);
394 }
395 }
396
397
398 static void _save_copy_from_current( GLcontext *ctx )
399 {
400 TNLcontext *tnl = TNL_CONTEXT(ctx);
401 GLint i;
402
403 for (i = _TNL_ATTRIB_POS+1 ; i <= _TNL_ATTRIB_EDGEFLAG ; i++)
404 switch (tnl->save.attrsz[i]) {
405 case 4: tnl->save.attrptr[i][3] = tnl->save.current[i][3];
406 case 3: tnl->save.attrptr[i][2] = tnl->save.current[i][2];
407 case 2: tnl->save.attrptr[i][1] = tnl->save.current[i][1];
408 case 1: tnl->save.attrptr[i][0] = tnl->save.current[i][0];
409 case 0: break;
410 }
411
412 /* Edgeflag requires special treatment:
413 */
414 if (tnl->save.attrsz[_TNL_ATTRIB_EDGEFLAG]) {
415 tnl->save.CurrentFloatEdgeFlag = (GLfloat)ctx->ListState.CurrentEdgeFlag;
416 tnl->save.attrptr[_TNL_ATTRIB_EDGEFLAG][0] = tnl->save.CurrentFloatEdgeFlag;
417 }
418 }
419
420
421
422
423 /* Flush existing data, set new attrib size, replay copied vertices.
424 */
425 static void _save_upgrade_vertex( GLcontext *ctx,
426 GLuint attr,
427 GLuint newsz )
428 {
429 TNLcontext *tnl = TNL_CONTEXT(ctx);
430 GLuint oldsz;
431 GLuint i;
432 GLfloat *tmp;
433
434 /* Store the current run of vertices, and emit a GL_END. Emit a
435 * BEGIN in the new buffer.
436 */
437 if (tnl->save.initial_counter != tnl->save.counter)
438 _save_wrap_buffers( ctx );
439 else
440 assert( tnl->save.copied.nr == 0 );
441
442 /* Do a COPY_TO_CURRENT to ensure back-copying works for the case
443 * when the attribute already exists in the vertex and is having
444 * its size increased.
445 */
446 _save_copy_to_current( ctx );
447
448 /* Fix up sizes:
449 */
450 oldsz = tnl->save.attrsz[attr];
451 tnl->save.attrsz[attr] = newsz;
452
453 tnl->save.vertex_size += newsz - oldsz;
454 tnl->save.counter = ((SAVE_BUFFER_SIZE - tnl->save.vertex_store->used) /
455 tnl->save.vertex_size);
456 if (tnl->save.counter > ctx->Const.MaxArrayLockSize )
457 tnl->save.counter = ctx->Const.MaxArrayLockSize;
458 tnl->save.initial_counter = tnl->save.counter;
459
460 /* Recalculate all the attrptr[] values:
461 */
462 for (i = 0, tmp = tnl->save.vertex ; i < _TNL_ATTRIB_MAX ; i++) {
463 if (tnl->save.attrsz[i]) {
464 tnl->save.attrptr[i] = tmp;
465 tmp += tnl->save.attrsz[i];
466 }
467 else
468 tnl->save.attrptr[i] = NULL; /* will not be dereferenced. */
469 }
470
471 /* Copy from current to repopulate the vertex with correct values.
472 */
473 _save_copy_from_current( ctx );
474
475 /* Replay stored vertices to translate them to new format here.
476 *
477 * If there are copied vertices and the new (upgraded) attribute
478 * has not been defined before, this list is somewhat degenerate,
479 * and will need fixup at runtime.
480 */
481 if (tnl->save.copied.nr)
482 {
483 GLfloat *data = tnl->save.copied.buffer;
484 GLfloat *dest = tnl->save.buffer;
485 GLuint j;
486
487 /* Need to note this and fix up at runtime (or loopback):
488 */
489 if (tnl->save.currentsz[attr][0] == 0) {
490 assert(oldsz == 0);
491 tnl->save.dangling_attr_ref = GL_TRUE;
492
493 /* _mesa_debug(NULL, "_save_upgrade_vertex: dangling reference attr %d\n", */
494 /* attr); */
495
496 #if 0
497 /* The current strategy is to punt these degenerate cases
498 * through _tnl_loopback_vertex_list(), a lower-performance
499 * option. To minimize the impact of this, artificially
500 * reduce the size of this vertex_list.
501 */
502 if (t->save.counter > 10) {
503 t->save.initial_counter = 10;
504 t->save.counter = 10;
505 }
506 #endif
507 }
508
509 for (i = 0 ; i < tnl->save.copied.nr ; i++) {
510 for (j = 0 ; j < _TNL_ATTRIB_MAX ; j++) {
511 if (tnl->save.attrsz[j]) {
512 if (j == attr) {
513 if (oldsz) {
514 COPY_CLEAN_4V( dest, oldsz, data );
515 data += oldsz;
516 dest += newsz;
517 }
518 else {
519 COPY_SZ_4V( dest, newsz, tnl->save.current[attr] );
520 dest += newsz;
521 }
522 }
523 else {
524 GLint sz = tnl->save.attrsz[j];
525 COPY_SZ_4V( dest, sz, data );
526 data += sz;
527 dest += sz;
528 }
529 }
530 }
531 }
532
533 tnl->save.vbptr = dest;
534 tnl->save.counter -= tnl->save.copied.nr;
535 }
536 }
537
538
539
540
541 /* Helper function for 'CHOOSE' macro. Do what's necessary when an
542 * entrypoint is called for the first time.
543 */
544 static void do_choose( GLuint attr, GLuint sz,
545 void (*attr_func)( const GLfloat *),
546 void (*choose1)( const GLfloat *),
547 void (*choose2)( const GLfloat *),
548 void (*choose3)( const GLfloat *),
549 void (*choose4)( const GLfloat *),
550 const GLfloat *v )
551 {
552 GET_CURRENT_CONTEXT( ctx );
553 TNLcontext *tnl = TNL_CONTEXT(ctx);
554 static GLfloat id[4] = { 0, 0, 0, 1 };
555 int i;
556
557 if (tnl->save.attrsz[attr] < sz) {
558 /* New size is larger. Need to flush existing vertices and get
559 * an enlarged vertex format.
560 */
561 _save_upgrade_vertex( ctx, attr, sz );
562 }
563 else {
564 /* New size is equal or smaller - just need to fill in some
565 * zeros.
566 */
567 for (i = sz ; i <= tnl->save.attrsz[attr] ; i++)
568 tnl->save.attrptr[attr][i-1] = id[i-1];
569 }
570
571 /* Reset any active pointers for this attribute
572 */
573 tnl->save.tabfv[attr][0] = choose1;
574 tnl->save.tabfv[attr][1] = choose2;
575 tnl->save.tabfv[attr][2] = choose3;
576 tnl->save.tabfv[attr][3] = choose4;
577
578 /* Update the secondary dispatch table with the new function
579 */
580 tnl->save.tabfv[attr][sz-1] = attr_func;
581
582 (*attr_func)(v);
583 }
584
585
586
587 /* Only one size for each attribute may be active at once. Eg. if
588 * Color3f is installed/active, then Color4f may not be, even if the
589 * vertex actually contains 4 color coordinates. This is because the
590 * 3f version won't otherwise set color[3] to 1.0 -- this is the job
591 * of the chooser function when switching between Color4f and Color3f.
592 */
593 #define ATTRFV( ATTR, N ) \
594 static void save_choose_##ATTR##_##N( const GLfloat *v ); \
595 \
596 static void save_attrib_##ATTR##_##N( const GLfloat *v ) \
597 { \
598 GET_CURRENT_CONTEXT( ctx ); \
599 TNLcontext *tnl = TNL_CONTEXT(ctx); \
600 \
601 if ((ATTR) == 0) { \
602 GLuint i; \
603 \
604 if (N>0) tnl->save.vbptr[0] = v[0]; \
605 if (N>1) tnl->save.vbptr[1] = v[1]; \
606 if (N>2) tnl->save.vbptr[2] = v[2]; \
607 if (N>3) tnl->save.vbptr[3] = v[3]; \
608 \
609 for (i = N; i < tnl->save.vertex_size; i++) \
610 tnl->save.vbptr[i] = tnl->save.vertex[i]; \
611 \
612 tnl->save.vbptr += tnl->save.vertex_size; \
613 \
614 if (--tnl->save.counter == 0) \
615 _save_wrap_filled_vertex( ctx ); \
616 } \
617 else { \
618 GLfloat *dest = tnl->save.attrptr[ATTR]; \
619 if (N>0) dest[0] = v[0]; \
620 if (N>1) dest[1] = v[1]; \
621 if (N>2) dest[2] = v[2]; \
622 if (N>3) dest[3] = v[3]; \
623 } \
624 }
625
626 #define CHOOSE( ATTR, N ) \
627 static void save_choose_##ATTR##_##N( const GLfloat *v ) \
628 { \
629 do_choose(ATTR, N, \
630 save_attrib_##ATTR##_##N, \
631 save_choose_##ATTR##_1, \
632 save_choose_##ATTR##_2, \
633 save_choose_##ATTR##_3, \
634 save_choose_##ATTR##_4, \
635 v ); \
636 }
637
638 #define INIT(ATTR) \
639 static void save_init_##ATTR( TNLcontext *tnl ) \
640 { \
641 tnl->save.tabfv[ATTR][0] = save_choose_##ATTR##_1; \
642 tnl->save.tabfv[ATTR][1] = save_choose_##ATTR##_2; \
643 tnl->save.tabfv[ATTR][2] = save_choose_##ATTR##_3; \
644 tnl->save.tabfv[ATTR][3] = save_choose_##ATTR##_4; \
645 }
646
647 #define ATTRS( ATTRIB ) \
648 ATTRFV( ATTRIB, 1 ) \
649 ATTRFV( ATTRIB, 2 ) \
650 ATTRFV( ATTRIB, 3 ) \
651 ATTRFV( ATTRIB, 4 ) \
652 CHOOSE( ATTRIB, 1 ) \
653 CHOOSE( ATTRIB, 2 ) \
654 CHOOSE( ATTRIB, 3 ) \
655 CHOOSE( ATTRIB, 4 ) \
656 INIT( ATTRIB ) \
657
658
659 /* Generate a lot of functions. These are the actual worker
660 * functions, which are equivalent to those generated via codegen
661 * elsewhere.
662 */
663 ATTRS( 0 )
664 ATTRS( 1 )
665 ATTRS( 2 )
666 ATTRS( 3 )
667 ATTRS( 4 )
668 ATTRS( 5 )
669 ATTRS( 6 )
670 ATTRS( 7 )
671 ATTRS( 8 )
672 ATTRS( 9 )
673 ATTRS( 10 )
674 ATTRS( 11 )
675 ATTRS( 12 )
676 ATTRS( 13 )
677 ATTRS( 14 )
678 ATTRS( 15 )
679
680 ATTRS( 16 )
681 ATTRS( 17 )
682 ATTRS( 18 )
683 ATTRS( 19 )
684 ATTRS( 20 )
685 ATTRS( 21 )
686 ATTRS( 22 )
687 ATTRS( 23 )
688 ATTRS( 24 )
689 ATTRS( 25 )
690 ATTRS( 26 )
691 ATTRS( 27 )
692 ATTRS( 28 )
693 ATTRS( 29 )
694 ATTRS( 30 )
695 ATTRS( 31 )
696
697
698 static void _save_reset_vertex( GLcontext *ctx )
699 {
700 TNLcontext *tnl = TNL_CONTEXT(ctx);
701 GLuint i;
702
703 /* conventional attributes */
704 save_init_0( tnl );
705 save_init_1( tnl );
706 save_init_2( tnl );
707 save_init_3( tnl );
708 save_init_4( tnl );
709 save_init_5( tnl );
710 save_init_6( tnl );
711 save_init_7( tnl );
712 save_init_8( tnl );
713 save_init_9( tnl );
714 save_init_10( tnl );
715 save_init_11( tnl );
716 save_init_12( tnl );
717 save_init_13( tnl );
718 save_init_14( tnl );
719 save_init_15( tnl );
720
721 /* generic attributes */
722 save_init_16( tnl );
723 save_init_17( tnl );
724 save_init_18( tnl );
725 save_init_19( tnl );
726 save_init_20( tnl );
727 save_init_21( tnl );
728 save_init_22( tnl );
729 save_init_23( tnl );
730 save_init_24( tnl );
731 save_init_25( tnl );
732 save_init_26( tnl );
733 save_init_27( tnl );
734 save_init_28( tnl );
735 save_init_29( tnl );
736 save_init_30( tnl );
737 save_init_31( tnl );
738
739 for (i = 0 ; i < _TNL_ATTRIB_MAX ; i++)
740 tnl->save.attrsz[i] = 0;
741
742 tnl->save.vertex_size = 0;
743 tnl->save.have_materials = 0;
744
745 _save_reset_counters( ctx );
746 }
747
748
749
750 /* Cope with aliasing of classic Vertex, Normal, etc. and the fan-out
751 * of glMultTexCoord and glProgramParamterNV by routing all these
752 * through a second level dispatch table.
753 */
754 #define DISPATCH_ATTRFV( ATTR, COUNT, P ) \
755 do { \
756 GET_CURRENT_CONTEXT( ctx ); \
757 TNLcontext *tnl = TNL_CONTEXT(ctx); \
758 tnl->save.tabfv[ATTR][COUNT-1]( P ); \
759 } while (0)
760
761 #define DISPATCH_ATTR1FV( ATTR, V ) DISPATCH_ATTRFV( ATTR, 1, V )
762 #define DISPATCH_ATTR2FV( ATTR, V ) DISPATCH_ATTRFV( ATTR, 2, V )
763 #define DISPATCH_ATTR3FV( ATTR, V ) DISPATCH_ATTRFV( ATTR, 3, V )
764 #define DISPATCH_ATTR4FV( ATTR, V ) DISPATCH_ATTRFV( ATTR, 4, V )
765
766 #define DISPATCH_ATTR1F( ATTR, S ) DISPATCH_ATTRFV( ATTR, 1, &(S) )
767
768 #if defined(USE_X86_ASM) && 0 /* will break register calling convention */
769 /* Naughty cheat:
770 */
771 #define DISPATCH_ATTR2F( ATTR, S,T ) DISPATCH_ATTRFV( ATTR, 2, &(S) )
772 #define DISPATCH_ATTR3F( ATTR, S,T,R ) DISPATCH_ATTRFV( ATTR, 3, &(S) )
773 #define DISPATCH_ATTR4F( ATTR, S,T,R,Q ) DISPATCH_ATTRFV( ATTR, 4, &(S) )
774 #else
775 /* Safe:
776 */
777 #define DISPATCH_ATTR2F( ATTR, S,T ) \
778 do { \
779 GLfloat v[2]; \
780 v[0] = S; v[1] = T; \
781 DISPATCH_ATTR2FV( ATTR, v ); \
782 } while (0)
783 #define DISPATCH_ATTR3F( ATTR, S,T,R ) \
784 do { \
785 GLfloat v[3]; \
786 v[0] = S; v[1] = T; v[2] = R; \
787 DISPATCH_ATTR3FV( ATTR, v ); \
788 } while (0)
789 #define DISPATCH_ATTR4F( ATTR, S,T,R,Q ) \
790 do { \
791 GLfloat v[4]; \
792 v[0] = S; v[1] = T; v[2] = R; v[3] = Q; \
793 DISPATCH_ATTR4FV( ATTR, v ); \
794 } while (0)
795 #endif
796
797
798 static void enum_error( void )
799 {
800 GET_CURRENT_CONTEXT( ctx );
801 _mesa_compile_error( ctx, GL_INVALID_ENUM, "glVertexAttrib" );
802 }
803
804 static void GLAPIENTRY _save_Vertex2f( GLfloat x, GLfloat y )
805 {
806 DISPATCH_ATTR2F( _TNL_ATTRIB_POS, x, y );
807 }
808
809 static void GLAPIENTRY _save_Vertex2fv( const GLfloat *v )
810 {
811 DISPATCH_ATTR2FV( _TNL_ATTRIB_POS, v );
812 }
813
814 static void GLAPIENTRY _save_Vertex3f( GLfloat x, GLfloat y, GLfloat z )
815 {
816 DISPATCH_ATTR3F( _TNL_ATTRIB_POS, x, y, z );
817 }
818
819 static void GLAPIENTRY _save_Vertex3fv( const GLfloat *v )
820 {
821 DISPATCH_ATTR3FV( _TNL_ATTRIB_POS, v );
822 }
823
824 static void GLAPIENTRY _save_Vertex4f( GLfloat x, GLfloat y, GLfloat z, GLfloat w )
825 {
826 DISPATCH_ATTR4F( _TNL_ATTRIB_POS, x, y, z, w );
827 }
828
829 static void GLAPIENTRY _save_Vertex4fv( const GLfloat *v )
830 {
831 DISPATCH_ATTR4FV( _TNL_ATTRIB_POS, v );
832 }
833
834 static void GLAPIENTRY _save_TexCoord1f( GLfloat x )
835 {
836 DISPATCH_ATTR1F( _TNL_ATTRIB_TEX0, x );
837 }
838
839 static void GLAPIENTRY _save_TexCoord1fv( const GLfloat *v )
840 {
841 DISPATCH_ATTR1FV( _TNL_ATTRIB_TEX0, v );
842 }
843
844 static void GLAPIENTRY _save_TexCoord2f( GLfloat x, GLfloat y )
845 {
846 DISPATCH_ATTR2F( _TNL_ATTRIB_TEX0, x, y );
847 }
848
849 static void GLAPIENTRY _save_TexCoord2fv( const GLfloat *v )
850 {
851 DISPATCH_ATTR2FV( _TNL_ATTRIB_TEX0, v );
852 }
853
854 static void GLAPIENTRY _save_TexCoord3f( GLfloat x, GLfloat y, GLfloat z )
855 {
856 DISPATCH_ATTR3F( _TNL_ATTRIB_TEX0, x, y, z );
857 }
858
859 static void GLAPIENTRY _save_TexCoord3fv( const GLfloat *v )
860 {
861 DISPATCH_ATTR3FV( _TNL_ATTRIB_TEX0, v );
862 }
863
864 static void GLAPIENTRY _save_TexCoord4f( GLfloat x, GLfloat y, GLfloat z, GLfloat w )
865 {
866 DISPATCH_ATTR4F( _TNL_ATTRIB_TEX0, x, y, z, w );
867 }
868
869 static void GLAPIENTRY _save_TexCoord4fv( const GLfloat *v )
870 {
871 DISPATCH_ATTR4FV( _TNL_ATTRIB_TEX0, v );
872 }
873
874 static void GLAPIENTRY _save_Normal3f( GLfloat x, GLfloat y, GLfloat z )
875 {
876 DISPATCH_ATTR3F( _TNL_ATTRIB_NORMAL, x, y, z );
877 }
878
879 static void GLAPIENTRY _save_Normal3fv( const GLfloat *v )
880 {
881 DISPATCH_ATTR3FV( _TNL_ATTRIB_NORMAL, v );
882 }
883
884 static void GLAPIENTRY _save_FogCoordfEXT( GLfloat x )
885 {
886 DISPATCH_ATTR1F( _TNL_ATTRIB_FOG, x );
887 }
888
889 static void GLAPIENTRY _save_FogCoordfvEXT( const GLfloat *v )
890 {
891 DISPATCH_ATTR1FV( _TNL_ATTRIB_FOG, v );
892 }
893
894 static void GLAPIENTRY _save_Color3f( GLfloat x, GLfloat y, GLfloat z )
895 {
896 DISPATCH_ATTR3F( _TNL_ATTRIB_COLOR0, x, y, z );
897 }
898
899 static void GLAPIENTRY _save_Color3fv( const GLfloat *v )
900 {
901 DISPATCH_ATTR3FV( _TNL_ATTRIB_COLOR0, v );
902 }
903
904 static void GLAPIENTRY _save_Color4f( GLfloat x, GLfloat y, GLfloat z, GLfloat w )
905 {
906 DISPATCH_ATTR4F( _TNL_ATTRIB_COLOR0, x, y, z, w );
907 }
908
909 static void GLAPIENTRY _save_Color4fv( const GLfloat *v )
910 {
911 DISPATCH_ATTR4FV( _TNL_ATTRIB_COLOR0, v );
912 }
913
914 static void GLAPIENTRY _save_SecondaryColor3fEXT( GLfloat x, GLfloat y, GLfloat z )
915 {
916 DISPATCH_ATTR3F( _TNL_ATTRIB_COLOR1, x, y, z );
917 }
918
919 static void GLAPIENTRY _save_SecondaryColor3fvEXT( const GLfloat *v )
920 {
921 DISPATCH_ATTR3FV( _TNL_ATTRIB_COLOR1, v );
922 }
923
924 static void GLAPIENTRY _save_MultiTexCoord1f( GLenum target, GLfloat x )
925 {
926 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
927 DISPATCH_ATTR1F( attr, x );
928 }
929
930 static void GLAPIENTRY _save_MultiTexCoord1fv( GLenum target, const GLfloat *v )
931 {
932 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
933 DISPATCH_ATTR1FV( attr, v );
934 }
935
936 static void GLAPIENTRY _save_MultiTexCoord2f( GLenum target, GLfloat x, GLfloat y )
937 {
938 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
939 DISPATCH_ATTR2F( attr, x, y );
940 }
941
942 static void GLAPIENTRY _save_MultiTexCoord2fv( GLenum target, const GLfloat *v )
943 {
944 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
945 DISPATCH_ATTR2FV( attr, v );
946 }
947
948 static void GLAPIENTRY _save_MultiTexCoord3f( GLenum target, GLfloat x, GLfloat y,
949 GLfloat z)
950 {
951 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
952 DISPATCH_ATTR3F( attr, x, y, z );
953 }
954
955 static void GLAPIENTRY _save_MultiTexCoord3fv( GLenum target, const GLfloat *v )
956 {
957 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
958 DISPATCH_ATTR3FV( attr, v );
959 }
960
961 static void GLAPIENTRY _save_MultiTexCoord4f( GLenum target, GLfloat x, GLfloat y,
962 GLfloat z, GLfloat w )
963 {
964 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
965 DISPATCH_ATTR4F( attr, x, y, z, w );
966 }
967
968 static void GLAPIENTRY _save_MultiTexCoord4fv( GLenum target, const GLfloat *v )
969 {
970 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
971 DISPATCH_ATTR4FV( attr, v );
972 }
973
974
975
976 static void GLAPIENTRY
977 _save_VertexAttrib1fNV(GLuint index, GLfloat x)
978 {
979 if (index < MAX_VERTEX_PROGRAM_ATTRIBS) {
980 if (index > 0)
981 index += VERT_ATTRIB_GENERIC0;
982 DISPATCH_ATTR1F( index, x );
983 }
984 else
985 enum_error();
986 }
987
988 static void GLAPIENTRY
989 _save_VertexAttrib1fvNV(GLuint index, const GLfloat *v)
990 {
991 if (index < MAX_VERTEX_PROGRAM_ATTRIBS) {
992 if (index > 0)
993 index += VERT_ATTRIB_GENERIC0;
994 DISPATCH_ATTR1FV( index, v );
995 }
996 else
997 enum_error();
998 }
999
1000 static void GLAPIENTRY
1001 _save_VertexAttrib2fNV(GLuint index, GLfloat x, GLfloat y)
1002 {
1003 if (index < MAX_VERTEX_PROGRAM_ATTRIBS) {
1004 if (index > 0)
1005 index += VERT_ATTRIB_GENERIC0;
1006 DISPATCH_ATTR2F( index, x, y );
1007 }
1008 else
1009 enum_error();
1010 }
1011
1012 static void GLAPIENTRY
1013 _save_VertexAttrib2fvNV(GLuint index, const GLfloat *v)
1014 {
1015 if (index < MAX_VERTEX_PROGRAM_ATTRIBS) {
1016 if (index > 0)
1017 index += VERT_ATTRIB_GENERIC0;
1018 DISPATCH_ATTR2FV( index, v );
1019 }
1020 else
1021 enum_error();
1022 }
1023
1024 static void GLAPIENTRY
1025 _save_VertexAttrib3fNV(GLuint index, GLfloat x, GLfloat y, GLfloat z)
1026 {
1027 if (index < MAX_VERTEX_PROGRAM_ATTRIBS) {
1028 if (index > 0)
1029 index += VERT_ATTRIB_GENERIC0;
1030 DISPATCH_ATTR3F( index, x, y, z );
1031 }
1032 else
1033 enum_error();
1034 }
1035
1036 static void GLAPIENTRY
1037 _save_VertexAttrib3fvNV(GLuint index, const GLfloat *v)
1038 {
1039 if (index < MAX_VERTEX_PROGRAM_ATTRIBS) {
1040 if (index > 0)
1041 index += VERT_ATTRIB_GENERIC0;
1042 DISPATCH_ATTR3FV( index, v );
1043 }
1044 else
1045 enum_error();
1046 }
1047
1048 static void GLAPIENTRY
1049 _save_VertexAttrib4fNV(GLuint index, GLfloat x, GLfloat y,
1050 GLfloat z, GLfloat w)
1051 {
1052 if (index < MAX_VERTEX_PROGRAM_ATTRIBS) {
1053 if (index > 0)
1054 index += VERT_ATTRIB_GENERIC0;
1055 DISPATCH_ATTR4F( index, x, y, z, w );
1056 }
1057 else
1058 enum_error();
1059 }
1060
1061 static void GLAPIENTRY
1062 _save_VertexAttrib4fvNV(GLuint index, const GLfloat *v)
1063 {
1064 if (index < MAX_VERTEX_PROGRAM_ATTRIBS) {
1065 if (index > 0)
1066 index += VERT_ATTRIB_GENERIC0;
1067 DISPATCH_ATTR4FV( index, v );
1068 }
1069 else
1070 enum_error();
1071 }
1072
1073 static void GLAPIENTRY
1074 _save_VertexAttrib1fARB(GLuint index, GLfloat x)
1075 {
1076 if (index < MAX_VERTEX_ATTRIBS) {
1077 if (index > 0)
1078 index += VERT_ATTRIB_GENERIC0;
1079 DISPATCH_ATTR1F( index, x );
1080 }
1081 else
1082 enum_error();
1083 }
1084
1085 static void GLAPIENTRY
1086 _save_VertexAttrib1fvARB(GLuint index, const GLfloat *v)
1087 {
1088 if (index < MAX_VERTEX_ATTRIBS) {
1089 if (index > 0)
1090 index += VERT_ATTRIB_GENERIC0;
1091 DISPATCH_ATTR1FV( index, v );
1092 }
1093 else
1094 enum_error();
1095 }
1096
1097 static void GLAPIENTRY
1098 _save_VertexAttrib2fARB(GLuint index, GLfloat x, GLfloat y)
1099 {
1100 if (index < MAX_VERTEX_ATTRIBS) {
1101 if (index > 0)
1102 index += VERT_ATTRIB_GENERIC0;
1103 DISPATCH_ATTR2F( index, x, y );
1104 }
1105 else
1106 enum_error();
1107 }
1108
1109 static void GLAPIENTRY
1110 _save_VertexAttrib2fvARB(GLuint index, const GLfloat *v)
1111 {
1112 if (index < MAX_VERTEX_ATTRIBS) {
1113 if (index > 0)
1114 index += VERT_ATTRIB_GENERIC0;
1115 DISPATCH_ATTR2FV( index, v );
1116 }
1117 else
1118 enum_error();
1119 }
1120
1121 static void GLAPIENTRY
1122 _save_VertexAttrib3fARB(GLuint index, GLfloat x, GLfloat y, GLfloat z)
1123 {
1124 if (index < MAX_VERTEX_ATTRIBS) {
1125 if (index > 0)
1126 index += VERT_ATTRIB_GENERIC0;
1127 DISPATCH_ATTR3F( index, x, y, z );
1128 }
1129 else
1130 enum_error();
1131 }
1132
1133 static void GLAPIENTRY
1134 _save_VertexAttrib3fvARB(GLuint index, const GLfloat *v)
1135 {
1136 if (index < MAX_VERTEX_ATTRIBS) {
1137 if (index > 0)
1138 index += VERT_ATTRIB_GENERIC0;
1139 DISPATCH_ATTR3FV( index, v );
1140 }
1141 else
1142 enum_error();
1143 }
1144
1145 static void GLAPIENTRY
1146 _save_VertexAttrib4fARB(GLuint index, GLfloat x, GLfloat y,
1147 GLfloat z, GLfloat w)
1148 {
1149 if (index < MAX_VERTEX_ATTRIBS) {
1150 if (index > 0)
1151 index += VERT_ATTRIB_GENERIC0;
1152 DISPATCH_ATTR4F( index, x, y, z, w );
1153 }
1154 else
1155 enum_error();
1156 }
1157
1158 static void GLAPIENTRY
1159 _save_VertexAttrib4fvARB(GLuint index, const GLfloat *v)
1160 {
1161 if (index < MAX_VERTEX_ATTRIBS) {
1162 if (index > 0)
1163 index += VERT_ATTRIB_GENERIC0;
1164 DISPATCH_ATTR4FV( index, v );
1165 }
1166 else
1167 enum_error();
1168 }
1169
1170
1171 /* Materials:
1172 *
1173 * These are treated as per-vertex attributes, at indices above where
1174 * the NV_vertex_program leaves off. There are a lot of good things
1175 * about treating materials this way.
1176 *
1177 * However: I don't want to double the number of generated functions
1178 * just to cope with this, so I unroll the 'C' varients of CHOOSE and
1179 * ATTRF into this function, and dispense with codegen and
1180 * second-level dispatch.
1181 *
1182 * There is no aliasing of material attributes with other entrypoints.
1183 */
1184 #define MAT_ATTR( A, N, params ) \
1185 do { \
1186 if (tnl->save.attrsz[A] < N) { \
1187 _save_upgrade_vertex( ctx, A, N ); \
1188 tnl->save.have_materials = GL_TRUE; \
1189 } \
1190 \
1191 { \
1192 GLfloat *dest = tnl->save.attrptr[A]; \
1193 if (N>0) dest[0] = params[0]; \
1194 if (N>1) dest[1] = params[1]; \
1195 if (N>2) dest[2] = params[2]; \
1196 if (N>3) dest[3] = params[3]; \
1197 } \
1198 } while (0)
1199
1200
1201 #define MAT( ATTR, N, face, params ) \
1202 do { \
1203 if (face != GL_BACK) \
1204 MAT_ATTR( ATTR, N, params ); /* front */ \
1205 if (face != GL_FRONT) \
1206 MAT_ATTR( ATTR + 1, N, params ); /* back */ \
1207 } while (0)
1208
1209
1210 /* NOTE: Have to remove/deal-with colormaterial crossovers, probably
1211 * later on - in the meantime just store everything.
1212 */
1213 static void GLAPIENTRY _save_Materialfv( GLenum face, GLenum pname,
1214 const GLfloat *params )
1215 {
1216 GET_CURRENT_CONTEXT( ctx );
1217 TNLcontext *tnl = TNL_CONTEXT(ctx);
1218
1219 switch (pname) {
1220 case GL_EMISSION:
1221 MAT( _TNL_ATTRIB_MAT_FRONT_EMISSION, 4, face, params );
1222 break;
1223 case GL_AMBIENT:
1224 MAT( _TNL_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params );
1225 break;
1226 case GL_DIFFUSE:
1227 MAT( _TNL_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params );
1228 break;
1229 case GL_SPECULAR:
1230 MAT( _TNL_ATTRIB_MAT_FRONT_SPECULAR, 4, face, params );
1231 break;
1232 case GL_SHININESS:
1233 MAT( _TNL_ATTRIB_MAT_FRONT_SHININESS, 1, face, params );
1234 break;
1235 case GL_COLOR_INDEXES:
1236 MAT( _TNL_ATTRIB_MAT_FRONT_INDEXES, 3, face, params );
1237 break;
1238 case GL_AMBIENT_AND_DIFFUSE:
1239 MAT( _TNL_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params );
1240 MAT( _TNL_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params );
1241 break;
1242 default:
1243 _mesa_compile_error( ctx, GL_INVALID_ENUM, "glMaterialfv" );
1244 return;
1245 }
1246 }
1247
1248
1249 #define IDX_ATTR( A, IDX ) \
1250 do { \
1251 GET_CURRENT_CONTEXT( ctx ); \
1252 TNLcontext *tnl = TNL_CONTEXT(ctx); \
1253 \
1254 if (tnl->save.attrsz[A] < 1) { \
1255 _save_upgrade_vertex( ctx, A, 1 ); \
1256 } \
1257 \
1258 { \
1259 GLfloat *dest = tnl->save.attrptr[A]; \
1260 dest[0] = IDX; \
1261 } \
1262 } while (0)
1263
1264
1265 static void GLAPIENTRY _save_EdgeFlag( GLboolean b )
1266 {
1267 IDX_ATTR( _TNL_ATTRIB_EDGEFLAG, (GLfloat)b );
1268 }
1269
1270
1271 static void GLAPIENTRY _save_Indexf( GLfloat f )
1272 {
1273 IDX_ATTR( _TNL_ATTRIB_COLOR_INDEX, f );
1274 }
1275
1276 static void GLAPIENTRY _save_Indexfv( const GLfloat *f )
1277 {
1278 IDX_ATTR( _TNL_ATTRIB_COLOR_INDEX, f[0] );
1279 }
1280
1281
1282
1283
1284 /* Cope with EvalCoord/CallList called within a begin/end object:
1285 * -- Flush current buffer
1286 * -- Fallback to opcodes for the rest of the begin/end object.
1287 */
1288 #define FALLBACK(ctx) \
1289 do { \
1290 TNLcontext *tnl = TNL_CONTEXT(ctx); \
1291 \
1292 if (tnl->save.initial_counter != tnl->save.counter || \
1293 tnl->save.prim_count) \
1294 _save_compile_vertex_list( ctx ); \
1295 \
1296 _save_copy_to_current( ctx ); \
1297 _save_reset_vertex( ctx ); \
1298 _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt ); \
1299 ctx->Driver.SaveNeedFlush = 0; \
1300 } while (0)
1301
1302 static void GLAPIENTRY _save_EvalCoord1f( GLfloat u )
1303 {
1304 GET_CURRENT_CONTEXT(ctx);
1305 FALLBACK(ctx);
1306 CALL_EvalCoord1f(ctx->Save, ( u ));
1307 }
1308
1309 static void GLAPIENTRY _save_EvalCoord1fv( const GLfloat *v )
1310 {
1311 GET_CURRENT_CONTEXT(ctx);
1312 FALLBACK(ctx);
1313 CALL_EvalCoord1fv(ctx->Save, ( v ));
1314 }
1315
1316 static void GLAPIENTRY _save_EvalCoord2f( GLfloat u, GLfloat v )
1317 {
1318 GET_CURRENT_CONTEXT(ctx);
1319 FALLBACK(ctx);
1320 CALL_EvalCoord2f(ctx->Save, ( u, v ));
1321 }
1322
1323 static void GLAPIENTRY _save_EvalCoord2fv( const GLfloat *v )
1324 {
1325 GET_CURRENT_CONTEXT(ctx);
1326 FALLBACK(ctx);
1327 CALL_EvalCoord2fv(ctx->Save, ( v ));
1328 }
1329
1330 static void GLAPIENTRY _save_EvalPoint1( GLint i )
1331 {
1332 GET_CURRENT_CONTEXT(ctx);
1333 FALLBACK(ctx);
1334 CALL_EvalPoint1(ctx->Save, ( i ));
1335 }
1336
1337 static void GLAPIENTRY _save_EvalPoint2( GLint i, GLint j )
1338 {
1339 GET_CURRENT_CONTEXT(ctx);
1340 FALLBACK(ctx);
1341 CALL_EvalPoint2(ctx->Save, ( i, j ));
1342 }
1343
1344 static void GLAPIENTRY _save_CallList( GLuint l )
1345 {
1346 GET_CURRENT_CONTEXT(ctx);
1347 FALLBACK(ctx);
1348 CALL_CallList(ctx->Save, ( l ));
1349 }
1350
1351 static void GLAPIENTRY _save_CallLists( GLsizei n, GLenum type, const GLvoid *v )
1352 {
1353 GET_CURRENT_CONTEXT(ctx);
1354 FALLBACK(ctx);
1355 CALL_CallLists(ctx->Save, ( n, type, v ));
1356 }
1357
1358
1359
1360
1361 /**
1362 * Called via ctx->Driver.NotifySaveBegin(ctx, mode) when we get a
1363 * glBegin() call while compiling a display list.
1364 * See save_Begin() in dlist.c
1365 *
1366 * This plugs in our special TNL-related display list functions.
1367 * All subsequent glBegin/glVertex/glEnd()s found while compiling a
1368 * display list will get routed to the functions in this file.
1369 *
1370 * Updating of ctx->Driver.CurrentSavePrimitive is already taken care of.
1371 */
1372 static GLboolean _save_NotifyBegin( GLcontext *ctx, GLenum mode )
1373 {
1374 TNLcontext *tnl = TNL_CONTEXT(ctx);
1375
1376 if (1) {
1377 GLuint i = tnl->save.prim_count++;
1378
1379 assert(i < tnl->save.prim_max);
1380 tnl->save.prim[i].mode = mode | PRIM_BEGIN;
1381 tnl->save.prim[i].start = tnl->save.initial_counter - tnl->save.counter;
1382 tnl->save.prim[i].count = 0;
1383
1384 _mesa_install_save_vtxfmt( ctx, &tnl->save_vtxfmt );
1385 ctx->Driver.SaveNeedFlush = 1;
1386 return GL_TRUE;
1387 }
1388 else
1389 return GL_FALSE;
1390 }
1391
1392
1393
1394 static void GLAPIENTRY _save_End( void )
1395 {
1396 GET_CURRENT_CONTEXT( ctx );
1397 TNLcontext *tnl = TNL_CONTEXT(ctx);
1398 GLint i = tnl->save.prim_count - 1;
1399
1400 ctx->Driver.CurrentSavePrimitive = PRIM_OUTSIDE_BEGIN_END;
1401 if (ctx->ExecuteFlag)
1402 ctx->Driver.CurrentExecPrimitive = PRIM_OUTSIDE_BEGIN_END;
1403
1404 tnl->save.prim[i].mode |= PRIM_END;
1405 tnl->save.prim[i].count = ((tnl->save.initial_counter - tnl->save.counter) -
1406 tnl->save.prim[i].start);
1407
1408 if (i == (GLint) tnl->save.prim_max - 1) {
1409 _save_compile_vertex_list( ctx );
1410 assert(tnl->save.copied.nr == 0);
1411 }
1412
1413 /* Swap out this vertex format while outside begin/end. Any color,
1414 * etc. received between here and the next begin will be compiled
1415 * as opcodes.
1416 */
1417 _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt );
1418 }
1419
1420
1421 /* These are all errors as this vtxfmt is only installed inside
1422 * begin/end pairs.
1423 */
1424 static void GLAPIENTRY _save_DrawElements(GLenum mode, GLsizei count, GLenum type,
1425 const GLvoid *indices)
1426 {
1427 GET_CURRENT_CONTEXT(ctx);
1428 (void) mode; (void) count; (void) type; (void) indices;
1429 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawElements" );
1430 }
1431
1432
1433 static void GLAPIENTRY _save_DrawRangeElements(GLenum mode,
1434 GLuint start, GLuint end,
1435 GLsizei count, GLenum type,
1436 const GLvoid *indices)
1437 {
1438 GET_CURRENT_CONTEXT(ctx);
1439 (void) mode; (void) start; (void) end; (void) count; (void) type; (void) indices;
1440 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawRangeElements" );
1441 }
1442
1443 static void GLAPIENTRY _save_DrawArrays(GLenum mode, GLint start, GLsizei count)
1444 {
1445 GET_CURRENT_CONTEXT(ctx);
1446 (void) mode; (void) start; (void) count;
1447 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glDrawArrays" );
1448 }
1449
1450 static void GLAPIENTRY _save_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 )
1451 {
1452 GET_CURRENT_CONTEXT(ctx);
1453 (void) x1; (void) y1; (void) x2; (void) y2;
1454 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glRectf" );
1455 }
1456
1457 static void GLAPIENTRY _save_EvalMesh1( GLenum mode, GLint i1, GLint i2 )
1458 {
1459 GET_CURRENT_CONTEXT(ctx);
1460 (void) mode; (void) i1; (void) i2;
1461 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh1" );
1462 }
1463
1464 static void GLAPIENTRY _save_EvalMesh2( GLenum mode, GLint i1, GLint i2,
1465 GLint j1, GLint j2 )
1466 {
1467 GET_CURRENT_CONTEXT(ctx);
1468 (void) mode; (void) i1; (void) i2; (void) j1; (void) j2;
1469 _mesa_compile_error( ctx, GL_INVALID_OPERATION, "glEvalMesh2" );
1470 }
1471
1472 /**
1473 * This is only called if someone tries to compile nested glBegin()s
1474 * in their display list.
1475 */
1476 static void GLAPIENTRY _save_Begin( GLenum mode )
1477 {
1478 GET_CURRENT_CONTEXT( ctx );
1479 (void) mode;
1480 _mesa_compile_error(ctx, GL_INVALID_OPERATION,
1481 "glBegin(called inside glBegin/End)");
1482 }
1483
1484
1485 /* Unlike the functions above, these are to be hooked into the vtxfmt
1486 * maintained in ctx->ListState, active when the list is known or
1487 * suspected to be outside any begin/end primitive.
1488 */
1489 static void GLAPIENTRY _save_OBE_Rectf( GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2 )
1490 {
1491 GET_CURRENT_CONTEXT(ctx);
1492 _save_NotifyBegin( ctx, GL_QUADS | PRIM_WEAK );
1493 CALL_Vertex2f(GET_DISPATCH(), ( x1, y1 ));
1494 CALL_Vertex2f(GET_DISPATCH(), ( x2, y1 ));
1495 CALL_Vertex2f(GET_DISPATCH(), ( x2, y2 ));
1496 CALL_Vertex2f(GET_DISPATCH(), ( x1, y2 ));
1497 CALL_End(GET_DISPATCH(), ());
1498 }
1499
1500
1501 static void GLAPIENTRY _save_OBE_DrawArrays(GLenum mode, GLint start, GLsizei count)
1502 {
1503 GET_CURRENT_CONTEXT(ctx);
1504 GLint i;
1505
1506 if (!_mesa_validate_DrawArrays( ctx, mode, start, count ))
1507 return;
1508
1509 _ae_map_vbos( ctx );
1510
1511 _save_NotifyBegin( ctx, mode | PRIM_WEAK );
1512 for (i = 0; i < count; i++)
1513 CALL_ArrayElement(GET_DISPATCH(), (start + i));
1514 CALL_End(GET_DISPATCH(), ());
1515
1516 _ae_unmap_vbos( ctx );
1517 }
1518
1519
1520 static void GLAPIENTRY _save_OBE_DrawElements(GLenum mode, GLsizei count, GLenum type,
1521 const GLvoid *indices)
1522 {
1523 GET_CURRENT_CONTEXT(ctx);
1524 GLint i;
1525
1526 if (!_mesa_validate_DrawElements( ctx, mode, count, type, indices ))
1527 return;
1528
1529 _ae_map_vbos( ctx );
1530
1531 _save_NotifyBegin( ctx, mode | PRIM_WEAK );
1532
1533 switch (type) {
1534 case GL_UNSIGNED_BYTE:
1535 for (i = 0 ; i < count ; i++)
1536 CALL_ArrayElement(GET_DISPATCH(), ( ((GLubyte *)indices)[i] ));
1537 break;
1538 case GL_UNSIGNED_SHORT:
1539 for (i = 0 ; i < count ; i++)
1540 CALL_ArrayElement(GET_DISPATCH(), ( ((GLushort *)indices)[i] ));
1541 break;
1542 case GL_UNSIGNED_INT:
1543 for (i = 0 ; i < count ; i++)
1544 CALL_ArrayElement(GET_DISPATCH(), ( ((GLuint *)indices)[i] ));
1545 break;
1546 default:
1547 _mesa_error( ctx, GL_INVALID_ENUM, "glDrawElements(type)" );
1548 break;
1549 }
1550
1551 CALL_End(GET_DISPATCH(), ());
1552
1553 _ae_unmap_vbos( ctx );
1554 }
1555
1556 static void GLAPIENTRY _save_OBE_DrawRangeElements(GLenum mode,
1557 GLuint start, GLuint end,
1558 GLsizei count, GLenum type,
1559 const GLvoid *indices)
1560 {
1561 GET_CURRENT_CONTEXT(ctx);
1562 if (_mesa_validate_DrawRangeElements( ctx, mode,
1563 start, end,
1564 count, type, indices ))
1565 _save_OBE_DrawElements( mode, count, type, indices );
1566 }
1567
1568
1569
1570
1571
1572 static void _save_vtxfmt_init( GLcontext *ctx )
1573 {
1574 TNLcontext *tnl = TNL_CONTEXT(ctx);
1575 GLvertexformat *vfmt = &tnl->save_vtxfmt;
1576
1577 vfmt->ArrayElement = _ae_loopback_array_elt; /* generic helper */
1578 vfmt->Begin = _save_Begin;
1579 vfmt->Color3f = _save_Color3f;
1580 vfmt->Color3fv = _save_Color3fv;
1581 vfmt->Color4f = _save_Color4f;
1582 vfmt->Color4fv = _save_Color4fv;
1583 vfmt->EdgeFlag = _save_EdgeFlag;
1584 vfmt->End = _save_End;
1585 vfmt->FogCoordfEXT = _save_FogCoordfEXT;
1586 vfmt->FogCoordfvEXT = _save_FogCoordfvEXT;
1587 vfmt->Indexf = _save_Indexf;
1588 vfmt->Indexfv = _save_Indexfv;
1589 vfmt->Materialfv = _save_Materialfv;
1590 vfmt->MultiTexCoord1fARB = _save_MultiTexCoord1f;
1591 vfmt->MultiTexCoord1fvARB = _save_MultiTexCoord1fv;
1592 vfmt->MultiTexCoord2fARB = _save_MultiTexCoord2f;
1593 vfmt->MultiTexCoord2fvARB = _save_MultiTexCoord2fv;
1594 vfmt->MultiTexCoord3fARB = _save_MultiTexCoord3f;
1595 vfmt->MultiTexCoord3fvARB = _save_MultiTexCoord3fv;
1596 vfmt->MultiTexCoord4fARB = _save_MultiTexCoord4f;
1597 vfmt->MultiTexCoord4fvARB = _save_MultiTexCoord4fv;
1598 vfmt->Normal3f = _save_Normal3f;
1599 vfmt->Normal3fv = _save_Normal3fv;
1600 vfmt->SecondaryColor3fEXT = _save_SecondaryColor3fEXT;
1601 vfmt->SecondaryColor3fvEXT = _save_SecondaryColor3fvEXT;
1602 vfmt->TexCoord1f = _save_TexCoord1f;
1603 vfmt->TexCoord1fv = _save_TexCoord1fv;
1604 vfmt->TexCoord2f = _save_TexCoord2f;
1605 vfmt->TexCoord2fv = _save_TexCoord2fv;
1606 vfmt->TexCoord3f = _save_TexCoord3f;
1607 vfmt->TexCoord3fv = _save_TexCoord3fv;
1608 vfmt->TexCoord4f = _save_TexCoord4f;
1609 vfmt->TexCoord4fv = _save_TexCoord4fv;
1610 vfmt->Vertex2f = _save_Vertex2f;
1611 vfmt->Vertex2fv = _save_Vertex2fv;
1612 vfmt->Vertex3f = _save_Vertex3f;
1613 vfmt->Vertex3fv = _save_Vertex3fv;
1614 vfmt->Vertex4f = _save_Vertex4f;
1615 vfmt->Vertex4fv = _save_Vertex4fv;
1616 vfmt->VertexAttrib1fNV = _save_VertexAttrib1fNV;
1617 vfmt->VertexAttrib1fvNV = _save_VertexAttrib1fvNV;
1618 vfmt->VertexAttrib2fNV = _save_VertexAttrib2fNV;
1619 vfmt->VertexAttrib2fvNV = _save_VertexAttrib2fvNV;
1620 vfmt->VertexAttrib3fNV = _save_VertexAttrib3fNV;
1621 vfmt->VertexAttrib3fvNV = _save_VertexAttrib3fvNV;
1622 vfmt->VertexAttrib4fNV = _save_VertexAttrib4fNV;
1623 vfmt->VertexAttrib4fvNV = _save_VertexAttrib4fvNV;
1624 vfmt->VertexAttrib1fARB = _save_VertexAttrib1fARB;
1625 vfmt->VertexAttrib1fvARB = _save_VertexAttrib1fvARB;
1626 vfmt->VertexAttrib2fARB = _save_VertexAttrib2fARB;
1627 vfmt->VertexAttrib2fvARB = _save_VertexAttrib2fvARB;
1628 vfmt->VertexAttrib3fARB = _save_VertexAttrib3fARB;
1629 vfmt->VertexAttrib3fvARB = _save_VertexAttrib3fvARB;
1630 vfmt->VertexAttrib4fARB = _save_VertexAttrib4fARB;
1631 vfmt->VertexAttrib4fvARB = _save_VertexAttrib4fvARB;
1632
1633 /* This will all require us to fallback to saving the list as opcodes:
1634 */
1635 vfmt->CallList = _save_CallList; /* inside begin/end */
1636 vfmt->CallLists = _save_CallLists; /* inside begin/end */
1637 vfmt->EvalCoord1f = _save_EvalCoord1f;
1638 vfmt->EvalCoord1fv = _save_EvalCoord1fv;
1639 vfmt->EvalCoord2f = _save_EvalCoord2f;
1640 vfmt->EvalCoord2fv = _save_EvalCoord2fv;
1641 vfmt->EvalPoint1 = _save_EvalPoint1;
1642 vfmt->EvalPoint2 = _save_EvalPoint2;
1643
1644 /* These are all errors as we at least know we are in some sort of
1645 * begin/end pair:
1646 */
1647 vfmt->EvalMesh1 = _save_EvalMesh1;
1648 vfmt->EvalMesh2 = _save_EvalMesh2;
1649 vfmt->Begin = _save_Begin;
1650 vfmt->Rectf = _save_Rectf;
1651 vfmt->DrawArrays = _save_DrawArrays;
1652 vfmt->DrawElements = _save_DrawElements;
1653 vfmt->DrawRangeElements = _save_DrawRangeElements;
1654
1655 }
1656
1657
1658 void _tnl_SaveFlushVertices( GLcontext *ctx )
1659 {
1660 TNLcontext *tnl = TNL_CONTEXT(ctx);
1661
1662 /* Noop when we are actually active:
1663 */
1664 if (ctx->Driver.CurrentSavePrimitive == PRIM_INSIDE_UNKNOWN_PRIM ||
1665 ctx->Driver.CurrentSavePrimitive <= GL_POLYGON)
1666 return;
1667
1668 if (tnl->save.initial_counter != tnl->save.counter ||
1669 tnl->save.prim_count)
1670 _save_compile_vertex_list( ctx );
1671
1672 _save_copy_to_current( ctx );
1673 _save_reset_vertex( ctx );
1674 ctx->Driver.SaveNeedFlush = 0;
1675 }
1676
1677 void _tnl_NewList( GLcontext *ctx, GLuint list, GLenum mode )
1678 {
1679 TNLcontext *tnl = TNL_CONTEXT(ctx);
1680
1681 (void) list; (void) mode;
1682
1683 if (!tnl->save.prim_store)
1684 tnl->save.prim_store = alloc_prim_store( ctx );
1685
1686 if (!tnl->save.vertex_store) {
1687 tnl->save.vertex_store = alloc_vertex_store( ctx );
1688 tnl->save.vbptr = tnl->save.vertex_store->buffer;
1689 }
1690
1691 _save_reset_vertex( ctx );
1692 ctx->Driver.SaveNeedFlush = 0;
1693 }
1694
1695 void _tnl_EndList( GLcontext *ctx )
1696 {
1697 (void) ctx;
1698 assert(TNL_CONTEXT(ctx)->save.vertex_size == 0);
1699 }
1700
1701 void _tnl_BeginCallList( GLcontext *ctx, struct mesa_display_list *dlist )
1702 {
1703 TNLcontext *tnl = TNL_CONTEXT(ctx);
1704 tnl->save.replay_flags |= dlist->flags;
1705 tnl->save.replay_flags |= tnl->LoopbackDListCassettes;
1706 }
1707
1708 void _tnl_EndCallList( GLcontext *ctx )
1709 {
1710 TNLcontext *tnl = TNL_CONTEXT(ctx);
1711
1712 if (ctx->ListState.CallDepth == 1)
1713 tnl->save.replay_flags = 0;
1714 }
1715
1716
1717 static void _tnl_destroy_vertex_list( GLcontext *ctx, void *data )
1718 {
1719 struct tnl_vertex_list *node = (struct tnl_vertex_list *)data;
1720 (void) ctx;
1721
1722 if ( --node->vertex_store->refcount == 0 )
1723 FREE( node->vertex_store );
1724
1725 if ( --node->prim_store->refcount == 0 )
1726 FREE( node->prim_store );
1727
1728 if ( node->normal_lengths )
1729 FREE( node->normal_lengths );
1730 }
1731
1732
1733 static void _tnl_print_vertex_list( GLcontext *ctx, void *data )
1734 {
1735 struct tnl_vertex_list *node = (struct tnl_vertex_list *)data;
1736 GLuint i;
1737 (void) ctx;
1738
1739 _mesa_debug(NULL, "TNL-VERTEX-LIST, %u vertices %d primitives, %d vertsize\n",
1740 node->count,
1741 node->prim_count,
1742 node->vertex_size);
1743
1744 for (i = 0 ; i < node->prim_count ; i++) {
1745 struct tnl_prim *prim = &node->prim[i];
1746 _mesa_debug(NULL, " prim %d: %s %d..%d %s %s\n",
1747 i,
1748 _mesa_lookup_enum_by_nr(prim->mode & PRIM_MODE_MASK),
1749 prim->start,
1750 prim->start + prim->count,
1751 (prim->mode & PRIM_BEGIN) ? "BEGIN" : "(wrap)",
1752 (prim->mode & PRIM_END) ? "END" : "(wrap)");
1753 }
1754 }
1755
1756
1757 static void _save_current_init( GLcontext *ctx )
1758 {
1759 TNLcontext *tnl = TNL_CONTEXT(ctx);
1760 GLint i;
1761
1762 for (i = 0; i < _TNL_ATTRIB_MAT_FRONT_AMBIENT; i++) {
1763 ASSERT(i < VERT_ATTRIB_MAX);
1764 tnl->save.currentsz[i] = &ctx->ListState.ActiveAttribSize[i];
1765 tnl->save.current[i] = ctx->ListState.CurrentAttrib[i];
1766 }
1767
1768 for (i = _TNL_FIRST_MAT; i <= _TNL_LAST_MAT; i++) {
1769 const GLuint j = i - _TNL_FIRST_MAT;
1770 ASSERT(j < MAT_ATTRIB_MAX);
1771 tnl->save.currentsz[i] = &ctx->ListState.ActiveMaterialSize[j];
1772 tnl->save.current[i] = ctx->ListState.CurrentMaterial[j];
1773 }
1774
1775 tnl->save.currentsz[_TNL_ATTRIB_EDGEFLAG] = &ctx->ListState.ActiveEdgeFlag;
1776 tnl->save.current[_TNL_ATTRIB_EDGEFLAG] = &tnl->save.CurrentFloatEdgeFlag;
1777 }
1778
1779 /**
1780 * Initialize the display list compiler
1781 */
1782 void _tnl_save_init( GLcontext *ctx )
1783 {
1784 TNLcontext *tnl = TNL_CONTEXT(ctx);
1785 struct tnl_vertex_arrays *tmp = &tnl->save_inputs;
1786 GLuint i;
1787
1788
1789 for (i = 0; i < _TNL_ATTRIB_MAX; i++)
1790 _mesa_vector4f_init( &tmp->Attribs[i], 0, NULL);
1791
1792 tnl->save.opcode_vertex_list =
1793 _mesa_alloc_opcode( ctx,
1794 sizeof(struct tnl_vertex_list),
1795 _tnl_playback_vertex_list,
1796 _tnl_destroy_vertex_list,
1797 _tnl_print_vertex_list );
1798
1799 ctx->Driver.NotifySaveBegin = _save_NotifyBegin;
1800
1801 _save_vtxfmt_init( ctx );
1802 _save_current_init( ctx );
1803
1804 /* Hook our array functions into the outside-begin-end vtxfmt in
1805 * ctx->ListState.
1806 */
1807 ctx->ListState.ListVtxfmt.Rectf = _save_OBE_Rectf;
1808 ctx->ListState.ListVtxfmt.DrawArrays = _save_OBE_DrawArrays;
1809 ctx->ListState.ListVtxfmt.DrawElements = _save_OBE_DrawElements;
1810 ctx->ListState.ListVtxfmt.DrawRangeElements = _save_OBE_DrawRangeElements;
1811 _mesa_install_save_vtxfmt( ctx, &ctx->ListState.ListVtxfmt );
1812 }
1813
1814
1815 /**
1816 * Deallocate the immediate-mode buffer for the given context, if
1817 * its reference count goes to zero.
1818 */
1819 void _tnl_save_destroy( GLcontext *ctx )
1820 {
1821 TNLcontext *tnl = TNL_CONTEXT(ctx);
1822
1823 /* Decrement the refcounts. References may still be held by
1824 * display lists yet to be destroyed, so it may not yet be time to
1825 * free these items.
1826 */
1827 if (tnl->save.prim_store &&
1828 --tnl->save.prim_store->refcount == 0 )
1829 FREE( tnl->save.prim_store );
1830
1831 if (tnl->save.vertex_store &&
1832 --tnl->save.vertex_store->refcount == 0 )
1833 FREE( tnl->save.vertex_store );
1834 }