d60b8740270e87d39d5f3c0141c21e8f1de7bfdb
[mesa.git] / src / mesa / drivers / glide / fxtris.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 4.0
4 *
5 * Copyright (C) 1999-2001 Brian Paul 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 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25 /* Authors:
26 * Keith Whitwell <keith@tungstengraphics.com>
27 */
28
29 #include "glheader.h"
30
31 #ifdef FX
32
33 #include "mtypes.h"
34 #include "macros.h"
35 #include "colormac.h"
36
37 #include "swrast/swrast.h"
38 #include "swrast_setup/swrast_setup.h"
39 #include "tnl/t_context.h"
40 #include "tnl/t_pipeline.h"
41
42 #include "fxdrv.h"
43
44
45 /*
46 * Subpixel offsets to adjust Mesa's (true) window coordinates to
47 * Glide coordinates. We need these to ensure precise rasterization.
48 * Otherwise, we'll fail a bunch of conformance tests.
49 */
50 #define TRI_X_OFFSET ( 0.0F)
51 #define TRI_Y_OFFSET ( 0.0F)
52 #define LINE_X_OFFSET ( 0.0F)
53 #define LINE_Y_OFFSET ( 0.125F)
54 #define PNT_X_OFFSET ( 0.375F)
55 #define PNT_Y_OFFSET ( 0.375F)
56
57 static void fxRasterPrimitive( GLcontext *ctx, GLenum prim );
58 static void fxRenderPrimitive( GLcontext *ctx, GLenum prim );
59
60 /***********************************************************************
61 * Macros for t_dd_tritmp.h to draw basic primitives *
62 ***********************************************************************/
63
64 #define TRI( a, b, c ) \
65 do { \
66 if (DO_FALLBACK) \
67 fxMesa->draw_tri( fxMesa, a, b, c ); \
68 else \
69 grDrawTriangle( a, b, c ); \
70 } while (0) \
71
72 #define QUAD( a, b, c, d ) \
73 do { \
74 if (DO_FALLBACK) { \
75 fxMesa->draw_tri( fxMesa, a, b, d ); \
76 fxMesa->draw_tri( fxMesa, b, c, d ); \
77 } else { \
78 grDrawTriangle( a, b, d ); \
79 grDrawTriangle( b, c, d ); \
80 } \
81 } while (0)
82
83 #define LINE( v0, v1 ) \
84 do { \
85 if (DO_FALLBACK) \
86 fxMesa->draw_line( fxMesa, v0, v1 ); \
87 else { \
88 v0->x += LINE_X_OFFSET - TRI_X_OFFSET; \
89 v0->y += LINE_Y_OFFSET - TRI_Y_OFFSET; \
90 v1->x += LINE_X_OFFSET - TRI_X_OFFSET; \
91 v1->y += LINE_Y_OFFSET - TRI_Y_OFFSET; \
92 grDrawLine( v0, v1 ); \
93 v0->x -= LINE_X_OFFSET - TRI_X_OFFSET; \
94 v0->y -= LINE_Y_OFFSET - TRI_Y_OFFSET; \
95 v1->x -= LINE_X_OFFSET - TRI_X_OFFSET; \
96 v1->y -= LINE_Y_OFFSET - TRI_Y_OFFSET; \
97 } \
98 } while (0)
99
100 #define POINT( v0 ) \
101 do { \
102 if (DO_FALLBACK) \
103 fxMesa->draw_point( fxMesa, v0 ); \
104 else { \
105 v0->x += PNT_X_OFFSET - TRI_X_OFFSET; \
106 v0->y += PNT_Y_OFFSET - TRI_Y_OFFSET; \
107 grDrawPoint( v0 ); \
108 v0->x -= PNT_X_OFFSET - TRI_X_OFFSET; \
109 v0->y -= PNT_Y_OFFSET - TRI_Y_OFFSET; \
110 } \
111 } while (0)
112
113
114 /***********************************************************************
115 * Fallback to swrast for basic primitives *
116 ***********************************************************************/
117
118 /* Build an SWvertex from a hardware vertex.
119 *
120 * This code is hit only when a mix of accelerated and unaccelerated
121 * primitives are being drawn, and only for the unaccelerated
122 * primitives.
123 */
124 static void
125 fx_translate_vertex( GLcontext *ctx, const GrVertex *src, SWvertex *dst)
126 {
127 fxMesaContext fxMesa = FX_CONTEXT(ctx);
128 GLuint ts0 = fxMesa->tmu_source[0];
129 GLuint ts1 = fxMesa->tmu_source[1];
130 GLfloat w = 1.0 / src->oow;
131
132 dst->win[0] = src->x;
133 dst->win[1] = src->y;
134 dst->win[2] = src->ooz;
135 dst->win[3] = src->oow;
136
137 dst->color[0] = src->pargb[2];
138 dst->color[1] = src->pargb[1];
139 dst->color[2] = src->pargb[0];
140 dst->color[3] = src->pargb[3];
141
142 dst->texcoord[ts0][0] = fxMesa->inv_s0scale * src->tmuvtx[0].sow * w;
143 dst->texcoord[ts0][1] = fxMesa->inv_t0scale * src->tmuvtx[0].tow * w;
144
145 if (fxMesa->stw_hint_state & GR_STWHINT_W_DIFF_TMU0)
146 dst->texcoord[ts0][3] = src->tmuvtx[0].oow * w;
147 else
148 dst->texcoord[ts0][3] = 1.0;
149
150 if (fxMesa->SetupIndex & SETUP_TMU1) {
151 dst->texcoord[ts1][0] = fxMesa->inv_s1scale * src->tmuvtx[1].sow * w;
152 dst->texcoord[ts1][1] = fxMesa->inv_t1scale * src->tmuvtx[1].tow * w;
153
154 if (fxMesa->stw_hint_state & GR_STWHINT_W_DIFF_TMU1)
155 dst->texcoord[ts1][3] = src->tmuvtx[1].oow * w;
156 else
157 dst->texcoord[ts1][3] = 1.0;
158 }
159
160 dst->pointSize = ctx->Point._Size;
161 }
162
163
164 static void
165 fx_fallback_tri( fxMesaContext fxMesa,
166 GrVertex *v0,
167 GrVertex *v1,
168 GrVertex *v2 )
169 {
170 GLcontext *ctx = fxMesa->glCtx;
171 SWvertex v[3];
172
173 fx_translate_vertex( ctx, v0, &v[0] );
174 fx_translate_vertex( ctx, v1, &v[1] );
175 fx_translate_vertex( ctx, v2, &v[2] );
176 _swrast_Triangle( ctx, &v[0], &v[1], &v[2] );
177 }
178
179
180 static void
181 fx_fallback_line( fxMesaContext fxMesa,
182 GrVertex *v0,
183 GrVertex *v1 )
184 {
185 GLcontext *ctx = fxMesa->glCtx;
186 SWvertex v[2];
187 fx_translate_vertex( ctx, v0, &v[0] );
188 fx_translate_vertex( ctx, v1, &v[1] );
189 _swrast_Line( ctx, &v[0], &v[1] );
190 }
191
192
193 static void
194 fx_fallback_point( fxMesaContext fxMesa,
195 GrVertex *v0 )
196 {
197 GLcontext *ctx = fxMesa->glCtx;
198 SWvertex v[1];
199 fx_translate_vertex( ctx, v0, &v[0] );
200 _swrast_Point( ctx, &v[0] );
201 }
202
203 /***********************************************************************
204 * Functions to draw basic primitives *
205 ***********************************************************************/
206
207 static void fx_print_vertex( GLcontext *ctx, const GrVertex *v )
208 {
209 fprintf(stderr, "%s:\n", __FUNCTION__);
210
211 fprintf(stderr, "\tvertex at %p\n", (void *) v);
212
213 fprintf(stderr, "\tx %f y %f z %f oow %f\n", v->x, v->y, v->ooz, v->oow);
214 fprintf(stderr, "\tr %d g %d b %d a %d\n", v->pargb[2], v->pargb[1], v->pargb[0], v->pargb[3]);
215
216 fprintf(stderr, "\n");
217 }
218
219 #define DO_FALLBACK 0
220
221 /* Need to do clip loop at each triangle when mixing swrast and hw
222 * rendering. These functions are only used when mixed-mode rendering
223 * is occurring.
224 */
225 static void fx_draw_quad( fxMesaContext fxMesa,
226 GrVertex *v0,
227 GrVertex *v1,
228 GrVertex *v2,
229 GrVertex *v3 )
230 {
231 BEGIN_CLIP_LOOP();
232 QUAD( v0, v1, v2, v3 );
233 END_CLIP_LOOP();
234 }
235
236 static void fx_draw_triangle( fxMesaContext fxMesa,
237 GrVertex *v0,
238 GrVertex *v1,
239 GrVertex *v2 )
240 {
241 BEGIN_CLIP_LOOP();
242 TRI( v0, v1, v2 );
243 END_CLIP_LOOP();
244 }
245
246 static void fx_draw_line( fxMesaContext fxMesa,
247 GrVertex *v0,
248 GrVertex *v1 )
249 {
250 /* No support for wide lines (avoid wide/aa line fallback).
251 */
252 BEGIN_CLIP_LOOP();
253 LINE(v0, v1);
254 END_CLIP_LOOP();
255 }
256
257 static void fx_draw_point( fxMesaContext fxMesa,
258 GrVertex *v0 )
259 {
260 /* No support for wide points.
261 */
262 BEGIN_CLIP_LOOP();
263 POINT( v0 );
264 END_CLIP_LOOP();
265 }
266
267 #undef DO_FALLBACK
268
269
270 #define FX_UNFILLED_BIT 0x1
271 #define FX_OFFSET_BIT 0x2
272 #define FX_TWOSIDE_BIT 0x4
273 #define FX_FLAT_BIT 0x8
274 #define FX_FALLBACK_BIT 0x10
275 #define FX_MAX_TRIFUNC 0x20
276
277 static struct {
278 points_func points;
279 line_func line;
280 triangle_func triangle;
281 quad_func quad;
282 } rast_tab[FX_MAX_TRIFUNC];
283
284 #define DO_FALLBACK (IND & FX_FALLBACK_BIT)
285 #define DO_OFFSET (IND & FX_OFFSET_BIT)
286 #define DO_UNFILLED (IND & FX_UNFILLED_BIT)
287 #define DO_TWOSIDE (IND & FX_TWOSIDE_BIT)
288 #define DO_FLAT (IND & FX_FLAT_BIT)
289 #define DO_TRI 1
290 #define DO_QUAD 1
291 #define DO_LINE 1
292 #define DO_POINTS 1
293 #define DO_FULL_QUAD 1
294
295 #define HAVE_RGBA 1
296 #define HAVE_SPEC 0
297 #define HAVE_HW_FLATSHADE 0
298 #define HAVE_BACK_COLORS 0
299 #define VERTEX GrVertex
300 #define TAB rast_tab
301
302 #define DEPTH_SCALE 1.0
303 #define UNFILLED_TRI unfilled_tri
304 #define UNFILLED_QUAD unfilled_quad
305 #define VERT_X(_v) _v->x
306 #define VERT_Y(_v) _v->y
307 #define VERT_Z(_v) _v->ooz
308 #define AREA_IS_CCW( a ) (a < 0)
309 #define GET_VERTEX(e) (fxMesa->verts + e)
310
311 #define VERT_SET_RGBA( dst, f ) \
312 do { \
313 dst->pargb[2] = f[0]; \
314 dst->pargb[1] = f[1]; \
315 dst->pargb[0] = f[2]; \
316 dst->pargb[3] = f[3]; \
317 } while (0)
318
319 #define VERT_COPY_RGBA( v0, v1 ) \
320 *(GLuint *)&v0->pargb = *(GLuint *)&v1->pargb
321
322 #define VERT_SAVE_RGBA( idx ) \
323 *(GLuint *)&color[idx] = *(GLuint *)&v[idx]->pargb
324
325
326 #define VERT_RESTORE_RGBA( idx ) \
327 *(GLuint *)&v[idx]->pargb = *(GLuint *)&color[idx]
328
329
330 #define LOCAL_VARS(n) \
331 fxMesaContext fxMesa = FX_CONTEXT(ctx); \
332 GLubyte color[n][4]; \
333 (void) color;
334
335
336
337 /***********************************************************************
338 * Functions to draw basic unfilled primitives *
339 ***********************************************************************/
340
341 #define RASTERIZE(x) if (fxMesa->raster_primitive != x) \
342 fxRasterPrimitive( ctx, x )
343 #define RENDER_PRIMITIVE fxMesa->render_primitive
344 #define IND FX_FALLBACK_BIT
345 #define TAG(x) x
346 #include "../common/t_dd_unfilled.h"
347 #undef IND
348
349 /***********************************************************************
350 * Functions to draw GL primitives *
351 ***********************************************************************/
352
353 #define IND (0)
354 #define TAG(x) x
355 #include "../common/t_dd_tritmp.h"
356
357 #define IND (FX_OFFSET_BIT)
358 #define TAG(x) x##_offset
359 #include "../common/t_dd_tritmp.h"
360
361 #define IND (FX_TWOSIDE_BIT)
362 #define TAG(x) x##_twoside
363 #include "../common/t_dd_tritmp.h"
364
365 #define IND (FX_TWOSIDE_BIT|FX_OFFSET_BIT)
366 #define TAG(x) x##_twoside_offset
367 #include "../common/t_dd_tritmp.h"
368
369 #define IND (FX_UNFILLED_BIT)
370 #define TAG(x) x##_unfilled
371 #include "../common/t_dd_tritmp.h"
372
373 #define IND (FX_OFFSET_BIT|FX_UNFILLED_BIT)
374 #define TAG(x) x##_offset_unfilled
375 #include "../common/t_dd_tritmp.h"
376
377 #define IND (FX_TWOSIDE_BIT|FX_UNFILLED_BIT)
378 #define TAG(x) x##_twoside_unfilled
379 #include "../common/t_dd_tritmp.h"
380
381 #define IND (FX_TWOSIDE_BIT|FX_OFFSET_BIT|FX_UNFILLED_BIT)
382 #define TAG(x) x##_twoside_offset_unfilled
383 #include "../common/t_dd_tritmp.h"
384
385 #define IND (FX_FALLBACK_BIT)
386 #define TAG(x) x##_fallback
387 #include "../common/t_dd_tritmp.h"
388
389 #define IND (FX_OFFSET_BIT|FX_FALLBACK_BIT)
390 #define TAG(x) x##_offset_fallback
391 #include "../common/t_dd_tritmp.h"
392
393 #define IND (FX_TWOSIDE_BIT|FX_FALLBACK_BIT)
394 #define TAG(x) x##_twoside_fallback
395 #include "../common/t_dd_tritmp.h"
396
397 #define IND (FX_TWOSIDE_BIT|FX_OFFSET_BIT|FX_FALLBACK_BIT)
398 #define TAG(x) x##_twoside_offset_fallback
399 #include "../common/t_dd_tritmp.h"
400
401 #define IND (FX_UNFILLED_BIT|FX_FALLBACK_BIT)
402 #define TAG(x) x##_unfilled_fallback
403 #include "../common/t_dd_tritmp.h"
404
405 #define IND (FX_OFFSET_BIT|FX_UNFILLED_BIT|FX_FALLBACK_BIT)
406 #define TAG(x) x##_offset_unfilled_fallback
407 #include "../common/t_dd_tritmp.h"
408
409 #define IND (FX_TWOSIDE_BIT|FX_UNFILLED_BIT|FX_FALLBACK_BIT)
410 #define TAG(x) x##_twoside_unfilled_fallback
411 #include "../common/t_dd_tritmp.h"
412
413 #define IND (FX_TWOSIDE_BIT|FX_OFFSET_BIT|FX_UNFILLED_BIT| \
414 FX_FALLBACK_BIT)
415 #define TAG(x) x##_twoside_offset_unfilled_fallback
416 #include "../common/t_dd_tritmp.h"
417
418
419 /* Fx doesn't support provoking-vertex flat-shading?
420 */
421 #define IND (FX_FLAT_BIT)
422 #define TAG(x) x##_flat
423 #include "../common/t_dd_tritmp.h"
424
425 #define IND (FX_OFFSET_BIT|FX_FLAT_BIT)
426 #define TAG(x) x##_offset_flat
427 #include "../common/t_dd_tritmp.h"
428
429 #define IND (FX_TWOSIDE_BIT|FX_FLAT_BIT)
430 #define TAG(x) x##_twoside_flat
431 #include "../common/t_dd_tritmp.h"
432
433 #define IND (FX_TWOSIDE_BIT|FX_OFFSET_BIT|FX_FLAT_BIT)
434 #define TAG(x) x##_twoside_offset_flat
435 #include "../common/t_dd_tritmp.h"
436
437 #define IND (FX_UNFILLED_BIT|FX_FLAT_BIT)
438 #define TAG(x) x##_unfilled_flat
439 #include "../common/t_dd_tritmp.h"
440
441 #define IND (FX_OFFSET_BIT|FX_UNFILLED_BIT|FX_FLAT_BIT)
442 #define TAG(x) x##_offset_unfilled_flat
443 #include "../common/t_dd_tritmp.h"
444
445 #define IND (FX_TWOSIDE_BIT|FX_UNFILLED_BIT|FX_FLAT_BIT)
446 #define TAG(x) x##_twoside_unfilled_flat
447 #include "../common/t_dd_tritmp.h"
448
449 #define IND (FX_TWOSIDE_BIT|FX_OFFSET_BIT|FX_UNFILLED_BIT|FX_FLAT_BIT)
450 #define TAG(x) x##_twoside_offset_unfilled_flat
451 #include "../common/t_dd_tritmp.h"
452
453 #define IND (FX_FALLBACK_BIT|FX_FLAT_BIT)
454 #define TAG(x) x##_fallback_flat
455 #include "../common/t_dd_tritmp.h"
456
457 #define IND (FX_OFFSET_BIT|FX_FALLBACK_BIT|FX_FLAT_BIT)
458 #define TAG(x) x##_offset_fallback_flat
459 #include "../common/t_dd_tritmp.h"
460
461 #define IND (FX_TWOSIDE_BIT|FX_FALLBACK_BIT|FX_FLAT_BIT)
462 #define TAG(x) x##_twoside_fallback_flat
463 #include "../common/t_dd_tritmp.h"
464
465 #define IND (FX_TWOSIDE_BIT|FX_OFFSET_BIT|FX_FALLBACK_BIT|FX_FLAT_BIT)
466 #define TAG(x) x##_twoside_offset_fallback_flat
467 #include "../common/t_dd_tritmp.h"
468
469 #define IND (FX_UNFILLED_BIT|FX_FALLBACK_BIT|FX_FLAT_BIT)
470 #define TAG(x) x##_unfilled_fallback_flat
471 #include "../common/t_dd_tritmp.h"
472
473 #define IND (FX_OFFSET_BIT|FX_UNFILLED_BIT|FX_FALLBACK_BIT|FX_FLAT_BIT)
474 #define TAG(x) x##_offset_unfilled_fallback_flat
475 #include "../common/t_dd_tritmp.h"
476
477 #define IND (FX_TWOSIDE_BIT|FX_UNFILLED_BIT|FX_FALLBACK_BIT|FX_FLAT_BIT)
478 #define TAG(x) x##_twoside_unfilled_fallback_flat
479 #include "../common/t_dd_tritmp.h"
480
481 #define IND (FX_TWOSIDE_BIT|FX_OFFSET_BIT|FX_UNFILLED_BIT| \
482 FX_FALLBACK_BIT|FX_FLAT_BIT)
483 #define TAG(x) x##_twoside_offset_unfilled_fallback_flat
484 #include "../common/t_dd_tritmp.h"
485
486
487 static void init_rast_tab( void )
488 {
489 init();
490 init_offset();
491 init_twoside();
492 init_twoside_offset();
493 init_unfilled();
494 init_offset_unfilled();
495 init_twoside_unfilled();
496 init_twoside_offset_unfilled();
497 init_fallback();
498 init_offset_fallback();
499 init_twoside_fallback();
500 init_twoside_offset_fallback();
501 init_unfilled_fallback();
502 init_offset_unfilled_fallback();
503 init_twoside_unfilled_fallback();
504 init_twoside_offset_unfilled_fallback();
505
506 init_flat();
507 init_offset_flat();
508 init_twoside_flat();
509 init_twoside_offset_flat();
510 init_unfilled_flat();
511 init_offset_unfilled_flat();
512 init_twoside_unfilled_flat();
513 init_twoside_offset_unfilled_flat();
514 init_fallback_flat();
515 init_offset_fallback_flat();
516 init_twoside_fallback_flat();
517 init_twoside_offset_fallback_flat();
518 init_unfilled_fallback_flat();
519 init_offset_unfilled_fallback_flat();
520 init_twoside_unfilled_fallback_flat();
521 init_twoside_offset_unfilled_fallback_flat();
522 }
523
524
525 /**********************************************************************/
526 /* Render whole begin/end objects */
527 /**********************************************************************/
528
529
530 /* Accelerate vertex buffer rendering when renderindex == 0 and
531 * there is no clipping.
532 */
533
534 static void fx_render_vb_points( GLcontext *ctx,
535 GLuint start,
536 GLuint count,
537 GLuint flags )
538 {
539 fxMesaContext fxMesa = FX_CONTEXT(ctx);
540 GrVertex *fxVB = fxMesa->verts;
541 GLint i;
542 (void) flags;
543
544 if (TDFX_DEBUG & VERBOSE_VARRAY) {
545 fprintf(stderr, "fx_render_vb_points\n");
546 }
547
548 fxRenderPrimitive(ctx, GL_POINTS);
549
550 /* Adjust point coords */
551 for (i = start; i < count; i++) {
552 fxVB[i].x += PNT_X_OFFSET - TRI_X_OFFSET;
553 fxVB[i].y += PNT_Y_OFFSET - TRI_Y_OFFSET;
554 }
555
556 grDrawVertexArrayContiguous( GR_POINTS, count-start,
557 fxVB + start, sizeof(GrVertex));
558 /* restore point coords */
559 for (i = start; i < count; i++) {
560 fxVB[i].x -= PNT_X_OFFSET - TRI_X_OFFSET;
561 fxVB[i].y -= PNT_Y_OFFSET - TRI_Y_OFFSET;
562 }
563 }
564
565 static void fx_render_vb_line_strip( GLcontext *ctx,
566 GLuint start,
567 GLuint count,
568 GLuint flags )
569 {
570 fxMesaContext fxMesa = FX_CONTEXT(ctx);
571 GrVertex *fxVB = fxMesa->verts;
572 GLint i;
573 (void) flags;
574
575 if (TDFX_DEBUG & VERBOSE_VARRAY) {
576 fprintf(stderr, "fx_render_vb_line_strip\n");
577 }
578
579 fxRenderPrimitive(ctx, GL_LINE_STRIP);
580
581 /* adjust line coords */
582 for (i = start; i < count; i++) {
583 fxVB[i].x += LINE_X_OFFSET - TRI_X_OFFSET;
584 fxVB[i].y += LINE_Y_OFFSET - TRI_Y_OFFSET;
585 }
586
587 grDrawVertexArrayContiguous( GR_LINE_STRIP, count-start,
588 fxVB + start, sizeof(GrVertex));
589
590 /* restore line coords */
591 for (i = start; i < count; i++) {
592 fxVB[i].x -= LINE_X_OFFSET - TRI_X_OFFSET;
593 fxVB[i].y -= LINE_Y_OFFSET - TRI_Y_OFFSET;
594 }
595 }
596
597 static void fx_render_vb_line_loop( GLcontext *ctx,
598 GLuint start,
599 GLuint count,
600 GLuint flags )
601 {
602 fxMesaContext fxMesa = FX_CONTEXT(ctx);
603 GrVertex *fxVB = fxMesa->verts;
604 GLint i;
605 GLint j = start;
606 (void) flags;
607
608 if (TDFX_DEBUG & VERBOSE_VARRAY) {
609 fprintf(stderr, "fx_render_vb_line_loop\n");
610 }
611
612 fxRenderPrimitive(ctx, GL_LINE_LOOP);
613
614 if (!(flags & PRIM_BEGIN)) {
615 j++;
616 }
617
618 /* adjust line coords */
619 for (i = start; i < count; i++) {
620 fxVB[i].x += LINE_X_OFFSET - TRI_X_OFFSET;
621 fxVB[i].y += LINE_Y_OFFSET - TRI_Y_OFFSET;
622 }
623
624 grDrawVertexArrayContiguous( GR_LINE_STRIP, count-j,
625 fxVB + j, sizeof(GrVertex));
626
627 if (flags & PRIM_END)
628 grDrawLine( fxVB + (count - 1),
629 fxVB + start );
630
631 /* restore line coords */
632 for (i = start; i < count; i++) {
633 fxVB[i].x -= LINE_X_OFFSET - TRI_X_OFFSET;
634 fxVB[i].y -= LINE_Y_OFFSET - TRI_Y_OFFSET;
635 }
636 }
637
638 static void fx_render_vb_lines( GLcontext *ctx,
639 GLuint start,
640 GLuint count,
641 GLuint flags )
642 {
643 fxMesaContext fxMesa = FX_CONTEXT(ctx);
644 GrVertex *fxVB = fxMesa->verts;
645 GLint i;
646 (void) flags;
647
648 if (TDFX_DEBUG & VERBOSE_VARRAY) {
649 fprintf(stderr, "fx_render_vb_lines\n");
650 }
651
652 fxRenderPrimitive(ctx, GL_LINES);
653
654 /* adjust line coords */
655 for (i = start; i < count; i++) {
656 fxVB[i].x += LINE_X_OFFSET - TRI_X_OFFSET;
657 fxVB[i].y += LINE_Y_OFFSET - TRI_Y_OFFSET;
658 }
659
660 grDrawVertexArrayContiguous( GR_LINES, count-start,
661 fxVB + start, sizeof(GrVertex));
662
663 /* restore line coords */
664 for (i = start; i < count; i++) {
665 fxVB[i].x -= LINE_X_OFFSET - TRI_X_OFFSET;
666 fxVB[i].y -= LINE_Y_OFFSET - TRI_Y_OFFSET;
667 }
668 }
669
670 static void fx_render_vb_triangles( GLcontext *ctx,
671 GLuint start,
672 GLuint count,
673 GLuint flags )
674 {
675 fxMesaContext fxMesa = FX_CONTEXT(ctx);
676 GrVertex *fxVB = fxMesa->verts;
677 (void) flags;
678
679 if (TDFX_DEBUG & VERBOSE_VARRAY) {
680 fprintf(stderr, "fx_render_vb_triangles\n");
681 }
682
683 fxRenderPrimitive(ctx, GL_TRIANGLES);
684
685 #if 0
686 /* [dBorca]
687 * apparently, this causes troubles with some programs (GLExcess);
688 * might be a bug in Glide... However, "grDrawVertexArrayContiguous"
689 * eventually calls "grDrawTriangle" for GR_TRIANGLES, so we're better
690 * off doing it by hand...
691 */
692 grDrawVertexArrayContiguous( GR_TRIANGLES, count-start,
693 fxVB + start, sizeof(GrVertex));
694 #else
695 {
696 GLuint j;
697 for (j=start+2; j<count; j+=3) {
698 grDrawTriangle(fxVB + (j-2), fxVB + (j-1), fxVB + j);
699 }
700 }
701 #endif
702 }
703
704
705 static void fx_render_vb_tri_strip( GLcontext *ctx,
706 GLuint start,
707 GLuint count,
708 GLuint flags )
709 {
710 fxMesaContext fxMesa = FX_CONTEXT(ctx);
711 GrVertex *fxVB = fxMesa->verts;
712 int mode;
713 (void) flags;
714
715 if (TDFX_DEBUG & VERBOSE_VARRAY) {
716 fprintf(stderr, "fx_render_vb_tri_strip\n");
717 }
718
719 fxRenderPrimitive(ctx, GL_TRIANGLE_STRIP);
720
721 if (flags & PRIM_PARITY)
722 mode = GR_TRIANGLE_STRIP_CONTINUE;
723 else
724 mode = GR_TRIANGLE_STRIP;
725
726 grDrawVertexArrayContiguous( mode, count-start,
727 fxVB + start, sizeof(GrVertex));
728 }
729
730
731 static void fx_render_vb_tri_fan( GLcontext *ctx,
732 GLuint start,
733 GLuint count,
734 GLuint flags )
735 {
736 fxMesaContext fxMesa = FX_CONTEXT(ctx);
737 GrVertex *fxVB = fxMesa->verts;
738 (void) flags;
739
740 if (TDFX_DEBUG & VERBOSE_VARRAY) {
741 fprintf(stderr, "fx_render_vb_tri_fan\n");
742 }
743
744 fxRenderPrimitive(ctx, GL_TRIANGLE_FAN);
745
746 grDrawVertexArrayContiguous( GR_TRIANGLE_FAN, count-start,
747 fxVB + start, sizeof(GrVertex) );
748 }
749
750 static void fx_render_vb_quads( GLcontext *ctx,
751 GLuint start,
752 GLuint count,
753 GLuint flags )
754 {
755 fxMesaContext fxMesa = FX_CONTEXT(ctx);
756 GrVertex *fxVB = fxMesa->verts;
757 GLuint i;
758 (void) flags;
759
760 if (TDFX_DEBUG & VERBOSE_VARRAY) {
761 fprintf(stderr, "fx_render_vb_quads\n");
762 }
763
764 fxRenderPrimitive(ctx, GL_QUADS);
765
766 for (i = start ; i < count-3 ; i += 4 ) {
767 #define VERT(x) (fxVB + (x))
768 grDrawTriangle( VERT(i), VERT(i+1), VERT(i+3) );
769 grDrawTriangle( VERT(i+1), VERT(i+2), VERT(i+3) );
770 #undef VERT
771 }
772 }
773
774 static void fx_render_vb_quad_strip( GLcontext *ctx,
775 GLuint start,
776 GLuint count,
777 GLuint flags )
778 {
779 fxMesaContext fxMesa = FX_CONTEXT(ctx);
780 GrVertex *fxVB = fxMesa->verts;
781 (void) flags;
782
783 if (TDFX_DEBUG & VERBOSE_VARRAY) {
784 fprintf(stderr, "fx_render_vb_quad_strip\n");
785 }
786
787 fxRenderPrimitive(ctx, GL_QUAD_STRIP);
788
789 count -= (count-start)&1;
790
791 grDrawVertexArrayContiguous( GR_TRIANGLE_STRIP,
792 count-start, fxVB + start, sizeof(GrVertex));
793 }
794
795 static void fx_render_vb_poly( GLcontext *ctx,
796 GLuint start,
797 GLuint count,
798 GLuint flags )
799 {
800 fxMesaContext fxMesa = FX_CONTEXT(ctx);
801 GrVertex *fxVB = fxMesa->verts;
802 (void) flags;
803
804 if (TDFX_DEBUG & VERBOSE_VARRAY) {
805 fprintf(stderr, "fx_render_vb_poly\n");
806 }
807
808 fxRenderPrimitive(ctx, GL_POLYGON);
809
810 grDrawVertexArrayContiguous( GR_POLYGON, count-start,
811 fxVB + start, sizeof(GrVertex));
812 }
813
814 static void fx_render_vb_noop( GLcontext *ctx,
815 GLuint start,
816 GLuint count,
817 GLuint flags )
818 {
819 (void) (ctx && start && count && flags);
820 }
821
822 static void (*fx_render_tab_verts[GL_POLYGON+2])(GLcontext *,
823 GLuint,
824 GLuint,
825 GLuint) =
826 {
827 fx_render_vb_points,
828 fx_render_vb_lines,
829 fx_render_vb_line_loop,
830 fx_render_vb_line_strip,
831 fx_render_vb_triangles,
832 fx_render_vb_tri_strip,
833 fx_render_vb_tri_fan,
834 fx_render_vb_quads,
835 fx_render_vb_quad_strip,
836 fx_render_vb_poly,
837 fx_render_vb_noop,
838 };
839
840
841 /**********************************************************************/
842 /* Render whole (indexed) begin/end objects */
843 /**********************************************************************/
844
845
846 #define VERT(x) (vertptr + x)
847
848 #define RENDER_POINTS( start, count ) \
849 for ( ; start < count ; start++) \
850 grDrawPoint( VERT(ELT(start)) );
851
852 #define RENDER_LINE( v0, v1 ) \
853 grDrawLine( VERT(v0), VERT(v1) )
854
855 #define RENDER_TRI( v0, v1, v2 ) \
856 grDrawTriangle( VERT(v0), VERT(v1), VERT(v2) )
857
858 #define RENDER_QUAD( v0, v1, v2, v3 ) \
859 fx_draw_quad( fxMesa, VERT(v0), VERT(v1), VERT(v2), VERT(v3) )
860
861 #define INIT(x) fxRenderPrimitive( ctx, x )
862
863 #undef LOCAL_VARS
864 #define LOCAL_VARS \
865 fxMesaContext fxMesa = FX_CONTEXT(ctx); \
866 GrVertex *vertptr = fxMesa->verts; \
867 const GLuint * const elt = TNL_CONTEXT(ctx)->vb.Elts; \
868 (void) elt;
869
870 #define RESET_STIPPLE
871 #define RESET_OCCLUSION
872 #define PRESERVE_VB_DEFS
873
874 /* Elts, no clipping.
875 */
876 #undef ELT
877 #undef TAG
878 #define TAG(x) fx_##x##_elts
879 #define ELT(x) elt[x]
880 #include "../common/t_dd_rendertmp.h"
881
882 /* Verts, no clipping.
883 */
884 #undef ELT
885 #undef TAG
886 #define TAG(x) fx_##x##_verts
887 #define ELT(x) x
888 /*#include "../common/t_dd_rendertmp.h"*/ /* we have fx_render_vb_* now */
889
890
891
892 /**********************************************************************/
893 /* Render clipped primitives */
894 /**********************************************************************/
895
896
897
898 static void fxRenderClippedPoly( GLcontext *ctx, const GLuint *elts,
899 GLuint n )
900 {
901 fxMesaContext fxMesa = FX_CONTEXT(ctx);
902 TNLcontext *tnl = TNL_CONTEXT(ctx);
903 struct vertex_buffer *VB = &tnl->vb;
904 GLuint prim = fxMesa->render_primitive;
905
906 /* Render the new vertices as an unclipped polygon.
907 */
908 {
909 GLuint *tmp = VB->Elts;
910 VB->Elts = (GLuint *)elts;
911 tnl->Driver.Render.PrimTabElts[GL_POLYGON]( ctx, 0, n,
912 PRIM_BEGIN|PRIM_END );
913 VB->Elts = tmp;
914 }
915
916 /* Restore the render primitive
917 */
918 if (prim != GL_POLYGON)
919 tnl->Driver.Render.PrimitiveNotify( ctx, prim );
920 }
921
922
923 static void fxFastRenderClippedPoly( GLcontext *ctx, const GLuint *elts,
924 GLuint n )
925 {
926 fxMesaContext fxMesa = FX_CONTEXT( ctx );
927 GrVertex *vertptr = fxMesa->verts;
928 const GrVertex *start = VERT(elts[0]);
929 int i;
930 for (i = 2 ; i < n ; i++) {
931 grDrawTriangle( start, VERT(elts[i-1]), VERT(elts[i]) );
932 }
933 }
934
935 /**********************************************************************/
936 /* Choose render functions */
937 /**********************************************************************/
938
939
940 #define POINT_FALLBACK (DD_POINT_SMOOTH)
941 #define LINE_FALLBACK (DD_LINE_STIPPLE)
942 #define TRI_FALLBACK (DD_TRI_SMOOTH | DD_TRI_STIPPLE)
943 #define ANY_FALLBACK_FLAGS (POINT_FALLBACK | LINE_FALLBACK | TRI_FALLBACK)
944 #define ANY_RASTER_FLAGS (DD_FLATSHADE | DD_TRI_LIGHT_TWOSIDE | DD_TRI_OFFSET \
945 | DD_TRI_UNFILLED)
946
947
948
949 void fxDDChooseRenderState(GLcontext *ctx)
950 {
951 TNLcontext *tnl = TNL_CONTEXT(ctx);
952 fxMesaContext fxMesa = FX_CONTEXT(ctx);
953 GLuint flags = ctx->_TriangleCaps;
954 GLuint index = 0;
955
956 if (flags & (ANY_FALLBACK_FLAGS|ANY_RASTER_FLAGS)) {
957 if (flags & ANY_RASTER_FLAGS) {
958 if (flags & DD_TRI_LIGHT_TWOSIDE) index |= FX_TWOSIDE_BIT;
959 if (flags & DD_TRI_OFFSET) index |= FX_OFFSET_BIT;
960 if (flags & DD_TRI_UNFILLED) index |= FX_UNFILLED_BIT;
961 if (flags & DD_FLATSHADE) index |= FX_FLAT_BIT;
962 }
963
964 fxMesa->draw_point = fx_draw_point;
965 fxMesa->draw_line = fx_draw_line;
966 fxMesa->draw_tri = fx_draw_triangle;
967
968 /* Hook in fallbacks for specific primitives.
969 * [dBorca] Hack alert:
970 * If we're in FSAA mode, we always do anti-aliased primitives.
971 */
972 if (flags & (POINT_FALLBACK|
973 LINE_FALLBACK|
974 TRI_FALLBACK))
975 {
976 if (fxMesa->verbose) {
977 fprintf(stderr, "Voodoo ! fallback (%x), raster (%x)\n",
978 flags & ANY_FALLBACK_FLAGS, flags & ANY_RASTER_FLAGS);
979 }
980
981 if (flags & POINT_FALLBACK)
982 fxMesa->draw_point = fx_fallback_point;
983
984 if (flags & LINE_FALLBACK)
985 fxMesa->draw_line = fx_fallback_line;
986
987 if (flags & TRI_FALLBACK)
988 fxMesa->draw_tri = fx_fallback_tri;
989
990 index |= FX_FALLBACK_BIT;
991 }
992 }
993
994 tnl->Driver.Render.Points = rast_tab[index].points;
995 tnl->Driver.Render.Line = rast_tab[index].line;
996 tnl->Driver.Render.ClippedLine = rast_tab[index].line;
997 tnl->Driver.Render.Triangle = rast_tab[index].triangle;
998 tnl->Driver.Render.Quad = rast_tab[index].quad;
999
1000 if (index == 0) {
1001 tnl->Driver.Render.PrimTabVerts = fx_render_tab_verts;
1002 tnl->Driver.Render.PrimTabElts = fx_render_tab_elts;
1003 tnl->Driver.Render.ClippedPolygon = fxFastRenderClippedPoly;
1004 } else {
1005 tnl->Driver.Render.PrimTabVerts = _tnl_render_tab_verts;
1006 tnl->Driver.Render.PrimTabElts = _tnl_render_tab_elts;
1007 tnl->Driver.Render.ClippedPolygon = fxRenderClippedPoly;
1008 }
1009
1010 fxMesa->render_index = index;
1011 }
1012
1013
1014 /**********************************************************************/
1015 /* Runtime render state and callbacks */
1016 /**********************************************************************/
1017
1018 static void fxRunPipeline( GLcontext *ctx )
1019 {
1020 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1021 GLuint new_gl_state = fxMesa->new_gl_state;
1022
1023 if (TDFX_DEBUG & VERBOSE_PIPELINE) {
1024 fprintf(stderr, "fxRunPipeline()\n");
1025 }
1026
1027 #if 0
1028 /* Recalculate fog table on projection matrix changes. This used to
1029 * be triggered by the NearFar callback.
1030 */
1031 if (new_gl_state & _NEW_PROJECTION)
1032 fxMesa->new_state |= FX_NEW_FOG;
1033 /* [dBorca] Hack alert:
1034 * the above _NEW_PROJECTION is not included in the test below,
1035 * so we may end up with fxMesa->new_state still dirty by the end
1036 * of the routine. The fact is, we don't have NearFar callback
1037 * anymore. We could use fxDDDepthRange instead, but it seems
1038 * fog needs to be updated only by a fog-basis.
1039 * Implementing fxDDDepthRange correctly is another story:
1040 * that, together with a presumable fxDDViewport function would set
1041 * fxMesa->SetupNewInputs |= VERT_BIT_CLIP;
1042 * which might be useful in fxBuildVertices...
1043 */
1044 #endif
1045
1046 if (new_gl_state & (_FX_NEW_IS_IN_HARDWARE |
1047 _FX_NEW_RENDERSTATE |
1048 _FX_NEW_SETUP_FUNCTION |
1049 _NEW_TEXTURE)) {
1050
1051 if (new_gl_state & _FX_NEW_IS_IN_HARDWARE)
1052 fxCheckIsInHardware(ctx);
1053
1054 if (fxMesa->new_state)
1055 fxSetupFXUnits(ctx);
1056
1057 if (!fxMesa->fallback) {
1058 if (new_gl_state & _FX_NEW_RENDERSTATE)
1059 fxDDChooseRenderState(ctx);
1060
1061 if (new_gl_state & _FX_NEW_SETUP_FUNCTION)
1062 fxChooseVertexState(ctx);
1063 }
1064
1065 if (new_gl_state & _NEW_TEXTURE) {
1066 struct gl_texture_unit *t0 = &ctx->Texture.Unit[fxMesa->tmu_source[0]];
1067 struct gl_texture_unit *t1 = &ctx->Texture.Unit[fxMesa->tmu_source[1]];
1068
1069 if (t0 && t0->_Current && FX_TEXTURE_DATA(t0)) {
1070 fxMesa->s0scale = FX_TEXTURE_DATA(t0)->sScale;
1071 fxMesa->t0scale = FX_TEXTURE_DATA(t0)->tScale;
1072 fxMesa->inv_s0scale = 1.0 / fxMesa->s0scale;
1073 fxMesa->inv_t0scale = 1.0 / fxMesa->t0scale;
1074 }
1075
1076 if (t1 && t1->_Current && FX_TEXTURE_DATA(t1)) {
1077 fxMesa->s1scale = FX_TEXTURE_DATA(t1)->sScale;
1078 fxMesa->t1scale = FX_TEXTURE_DATA(t1)->tScale;
1079 fxMesa->inv_s1scale = 1.0 / fxMesa->s1scale;
1080 fxMesa->inv_t1scale = 1.0 / fxMesa->t1scale;
1081 }
1082 }
1083 }
1084
1085 fxMesa->new_gl_state = 0;
1086
1087 _tnl_run_pipeline( ctx );
1088 }
1089
1090
1091 static GLenum reduced_prim[GL_POLYGON+1] = {
1092 GL_POINTS,
1093 GL_LINES,
1094 GL_LINES,
1095 GL_LINES,
1096 GL_TRIANGLES,
1097 GL_TRIANGLES,
1098 GL_TRIANGLES,
1099 GL_TRIANGLES,
1100 GL_TRIANGLES,
1101 GL_TRIANGLES
1102 };
1103
1104
1105
1106 /* Always called between RenderStart and RenderFinish --> We already
1107 * hold the lock.
1108 */
1109 static void fxRasterPrimitive( GLcontext *ctx, GLenum prim )
1110 {
1111 fxMesaContext fxMesa = FX_CONTEXT( ctx );
1112
1113 fxMesa->raster_primitive = prim;
1114
1115 fxSetupCull(ctx);
1116 }
1117
1118
1119
1120 /* Determine the rasterized primitive when not drawing unfilled
1121 * polygons.
1122 */
1123 static void fxRenderPrimitive( GLcontext *ctx, GLenum prim )
1124 {
1125 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1126 GLuint rprim = reduced_prim[prim];
1127
1128 fxMesa->render_primitive = prim;
1129
1130 if (rprim == GL_TRIANGLES && (ctx->_TriangleCaps & DD_TRI_UNFILLED))
1131 return;
1132
1133 if (fxMesa->raster_primitive != rprim) {
1134 fxRasterPrimitive( ctx, rprim );
1135 }
1136 }
1137
1138 static void fxRenderFinish( GLcontext *ctx )
1139 {
1140 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1141
1142 if (fxMesa->render_index & FX_FALLBACK_BIT)
1143 _swrast_flush( ctx );
1144 }
1145
1146
1147
1148 /**********************************************************************/
1149 /* Manage total rasterization fallbacks */
1150 /**********************************************************************/
1151
1152 static char *fallbackStrings[] = {
1153 "1D/3D Texture map",
1154 "glDrawBuffer(GL_FRONT_AND_BACK)",
1155 "Separate specular color",
1156 "glEnable/Disable(GL_STENCIL_TEST)",
1157 "glRenderMode(selection or feedback)",
1158 "glLogicOp()",
1159 "Texture env mode",
1160 "Texture border",
1161 "glColorMask",
1162 "blend mode",
1163 "line stipple"
1164 };
1165
1166
1167 static char *getFallbackString(GLuint bit)
1168 {
1169 int i = 0;
1170 while (bit > 1) {
1171 i++;
1172 bit >>= 1;
1173 }
1174 return fallbackStrings[i];
1175 }
1176
1177
1178 void fxCheckIsInHardware( GLcontext *ctx )
1179 {
1180 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1181 TNLcontext *tnl = TNL_CONTEXT(ctx);
1182 GLuint oldfallback = fxMesa->fallback;
1183 GLuint newfallback = fxMesa->fallback = fx_check_IsInHardware( ctx );
1184
1185 if (newfallback) {
1186 if (oldfallback == 0) {
1187 if (fxMesa->verbose) {
1188 fprintf(stderr, "Voodoo ! enter SW 0x%08x %s\n", newfallback, getFallbackString(newfallback));
1189 }
1190 _swsetup_Wakeup( ctx );
1191 }
1192 }
1193 else {
1194 if (oldfallback) {
1195 _swrast_flush( ctx );
1196 tnl->Driver.Render.Start = fxCheckTexSizes;
1197 tnl->Driver.Render.Finish = fxRenderFinish;
1198 tnl->Driver.Render.PrimitiveNotify = fxRenderPrimitive;
1199 tnl->Driver.Render.ClippedPolygon = _tnl_RenderClippedPolygon;
1200 tnl->Driver.Render.ClippedLine = _tnl_RenderClippedLine;
1201 tnl->Driver.Render.PrimTabVerts = _tnl_render_tab_verts;
1202 tnl->Driver.Render.PrimTabElts = _tnl_render_tab_elts;
1203 tnl->Driver.Render.ResetLineStipple = _swrast_ResetLineStipple;
1204 tnl->Driver.Render.BuildVertices = fxBuildVertices;
1205 tnl->Driver.Render.Multipass = 0;
1206 fxChooseVertexState(ctx);
1207 fxDDChooseRenderState(ctx);
1208 if (fxMesa->verbose) {
1209 fprintf(stderr, "Voodoo ! leave SW 0x%08x %s\n", oldfallback, getFallbackString(oldfallback));
1210 }
1211 }
1212 }
1213 }
1214
1215 void fxDDInitTriFuncs( GLcontext *ctx )
1216 {
1217 TNLcontext *tnl = TNL_CONTEXT(ctx);
1218 static int firsttime = 1;
1219
1220 if (firsttime) {
1221 init_rast_tab();
1222 firsttime = 0;
1223 }
1224
1225 tnl->Driver.RunPipeline = fxRunPipeline;
1226 tnl->Driver.Render.Start = fxCheckTexSizes;
1227 tnl->Driver.Render.Finish = fxRenderFinish;
1228 tnl->Driver.Render.PrimitiveNotify = fxRenderPrimitive;
1229 tnl->Driver.Render.ClippedPolygon = _tnl_RenderClippedPolygon;
1230 tnl->Driver.Render.ClippedLine = _tnl_RenderClippedLine;
1231 tnl->Driver.Render.PrimTabVerts = _tnl_render_tab_verts;
1232 tnl->Driver.Render.PrimTabElts = _tnl_render_tab_elts;
1233 tnl->Driver.Render.ResetLineStipple = _swrast_ResetLineStipple;
1234 tnl->Driver.Render.BuildVertices = fxBuildVertices;
1235 tnl->Driver.Render.Multipass = 0;
1236
1237 (void) fx_print_vertex;
1238 }
1239
1240
1241 #else
1242
1243
1244 /*
1245 * Need this to provide at least one external definition.
1246 */
1247
1248 extern int gl_fx_dummy_function_tris(void);
1249 int
1250 gl_fx_dummy_function_tris(void)
1251 {
1252 return 0;
1253 }
1254
1255 #endif /* FX */