fixup draw/depth region handling in i830 along lines of i915
[mesa.git] / src / mesa / drivers / glide / fxtexman.c
index 3f899cbf5a80c8b8cc88ee9612e086cf86aa05fa..940c8fd0b97d92c9d065b6b8753ae2a61a0193aa 100644 (file)
-/* -*- mode: C; tab-width:8;  -*-
-
-             fxtexman.c - 3Dfx VooDoo texture memory functions
-*/
-
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * Mesa 3-D graphics library
+ * Version:  4.0
  *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Copyright (C) 1999-2001  Brian Paul   All Rights Reserved.
  *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 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:
  *
- * See the file fxapi.c for more informations about authors
+ * 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"
 
-static tfxTMFreeNode *fxTMNewTMFreeNode(FxU32 start, FxU32 end)
-{
-  tfxTMFreeNode *tmn;
+int texSwaps = 0;
+static FxU32 texBoundMask;
 
-  if(!(tmn=MALLOC(sizeof(tfxTMFreeNode)))) {
-    fprintf(stderr,"fx Driver: out of memory !\n");
-    fxCloseHardware();
-    exit(-1);
-  }
+#define FX_2MB_SPLIT 0x200000
 
-  tmn->next=NULL;
-  tmn->startAddress=start;
-  tmn->endAddress=end;
+static struct gl_texture_object *fxTMFindOldestObject(fxMesaContext fxMesa,
+                                                     int tmu);
 
-  return tmn;
-}
 
-static void fxTMUInit(fxMesaContext fxMesa, int tmu)
+#ifdef TEXSANITY
+static void
+fubar()
 {
-  tfxTMFreeNode *tmn,*tmntmp;
-  FxU32 start,end,blockstart,blockend;
-
-  start=grTexMinAddress(tmu);
-  end=grTexMaxAddress(tmu);
-
-  if(fxMesa->verbose) {
-    fprintf(stderr,"%s configuration:",(tmu==FX_TMU0) ? "TMU0" : "TMU1");
-    fprintf(stderr,"  Lower texture memory address (%u)\n",(unsigned int)start);
-    fprintf(stderr,"  Higher texture memory address (%u)\n",(unsigned int)end);
-    fprintf(stderr,"  Splitting Texture memory in 2Mb blocks:\n");
-  }
-
-  fxMesa->freeTexMem[tmu]=end-start;
-  fxMesa->tmFree[tmu]=NULL;
-  fxMesa->tmAlloc[tmu]=NULL;
-
-  blockstart=start;
-  while(blockstart<=end) {
-    if(blockstart+0x1fffff>end)
-      blockend=end;
-    else
-      blockend=blockstart+0x1fffff;
-
-    if(fxMesa->verbose)
-      fprintf(stderr,"    %07u-%07u\n",(unsigned int)blockstart,(unsigned int)blockend);
-
-    tmn=fxTMNewTMFreeNode(blockstart,blockend);
-
-    if(fxMesa->tmFree[tmu]) {
-      for(tmntmp=fxMesa->tmFree[tmu];tmntmp->next!=NULL;tmntmp=tmntmp->next){};
-      tmntmp->next=tmn;
-    } else
-      fxMesa->tmFree[tmu]=tmn;
-
-    blockstart+=0x1fffff+1;
-  }
 }
 
