/* -*- mode: c; c-basic-offset: 3 -*- * * Copyright 2000 VA Linux Systems Inc., Fremont, California. * * 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 (including the next * paragraph) 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 * VA LINUX SYSTEMS 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. */ /* * Original rewrite: * Gareth Hughes , 29 Sep - 1 Oct 2000 * * Authors: * Gareth Hughes * Brian Paul * */ #include "tdfx_context.h" #include "tdfx_texman.h" #include "main/texobj.h" #include "main/hash.h" #define BAD_ADDRESS ((FxU32) -1) #if 0 /* DEBUG use */ /* * Verify the consistancy of the texture memory manager. * This involves: * Traversing all texture objects and computing total memory used. * Traverse the free block list and computing total memory free. * Compare the total free and total used amounts to the total memory size. * Make various assertions about the results. */ static void VerifyFreeList(tdfxContextPtr fxMesa, FxU32 tmu) { struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared; struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData; tdfxMemRange *block; int prevStart = -1, prevEnd = -1; int totalFree = 0; int numObj = 0, numRes = 0; int totalUsed = 0; for (block = shared->tmFree[tmu]; block; block = block->next) { assert( block->endAddr > 0 ); assert( block->startAddr <= shared->totalTexMem[tmu] ); assert( block->endAddr <= shared->totalTexMem[tmu] ); assert( (int) block->startAddr > prevStart ); assert( (int) block->startAddr >= prevEnd ); prevStart = (int) block->startAddr; prevEnd = (int) block->endAddr; totalFree += (block->endAddr - block->startAddr); } assert(totalFree == shared->freeTexMem[tmu]); { struct _mesa_HashTable *textures = fxMesa->glCtx->Shared->TexObjects; GLuint id; for (id = _mesa_HashFirstEntry(textures); id; id = _mesa_HashNextEntry(textures, id)) { struct gl_texture_object *tObj = _mesa_lookup_texture(fxMesa->glCtx, id); tdfxTexInfo *ti = TDFX_TEXTURE_DATA(tObj); if (ti) { if (ti->isInTM) { numRes++; assert(ti->tm[0]); if (ti->tm[tmu]) totalUsed += (ti->tm[tmu]->endAddr - ti->tm[tmu]->startAddr); } else { assert(!ti->tm[0]); } } } } printf("totalFree: %d totalUsed: %d totalMem: %d #objs=%d #res=%d\n", shared->freeTexMem[tmu], totalUsed, shared->totalTexMem[tmu], numObj, numRes); assert(totalUsed + totalFree == shared->totalTexMem[tmu]); } static void dump_texmem(tdfxContextPtr fxMesa) { struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared; struct _mesa_HashTable *textures = mesaShared->TexObjects; struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData; tdfxMemRange *r; FxU32 prev; GLuint id; printf("DUMP Objects:\n"); for (id = _mesa_HashFirstEntry(textures); id; id = _mesa_HashNextEntry(textures, id)) { struct gl_texture_object *obj = _mesa_lookup_texture(fxMesa->glCtx, id); tdfxTexInfo *info = TDFX_TEXTURE_DATA(obj); if (info && info->isInTM) { printf("Obj %8p: %4d info = %p\n", obj, obj->Name, info); printf(" isInTM=%d whichTMU=%d lastTimeUsed=%d\n", info->isInTM, info->whichTMU, info->lastTimeUsed); printf(" tm[0] = %p", info->tm[0]); assert(info->tm[0]); if (info->tm[0]) { printf(" tm startAddr = %d endAddr = %d", info->tm[0]->startAddr, info->tm[0]->endAddr); } printf("\n"); printf(" tm[1] = %p", info->tm[1]); if (info->tm[1]) { printf(" tm startAddr = %d endAddr = %d", info->tm[1]->startAddr, info->tm[1]->endAddr); } printf("\n"); } } VerifyFreeList(fxMesa, 0); VerifyFreeList(fxMesa, 1); printf("Free memory unit 0: %d bytes\n", shared->freeTexMem[0]); prev = 0; for (r = shared->tmFree[0]; r; r = r->next) { printf("%8p: start %8d end %8d size %8d gap %8d\n", r, r->startAddr, r->endAddr, r->endAddr - r->startAddr, r->startAddr - prev); prev = r->endAddr; } printf("Free memory unit 1: %d bytes\n", shared->freeTexMem[1]); prev = 0; for (r = shared->tmFree[1]; r; r = r->next) { printf("%8p: start %8d end %8d size %8d gap %8d\n", r, r->startAddr, r->endAddr, r->endAddr - r->startAddr, r->startAddr - prev); prev = r->endAddr; } } #endif #ifdef TEXSANITY static void fubar(void) { } /* * Sanity Check */ static void sanity(tdfxContextPtr fxMesa) { tdfxMemRange *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 /* * Allocate and initialize a new MemRange struct. * Try to allocate it from the pool of free MemRange nodes rather than malloc. */ static tdfxMemRange * NewRangeNode(tdfxContextPtr fxMesa, FxU32 start, FxU32 end) { struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared; struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData; tdfxMemRange *result; _glthread_LOCK_MUTEX(mesaShared->Mutex); if (shared && shared->tmPool) { result = shared->tmPool; shared->tmPool = shared->tmPool->next; } else { result = MALLOC(sizeof(tdfxMemRange)); } _glthread_UNLOCK_MUTEX(mesaShared->Mutex); if (!result) { /*fprintf(stderr, "fxDriver: out of memory!\n");*/ return NULL; } result->startAddr = start; result->endAddr = end; result->next = NULL; return result; } /* * Initialize texture memory. * We take care of one or both TMU's here. */ void tdfxTMInit(tdfxContextPtr fxMesa) { if (!fxMesa->glCtx->Shared->DriverData) { const char *extensions; struct tdfxSharedState *shared = CALLOC_STRUCT(tdfxSharedState); if (!shared) return; LOCK_HARDWARE(fxMesa); extensions = fxMesa->Glide.grGetString(GR_EXTENSION); UNLOCK_HARDWARE(fxMesa); if (strstr(extensions, "TEXUMA")) { FxU32 start, end; shared->umaTexMemory = GL_TRUE; LOCK_HARDWARE(fxMesa); fxMesa->Glide.grEnable(GR_TEXTURE_UMA_EXT); start = fxMesa->Glide.grTexMinAddress(0); end = fxMesa->Glide.grTexMaxAddress(0); UNLOCK_HARDWARE(fxMesa); shared->totalTexMem[0] = end - start; shared->totalTexMem[1] = 0; shared->freeTexMem[0] = end - start; shared->freeTexMem[1] = 0; shared->tmFree[0] = NewRangeNode(fxMesa, start, end); shared->tmFree[1] = NULL; /*printf("UMA tex memory: %d\n", (int) (end - start));*/ } else { const int numTMUs = fxMesa->haveTwoTMUs ? 2 : 1; int tmu; shared->umaTexMemory = GL_FALSE; LOCK_HARDWARE(fxMesa); for (tmu = 0; tmu < numTMUs; tmu++) { FxU32 start = fxMesa->Glide.grTexMinAddress(tmu); FxU32 end = fxMesa->Glide.grTexMaxAddress(tmu); shared->totalTexMem[tmu] = end - start; shared->freeTexMem[tmu] = end - start; shared->tmFree[tmu] = NewRangeNode(fxMesa, start, end); /*printf("Split tex memory: %d\n", (int) (end - start));*/ } UNLOCK_HARDWARE(fxMesa); } shared->tmPool = NULL; fxMesa->glCtx->Shared->DriverData = shared; /*printf("Texture memory init UMA: %d\n", shared->umaTexMemory);*/ } } /* * Clean-up texture memory before destroying context. */ void tdfxTMClose(tdfxContextPtr fxMesa) { if (fxMesa->glCtx->Shared->RefCount == 1 && fxMesa->driDrawable) { /* refcount will soon go to zero, free our 3dfx stuff */ struct tdfxSharedState *shared = (struct tdfxSharedState *) fxMesa->glCtx->Shared->DriverData; const int numTMUs = fxMesa->haveTwoTMUs ? 2 : 1; int tmu; tdfxMemRange *tmp, *next; /* Deallocate the pool of free tdfxMemRange nodes */ tmp = shared->tmPool; while (tmp) { next = tmp->next; FREE(tmp); tmp = next; } /* Delete the texture memory block tdfxMemRange nodes */ for (tmu = 0; tmu < numTMUs; tmu++) { tmp = shared->tmFree[tmu]; while (tmp) { next = tmp->next; FREE(tmp); tmp = next; } } FREE(shared); fxMesa->glCtx->Shared->DriverData = NULL; } } /* * Delete a tdfxMemRange struct. * We keep a linked list of free/available tdfxMemRange structs to * avoid extra malloc/free calls. */ #if 0 static void DeleteRangeNode_NoLock(struct TdfxSharedState *shared, tdfxMemRange *range) { /* insert at head of list */ range->next = shared->tmPool; shared->tmPool = range; } #endif #define DELETE_RANGE_NODE(shared, range) \ (range)->next = (shared)->tmPool; \ (shared)->tmPool = (range) /* * When we've run out of texture memory we have to throw out an * existing texture to make room for the new one. This function * determins the texture to throw out. */ static struct gl_texture_object * FindOldestObject(tdfxContextPtr fxMesa, FxU32 tmu) { const GLuint bindnumber = fxMesa->texBindNumber; struct gl_texture_object *oldestObj, *lowestPriorityObj; GLfloat lowestPriority; GLuint oldestAge; GLuint id; struct _mesa_HashTable *textures = fxMesa->glCtx->Shared->TexObjects; oldestObj = NULL; oldestAge = 0; lowestPriority = 1.0F; lowestPriorityObj = NULL; for (id = _mesa_HashFirstEntry(textures); id; id = _mesa_HashNextEntry(textures, id)) { struct gl_texture_object *obj = _mesa_lookup_texture(fxMesa->glCtx, id); tdfxTexInfo *info = TDFX_TEXTURE_DATA(obj); if (info && info->isInTM && ((info->whichTMU == tmu) || (info->whichTMU == TDFX_TMU_BOTH) || (info->whichTMU == TDFX_TMU_SPLIT))) { GLuint age, lasttime; assert(info->tm[0]); lasttime = info->lastTimeUsed; if (lasttime > bindnumber) age = bindnumber + (UINT_MAX - lasttime + 1); /* TO DO: check wrap around */ else age = bindnumber - lasttime; if (age >= oldestAge) { oldestAge = age; oldestObj = obj; } /* examine priority */ if (obj->Priority < lowestPriority) { lowestPriority = obj->Priority; lowestPriorityObj = obj; } } } if (lowestPriority < 1.0) { ASSERT(lowestPriorityObj); /* printf("discard %d pri=%f\n", lowestPriorityObj->Name, lowestPriority); */ return lowestPriorityObj; } else { /* printf("discard %d age=%d\n", oldestObj->Name, oldestAge); */ return oldestObj; } } #if 0 static void FlushTexMemory(tdfxContextPtr fxMesa) { struct _mesa_HashTable *textures = fxMesa->glCtx->Shared->TexObjects; GLuint id; for (id = _mesa_HashFirstEntry(textures); id; id = _mesa_HashNextEntry(textures, id)) { struct gl_texture_object *obj = _mesa_lookup_texture(fxMesa->glCtx, id); if (obj->RefCount < 2) { /* don't flush currently bound textures */ tdfxTMMoveOutTM_NoLock(fxMesa, obj); } } } #endif /* * Find the address (offset?) at which we can store a new texture. * is the texture unit. * is the texture size in bytes. */ static FxU32 FindStartAddr(tdfxContextPtr fxMesa, FxU32 tmu, FxU32 size) { struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared; struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData; tdfxMemRange *prev, *block; FxU32 result; #if 0 int discardedCount = 0; #define MAX_DISCARDS 10 #endif if (shared->umaTexMemory) { assert(tmu == TDFX_TMU0); } _glthread_LOCK_MUTEX(mesaShared->Mutex); while (1) { prev = NULL; block = shared->tmFree[tmu]; while (block) { if (block->endAddr - block->startAddr >= size) { /* The texture will fit here */ result = block->startAddr; block->startAddr += size; if (block->startAddr == block->endAddr) { /* Remove this node since it's empty */ if (prev) { prev->next = block->next; } else { shared->tmFree[tmu] = block->next; } DELETE_RANGE_NODE(shared, block); } shared->freeTexMem[tmu] -= size; _glthread_UNLOCK_MUTEX(mesaShared->Mutex); return result; } prev = block; block = block->next; } /* We failed to find a block large enough to accomodate bytes. * Find the oldest texObject and free it. */ #if 0 discardedCount++; if (discardedCount > MAX_DISCARDS + 1) { _mesa_problem(NULL, "%s: extreme texmem fragmentation", __FUNCTION__); _glthread_UNLOCK_MUTEX(mesaShared->Mutex); return BAD_ADDRESS; } else if (discardedCount > MAX_DISCARDS) { /* texture memory is probably really fragmented, flush it */ FlushTexMemory(fxMesa); } else #endif { struct gl_texture_object *obj = FindOldestObject(fxMesa, tmu); if (obj) { tdfxTMMoveOutTM_NoLock(fxMesa, obj); fxMesa->stats.texSwaps++; } else { _mesa_problem(NULL, "%s: extreme texmem fragmentation", __FUNCTION__); _glthread_UNLOCK_MUTEX(mesaShared->Mutex); return BAD_ADDRESS; } } } /* never get here, but play it safe */ _glthread_UNLOCK_MUTEX(mesaShared->Mutex); return BAD_ADDRESS; } /* * Remove the given tdfxMemRange node from hardware texture memory. */ static void RemoveRange_NoLock(tdfxContextPtr fxMesa, FxU32 tmu, tdfxMemRange *range) { struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared; struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData; tdfxMemRange *block, *prev; if (shared->umaTexMemory) { assert(tmu == TDFX_TMU0); } if (!range) return; if (range->startAddr == range->endAddr) { DELETE_RANGE_NODE(shared, range); return; } shared->freeTexMem[tmu] += range->endAddr - range->startAddr; /* find position in linked list to insert this tdfxMemRange node */ prev = NULL; block = shared->tmFree[tmu]; while (block) { assert(range->startAddr != block->startAddr); if (range->startAddr > block->startAddr) { prev = block; block = block->next; } else { break; } } /* Insert the free block, combine with adjacent blocks when possible */ range->next = block; if (block) { if (range->endAddr == block->startAddr) { /* Combine */ block->startAddr = range->startAddr; DELETE_RANGE_NODE(shared, range); range = block; } } if (prev) { if (prev->endAddr == range->startAddr) { /* Combine */ prev->endAddr = range->endAddr; prev->next = range->next; DELETE_RANGE_NODE(shared, range); } else { prev->next = range; } } else { shared->tmFree[tmu] = range; } } #if 0 /* NOT USED */ static void RemoveRange(tdfxContextPtr fxMesa, FxU32 tmu, tdfxMemRange *range) { struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared; _glthread_LOCK_MUTEX(mesaShared->Mutex); RemoveRange_NoLock(fxMesa, tmu, range); _glthread_UNLOCK_MUTEX(mesaShared->Mutex); } #endif /* * Allocate space for a texture image. * is the texture unit * is the number of bytes to allocate */ static tdfxMemRange * AllocTexMem(tdfxContextPtr fxMesa, FxU32 tmu, FxU32 texmemsize) { FxU32 startAddr; startAddr = FindStartAddr(fxMesa, tmu, texmemsize); if (startAddr == BAD_ADDRESS) { _mesa_problem(fxMesa->glCtx, "%s returned NULL! tmu=%d texmemsize=%d", __FUNCTION__, (int) tmu, (int) texmemsize); return NULL; } else { tdfxMemRange *range; range = NewRangeNode(fxMesa, startAddr, startAddr + texmemsize); return range; } } /* * Download (copy) the given texture data (all mipmap levels) into the * Voodoo's texture memory. * The texture memory must have already been allocated. */ void tdfxTMDownloadTexture(tdfxContextPtr fxMesa, struct gl_texture_object *tObj) { tdfxTexInfo *ti; GLint l; FxU32 targetTMU; assert(tObj); ti = TDFX_TEXTURE_DATA(tObj); assert(ti); targetTMU = ti->whichTMU; switch (targetTMU) { case TDFX_TMU0: case TDFX_TMU1: if (ti->tm[targetTMU]) { for (l = ti->minLevel; l <= ti->maxLevel && tObj->Image[0][l]->Data; l++) { GrLOD_t glideLod = ti->info.largeLodLog2 - l + tObj->BaseLevel; fxMesa->Glide.grTexDownloadMipMapLevel(targetTMU, ti->tm[targetTMU]->startAddr, glideLod, ti->info.largeLodLog2, ti->info.aspectRatioLog2, ti->info.format, GR_MIPMAPLEVELMASK_BOTH, tObj->Image[0][l]->Data); } } break; case TDFX_TMU_SPLIT: if (ti->tm[TDFX_TMU0] && ti->tm[TDFX_TMU1]) { for (l = ti->minLevel; l <= ti->maxLevel && tObj->Image[0][l]->Data; l++) { GrLOD_t glideLod = ti->info.largeLodLog2 - l + tObj->BaseLevel; fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU0, ti->tm[TDFX_TMU0]->startAddr, glideLod, ti->info.largeLodLog2, ti->info.aspectRatioLog2, ti->info.format, GR_MIPMAPLEVELMASK_ODD, tObj->Image[0][l]->Data); fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU1, ti->tm[TDFX_TMU1]->startAddr, glideLod, ti->info.largeLodLog2, ti->info.aspectRatioLog2, ti->info.format, GR_MIPMAPLEVELMASK_EVEN, tObj->Image[0][l]->Data); } } break; case TDFX_TMU_BOTH: if (ti->tm[TDFX_TMU0] && ti->tm[TDFX_TMU1]) { for (l = ti->minLevel; l <= ti->maxLevel && tObj->Image[0][l]->Data; l++) { GrLOD_t glideLod = ti->info.largeLodLog2 - l + tObj->BaseLevel; fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU0, ti->tm[TDFX_TMU0]->startAddr, glideLod, ti->info.largeLodLog2, ti->info.aspectRatioLog2, ti->info.format, GR_MIPMAPLEVELMASK_BOTH, tObj->Image[0][l]->Data); fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU1, ti->tm[TDFX_TMU1]->startAddr, glideLod, ti->info.largeLodLog2, ti->info.aspectRatioLog2, ti->info.format, GR_MIPMAPLEVELMASK_BOTH, tObj->Image[0][l]->Data); } } break; default: _mesa_problem(NULL, "%s: bad tmu (%d)", __FUNCTION__, (int)targetTMU); return; } } void tdfxTMReloadMipMapLevel(struct gl_context *ctx, struct gl_texture_object *tObj, GLint level) { tdfxContextPtr fxMesa = TDFX_CONTEXT(ctx); tdfxTexInfo *ti = TDFX_TEXTURE_DATA(tObj); GrLOD_t glideLod; FxU32 tmu; tmu = ti->whichTMU; glideLod = ti->info.largeLodLog2 - level + tObj->BaseLevel; ASSERT(ti->isInTM); LOCK_HARDWARE(fxMesa); switch (tmu) { case TDFX_TMU0: case TDFX_TMU1: fxMesa->Glide.grTexDownloadMipMapLevel(tmu, ti->tm[tmu]->startAddr, glideLod, ti->info.largeLodLog2, ti->info.aspectRatioLog2, ti->info.format, GR_MIPMAPLEVELMASK_BOTH, tObj->Image[0][level]->Data); break; case TDFX_TMU_SPLIT: fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU0, ti->tm[GR_TMU0]->startAddr, glideLod, ti->info.largeLodLog2, ti->info.aspectRatioLog2, ti->info.format, GR_MIPMAPLEVELMASK_ODD, tObj->Image[0][level]->Data); fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU1, ti->tm[GR_TMU1]->startAddr, glideLod, ti->info.largeLodLog2, ti->info.aspectRatioLog2, ti->info.format, GR_MIPMAPLEVELMASK_EVEN, tObj->Image[0][level]->Data); break; case TDFX_TMU_BOTH: fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU0, ti->tm[GR_TMU0]->startAddr, glideLod, ti->info.largeLodLog2, ti->info.aspectRatioLog2, ti->info.format, GR_MIPMAPLEVELMASK_BOTH, tObj->Image[0][level]->Data); fxMesa->Glide.grTexDownloadMipMapLevel(GR_TMU1, ti->tm[GR_TMU1]->startAddr, glideLod, ti->info.largeLodLog2, ti->info.aspectRatioLog2, ti->info.format, GR_MIPMAPLEVELMASK_BOTH, tObj->Image[0][level]->Data); break; default: _mesa_problem(ctx, "%s: bad tmu (%d)", __FUNCTION__, (int)tmu); break; } UNLOCK_HARDWARE(fxMesa); } /* * Allocate space for the given texture in texture memory then * download (copy) it into that space. */ void tdfxTMMoveInTM_NoLock( tdfxContextPtr fxMesa, struct gl_texture_object *tObj, FxU32 targetTMU ) { tdfxTexInfo *ti = TDFX_TEXTURE_DATA(tObj); FxU32 texmemsize; fxMesa->stats.reqTexUpload++; if (ti->isInTM) { if (ti->whichTMU == targetTMU) return; if (targetTMU == TDFX_TMU_SPLIT || ti->whichTMU == TDFX_TMU_SPLIT) { tdfxTMMoveOutTM_NoLock(fxMesa, tObj); } else { if (ti->whichTMU == TDFX_TMU_BOTH) return; targetTMU = TDFX_TMU_BOTH; } } ti->whichTMU = targetTMU; switch (targetTMU) { case TDFX_TMU0: case TDFX_TMU1: texmemsize = fxMesa->Glide.grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH, &(ti->info)); ti->tm[targetTMU] = AllocTexMem(fxMesa, targetTMU, texmemsize); break; case TDFX_TMU_SPLIT: texmemsize = fxMesa->Glide.grTexTextureMemRequired(GR_MIPMAPLEVELMASK_ODD, &(ti->info)); ti->tm[TDFX_TMU0] = AllocTexMem(fxMesa, TDFX_TMU0, texmemsize); if (ti->tm[TDFX_TMU0]) fxMesa->stats.memTexUpload += texmemsize; texmemsize = fxMesa->Glide.grTexTextureMemRequired(GR_MIPMAPLEVELMASK_EVEN, &(ti->info)); ti->tm[TDFX_TMU1] = AllocTexMem(fxMesa, TDFX_TMU1, texmemsize); break; case TDFX_TMU_BOTH: texmemsize = fxMesa->Glide.grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH, &(ti->info)); ti->tm[TDFX_TMU0] = AllocTexMem(fxMesa, TDFX_TMU0, texmemsize); if (ti->tm[TDFX_TMU0]) fxMesa->stats.memTexUpload += texmemsize; /*texmemsize = fxMesa->Glide.grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH, &(ti->info));*/ ti->tm[TDFX_TMU1] = AllocTexMem(fxMesa, TDFX_TMU1, texmemsize); break; default: _mesa_problem(NULL, "%s: bad tmu (%d)", __FUNCTION__, (int)targetTMU); return; } ti->reloadImages = GL_TRUE; ti->isInTM = GL_TRUE; fxMesa->stats.texUpload++; } /* * Move the given texture out of hardware texture memory. * This deallocates the texture's memory space. */ void tdfxTMMoveOutTM_NoLock( tdfxContextPtr fxMesa, struct gl_texture_object *tObj ) { struct gl_shared_state *mesaShared = fxMesa->glCtx->Shared; struct tdfxSharedState *shared = (struct tdfxSharedState *) mesaShared->DriverData; tdfxTexInfo *ti = TDFX_TEXTURE_DATA(tObj); if (MESA_VERBOSE & VERBOSE_DRIVER) { fprintf(stderr, "fxmesa: %s(%p (%d))\n", __FUNCTION__, (void *)tObj, tObj->Name); } /* VerifyFreeList(fxMesa, 0); VerifyFreeList(fxMesa, 1); */ if (!ti || !ti->isInTM) return; switch (ti->whichTMU) { case TDFX_TMU0: case TDFX_TMU1: RemoveRange_NoLock(fxMesa, ti->whichTMU, ti->tm[ti->whichTMU]); break; case TDFX_TMU_SPLIT: case TDFX_TMU_BOTH: assert(!shared->umaTexMemory); (void) shared; RemoveRange_NoLock(fxMesa, TDFX_TMU0, ti->tm[TDFX_TMU0]); RemoveRange_NoLock(fxMesa, TDFX_TMU1, ti->tm[TDFX_TMU1]); break; default: _mesa_problem(NULL, "%s: bad tmu (%d)", __FUNCTION__, (int)ti->whichTMU); return; } ti->isInTM = GL_FALSE; ti->tm[0] = NULL; ti->tm[1] = NULL; ti->whichTMU = TDFX_TMU_NONE; /* VerifyFreeList(fxMesa, 0); VerifyFreeList(fxMesa, 1); */ } /* * Called via glDeleteTexture to delete a texture object. */ void tdfxTMFreeTexture(tdfxContextPtr fxMesa, struct gl_texture_object *tObj) { tdfxTexInfo *ti = TDFX_TEXTURE_DATA(tObj); if (ti) { tdfxTMMoveOutTM(fxMesa, tObj); FREE(ti); tObj->DriverData = NULL; } /* VerifyFreeList(fxMesa, 0); VerifyFreeList(fxMesa, 1); */ } /* * After a context switch this function will be called to restore * texture memory for the new context. */ void tdfxTMRestoreTextures_NoLock( tdfxContextPtr fxMesa ) { struct gl_context *ctx = fxMesa->glCtx; struct _mesa_HashTable *textures = fxMesa->glCtx->Shared->TexObjects; GLuint id; for (id = _mesa_HashFirstEntry(textures); id; id = _mesa_HashNextEntry(textures, id)) { struct gl_texture_object *tObj = _mesa_lookup_texture(fxMesa->glCtx, id); tdfxTexInfo *ti = TDFX_TEXTURE_DATA( tObj ); if ( ti && ti->isInTM ) { int i; for ( i = 0 ; i < MAX_TEXTURE_UNITS ; i++ ) { if ( ctx->Texture.Unit[i]._Current == tObj ) { tdfxTMDownloadTexture( fxMesa, tObj ); break; } } if ( i == MAX_TEXTURE_UNITS ) { tdfxTMMoveOutTM_NoLock( fxMesa, tObj ); } } } /* VerifyFreeList(fxMesa, 0); VerifyFreeList(fxMesa, 1); */ }