/* * Mesa 3-D graphics library * Version: 4.0 * * Copyright (C) 1999-2001 Brian Paul All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Authors: * David Bucciarelli * Brian Paul * Daryll Strauss * Keith Whitwell * Daniel Borca * Hiroshi Morii */ /* fxtexman.c - 3Dfx VooDoo texture memory functions */ #ifdef HAVE_CONFIG_H #include "conf.h" #endif #if defined(FX) #include "hash.h" #include "fxdrv.h" int texSwaps = 0; static FxU32 texBoundMask; #define FX_2MB_SPLIT 0x200000 static struct gl_texture_object *fxTMFindOldestObject(fxMesaContext fxMesa, int tmu); #ifdef TEXSANITY static void fubar() { } /* Sanity Check */ static void sanity(fxMesaContext fxMesa) { MemRange *tmp, *prev, *pos; prev = 0; tmp = fxMesa->tmFree[0]; while (tmp) { if (!tmp->startAddr && !tmp->endAddr) { fprintf(stderr, "Textures fubar\n"); fubar(); } if (tmp->startAddr >= tmp->endAddr) { fprintf(stderr, "Node fubar\n"); fubar(); } if (prev && (prev->startAddr >= tmp->startAddr || prev->endAddr > tmp->startAddr)) { fprintf(stderr, "Sorting fubar\n"); fubar(); } prev = tmp; tmp = tmp->next; } prev = 0; tmp = fxMesa->tmFree[1]; while (tmp) { if (!tmp->startAddr && !tmp->endAddr) { fprintf(stderr, "Textures fubar\n"); fubar(); } if (tmp->startAddr >= tmp->endAddr) { fprintf(stderr, "Node fubar\n"); fubar(); } if (prev && (prev->startAddr >= tmp->startAddr || prev->endAddr > tmp->startAddr)) { fprintf(stderr, "Sorting fubar\n"); fubar(); } prev = tmp; tmp = tmp->next; } } #endif static MemRange * fxTMNewRangeNode(fxMesaContext fxMesa, FxU32 start, FxU32 end) { MemRange *result = 0; if (fxMesa->tmPool) { result = fxMesa->tmPool; fxMesa->tmPool = fxMesa->tmPool->next; } else { if (!(result = MALLOC(sizeof(MemRange)))) { fprintf(stderr, "fxTMNewRangeNode: ERROR: out of memory!\n"); fxCloseHardware(); exit(-1); } } result->startAddr = start; result->endAddr = end; return result; } #if 1 #define fxTMDeleteRangeNode(fxMesa, range) \ do { \ range->next = fxMesa->tmPool; \ fxMesa->tmPool = range; \ } while (0); #else static void fxTMDeleteRangeNode(fxMesaContext fxMesa, MemRange * range) { range->next = fxMesa->tmPool; fxMesa->tmPool = range; } #endif static void fxTMUInit(fxMesaContext fxMesa, int tmu) { MemRange *tmn, *last; FxU32 start, end, blockstart, blockend, chunk; start = grTexMinAddress(tmu); end = grTexMaxAddress(tmu); chunk = (fxMesa->type >= GR_SSTTYPE_Banshee) ? (end - start) : FX_2MB_SPLIT; if (fxMesa->verbose) { fprintf(stderr, "Voodoo TMU%d configuration:\n", tmu); } fxMesa->freeTexMem[tmu] = end - start; fxMesa->tmFree[tmu] = NULL; last = 0; blockstart = start; while (blockstart < end) { if (blockstart + chunk > end) blockend = end; else blockend = blockstart + chunk; if (fxMesa->verbose) fprintf(stderr, "Voodoo %08u-%08u\n", (unsigned int) blockstart, (unsigned int) blockend); tmn = fxTMNewRangeNode(fxMesa, blockstart, blockend); tmn->next = NULL; if (last) last->next = tmn; else fxMesa->tmFree[tmu] = tmn; last = tmn; blockstart += chunk; } } static int fxTMFindStartAddr(fxMesaContext fxMesa, GLint tmu, int size) { MemRange *prev, *tmp; int result; struct gl_texture_object *obj; if (fxMesa->HaveTexUma) { tmu = FX_TMU0; } while (1) { prev = 0; tmp = fxMesa->tmFree[tmu]; while (tmp) { if (tmp->endAddr - tmp->startAddr >= size) { /* Fits here */ result = tmp->startAddr; tmp->startAddr += size; if (tmp->startAddr == tmp->endAddr) { /* Empty */ if (prev) { prev->next = tmp->next; } else { fxMesa->tmFree[tmu] = tmp->next; } fxTMDeleteRangeNode(fxMesa, tmp); } fxMesa->freeTexMem[tmu] -= size; return result; } prev = tmp; tmp = tmp->next; } /* No free space. Discard oldest */ if (TDFX_DEBUG & VERBOSE_TEXTURE) { fprintf(stderr, "fxTMFindStartAddr: No free space. Discard oldest\n"); } obj = fxTMFindOldestObject(fxMesa, tmu); if (!obj) { fprintf(stderr, "fxTMFindStartAddr: ERROR: No space for texture\n"); return -1; } fxTMMoveOutTM(fxMesa, obj); texSwaps++; } } int fxTMCheckStartAddr (fxMesaContext fxMesa, GLint tmu, tfxTexInfo *ti) { MemRange *tmp; int size; if (fxMesa->HaveTexUma) { return FXTRUE; } size = grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH, &(ti->info)); tmp = fxMesa->tmFree[tmu]; while (tmp) { if (tmp->endAddr - tmp->startAddr >= size) { /* Fits here */ return FXTRUE; } tmp = tmp->next; } return FXFALSE; } static void fxTMRemoveRange(fxMesaContext fxMesa, GLint tmu, MemRange * range) { MemRange *tmp, *prev; if (fxMesa->HaveTexUma) { tmu = FX_TMU0; } if (range->startAddr == range->endAddr) { fxTMDeleteRangeNode(fxMesa, range); return; } fxMesa->freeTexMem[tmu] += range->endAddr - range->startAddr; prev = 0; tmp = fxMesa->tmFree[tmu]; while (tmp) { if (range->startAddr > tmp->startAddr) { prev = tmp; tmp = tmp->next; } else break; } /* When we create the regions, we make a split at the 2MB boundary. Now we have to make sure we don't join those 2MB boundary regions back together again. */ range->next = tmp; if (tmp) { if (range->endAddr == tmp->startAddr && tmp->startAddr & texBoundMask) { /* Combine */ tmp->startAddr = range->startAddr; fxTMDeleteRangeNode(fxMesa, range); range = tmp; } } if (prev) { if (prev->endAddr == range->startAddr && range->startAddr & texBoundMask) { /* Combine */ prev->endAddr = range->endAddr; prev->next = range->next; fxTMDeleteRangeNode(fxMesa, range); } else prev->next = range; } else { fxMesa->tmFree[tmu] = range; } } static struct gl_texture_object * fxTMFindOldestObject(fxMesaContext fxMesa, int tmu) { GLuint age, old, lasttime, bindnumber; GLfloat lowestPriority; struct gl_texture_object *obj, *lowestPriorityObj; struct _mesa_HashTable *textures = fxMesa->glCtx->Shared->TexObjects; GLuint id; if (!_mesa_HashFirstEntry(textures)) return 0; obj = NULL; old = 0; lowestPriorityObj = NULL; lowestPriority = 1.0F; bindnumber = fxMesa->texBindNumber; for (id = _mesa_HashFirstEntry(textures); id; id = _mesa_HashNextEntry(textures, id)) { struct gl_texture_object *tmp = (struct gl_texture_object *) _mesa_HashLookup(textures, id); tfxTexInfo *info = fxTMGetTexInfo(tmp); if (info && info->isInTM && ((info->whichTMU == tmu) || (info->whichTMU == FX_TMU_BOTH) || (info->whichTMU == FX_TMU_SPLIT) || fxMesa->HaveTexUma ) ) { lasttime = info->lastTimeUsed; if (lasttime > bindnumber) age = bindnumber + (UINT_MAX - lasttime + 1); /* TO DO: check wrap around */ else age = bindnumber - lasttime; if (age >= old) { old = age; obj = tmp; } /* examine priority */ if (tmp->Priority < lowestPriority) { lowestPriority = tmp->Priority; lowestPriorityObj = tmp; } } } if (lowestPriorityObj != NULL) { if (TDFX_DEBUG & VERBOSE_TEXTURE) { fprintf(stderr, "fxTMFindOldestObject: %d pri=%f\n", lowestPriorityObj->Name, lowestPriority); } return lowestPriorityObj; } else { if (TDFX_DEBUG & VERBOSE_TEXTURE) { if (obj != NULL) { fprintf(stderr, "fxTMFindOldestObject: %d age=%d\n", obj->Name, old); } } return obj; } } static MemRange * fxTMAddObj(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint tmu, int texmemsize) { FxI32 startAddr; MemRange *range; startAddr = fxTMFindStartAddr(fxMesa, tmu, texmemsize); if (startAddr < 0) return 0; range = fxTMNewRangeNode(fxMesa, startAddr, startAddr + texmemsize); return range; } /* External Functions */ void fxTMMoveInTM_NoLock(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint where) { tfxTexInfo *ti = fxTMGetTexInfo(tObj); int i, l; int texmemsize; if (TDFX_DEBUG & VERBOSE_DRIVER) { fprintf(stderr, "fxTMMoveInTM_NoLock(%d)\n", tObj->Name); } fxMesa->stats.reqTexUpload++; if (!ti->validated) { fprintf(stderr, "fxTMMoveInTM_NoLock: INTERNAL ERROR: not validated\n"); fxCloseHardware(); exit(-1); } if (ti->isInTM) { if (ti->whichTMU == where) return; if (where == FX_TMU_SPLIT || ti->whichTMU == FX_TMU_SPLIT) fxTMMoveOutTM_NoLock(fxMesa, tObj); else { if (ti->whichTMU == FX_TMU_BOTH) return; where = FX_TMU_BOTH; } } if (TDFX_DEBUG & (VERBOSE_DRIVER | VERBOSE_TEXTURE)) { fprintf(stderr, "fxTMMoveInTM_NoLock: downloading %p (%d) in texture memory in %d\n", (void *)tObj, tObj->Name, where); } ti->whichTMU = (FxU32) where; switch (where) { case FX_TMU0: case FX_TMU1: texmemsize = (int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH, &(ti->info)); ti->tm[where] = fxTMAddObj(fxMesa, tObj, where, texmemsize); fxMesa->stats.memTexUpload += texmemsize; for (i = FX_largeLodValue(ti->info), l = ti->minLevel; i <= FX_smallLodValue(ti->info); i++, l++) { struct gl_texture_image *texImage = tObj->Image[0][l]; grTexDownloadMipMapLevel(where, ti->tm[where]->startAddr, FX_valueToLod(i), FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_BOTH, texImage->Data); } break; case FX_TMU_SPLIT: texmemsize = (int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_ODD, &(ti->info)); ti->tm[FX_TMU0] = fxTMAddObj(fxMesa, tObj, FX_TMU0, texmemsize); fxMesa->stats.memTexUpload += texmemsize; texmemsize = (int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_EVEN, &(ti->info)); ti->tm[FX_TMU1] = fxTMAddObj(fxMesa, tObj, FX_TMU1, texmemsize); fxMesa->stats.memTexUpload += texmemsize; for (i = FX_largeLodValue(ti->info), l = ti->minLevel; i <= FX_smallLodValue(ti->info); i++, l++) { struct gl_texture_image *texImage = tObj->Image[0][l]; grTexDownloadMipMapLevel(GR_TMU0, ti->tm[FX_TMU0]->startAddr, FX_valueToLod(i), FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_ODD, texImage->Data); grTexDownloadMipMapLevel(GR_TMU1, ti->tm[FX_TMU1]->startAddr, FX_valueToLod(i), FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_EVEN, texImage->Data); } break; case FX_TMU_BOTH: texmemsize = (int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH, &(ti->info)); ti->tm[FX_TMU0] = fxTMAddObj(fxMesa, tObj, FX_TMU0, texmemsize); fxMesa->stats.memTexUpload += texmemsize; /*texmemsize = (int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH, &(ti->info));*/ ti->tm[FX_TMU1] = fxTMAddObj(fxMesa, tObj, FX_TMU1, texmemsize); fxMesa->stats.memTexUpload += texmemsize; for (i = FX_largeLodValue(ti->info), l = ti->minLevel; i <= FX_smallLodValue(ti->info); i++, l++) { struct gl_texture_image *texImage = tObj->Image[0][l]; grTexDownloadMipMapLevel(GR_TMU0, ti->tm[FX_TMU0]->startAddr, FX_valueToLod(i), FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_BOTH, texImage->Data); grTexDownloadMipMapLevel(GR_TMU1, ti->tm[FX_TMU1]->startAddr, FX_valueToLod(i), FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_BOTH, texImage->Data); } break; default: fprintf(stderr, "fxTMMoveInTM_NoLock: INTERNAL ERROR: wrong tmu (%d)\n", where); fxCloseHardware(); exit(-1); } fxMesa->stats.texUpload++; ti->isInTM = GL_TRUE; } void fxTMMoveInTM(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint where) { BEGIN_BOARD_LOCK(); fxTMMoveInTM_NoLock(fxMesa, tObj, where); END_BOARD_LOCK(); } void fxTMReloadMipMapLevel(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint level) { tfxTexInfo *ti = fxTMGetTexInfo(tObj); GrLOD_t lodlevel; GLint tmu; struct gl_texture_image *texImage = tObj->Image[0][level]; tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage); if (TDFX_DEBUG & VERBOSE_TEXTURE) { fprintf(stderr, "fxTMReloadMipMapLevel(%p (%d), %d)\n", (void *)tObj, tObj->Name, level); } assert(mml); assert(mml->width > 0); assert(mml->height > 0); assert(mml->glideFormat > 0); assert(ti->isInTM); if (!ti->validated) { fprintf(stderr, "fxTMReloadMipMapLevel: INTERNAL ERROR: not validated\n"); fxCloseHardware(); exit(-1); } tmu = (int) ti->whichTMU; fxMesa->stats.reqTexUpload++; fxMesa->stats.texUpload++; lodlevel = ti->info.largeLodLog2 - (level - ti->minLevel); switch (tmu) { case FX_TMU0: case FX_TMU1: grTexDownloadMipMapLevel(tmu, ti->tm[tmu]->startAddr, lodlevel, FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_BOTH, texImage->Data); break; case FX_TMU_SPLIT: grTexDownloadMipMapLevel(GR_TMU0, ti->tm[GR_TMU0]->startAddr, lodlevel, FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_ODD, texImage->Data); grTexDownloadMipMapLevel(GR_TMU1, ti->tm[GR_TMU1]->startAddr, lodlevel, FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_EVEN, texImage->Data); break; case FX_TMU_BOTH: grTexDownloadMipMapLevel(GR_TMU0, ti->tm[GR_TMU0]->startAddr, lodlevel, FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_BOTH, texImage->Data); grTexDownloadMipMapLevel(GR_TMU1, ti->tm[GR_TMU1]->startAddr, lodlevel, FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_BOTH, texImage->Data); break; default: fprintf(stderr, "fxTMReloadMipMapLevel: INTERNAL ERROR: wrong tmu (%d)\n", tmu); fxCloseHardware(); exit(-1); } } void fxTMReloadSubMipMapLevel(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint level, GLint yoffset, GLint height) { tfxTexInfo *ti = fxTMGetTexInfo(tObj); GrLOD_t lodlevel; unsigned short *data; GLint tmu; struct gl_texture_image *texImage = tObj->Image[0][level]; tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage); assert(mml); if (!ti->validated) { fprintf(stderr, "fxTMReloadSubMipMapLevel: INTERNAL ERROR: not validated\n"); fxCloseHardware(); exit(-1); } tmu = (int) ti->whichTMU; fxTMMoveInTM(fxMesa, tObj, tmu); fxTexGetInfo(mml->width, mml->height, &lodlevel, NULL, NULL, NULL, NULL, NULL); if ((ti->info.format == GR_TEXFMT_INTENSITY_8) || (ti->info.format == GR_TEXFMT_P_8) || (ti->info.format == GR_TEXFMT_ALPHA_8)) data = (GLushort *) texImage->Data + ((yoffset * mml->width) >> 1); else data = (GLushort *) texImage->Data + yoffset * mml->width; switch (tmu) { case FX_TMU0: case FX_TMU1: grTexDownloadMipMapLevelPartial(tmu, ti->tm[tmu]->startAddr, FX_valueToLod(FX_lodToValue(lodlevel) + level), FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_BOTH, data, yoffset, yoffset + height - 1); break; case FX_TMU_SPLIT: grTexDownloadMipMapLevelPartial(GR_TMU0, ti->tm[FX_TMU0]->startAddr, FX_valueToLod(FX_lodToValue(lodlevel) + level), FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_ODD, data, yoffset, yoffset + height - 1); grTexDownloadMipMapLevelPartial(GR_TMU1, ti->tm[FX_TMU1]->startAddr, FX_valueToLod(FX_lodToValue(lodlevel) + level), FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_EVEN, data, yoffset, yoffset + height - 1); break; case FX_TMU_BOTH: grTexDownloadMipMapLevelPartial(GR_TMU0, ti->tm[FX_TMU0]->startAddr, FX_valueToLod(FX_lodToValue(lodlevel) + level), FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_BOTH, data, yoffset, yoffset + height - 1); grTexDownloadMipMapLevelPartial(GR_TMU1, ti->tm[FX_TMU1]->startAddr, FX_valueToLod(FX_lodToValue(lodlevel) + level), FX_largeLodLog2(ti->info), FX_aspectRatioLog2(ti->info), ti->info.format, GR_MIPMAPLEVELMASK_BOTH, data, yoffset, yoffset + height - 1); break; default: fprintf(stderr, "fxTMReloadSubMipMapLevel: INTERNAL ERROR: wrong tmu (%d)\n", tmu); fxCloseHardware(); exit(-1); } } void fxTMMoveOutTM(fxMesaContext fxMesa, struct gl_texture_object *tObj) { tfxTexInfo *ti = fxTMGetTexInfo(tObj); if (TDFX_DEBUG & VERBOSE_DRIVER) { fprintf(stderr, "fxTMMoveOutTM(%p (%d))\n", (void *)tObj, tObj->Name); } if (!ti->isInTM) return; switch (ti->whichTMU) { case FX_TMU0: case FX_TMU1: fxTMRemoveRange(fxMesa, (int) ti->whichTMU, ti->tm[ti->whichTMU]); break; case FX_TMU_SPLIT: case FX_TMU_BOTH: fxTMRemoveRange(fxMesa, FX_TMU0, ti->tm[FX_TMU0]); fxTMRemoveRange(fxMesa, FX_TMU1, ti->tm[FX_TMU1]); break; default: fprintf(stderr, "fxTMMoveOutTM: INTERNAL ERROR: bad TMU (%ld)\n", ti->whichTMU); fxCloseHardware(); exit(-1); } ti->isInTM = GL_FALSE; ti->whichTMU = FX_TMU_NONE; } void fxTMFreeTexture(fxMesaContext fxMesa, struct gl_texture_object *tObj) { tfxTexInfo *ti = fxTMGetTexInfo(tObj); int i; if (TDFX_DEBUG & VERBOSE_TEXTURE) { fprintf(stderr, "fxTMFreeTexture(%p (%d))\n", (void *)tObj, tObj->Name); } fxTMMoveOutTM(fxMesa, tObj); for (i = 0; i < MAX_TEXTURE_LEVELS; i++) { struct gl_texture_image *texImage = tObj->Image[0][i]; if (texImage) { if (texImage->DriverData) { FREE(texImage->DriverData); texImage->DriverData = NULL; } } } switch (ti->whichTMU) { case FX_TMU0: case FX_TMU1: fxTMDeleteRangeNode(fxMesa, ti->tm[ti->whichTMU]); break; case FX_TMU_SPLIT: case FX_TMU_BOTH: fxTMDeleteRangeNode(fxMesa, ti->tm[FX_TMU0]); fxTMDeleteRangeNode(fxMesa, ti->tm[FX_TMU1]); break; } } void fxTMInit(fxMesaContext fxMesa) { fxMesa->texBindNumber = 0; fxMesa->tmPool = 0; if (fxMesa->HaveTexUma) { grEnable(GR_TEXTURE_UMA_EXT); } fxTMUInit(fxMesa, FX_TMU0); if (!fxMesa->HaveTexUma && fxMesa->haveTwoTMUs) fxTMUInit(fxMesa, FX_TMU1); texBoundMask = (fxMesa->type >= GR_SSTTYPE_Banshee) ? -1 : (FX_2MB_SPLIT - 1); } void fxTMClose(fxMesaContext fxMesa) { MemRange *tmp, *next; tmp = fxMesa->tmPool; while (tmp) { next = tmp->next; FREE(tmp); tmp = next; } tmp = fxMesa->tmFree[FX_TMU0]; while (tmp) { next = tmp->next; FREE(tmp); tmp = next; } if (fxMesa->haveTwoTMUs) { tmp = fxMesa->tmFree[FX_TMU1]; while (tmp) { next = tmp->next; FREE(tmp); tmp = next; } } } void fxTMRestoreTextures_NoLock(fxMesaContext ctx) { struct _mesa_HashTable *textures = ctx->glCtx->Shared->TexObjects; GLuint id; for (id = _mesa_HashFirstEntry(textures); id; id = _mesa_HashNextEntry(textures, id)) { struct gl_texture_object *tObj = (struct gl_texture_object *) _mesa_HashLookup(textures, id); tfxTexInfo *ti = fxTMGetTexInfo(tObj); if (ti && ti->isInTM) { int i; for (i = 0; i < MAX_TEXTURE_UNITS; i++) { if (ctx->glCtx->Texture.Unit[i]._Current == tObj) { /* Force the texture onto the board, as it could be in use */ int where = ti->whichTMU; fxTMMoveOutTM_NoLock(ctx, tObj); fxTMMoveInTM_NoLock(ctx, tObj, where); break; } } if (i == MAX_TEXTURE_UNITS) /* Mark the texture as off the board */ fxTMMoveOutTM_NoLock(ctx, tObj); } } } #else /* * Need this to provide at least one external definition. */ extern int gl_fx_dummy_function_texman(void); int gl_fx_dummy_function_texman(void) { return 0; } #endif /* FX */