-void fxTMInit(fxMesaContext fxMesa)
+  /* Sanity Check */
+static void
+sanity(fxMesaContext fxMesa)
 {
-  fxTMUInit(fxMesa,FX_TMU0);
-
-  if(fxMesa->haveTwoTMUs)
-    fxTMUInit(fxMesa,FX_TMU1);
-
-  fxMesa->texBindNumber=0;
+   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 struct gl_texture_object *fxTMFindOldestTMBlock(fxMesaContext fxMesa,
-                                                      tfxTMAllocNode *tmalloc,
-                                                      GLuint texbindnumber)
+static MemRange *
+fxTMNewRangeNode(fxMesaContext fxMesa, FxU32 start, FxU32 end)
 {
-  GLuint age,oldestage,lasttimeused;
-  struct gl_texture_object *oldesttexobj;
-
-  (void)fxMesa;
-  oldesttexobj=tmalloc->tObj;
-  oldestage=0;
-
-  while(tmalloc) {
-    lasttimeused=((tfxTexInfo *)(tmalloc->tObj->DriverData))->tmi.lastTimeUsed;
-
-    if(lasttimeused>texbindnumber)
-      age=texbindnumber+(UINT_MAX-lasttimeused+1); /* TO DO: check */
-    else
-      age=texbindnumber-lasttimeused;
-
-    if(age>=oldestage) {
-      oldestage=age;
-      oldesttexobj=tmalloc->tObj;
-    }
-
-    tmalloc=tmalloc->next;
-  }
-
-  return oldesttexobj;
+   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;
 }
 
-static GLboolean fxTMFreeOldTMBlock(fxMesaContext fxMesa, GLint tmu)
+#if 1
+#define fxTMDeleteRangeNode(fxMesa, range) \
+   do {                                    \
+       range->next = fxMesa->tmPool;       \
+       fxMesa->tmPool = range;             \
+   } while (0);
+#else
+static void
+fxTMDeleteRangeNode(fxMesaContext fxMesa, MemRange * range)
 {
-  struct gl_texture_object *oldesttexobj;
-
-  if(!fxMesa->tmAlloc[tmu])
-    return GL_FALSE;
-
-  oldesttexobj=fxTMFindOldestTMBlock(fxMesa,fxMesa->tmAlloc[tmu],fxMesa->texBindNumber);
-
-  fxTMMoveOutTM(fxMesa,oldesttexobj);
-
-  return GL_TRUE;
+   range->next = fxMesa->tmPool;
+   fxMesa->tmPool = range;
 }
+#endif
 
-static tfxTMFreeNode *fxTMExtractTMFreeBlock(tfxTMFreeNode *tmfree, int texmemsize,
-                                            GLboolean *success, FxU32 *startadr)
+static void
+fxTMUInit(fxMesaContext fxMesa, int tmu)
 {
-  int blocksize;
-
-  /* TO DO: cut recursion */
-
-  if(!tmfree) {
-    *success=GL_FALSE;
-    return NULL;
-  }
+   MemRange *tmn, *last;
+   FxU32 start, end, blockstart, blockend, chunk;
 
-  blocksize=(int)tmfree->endAddress-(int)tmfree->startAddress+1;
+   start = grTexMinAddress(tmu);
+   end = grTexMaxAddress(tmu);
 
-  if(blocksize==texmemsize) {
-    tfxTMFreeNode *nexttmfree;
+   chunk = (fxMesa->type >= GR_SSTTYPE_Banshee) ? (end - start) : FX_2MB_SPLIT;
 
-    *success=GL_TRUE;
-    *startadr=tmfree->startAddress;
+   if (fxMesa->verbose) {
+      fprintf(stderr, "Voodoo TMU%d configuration:\n", tmu);
+   }
 
-    nexttmfree=tmfree->next;
-    FREE(tmfree);
+   fxMesa->freeTexMem[tmu] = end - start;
+   fxMesa->tmFree[tmu] = NULL;
 
-    return nexttmfree;
-  }
+   last = 0;
+   blockstart = start;
+   while (blockstart < end) {
+      if (blockstart + chunk > end)
+        blockend = end;
+      else
+        blockend = blockstart + chunk;
 
-  if(blocksize>texmemsize) {
-    *success=GL_TRUE;
-    *startadr=tmfree->startAddress;
+      if (fxMesa->verbose)
+        fprintf(stderr, "Voodoo   %08u-%08u\n",
+                (unsigned int) blockstart, (unsigned int) blockend);
 
-    tmfree->startAddress+=texmemsize;
+      tmn = fxTMNewRangeNode(fxMesa, blockstart, blockend);
+      tmn->next = NULL;
 
-    return tmfree;
-  }
 
-  tmfree->next=fxTMExtractTMFreeBlock(tmfree->next,texmemsize,success,startadr);
+      if (last)
+        last->next = tmn;
+      else
+        fxMesa->tmFree[tmu] = tmn;
+      last = tmn;
 
-  return tmfree;
+      blockstart += chunk;
+   }
 }
 
-static tfxTMAllocNode *fxTMGetTMBlock(fxMesaContext fxMesa, struct gl_texture_object *tObj,
-                                     GLint tmu, int texmemsize)
+static int
+fxTMFindStartAddr(fxMesaContext fxMesa, GLint tmu, int size)
 {
-  tfxTMFreeNode *newtmfree;
-  tfxTMAllocNode *newtmalloc;
-  GLboolean success;
-  FxU32 startadr;
-
-  for(;;) { /* TO DO: improve performaces */
-    newtmfree=fxTMExtractTMFreeBlock(fxMesa->tmFree[tmu],texmemsize,&success,&startadr);
-
-    if(success) {
-      fxMesa->tmFree[tmu]=newtmfree;
+   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++;
+   }
+}
 
-      fxMesa->freeTexMem[tmu]-=texmemsize;
+int fxTMCheckStartAddr (fxMesaContext fxMesa, GLint tmu, tfxTexInfo *ti)
+{
+ MemRange *tmp;
+ int size;
 
-      if(!(newtmalloc=MALLOC(sizeof(tfxTMAllocNode)))) {
-       fprintf(stderr,"fx Driver: out of memory !\n");
-       fxCloseHardware();
-       exit(-1);
-      }
-      
-      newtmalloc->next=fxMesa->tmAlloc[tmu];
-      newtmalloc->startAddress=startadr;
-      newtmalloc->endAddress=startadr+texmemsize-1;
-      newtmalloc->tObj=tObj;
+ if (fxMesa->HaveTexUma) {
+    return FXTRUE;
+ }
 
     fxMesa->tmAlloc[tmu]=newtmalloc;
size = grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH, &(ti->info));
 
-      return newtmalloc;
-    }
+ tmp = fxMesa->tmFree[tmu];
+ while (tmp) {
+       if (tmp->endAddr - tmp->startAddr >= size) { /* Fits here */
+          return FXTRUE;
+       }
+       tmp = tmp->next;
+ }
 
-    if(!fxTMFreeOldTMBlock(fxMesa,tmu)) {
-      fprintf(stderr,"fx Driver: internal error in fxTMGetTMBlock()\n");
-      fprintf(stderr,"           TMU: %d Size: %d\n",tmu,texmemsize);
-    
-      fxCloseHardware();
-      exit(-1);
-    }
-  }
+ return FXFALSE;
 }
 
