2e989a04b4105b5c9cc2e9eecc79bc83db33743a
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
,
72 sanity(fxMesaContext fxMesa
)
74 MemRange
*tmp
, *prev
, *pos
;
77 tmp
= fxMesa
->tmFree
[0];
79 if (!tmp
->startAddr
&& !tmp
->endAddr
) {
80 fprintf(stderr
, "Textures fubar\n");
83 if (tmp
->startAddr
>= tmp
->endAddr
) {
84 fprintf(stderr
, "Node fubar\n");
87 if (prev
&& (prev
->startAddr
>= tmp
->startAddr
||
88 prev
->endAddr
> tmp
->startAddr
)) {
89 fprintf(stderr
, "Sorting fubar\n");
96 tmp
= fxMesa
->tmFree
[1];
98 if (!tmp
->startAddr
&& !tmp
->endAddr
) {
99 fprintf(stderr
, "Textures fubar\n");
102 if (tmp
->startAddr
>= tmp
->endAddr
) {
103 fprintf(stderr
, "Node fubar\n");
106 if (prev
&& (prev
->startAddr
>= tmp
->startAddr
||
107 prev
->endAddr
> tmp
->startAddr
)) {
108 fprintf(stderr
, "Sorting fubar\n");
118 fxTMNewRangeNode(fxMesaContext fxMesa
, FxU32 start
, FxU32 end
)
120 MemRange
*result
= 0;
122 if (fxMesa
->tmPool
) {
123 result
= fxMesa
->tmPool
;
124 fxMesa
->tmPool
= fxMesa
->tmPool
->next
;
127 if (!(result
= MALLOC(sizeof(MemRange
)))) {
128 fprintf(stderr
, "fxDriver: out of memory!\n");
133 result
->startAddr
= start
;
134 result
->endAddr
= end
;
139 fxTMDeleteRangeNode(fxMesaContext fxMesa
, MemRange
* range
)
141 range
->next
= fxMesa
->tmPool
;
142 fxMesa
->tmPool
= range
;
146 fxTMUInit(fxMesaContext fxMesa
, int tmu
)
148 MemRange
*tmn
, *last
;
149 FxU32 start
, end
, blockstart
, blockend
;
151 start
= FX_grTexMinAddress(tmu
);
152 end
= FX_grTexMaxAddress(tmu
);
154 if (fxMesa
->verbose
) {
155 fprintf(stderr
, "Voodoo %s configuration:",
156 (tmu
== FX_TMU0
) ? "TMU0" : "TMU1");
157 fprintf(stderr
, "Voodoo Lower texture memory address (%u)\n",
158 (unsigned int) start
);
159 fprintf(stderr
, "Voodoo Higher texture memory address (%u)\n",
161 fprintf(stderr
, "Voodoo Splitting Texture memory in 2b blocks:\n");
164 fxMesa
->freeTexMem
[tmu
] = end
- start
;
165 fxMesa
->tmFree
[tmu
] = NULL
;
169 while (blockstart
< end
) {
170 if (blockstart
+ FX_2MB_SPLIT
> end
)
173 blockend
= blockstart
+ FX_2MB_SPLIT
;
176 fprintf(stderr
, "Voodoo %07u-%07u\n",
177 (unsigned int) blockstart
, (unsigned int) blockend
);
179 tmn
= fxTMNewRangeNode(fxMesa
, blockstart
, blockend
);
185 fxMesa
->tmFree
[tmu
] = tmn
;
188 blockstart
+= FX_2MB_SPLIT
;
193 fxTMFindStartAddr(fxMesaContext fxMesa
, GLint tmu
, int size
)
195 MemRange
*prev
, *tmp
;
197 struct gl_texture_object
*obj
;
201 tmp
= fxMesa
->tmFree
[tmu
];
203 if (tmp
->endAddr
- tmp
->startAddr
>= size
) { /* Fits here */
204 result
= tmp
->startAddr
;
205 tmp
->startAddr
+= size
;
206 if (tmp
->startAddr
== tmp
->endAddr
) { /* Empty */
208 prev
->next
= tmp
->next
;
211 fxMesa
->tmFree
[tmu
] = tmp
->next
;
213 fxTMDeleteRangeNode(fxMesa
, tmp
);
215 fxMesa
->freeTexMem
[tmu
] -= size
;
221 /* No free space. Discard oldest */
222 obj
= fxTMFindOldestObject(fxMesa
, tmu
);
224 fprintf(stderr
, "fx Driver: No space for texture\n");
227 fxTMMoveOutTM(fxMesa
, obj
);
233 fxTMRemoveRange(fxMesaContext fxMesa
, GLint tmu
, MemRange
* range
)
235 MemRange
*tmp
, *prev
;
237 if (range
->startAddr
== range
->endAddr
) {
238 fxTMDeleteRangeNode(fxMesa
, range
);
241 fxMesa
->freeTexMem
[tmu
] += range
->endAddr
- range
->startAddr
;
243 tmp
= fxMesa
->tmFree
[tmu
];
245 if (range
->startAddr
> tmp
->startAddr
) {
252 /* When we create the regions, we make a split at the 2MB boundary.
253 Now we have to make sure we don't join those 2MB boundary regions
254 back together again. */
257 if (range
->endAddr
== tmp
->startAddr
258 && tmp
->startAddr
& (FX_2MB_SPLIT
- 1)) {
260 tmp
->startAddr
= range
->startAddr
;
261 fxTMDeleteRangeNode(fxMesa
, range
);
266 if (prev
->endAddr
== range
->startAddr
267 && range
->startAddr
& (FX_2MB_SPLIT
- 1)) {
269 prev
->endAddr
= range
->endAddr
;
270 prev
->next
= range
->next
;
271 fxTMDeleteRangeNode(fxMesa
, range
);
277 fxMesa
->tmFree
[tmu
] = range
;
281 static struct gl_texture_object
*
282 fxTMFindOldestObject(fxMesaContext fxMesa
, int tmu
)
284 GLuint age
, old
, lasttime
, bindnumber
;
286 struct gl_texture_object
*obj
, *tmp
;
288 tmp
= fxMesa
->glCtx
->Shared
->TexObjectList
;
294 bindnumber
= fxMesa
->texBindNumber
;
296 info
= fxTMGetTexInfo(tmp
);
298 if (info
&& info
->isInTM
&&
299 ((info
->whichTMU
== tmu
) || (info
->whichTMU
== FX_TMU_BOTH
) ||
300 (info
->whichTMU
== FX_TMU_SPLIT
))) {
301 lasttime
= info
->lastTimeUsed
;
303 if (lasttime
> bindnumber
)
304 age
= bindnumber
+ (UINT_MAX
- lasttime
+ 1); /* TO DO: check wrap around */
306 age
= bindnumber
- lasttime
;
319 fxTMAddObj(fxMesaContext fxMesa
,
320 struct gl_texture_object
*tObj
, GLint tmu
, int texmemsize
)
325 startAddr
= fxTMFindStartAddr(fxMesa
, tmu
, texmemsize
);
328 range
= fxTMNewRangeNode(fxMesa
, startAddr
, startAddr
+ texmemsize
);
332 /* External Functions */
335 fxTMMoveInTM_NoLock(fxMesaContext fxMesa
, struct gl_texture_object
*tObj
,
338 tfxTexInfo
*ti
= fxTMGetTexInfo(tObj
);
342 if (MESA_VERBOSE
& VERBOSE_DRIVER
) {
343 fprintf(stderr
, "fxmesa: fxTMMoveInTM(%d)\n", tObj
->Name
);
346 fxMesa
->stats
.reqTexUpload
++;
348 if (!ti
->validated
) {
350 "fx Driver: internal error in fxTMMoveInTM() -> not validated\n");
356 if (ti
->whichTMU
== where
)
358 if (where
== FX_TMU_SPLIT
|| ti
->whichTMU
== FX_TMU_SPLIT
)
359 fxTMMoveOutTM_NoLock(fxMesa
, tObj
);
361 if (ti
->whichTMU
== FX_TMU_BOTH
)
367 if (MESA_VERBOSE
& (VERBOSE_DRIVER
| VERBOSE_TEXTURE
)) {
368 fprintf(stderr
, "fxmesa: downloading %x (%d) in texture memory in %d\n",
369 (GLuint
) tObj
, tObj
->Name
, where
);
372 ti
->whichTMU
= (FxU32
) where
;
378 (int) FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_BOTH
,
380 ti
->tm
[where
] = fxTMAddObj(fxMesa
, tObj
, where
, texmemsize
);
381 fxMesa
->stats
.memTexUpload
+= texmemsize
;
383 for (i
= FX_largeLodValue(ti
->info
), l
= ti
->minLevel
;
384 i
<= FX_smallLodValue(ti
->info
); i
++, l
++) {
385 struct gl_texture_image
*texImage
= tObj
->Image
[l
];
386 FX_grTexDownloadMipMapLevel_NoLock(where
,
387 ti
->tm
[where
]->startAddr
,
389 FX_largeLodLog2(ti
->info
),
390 FX_aspectRatioLog2(ti
->info
),
392 GR_MIPMAPLEVELMASK_BOTH
,
398 (int) FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_ODD
,
400 ti
->tm
[FX_TMU0
] = fxTMAddObj(fxMesa
, tObj
, FX_TMU0
, texmemsize
);
401 fxMesa
->stats
.memTexUpload
+= texmemsize
;
404 (int) FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_EVEN
,
406 ti
->tm
[FX_TMU1
] = fxTMAddObj(fxMesa
, tObj
, FX_TMU1
, texmemsize
);
407 fxMesa
->stats
.memTexUpload
+= texmemsize
;
409 for (i
= FX_largeLodValue(ti
->info
), l
= ti
->minLevel
;
410 i
<= FX_smallLodValue(ti
->info
); i
++, l
++) {
411 struct gl_texture_image
*texImage
= tObj
->Image
[l
];
413 FX_grTexDownloadMipMapLevel_NoLock(GR_TMU0
,
414 ti
->tm
[FX_TMU0
]->startAddr
,
416 FX_largeLodLog2(ti
->info
),
417 FX_aspectRatioLog2(ti
->info
),
419 GR_MIPMAPLEVELMASK_ODD
,
422 FX_grTexDownloadMipMapLevel_NoLock(GR_TMU1
,
423 ti
->tm
[FX_TMU1
]->startAddr
,
425 FX_largeLodLog2(ti
->info
),
426 FX_aspectRatioLog2(ti
->info
),
428 GR_MIPMAPLEVELMASK_EVEN
,
434 (int) FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_BOTH
,
436 ti
->tm
[FX_TMU0
] = fxTMAddObj(fxMesa
, tObj
, FX_TMU0
, texmemsize
);
437 fxMesa
->stats
.memTexUpload
+= texmemsize
;
440 (int) FX_grTexTextureMemRequired_NoLock(GR_MIPMAPLEVELMASK_BOTH
,
442 ti
->tm
[FX_TMU1
] = fxTMAddObj(fxMesa
, tObj
, FX_TMU1
, texmemsize
);
443 fxMesa
->stats
.memTexUpload
+= texmemsize
;
445 for (i
= FX_largeLodValue(ti
->info
), l
= ti
->minLevel
;
446 i
<= FX_smallLodValue(ti
->info
); i
++, l
++) {
447 struct gl_texture_image
*texImage
= tObj
->Image
[l
];
448 FX_grTexDownloadMipMapLevel_NoLock(GR_TMU0
,
449 ti
->tm
[FX_TMU0
]->startAddr
,
451 FX_largeLodLog2(ti
->info
),
452 FX_aspectRatioLog2(ti
->info
),
454 GR_MIPMAPLEVELMASK_BOTH
,
457 FX_grTexDownloadMipMapLevel_NoLock(GR_TMU1
,
458 ti
->tm
[FX_TMU1
]->startAddr
,
460 FX_largeLodLog2(ti
->info
),
461 FX_aspectRatioLog2(ti
->info
),
463 GR_MIPMAPLEVELMASK_BOTH
,
469 "fx Driver: internal error in fxTMMoveInTM() -> wrong tmu (%d)\n",
475 fxMesa
->stats
.texUpload
++;
477 ti
->isInTM
= GL_TRUE
;
482 fxTMMoveInTM(fxMesaContext fxMesa
, struct gl_texture_object
*tObj
,
486 fxTMMoveInTM_NoLock(fxMesa
, tObj
, where
);
492 fxTMReloadMipMapLevel(fxMesaContext fxMesa
, struct gl_texture_object
*tObj
,
495 tfxTexInfo
*ti
= fxTMGetTexInfo(tObj
);
498 struct gl_texture_image
*texImage
= tObj
->Image
[level
];
499 tfxMipMapLevel
*mml
= FX_MIPMAP_DATA(texImage
);
503 if (!ti
->validated
) {
505 "fx Driver: internal error in fxTMReloadMipMapLevel() -> not validated\n");
510 tmu
= (int) ti
->whichTMU
;
511 fxTMMoveInTM(fxMesa
, tObj
, tmu
);
513 fxTexGetInfo(mml
->width
, mml
->height
,
514 &lodlevel
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
524 FX_grTexDownloadMipMapLevel(tmu
,
525 ti
->tm
[tmu
]->startAddr
,
526 FX_valueToLod(FX_lodToValue(lodlevel
)),
527 FX_largeLodLog2(ti
->info
),
528 FX_aspectRatioLog2(ti
->info
),
530 GR_MIPMAPLEVELMASK_BOTH
, texImage
->Data
);
533 FX_grTexDownloadMipMapLevel(GR_TMU0
,
534 ti
->tm
[GR_TMU0
]->startAddr
,
535 FX_valueToLod(FX_lodToValue(lodlevel
)),
536 FX_largeLodLog2(ti
->info
),
537 FX_aspectRatioLog2(ti
->info
),
539 GR_MIPMAPLEVELMASK_ODD
, texImage
->Data
);
541 FX_grTexDownloadMipMapLevel(GR_TMU1
,
542 ti
->tm
[GR_TMU1
]->startAddr
,
543 FX_valueToLod(FX_lodToValue(lodlevel
)),
544 FX_largeLodLog2(ti
->info
),
545 FX_aspectRatioLog2(ti
->info
),
547 GR_MIPMAPLEVELMASK_EVEN
, texImage
->Data
);
550 FX_grTexDownloadMipMapLevel(GR_TMU0
,
551 ti
->tm
[GR_TMU0
]->startAddr
,
552 FX_valueToLod(FX_lodToValue(lodlevel
)),
553 FX_largeLodLog2(ti
->info
),
554 FX_aspectRatioLog2(ti
->info
),
556 GR_MIPMAPLEVELMASK_BOTH
, texImage
->Data
);
558 FX_grTexDownloadMipMapLevel(GR_TMU1
,
559 ti
->tm
[GR_TMU1
]->startAddr
,
560 FX_valueToLod(FX_lodToValue(lodlevel
)),
561 FX_largeLodLog2(ti
->info
),
562 FX_aspectRatioLog2(ti
->info
),
564 GR_MIPMAPLEVELMASK_BOTH
, texImage
->Data
);
569 "fx Driver: internal error in fxTMReloadMipMapLevel() -> wrong tmu (%d)\n",
577 fxTMReloadSubMipMapLevel(fxMesaContext fxMesa
,
578 struct gl_texture_object
*tObj
,
579 GLint level
, GLint yoffset
, GLint height
)
581 tfxTexInfo
*ti
= fxTMGetTexInfo(tObj
);
583 unsigned short *data
;
585 struct gl_texture_image
*texImage
= tObj
->Image
[level
];
586 tfxMipMapLevel
*mml
= FX_MIPMAP_DATA(texImage
);
590 if (!ti
->validated
) {
592 "fx Driver: internal error in fxTMReloadSubMipMapLevel() -> not validated\n");
597 tmu
= (int) ti
->whichTMU
;
598 fxTMMoveInTM(fxMesa
, tObj
, tmu
);
600 fxTexGetInfo(mml
->width
, mml
->height
,
601 &lodlevel
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
603 if ((ti
->info
.format
== GR_TEXFMT_INTENSITY_8
) ||
604 (ti
->info
.format
== GR_TEXFMT_P_8
) ||
605 (ti
->info
.format
== GR_TEXFMT_ALPHA_8
))
606 data
= (GLushort
*) texImage
->Data
+ ((yoffset
* mml
->width
) >> 1);
608 data
= (GLushort
*) texImage
->Data
+ yoffset
* mml
->width
;
613 FX_grTexDownloadMipMapLevelPartial(tmu
,
614 ti
->tm
[tmu
]->startAddr
,
615 FX_valueToLod(FX_lodToValue(lodlevel
)
617 FX_largeLodLog2(ti
->info
),
618 FX_aspectRatioLog2(ti
->info
),
620 GR_MIPMAPLEVELMASK_BOTH
, data
,
621 yoffset
, yoffset
+ height
- 1);
624 FX_grTexDownloadMipMapLevelPartial(GR_TMU0
,
625 ti
->tm
[FX_TMU0
]->startAddr
,
626 FX_valueToLod(FX_lodToValue(lodlevel
)
628 FX_largeLodLog2(ti
->info
),
629 FX_aspectRatioLog2(ti
->info
),
631 GR_MIPMAPLEVELMASK_ODD
, data
,
632 yoffset
, yoffset
+ height
- 1);
634 FX_grTexDownloadMipMapLevelPartial(GR_TMU1
,
635 ti
->tm
[FX_TMU1
]->startAddr
,
636 FX_valueToLod(FX_lodToValue(lodlevel
)
638 FX_largeLodLog2(ti
->info
),
639 FX_aspectRatioLog2(ti
->info
),
641 GR_MIPMAPLEVELMASK_EVEN
, data
,
642 yoffset
, yoffset
+ height
- 1);
645 FX_grTexDownloadMipMapLevelPartial(GR_TMU0
,
646 ti
->tm
[FX_TMU0
]->startAddr
,
647 FX_valueToLod(FX_lodToValue(lodlevel
)
649 FX_largeLodLog2(ti
->info
),
650 FX_aspectRatioLog2(ti
->info
),
652 GR_MIPMAPLEVELMASK_BOTH
, data
,
653 yoffset
, yoffset
+ height
- 1);
655 FX_grTexDownloadMipMapLevelPartial(GR_TMU1
,
656 ti
->tm
[FX_TMU1
]->startAddr
,
657 FX_valueToLod(FX_lodToValue(lodlevel
)
659 FX_largeLodLog2(ti
->info
),
660 FX_aspectRatioLog2(ti
->info
),
662 GR_MIPMAPLEVELMASK_BOTH
, data
,
663 yoffset
, yoffset
+ height
- 1);
667 "fx Driver: internal error in fxTMReloadSubMipMapLevel() -> wrong tmu (%d)\n",
675 fxTMMoveOutTM(fxMesaContext fxMesa
, struct gl_texture_object
*tObj
)
677 tfxTexInfo
*ti
= fxTMGetTexInfo(tObj
);
679 if (MESA_VERBOSE
& VERBOSE_DRIVER
) {
680 fprintf(stderr
, "fxmesa: fxTMMoveOutTM(%x (%d))\n", (GLuint
) tObj
,
687 switch (ti
->whichTMU
) {
690 fxTMRemoveRange(fxMesa
, (int) ti
->whichTMU
, ti
->tm
[ti
->whichTMU
]);
694 fxTMRemoveRange(fxMesa
, FX_TMU0
, ti
->tm
[FX_TMU0
]);
695 fxTMRemoveRange(fxMesa
, FX_TMU1
, ti
->tm
[FX_TMU1
]);
698 fprintf(stderr
, "fx Driver: internal error in fxTMMoveOutTM()\n");
703 ti
->isInTM
= GL_FALSE
;
704 ti
->whichTMU
= FX_TMU_NONE
;
708 fxTMFreeTexture(fxMesaContext fxMesa
, struct gl_texture_object
*tObj
)
710 tfxTexInfo
*ti
= fxTMGetTexInfo(tObj
);
713 fxTMMoveOutTM(fxMesa
, tObj
);
715 for (i
= 0; i
< MAX_TEXTURE_LEVELS
; i
++) {
716 struct gl_texture_image
*texImage
= tObj
->Image
[i
];
717 if (texImage
->Data
) {
718 FREE(texImage
->Data
);
719 texImage
->Data
= NULL
;
721 if (texImage
->DriverData
) {
722 FREE(texImage
->DriverData
);
723 texImage
->DriverData
= NULL
;
726 switch (ti
->whichTMU
) {
729 fxTMDeleteRangeNode(fxMesa
, ti
->tm
[ti
->whichTMU
]);
733 fxTMDeleteRangeNode(fxMesa
, ti
->tm
[FX_TMU0
]);
734 fxTMDeleteRangeNode(fxMesa
, ti
->tm
[FX_TMU1
]);
740 fxTMInit(fxMesaContext fxMesa
)
742 fxMesa
->texBindNumber
= 0;
745 fxTMUInit(fxMesa
, FX_TMU0
);
747 if (fxMesa
->haveTwoTMUs
)
748 fxTMUInit(fxMesa
, FX_TMU1
);
752 fxTMClose(fxMesaContext fxMesa
)
754 MemRange
*tmp
, *next
;
756 tmp
= fxMesa
->tmPool
;
762 tmp
= fxMesa
->tmFree
[FX_TMU0
];
768 if (fxMesa
->haveTwoTMUs
) {
769 tmp
= fxMesa
->tmFree
[FX_TMU1
];
779 fxTMRestoreTextures_NoLock(fxMesaContext ctx
)
782 struct gl_texture_object
*tObj
;
785 tObj
= ctx
->glCtx
->Shared
->TexObjectList
;
787 ti
= fxTMGetTexInfo(tObj
);
788 if (ti
&& ti
->isInTM
) {
789 for (i
= 0; i
< MAX_TEXTURE_UNITS
; i
++)
790 if (ctx
->glCtx
->Texture
.Unit
[i
]._Current
== tObj
) {
791 /* Force the texture onto the board, as it could be in use */
792 where
= ti
->whichTMU
;
793 fxTMMoveOutTM_NoLock(ctx
, tObj
);
794 fxTMMoveInTM_NoLock(ctx
, tObj
, where
);
797 if (i
== MAX_TEXTURE_UNITS
) /* Mark the texture as off the board */
798 fxTMMoveOutTM_NoLock(ctx
, tObj
);
808 * Need this to provide at least one external definition.
811 extern int gl_fx_dummy_function_texman(void);
813 gl_fx_dummy_function_texman(void)