| /* -*- 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. |
| */ |
| /* $XFree86: xc/lib/GL/mesa/src/drv/tdfx/tdfx_texman.c,v 1.5 2002/02/22 21:45:04 dawes Exp $ */ |
| |
| /* |
| * Original rewrite: |
| * Gareth Hughes <gareth@valinux.com>, 29 Sep - 1 Oct 2000 |
| * |
| * Authors: |
| * Gareth Hughes <gareth@valinux.com> |
| * Brian Paul <brianp@valinux.com> |
| * |
| */ |
| |
| #include "tdfx_context.h" |
| #include "tdfx_tex.h" |
| #include "tdfx_texman.h" |
| #include "texobj.h" |
| #include "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. |
| * <tmu> is the texture unit. |
| * <size> 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 <size> 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. |
| * <tmu> is the texture unit |
| * <texmemsize> 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(GLcontext *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); |
| 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 ) |
| { |
| GLcontext *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); |
| */ |
| } |