-void fxTMMoveInTM(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint where)
+static void
+fxTMRemoveRange(fxMesaContext fxMesa, GLint tmu, MemRange * range)
 {
-  tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
-  int i,l;
-  int texmemsize;
-
-  if (MESA_VERBOSE&VERBOSE_DRIVER) {
-     fprintf(stderr,"fxmesa: fxTMMoveInTM(%d)\n",tObj->Name);
-  }
-
-  fxMesa->stats.reqTexUpload++;
-
-  if(!ti->validated) {
-    fprintf(stderr,"fx Driver: internal error in fxTMMoveInTM() -> not validated\n");
-    fxCloseHardware();
-    exit(-1);
-  }
-
-  if(ti->tmi.isInTM)
-    return;
-
-  if (MESA_VERBOSE&(VERBOSE_DRIVER|VERBOSE_TEXTURE)) {
-     fprintf(stderr,"fxmesa: downloading %x (%d) in texture memory in %d\n",(GLuint)tObj,tObj->Name,where);
-  }
-
-  ti->tmi.whichTMU=(FxU32)where;
-
-  switch(where) {
-  case FX_TMU0:
-  case FX_TMU1:
-    texmemsize=(int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH,&(ti->info));
-    ti->tmi.tm[where]=fxTMGetTMBlock(fxMesa,tObj,where,texmemsize);
-    fxMesa->stats.memTexUpload+=texmemsize;
-
-    for(i=FX_largeLodValue(ti->info),l=ti->minLevel;i<=FX_smallLodValue(ti->info);i++,l++)
-      grTexDownloadMipMapLevel(where,
-                              ti->tmi.tm[where]->startAddress,FX_valueToLod(i),
-                              FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
-                              ti->info.format,GR_MIPMAPLEVELMASK_BOTH,
-                              ti->tmi.mipmapLevel[l].data);
-    break;
-  case FX_TMU_SPLIT: /* TO DO: alternate even/odd TMU0/TMU1 */
-    texmemsize=(int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_ODD,&(ti->info));
-    ti->tmi.tm[FX_TMU0]=fxTMGetTMBlock(fxMesa,tObj,FX_TMU0,texmemsize);
-    fxMesa->stats.memTexUpload+=texmemsize;
-
-    texmemsize=(int)grTexTextureMemRequired(GR_MIPMAPLEVELMASK_EVEN,&(ti->info));
-    ti->tmi.tm[FX_TMU1]=fxTMGetTMBlock(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++) {
-      grTexDownloadMipMapLevel(GR_TMU0,ti->tmi.tm[FX_TMU0]->startAddress,FX_valueToLod(i),
-                              FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
-                              ti->info.format,GR_MIPMAPLEVELMASK_ODD,
-                              ti->tmi.mipmapLevel[l].data);
-
-      grTexDownloadMipMapLevel(GR_TMU1,ti->tmi.tm[FX_TMU1]->startAddress,FX_valueToLod(i),
-                              FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
-                              ti->info.format,GR_MIPMAPLEVELMASK_EVEN,
-                              ti->tmi.mipmapLevel[l].data);
-    }
-    break;
-  default:
-    fprintf(stderr,"fx Driver: internal error in fxTMMoveInTM() -> wrong tmu (%d)\n",where);
-    fxCloseHardware();
-    exit(-1);
-  }
-
-  fxMesa->stats.texUpload++;
-
-  ti->tmi.isInTM=GL_TRUE;
+   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;
+   }
 }
 
