fix stores to vertex state program registers
[mesa.git] / src / mesa / tnl / t_vtx_api.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 "glheader.h"
35 #include "context.h"
36 #include "macros.h"
37 #include "vtxfmt.h"
38 #include "dlist.h"
39 #include "state.h"
40 #include "light.h"
41 #include "api_arrayelt.h"
42 #include "api_noop.h"
43 #include "t_vtx_api.h"
44
45
46 static void init_attrfv( TNLcontext *tnl );
47
48
49 /* Close off the last primitive, execute the buffer, restart the
50 * primitive.
51 */
52 static void _tnl_wrap_buffers( GLcontext *ctx )
53 {
54 TNLcontext *tnl = TNL_CONTEXT(ctx);
55
56
57 if (tnl->vtx.prim_count == 0) {
58 tnl->vtx.copied.nr = 0;
59 tnl->vtx.counter = tnl->vtx.initial_counter;
60 tnl->vtx.vbptr = tnl->vtx.buffer;
61 }
62 else {
63 GLuint last_prim = tnl->vtx.prim[tnl->vtx.prim_count-1].mode;
64 GLuint last_count = tnl->vtx.prim[tnl->vtx.prim_count-1].count;
65
66 if (ctx->Driver.CurrentExecPrimitive != GL_POLYGON+1) {
67 GLint i = tnl->vtx.prim_count - 1;
68 assert(i >= 0);
69 tnl->vtx.prim[i].count = ((tnl->vtx.initial_counter -
70 tnl->vtx.counter) -
71 tnl->vtx.prim[i].start);
72 }
73
74 /* Execute the buffer and save copied vertices.
75 */
76 if (tnl->vtx.counter != tnl->vtx.initial_counter)
77 _tnl_flush_vtx( ctx );
78 else {
79 tnl->vtx.prim_count = 0;
80 tnl->vtx.copied.nr = 0;
81 }
82
83 /* Emit a glBegin to start the new list.
84 */
85 assert(tnl->vtx.prim_count == 0);
86
87 if (ctx->Driver.CurrentExecPrimitive != GL_POLYGON+1) {
88 tnl->vtx.prim[0].mode = ctx->Driver.CurrentExecPrimitive;
89 tnl->vtx.prim[0].start = 0;
90 tnl->vtx.prim[0].count = 0;
91 tnl->vtx.prim_count++;
92
93 if (tnl->vtx.copied.nr == last_count)
94 tnl->vtx.prim[0].mode |= last_prim & PRIM_BEGIN;
95 }
96 }
97 }
98
99
100 static void _tnl_wrap_filled_vertex( GLcontext *ctx )
101 {
102 TNLcontext *tnl = TNL_CONTEXT(ctx);
103 GLfloat *data = tnl->vtx.copied.buffer;
104 GLuint i;
105
106 /* Run pipeline on current vertices, copy wrapped vertices
107 * to tnl->copied.
108 */
109 _tnl_wrap_buffers( ctx );
110
111 /* Copy stored stored vertices to start of new list.
112 */
113 assert(tnl->vtx.counter > tnl->vtx.copied.nr);
114
115 for (i = 0 ; i < tnl->vtx.copied.nr ; i++) {
116 _mesa_memcpy( tnl->vtx.vbptr, data,
117 tnl->vtx.vertex_size * sizeof(GLfloat));
118 tnl->vtx.vbptr += tnl->vtx.vertex_size;
119 data += tnl->vtx.vertex_size;
120 tnl->vtx.counter--;
121 }
122
123 tnl->vtx.copied.nr = 0;
124 }
125
126
127 /*
128 * Copy the active vertex's values to the ctx->Current fields.
129 */
130 static void _tnl_copy_to_current( GLcontext *ctx )
131 {
132 TNLcontext *tnl = TNL_CONTEXT(ctx);
133 GLuint i;
134
135 for (i = _TNL_ATTRIB_POS+1 ; i <= _TNL_ATTRIB_INDEX ; i++)
136 if (tnl->vtx.attrsz[i]) {
137 /* Note: the tnl->vtx.current[i] pointers points to
138 * the ctx->Current fields. The first 16 or so, anyway.
139 */
140 ASSIGN_4V( tnl->vtx.current[i], 0, 0, 0, 1 );
141 COPY_SZ_4V(tnl->vtx.current[i],
142 tnl->vtx.attrsz[i],
143 tnl->vtx.attrptr[i]);
144 }
145
146 /* Edgeflag requires special treatment:
147 */
148 if (tnl->vtx.attrsz[_TNL_ATTRIB_EDGEFLAG])
149 ctx->Current.EdgeFlag =
150 (tnl->vtx.attrptr[_TNL_ATTRIB_EDGEFLAG][0] == 1.0);
151
152
153 /* Colormaterial -- this kindof sucks.
154 */
155 if (ctx->Light.ColorMaterialEnabled) {
156 _mesa_update_color_material(ctx, ctx->Current.Attrib[VERT_ATTRIB_COLOR0]);
157 }
158
159 if (tnl->vtx.have_materials) {
160 tnl->Driver.NotifyMaterialChange( ctx );
161 }
162
163 ctx->Driver.NeedFlush &= ~FLUSH_UPDATE_CURRENT;
164 }
165
166
167 static void _tnl_copy_from_current( GLcontext *ctx )
168 {
169 TNLcontext *tnl = TNL_CONTEXT(ctx);
170 GLint i;
171
172 for (i = _TNL_ATTRIB_POS+1 ; i <= _TNL_ATTRIB_INDEX ; i++)
173 switch (tnl->vtx.attrsz[i]) {
174 case 4: tnl->vtx.attrptr[i][3] = tnl->vtx.current[i][3];
175 case 3: tnl->vtx.attrptr[i][2] = tnl->vtx.current[i][2];
176 case 2: tnl->vtx.attrptr[i][1] = tnl->vtx.current[i][1];
177 case 1: tnl->vtx.attrptr[i][0] = tnl->vtx.current[i][0];
178 break;
179 }
180
181 /* Edgeflag requires special treatment:
182 */
183 if (tnl->vtx.attrsz[_TNL_ATTRIB_EDGEFLAG])
184 tnl->vtx.attrptr[_TNL_ATTRIB_EDGEFLAG][0] =
185 (GLfloat)ctx->Current.EdgeFlag;
186
187
188 ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT;
189 }
190
191
192 /* Flush existing data, set new attrib size, replay copied vertices.
193 */
194 static void _tnl_wrap_upgrade_vertex( GLcontext *ctx,
195 GLuint attr,
196 GLuint newsz )
197 {
198 TNLcontext *tnl = TNL_CONTEXT(ctx);
199 GLuint oldsz;
200 GLuint i;
201 GLfloat *tmp;
202 GLint lastcount = tnl->vtx.initial_counter - tnl->vtx.counter;
203
204
205 /* Run pipeline on current vertices, copy wrapped vertices
206 * to tnl->vtx.copied.
207 */
208 _tnl_wrap_buffers( ctx );
209
210
211 /* Do a COPY_TO_CURRENT to ensure back-copying works for the case
212 * when the attribute already exists in the vertex and is having
213 * its size increased.
214 */
215 _tnl_copy_to_current( ctx );
216
217
218 /* Heuristic: Attempt to isolate attributes received outside
219 * begin/end so that they don't bloat the vertices.
220 */
221 if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END &&
222 tnl->vtx.attrsz[attr] == 0
223 && lastcount > 8
224 ) {
225 init_attrfv( tnl );
226 }
227
228 /* Fix up sizes:
229 */
230 oldsz = tnl->vtx.attrsz[attr];
231 tnl->vtx.attrsz[attr] = newsz;
232
233 tnl->vtx.vertex_size += newsz - oldsz;
234 tnl->vtx.counter = MIN2( VERT_BUFFER_SIZE / tnl->vtx.vertex_size,
235 ctx->Const.MaxArrayLockSize );
236 tnl->vtx.initial_counter = tnl->vtx.counter;
237 tnl->vtx.vbptr = tnl->vtx.buffer;
238
239
240 /* Recalculate all the attrptr[] values
241 */
242 for (i = 0, tmp = tnl->vtx.vertex ; i < _TNL_ATTRIB_MAX ; i++) {
243 if (tnl->vtx.attrsz[i]) {
244 tnl->vtx.attrptr[i] = tmp;
245 tmp += tnl->vtx.attrsz[i];
246 }
247 else
248 tnl->vtx.attrptr[i] = 0; /* will not be dereferenced */
249 }
250
251 /* Copy from current to repopulate the vertex with correct values.
252 */
253 _tnl_copy_from_current( ctx );
254
255 /* Replay stored vertices to translate them
256 * to new format here.
257 *
258 * -- No need to replay - just copy piecewise
259 */
260 if (tnl->vtx.copied.nr)
261 {
262 GLfloat *data = tnl->vtx.copied.buffer;
263 GLfloat *dest = tnl->vtx.buffer;
264 GLuint j;
265
266 for (i = 0 ; i < tnl->vtx.copied.nr ; i++) {
267 for (j = 0 ; j < _TNL_ATTRIB_MAX ; j++) {
268 if (tnl->vtx.attrsz[j]) {
269 if (j == attr) {
270 COPY_SZ_4V( dest, newsz, tnl->vtx.current[j] );
271 COPY_SZ_4V( dest, oldsz, data );
272 data += oldsz;
273 dest += newsz;
274 }
275 else {
276 GLuint sz = tnl->vtx.attrsz[j];
277 COPY_SZ_4V( dest, sz, data );
278 dest += sz;
279 data += sz;
280 }
281 }
282 }
283 }
284
285 tnl->vtx.vbptr = dest;
286 tnl->vtx.counter -= tnl->vtx.copied.nr;
287 tnl->vtx.copied.nr = 0;
288 }
289 }
290
291
292 static void _tnl_fixup_vertex( GLcontext *ctx, GLuint attr, GLuint sz )
293 {
294 TNLcontext *tnl = TNL_CONTEXT(ctx);
295 static const GLfloat id[4] = { 0, 0, 0, 1 };
296 int i;
297
298 if (tnl->vtx.attrsz[attr] < sz) {
299 /* New size is larger. Need to flush existing vertices and get
300 * an enlarged vertex format.
301 */
302 _tnl_wrap_upgrade_vertex( ctx, attr, sz );
303 }
304 else if (tnl->vtx.attrsz[attr] > sz) {
305 /* New size is smaller - just need to fill in some
306 * zeros. Don't need to flush or wrap.
307 */
308 for (i = sz ; i <= tnl->vtx.attrsz[attr] ; i++)
309 tnl->vtx.attrptr[attr][i-1] = id[i-1];
310 }
311 }
312
313
314
315
316 /* Helper function for 'CHOOSE' macro. Do what's necessary when an
317 * entrypoint is called for the first time.
318 */
319 static void do_choose( GLuint attr, GLuint sz,
320 void (*fallback_attr_func)( const GLfloat *),
321 void (*choose1)( const GLfloat *),
322 void (*choose2)( const GLfloat *),
323 void (*choose3)( const GLfloat *),
324 void (*choose4)( const GLfloat *),
325 const GLfloat *v )
326 {
327 GET_CURRENT_CONTEXT( ctx );
328 TNLcontext *tnl = TNL_CONTEXT(ctx);
329
330 if (tnl->vtx.attrsz[attr] != sz)
331 _tnl_fixup_vertex( ctx, attr, sz );
332
333 /* Does this belong here? Necessitates resetting vtxfmt on each
334 * flush (otherwise flags won't get reset afterwards).
335 */
336 if (attr == 0)
337 ctx->Driver.NeedFlush |= FLUSH_STORED_VERTICES;
338 else
339 ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT;
340
341 /* Reset any active pointers for this attribute
342 */
343 tnl->vtx.tabfv[attr][0] = choose1;
344 tnl->vtx.tabfv[attr][1] = choose2;
345 tnl->vtx.tabfv[attr][2] = choose3;
346 tnl->vtx.tabfv[attr][3] = choose4;
347
348 /* Update the secondary dispatch table with the new function
349 */
350 tnl->vtx.tabfv[attr][sz-1] = fallback_attr_func;
351
352 (*fallback_attr_func)(v);
353 }
354
355
356 /* Versions of all the entrypoints for situations where codegen isn't
357 * available.
358 *
359 * Note: Only one size for each attribute may be active at once.
360 * Eg. if Color3f is installed/active, then Color4f may not be, even
361 * if the vertex actually contains 4 color coordinates. This is
362 * because the 3f version won't otherwise set color[3] to 1.0 -- this
363 * is the job of the chooser function when switching between Color4f
364 * and Color3f.
365 */
366 #define ATTRFV( ATTR, N ) \
367 static void choose_##ATTR##_##N( const GLfloat *v ); \
368 \
369 static void attrib_##ATTR##_##N( const GLfloat *v ) \
370 { \
371 GET_CURRENT_CONTEXT( ctx ); \
372 TNLcontext *tnl = TNL_CONTEXT(ctx); \
373 \
374 if ((ATTR) == 0) { \
375 GLuint i; \
376 \
377 if (N>0) tnl->vtx.vbptr[0] = v[0]; \
378 if (N>1) tnl->vtx.vbptr[1] = v[1]; \
379 if (N>2) tnl->vtx.vbptr[2] = v[2]; \
380 if (N>3) tnl->vtx.vbptr[3] = v[3]; \
381 \
382 for (i = N; i < tnl->vtx.vertex_size; i++) \
383 tnl->vtx.vbptr[i] = tnl->vtx.vertex[i]; \
384 \
385 tnl->vtx.vbptr += tnl->vtx.vertex_size; \
386 \
387 if (--tnl->vtx.counter == 0) \
388 _tnl_wrap_filled_vertex( ctx ); \
389 } \
390 else { \
391 GLfloat *dest = tnl->vtx.attrptr[ATTR]; \
392 if (N>0) dest[0] = v[0]; \
393 if (N>1) dest[1] = v[1]; \
394 if (N>2) dest[2] = v[2]; \
395 if (N>3) dest[3] = v[3]; \
396 } \
397 }
398
399 #define CHOOSE( ATTR, N ) \
400 static void choose_##ATTR##_##N( const GLfloat *v ) \
401 { \
402 do_choose(ATTR, N, \
403 attrib_##ATTR##_##N, \
404 choose_##ATTR##_1, \
405 choose_##ATTR##_2, \
406 choose_##ATTR##_3, \
407 choose_##ATTR##_4, \
408 v ); \
409 }
410
411 #define INIT(ATTR) \
412 static void init_##ATTR( TNLcontext *tnl ) \
413 { \
414 tnl->vtx.tabfv[ATTR][0] = choose_##ATTR##_1; \
415 tnl->vtx.tabfv[ATTR][1] = choose_##ATTR##_2; \
416 tnl->vtx.tabfv[ATTR][2] = choose_##ATTR##_3; \
417 tnl->vtx.tabfv[ATTR][3] = choose_##ATTR##_4; \
418 }
419
420
421 #define ATTRS( ATTRIB ) \
422 ATTRFV( ATTRIB, 1 ) \
423 ATTRFV( ATTRIB, 2 ) \
424 ATTRFV( ATTRIB, 3 ) \
425 ATTRFV( ATTRIB, 4 ) \
426 CHOOSE( ATTRIB, 1 ) \
427 CHOOSE( ATTRIB, 2 ) \
428 CHOOSE( ATTRIB, 3 ) \
429 CHOOSE( ATTRIB, 4 ) \
430 INIT( ATTRIB ) \
431
432
433 /* Generate a lot of functions. These are the actual worker
434 * functions, which are equivalent to those generated via codegen
435 * elsewhere.
436 */
437 ATTRS( 0 )
438 ATTRS( 1 )
439 ATTRS( 2 )
440 ATTRS( 3 )
441 ATTRS( 4 )
442 ATTRS( 5 )
443 ATTRS( 6 )
444 ATTRS( 7 )
445 ATTRS( 8 )
446 ATTRS( 9 )
447 ATTRS( 10 )
448 ATTRS( 11 )
449 ATTRS( 12 )
450 ATTRS( 13 )
451 ATTRS( 14 )
452 ATTRS( 15 )
453
454 static void init_attrfv( TNLcontext *tnl )
455 {
456 if (tnl->vtx.vertex_size) {
457 GLuint i;
458
459 init_0( tnl );
460 init_1( tnl );
461 init_2( tnl );
462 init_3( tnl );
463 init_4( tnl );
464 init_5( tnl );
465 init_6( tnl );
466 init_7( tnl );
467 init_8( tnl );
468 init_9( tnl );
469 init_10( tnl );
470 init_11( tnl );
471 init_12( tnl );
472 init_13( tnl );
473 init_14( tnl );
474 init_15( tnl );
475
476 for (i = 0 ; i < _TNL_ATTRIB_MAX ; i++)
477 tnl->vtx.attrsz[i] = 0;
478
479 tnl->vtx.vertex_size = 0;
480 tnl->vtx.have_materials = 0;
481 }
482 }
483
484 /* These can be made efficient with codegen. Further, by adding more
485 * logic to do_choose(), the double-dispatch for legacy entrypoints
486 * like glVertex3f() can be removed.
487 */
488 #define DISPATCH_ATTRFV( ATTR, COUNT, P ) \
489 do { \
490 GET_CURRENT_CONTEXT( ctx ); \
491 TNLcontext *tnl = TNL_CONTEXT(ctx); \
492 tnl->vtx.tabfv[ATTR][COUNT-1]( P ); \
493 } while (0)
494
495 #define DISPATCH_ATTR1FV( ATTR, V ) DISPATCH_ATTRFV( ATTR, 1, V )
496 #define DISPATCH_ATTR2FV( ATTR, V ) DISPATCH_ATTRFV( ATTR, 2, V )
497 #define DISPATCH_ATTR3FV( ATTR, V ) DISPATCH_ATTRFV( ATTR, 3, V )
498 #define DISPATCH_ATTR4FV( ATTR, V ) DISPATCH_ATTRFV( ATTR, 4, V )
499
500 #define DISPATCH_ATTR1F( ATTR, S ) DISPATCH_ATTRFV( ATTR, 1, &(S) )
501
502 #define DISPATCH_ATTR2F( ATTR, S,T ) \
503 do { \
504 GLfloat v[2]; \
505 v[0] = S; v[1] = T; \
506 DISPATCH_ATTR2FV( ATTR, v ); \
507 } while (0)
508 #define DISPATCH_ATTR3F( ATTR, S,T,R ) \
509 do { \
510 GLfloat v[3]; \
511 v[0] = S; v[1] = T; v[2] = R; \
512 DISPATCH_ATTR3FV( ATTR, v ); \
513 } while (0)
514 #define DISPATCH_ATTR4F( ATTR, S,T,R,Q ) \
515 do { \
516 GLfloat v[4]; \
517 v[0] = S; v[1] = T; v[2] = R; v[3] = Q; \
518 DISPATCH_ATTR4FV( ATTR, v ); \
519 } while (0)
520
521
522 static void enum_error( void )
523 {
524 GET_CURRENT_CONTEXT( ctx );
525 _mesa_error( ctx, GL_INVALID_ENUM, "glVertexAttrib" );
526 }
527
528 static void GLAPIENTRY _tnl_Vertex2f( GLfloat x, GLfloat y )
529 {
530 DISPATCH_ATTR2F( _TNL_ATTRIB_POS, x, y );
531 }
532
533 static void GLAPIENTRY _tnl_Vertex2fv( const GLfloat *v )
534 {
535 DISPATCH_ATTR2FV( _TNL_ATTRIB_POS, v );
536 }
537
538 static void GLAPIENTRY _tnl_Vertex3f( GLfloat x, GLfloat y, GLfloat z )
539 {
540 DISPATCH_ATTR3F( _TNL_ATTRIB_POS, x, y, z );
541 }
542
543 static void GLAPIENTRY _tnl_Vertex3fv( const GLfloat *v )
544 {
545 DISPATCH_ATTR3FV( _TNL_ATTRIB_POS, v );
546 }
547
548 static void GLAPIENTRY _tnl_Vertex4f( GLfloat x, GLfloat y, GLfloat z, GLfloat w )
549 {
550 DISPATCH_ATTR4F( _TNL_ATTRIB_POS, x, y, z, w );
551 }
552
553 static void GLAPIENTRY _tnl_Vertex4fv( const GLfloat *v )
554 {
555 DISPATCH_ATTR4FV( _TNL_ATTRIB_POS, v );
556 }
557
558 static void GLAPIENTRY _tnl_TexCoord1f( GLfloat x )
559 {
560 DISPATCH_ATTR1F( _TNL_ATTRIB_TEX0, x );
561 }
562
563 static void GLAPIENTRY _tnl_TexCoord1fv( const GLfloat *v )
564 {
565 DISPATCH_ATTR1FV( _TNL_ATTRIB_TEX0, v );
566 }
567
568 static void GLAPIENTRY _tnl_TexCoord2f( GLfloat x, GLfloat y )
569 {
570 DISPATCH_ATTR2F( _TNL_ATTRIB_TEX0, x, y );
571 }
572
573 static void GLAPIENTRY _tnl_TexCoord2fv( const GLfloat *v )
574 {
575 DISPATCH_ATTR2FV( _TNL_ATTRIB_TEX0, v );
576 }
577
578 static void GLAPIENTRY _tnl_TexCoord3f( GLfloat x, GLfloat y, GLfloat z )
579 {
580 DISPATCH_ATTR3F( _TNL_ATTRIB_TEX0, x, y, z );
581 }
582
583 static void GLAPIENTRY _tnl_TexCoord3fv( const GLfloat *v )
584 {
585 DISPATCH_ATTR3FV( _TNL_ATTRIB_TEX0, v );
586 }
587
588 static void GLAPIENTRY _tnl_TexCoord4f( GLfloat x, GLfloat y, GLfloat z, GLfloat w )
589 {
590 DISPATCH_ATTR4F( _TNL_ATTRIB_TEX0, x, y, z, w );
591 }
592
593 static void GLAPIENTRY _tnl_TexCoord4fv( const GLfloat *v )
594 {
595 DISPATCH_ATTR4FV( _TNL_ATTRIB_TEX0, v );
596 }
597
598 static void GLAPIENTRY _tnl_Normal3f( GLfloat x, GLfloat y, GLfloat z )
599 {
600 DISPATCH_ATTR3F( _TNL_ATTRIB_NORMAL, x, y, z );
601 }
602
603 static void GLAPIENTRY _tnl_Normal3fv( const GLfloat *v )
604 {
605 DISPATCH_ATTR3FV( _TNL_ATTRIB_NORMAL, v );
606 }
607
608 static void GLAPIENTRY _tnl_FogCoordfEXT( GLfloat x )
609 {
610 DISPATCH_ATTR1F( _TNL_ATTRIB_FOG, x );
611 }
612
613 static void GLAPIENTRY _tnl_FogCoordfvEXT( const GLfloat *v )
614 {
615 DISPATCH_ATTR1FV( _TNL_ATTRIB_FOG, v );
616 }
617
618 static void GLAPIENTRY _tnl_Color3f( GLfloat x, GLfloat y, GLfloat z )
619 {
620 DISPATCH_ATTR3F( _TNL_ATTRIB_COLOR0, x, y, z );
621 }
622
623 static void GLAPIENTRY _tnl_Color3fv( const GLfloat *v )
624 {
625 DISPATCH_ATTR3FV( _TNL_ATTRIB_COLOR0, v );
626 }
627
628 static void GLAPIENTRY _tnl_Color4f( GLfloat x, GLfloat y, GLfloat z, GLfloat w )
629 {
630 DISPATCH_ATTR4F( _TNL_ATTRIB_COLOR0, x, y, z, w );
631 }
632
633 static void GLAPIENTRY _tnl_Color4fv( const GLfloat *v )
634 {
635 DISPATCH_ATTR4FV( _TNL_ATTRIB_COLOR0, v );
636 }
637
638 static void GLAPIENTRY _tnl_SecondaryColor3fEXT( GLfloat x, GLfloat y, GLfloat z )
639 {
640 DISPATCH_ATTR3F( _TNL_ATTRIB_COLOR1, x, y, z );
641 }
642
643 static void GLAPIENTRY _tnl_SecondaryColor3fvEXT( const GLfloat *v )
644 {
645 DISPATCH_ATTR3FV( _TNL_ATTRIB_COLOR1, v );
646 }
647
648 static void GLAPIENTRY _tnl_MultiTexCoord1f( GLenum target, GLfloat x )
649 {
650 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
651 DISPATCH_ATTR1F( attr, x );
652 }
653
654 static void GLAPIENTRY _tnl_MultiTexCoord1fv( GLenum target, const GLfloat *v )
655 {
656 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
657 DISPATCH_ATTR1FV( attr, v );
658 }
659
660 static void GLAPIENTRY _tnl_MultiTexCoord2f( GLenum target, GLfloat x, GLfloat y )
661 {
662 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
663 DISPATCH_ATTR2F( attr, x, y );
664 }
665
666 static void GLAPIENTRY _tnl_MultiTexCoord2fv( GLenum target, const GLfloat *v )
667 {
668 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
669 DISPATCH_ATTR2FV( attr, v );
670 }
671
672 static void GLAPIENTRY _tnl_MultiTexCoord3f( GLenum target, GLfloat x, GLfloat y,
673 GLfloat z)
674 {
675 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
676 DISPATCH_ATTR3F( attr, x, y, z );
677 }
678
679 static void GLAPIENTRY _tnl_MultiTexCoord3fv( GLenum target, const GLfloat *v )
680 {
681 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
682 DISPATCH_ATTR3FV( attr, v );
683 }
684
685 static void GLAPIENTRY _tnl_MultiTexCoord4f( GLenum target, GLfloat x, GLfloat y,
686 GLfloat z, GLfloat w )
687 {
688 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
689 DISPATCH_ATTR4F( attr, x, y, z, w );
690 }
691
692 static void GLAPIENTRY _tnl_MultiTexCoord4fv( GLenum target, const GLfloat *v )
693 {
694 GLuint attr = (target & 0x7) + _TNL_ATTRIB_TEX0;
695 DISPATCH_ATTR4FV( attr, v );
696 }
697
698 static void GLAPIENTRY _tnl_VertexAttrib1fNV( GLuint index, GLfloat x )
699 {
700 if (index < VERT_ATTRIB_MAX)
701 DISPATCH_ATTR1F( index, x );
702 else
703 enum_error();
704 }
705
706 static void GLAPIENTRY _tnl_VertexAttrib1fvNV( GLuint index, const GLfloat *v )
707 {
708 if (index < VERT_ATTRIB_MAX)
709 DISPATCH_ATTR1FV( index, v );
710 else
711 enum_error();
712 }
713
714 static void GLAPIENTRY _tnl_VertexAttrib2fNV( GLuint index, GLfloat x, GLfloat y )
715 {
716 if (index < VERT_ATTRIB_MAX)
717 DISPATCH_ATTR2F( index, x, y );
718 else
719 enum_error();
720 }
721
722 static void GLAPIENTRY _tnl_VertexAttrib2fvNV( GLuint index, const GLfloat *v )
723 {
724 if (index < VERT_ATTRIB_MAX)
725 DISPATCH_ATTR2FV( index, v );
726 else
727 enum_error();
728 }
729
730 static void GLAPIENTRY _tnl_VertexAttrib3fNV( GLuint index, GLfloat x, GLfloat y,
731 GLfloat z )
732 {
733 if (index < VERT_ATTRIB_MAX)
734 DISPATCH_ATTR3F( index, x, y, z );
735 else
736 enum_error();
737 }
738
739 static void GLAPIENTRY _tnl_VertexAttrib3fvNV( GLuint index, const GLfloat *v )
740 {
741 if (index < VERT_ATTRIB_MAX)
742 DISPATCH_ATTR3FV( index, v );
743 else
744 enum_error();
745 }
746
747 static void GLAPIENTRY _tnl_VertexAttrib4fNV( GLuint index, GLfloat x, GLfloat y,
748 GLfloat z, GLfloat w )
749 {
750 if (index < VERT_ATTRIB_MAX)
751 DISPATCH_ATTR4F( index, x, y, z, w );
752 else
753 enum_error();
754 }
755
756 static void GLAPIENTRY _tnl_VertexAttrib4fvNV( GLuint index, const GLfloat *v )
757 {
758 if (index < VERT_ATTRIB_MAX)
759 DISPATCH_ATTR4FV( index, v );
760 else
761 enum_error();
762 }
763
764
765 /* Materials:
766 *
767 * These are treated as per-vertex attributes, at indices above where
768 * the NV_vertex_program leaves off. There are a lot of good things
769 * about treating materials this way.
770 *
771 * However: I don't want to double the number of generated functions
772 * just to cope with this, so I unroll the 'C' varients of CHOOSE and
773 * ATTRF into this function, and dispense with codegen and
774 * second-level dispatch.
775 *
776 * There is no aliasing of material attributes with other entrypoints.
777 */
778 #define MAT_ATTR( A, N, params ) \
779 do { \
780 if (tnl->vtx.attrsz[A] != N) { \
781 _tnl_fixup_vertex( ctx, A, N ); \
782 tnl->vtx.have_materials = GL_TRUE; \
783 } \
784 \
785 { \
786 GLfloat *dest = tnl->vtx.attrptr[A]; \
787 if (N>0) dest[0] = params[0]; \
788 if (N>1) dest[1] = params[1]; \
789 if (N>2) dest[2] = params[2]; \
790 if (N>3) dest[3] = params[3]; \
791 ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT; \
792 } \
793 } while (0)
794
795
796 #define MAT( ATTR, N, face, params ) \
797 do { \
798 if (face != GL_BACK) \
799 MAT_ATTR( ATTR, N, params ); /* front */ \
800 if (face != GL_FRONT) \
801 MAT_ATTR( ATTR + 1, N, params ); /* back */ \
802 } while (0)
803
804
805 /* NOTE: Have to remove/deal-with colormaterial crossovers, probably
806 * later on - in the meantime just store everything.
807 */
808 static void GLAPIENTRY _tnl_Materialfv( GLenum face, GLenum pname,
809 const GLfloat *params )
810 {
811 GET_CURRENT_CONTEXT( ctx );
812 TNLcontext *tnl = TNL_CONTEXT(ctx);
813
814 switch (face) {
815 case GL_FRONT:
816 case GL_BACK:
817 case GL_FRONT_AND_BACK:
818 break;
819
820 default:
821 _mesa_error( ctx, GL_INVALID_ENUM, "glMaterialfv" );
822 return;
823 }
824
825 switch (pname) {
826 case GL_EMISSION:
827 MAT( _TNL_ATTRIB_MAT_FRONT_EMISSION, 4, face, params );
828 break;
829 case GL_AMBIENT:
830 MAT( _TNL_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params );
831 break;
832 case GL_DIFFUSE:
833 MAT( _TNL_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params );
834 break;
835 case GL_SPECULAR:
836 MAT( _TNL_ATTRIB_MAT_FRONT_SPECULAR, 4, face, params );
837 break;
838 case GL_SHININESS:
839 MAT( _TNL_ATTRIB_MAT_FRONT_SHININESS, 1, face, params );
840 break;
841 case GL_COLOR_INDEXES:
842 MAT( _TNL_ATTRIB_MAT_FRONT_INDEXES, 3, face, params );
843 break;
844 case GL_AMBIENT_AND_DIFFUSE:
845 MAT( _TNL_ATTRIB_MAT_FRONT_AMBIENT, 4, face, params );
846 MAT( _TNL_ATTRIB_MAT_FRONT_DIFFUSE, 4, face, params );
847 break;
848 default:
849 _mesa_error( ctx, GL_INVALID_ENUM, "glMaterialfv" );
850 return;
851 }
852 }
853
854
855 #define IDX_ATTR( A, IDX ) \
856 do { \
857 GET_CURRENT_CONTEXT( ctx ); \
858 TNLcontext *tnl = TNL_CONTEXT(ctx); \
859 \
860 if (tnl->vtx.attrsz[A] != 1) { \
861 _tnl_fixup_vertex( ctx, A, 1 ); \
862 } \
863 \
864 { \
865 GLfloat *dest = tnl->vtx.attrptr[A]; \
866 dest[0] = IDX; \
867 ctx->Driver.NeedFlush |= FLUSH_UPDATE_CURRENT; \
868 } \
869 } while (0)
870
871
872 static void GLAPIENTRY _tnl_EdgeFlag( GLboolean b )
873 {
874 IDX_ATTR( _TNL_ATTRIB_EDGEFLAG, (GLfloat)b );
875 }
876
877 static void GLAPIENTRY _tnl_EdgeFlagv( const GLboolean *v )
878 {
879 IDX_ATTR( _TNL_ATTRIB_EDGEFLAG, (GLfloat)v[0] );
880 }
881
882 static void GLAPIENTRY _tnl_Indexf( GLfloat f )
883 {
884 IDX_ATTR( _TNL_ATTRIB_INDEX, f );
885 }
886
887 static void GLAPIENTRY _tnl_Indexfv( const GLfloat *v )
888 {
889 IDX_ATTR( _TNL_ATTRIB_INDEX, v[0] );
890 }
891
892 /* Eval
893 */
894 static void GLAPIENTRY _tnl_EvalCoord1f( GLfloat u )
895 {
896 GET_CURRENT_CONTEXT( ctx );
897 TNLcontext *tnl = TNL_CONTEXT(ctx);
898
899 /* TODO: use a CHOOSE() function for this: */
900 {
901 GLint i;
902 if (tnl->vtx.eval.new_state)
903 _tnl_update_eval( ctx );
904
905 for (i = 0 ; i <= _TNL_ATTRIB_INDEX ; i++) {
906 if (tnl->vtx.eval.map1[i].map)
907 if (tnl->vtx.attrsz[i] < tnl->vtx.eval.map1[i].sz)
908 _tnl_fixup_vertex( ctx, i, tnl->vtx.eval.map1[i].sz );
909 }
910 }
911
912
913 _mesa_memcpy( tnl->vtx.copied.buffer, tnl->vtx.vertex,
914 tnl->vtx.vertex_size * sizeof(GLfloat));
915
916 _tnl_do_EvalCoord1f( ctx, u );
917
918 _mesa_memcpy( tnl->vtx.vertex, tnl->vtx.copied.buffer,
919 tnl->vtx.vertex_size * sizeof(GLfloat));
920 }
921
922 static void GLAPIENTRY _tnl_EvalCoord2f( GLfloat u, GLfloat v )
923 {
924 GET_CURRENT_CONTEXT( ctx );
925 TNLcontext *tnl = TNL_CONTEXT(ctx);
926
927 /* TODO: use a CHOOSE() function for this: */
928 {
929 GLint i;
930 if (tnl->vtx.eval.new_state)
931 _tnl_update_eval( ctx );
932
933 for (i = 0 ; i <= _TNL_ATTRIB_INDEX ; i++) {
934 if (tnl->vtx.eval.map2[i].map)
935 if (tnl->vtx.attrsz[i] < tnl->vtx.eval.map2[i].sz)
936 _tnl_fixup_vertex( ctx, i, tnl->vtx.eval.map2[i].sz );
937 }
938
939 if (ctx->Eval.AutoNormal)
940 if (tnl->vtx.attrsz[_TNL_ATTRIB_NORMAL] < 3)
941 _tnl_fixup_vertex( ctx, _TNL_ATTRIB_NORMAL, 3 );
942 }
943
944 _mesa_memcpy( tnl->vtx.copied.buffer, tnl->vtx.vertex,
945 tnl->vtx.vertex_size * sizeof(GLfloat));
946
947 _tnl_do_EvalCoord2f( ctx, u, v );
948
949 _mesa_memcpy( tnl->vtx.vertex, tnl->vtx.copied.buffer,
950 tnl->vtx.vertex_size * sizeof(GLfloat));
951 }
952
953 static void GLAPIENTRY _tnl_EvalCoord1fv( const GLfloat *u )
954 {
955 _tnl_EvalCoord1f( u[0] );
956 }
957
958 static void GLAPIENTRY _tnl_EvalCoord2fv( const GLfloat *u )
959 {
960 _tnl_EvalCoord2f( u[0], u[1] );
961 }
962
963 static void GLAPIENTRY _tnl_EvalPoint1( GLint i )
964 {
965 GET_CURRENT_CONTEXT( ctx );
966 GLfloat du = ((ctx->Eval.MapGrid1u2 - ctx->Eval.MapGrid1u1) /
967 (GLfloat) ctx->Eval.MapGrid1un);
968 GLfloat u = i * du + ctx->Eval.MapGrid1u1;
969
970 _tnl_EvalCoord1f( u );
971 }
972
973
974 static void GLAPIENTRY _tnl_EvalPoint2( GLint i, GLint j )
975 {
976 GET_CURRENT_CONTEXT( ctx );
977 GLfloat du = ((ctx->Eval.MapGrid2u2 - ctx->Eval.MapGrid2u1) /
978 (GLfloat) ctx->Eval.MapGrid2un);
979 GLfloat dv = ((ctx->Eval.MapGrid2v2 - ctx->Eval.MapGrid2v1) /
980 (GLfloat) ctx->Eval.MapGrid2vn);
981 GLfloat u = i * du + ctx->Eval.MapGrid2u1;
982 GLfloat v = j * dv + ctx->Eval.MapGrid2v1;
983
984 _tnl_EvalCoord2f( u, v );
985 }
986
987
988 /* Build a list of primitives on the fly. Keep
989 * ctx->Driver.CurrentExecPrimitive uptodate as well.
990 */
991 static void GLAPIENTRY _tnl_Begin( GLenum mode )
992 {
993 GET_CURRENT_CONTEXT( ctx );
994
995 if (ctx->Driver.CurrentExecPrimitive == GL_POLYGON+1) {
996 TNLcontext *tnl = TNL_CONTEXT(ctx);
997 int i;
998
999 if (ctx->NewState) {
1000 _mesa_update_state( ctx );
1001 if (!(tnl->Driver.NotifyBegin && tnl->Driver.NotifyBegin( ctx, mode )))
1002 ctx->Exec->Begin(mode);
1003 return;
1004 }
1005
1006 /* Heuristic: attempt to isolate attributes occuring outside
1007 * begin/end pairs.
1008 */
1009 if (tnl->vtx.vertex_size && !tnl->vtx.attrsz[0])
1010 _tnl_FlushVertices( ctx, ~0 );
1011
1012 i = tnl->vtx.prim_count++;
1013 tnl->vtx.prim[i].mode = mode | PRIM_BEGIN;
1014 tnl->vtx.prim[i].start = tnl->vtx.initial_counter - tnl->vtx.counter;
1015 tnl->vtx.prim[i].count = 0;
1016
1017 ctx->Driver.CurrentExecPrimitive = mode;
1018 }
1019 else
1020 _mesa_error( ctx, GL_INVALID_OPERATION, "glBegin" );
1021
1022 }
1023
1024 static void GLAPIENTRY _tnl_End( void )
1025 {
1026 GET_CURRENT_CONTEXT( ctx );
1027
1028 if (ctx->Driver.CurrentExecPrimitive != GL_POLYGON+1) {
1029 TNLcontext *tnl = TNL_CONTEXT(ctx);
1030 int idx = tnl->vtx.initial_counter - tnl->vtx.counter;
1031 int i = tnl->vtx.prim_count - 1;
1032
1033 tnl->vtx.prim[i].mode |= PRIM_END;
1034 tnl->vtx.prim[i].count = idx - tnl->vtx.prim[i].start;
1035
1036 ctx->Driver.CurrentExecPrimitive = GL_POLYGON+1;
1037
1038 /* Two choices which effect the way vertex attributes are
1039 * carried over (or not) between adjacent primitives.
1040 */
1041 #if 0
1042 if (tnl->vtx.prim_count == TNL_MAX_PRIM)
1043 _tnl_FlushVertices( ctx, ~0 );
1044 #else
1045 if (tnl->vtx.prim_count == TNL_MAX_PRIM)
1046 _tnl_flush_vtx( ctx );
1047 #endif
1048
1049 }
1050 else
1051 _mesa_error( ctx, GL_INVALID_OPERATION, "glEnd" );
1052 }
1053
1054
1055 static void _tnl_exec_vtxfmt_init( GLcontext *ctx )
1056 {
1057 GLvertexformat *vfmt = &(TNL_CONTEXT(ctx)->exec_vtxfmt);
1058 vfmt->ArrayElement = _ae_loopback_array_elt; /* generic helper */
1059 vfmt->Begin = _tnl_Begin;
1060 vfmt->CallList = _mesa_CallList;
1061 vfmt->CallLists = _mesa_CallLists;
1062 vfmt->Color3f = _tnl_Color3f;
1063 vfmt->Color3fv = _tnl_Color3fv;
1064 vfmt->Color4f = _tnl_Color4f;
1065 vfmt->Color4fv = _tnl_Color4fv;
1066 vfmt->EdgeFlag = _tnl_EdgeFlag;
1067 vfmt->EdgeFlagv = _tnl_EdgeFlagv;
1068 vfmt->End = _tnl_End;
1069 vfmt->EvalCoord1f = _tnl_EvalCoord1f;
1070 vfmt->EvalCoord1fv = _tnl_EvalCoord1fv;
1071 vfmt->EvalCoord2f = _tnl_EvalCoord2f;
1072 vfmt->EvalCoord2fv = _tnl_EvalCoord2fv;
1073 vfmt->EvalPoint1 = _tnl_EvalPoint1;
1074 vfmt->EvalPoint2 = _tnl_EvalPoint2;
1075 vfmt->FogCoordfEXT = _tnl_FogCoordfEXT;
1076 vfmt->FogCoordfvEXT = _tnl_FogCoordfvEXT;
1077 vfmt->Indexf = _tnl_Indexf;
1078 vfmt->Indexfv = _tnl_Indexfv;
1079 vfmt->Materialfv = _tnl_Materialfv;
1080 vfmt->MultiTexCoord1fARB = _tnl_MultiTexCoord1f;
1081 vfmt->MultiTexCoord1fvARB = _tnl_MultiTexCoord1fv;
1082 vfmt->MultiTexCoord2fARB = _tnl_MultiTexCoord2f;
1083 vfmt->MultiTexCoord2fvARB = _tnl_MultiTexCoord2fv;
1084 vfmt->MultiTexCoord3fARB = _tnl_MultiTexCoord3f;
1085 vfmt->MultiTexCoord3fvARB = _tnl_MultiTexCoord3fv;
1086 vfmt->MultiTexCoord4fARB = _tnl_MultiTexCoord4f;
1087 vfmt->MultiTexCoord4fvARB = _tnl_MultiTexCoord4fv;
1088 vfmt->Normal3f = _tnl_Normal3f;
1089 vfmt->Normal3fv = _tnl_Normal3fv;
1090 vfmt->SecondaryColor3fEXT = _tnl_SecondaryColor3fEXT;
1091 vfmt->SecondaryColor3fvEXT = _tnl_SecondaryColor3fvEXT;
1092 vfmt->TexCoord1f = _tnl_TexCoord1f;
1093 vfmt->TexCoord1fv = _tnl_TexCoord1fv;
1094 vfmt->TexCoord2f = _tnl_TexCoord2f;
1095 vfmt->TexCoord2fv = _tnl_TexCoord2fv;
1096 vfmt->TexCoord3f = _tnl_TexCoord3f;
1097 vfmt->TexCoord3fv = _tnl_TexCoord3fv;
1098 vfmt->TexCoord4f = _tnl_TexCoord4f;
1099 vfmt->TexCoord4fv = _tnl_TexCoord4fv;
1100 vfmt->Vertex2f = _tnl_Vertex2f;
1101 vfmt->Vertex2fv = _tnl_Vertex2fv;
1102 vfmt->Vertex3f = _tnl_Vertex3f;
1103 vfmt->Vertex3fv = _tnl_Vertex3fv;
1104 vfmt->Vertex4f = _tnl_Vertex4f;
1105 vfmt->Vertex4fv = _tnl_Vertex4fv;
1106 vfmt->VertexAttrib1fNV = _tnl_VertexAttrib1fNV;
1107 vfmt->VertexAttrib1fvNV = _tnl_VertexAttrib1fvNV;
1108 vfmt->VertexAttrib2fNV = _tnl_VertexAttrib2fNV;
1109 vfmt->VertexAttrib2fvNV = _tnl_VertexAttrib2fvNV;
1110 vfmt->VertexAttrib3fNV = _tnl_VertexAttrib3fNV;
1111 vfmt->VertexAttrib3fvNV = _tnl_VertexAttrib3fvNV;
1112 vfmt->VertexAttrib4fNV = _tnl_VertexAttrib4fNV;
1113 vfmt->VertexAttrib4fvNV = _tnl_VertexAttrib4fvNV;
1114
1115 vfmt->Rectf = _mesa_noop_Rectf;
1116 vfmt->EvalMesh1 = _mesa_noop_EvalMesh1;
1117 vfmt->EvalMesh2 = _mesa_noop_EvalMesh2;
1118 }
1119
1120
1121
1122 void _tnl_FlushVertices( GLcontext *ctx, GLuint flags )
1123 {
1124 TNLcontext *tnl = TNL_CONTEXT(ctx);
1125
1126 if (ctx->Driver.CurrentExecPrimitive != PRIM_OUTSIDE_BEGIN_END)
1127 return;
1128
1129 if (tnl->vtx.counter != tnl->vtx.initial_counter) {
1130 _tnl_flush_vtx( ctx );
1131 }
1132
1133 {
1134 _tnl_copy_to_current( ctx );
1135
1136 /* reset attrfv table
1137 */
1138 init_attrfv( tnl );
1139 flags |= FLUSH_UPDATE_CURRENT;
1140 }
1141
1142 ctx->Driver.NeedFlush = 0;
1143 }
1144
1145
1146 static void _tnl_current_init( GLcontext *ctx )
1147 {
1148 TNLcontext *tnl = TNL_CONTEXT(ctx);
1149 GLint i;
1150
1151 /* setup the pointers for the typical 16 vertex attributes */
1152 for (i = 0; i < VERT_ATTRIB_MAX; i++)
1153 tnl->vtx.current[i] = ctx->Current.Attrib[i];
1154
1155 /* setup pointers for the 12 material attributes */
1156 for (i = 0; i < MAT_ATTRIB_MAX; i++)
1157 tnl->vtx.current[_TNL_ATTRIB_MAT_FRONT_AMBIENT + i] =
1158 ctx->Light.Material.Attrib[i];
1159
1160 tnl->vtx.current[_TNL_ATTRIB_INDEX] = &ctx->Current.Index;
1161 }
1162
1163
1164 void _tnl_vtx_init( GLcontext *ctx )
1165 {
1166 TNLcontext *tnl = TNL_CONTEXT(ctx);
1167 struct tnl_vertex_arrays *tmp = &tnl->vtx_inputs;
1168 GLuint i;
1169
1170 for (i = 0; i < _TNL_ATTRIB_INDEX; i++)
1171 _mesa_vector4f_init( &tmp->Attribs[i], 0, 0);
1172
1173 _tnl_current_init( ctx );
1174 _tnl_exec_vtxfmt_init( ctx );
1175
1176 _mesa_install_exec_vtxfmt( ctx, &tnl->exec_vtxfmt );
1177 tnl->vtx.vertex_size = 1; init_attrfv( tnl );
1178 }
1179
1180
1181
1182 void _tnl_vtx_destroy( GLcontext *ctx )
1183 {
1184 }
1185