8743148b491be3943ff3a7204cbe187af70f6877
[mesa.git] / src / mesa / drivers / glide / fxdd.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 5.1
4 *
5 * Copyright (C) 1999-2003 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 * David Bucciarelli
27 * Brian Paul
28 * Daryll Strauss
29 * Keith Whitwell
30 * Daniel Borca
31 * Hiroshi Morii
32 */
33
34 /* fxdd.c - 3Dfx VooDoo Mesa device driver functions */
35
36
37 #ifdef HAVE_CONFIG_H
38 #include "conf.h"
39 #endif
40
41 #if defined(FX)
42
43 #include "image.h"
44 #include "mtypes.h"
45 #include "fxdrv.h"
46 #include "enums.h"
47 #include "extensions.h"
48 #include "macros.h"
49 #include "texstore.h"
50 #include "teximage.h"
51 #include "swrast/swrast.h"
52 #include "swrast/s_context.h"
53 #include "swrast_setup/swrast_setup.h"
54 #include "tnl/tnl.h"
55 #include "tnl/t_context.h"
56 #include "tnl/t_pipeline.h"
57 #include "array_cache/acache.h"
58
59
60
61 /* lookup table for scaling 4 bit colors up to 8 bits */
62 GLuint FX_rgb_scale_4[16] = {
63 0, 17, 34, 51, 68, 85, 102, 119,
64 136, 153, 170, 187, 204, 221, 238, 255
65 };
66
67 /* lookup table for scaling 5 bit colors up to 8 bits */
68 GLuint FX_rgb_scale_5[32] = {
69 0, 8, 16, 25, 33, 41, 49, 58,
70 66, 74, 82, 90, 99, 107, 115, 123,
71 132, 140, 148, 156, 165, 173, 181, 189,
72 197, 206, 214, 222, 230, 239, 247, 255
73 };
74
75 /* lookup table for scaling 6 bit colors up to 8 bits */
76 GLuint FX_rgb_scale_6[64] = {
77 0, 4, 8, 12, 16, 20, 24, 28,
78 32, 36, 40, 45, 49, 53, 57, 61,
79 65, 69, 73, 77, 81, 85, 89, 93,
80 97, 101, 105, 109, 113, 117, 121, 125,
81 130, 134, 138, 142, 146, 150, 154, 158,
82 162, 166, 170, 174, 178, 182, 186, 190,
83 194, 198, 202, 206, 210, 215, 219, 223,
84 227, 231, 235, 239, 243, 247, 251, 255
85 };
86
87
88 /*
89 * Disable color by masking out R, G, B, A
90 */
91 static void fxDisableColor (fxMesaContext fxMesa)
92 {
93 if (fxMesa->colDepth != 16) {
94 /* 32bpp mode or 15bpp mode */
95 fxMesa->Glide.grColorMaskExt(FXFALSE, FXFALSE, FXFALSE, FXFALSE);
96 } else {
97 /* 16 bpp mode */
98 grColorMask(FXFALSE, FXFALSE);
99 }
100 }
101
102
103 /**********************************************************************/
104 /***** Miscellaneous functions *****/
105 /**********************************************************************/
106
107 /* Return buffer size information */
108 static void
109 fxDDBufferSize(GLframebuffer *buffer, GLuint *width, GLuint *height)
110 {
111 GET_CURRENT_CONTEXT(ctx);
112 if (ctx && FX_CONTEXT(ctx)) {
113 fxMesaContext fxMesa = FX_CONTEXT(ctx);
114
115 if (TDFX_DEBUG & VERBOSE_DRIVER) {
116 fprintf(stderr, "%s(...)\n", __FUNCTION__);
117 }
118
119 *width = fxMesa->width;
120 *height = fxMesa->height;
121 }
122 }
123
124
125 /* Implements glClearColor() */
126 static void
127 fxDDClearColor(GLcontext * ctx, const GLfloat color[4])
128 {
129 fxMesaContext fxMesa = FX_CONTEXT(ctx);
130 GLubyte col[4];
131
132 if (TDFX_DEBUG & VERBOSE_DRIVER) {
133 fprintf(stderr, "%s(%f, %f, %f, %f)\n", __FUNCTION__,
134 color[0], color[1], color[2], color[3]);
135 }
136
137 CLAMPED_FLOAT_TO_UBYTE(col[0], color[0]);
138 CLAMPED_FLOAT_TO_UBYTE(col[1], color[1]);
139 CLAMPED_FLOAT_TO_UBYTE(col[2], color[2]);
140 CLAMPED_FLOAT_TO_UBYTE(col[3], color[3]);
141
142 fxMesa->clearC = FXCOLOR4(col);
143 fxMesa->clearA = col[3];
144 }
145
146
147 /* Clear the color and/or depth buffers */
148 static void fxDDClear( GLcontext *ctx,
149 GLbitfield mask, GLboolean all,
150 GLint x, GLint y, GLint width, GLint height )
151 {
152 fxMesaContext fxMesa = FX_CONTEXT(ctx);
153 GLbitfield softwareMask = mask & (DD_ACCUM_BIT);
154 const GLuint stencil_size = fxMesa->haveHwStencil ? ctx->Visual.stencilBits : 0;
155 const FxU32 clearD = (FxU32) (((1 << ctx->Visual.depthBits) - 1) * ctx->Depth.Clear);
156 const FxU8 clearS = (FxU8) (ctx->Stencil.Clear & 0xff);
157
158 /* [dBorca] Hack alert:
159 * if we set Mesa for 32bit depth, we'll get
160 * clearD == 0
161 * due to 32bit integer overflow!
162 */
163
164 if ( TDFX_DEBUG & MESA_VERBOSE ) {
165 fprintf( stderr, "%s( %d, %d, %d, %d )\n",
166 __FUNCTION__, (int) x, (int) y, (int) width, (int) height );
167 }
168
169 /* Need this check to respond to glScissor and clipping updates */
170 /* should also take care of FX_NEW_COLOR_MASK, FX_NEW_STENCIL, depth? */
171 if (fxMesa->new_state & FX_NEW_SCISSOR) {
172 fxSetupScissor(ctx);
173 fxMesa->new_state &= ~FX_NEW_SCISSOR;
174 }
175
176 /* we can't clear accum buffers */
177 mask &= ~(DD_ACCUM_BIT);
178
179 /*
180 * As per GL spec, stencil masking should be obeyed when clearing
181 */
182 if (mask & DD_STENCIL_BIT) {
183 if (!fxMesa->haveHwStencil || fxMesa->unitsState.stencilWriteMask != 0xff) {
184 /* Napalm seems to have trouble with stencil write masks != 0xff */
185 /* do stencil clear in software */
186 softwareMask |= DD_STENCIL_BIT;
187 mask &= ~(DD_STENCIL_BIT);
188 }
189 }
190
191 /*
192 * As per GL spec, color masking should be obeyed when clearing
193 */
194 if (ctx->Visual.greenBits != 8 && ctx->Visual.greenBits != 5) {
195 /* can only do color masking if running in 24/32bpp on Napalm */
196 if (ctx->Color.ColorMask[RCOMP] != ctx->Color.ColorMask[GCOMP] ||
197 ctx->Color.ColorMask[GCOMP] != ctx->Color.ColorMask[BCOMP]) {
198 softwareMask |= (mask & (DD_FRONT_LEFT_BIT | DD_BACK_LEFT_BIT));
199 mask &= ~(DD_FRONT_LEFT_BIT | DD_BACK_LEFT_BIT);
200 }
201 }
202
203 if (fxMesa->haveHwStencil) {
204 /*
205 * If we want to clear stencil, it must be enabled
206 * in the HW, even if the stencil test is not enabled
207 * in the OGL state.
208 */
209 BEGIN_BOARD_LOCK();
210 if (mask & DD_STENCIL_BIT) {
211 fxMesa->Glide.grStencilMaskExt(0xff /*fxMesa->unitsState.stencilWriteMask*/);
212 /* set stencil ref value = desired clear value */
213 fxMesa->Glide.grStencilFuncExt(GR_CMP_ALWAYS, clearS, 0xff);
214 fxMesa->Glide.grStencilOpExt(GR_STENCILOP_REPLACE,
215 GR_STENCILOP_REPLACE, GR_STENCILOP_REPLACE);
216 grEnable(GR_STENCIL_MODE_EXT);
217 }
218 else {
219 grDisable(GR_STENCIL_MODE_EXT);
220 }
221 END_BOARD_LOCK();
222 }
223
224 /*
225 * This may be ugly, but it's needed in order to work around a number
226 * of Glide bugs.
227 */
228 BEGIN_CLIP_LOOP();
229 {
230 /*
231 * This could probably be done fancier but doing each possible case
232 * explicitly is less error prone.
233 */
234 switch (mask & ~DD_STENCIL_BIT) {
235 case DD_BACK_LEFT_BIT | DD_DEPTH_BIT:
236 /* back buffer & depth */
237 /* FX_grColorMaskv_NoLock(ctx, true4); */ /* work around Voodoo3 bug */
238 grDepthMask(FXTRUE);
239 grRenderBuffer(GR_BUFFER_BACKBUFFER);
240 if (stencil_size > 0) {
241 fxMesa->Glide.grBufferClearExt(fxMesa->clearC,
242 fxMesa->clearA,
243 clearD, clearS);
244 }
245 else
246 grBufferClear(fxMesa->clearC,
247 fxMesa->clearA,
248 clearD);
249 if (!fxMesa->unitsState.depthTestEnabled) {
250 grDepthMask(FXFALSE);
251 }
252 break;
253 case DD_FRONT_LEFT_BIT | DD_DEPTH_BIT:
254 /* XXX it appears that the depth buffer isn't cleared when
255 * glRenderBuffer(GR_BUFFER_FRONTBUFFER) is set.
256 * This is a work-around/
257 */
258 /* clear depth */
259 grDepthMask(FXTRUE);
260 grRenderBuffer(GR_BUFFER_BACKBUFFER);
261 fxDisableColor(fxMesa);
262 if (stencil_size > 0)
263 fxMesa->Glide.grBufferClearExt(fxMesa->clearC,
264 fxMesa->clearA,
265 clearD, clearS);
266 else
267 grBufferClear(fxMesa->clearC,
268 fxMesa->clearA,
269 clearD);
270 /* clear front */
271 fxSetupColorMask(ctx);
272 grRenderBuffer(GR_BUFFER_FRONTBUFFER);
273 if (stencil_size > 0)
274 fxMesa->Glide.grBufferClearExt(fxMesa->clearC,
275 fxMesa->clearA,
276 clearD, clearS);
277 else
278 grBufferClear(fxMesa->clearC,
279 fxMesa->clearA,
280 clearD);
281 if (!fxMesa->unitsState.depthTestEnabled) {
282 grDepthMask(FXFALSE);
283 }
284 break;
285 case DD_BACK_LEFT_BIT:
286 /* back buffer only */
287 grDepthMask(FXFALSE);
288 grRenderBuffer(GR_BUFFER_BACKBUFFER);
289 if (stencil_size > 0)
290 fxMesa->Glide.grBufferClearExt(fxMesa->clearC,
291 fxMesa->clearA,
292 clearD, clearS);
293 else
294 grBufferClear(fxMesa->clearC,
295 fxMesa->clearA,
296 clearD);
297 if (fxMesa->unitsState.depthTestEnabled) {
298 grDepthMask(FXTRUE);
299 }
300 break;
301 case DD_FRONT_LEFT_BIT:
302 /* front buffer only */
303 grDepthMask(FXFALSE);
304 grRenderBuffer(GR_BUFFER_FRONTBUFFER);
305 if (stencil_size > 0)
306 fxMesa->Glide.grBufferClearExt(fxMesa->clearC,
307 fxMesa->clearA,
308 clearD, clearS);
309 else
310 grBufferClear(fxMesa->clearC,
311 fxMesa->clearA,
312 clearD);
313 if (fxMesa->unitsState.depthTestEnabled) {
314 grDepthMask(FXTRUE);
315 }
316 break;
317 case DD_FRONT_LEFT_BIT | DD_BACK_LEFT_BIT:
318 /* front and back */
319 grDepthMask(FXFALSE);
320 grRenderBuffer(GR_BUFFER_BACKBUFFER);
321 if (stencil_size > 0)
322 fxMesa->Glide.grBufferClearExt(fxMesa->clearC,
323 fxMesa->clearA,
324 clearD, clearS);
325 else
326 grBufferClear(fxMesa->clearC,
327 fxMesa->clearA,
328 clearD);
329 grRenderBuffer(GR_BUFFER_FRONTBUFFER);
330 if (stencil_size > 0)
331 fxMesa->Glide.grBufferClearExt(fxMesa->clearC,
332 fxMesa->clearA,
333 clearD, clearS);
334 else
335 grBufferClear(fxMesa->clearC,
336 fxMesa->clearA,
337 clearD);
338 if (fxMesa->unitsState.depthTestEnabled) {
339 grDepthMask(FXTRUE);
340 }
341 break;
342 case DD_FRONT_LEFT_BIT | DD_BACK_LEFT_BIT | DD_DEPTH_BIT:
343 /* clear front */
344 grDepthMask(FXFALSE);
345 grRenderBuffer(GR_BUFFER_FRONTBUFFER);
346 if (stencil_size > 0)
347 fxMesa->Glide.grBufferClearExt(fxMesa->clearC,
348 fxMesa->clearA,
349 clearD, clearS);
350 else
351 grBufferClear(fxMesa->clearC,
352 fxMesa->clearA,
353 clearD);
354 /* clear back and depth */
355 grDepthMask(FXTRUE);
356 grRenderBuffer(GR_BUFFER_BACKBUFFER);
357 if (stencil_size > 0)
358 fxMesa->Glide.grBufferClearExt(fxMesa->clearC,
359 fxMesa->clearA,
360 clearD, clearS);
361 else
362 grBufferClear(fxMesa->clearC,
363 fxMesa->clearA,
364 clearD);
365 if (!fxMesa->unitsState.depthTestEnabled) {
366 grDepthMask(FXFALSE);
367 }
368 break;
369 case DD_DEPTH_BIT:
370 /* just the depth buffer */
371 grRenderBuffer(GR_BUFFER_BACKBUFFER);
372 fxDisableColor(fxMesa);
373 grDepthMask(FXTRUE);
374 if (stencil_size > 0)
375 fxMesa->Glide.grBufferClearExt(fxMesa->clearC,
376 fxMesa->clearA,
377 clearD, clearS);
378 else
379 grBufferClear(fxMesa->clearC,
380 fxMesa->clearA,
381 clearD);
382 fxSetupColorMask(ctx);
383 if (ctx->Color._DrawDestMask & FRONT_LEFT_BIT) {
384 grRenderBuffer(GR_BUFFER_FRONTBUFFER);
385 }
386 if (!fxMesa->unitsState.depthTestEnabled) {
387 grDepthMask(FXFALSE);
388 }
389 break;
390 default:
391 /* clear no color buffers or depth buffer but might clear stencil */
392 if (stencil_size > 0 && (mask & DD_STENCIL_BIT)) {
393 /* XXX need this RenderBuffer call to work around Glide bug */
394 grRenderBuffer(GR_BUFFER_BACKBUFFER);
395 grDepthMask(FXFALSE);
396 fxDisableColor(fxMesa);
397 fxMesa->Glide.grBufferClearExt(fxMesa->clearC,
398 fxMesa->clearA,
399 clearD, clearS);
400 if (fxMesa->unitsState.depthTestEnabled) {
401 grDepthMask(FXTRUE);
402 }
403 fxSetupColorMask(ctx);
404 if (ctx->Color._DrawDestMask & FRONT_LEFT_BIT) {
405 grRenderBuffer(GR_BUFFER_FRONTBUFFER);
406 }
407 }
408 }
409 }
410 END_CLIP_LOOP();
411
412 if (fxMesa->haveHwStencil && (mask & DD_STENCIL_BIT)) {
413 /* We changed the stencil state above. Signal that we need to
414 * upload it again.
415 */
416 fxMesa->new_state |= FX_NEW_STENCIL;
417 }
418
419 if (softwareMask)
420 _swrast_Clear( ctx, softwareMask, all, x, y, width, height );
421 }
422
423
424 /* Set the buffer used for drawing */
425 /* XXX support for separate read/draw buffers hasn't been tested */
426 static void
427 fxDDSetDrawBuffer(GLcontext * ctx, GLenum mode)
428 {
429 fxMesaContext fxMesa = FX_CONTEXT(ctx);
430
431 if (TDFX_DEBUG & VERBOSE_DRIVER) {
432 fprintf(stderr, "%s(%x)\n", __FUNCTION__, (int)mode);
433 }
434
435 if (mode == GL_FRONT_LEFT) {
436 fxMesa->currentFB = GR_BUFFER_FRONTBUFFER;
437 grRenderBuffer(fxMesa->currentFB);
438 }
439 else if (mode == GL_BACK_LEFT) {
440 fxMesa->currentFB = GR_BUFFER_BACKBUFFER;
441 grRenderBuffer(fxMesa->currentFB);
442 }
443 else if (mode == GL_NONE) {
444 fxDisableColor(fxMesa);
445 }
446 else {
447 /* we'll need a software fallback */
448 /* XXX not implemented */
449 }
450
451 /* update s/w fallback state */
452 _swrast_DrawBuffer(ctx, mode);
453 }
454
455
456 static void
457 fxDDDrawBitmap2 (GLcontext *ctx, GLint px, GLint py,
458 GLsizei width, GLsizei height,
459 const struct gl_pixelstore_attrib *unpack,
460 const GLubyte *bitmap)
461 {
462 fxMesaContext fxMesa = FX_CONTEXT(ctx);
463 SWcontext *swrast = SWRAST_CONTEXT(ctx);
464 GrLfbInfo_t info;
465 GrLfbWriteMode_t mode;
466 FxU16 color;
467 const struct gl_pixelstore_attrib *finalUnpack;
468 struct gl_pixelstore_attrib scissoredUnpack;
469
470 /* check if there's any raster operations enabled which we can't handle */
471 if ((swrast->_RasterMask & (ALPHATEST_BIT |
472 /*BLEND_BIT |*/ /* blending ok, through pixpipe */
473 DEPTH_BIT | /* could be done with RGB:DEPTH */
474 FOG_BIT | /* could be done with RGB:DEPTH */
475 LOGIC_OP_BIT |
476 /*CLIP_BIT |*/ /* clipping ok, below */
477 STENCIL_BIT |
478 /*MASKING_BIT |*/ /* masking ok, test follows */
479 ALPHABUF_BIT | /* nope! see 565 span kludge */
480 MULTI_DRAW_BIT |
481 OCCLUSION_BIT | /* nope! at least not yet */
482 TEXTURE_BIT |
483 FRAGPROG_BIT))
484 ||
485 ((swrast->_RasterMask & MASKING_BIT) /*&& (ctx->Visual.greenBits != 8)*/ && (ctx->Visual.greenBits != 5))
486 ) {
487 _swrast_Bitmap(ctx, px, py, width, height, unpack, bitmap);
488 return;
489 }
490
491 /* make sure the pixelpipe is configured correctly */
492 fxSetupFXUnits(ctx);
493
494 if (ctx->Scissor.Enabled) {
495 /* This is a bit tricky, but by carefully adjusting the px, py,
496 * width, height, skipPixels and skipRows values we can do
497 * scissoring without special code in the rendering loop.
498 */
499
500 /* we'll construct a new pixelstore struct */
501 finalUnpack = &scissoredUnpack;
502 scissoredUnpack = *unpack;
503 if (scissoredUnpack.RowLength == 0)
504 scissoredUnpack.RowLength = width;
505
506 /* clip left */
507 if (px < ctx->Scissor.X) {
508 scissoredUnpack.SkipPixels += (ctx->Scissor.X - px);
509 width -= (ctx->Scissor.X - px);
510 px = ctx->Scissor.X;
511 }
512 /* clip right */
513 if (px + width >= ctx->Scissor.X + ctx->Scissor.Width) {
514 width -= (px + width - (ctx->Scissor.X + ctx->Scissor.Width));
515 }
516 /* clip bottom */
517 if (py < ctx->Scissor.Y) {
518 scissoredUnpack.SkipRows += (ctx->Scissor.Y - py);
519 height -= (ctx->Scissor.Y - py);
520 py = ctx->Scissor.Y;
521 }
522 /* clip top */
523 if (py + height >= ctx->Scissor.Y + ctx->Scissor.Height) {
524 height -= (py + height - (ctx->Scissor.Y + ctx->Scissor.Height));
525 }
526
527 if (width <= 0 || height <= 0)
528 return;
529 }
530 else {
531 finalUnpack = unpack;
532 }
533
534 /* compute pixel value */
535 {
536 GLint r = (GLint) (ctx->Current.RasterColor[RCOMP] * 255.0f);
537 GLint g = (GLint) (ctx->Current.RasterColor[GCOMP] * 255.0f);
538 GLint b = (GLint) (ctx->Current.RasterColor[BCOMP] * 255.0f);
539 GLint a = (GLint) (ctx->Current.RasterColor[ACOMP] * 255.0f);
540 if (fxMesa->colDepth == 15) {
541 color = TDFXPACKCOLOR1555(b, g, r, a);
542 mode = GR_LFBWRITEMODE_1555;
543 } else {
544 color = fxMesa->bgrOrder ? TDFXPACKCOLOR565(r, g, b) : TDFXPACKCOLOR565(b, g, r);
545 mode = GR_LFBWRITEMODE_565;
546 }
547 }
548
549 info.size = sizeof(info);
550 if (!grLfbLock(GR_LFB_WRITE_ONLY,
551 fxMesa->currentFB,
552 mode,
553 GR_ORIGIN_UPPER_LEFT, FXTRUE, &info)) {
554 _swrast_Bitmap(ctx, px, py, width, height, unpack, bitmap);
555 return;
556 }
557
558 {
559 const GLint winX = 0;
560 const GLint winY = fxMesa->height - 1;
561 /* The dest stride depends on the hardware and whether we're drawing
562 * to the front or back buffer. This compile-time test seems to do
563 * the job for now.
564 */
565 const GLint dstStride = info.strideInBytes / 2; /* stride in GLushorts */
566
567 GLint row;
568 /* compute dest address of bottom-left pixel in bitmap */
569 GLushort *dst = (GLushort *) info.lfbPtr
570 + (winY - py) * dstStride + (winX + px);
571
572 for (row = 0; row < height; row++) {
573 const GLubyte *src =
574 (const GLubyte *) _mesa_image_address(finalUnpack,
575 bitmap, width, height,
576 GL_COLOR_INDEX, GL_BITMAP,
577 0, row, 0);
578 if (finalUnpack->LsbFirst) {
579 /* least significan bit first */
580 GLubyte mask = 1U << (finalUnpack->SkipPixels & 0x7);
581 GLint col;
582 for (col = 0; col < width; col++) {
583 if (*src & mask) {
584 dst[col] = color;
585 }
586 if (mask == 128U) {
587 src++;
588 mask = 1U;
589 }
590 else {
591 mask = mask << 1;
592 }
593 }
594 if (mask != 1)
595 src++;
596 }
597 else {
598 /* most significan bit first */
599 GLubyte mask = 128U >> (finalUnpack->SkipPixels & 0x7);
600 GLint col;
601 for (col = 0; col < width; col++) {
602 if (*src & mask) {
603 dst[col] = color;
604 }
605 if (mask == 1U) {
606 src++;
607 mask = 128U;
608 }
609 else {
610 mask = mask >> 1;
611 }
612 }
613 if (mask != 128)
614 src++;
615 }
616 dst -= dstStride;
617 }
618 }
619
620 grLfbUnlock(GR_LFB_WRITE_ONLY, fxMesa->currentFB);
621 }
622
623 static void
624 fxDDDrawBitmap4 (GLcontext *ctx, GLint px, GLint py,
625 GLsizei width, GLsizei height,
626 const struct gl_pixelstore_attrib *unpack,
627 const GLubyte *bitmap)
628 {
629 fxMesaContext fxMesa = FX_CONTEXT(ctx);
630 SWcontext *swrast = SWRAST_CONTEXT(ctx);
631 GrLfbInfo_t info;
632 FxU32 color;
633 const struct gl_pixelstore_attrib *finalUnpack;
634 struct gl_pixelstore_attrib scissoredUnpack;
635
636 /* check if there's any raster operations enabled which we can't handle */
637 if ((swrast->_RasterMask & (ALPHATEST_BIT |
638 /*BLEND_BIT |*/ /* blending ok, through pixpipe */
639 DEPTH_BIT | /* could be done with RGB:DEPTH */
640 FOG_BIT | /* could be done with RGB:DEPTH */
641 LOGIC_OP_BIT |
642 /*CLIP_BIT |*/ /* clipping ok, below */
643 STENCIL_BIT |
644 /*MASKING_BIT |*/ /* masking ok, we're in 32bpp */
645 /*ALPHABUF_BIT |*//* alpha ok, we're in 32bpp */
646 MULTI_DRAW_BIT |
647 OCCLUSION_BIT | /* nope! at least not yet */
648 TEXTURE_BIT |
649 FRAGPROG_BIT))
650 ) {
651 _swrast_Bitmap(ctx, px, py, width, height, unpack, bitmap);
652 return;
653 }
654
655 /* make sure the pixelpipe is configured correctly */
656 fxSetupFXUnits(ctx);
657
658 if (ctx->Scissor.Enabled) {
659 /* This is a bit tricky, but by carefully adjusting the px, py,
660 * width, height, skipPixels and skipRows values we can do
661 * scissoring without special code in the rendering loop.
662 */
663
664 /* we'll construct a new pixelstore struct */
665 finalUnpack = &scissoredUnpack;
666 scissoredUnpack = *unpack;
667 if (scissoredUnpack.RowLength == 0)
668 scissoredUnpack.RowLength = width;
669
670 /* clip left */
671 if (px < ctx->Scissor.X) {
672 scissoredUnpack.SkipPixels += (ctx->Scissor.X - px);
673 width -= (ctx->Scissor.X - px);
674 px = ctx->Scissor.X;
675 }
676 /* clip right */
677 if (px + width >= ctx->Scissor.X + ctx->Scissor.Width) {
678 width -= (px + width - (ctx->Scissor.X + ctx->Scissor.Width));
679 }
680 /* clip bottom */
681 if (py < ctx->Scissor.Y) {
682 scissoredUnpack.SkipRows += (ctx->Scissor.Y - py);
683 height -= (ctx->Scissor.Y - py);
684 py = ctx->Scissor.Y;
685 }
686 /* clip top */
687 if (py + height >= ctx->Scissor.Y + ctx->Scissor.Height) {
688 height -= (py + height - (ctx->Scissor.Y + ctx->Scissor.Height));
689 }
690
691 if (width <= 0 || height <= 0)
692 return;
693 }
694 else {
695 finalUnpack = unpack;
696 }
697
698 /* compute pixel value */
699 {
700 GLint r = (GLint) (ctx->Current.RasterColor[RCOMP] * 255.0f);
701 GLint g = (GLint) (ctx->Current.RasterColor[GCOMP] * 255.0f);
702 GLint b = (GLint) (ctx->Current.RasterColor[BCOMP] * 255.0f);
703 GLint a = (GLint) (ctx->Current.RasterColor[ACOMP] * 255.0f);
704 color = TDFXPACKCOLOR8888(b, g, r, a);
705 }
706
707 info.size = sizeof(info);
708 if (!grLfbLock(GR_LFB_WRITE_ONLY,
709 fxMesa->currentFB,
710 GR_LFBWRITEMODE_8888,
711 GR_ORIGIN_UPPER_LEFT, FXTRUE, &info)) {
712 _swrast_Bitmap(ctx, px, py, width, height, unpack, bitmap);
713 return;
714 }
715
716 {
717 const GLint winX = 0;
718 const GLint winY = fxMesa->height - 1;
719 /* The dest stride depends on the hardware and whether we're drawing
720 * to the front or back buffer. This compile-time test seems to do
721 * the job for now.
722 */
723 const GLint dstStride = info.strideInBytes / 4; /* stride in GLuints */
724
725 GLint row;
726 /* compute dest address of bottom-left pixel in bitmap */
727 GLuint *dst = (GLuint *) info.lfbPtr
728 + (winY - py) * dstStride + (winX + px);
729
730 for (row = 0; row < height; row++) {
731 const GLubyte *src =
732 (const GLubyte *) _mesa_image_address(finalUnpack,
733 bitmap, width, height,
734 GL_COLOR_INDEX, GL_BITMAP,
735 0, row, 0);
736 if (finalUnpack->LsbFirst) {
737 /* least significan bit first */
738 GLubyte mask = 1U << (finalUnpack->SkipPixels & 0x7);
739 GLint col;
740 for (col = 0; col < width; col++) {
741 if (*src & mask) {
742 dst[col] = color;
743 }
744 if (mask == 128U) {
745 src++;
746 mask = 1U;
747 }
748 else {
749 mask = mask << 1;
750 }
751 }
752 if (mask != 1)
753 src++;
754 }
755 else {
756 /* most significan bit first */
757 GLubyte mask = 128U >> (finalUnpack->SkipPixels & 0x7);
758 GLint col;
759 for (col = 0; col < width; col++) {
760 if (*src & mask) {
761 dst[col] = color;
762 }
763 if (mask == 1U) {
764 src++;
765 mask = 128U;
766 }
767 else {
768 mask = mask >> 1;
769 }
770 }
771 if (mask != 128)
772 src++;
773 }
774 dst -= dstStride;
775 }
776 }
777
778 grLfbUnlock(GR_LFB_WRITE_ONLY, fxMesa->currentFB);
779 }
780
781
782 static void
783 fxDDReadPixels565 (GLcontext * ctx,
784 GLint x, GLint y,
785 GLsizei width, GLsizei height,
786 GLenum format, GLenum type,
787 const struct gl_pixelstore_attrib *packing,
788 GLvoid *dstImage)
789 {
790 if (ctx->_ImageTransferState/* & (IMAGE_SCALE_BIAS_BIT|IMAGE_MAP_COLOR_BIT)*/) {
791 _swrast_ReadPixels(ctx, x, y, width, height, format, type,
792 packing, dstImage);
793 return;
794 }
795 else {
796 fxMesaContext fxMesa = FX_CONTEXT(ctx);
797 GrLfbInfo_t info;
798
799 BEGIN_BOARD_LOCK();
800 if (grLfbLock(GR_LFB_READ_ONLY,
801 fxMesa->currentFB,
802 GR_LFBWRITEMODE_ANY,
803 GR_ORIGIN_UPPER_LEFT, FXFALSE, &info)) {
804 const GLint winX = 0;
805 const GLint winY = fxMesa->height - 1;
806 const GLint srcStride = info.strideInBytes / 2; /* stride in GLushorts */
807 const GLushort *src = (const GLushort *) info.lfbPtr
808 + (winY - y) * srcStride + (winX + x);
809 GLubyte *dst = (GLubyte *) _mesa_image_address(packing, dstImage,
810 width, height, format,
811 type, 0, 0, 0);
812 GLint dstStride =
813 _mesa_image_row_stride(packing, width, format, type);
814
815 if (format == GL_RGB && type == GL_UNSIGNED_BYTE) {
816 /* convert 5R6G5B into 8R8G8B */
817 GLint row, col;
818 const GLint halfWidth = width >> 1;
819 const GLint extraPixel = (width & 1);
820 for (row = 0; row < height; row++) {
821 GLubyte *d = dst;
822 for (col = 0; col < halfWidth; col++) {
823 const GLuint pixel = ((const GLuint *) src)[col];
824 *d++ = FX_rgb_scale_5[(pixel >> 11) & 0x1f];
825 *d++ = FX_rgb_scale_6[(pixel >> 5) & 0x3f];
826 *d++ = FX_rgb_scale_5[ pixel & 0x1f];
827 *d++ = FX_rgb_scale_5[(pixel >> 27) & 0x1f];
828 *d++ = FX_rgb_scale_6[(pixel >> 21) & 0x3f];
829 *d++ = FX_rgb_scale_5[(pixel >> 16) & 0x1f];
830 }
831 if (extraPixel) {
832 GLushort pixel = src[width - 1];
833 *d++ = FX_rgb_scale_5[(pixel >> 11) & 0x1f];
834 *d++ = FX_rgb_scale_6[(pixel >> 5) & 0x3f];
835 *d++ = FX_rgb_scale_5[ pixel & 0x1f];
836 }
837 dst += dstStride;
838 src -= srcStride;
839 }
840 }
841 else if (format == GL_RGBA && type == GL_UNSIGNED_BYTE) {
842 /* convert 5R6G5B into 8R8G8B8A */
843 GLint row, col;
844 const GLint halfWidth = width >> 1;
845 const GLint extraPixel = (width & 1);
846 for (row = 0; row < height; row++) {
847 GLubyte *d = dst;
848 for (col = 0; col < halfWidth; col++) {
849 const GLuint pixel = ((const GLuint *) src)[col];
850 *d++ = FX_rgb_scale_5[(pixel >> 11) & 0x1f];
851 *d++ = FX_rgb_scale_6[(pixel >> 5) & 0x3f];
852 *d++ = FX_rgb_scale_5[ pixel & 0x1f];
853 *d++ = 255;
854 *d++ = FX_rgb_scale_5[(pixel >> 27) & 0x1f];
855 *d++ = FX_rgb_scale_6[(pixel >> 21) & 0x3f];
856 *d++ = FX_rgb_scale_5[(pixel >> 16) & 0x1f];
857 *d++ = 255;
858 }
859 if (extraPixel) {
860 const GLushort pixel = src[width - 1];
861 *d++ = FX_rgb_scale_5[(pixel >> 11) & 0x1f];
862 *d++ = FX_rgb_scale_6[(pixel >> 5) & 0x3f];
863 *d++ = FX_rgb_scale_5[ pixel & 0x1f];
864 *d++ = 255;
865 }
866 dst += dstStride;
867 src -= srcStride;
868 }
869 }
870 else if (format == GL_RGB && type == GL_UNSIGNED_SHORT_5_6_5) {
871 /* directly memcpy 5R6G5B pixels into client's buffer */
872 const GLint widthInBytes = width * 2;
873 GLint row;
874 for (row = 0; row < height; row++) {
875 MEMCPY(dst, src, widthInBytes);
876 dst += dstStride;
877 src -= srcStride;
878 }
879 }
880 else {
881 grLfbUnlock(GR_LFB_READ_ONLY, fxMesa->currentFB);
882 END_BOARD_LOCK();
883 _swrast_ReadPixels(ctx, x, y, width, height, format, type,
884 packing, dstImage);
885 return;
886 }
887
888 grLfbUnlock(GR_LFB_READ_ONLY, fxMesa->currentFB);
889 }
890 END_BOARD_LOCK();
891 }
892 }
893
894 static void
895 fxDDReadPixels555 (GLcontext * ctx,
896 GLint x, GLint y,
897 GLsizei width, GLsizei height,
898 GLenum format, GLenum type,
899 const struct gl_pixelstore_attrib *packing,
900 GLvoid *dstImage)
901 {
902 if (ctx->_ImageTransferState/* & (IMAGE_SCALE_BIAS_BIT|IMAGE_MAP_COLOR_BIT)*/) {
903 _swrast_ReadPixels(ctx, x, y, width, height, format, type,
904 packing, dstImage);
905 return;
906 }
907 else {
908 fxMesaContext fxMesa = FX_CONTEXT(ctx);
909 GrLfbInfo_t info;
910
911 BEGIN_BOARD_LOCK();
912 if (grLfbLock(GR_LFB_READ_ONLY,
913 fxMesa->currentFB,
914 GR_LFBWRITEMODE_ANY,
915 GR_ORIGIN_UPPER_LEFT, FXFALSE, &info)) {
916 const GLint winX = 0;
917 const GLint winY = fxMesa->height - 1;
918 const GLint srcStride = info.strideInBytes / 2; /* stride in GLushorts */
919 const GLushort *src = (const GLushort *) info.lfbPtr
920 + (winY - y) * srcStride + (winX + x);
921 GLubyte *dst = (GLubyte *) _mesa_image_address(packing, dstImage,
922 width, height, format,
923 type, 0, 0, 0);
924 GLint dstStride =
925 _mesa_image_row_stride(packing, width, format, type);
926
927 if (format == GL_RGB && type == GL_UNSIGNED_BYTE) {
928 /* convert 5R5G5B into 8R8G8B */
929 GLint row, col;
930 const GLint halfWidth = width >> 1;
931 const GLint extraPixel = (width & 1);
932 for (row = 0; row < height; row++) {
933 GLubyte *d = dst;
934 for (col = 0; col < halfWidth; col++) {
935 const GLuint pixel = ((const GLuint *) src)[col];
936 *d++ = FX_rgb_scale_5[(pixel >> 10) & 0x1f];
937 *d++ = FX_rgb_scale_5[(pixel >> 5) & 0x1f];
938 *d++ = FX_rgb_scale_5[ pixel & 0x1f];
939 *d++ = FX_rgb_scale_5[(pixel >> 26) & 0x1f];
940 *d++ = FX_rgb_scale_5[(pixel >> 21) & 0x1f];
941 *d++ = FX_rgb_scale_5[(pixel >> 16) & 0x1f];
942 }
943 if (extraPixel) {
944 GLushort pixel = src[width - 1];
945 *d++ = FX_rgb_scale_5[(pixel >> 10) & 0x1f];
946 *d++ = FX_rgb_scale_5[(pixel >> 5) & 0x1f];
947 *d++ = FX_rgb_scale_5[ pixel & 0x1f];
948 }
949 dst += dstStride;
950 src -= srcStride;
951 }
952 }
953 else if (format == GL_RGBA && type == GL_UNSIGNED_BYTE) {
954 /* convert 5R6G5B into 8R8G8B8A */
955 GLint row, col;
956 const GLint halfWidth = width >> 1;
957 const GLint extraPixel = (width & 1);
958 for (row = 0; row < height; row++) {
959 GLubyte *d = dst;
960 for (col = 0; col < halfWidth; col++) {
961 const GLuint pixel = ((const GLuint *) src)[col];
962 *d++ = FX_rgb_scale_5[(pixel >> 10) & 0x1f];
963 *d++ = FX_rgb_scale_5[(pixel >> 5) & 0x1f];
964 *d++ = FX_rgb_scale_5[ pixel & 0x1f];
965 *d++ = (pixel & 0x8000) ? 255 : 0;
966 *d++ = FX_rgb_scale_5[(pixel >> 26) & 0x1f];
967 *d++ = FX_rgb_scale_5[(pixel >> 21) & 0x1f];
968 *d++ = FX_rgb_scale_5[(pixel >> 16) & 0x1f];
969 *d++ = (pixel & 0x80000000) ? 255 : 0;
970 }
971 if (extraPixel) {
972 const GLushort pixel = src[width - 1];
973 *d++ = FX_rgb_scale_5[(pixel >> 10) & 0x1f];
974 *d++ = FX_rgb_scale_5[(pixel >> 5) & 0x1f];
975 *d++ = FX_rgb_scale_5[ pixel & 0x1f];
976 *d++ = (pixel & 0x8000) ? 255 : 0;
977 }
978 dst += dstStride;
979 src -= srcStride;
980 }
981 }
982 else if (format == GL_RGB && type == GL_UNSIGNED_SHORT_5_6_5) {
983 /* directly memcpy 5R5G5B pixels into client's buffer */
984 const GLint widthInBytes = width * 2;
985 GLint row;
986 for (row = 0; row < height; row++) {
987 MEMCPY(dst, src, widthInBytes);
988 dst += dstStride;
989 src -= srcStride;
990 }
991 }
992 else {
993 grLfbUnlock(GR_LFB_READ_ONLY, fxMesa->currentFB);
994 END_BOARD_LOCK();
995 _swrast_ReadPixels(ctx, x, y, width, height, format, type,
996 packing, dstImage);
997 return;
998 }
999
1000 grLfbUnlock(GR_LFB_READ_ONLY, fxMesa->currentFB);
1001 }
1002 END_BOARD_LOCK();
1003 }
1004 }
1005
1006 static void
1007 fxDDReadPixels8888 (GLcontext * ctx,
1008 GLint x, GLint y,
1009 GLsizei width, GLsizei height,
1010 GLenum format, GLenum type,
1011 const struct gl_pixelstore_attrib *packing,
1012 GLvoid *dstImage)
1013 {
1014 if (ctx->_ImageTransferState/* & (IMAGE_SCALE_BIAS_BIT|IMAGE_MAP_COLOR_BIT)*/) {
1015 _swrast_ReadPixels(ctx, x, y, width, height, format, type,
1016 packing, dstImage);
1017 return;
1018 }
1019 else {
1020 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1021 GrLfbInfo_t info;
1022
1023 BEGIN_BOARD_LOCK();
1024 if (grLfbLock(GR_LFB_READ_ONLY,
1025 fxMesa->currentFB,
1026 GR_LFBWRITEMODE_ANY,
1027 GR_ORIGIN_UPPER_LEFT, FXFALSE, &info)) {
1028 const GLint winX = 0;
1029 const GLint winY = fxMesa->height - 1;
1030 const GLint srcStride = info.strideInBytes / 4; /* stride in GLuints */
1031 const GLuint *src = (const GLuint *) info.lfbPtr
1032 + (winY - y) * srcStride + (winX + x);
1033 GLubyte *dst = (GLubyte *) _mesa_image_address(packing, dstImage,
1034 width, height, format,
1035 type, 0, 0, 0);
1036 GLint dstStride =
1037 _mesa_image_row_stride(packing, width, format, type);
1038
1039 if (format == GL_RGB && type == GL_UNSIGNED_BYTE) {
1040 /* convert 8A8R8G8B into 8R8G8B */
1041 GLint row, col;
1042 for (row = 0; row < height; row++) {
1043 GLubyte *d = dst;
1044 for (col = 0; col < width; col++) {
1045 const GLuint pixel = ((const GLuint *) src)[col];
1046 *d++ = pixel >> 16;
1047 *d++ = pixel >> 8;
1048 *d++ = pixel;
1049 }
1050 dst += dstStride;
1051 src -= srcStride;
1052 }
1053 }
1054 else if (format == GL_RGBA && type == GL_UNSIGNED_BYTE) {
1055 /* 8A8R8G8B pixels into client's buffer */
1056 GLint row, col;
1057 for (row = 0; row < height; row++) {
1058 GLubyte *d = dst;
1059 for (col = 0; col < width; col++) {
1060 const GLuint pixel = ((const GLuint *) src)[col];
1061 *d++ = pixel >> 16;
1062 *d++ = pixel >> 8;
1063 *d++ = pixel;
1064 *d++ = pixel >> 24;
1065 }
1066 dst += dstStride;
1067 src -= srcStride;
1068 }
1069 }
1070 else if (format == GL_RGB && type == GL_UNSIGNED_SHORT_5_6_5) {
1071 /* convert 8A8R8G8B into 5R6G5B */
1072 GLint row, col;
1073 for (row = 0; row < height; row++) {
1074 GLushort *d = (GLushort *)dst;
1075 for (col = 0; col < width; col++) {
1076 const GLuint pixel = ((const GLuint *) src)[col];
1077 *d++ = (((pixel >> 16) & 0xf8) << 8) |
1078 (((pixel >> 8) & 0xfc) << 3) |
1079 ((pixel & 0xf8) >> 3);
1080 }
1081 dst += dstStride;
1082 src -= srcStride;
1083 }
1084 }
1085 else {
1086 grLfbUnlock(GR_LFB_READ_ONLY, fxMesa->currentFB);
1087 END_BOARD_LOCK();
1088 _swrast_ReadPixels(ctx, x, y, width, height, format, type,
1089 packing, dstImage);
1090 return;
1091 }
1092
1093 grLfbUnlock(GR_LFB_READ_ONLY, fxMesa->currentFB);
1094 }
1095 END_BOARD_LOCK();
1096 }
1097 }
1098
1099
1100 /* [dBorca] Hack alert:
1101 * not finished!!!
1102 * revise fallback tests and fix scissor; implement new formats
1103 * also write its siblings: 565 and 1555
1104 */
1105 void
1106 fxDDDrawPixels8888 (GLcontext * ctx, GLint x, GLint y,
1107 GLsizei width, GLsizei height,
1108 GLenum format, GLenum type,
1109 const struct gl_pixelstore_attrib *unpack,
1110 const GLvoid * pixels)
1111 {
1112 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1113 GrLfbInfo_t info;
1114
1115 if (ctx->Pixel.ZoomX != 1.0F ||
1116 ctx->Pixel.ZoomY != 1.0F ||
1117 (ctx->_ImageTransferState & (IMAGE_SCALE_BIAS_BIT|
1118 IMAGE_MAP_COLOR_BIT)) ||
1119 ctx->Color.AlphaEnabled ||
1120 ctx->Depth.Test ||
1121 ctx->Fog.Enabled ||
1122 ctx->Scissor.Enabled ||
1123 ctx->Stencil.Enabled ||
1124 !ctx->Color.ColorMask[0] ||
1125 !ctx->Color.ColorMask[1] ||
1126 !ctx->Color.ColorMask[2] ||
1127 !ctx->Color.ColorMask[3] ||
1128 ctx->Color.ColorLogicOpEnabled ||
1129 ctx->Texture._EnabledUnits ||
1130 ctx->Depth.OcclusionTest ||
1131 fxMesa->fallback)
1132 {
1133 _swrast_DrawPixels( ctx, x, y, width, height, format, type,
1134 unpack, pixels );
1135 return;
1136 }
1137
1138 /* lock early to make sure cliprects are right */
1139 BEGIN_BOARD_LOCK();
1140
1141 /* make sure the pixelpipe is configured correctly */
1142 fxSetupFXUnits(ctx);
1143
1144 /* look for clipmasks, giveup if region obscured */
1145 #if 0
1146 if (ctx->Color.DrawBuffer == GL_FRONT) {
1147 if (!inClipRects_Region(fxMesa, scrX, scrY, width, height)) {
1148 END_BOARD_LOCK(fxMesa);
1149 _swrast_DrawPixels(ctx, x, y, width, height, format, type, unpack, pixels);
1150 return;
1151 }
1152 }
1153 #endif
1154
1155 info.size = sizeof(info);
1156 if (!grLfbLock(GR_LFB_WRITE_ONLY,
1157 fxMesa->currentFB,
1158 GR_LFBWRITEMODE_8888,
1159 GR_ORIGIN_UPPER_LEFT, FXTRUE, &info)) {
1160 _swrast_DrawPixels(ctx, x, y, width, height, format, type, unpack, pixels);
1161 return;
1162 }
1163
1164 {
1165 const GLint winX = 0;
1166 const GLint winY = fxMesa->height - 1;
1167
1168 const GLint dstStride = info.strideInBytes / 4; /* stride in GLuints */
1169 GLuint *dst = (GLuint *) info.lfbPtr + (winY - y) * dstStride + (winX + x);
1170 const GLubyte *src = (GLubyte *)_mesa_image_address(unpack, pixels,
1171 width, height, format,
1172 type, 0, 0, 0);
1173 const GLint srcStride = _mesa_image_row_stride(unpack, width, format, type);
1174
1175 if (format == GL_RGBA && type == GL_UNSIGNED_BYTE) {
1176 /* directly memcpy 8A8R8G8B pixels to screen */
1177 const GLint widthInBytes = width * 4;
1178 GLint row;
1179 for (row = 0; row < height; row++) {
1180 MEMCPY(dst, src, widthInBytes);
1181 dst -= dstStride;
1182 src += srcStride;
1183 }
1184 }
1185 else {
1186 grLfbUnlock(GR_LFB_WRITE_ONLY, fxMesa->currentFB);
1187 END_BOARD_LOCK();
1188 _swrast_DrawPixels(ctx, x, y, width, height, format, type, unpack, pixels);
1189 return;
1190 }
1191
1192 }
1193
1194 grLfbUnlock(GR_LFB_WRITE_ONLY, fxMesa->currentFB);
1195 END_BOARD_LOCK();
1196 }
1197
1198
1199 static void
1200 fxDDFinish(GLcontext * ctx)
1201 {
1202 grFlush();
1203 }
1204
1205
1206
1207
1208
1209 /* KW: Put the word Mesa in the render string because quakeworld
1210 * checks for this rather than doing a glGet(GL_MAX_TEXTURE_SIZE).
1211 * Why?
1212 */
1213 static const GLubyte *
1214 fxDDGetString(GLcontext * ctx, GLenum name)
1215 {
1216 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1217
1218 switch (name) {
1219 case GL_RENDERER:
1220 return (GLubyte *)fxMesa->rendererString;
1221 default:
1222 return NULL;
1223 }
1224 }
1225
1226 static const struct gl_pipeline_stage *fx_pipeline[] = {
1227 &_tnl_vertex_transform_stage, /* TODO: Add the fastpath here */
1228 &_tnl_normal_transform_stage,
1229 &_tnl_lighting_stage,
1230 /*&_tnl_fog_coordinate_stage,*/ /* TODO: Omit fog stage ZZZ ZZZ ZZZ */
1231 &_tnl_texgen_stage,
1232 &_tnl_texture_transform_stage,
1233 /*&_tnl_point_attenuation_stage,*/ /* TODO: For AA primitives ZZZ ZZZ ZZZ */
1234 &_tnl_render_stage,
1235 0,
1236 };
1237
1238
1239
1240
1241 int
1242 fxDDInitFxMesaContext(fxMesaContext fxMesa)
1243 {
1244 int i;
1245 GLcontext *ctx = fxMesa->glCtx;
1246
1247 FX_setupGrVertexLayout();
1248
1249 fxMesa->color = 0xffffffff;
1250 fxMesa->clearC = 0;
1251 fxMesa->clearA = 0;
1252
1253 fxMesa->stats.swapBuffer = 0;
1254 fxMesa->stats.reqTexUpload = 0;
1255 fxMesa->stats.texUpload = 0;
1256 fxMesa->stats.memTexUpload = 0;
1257
1258 fxMesa->tmuSrc = FX_TMU_NONE;
1259 fxMesa->lastUnitsMode = FX_UM_NONE;
1260 fxTMInit(fxMesa);
1261
1262 /* FX units setup */
1263
1264 fxMesa->unitsState.alphaTestEnabled = GL_FALSE;
1265 fxMesa->unitsState.alphaTestFunc = GL_ALWAYS;
1266 fxMesa->unitsState.alphaTestRefValue = 0.0;
1267
1268 fxMesa->unitsState.blendEnabled = GL_FALSE;
1269 fxMesa->unitsState.blendSrcFuncRGB = GR_BLEND_ONE;
1270 fxMesa->unitsState.blendDstFuncRGB = GR_BLEND_ZERO;
1271 fxMesa->unitsState.blendSrcFuncAlpha = GR_BLEND_ONE;
1272 fxMesa->unitsState.blendDstFuncAlpha = GR_BLEND_ZERO;
1273 fxMesa->unitsState.blendEq = GR_BLEND_OP_ADD;
1274
1275 fxMesa->unitsState.depthTestEnabled = GL_FALSE;
1276 fxMesa->unitsState.depthMask = GL_TRUE;
1277 fxMesa->unitsState.depthTestFunc = GL_LESS;
1278 fxMesa->unitsState.depthBias = 0;
1279
1280 fxMesa->unitsState.stencilWriteMask = 0xff;
1281
1282 if (fxMesa->colDepth != 16) {
1283 /* 32bpp mode or 15bpp mode */
1284 fxMesa->Glide.grColorMaskExt(FXTRUE, FXTRUE, FXTRUE, fxMesa->haveHwAlpha);
1285 } else {
1286 /* 16 bpp mode */
1287 grColorMask(FXTRUE, fxMesa->haveHwAlpha);
1288 }
1289
1290 fxMesa->currentFB = fxMesa->haveDoubleBuffer ? GR_BUFFER_BACKBUFFER : GR_BUFFER_FRONTBUFFER;
1291 grRenderBuffer(fxMesa->currentFB);
1292
1293 fxMesa->state = MALLOC(FX_grGetInteger(GR_GLIDE_STATE_SIZE));
1294 fxMesa->fogTable = (GrFog_t *) MALLOC(FX_grGetInteger(GR_FOG_TABLE_ENTRIES) *
1295 sizeof(GrFog_t));
1296
1297 if (!fxMesa->state || !fxMesa->fogTable) {
1298 if (fxMesa->state)
1299 FREE(fxMesa->state);
1300 if (fxMesa->fogTable)
1301 FREE(fxMesa->fogTable);
1302 return 0;
1303 }
1304
1305 if (fxMesa->haveZBuffer)
1306 grDepthBufferMode(GR_DEPTHBUFFER_ZBUFFER);
1307
1308 if (!fxMesa->bgrOrder) {
1309 grLfbWriteColorFormat(GR_COLORFORMAT_ABGR);
1310 }
1311
1312 fxMesa->textureAlign = FX_grGetInteger(GR_TEXTURE_ALIGN);
1313 /* [koolsmoky] */
1314 {
1315 int textureLevels = 0;
1316 int textureSize = FX_grGetInteger(GR_MAX_TEXTURE_SIZE);
1317 do {
1318 textureLevels++;
1319 } while ((textureSize >>= 0x1) & 0x7ff);
1320 ctx->Const.MaxTextureLevels = textureLevels;
1321 }
1322 ctx->Const.MaxTextureCoordUnits = fxMesa->haveTwoTMUs ? 2 : 1;
1323 ctx->Const.MaxTextureImageUnits = fxMesa->haveTwoTMUs ? 2 : 1;
1324 ctx->Const.MaxTextureUnits = MAX2(ctx->Const.MaxTextureImageUnits, ctx->Const.MaxTextureCoordUnits);
1325
1326 fxMesa->new_state = _NEW_ALL;
1327 if (!fxMesa->haveHwStencil) {
1328 /* don't touch stencil if there is none */
1329 fxMesa->new_state &= ~FX_NEW_STENCIL;
1330 }
1331
1332 /* Initialize the software rasterizer and helper modules.
1333 */
1334 _swrast_CreateContext(ctx);
1335 _ac_CreateContext(ctx);
1336 _tnl_CreateContext(ctx);
1337 _swsetup_CreateContext(ctx);
1338
1339 /* Install customized pipeline */
1340 _tnl_destroy_pipeline(ctx);
1341 _tnl_install_pipeline(ctx, fx_pipeline);
1342
1343 fxAllocVB(ctx);
1344
1345 fxSetupDDPointers(ctx);
1346 fxDDInitTriFuncs(ctx);
1347
1348 /* Tell the software rasterizer to use pixel fog always.
1349 */
1350 _swrast_allow_vertex_fog(ctx, GL_FALSE);
1351 _swrast_allow_pixel_fog(ctx, GL_TRUE);
1352
1353 /* Tell tnl not to calculate or use vertex fog factors. (Needed to
1354 * tell render stage not to clip fog coords).
1355 */
1356 /* _tnl_calculate_vertex_fog( ctx, GL_FALSE ); */
1357
1358 fxDDInitExtensions(ctx);
1359
1360 #if 0
1361 /* [dBorca] Hack alert:
1362 * do we want dither? It just looks bad...
1363 */
1364 grEnable(GR_ALLOW_MIPMAP_DITHER);
1365 grTexNccTable(GR_NCCTABLE_NCC0); /* set this once... no multipass */
1366 #endif
1367 grGlideGetState((GrState *) fxMesa->state);
1368
1369 return 1;
1370 }
1371
1372 /* Undo the above.
1373 */
1374 void
1375 fxDDDestroyFxMesaContext(fxMesaContext fxMesa)
1376 {
1377 _swsetup_DestroyContext(fxMesa->glCtx);
1378 _tnl_DestroyContext(fxMesa->glCtx);
1379 _ac_DestroyContext(fxMesa->glCtx);
1380 _swrast_DestroyContext(fxMesa->glCtx);
1381
1382 if (fxMesa->state)
1383 FREE(fxMesa->state);
1384 if (fxMesa->fogTable)
1385 FREE(fxMesa->fogTable);
1386 fxTMClose(fxMesa);
1387 fxFreeVB(fxMesa->glCtx);
1388 }
1389
1390
1391
1392
1393 void
1394 fxDDInitExtensions(GLcontext * ctx)
1395 {
1396 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1397
1398 /*_mesa_add_extension(ctx, GL_TRUE, "3DFX_set_global_palette", 0);*/
1399 _mesa_enable_extension(ctx, "GL_EXT_point_parameters");
1400 _mesa_enable_extension(ctx, "GL_EXT_paletted_texture");
1401 _mesa_enable_extension(ctx, "GL_EXT_texture_lod_bias");
1402 _mesa_enable_extension(ctx, "GL_EXT_shared_texture_palette");
1403 _mesa_enable_extension(ctx, "GL_EXT_blend_func_separate");
1404
1405 if (fxMesa->haveTwoTMUs) {
1406 _mesa_enable_extension(ctx, "GL_EXT_texture_env_add");
1407 _mesa_enable_extension(ctx, "GL_ARB_multitexture");
1408 }
1409
1410 if (fxMesa->haveHwStencil) {
1411 _mesa_enable_extension( ctx, "GL_EXT_stencil_wrap" );
1412 }
1413
1414 /* [dBorca] Hack alert:
1415 * True texture compression can be done only on Napalm.
1416 * We will advertise, however, generic texture compression
1417 * on all Voodoo cards; the Mesa logic allows us to eventually
1418 * fallback to uncompressed. This will fix those dumb applications
1419 * which refuse to run w/o texture compression! We actually _can_
1420 * do texture compression for pre-Napalm cores, through NCC. But
1421 * NCC poses many issues:
1422 * 1) NCC w/o DITHER_ERR has poor quality and NCC w/ DITHER_ERR is
1423 * damn slow!
1424 * 2) NCC compression cannot be used with multitexturing, because
1425 * the decompression tables are not per TMU anymore (bear in mind
1426 * that earlier Voodoos could handle 2 NCC tables for each TMU --
1427 * just look for POINTCAST_PALETTE). As a last resort, we could
1428 * fake NCC multitexturing through multipass rendering, but...
1429 * ohwell, it's not worth the effort...
1430 * This stand true for multitexturing palletized textures.
1431 * 3) since NCC is not an OpenGL standard (as opposed to FXT1), we
1432 * would need to plug deeper into the core... First, we would need to
1433 * bind NCC to GL_COMPRESSED_RGB[A]. Then, we would need to trick
1434 * Mesa into reporting our texture as compressed. Last, we would need
1435 * to stash the NCC decompression table into the mipmap data and adjust
1436 * CompressedSize accordingly!
1437 */
1438 _mesa_enable_extension(ctx, "GL_ARB_texture_compression");
1439
1440 if (fxMesa->type >= GR_SSTTYPE_Voodoo4) {
1441 _mesa_enable_extension(ctx, "GL_3DFX_texture_compression_FXT1");
1442 _mesa_enable_extension(ctx, "GL_EXT_texture_compression_s3tc");
1443 /*_mesa_enable_extension(ctx, "GL_S3_s3tc");*/
1444 }
1445
1446 if (fxMesa->HaveCmbExt) {
1447 _mesa_enable_extension(ctx, "GL_EXT_texture_env_combine");
1448 }
1449
1450 if (fxMesa->HavePixExt) {
1451 _mesa_enable_extension(ctx, "GL_EXT_blend_subtract");
1452 }
1453
1454 if (fxMesa->HaveMirExt) {
1455 _mesa_enable_extension(ctx, "GL_ARB_texture_mirrored_repeat");
1456 }
1457 }
1458
1459
1460 /************************************************************************/
1461 /************************************************************************/
1462 /************************************************************************/
1463
1464 /* Check if the hardware supports the current context
1465 *
1466 * Performs similar work to fxDDChooseRenderState() - should be merged.
1467 */
1468 GLuint
1469 fx_check_IsInHardware(GLcontext * ctx)
1470 {
1471 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1472
1473 if (ctx->RenderMode != GL_RENDER) {
1474 return FX_FALLBACK_RENDER_MODE;
1475 }
1476
1477 if (ctx->Stencil.Enabled && !fxMesa->haveHwStencil) {
1478 return FX_FALLBACK_STENCIL;
1479 }
1480
1481 if (ctx->Color._DrawDestMask != FRONT_LEFT_BIT && ctx->Color._DrawDestMask != BACK_LEFT_BIT) {
1482 return FX_FALLBACK_DRAW_BUFFER;
1483 }
1484
1485 if (ctx->Color.BlendEnabled) {
1486 if (ctx->Color.BlendEquation != GL_FUNC_ADD_EXT) {
1487 if (fxMesa->HavePixExt) {
1488 if ((ctx->Color.BlendEquation != GL_FUNC_SUBTRACT_EXT) &&
1489 (ctx->Color.BlendEquation != GL_FUNC_REVERSE_SUBTRACT_EXT)) {
1490 return FX_FALLBACK_BLEND;
1491 }
1492 } else {
1493 return FX_FALLBACK_BLEND;
1494 }
1495 }
1496 }
1497
1498 if (ctx->Color.ColorLogicOpEnabled && (ctx->Color.LogicOp != GL_COPY)) {
1499 return FX_FALLBACK_LOGICOP;
1500 }
1501
1502 if (ctx->Light.Model.ColorControl == GL_SEPARATE_SPECULAR_COLOR) {
1503 return FX_FALLBACK_SPECULAR;
1504 }
1505
1506 if ((ctx->Color.ColorMask[RCOMP] != ctx->Color.ColorMask[GCOMP])
1507 ||
1508 (ctx->Color.ColorMask[GCOMP] != ctx->Color.ColorMask[BCOMP])
1509 ||
1510 (ctx->Color.ColorMask[BCOMP] != ctx->Color.ColorMask[ACOMP])
1511 ) {
1512 return FX_FALLBACK_COLORMASK;
1513 }
1514
1515 /* Unsupported texture/multitexture cases */
1516
1517 if (fxMesa->haveTwoTMUs) {
1518 /* we can only do 2D textures */
1519 if (ctx->Texture.Unit[0]._ReallyEnabled & ~TEXTURE_2D_BIT)
1520 return FX_FALLBACK_TEXTURE_1D_3D;
1521 if (ctx->Texture.Unit[1]._ReallyEnabled & ~TEXTURE_2D_BIT)
1522 return FX_FALLBACK_TEXTURE_1D_3D;
1523
1524 if (ctx->Texture.Unit[0]._ReallyEnabled & TEXTURE_2D_BIT) {
1525 if (fxMesa->type < GR_SSTTYPE_Voodoo2)
1526 if (ctx->Texture.Unit[0].EnvMode == GL_BLEND &&
1527 (ctx->Texture.Unit[1]._ReallyEnabled & TEXTURE_2D_BIT ||
1528 ctx->Texture.Unit[0].EnvColor[0] != 0 ||
1529 ctx->Texture.Unit[0].EnvColor[1] != 0 ||
1530 ctx->Texture.Unit[0].EnvColor[2] != 0 ||
1531 ctx->Texture.Unit[0].EnvColor[3] != 1)) {
1532 return FX_FALLBACK_TEXTURE_ENV;
1533 }
1534 if (ctx->Texture.Unit[0]._Current->Image[0]->Border > 0)
1535 return FX_FALLBACK_TEXTURE_BORDER;
1536 }
1537
1538 if (ctx->Texture.Unit[1]._ReallyEnabled & TEXTURE_2D_BIT) {
1539 if (fxMesa->type < GR_SSTTYPE_Voodoo2)
1540 if (ctx->Texture.Unit[1].EnvMode == GL_BLEND)
1541 return FX_FALLBACK_TEXTURE_ENV;
1542 if (ctx->Texture.Unit[1]._Current->Image[0]->Border > 0)
1543 return FX_FALLBACK_TEXTURE_BORDER;
1544 }
1545
1546 if (TDFX_DEBUG & (VERBOSE_DRIVER | VERBOSE_TEXTURE))
1547 fprintf(stderr, "%s: envmode is %s/%s\n", __FUNCTION__,
1548 _mesa_lookup_enum_by_nr(ctx->Texture.Unit[0].EnvMode),
1549 _mesa_lookup_enum_by_nr(ctx->Texture.Unit[1].EnvMode));
1550
1551 /* KW: This was wrong (I think) and I changed it... which doesn't mean
1552 * it is now correct...
1553 * BP: The old condition just seemed to test if both texture units
1554 * were enabled. That's easy!
1555 */
1556 if (ctx->Texture._EnabledUnits == 0x3) {
1557 /* Can't use multipass to blend a multitextured triangle - fall
1558 * back to software.
1559 */
1560 if (!fxMesa->haveTwoTMUs && ctx->Color.BlendEnabled) {
1561 return FX_FALLBACK_TEXTURE_MULTI;
1562 }
1563
1564 if ((ctx->Texture.Unit[0].EnvMode != ctx->Texture.Unit[1].EnvMode) &&
1565 (ctx->Texture.Unit[0].EnvMode != GL_MODULATE) &&
1566 (ctx->Texture.Unit[0].EnvMode != GL_REPLACE)) { /* q2, seems ok... */
1567 if (TDFX_DEBUG & VERBOSE_DRIVER)
1568 fprintf(stderr, "%s: unsupported multitex env mode\n", __FUNCTION__);
1569 return FX_FALLBACK_TEXTURE_MULTI;
1570 }
1571 }
1572 }
1573 else {
1574 /* we have just one texture unit */
1575 if (ctx->Texture._EnabledUnits > 0x1) {
1576 return FX_FALLBACK_TEXTURE_MULTI;
1577 }
1578
1579 if (fxMesa->type < GR_SSTTYPE_Voodoo2)
1580 if ((ctx->Texture.Unit[0]._ReallyEnabled & TEXTURE_2D_BIT) &&
1581 (ctx->Texture.Unit[0].EnvMode == GL_BLEND)) {
1582 return FX_FALLBACK_TEXTURE_ENV;
1583 }
1584 }
1585
1586 return 0;
1587 }
1588
1589
1590
1591 static void
1592 fxDDUpdateDDPointers(GLcontext * ctx, GLuint new_state)
1593 {
1594 /* TNLcontext *tnl = TNL_CONTEXT(ctx);*/
1595 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1596
1597 if (TDFX_DEBUG & VERBOSE_DRIVER) {
1598 fprintf(stderr, "fxDDUpdateDDPointers(%08x)\n", new_state);
1599 }
1600
1601 _swrast_InvalidateState(ctx, new_state);
1602 _ac_InvalidateState(ctx, new_state);
1603 _tnl_InvalidateState(ctx, new_state);
1604 _swsetup_InvalidateState(ctx, new_state);
1605
1606 fxMesa->new_gl_state |= new_state;
1607 }
1608
1609
1610
1611
1612 void
1613 fxSetupDDPointers(GLcontext * ctx)
1614 {
1615 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1616 TNLcontext *tnl = TNL_CONTEXT(ctx);
1617
1618 if (TDFX_DEBUG & VERBOSE_DRIVER) {
1619 fprintf(stderr, "%s()\n", __FUNCTION__);
1620 }
1621
1622 ctx->Driver.UpdateState = fxDDUpdateDDPointers;
1623 ctx->Driver.GetString = fxDDGetString;
1624 ctx->Driver.ClearIndex = NULL;
1625 ctx->Driver.ClearColor = fxDDClearColor;
1626 ctx->Driver.Clear = fxDDClear;
1627 ctx->Driver.DrawBuffer = fxDDSetDrawBuffer;
1628 ctx->Driver.GetBufferSize = fxDDBufferSize;
1629 ctx->Driver.Accum = _swrast_Accum;
1630 ctx->Driver.CopyPixels = _swrast_CopyPixels;
1631 ctx->Driver.DrawPixels = _swrast_DrawPixels;
1632 switch (fxMesa->colDepth) {
1633 case 15:
1634 ctx->Driver.ReadPixels = fxDDReadPixels555;
1635 ctx->Driver.Bitmap = fxDDDrawBitmap2;
1636 break;
1637 case 16:
1638 ctx->Driver.ReadPixels = fxDDReadPixels565;
1639 ctx->Driver.Bitmap = fxDDDrawBitmap2;
1640 break;
1641 case 32:
1642 ctx->Driver.DrawPixels = fxDDDrawPixels8888;
1643 ctx->Driver.ReadPixels = fxDDReadPixels8888;
1644 ctx->Driver.Bitmap = fxDDDrawBitmap4;
1645 break;
1646 }
1647 ctx->Driver.ResizeBuffers = _swrast_alloc_buffers;
1648 ctx->Driver.Finish = fxDDFinish;
1649 ctx->Driver.Flush = NULL;
1650 ctx->Driver.ChooseTextureFormat = fxDDChooseTextureFormat;
1651 ctx->Driver.TexImage1D = _mesa_store_teximage1d;
1652 ctx->Driver.TexImage2D = fxDDTexImage2D;
1653 ctx->Driver.TexImage3D = _mesa_store_teximage3d;
1654 ctx->Driver.TexSubImage1D = _mesa_store_texsubimage1d;
1655 ctx->Driver.TexSubImage2D = fxDDTexSubImage2D;
1656 ctx->Driver.TexSubImage3D = _mesa_store_texsubimage3d;
1657 ctx->Driver.CompressedTexImage1D = _mesa_store_compressed_teximage1d;
1658 ctx->Driver.CompressedTexImage2D = fxDDCompressedTexImage2D;
1659 ctx->Driver.CompressedTexImage3D = _mesa_store_compressed_teximage3d;
1660 ctx->Driver.CompressedTexSubImage1D = _mesa_store_compressed_texsubimage1d;
1661 ctx->Driver.CompressedTexSubImage2D = fxDDCompressedTexSubImage2D;
1662 ctx->Driver.CompressedTexSubImage3D = _mesa_store_compressed_texsubimage3d;
1663 ctx->Driver.IsCompressedFormat = fxDDIsCompressedFormat;
1664 ctx->Driver.CompressedTextureSize = fxDDCompressedTextureSize;
1665 ctx->Driver.CopyTexImage1D = _swrast_copy_teximage1d;
1666 ctx->Driver.CopyTexImage2D = _swrast_copy_teximage2d;
1667 ctx->Driver.CopyTexSubImage1D = _swrast_copy_texsubimage1d;
1668 ctx->Driver.CopyTexSubImage2D = _swrast_copy_texsubimage2d;
1669 ctx->Driver.CopyTexSubImage3D = _swrast_copy_texsubimage3d;
1670 ctx->Driver.TestProxyTexImage = _mesa_test_proxy_teximage;
1671 ctx->Driver.CopyColorTable = _swrast_CopyColorTable;
1672 ctx->Driver.CopyColorSubTable = _swrast_CopyColorSubTable;
1673 ctx->Driver.CopyConvolutionFilter1D = _swrast_CopyConvolutionFilter1D;
1674 ctx->Driver.CopyConvolutionFilter2D = _swrast_CopyConvolutionFilter2D;
1675 ctx->Driver.TexEnv = fxDDTexEnv;
1676 ctx->Driver.TexParameter = fxDDTexParam;
1677 ctx->Driver.BindTexture = fxDDTexBind;
1678 ctx->Driver.DeleteTexture = fxDDTexDel;
1679 ctx->Driver.IsTextureResident = fxDDIsTextureResident;
1680 ctx->Driver.UpdateTexturePalette = fxDDTexPalette;
1681 ctx->Driver.AlphaFunc = fxDDAlphaFunc;
1682 ctx->Driver.BlendFunc = fxDDBlendFunc;
1683 ctx->Driver.BlendFuncSeparate = fxDDBlendFuncSeparate;
1684 ctx->Driver.BlendEquation = fxDDBlendEquation;
1685 ctx->Driver.DepthFunc = fxDDDepthFunc;
1686 ctx->Driver.DepthMask = fxDDDepthMask;
1687 ctx->Driver.ColorMask = fxDDColorMask;
1688 ctx->Driver.Fogfv = fxDDFogfv;
1689 ctx->Driver.Scissor = fxDDScissor;
1690 ctx->Driver.FrontFace = fxDDFrontFace;
1691 ctx->Driver.CullFace = fxDDCullFace;
1692 ctx->Driver.ShadeModel = fxDDShadeModel;
1693 ctx->Driver.Enable = fxDDEnable;
1694 if (fxMesa->haveHwStencil) {
1695 ctx->Driver.StencilFunc = fxDDStencilFunc;
1696 ctx->Driver.StencilMask = fxDDStencilMask;
1697 ctx->Driver.StencilOp = fxDDStencilOp;
1698 }
1699
1700 fxSetupDDSpanPointers(ctx);
1701 fxDDUpdateDDPointers(ctx, ~0);
1702 }
1703
1704
1705 #else
1706
1707
1708 /*
1709 * Need this to provide at least one external definition.
1710 */
1711
1712 extern int gl_fx_dummy_function_dd(void);
1713 int
1714 gl_fx_dummy_function_dd(void)
1715 {
1716 return 0;
1717 }
1718
1719 #endif /* FX */