-void fxTMReloadMipMapLevel(fxMesaContext fxMesa, struct gl_texture_object *tObj, GLint level)
+static struct gl_texture_object *
+fxTMFindOldestObject(fxMesaContext fxMesa, int tmu)
 {
-  tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
-  GrLOD_t lodlevel;
-  GLint tmu;
-
-  if(!ti->validated) {
-    fprintf(stderr,"fx Driver: internal error in fxTMReloadMipMapLevel() -> not validated\n");
-    fxCloseHardware();
-    exit(-1);
-  }
-
-  tmu=(int)ti->tmi.whichTMU;
-  fxTMMoveInTM(fxMesa,tObj,tmu);
-
-  fxTexGetInfo(ti->tmi.mipmapLevel[0].width,ti->tmi.mipmapLevel[0].height,
-              &lodlevel,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-
-  switch(tmu) {
-  case FX_TMU0:
-  case FX_TMU1:
-    grTexDownloadMipMapLevel(tmu,
-                            ti->tmi.tm[tmu]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level),
-                            FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
-                            ti->info.format,GR_MIPMAPLEVELMASK_BOTH,
-                            ti->tmi.mipmapLevel[level].data);
-    break;
-  case FX_TMU_SPLIT: /* TO DO: alternate even/odd TMU0/TMU1 */
-    grTexDownloadMipMapLevel(GR_TMU0,
-                            ti->tmi.tm[GR_TMU0]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level),
-                            FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
-                            ti->info.format,GR_MIPMAPLEVELMASK_ODD,
-                            ti->tmi.mipmapLevel[level].data);
-    
-    grTexDownloadMipMapLevel(GR_TMU1,
-                            ti->tmi.tm[GR_TMU1]->startAddress,FX_valueToLod(FX_lodToValue(lodlevel)+level),
-                            FX_largeLodLog2(ti->info),FX_aspectRatioLog2(ti->info),
-                            ti->info.format,GR_MIPMAPLEVELMASK_EVEN,
-                            ti->tmi.mipmapLevel[level].data);
-    break;
-  default:
-    fprintf(stderr,"fx Driver: internal error in fxTMReloadMipMapLevel() -> wrong tmu (%d)\n",tmu);
-    fxCloseHardware();
-    exit(-1);
-  }
+   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;
+   }
 }
 
