3 * Mesa 3-D graphics library
6 * Copyright (C) 1999-2000 Brian Paul All Rights Reserved.
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 * Original Mesa / 3Dfx device driver (C) 1999 David Bucciarelli, by the
29 * Thank you for your contribution, David!
31 * Please make note of the above copyright/license statement. If you
32 * contributed code or bug fixes to this code under the previous (GNU
33 * Library) license and object to the new license, your code will be
34 * removed at your request. Please see the Mesa docs/COPYRIGHT file
35 * for more information.
37 * Additional Mesa/3Dfx driver developers:
38 * Daryll Strauss <daryll@precisioninsight.com>
39 * Keith Whitwell <keith@precisioninsight.com>
41 * See fxapi.h for more revision/author details.
45 /* fxtexman.c - 3Dfx VooDoo texture memory functions */
58 #define FX_2MB_SPLIT 0x200000
60 static struct gl_texture_object
*fxTMFindOldestObject(fxMesaContext fxMesa
,
70 static void sanity(fxMesaContext fxMesa
)
72 MemRange
*tmp
, *prev
, *pos
;
75 tmp
= fxMesa
->tmFree
[0];
77 if (!tmp
->startAddr
&& !tmp
->endAddr
) {
78 fprintf(stderr
, "Textures fubar\n");
81 if (tmp
->startAddr
>=tmp
->endAddr
) {
82 fprintf(stderr
, "Node fubar\n");
85 if (prev
&& (prev
->startAddr
>=tmp
->startAddr
||
86 prev
->endAddr
>tmp
->startAddr
)) {
87 fprintf(stderr
, "Sorting fubar\n");
94 tmp
= fxMesa
->tmFree
[1];
96 if (!tmp
->startAddr
&& !tmp
->endAddr
) {
97 fprintf(stderr
, "Textures fubar\n");
100 if (tmp
->startAddr
>=tmp
->endAddr
) {
101 fprintf(stderr
, "Node fubar\n");
104 if (prev
&& (prev
->startAddr
>=tmp
->startAddr
||
105 prev
->endAddr
>tmp
->startAddr
)) {
106 fprintf(stderr
, "Sorting fubar\n");
115 static MemRange
*fxTMNewRangeNode(fxMesaContext fxMesa
, FxU32 start
, FxU32 end
) {
118 if (fxMesa
->tmPool
) {
119 result
=fxMesa
->tmPool
;
120 fxMesa
->tmPool
=fxMesa
->tmPool
->next
;
122 if (!(result
=MALLOC(sizeof(MemRange
)))) {
123 fprintf(stderr
, "fxDriver: out of memory!\n");
128 result
->startAddr
=start
;
133 static void fxTMDeleteRangeNode(fxMesaContext fxMesa
, MemRange
*range
)
135 range
->next
=fxMesa
->tmPool
;
136 fxMesa
->tmPool
=range
;
139 static void fxTMUInit(fxMesaContext fxMesa
, int tmu
)
141 MemRange
*tmn
, *last
;
142 FxU32 start
,end
,blockstart
,blockend
;
144 start
=FX_grTexMinAddress(tmu
);
145 end
=FX_grTexMaxAddress(tmu
);
147 if(fxMesa
->verbose
) {
148 fprintf(stderr
,"Voodoo %s configuration:",(tmu
==FX_TMU0
) ? "TMU0" : "TMU1");
149 fprintf(stderr
,"Voodoo Lower texture memory address (%u)\n",(unsigned int)start
);
150 fprintf(stderr
,"Voodoo Higher texture memory address (%u)\n",(unsigned int)end
);
151 fprintf(stderr
,"Voodoo Splitting Texture memory in 2b blocks:\n");
154 fxMesa
->freeTexMem
[tmu
]=end
-start
;
155 fxMesa
->tmFree
[tmu
]=NULL
;
159 while (blockstart
<end
) {
160 if (blockstart
+FX_2MB_SPLIT
>end
) blockend
=end
;
161 else blockend
=blockstart
+FX_2MB_SPLIT
;
164 fprintf(stderr
,"Voodoo %07u-%07u\n",
165 (unsigned int)blockstart
,(unsigned int)blockend
);
167 tmn
=fxTMNewRangeNode(fxMesa
, blockstart
, blockend
);
170 if (last
) last
->next
=tmn
;
171 else fxMesa
->tmFree
[tmu
]=tmn
;
174 blockstart
+=FX_2MB_SPLIT
;
178 static int fxTMFindStartAddr(fxMesaContext fxMesa
, GLint tmu
, int size
)
180 MemRange
*prev
, *tmp
;
182 struct gl_texture_object
*obj
;
186 tmp
=fxMesa
->tmFree
[tmu
];
188 if (tmp
->endAddr
-tmp
->startAddr
>=size
) { /* Fits here */
189 result
=tmp
->startAddr
;
190 tmp
->startAddr
+=size
;
191 if (tmp
->startAddr
==tmp
->endAddr
) { /* Empty */
193 prev
->next
=tmp
->next
;
195 fxMesa
->tmFree
[tmu
]=tmp
->next
;
197 fxTMDeleteRangeNode(fxMesa
, tmp
);
199 fxMesa
->freeTexMem
[tmu
]-=size
;
205 /* No free space. Discard oldest */
206 obj
=fxTMFindOldestObject(fxMesa
, tmu
);
208 fprintf(stderr
, "fx Driver: No space for texture\n");
211 fxTMMoveOutTM(fxMesa
, obj
);
216 static void fxTMRemoveRange(fxMesaContext fxMesa
, GLint tmu
, MemRange
*range
)
218 MemRange
*tmp
, *prev
;
220 if (range
->startAddr
==range
->endAddr
) {
221 fxTMDeleteRangeNode(fxMesa
, range
);
224 fxMesa
->freeTexMem
[tmu
]+=range
->endAddr
-range
->startAddr
;
226 tmp
=fxMesa
->tmFree
[tmu
];
228 if (range
->startAddr
>tmp
->startAddr
) {
233 /* When we create the regions, we make a split at the 2MB boundary.
234 Now we have to make sure we don't join those 2MB boundary regions
235 back together again. */
238 if (range
->endAddr
==tmp
->startAddr
&& tmp
->startAddr
&(FX_2MB_SPLIT
-1)) {
240 tmp
->startAddr
=range
->startAddr
;
241 fxTMDeleteRangeNode(fxMesa
, range
);
246 if (prev
->endAddr
==range
->startAddr
&& range
->startAddr
&(FX_2MB_SPLIT
-1)) {
248 prev
->endAddr
=range
->endAddr
;
249 prev
->next
=range
->next
;
250 fxTMDeleteRangeNode(fxMesa
, range
);
251 } else prev
->next
=range
;
253 fxMesa
->tmFree
[tmu
]=range
;
257 static struct gl_texture_object
*fxTMFindOldestObject(fxMesaContext fxMesa
,
260 GLuint age
, old
, lasttime
, bindnumber
;
262 struct gl_texture_object
*obj
, *tmp
;
264 tmp
=fxMesa
->glCtx
->Shared
->TexObjectList
;
269 bindnumber
=fxMesa
->texBindNumber
;
271 info
=fxTMGetTexInfo(tmp
);
273 if (info
&& info
->isInTM
&&
274 ((info
->whichTMU
==tmu
) || (info
->whichTMU
==FX_TMU_BOTH
) ||
275 (info
->whichTMU
==FX_TMU_SPLIT
))) {
276 lasttime
=info
->lastTimeUsed
;
278 if (lasttime
>bindnumber
)
279 age
=bindnumber
+(UINT_MAX
-lasttime
+1); /* TO DO: check wrap around */
281 age
=bindnumber
-lasttime
;
293 static MemRange
*fxTMAddObj(fxMesaContext fxMesa
,
294 struct gl_texture_object
*tObj
,
295 GLint tmu
, int texmemsize
)
300 startAddr
=fxTMFindStartAddr(fxMesa
, tmu
, texmemsize
);
301 if (startAddr
<0) return 0;
302 range
=fxTMNewRangeNode(fxMesa
, startAddr
, startAddr
+texmemsize
);
306 /* External Functions */
308 void fxTMMoveInTM_NoLock(fxMesaContext fxMesa
, struct gl_texture_object
*tObj
, GLint where
)
310 tfxTexInfo
*ti
=fxTMGetTexInfo(tObj
);
314 if (MESA_VERBOSE
&VERBOSE_DRIVER
) {
315 fprintf(stderr
,"fxmesa: fxTMMoveInTM(%d)\n",tObj
->Name
);
318 fxMesa
->stats
.reqTexUpload
++;
320 if (!ti
->validated
) {
321 fprintf(stderr
,"fx Driver: internal error in fxTMMoveInTM() -> not validated\n");
327 if (ti
->whichTMU
==where
) return;
328 if (where
==FX_TMU_SPLIT
|| ti
->whichTMU
==FX_TMU_SPLIT
)
329 fxTMMoveOutTM_NoLock(fxMesa
, tObj
);
331 if (ti
->whichTMU
==FX_TMU_BOTH
) return;
336 if (MESA_VERBOSE
&(VERBOSE_DRIVER
|VERBOSE_TEXTURE
)) {
337 fprintf(stderr
,"fxmesa: downloading %x (%d) in texture memory in %d\n",(GLuint
)tObj
,tObj
->Name
,where
);
340 ti
->whichTMU
=(FxU32
)where
;
345 texmemsize
=(int)FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_BOTH
,
347 ti
->tm
[where
]=fxTMAddObj(fxMesa
, tObj
, where
, texmemsize
);
348 fxMesa
->stats
.memTexUpload
+=texmemsize
;
350 for (i
=FX_largeLodValue(ti
->info
), l
=ti
->minLevel
;
351 i
<=FX_smallLodValue(ti
->info
);
353 FX_grTexDownloadMipMapLevel_NoLock(where
,
354 ti
->tm
[where
]->startAddr
,
356 FX_largeLodLog2(ti
->info
),
357 FX_aspectRatioLog2(ti
->info
),
359 GR_MIPMAPLEVELMASK_BOTH
,
360 ti
->mipmapLevel
[l
].data
);
363 texmemsize
=(int)FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_ODD
,
365 ti
->tm
[FX_TMU0
]=fxTMAddObj(fxMesa
, tObj
, FX_TMU0
, texmemsize
);
366 fxMesa
->stats
.memTexUpload
+=texmemsize
;
368 texmemsize
=(int)FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_EVEN
,
370 ti
->tm
[FX_TMU1
]=fxTMAddObj(fxMesa
, tObj
, FX_TMU1
, texmemsize
);
371 fxMesa
->stats
.memTexUpload
+=texmemsize
;
373 for (i
=FX_largeLodValue(ti
->info
),l
=ti
->minLevel
;
374 i
<=FX_smallLodValue(ti
->info
);
376 FX_grTexDownloadMipMapLevel_NoLock(GR_TMU0
,
377 ti
->tm
[FX_TMU0
]->startAddr
,
379 FX_largeLodLog2(ti
->info
),
380 FX_aspectRatioLog2(ti
->info
),
382 GR_MIPMAPLEVELMASK_ODD
,
383 ti
->mipmapLevel
[l
].data
);
385 FX_grTexDownloadMipMapLevel_NoLock(GR_TMU1
,
386 ti
->tm
[FX_TMU1
]->startAddr
,
388 FX_largeLodLog2(ti
->info
),
389 FX_aspectRatioLog2(ti
->info
),
391 GR_MIPMAPLEVELMASK_EVEN
,
392 ti
->mipmapLevel
[l
].data
);
396 texmemsize
=(int)FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_BOTH
,
398 ti
->tm
[FX_TMU0
]=fxTMAddObj(fxMesa
, tObj
, FX_TMU0
, texmemsize
);
399 fxMesa
->stats
.memTexUpload
+=texmemsize
;
401 texmemsize
=(int)FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_BOTH
,
403 ti
->tm
[FX_TMU1
]=fxTMAddObj(fxMesa
, tObj
, FX_TMU1
, texmemsize
);
404 fxMesa
->stats
.memTexUpload
+=texmemsize
;
406 for (i
=FX_largeLodValue(ti
->info
),l
=ti
->minLevel
;
407 i
<=FX_smallLodValue(ti
->info
);
409 FX_grTexDownloadMipMapLevel_NoLock(GR_TMU0
,
410 ti
->tm
[FX_TMU0
]->startAddr
,
412 FX_largeLodLog2(ti
->info
),
413 FX_aspectRatioLog2(ti
->info
),
415 GR_MIPMAPLEVELMASK_BOTH
,
416 ti
->mipmapLevel
[l
].data
);
418 FX_grTexDownloadMipMapLevel_NoLock(GR_TMU1
,
419 ti
->tm
[FX_TMU1
]->startAddr
,
421 FX_largeLodLog2(ti
->info
),
422 FX_aspectRatioLog2(ti
->info
),
424 GR_MIPMAPLEVELMASK_BOTH
,
425 ti
->mipmapLevel
[l
].data
);
429 fprintf(stderr
,"fx Driver: internal error in fxTMMoveInTM() -> wrong tmu (%d)\n",where
);
434 fxMesa
->stats
.texUpload
++;
439 void fxTMMoveInTM(fxMesaContext fxMesa
, struct gl_texture_object
*tObj
, GLint where
) {
441 fxTMMoveInTM_NoLock(fxMesa
, tObj
, where
);
445 void fxTMReloadMipMapLevel(fxMesaContext fxMesa
, struct gl_texture_object
*tObj
, GLint level
)
447 tfxTexInfo
*ti
=fxTMGetTexInfo(tObj
);
451 if (!ti
->validated
) {
452 fprintf(stderr
,"fx Driver: internal error in fxTMReloadMipMapLevel() -> not validated\n");
457 tmu
=(int)ti
->whichTMU
;
458 fxTMMoveInTM(fxMesa
, tObj
, tmu
);
460 fxTexGetInfo(ti
->mipmapLevel
[0].width
,ti
->mipmapLevel
[0].height
,
461 &lodlevel
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
471 FX_grTexDownloadMipMapLevel(tmu
,
472 ti
->tm
[tmu
]->startAddr
,
473 FX_valueToLod(FX_lodToValue(lodlevel
)),
474 FX_largeLodLog2(ti
->info
),
475 FX_aspectRatioLog2(ti
->info
),
477 GR_MIPMAPLEVELMASK_BOTH
,
478 ti
->mipmapLevel
[level
].data
);
481 FX_grTexDownloadMipMapLevel(GR_TMU0
,
482 ti
->tm
[GR_TMU0
]->startAddr
,
483 FX_valueToLod(FX_lodToValue(lodlevel
)),
484 FX_largeLodLog2(ti
->info
),
485 FX_aspectRatioLog2(ti
->info
),
487 GR_MIPMAPLEVELMASK_ODD
,
488 ti
->mipmapLevel
[level
].data
);
490 FX_grTexDownloadMipMapLevel(GR_TMU1
,
491 ti
->tm
[GR_TMU1
]->startAddr
,
492 FX_valueToLod(FX_lodToValue(lodlevel
)),
493 FX_largeLodLog2(ti
->info
),
494 FX_aspectRatioLog2(ti
->info
),
496 GR_MIPMAPLEVELMASK_EVEN
,
497 ti
->mipmapLevel
[level
].data
);
500 FX_grTexDownloadMipMapLevel(GR_TMU0
,
501 ti
->tm
[GR_TMU0
]->startAddr
,
502 FX_valueToLod(FX_lodToValue(lodlevel
)),
503 FX_largeLodLog2(ti
->info
),
504 FX_aspectRatioLog2(ti
->info
),
506 GR_MIPMAPLEVELMASK_BOTH
,
507 ti
->mipmapLevel
[level
].data
);
509 FX_grTexDownloadMipMapLevel(GR_TMU1
,
510 ti
->tm
[GR_TMU1
]->startAddr
,
511 FX_valueToLod(FX_lodToValue(lodlevel
)),
512 FX_largeLodLog2(ti
->info
),
513 FX_aspectRatioLog2(ti
->info
),
515 GR_MIPMAPLEVELMASK_BOTH
,
516 ti
->mipmapLevel
[level
].data
);
520 fprintf(stderr
,"fx Driver: internal error in fxTMReloadMipMapLevel() -> wrong tmu (%d)\n",tmu
);
526 void fxTMReloadSubMipMapLevel(fxMesaContext fxMesa
,
527 struct gl_texture_object
*tObj
,
528 GLint level
, GLint yoffset
, GLint height
)
530 tfxTexInfo
*ti
=fxTMGetTexInfo(tObj
);
532 unsigned short *data
;
536 fprintf(stderr
,"fx Driver: internal error in fxTMReloadSubMipMapLevel() -> not validated\n");
541 tmu
=(int)ti
->whichTMU
;
542 fxTMMoveInTM(fxMesa
, tObj
, tmu
);
544 fxTexGetInfo(ti
->mipmapLevel
[0].width
, ti
->mipmapLevel
[0].height
,
545 &lodlevel
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
547 if((ti
->info
.format
==GR_TEXFMT_INTENSITY_8
) ||
548 (ti
->info
.format
==GR_TEXFMT_P_8
) ||
549 (ti
->info
.format
==GR_TEXFMT_ALPHA_8
))
550 data
=ti
->mipmapLevel
[level
].data
+((yoffset
*ti
->mipmapLevel
[level
].width
)>>1);
552 data
=ti
->mipmapLevel
[level
].data
+yoffset
*ti
->mipmapLevel
[level
].width
;
557 FX_grTexDownloadMipMapLevelPartial(tmu
,
558 ti
->tm
[tmu
]->startAddr
,
559 FX_valueToLod(FX_lodToValue(lodlevel
)+level
),
560 FX_largeLodLog2(ti
->info
),
561 FX_aspectRatioLog2(ti
->info
),
563 GR_MIPMAPLEVELMASK_BOTH
,
565 yoffset
,yoffset
+height
-1);
568 FX_grTexDownloadMipMapLevelPartial(GR_TMU0
,
569 ti
->tm
[FX_TMU0
]->startAddr
,
570 FX_valueToLod(FX_lodToValue(lodlevel
)+level
),
571 FX_largeLodLog2(ti
->info
),
572 FX_aspectRatioLog2(ti
->info
),
574 GR_MIPMAPLEVELMASK_ODD
,
576 yoffset
,yoffset
+height
-1);
578 FX_grTexDownloadMipMapLevelPartial(GR_TMU1
,
579 ti
->tm
[FX_TMU1
]->startAddr
,
580 FX_valueToLod(FX_lodToValue(lodlevel
)+level
),
581 FX_largeLodLog2(ti
->info
),
582 FX_aspectRatioLog2(ti
->info
),
584 GR_MIPMAPLEVELMASK_EVEN
,
586 yoffset
,yoffset
+height
-1);
589 FX_grTexDownloadMipMapLevelPartial(GR_TMU0
,
590 ti
->tm
[FX_TMU0
]->startAddr
,
591 FX_valueToLod(FX_lodToValue(lodlevel
)+level
),
592 FX_largeLodLog2(ti
->info
),
593 FX_aspectRatioLog2(ti
->info
),
595 GR_MIPMAPLEVELMASK_BOTH
,
597 yoffset
,yoffset
+height
-1);
599 FX_grTexDownloadMipMapLevelPartial(GR_TMU1
,
600 ti
->tm
[FX_TMU1
]->startAddr
,
601 FX_valueToLod(FX_lodToValue(lodlevel
)+level
),
602 FX_largeLodLog2(ti
->info
),
603 FX_aspectRatioLog2(ti
->info
),
605 GR_MIPMAPLEVELMASK_BOTH
,
607 yoffset
,yoffset
+height
-1);
610 fprintf(stderr
,"fx Driver: internal error in fxTMReloadSubMipMapLevel() -> wrong tmu (%d)\n",tmu
);
616 void fxTMMoveOutTM(fxMesaContext fxMesa
, struct gl_texture_object
*tObj
)
618 tfxTexInfo
*ti
=fxTMGetTexInfo(tObj
);
620 if (MESA_VERBOSE
&VERBOSE_DRIVER
) {
621 fprintf(stderr
,"fxmesa: fxTMMoveOutTM(%x (%d))\n",(GLuint
)tObj
,tObj
->Name
);
624 if (!ti
->isInTM
) return;
626 switch(ti
->whichTMU
) {
629 fxTMRemoveRange(fxMesa
, (int)ti
->whichTMU
, ti
->tm
[ti
->whichTMU
]);
633 fxTMRemoveRange(fxMesa
, FX_TMU0
, ti
->tm
[FX_TMU0
]);
634 fxTMRemoveRange(fxMesa
, FX_TMU1
, ti
->tm
[FX_TMU1
]);
637 fprintf(stderr
,"fx Driver: internal error in fxTMMoveOutTM()\n");
643 ti
->whichTMU
=FX_TMU_NONE
;
646 void fxTMFreeTexture(fxMesaContext fxMesa
, struct gl_texture_object
*tObj
)
648 tfxTexInfo
*ti
=fxTMGetTexInfo(tObj
);
651 fxTMMoveOutTM(fxMesa
, tObj
);
653 for (i
=0; i
<MAX_TEXTURE_LEVELS
; i
++) {
654 if (ti
->mipmapLevel
[i
].data
) {
655 FREE(ti
->mipmapLevel
[i
].data
);
656 ti
->mipmapLevel
[i
].data
= NULL
;
659 switch (ti
->whichTMU
) {
662 fxTMDeleteRangeNode(fxMesa
, ti
->tm
[ti
->whichTMU
]);
666 fxTMDeleteRangeNode(fxMesa
, ti
->tm
[FX_TMU0
]);
667 fxTMDeleteRangeNode(fxMesa
, ti
->tm
[FX_TMU1
]);
672 void fxTMInit(fxMesaContext fxMesa
)
674 fxMesa
->texBindNumber
=0;
677 fxTMUInit(fxMesa
,FX_TMU0
);
679 if(fxMesa
->haveTwoTMUs
)
680 fxTMUInit(fxMesa
,FX_TMU1
);
683 void fxTMClose(fxMesaContext fxMesa
)
685 MemRange
*tmp
, *next
;
693 tmp
=fxMesa
->tmFree
[FX_TMU0
];
699 if (fxMesa
->haveTwoTMUs
) {
700 tmp
=fxMesa
->tmFree
[FX_TMU1
];
710 fxTMRestoreTextures_NoLock(fxMesaContext ctx
) {
712 struct gl_texture_object
*tObj
;
715 tObj
=ctx
->glCtx
->Shared
->TexObjectList
;
717 ti
=fxTMGetTexInfo(tObj
);
718 if (ti
&& ti
->isInTM
) {
719 for (i
=0; i
<MAX_TEXTURE_UNITS
; i
++)
720 if (ctx
->glCtx
->Texture
.Unit
[i
]._Current
==tObj
) {
721 /* Force the texture onto the board, as it could be in use */
723 fxTMMoveOutTM_NoLock(ctx
, tObj
);
724 fxTMMoveInTM_NoLock(ctx
, tObj
, where
);
727 if (i
==MAX_TEXTURE_UNITS
) /* Mark the texture as off the board */
728 fxTMMoveOutTM_NoLock(ctx
, tObj
);
738 * Need this to provide at least one external definition.
741 extern int gl_fx_dummy_function_texman(void);
742 int gl_fx_dummy_function_texman(void)