08ecc6dd81b95dce146a879c260e66c2d916dc3a
[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 #define INIT(x) fxRenderPrimitive( ctx, x )
534
535 static void fx_render_vb_points( GLcontext *ctx,
536 GLuint start,
537 GLuint count,
538 GLuint flags )
539 {
540 fxMesaContext fxMesa = FX_CONTEXT(ctx);
541 GrVertex *fxVB = fxMesa->verts;
542 GLint i;
543 (void) flags;
544
545 if (TDFX_DEBUG & VERBOSE_VARRAY) {
546 fprintf(stderr, "fx_render_vb_points\n");
547 }
548
549 INIT(GL_POINTS);
550
551 /* Adjust point coords */
552 for (i = start; i < count; i++) {
553 fxVB[i].x += PNT_X_OFFSET - TRI_X_OFFSET;
554 fxVB[i].y += PNT_Y_OFFSET - TRI_Y_OFFSET;
555 }
556
557 grDrawVertexArrayContiguous( GR_POINTS, count-start,
558 fxVB + start, sizeof(GrVertex));
559 /* restore point coords */
560 for (i = start; i < count; i++) {
561 fxVB[i].x -= PNT_X_OFFSET - TRI_X_OFFSET;
562 fxVB[i].y -= PNT_Y_OFFSET - TRI_Y_OFFSET;
563 }
564 }
565
566 static void fx_render_vb_line_strip( GLcontext *ctx,
567 GLuint start,
568 GLuint count,
569 GLuint flags )
570 {
571 fxMesaContext fxMesa = FX_CONTEXT(ctx);
572 GrVertex *fxVB = fxMesa->verts;
573 GLint i;
574 (void) flags;
575
576 if (TDFX_DEBUG & VERBOSE_VARRAY) {
577 fprintf(stderr, "fx_render_vb_line_strip\n");
578 }
579
580 INIT(GL_LINE_STRIP);
581
582 /* adjust line coords */
583 for (i = start; i < count; i++) {
584 fxVB[i].x += LINE_X_OFFSET - TRI_X_OFFSET;
585 fxVB[i].y += LINE_Y_OFFSET - TRI_Y_OFFSET;
586 }
587
588 grDrawVertexArrayContiguous( GR_LINE_STRIP, count-start,
589 fxVB + start, sizeof(GrVertex));
590
591 /* restore line coords */
592 for (i = start; i < count; i++) {
593 fxVB[i].x -= LINE_X_OFFSET - TRI_X_OFFSET;
594 fxVB[i].y -= LINE_Y_OFFSET - TRI_Y_OFFSET;
595 }
596 }
597
598 static void fx_render_vb_line_loop( GLcontext *ctx,
599 GLuint start,
600 GLuint count,
601 GLuint flags )
602 {
603 fxMesaContext fxMesa = FX_CONTEXT(ctx);
604 GrVertex *fxVB = fxMesa->verts;
605 GLint i;
606 GLint j = start;
607 (void) flags;
608
609 if (TDFX_DEBUG & VERBOSE_VARRAY) {
610 fprintf(stderr, "fx_render_vb_line_loop\n");
611 }
612
613 INIT(GL_LINE_LOOP);
614
615 if (!(flags & PRIM_BEGIN)) {
616 j++;
617 }
618
619 /* adjust line coords */
620 for (i = start; i < count; i++) {
621 fxVB[i].x += LINE_X_OFFSET - TRI_X_OFFSET;
622 fxVB[i].y += LINE_Y_OFFSET - TRI_Y_OFFSET;
623 }
624
625 grDrawVertexArrayContiguous( GR_LINE_STRIP, count-j,
626 fxVB + j, sizeof(GrVertex));
627
628 if (flags & PRIM_END)
629 grDrawLine( fxVB + (count - 1),
630 fxVB + start );
631
632 /* restore line coords */
633 for (i = start; i < count; i++) {
634 fxVB[i].x -= LINE_X_OFFSET - TRI_X_OFFSET;
635 fxVB[i].y -= LINE_Y_OFFSET - TRI_Y_OFFSET;
636 }
637 }
638
639 static void fx_render_vb_lines( GLcontext *ctx,
640 GLuint start,
641 GLuint count,
642 GLuint flags )
643 {
644 fxMesaContext fxMesa = FX_CONTEXT(ctx);
645 GrVertex *fxVB = fxMesa->verts;
646 GLint i;
647 (void) flags;
648
649 if (TDFX_DEBUG & VERBOSE_VARRAY) {
650 fprintf(stderr, "fx_render_vb_lines\n");
651 }
652
653 INIT(GL_LINES);
654
655 /* adjust line coords */
656 for (i = start; i < count; i++) {
657 fxVB[i].x += LINE_X_OFFSET - TRI_X_OFFSET;
658 fxVB[i].y += LINE_Y_OFFSET - TRI_Y_OFFSET;
659 }
660
661 grDrawVertexArrayContiguous( GR_LINES, count-start,
662 fxVB + start, sizeof(GrVertex));
663
664 /* restore line coords */
665 for (i = start; i < count; i++) {
666 fxVB[i].x -= LINE_X_OFFSET - TRI_X_OFFSET;
667 fxVB[i].y -= LINE_Y_OFFSET - TRI_Y_OFFSET;
668 }
669 }
670
671 static void fx_render_vb_triangles( GLcontext *ctx,
672 GLuint start,
673 GLuint count,
674 GLuint flags )
675 {
676 fxMesaContext fxMesa = FX_CONTEXT(ctx);
677 GrVertex *fxVB = fxMesa->verts;
678 (void) flags;
679
680 if (TDFX_DEBUG & VERBOSE_VARRAY) {
681 fprintf(stderr, "fx_render_vb_triangles\n");
682 }
683
684 INIT(GL_TRIANGLES);
685
686 #if 0
687 /* [dBorca]
688 * apparently, this causes troubles with some programs (GLExcess);
689 * might be a bug in Glide... However, "grDrawVertexArrayContiguous"
690 * eventually calls "grDrawTriangle" for GR_TRIANGLES, so we're better
691 * off doing it by hand...
692 */
693 grDrawVertexArrayContiguous( GR_TRIANGLES, count-start,
694 fxVB + start, sizeof(GrVertex));
695 #else
696 {
697 GLuint j;
698 for (j=start+2; j<count; j+=3) {
699 grDrawTriangle(fxVB + (j-2), fxVB + (j-1), fxVB + j);
700 }
701 }
702 #endif
703 }
704
705
706 static void fx_render_vb_tri_strip( GLcontext *ctx,
707 GLuint start,
708 GLuint count,
709 GLuint flags )
710 {
711 fxMesaContext fxMesa = FX_CONTEXT(ctx);
712 GrVertex *fxVB = fxMesa->verts;
713 int mode;
714 (void) flags;
715
716 if (TDFX_DEBUG & VERBOSE_VARRAY) {
717 fprintf(stderr, "fx_render_vb_tri_strip\n");
718 }
719
720 INIT(GL_TRIANGLE_STRIP);
721
722 if (flags & PRIM_PARITY)
723 mode = GR_TRIANGLE_STRIP_CONTINUE;
724 else
725 mode = GR_TRIANGLE_STRIP;
726
727 grDrawVertexArrayContiguous( mode, count-start,
728 fxVB + start, sizeof(GrVertex));
729 }
730
731
732 static void fx_render_vb_tri_fan( GLcontext *ctx,
733 GLuint start,
734 GLuint count,
735 GLuint flags )
736 {
737 fxMesaContext fxMesa = FX_CONTEXT(ctx);
738 GrVertex *fxVB = fxMesa->verts;
739 (void) flags;
740
741 if (TDFX_DEBUG & VERBOSE_VARRAY) {
742 fprintf(stderr, "fx_render_vb_tri_fan\n");
743 }
744
745 INIT(GL_TRIANGLE_FAN);
746
747 grDrawVertexArrayContiguous( GR_TRIANGLE_FAN, count-start,
748 fxVB + start, sizeof(GrVertex) );
749 }
750
751 static void fx_render_vb_quads( GLcontext *ctx,
752 GLuint start,
753 GLuint count,
754 GLuint flags )
755 {
756 fxMesaContext fxMesa = FX_CONTEXT(ctx);
757 GrVertex *fxVB = fxMesa->verts;
758 GLuint i;
759 (void) flags;
760
761 if (TDFX_DEBUG & VERBOSE_VARRAY) {
762 fprintf(stderr, "fx_render_vb_quads\n");
763 }
764
765 INIT(GL_QUADS);
766
767 for (i = start ; i < count-3 ; i += 4 ) {
768 #define VERT(x) (fxVB + (x))
769 grDrawTriangle( VERT(i), VERT(i+1), VERT(i+3) );
770 grDrawTriangle( VERT(i+1), VERT(i+2), VERT(i+3) );
771 #undef VERT
772 }
773 }
774
775 static void fx_render_vb_quad_strip( GLcontext *ctx,
776 GLuint start,
777 GLuint count,
778 GLuint flags )
779 {
780 fxMesaContext fxMesa = FX_CONTEXT(ctx);
781 GrVertex *fxVB = fxMesa->verts;
782 (void) flags;
783
784 if (TDFX_DEBUG & VERBOSE_VARRAY) {
785 fprintf(stderr, "fx_render_vb_quad_strip\n");
786 }
787
788 INIT(GL_QUAD_STRIP);
789
790 count -= (count-start)&1;
791
792 grDrawVertexArrayContiguous( GR_TRIANGLE_STRIP,
793 count-start, fxVB + start, sizeof(GrVertex));
794 }
795
796 static void fx_render_vb_poly( GLcontext *ctx,
797 GLuint start,
798 GLuint count,
799 GLuint flags )
800 {
801 fxMesaContext fxMesa = FX_CONTEXT(ctx);
802 GrVertex *fxVB = fxMesa->verts;
803 (void) flags;
804
805 if (TDFX_DEBUG & VERBOSE_VARRAY) {
806 fprintf(stderr, "fx_render_vb_poly\n");
807 }
808
809 INIT(GL_POLYGON);
810
811 grDrawVertexArrayContiguous( GR_POLYGON, count-start,
812 fxVB + start, sizeof(GrVertex));
813 }
814
815 static void fx_render_vb_noop( GLcontext *ctx,
816 GLuint start,
817 GLuint count,
818 GLuint flags )
819 {
820 (void) (ctx && start && count && flags);
821 }
822
823 static void (*fx_render_tab_verts[GL_POLYGON+2])(GLcontext *,
824 GLuint,
825 GLuint,
826 GLuint) =
827 {
828 fx_render_vb_points,
829 fx_render_vb_lines,
830 fx_render_vb_line_loop,
831 fx_render_vb_line_strip,
832 fx_render_vb_triangles,
833 fx_render_vb_tri_strip,
834 fx_render_vb_tri_fan,
835 fx_render_vb_quads,
836 fx_render_vb_quad_strip,
837 fx_render_vb_poly,
838 fx_render_vb_noop,
839 };
840 #undef INIT(x)
841
842
843 /**********************************************************************/
844 /* Render whole (indexed) begin/end objects */
845 /**********************************************************************/
846
847
848 #define VERT(x) (vertptr + x)
849
850 #define RENDER_POINTS( start, count ) \
851 for ( ; start < count ; start++) \
852 grDrawPoint( VERT(ELT(start)) );
853
854 #define RENDER_LINE( v0, v1 ) \
855 grDrawLine( VERT(v0), VERT(v1) )
856
857 #define RENDER_TRI( v0, v1, v2 ) \
858 grDrawTriangle( VERT(v0), VERT(v1), VERT(v2) )
859
860 #define RENDER_QUAD( v0, v1, v2, v3 ) \
861 fx_draw_quad( fxMesa, VERT(v0), VERT(v1), VERT(v2), VERT(v3) )
862
863 #define INIT(x) fxRenderPrimitive( ctx, x )
864
865 #undef LOCAL_VARS
866 #define LOCAL_VARS \
867 fxMesaContext fxMesa = FX_CONTEXT(ctx); \
868 GrVertex *vertptr = fxMesa->verts; \
869 const GLuint * const elt = TNL_CONTEXT(ctx)->vb.Elts; \
870 (void) elt;
871
872 #define RESET_STIPPLE
873 #define RESET_OCCLUSION
874 #define PRESERVE_VB_DEFS
875
876 /* Elts, no clipping.
877 */
878 #undef ELT
879 #undef TAG
880 #define TAG(x) fx_##x##_elts
881 #define ELT(x) elt[x]
882 #include "../common/t_dd_rendertmp.h"
883
884 /* Verts, no clipping.
885 */
886 #undef ELT
887 #undef TAG
888 #define TAG(x) fx_##x##_verts
889 #define ELT(x) x
890 /*#include "../common/t_dd_rendertmp.h"*/ /* we have fx_render_vb_* now */
891
892
893
894 /**********************************************************************/
895 /* Render clipped primitives */
896 /**********************************************************************/
897
898
899
900 static void fxRenderClippedPoly( GLcontext *ctx, const GLuint *elts,
901 GLuint n )
902 {
903 fxMesaContext fxMesa = FX_CONTEXT(ctx);
904 TNLcontext *tnl = TNL_CONTEXT(ctx);
905 struct vertex_buffer *VB = &tnl->vb;
906 GLuint prim = fxMesa->render_primitive;
907
908 /* Render the new vertices as an unclipped polygon.
909 */
910 {
911 GLuint *tmp = VB->Elts;
912 VB->Elts = (GLuint *)elts;
913 tnl->Driver.Render.PrimTabElts[GL_POLYGON]( ctx, 0, n,
914 PRIM_BEGIN|PRIM_END );
915 VB->Elts = tmp;
916 }
917
918 /* Restore the render primitive
919 */
920 if (prim != GL_POLYGON)
921 tnl->Driver.Render.PrimitiveNotify( ctx, prim );
922 }
923
924
925 static void fxFastRenderClippedPoly( GLcontext *ctx, const GLuint *elts,
926 GLuint n )
927 {
928 fxMesaContext fxMesa = FX_CONTEXT( ctx );
929 GrVertex *vertptr = fxMesa->verts;
930 const GrVertex *start = VERT(elts[0]);
931 int i;
932 for (i = 2 ; i < n ; i++) {
933 grDrawTriangle( start, VERT(elts[i-1]), VERT(elts[i]) );
934 }
935 }
936
937 /**********************************************************************/
938 /* Choose render functions */
939 /**********************************************************************/
940
941
942 #define POINT_FALLBACK (DD_POINT_SMOOTH)
943 #define LINE_FALLBACK (DD_LINE_STIPPLE)
944 #define TRI_FALLBACK (DD_TRI_SMOOTH | DD_TRI_STIPPLE)
945 #define ANY_FALLBACK_FLAGS (POINT_FALLBACK | LINE_FALLBACK | TRI_FALLBACK)
946 #define ANY_RASTER_FLAGS (DD_FLATSHADE | DD_TRI_LIGHT_TWOSIDE | DD_TRI_OFFSET \
947 | DD_TRI_UNFILLED)
948
949
950
951 void fxDDChooseRenderState(GLcontext *ctx)
952 {
953 TNLcontext *tnl = TNL_CONTEXT(ctx);
954 fxMesaContext fxMesa = FX_CONTEXT(ctx);
955 GLuint flags = ctx->_TriangleCaps;
956 GLuint index = 0;
957
958 if (flags & (ANY_FALLBACK_FLAGS|ANY_RASTER_FLAGS)) {
959 if (flags & ANY_RASTER_FLAGS) {
960 if (flags & DD_TRI_LIGHT_TWOSIDE) index |= FX_TWOSIDE_BIT;
961 if (flags & DD_TRI_OFFSET) index |= FX_OFFSET_BIT;
962 if (flags & DD_TRI_UNFILLED) index |= FX_UNFILLED_BIT;
963 if (flags & DD_FLATSHADE) index |= FX_FLAT_BIT;
964 }
965
966 fxMesa->draw_point = fx_draw_point;
967 fxMesa->draw_line = fx_draw_line;
968 fxMesa->draw_tri = fx_draw_triangle;
969
970 /* Hook in fallbacks for specific primitives.
971 * [dBorca] Hack alert:
972 * If we're in FSAA mode, we always do anti-aliased primitives.
973 */
974 if (flags & (POINT_FALLBACK|
975 LINE_FALLBACK|
976 TRI_FALLBACK))
977 {
978 if (fxMesa->verbose) {
979 fprintf(stderr, "Voodoo ! fallback (%x), raster (%x)\n",
980 flags & ANY_FALLBACK_FLAGS, flags & ANY_RASTER_FLAGS);
981 }
982
983 if (flags & POINT_FALLBACK)
984 fxMesa->draw_point = fx_fallback_point;
985
986 if (flags & LINE_FALLBACK)
987 fxMesa->draw_line = fx_fallback_line;
988
989 if (flags & TRI_FALLBACK)
990 fxMesa->draw_tri = fx_fallback_tri;
991
992 index |= FX_FALLBACK_BIT;
993 }
994 }
995
996 tnl->Driver.Render.Points = rast_tab[index].points;
997 tnl->Driver.Render.Line = rast_tab[index].line;
998 tnl->Driver.Render.ClippedLine = rast_tab[index].line;
999 tnl->Driver.Render.Triangle = rast_tab[index].triangle;
1000 tnl->Driver.Render.Quad = rast_tab[index].quad;
1001
1002 if (index == 0) {
1003 tnl->Driver.Render.PrimTabVerts = fx_render_tab_verts;
1004 tnl->Driver.Render.PrimTabElts = fx_render_tab_elts;
1005 tnl->Driver.Render.ClippedPolygon = fxFastRenderClippedPoly;
1006 } else {
1007 tnl->Driver.Render.PrimTabVerts = _tnl_render_tab_verts;
1008 tnl->Driver.Render.PrimTabElts = _tnl_render_tab_elts;
1009 tnl->Driver.Render.ClippedPolygon = fxRenderClippedPoly;
1010 }
1011
1012 fxMesa->render_index = index;
1013 }
1014
1015
1016 /**********************************************************************/
1017 /* Runtime render state and callbacks */
1018 /**********************************************************************/
1019
1020 static void fxRunPipeline( GLcontext *ctx )
1021 {
1022 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1023 GLuint new_gl_state = fxMesa->new_gl_state;
1024
1025 if (TDFX_DEBUG & VERBOSE_PIPELINE) {
1026 fprintf(stderr, "fxRunPipeline()\n");
1027 }
1028
1029 #if 0
1030 /* Recalculate fog table on projection matrix changes. This used to
1031 * be triggered by the NearFar callback.
1032 */
1033 if (new_gl_state & _NEW_PROJECTION)
1034 fxMesa->new_state |= FX_NEW_FOG;
1035 /* [dBorca] Hack alert:
1036 * the above _NEW_PROJECTION is not included in the test below,
1037 * so we may end up with fxMesa->new_state still dirty by the end
1038 * of the routine. The fact is, we don't have NearFar callback
1039 * anymore. We could use fxDDDepthRange instead, but it seems
1040 * fog needs to be updated only by a fog-basis.
1041 * Implementing fxDDDepthRange correctly is another story:
1042 * that, together with a presumable fxDDViewport function would set
1043 * fxMesa->SetupNewInputs |= VERT_BIT_CLIP;
1044 * which might be useful in fxBuildVertices...
1045 */
1046 #endif
1047
1048 if (new_gl_state & (_FX_NEW_IS_IN_HARDWARE |
1049 _FX_NEW_RENDERSTATE |
1050 _FX_NEW_SETUP_FUNCTION |
1051 _NEW_TEXTURE)) {
1052
1053 if (new_gl_state & _FX_NEW_IS_IN_HARDWARE)
1054 fxCheckIsInHardware(ctx);
1055
1056 if (fxMesa->new_state)
1057 fxSetupFXUnits(ctx);
1058
1059 if (!fxMesa->fallback) {
1060 if (new_gl_state & _FX_NEW_RENDERSTATE)
1061 fxDDChooseRenderState(ctx);
1062
1063 if (new_gl_state & _FX_NEW_SETUP_FUNCTION)
1064 fxChooseVertexState(ctx);
1065 }
1066
1067 if (new_gl_state & _NEW_TEXTURE) {
1068 struct gl_texture_unit *t0 = &ctx->Texture.Unit[fxMesa->tmu_source[0]];
1069 struct gl_texture_unit *t1 = &ctx->Texture.Unit[fxMesa->tmu_source[1]];
1070
1071 if (t0 && t0->_Current && FX_TEXTURE_DATA(t0)) {
1072 fxMesa->s0scale = FX_TEXTURE_DATA(t0)->sScale;
1073 fxMesa->t0scale = FX_TEXTURE_DATA(t0)->tScale;
1074 fxMesa->inv_s0scale = 1.0 / fxMesa->s0scale;
1075 fxMesa->inv_t0scale = 1.0 / fxMesa->t0scale;
1076 }
1077
1078 if (t1 && t1->_Current && FX_TEXTURE_DATA(t1)) {
1079 fxMesa->s1scale = FX_TEXTURE_DATA(t1)->sScale;
1080 fxMesa->t1scale = FX_TEXTURE_DATA(t1)->tScale;
1081 fxMesa->inv_s1scale = 1.0 / fxMesa->s1scale;
1082 fxMesa->inv_t1scale = 1.0 / fxMesa->t1scale;
1083 }
1084 }
1085 }
1086
1087 fxMesa->new_gl_state = 0;
1088
1089 _tnl_run_pipeline( ctx );
1090 }
1091
1092
1093 static GLenum reduced_prim[GL_POLYGON+1] = {
1094 GL_POINTS,
1095 GL_LINES,
1096 GL_LINES,
1097 GL_LINES,
1098 GL_TRIANGLES,
1099 GL_TRIANGLES,
1100 GL_TRIANGLES,
1101 GL_TRIANGLES,
1102 GL_TRIANGLES,
1103 GL_TRIANGLES
1104 };
1105
1106
1107
1108 /* Always called between RenderStart and RenderFinish --> We already
1109 * hold the lock.
1110 */
1111 static void fxRasterPrimitive( GLcontext *ctx, GLenum prim )
1112 {
1113 fxMesaContext fxMesa = FX_CONTEXT( ctx );
1114
1115 fxMesa->raster_primitive = prim;
1116
1117 fxSetupCull(ctx);
1118 }
1119
1120
1121
1122 /* Determine the rasterized primitive when not drawing unfilled
1123 * polygons.
1124 */
1125 static void fxRenderPrimitive( GLcontext *ctx, GLenum prim )
1126 {
1127 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1128 GLuint rprim = reduced_prim[prim];
1129
1130 fxMesa->render_primitive = prim;
1131
1132 if (rprim == GL_TRIANGLES && (ctx->_TriangleCaps & DD_TRI_UNFILLED))
1133 return;
1134
1135 if (fxMesa->raster_primitive != rprim) {
1136 fxRasterPrimitive( ctx, rprim );
1137 }
1138 }
1139
1140 static void fxRenderFinish( GLcontext *ctx )
1141 {
1142 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1143
1144 if (fxMesa->render_index & FX_FALLBACK_BIT)
1145 _swrast_flush( ctx );
1146 }
1147
1148
1149
1150 /**********************************************************************/
1151 /* Manage total rasterization fallbacks */
1152 /**********************************************************************/
1153
1154 static char *fallbackStrings[] = {
1155 "1D/3D Texture map",
1156 "glDrawBuffer(GL_FRONT_AND_BACK)",
1157 "Separate specular color",
1158 "glEnable/Disable(GL_STENCIL_TEST)",
1159 "glRenderMode(selection or feedback)",
1160 "glLogicOp()",
1161 "Texture env mode",
1162 "Texture border",
1163 "glColorMask",
1164 "blend mode",
1165 "line stipple"
1166 };
1167
1168
1169 static char *getFallbackString(GLuint bit)
1170 {
1171 int i = 0;
1172 while (bit > 1) {
1173 i++;
1174 bit >>= 1;
1175 }
1176 return fallbackStrings[i];
1177 }
1178
1179
1180 void fxCheckIsInHardware( GLcontext *ctx )
1181 {
1182 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1183 TNLcontext *tnl = TNL_CONTEXT(ctx);
1184 GLuint oldfallback = fxMesa->fallback;
1185 GLuint newfallback = fxMesa->fallback = fx_check_IsInHardware( ctx );
1186
1187 if (newfallback) {
1188 if (oldfallback == 0) {
1189 if (fxMesa->verbose) {
1190 fprintf(stderr, "Voodoo ! enter SW 0x%08x %s\n", newfallback, getFallbackString(newfallback));
1191 }
1192 _swsetup_Wakeup( ctx );
1193 }
1194 }
1195 else {
1196 if (oldfallback) {
1197 _swrast_flush( ctx );
1198 tnl->Driver.Render.Start = fxCheckTexSizes;
1199 tnl->Driver.Render.Finish = fxRenderFinish;
1200 tnl->Driver.Render.PrimitiveNotify = fxRenderPrimitive;
1201 tnl->Driver.Render.ClippedPolygon = _tnl_RenderClippedPolygon;
1202 tnl->Driver.Render.ClippedLine = _tnl_RenderClippedLine;
1203 tnl->Driver.Render.PrimTabVerts = _tnl_render_tab_verts;
1204 tnl->Driver.Render.PrimTabElts = _tnl_render_tab_elts;
1205 tnl->Driver.Render.ResetLineStipple = _swrast_ResetLineStipple;
1206 tnl->Driver.Render.BuildVertices = fxBuildVertices;
1207 tnl->Driver.Render.Multipass = 0;
1208 fxChooseVertexState(ctx);
1209 fxDDChooseRenderState(ctx);
1210 if (fxMesa->verbose) {
1211 fprintf(stderr, "Voodoo ! leave SW 0x%08x %s\n", oldfallback, getFallbackString(oldfallback));
1212 }
1213 }
1214 }
1215 }
1216
1217 void fxDDInitTriFuncs( GLcontext *ctx )
1218 {
1219 TNLcontext *tnl = TNL_CONTEXT(ctx);
1220 static int firsttime = 1;
1221
1222 if (firsttime) {
1223 init_rast_tab();
1224 firsttime = 0;
1225 }
1226
1227 tnl->Driver.RunPipeline = fxRunPipeline;
1228 tnl->Driver.Render.Start = fxCheckTexSizes;
1229 tnl->Driver.Render.Finish = fxRenderFinish;
1230 tnl->Driver.Render.PrimitiveNotify = fxRenderPrimitive;
1231 tnl->Driver.Render.ClippedPolygon = _tnl_RenderClippedPolygon;
1232 tnl->Driver.Render.ClippedLine = _tnl_RenderClippedLine;
1233 tnl->Driver.Render.PrimTabVerts = _tnl_render_tab_verts;
1234 tnl->Driver.Render.PrimTabElts = _tnl_render_tab_elts;
1235 tnl->Driver.Render.ResetLineStipple = _swrast_ResetLineStipple;
1236 tnl->Driver.Render.BuildVertices = fxBuildVertices;
1237 tnl->Driver.Render.Multipass = 0;
1238
1239 (void) fx_print_vertex;
1240 }
1241
1242
1243 #else
1244
1245
1246 /*
1247 * Need this to provide at least one external definition.
1248 */
1249
1250 extern int gl_fx_dummy_function_tris(void);
1251 int
1252 gl_fx_dummy_function_tris(void)
1253 {
1254 return 0;
1255 }
1256
1257 #endif /* FX */