-void fxTMReloadSubMipMapLevel(fxMesaContext fxMesa, struct gl_texture_object *tObj,
-                             GLint level, GLint yoffset, GLint height)
+static MemRange *
+fxTMAddObj(fxMesaContext fxMesa,
+          struct gl_texture_object *tObj, GLint tmu, int texmemsize)
 {
-  tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
-  GrLOD_t lodlevel;
-  unsigned short *data;
-  GLint tmu;
-
-  if(!ti->validated) {
-    fprintf(stderr,"fx Driver: internal error in fxTMReloadSubMipMapLevel() -> not validated\n");
-    fxCloseHardware();
-    exit(-1);
-  }
-
-  tmu=(int)ti->tmi.whichTMU;
-  fxTMMoveInTM(fxMesa,tObj,tmu);
-
-  fxTexGetInfo(ti->tmi.mipmapLevel[0].width,ti->tmi.mipmapLevel[0].height,
-              &lodlevel,NULL,NULL,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=ti->tmi.mipmapLevel[level].data+((yoffset*ti->tmi.mipmapLevel[level].width)>>1);
-  else
-    data=ti->tmi.mipmapLevel[level].data+yoffset*ti->tmi.mipmapLevel[level].width;
-
-  switch(tmu) {
-  case FX_TMU0:
-  case FX_TMU1:
-    grTexDownloadMipMapLevelPartial(tmu,
-                                   ti->tmi.tm[tmu]->startAddress,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: /* TO DO: alternate even/odd TMU0/TMU1 */
-    grTexDownloadMipMapLevelPartial(GR_TMU0,
-                                   ti->tmi.tm[FX_TMU0]->startAddress,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->tmi.tm[FX_TMU1]->startAddress,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;
-  default:
-    fprintf(stderr,"fx Driver: internal error in fxTMReloadSubMipMapLevel() -> wrong tmu (%d)\n",tmu);
-    fxCloseHardware();
-    exit(-1);
-  }
+   FxI32 startAddr;
+   MemRange *range;
+
+   startAddr = fxTMFindStartAddr(fxMesa, tmu, texmemsize);
+   if (startAddr < 0)
+      return 0;
+   range = fxTMNewRangeNode(fxMesa, startAddr, startAddr + texmemsize);
+   return range;
 }
 
-static tfxTMAllocNode *fxTMFreeTMAllocBlock(tfxTMAllocNode *tmalloc,
-                                           tfxTMAllocNode *tmunalloc)
-{
-  if(!tmalloc)
-    return NULL;
-
-  if(tmalloc==tmunalloc) {
-    tfxTMAllocNode *newtmalloc;
-
-    newtmalloc=tmalloc->next;
-    FREE(tmalloc);
-
-    return newtmalloc;
-  }
-
-  tmalloc->next=fxTMFreeTMAllocBlock(tmalloc->next,tmunalloc);
-
-  return tmalloc;
-}
+/* External Functions */
 
-static tfxTMFreeNode *fxTMAddTMFree(tfxTMFreeNode *tmfree, FxU32 startadr, FxU32 endadr)
+void
+fxTMMoveInTM_NoLock(fxMesaContext fxMesa, struct gl_texture_object *tObj,
+                   GLint where)
 {
-  if(!tmfree)
-    return fxTMNewTMFreeNode(startadr,endadr);
-
-  if((endadr+1==tmfree->startAddress) && (tmfree->startAddress & 0x1fffff)) {
-    tmfree->startAddress=startadr;
-
-    return tmfree;
-  }
-
-  if((startadr-1==tmfree->endAddress) && (startadr & 0x1fffff)) {
-    tmfree->endAddress=endadr;
-
-    if((tmfree->next && (endadr+1==tmfree->next->startAddress) &&
-        (tmfree->next->startAddress & 0x1fffff))) {
-      tfxTMFreeNode *nexttmfree;
-
-      tmfree->endAddress=tmfree->next->endAddress;
+   tfxTexInfo *ti = fxTMGetTexInfo(tObj);
+   int i, l;
+   int texmemsize;
 
-      nexttmfree=tmfree->next->next;
-      FREE(tmfree->next);
+   if (TDFX_DEBUG & VERBOSE_DRIVER) {
+      fprintf(stderr, "fxTMMoveInTM_NoLock(%d)\n", tObj->Name);
+   }
 
-      tmfree->next=nexttmfree;
-    }
+   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);
+   }
 
-    return tmfree;
-  }
+   fxMesa->stats.texUpload++;
 
-  if(startadr<tmfree->startAddress) {
-    tfxTMFreeNode *newtmfree;
+   ti->isInTM = GL_TRUE;
+}
 
-    newtmfree=fxTMNewTMFreeNode(startadr,endadr);
-    newtmfree->next=tmfree;
 
-    return newtmfree;
-  }
+void
+fxTMMoveInTM(fxMesaContext fxMesa, struct gl_texture_object *tObj,
+            GLint where)
+{
+   BEGIN_BOARD_LOCK();
+   fxTMMoveInTM_NoLock(fxMesa, tObj, where);
+   END_BOARD_LOCK();
+}
 
-  tmfree->next=fxTMAddTMFree(tmfree->next,startadr,endadr);
 
-  return tmfree;
+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);
+   }
 }
 
-static void fxTMFreeTMBlock(fxMesaContext fxMesa, GLint tmu, tfxTMAllocNode *tmalloc)
+void
+fxTMReloadSubMipMapLevel(fxMesaContext fxMesa,
+                        struct gl_texture_object *tObj,
+                        GLint level, GLint yoffset, GLint height)
 {
-  FxU32 startadr,endadr;
+   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);
 
-  startadr=tmalloc->startAddress;
-  endadr=tmalloc->endAddress;
+   assert(mml);
 
-  fxMesa->tmAlloc[tmu]=fxTMFreeTMAllocBlock(fxMesa->tmAlloc[tmu],tmalloc);
+   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);
+   }
+}
 
-  fxMesa->tmFree[tmu]=fxTMAddTMFree(fxMesa->tmFree[tmu],startadr,endadr);
+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);
+   }
 
-  fxMesa->freeTexMem[tmu]+=endadr-startadr+1;
+   ti->isInTM = GL_FALSE;
+   ti->whichTMU = FX_TMU_NONE;
 }
 
-void fxTMMoveOutTM(fxMesaContext fxMesa, struct gl_texture_object *tObj)
+void
+fxTMFreeTexture(fxMesaContext fxMesa, struct gl_texture_object *tObj)
 {
-  tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
-
-  if (MESA_VERBOSE&VERBOSE_DRIVER) {
-     fprintf(stderr,"fxmesa: fxTMMoveOutTM(%x (%d))\n",(GLuint)tObj,tObj->Name);
-  }
-
-  if(!ti->tmi.isInTM)
-    return;
-
-  switch(ti->tmi.whichTMU) {
-  case FX_TMU0:
-  case FX_TMU1:
-    fxTMFreeTMBlock(fxMesa,(int)ti->tmi.whichTMU,ti->tmi.tm[ti->tmi.whichTMU]);
-    break;
-  case FX_TMU_SPLIT:
-    fxTMFreeTMBlock(fxMesa,FX_TMU0,ti->tmi.tm[FX_TMU0]);
-    fxTMFreeTMBlock(fxMesa,FX_TMU1,ti->tmi.tm[FX_TMU1]);
-    break;
-  default:
-    fprintf(stderr,"fx Driver: internal error in fxTMMoveOutTM()\n");
-    fxCloseHardware();
-    exit(-1);
-  }
-
-  ti->tmi.whichTMU=FX_TMU_NONE;
-  ti->tmi.isInTM=GL_FALSE;
+   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 fxTMFreeTexture(fxMesaContext fxMesa, struct gl_texture_object *tObj)
+void
+fxTMInit(fxMesaContext fxMesa)
 {
-  tfxTexInfo *ti=(tfxTexInfo *)tObj->DriverData;
-  int i;
-
-  fxTMMoveOutTM(fxMesa,tObj);
+   fxMesa->texBindNumber = 0;
+   fxMesa->tmPool = 0;
 
-  for(i=0;i<MAX_TEXTURE_LEVELS;i++) {
-    if(ti->tmi.mipmapLevel[i].used &&
-       ti->tmi.mipmapLevel[i].translated)
-      FREE(ti->tmi.mipmapLevel[i].data);
+   if (fxMesa->HaveTexUma) {
+      grEnable(GR_TEXTURE_UMA_EXT);
+   }
 
-    (void)ti->tmi.mipmapLevel[i].data;
-  }
-}
-
-void fxTMFreeAllFreeNode(tfxTMFreeNode *fn)
-{
-  if(!fn)
-    return;
+   fxTMUInit(fxMesa, FX_TMU0);
 
-  if(fn->next)
-    fxTMFreeAllFreeNode(fn->next);
+   if (!fxMesa->HaveTexUma && fxMesa->haveTwoTMUs)
+      fxTMUInit(fxMesa, FX_TMU1);
 
-  FREE(fn);
+   texBoundMask = (fxMesa->type >= GR_SSTTYPE_Banshee) ? -1 : (FX_2MB_SPLIT - 1);
 }
 
-void fxTMFreeAllAllocNode(tfxTMAllocNode *an)
+void
+fxTMClose(fxMesaContext fxMesa)
 {
-  if(!an)
-    return;
-
-  if(an->next)
-    fxTMFreeAllAllocNode(an->next);
-
-  FREE(an);
+   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 fxTMClose(fxMesaContext fxMesa)
+void
+fxTMRestoreTextures_NoLock(fxMesaContext ctx)
 {
-  fxTMFreeAllFreeNode(fxMesa->tmFree[FX_TMU0]);
-  fxTMFreeAllAllocNode(fxMesa->tmAlloc[FX_TMU0]);
-  fxMesa->tmFree[FX_TMU0] = NULL;
-  fxMesa->tmAlloc[FX_TMU0] = NULL;
-  if(fxMesa->haveTwoTMUs) {
-    fxTMFreeAllFreeNode(fxMesa->tmFree[FX_TMU1]);
-    fxTMFreeAllAllocNode(fxMesa->tmAlloc[FX_TMU1]);
-    fxMesa->tmFree[FX_TMU1] = NULL;
-    fxMesa->tmAlloc[FX_TMU1] = NULL;
-  }
+   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
 
 
@@ -571,9 +864,11 @@ void fxTMClose(fxMesaContext fxMesa)
  * Need this to provide at least one external definition.
  */
 
-int gl_fx_dummy_function_texman(void)
+extern int gl_fx_dummy_function_texman(void);
+int
+gl_fx_dummy_function_texman(void)
 {
-  return 0;
+   return 0;
 }
 
-#endif  /* FX */
+#endif /* FX */